summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormisskey-release-bot[bot] <157398866+misskey-release-bot[bot]@users.noreply.github.com>2025-08-31 08:42:43 +0000
committerGitHub <noreply@github.com>2025-08-31 08:42:43 +0000
commitec21336d45e6e3bb26a0225fc0aa57ac98d5be4b (patch)
tree2c7aef5ba1626009377faf96941a57411dd619e5
parentMerge pull request #16244 from misskey-dev/develop (diff)
parentRelease: 2025.8.0 (diff)
downloadmisskey-ec21336d45e6e3bb26a0225fc0aa57ac98d5be4b.tar.gz
misskey-ec21336d45e6e3bb26a0225fc0aa57ac98d5be4b.tar.bz2
misskey-ec21336d45e6e3bb26a0225fc0aa57ac98d5be4b.zip
Merge pull request #16335 from misskey-dev/develop
Release: 2025.8.0
-rw-r--r--.github/min.node-version2
-rw-r--r--.github/workflows/api-misskey-js.yml2
-rw-r--r--.github/workflows/changelog-check.yml2
-rw-r--r--.github/workflows/check-misskey-js-autogen.yml4
-rw-r--r--.github/workflows/check-misskey-js-version.yml2
-rw-r--r--.github/workflows/check-spdx-license-id.yml3
-rw-r--r--.github/workflows/check_copyright_year.yml2
-rw-r--r--.github/workflows/deploy-test-environment.yml2
-rw-r--r--.github/workflows/docker-develop.yml2
-rw-r--r--.github/workflows/docker.yml2
-rw-r--r--.github/workflows/dockle.yml4
-rw-r--r--.github/workflows/get-api-diff.yml2
-rw-r--r--.github/workflows/lint.yml11
-rw-r--r--.github/workflows/locale.yml2
-rw-r--r--.github/workflows/on-release-created.yml2
-rw-r--r--.github/workflows/storybook.yml4
-rw-r--r--.github/workflows/test-backend.yml7
-rw-r--r--.github/workflows/test-frontend.yml4
-rw-r--r--.github/workflows/test-misskey-js.yml2
-rw-r--r--.github/workflows/test-production.yml2
-rw-r--r--.github/workflows/validate-api-json.yml2
-rw-r--r--CHANGELOG.md82
-rw-r--r--CONTRIBUTING.md20
-rw-r--r--Dockerfile1
-rw-r--r--idea/MkAbuseReport.stories.impl.ts4
-rw-r--r--locales/ar-SA.yml10
-rw-r--r--locales/bn-BD.yml9
-rw-r--r--locales/ca-ES.yml65
-rw-r--r--locales/cs-CZ.yml11
-rw-r--r--locales/de-DE.yml12
-rw-r--r--locales/el-GR.yml2
-rw-r--r--locales/en-US.yml95
-rw-r--r--locales/es-ES.yml79
-rw-r--r--locales/fr-FR.yml11
-rw-r--r--locales/generateDTS.js28
-rw-r--r--locales/id-ID.yml11
-rw-r--r--locales/index.d.ts258
-rw-r--r--locales/index.js1
-rw-r--r--locales/it-IT.yml195
-rw-r--r--locales/ja-JP.yml80
-rw-r--r--locales/ja-KS.yml184
-rw-r--r--locales/kn-IN.yml8
-rw-r--r--locales/ko-GS.yml6
-rw-r--r--locales/ko-KR.yml61
-rw-r--r--locales/lo-LA.yml2
-rw-r--r--locales/nl-NL.yml2
-rw-r--r--locales/no-NO.yml7
-rw-r--r--locales/pl-PL.yml9
-rw-r--r--locales/pt-PT.yml12
-rw-r--r--locales/ro-RO.yml6
-rw-r--r--locales/ru-RU.yml69
-rw-r--r--locales/sk-SK.yml9
-rw-r--r--locales/sv-SE.yml7
-rw-r--r--locales/th-TH.yml61
-rw-r--r--locales/tr-TR.yml3311
-rw-r--r--locales/uk-UA.yml9
-rw-r--r--locales/uz-UZ.yml8
-rw-r--r--locales/vi-VN.yml11
-rw-r--r--locales/zh-CN.yml71
-rw-r--r--locales/zh-TW.yml65
-rw-r--r--package.json25
-rw-r--r--packages/backend/migration/1753863104203-remoteNotesCleaning.js20
-rw-r--r--packages/backend/migration/1753868431598-remove_note_constraints.js18
-rw-r--r--packages/backend/migration/1754019326356-tweakDefaultFederationSettings.js16
-rw-r--r--packages/backend/migration/1755168347001-PageCountInNote.js58
-rw-r--r--packages/backend/migration/1755574887486-entrancePageStyle.js16
-rw-r--r--packages/backend/migration/1756062689648-NonCascadingPageEyeCatching.js18
-rw-r--r--packages/backend/package.json95
-rw-r--r--packages/backend/scripts/dev.mjs2
-rw-r--r--packages/backend/src/boot/cli.ts49
-rw-r--r--packages/backend/src/cli/CommandModule.ts23
-rw-r--r--packages/backend/src/cli/CommandService.ts49
-rw-r--r--packages/backend/src/config.ts8
-rw-r--r--packages/backend/src/core/CoreModule.ts6
-rw-r--r--packages/backend/src/core/FanoutTimelineEndpointService.ts16
-rw-r--r--packages/backend/src/core/HttpRequestService.ts19
-rw-r--r--packages/backend/src/core/NoteCreateService.ts8
-rw-r--r--packages/backend/src/core/NoteDeleteService.ts36
-rw-r--r--packages/backend/src/core/PageService.ts223
-rw-r--r--packages/backend/src/core/QueryService.ts4
-rw-r--r--packages/backend/src/core/QueueService.ts118
-rw-r--r--packages/backend/src/core/RoleService.ts3
-rw-r--r--packages/backend/src/core/SearchService.ts4
-rw-r--r--packages/backend/src/core/WebhookTestService.ts5
-rw-r--r--packages/backend/src/core/entities/ChatEntityService.ts15
-rw-r--r--packages/backend/src/core/entities/MetaEntityService.ts1
-rw-r--r--packages/backend/src/core/entities/NoteEntityService.ts25
-rw-r--r--packages/backend/src/core/entities/NoteReactionEntityService.ts50
-rw-r--r--packages/backend/src/core/entities/UserEntityService.ts5
-rw-r--r--packages/backend/src/misc/emoji-regex.ts2
-rw-r--r--packages/backend/src/misc/json-schema.ts5
-rw-r--r--packages/backend/src/models/Meta.ts22
-rw-r--r--packages/backend/src/models/Note.ts11
-rw-r--r--packages/backend/src/models/Page.ts2
-rw-r--r--packages/backend/src/models/json-schema/meta.ts4
-rw-r--r--packages/backend/src/models/json-schema/note-reaction.ts31
-rw-r--r--packages/backend/src/models/json-schema/role.ts4
-rw-r--r--packages/backend/src/models/json-schema/user-webhook.ts55
-rw-r--r--packages/backend/src/models/json-schema/user.ts8
-rw-r--r--packages/backend/src/queue/QueueProcessorModule.ts4
-rw-r--r--packages/backend/src/queue/QueueProcessorService.ts3
-rw-r--r--packages/backend/src/queue/processors/CleanRemoteNotesProcessorService.ts289
-rw-r--r--packages/backend/src/queue/processors/DeleteAccountProcessorService.ts29
-rw-r--r--packages/backend/src/server/ServerService.ts24
-rw-r--r--packages/backend/src/server/api/GetterService.ts4
-rw-r--r--packages/backend/src/server/api/endpoint-list.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/announcements/list.ts28
-rw-r--r--packages/backend/src/server/api/endpoints/admin/drive/show-file.ts16
-rw-r--r--packages/backend/src/server/api/endpoints/admin/meta.ts26
-rw-r--r--packages/backend/src/server/api/endpoints/admin/queue/show-job-logs.ts45
-rw-r--r--packages/backend/src/server/api/endpoints/admin/system-webhook/create.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/admin/system-webhook/update.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/admin/update-meta.ts20
-rw-r--r--packages/backend/src/server/api/endpoints/chat/read-all.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/clips/list.ts17
-rw-r--r--packages/backend/src/server/api/endpoints/drive/files/attached-chat-messages.ts10
-rw-r--r--packages/backend/src/server/api/endpoints/flash/update.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/i/apps.ts10
-rw-r--r--packages/backend/src/server/api/endpoints/i/webhooks/list.ts48
-rw-r--r--packages/backend/src/server/api/endpoints/i/webhooks/show.ts24
-rw-r--r--packages/backend/src/server/api/endpoints/notes/create.ts10
-rw-r--r--packages/backend/src/server/api/endpoints/notes/global-timeline.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/notes/mentions.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/notes/show.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/notes/timeline.ts9
-rw-r--r--packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/pages/create.ts41
-rw-r--r--packages/backend/src/server/api/endpoints/pages/delete.ts41
-rw-r--r--packages/backend/src/server/api/endpoints/pages/update.ts69
-rw-r--r--packages/backend/src/server/api/endpoints/users/lists/show.ts10
-rw-r--r--packages/backend/src/server/api/endpoints/users/reactions.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/users/search.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/verify-email.ts66
-rw-r--r--packages/backend/src/server/api/stream/Connection.ts21
-rw-r--r--packages/backend/src/server/api/stream/channels/antenna.ts2
-rw-r--r--packages/backend/src/server/api/stream/channels/channel.ts2
-rw-r--r--packages/backend/src/server/api/stream/channels/global-timeline.ts2
-rw-r--r--packages/backend/src/server/api/stream/channels/hashtag.ts2
-rw-r--r--packages/backend/src/server/api/stream/channels/home-timeline.ts2
-rw-r--r--packages/backend/src/server/api/stream/channels/hybrid-timeline.ts2
-rw-r--r--packages/backend/src/server/api/stream/channels/local-timeline.ts2
-rw-r--r--packages/backend/src/server/api/stream/channels/main.ts2
-rw-r--r--packages/backend/src/server/api/stream/channels/user-list.ts2
-rw-r--r--packages/backend/src/server/web/ClientServerService.ts34
-rw-r--r--packages/backend/src/server/web/boot.embed.js85
-rw-r--r--packages/backend/src/server/web/boot.js147
-rw-r--r--packages/backend/src/server/web/manifest.json8
-rw-r--r--packages/backend/src/server/web/views/base-embed.pug3
-rw-r--r--packages/backend/src/server/web/views/base.pug3
-rw-r--r--packages/backend/test-federation/test/note.test.ts2
-rw-r--r--packages/backend/test-federation/test/utils.ts6
-rw-r--r--packages/backend/test/e2e/antennas.ts1
-rw-r--r--packages/backend/test/e2e/clips.ts9
-rw-r--r--packages/backend/test/e2e/endpoints.ts1
-rw-r--r--packages/backend/test/e2e/fetch-resource.ts3
-rw-r--r--packages/backend/test/e2e/ff-visibility.ts1
-rw-r--r--packages/backend/test/e2e/timelines.ts2478
-rw-r--r--packages/backend/test/e2e/well-known.ts3
-rw-r--r--packages/backend/test/unit/NoteCreateService.ts1
-rw-r--r--packages/backend/test/unit/misc/is-renote.ts1
-rw-r--r--packages/backend/test/unit/queue/processors/CleanRemoteNotesProcessorService.ts652
-rw-r--r--packages/backend/test/utils.ts10
-rw-r--r--packages/frontend-builder/README.txt1
-rw-r--r--packages/frontend-builder/eslint.config.js52
-rw-r--r--packages/frontend-builder/locale-inliner.ts153
-rw-r--r--packages/frontend-builder/locale-inliner/apply-with-locale.ts102
-rw-r--r--packages/frontend-builder/locale-inliner/collect-modifications.ts425
-rw-r--r--packages/frontend-builder/logger.ts73
-rw-r--r--packages/frontend-builder/package.json25
-rw-r--r--packages/frontend-builder/rollup-plugin-remove-unref-i18n.ts53
-rw-r--r--packages/frontend-builder/tsconfig.json29
-rw-r--r--packages/frontend-builder/utils.ts12
-rw-r--r--packages/frontend-embed/build.ts51
-rw-r--r--packages/frontend-embed/package.json53
-rw-r--r--packages/frontend-embed/src/boot.ts19
-rw-r--r--packages/frontend-embed/src/i18n.ts3
-rw-r--r--packages/frontend-embed/src/theme.ts2
-rw-r--r--packages/frontend-embed/src/workers/draw-blurhash.ts2
-rw-r--r--packages/frontend-embed/vite.config.ts13
-rw-r--r--packages/frontend-shared/js/config.ts9
-rw-r--r--packages/frontend-shared/js/const.ts1
-rw-r--r--packages/frontend-shared/js/emojilist.json168
-rw-r--r--packages/frontend-shared/js/i18n.ts9
-rw-r--r--packages/frontend-shared/js/locale.ts14
-rw-r--r--packages/frontend-shared/js/store-boot-errors.ts12
-rw-r--r--packages/frontend-shared/package.json14
-rw-r--r--packages/frontend/.gitignore1
-rw-r--r--packages/frontend/.storybook/charts.ts2
-rw-r--r--packages/frontend/.storybook/mocks.ts2
-rw-r--r--packages/frontend/.storybook/preview.ts2
-rw-r--r--packages/frontend/.vscode/storybook.code-snippets2
-rw-r--r--packages/frontend/build.ts51
-rw-r--r--packages/frontend/lib/vite-plugin-create-search-index.ts38
-rw-r--r--packages/frontend/package.json93
-rw-r--r--packages/frontend/src/_boot_.ts2
-rw-r--r--packages/frontend/src/accounts.ts35
-rw-r--r--packages/frontend/src/aiscript/ui.ts8
-rw-r--r--packages/frontend/src/boot/common.ts96
-rw-r--r--packages/frontend/src/boot/main-boot.ts30
-rw-r--r--packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts7
-rw-r--r--packages/frontend/src/components/MkAccountMoved.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkAchievements.vue12
-rw-r--r--packages/frontend/src/components/MkAnimBg.vue18
-rw-r--r--packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkAntennaEditor.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkAntennaEditorDialog.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkAutocomplete.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkButton.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkButton.vue3
-rw-r--r--packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts4
-rw-r--r--packages/frontend/src/components/MkChannelList.stories.impl.ts12
-rw-r--r--packages/frontend/src/components/MkChart.vue55
-rw-r--r--packages/frontend/src/components/MkChatHistories.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkClickerGame.stories.impl.ts4
-rw-r--r--packages/frontend/src/components/MkCodeEditor.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkColorInput.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkCropperDialog.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkCropperDialog.vue44
-rw-r--r--packages/frontend/src/components/MkCwButton.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkDialog.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkDonation.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkDrive.file.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkDrive.folder.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkDrive.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkDrive.vue9
-rw-r--r--packages/frontend/src/components/MkDriveFileThumbnail.vue2
-rw-r--r--packages/frontend/src/components/MkDriveFolderSelectDialog.vue6
-rw-r--r--packages/frontend/src/components/MkEmojiPicker.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkEmojiPicker.vue15
-rw-r--r--packages/frontend/src/components/MkEmojiPickerDialog.vue4
-rw-r--r--packages/frontend/src/components/MkExtensionInstaller.vue6
-rw-r--r--packages/frontend/src/components/MkFollowButton.vue3
-rw-r--r--packages/frontend/src/components/MkFormFooter.vue11
-rw-r--r--packages/frontend/src/components/MkImageEffectorDialog.Layer.vue86
-rw-r--r--packages/frontend/src/components/MkImageEffectorFxForm.vue95
-rw-r--r--packages/frontend/src/components/MkImgPreviewDialog.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkImgWithBlurhash.vue11
-rw-r--r--packages/frontend/src/components/MkLaunchPad.vue3
-rw-r--r--packages/frontend/src/components/MkLink.vue8
-rw-r--r--packages/frontend/src/components/MkMediaAudio.vue4
-rw-r--r--packages/frontend/src/components/MkMediaBanner.vue4
-rw-r--r--packages/frontend/src/components/MkMediaImage.vue6
-rw-r--r--packages/frontend/src/components/MkMediaList.vue2
-rw-r--r--packages/frontend/src/components/MkMediaVideo.vue4
-rw-r--r--packages/frontend/src/components/MkMenu.child.vue8
-rw-r--r--packages/frontend/src/components/MkMenu.vue2
-rw-r--r--packages/frontend/src/components/MkModal.vue4
-rw-r--r--packages/frontend/src/components/MkNote.vue46
-rw-r--r--packages/frontend/src/components/MkNoteDetailed.vue38
-rw-r--r--packages/frontend/src/components/MkNoteSimple.vue30
-rw-r--r--packages/frontend/src/components/MkNoteSub.vue21
-rw-r--r--packages/frontend/src/components/MkNotesTimeline.vue27
-rw-r--r--packages/frontend/src/components/MkPageWindow.vue33
-rw-r--r--packages/frontend/src/components/MkPagination.vue51
-rw-r--r--packages/frontend/src/components/MkPollEditor.vue2
-rw-r--r--packages/frontend/src/components/MkPositionSelector.vue5
-rw-r--r--packages/frontend/src/components/MkPostForm.vue16
-rw-r--r--packages/frontend/src/components/MkPostFormDialog.vue2
-rw-r--r--packages/frontend/src/components/MkPullToRefresh.vue3
-rw-r--r--packages/frontend/src/components/MkPushNotificationAllowButton.vue14
-rw-r--r--packages/frontend/src/components/MkRange.vue4
-rw-r--r--packages/frontend/src/components/MkReactionIcon.vue2
-rw-r--r--packages/frontend/src/components/MkReactionTooltip.vue4
-rw-r--r--packages/frontend/src/components/MkReactionsViewer.details.vue4
-rw-r--r--packages/frontend/src/components/MkReactionsViewer.reaction.vue32
-rw-r--r--packages/frontend/src/components/MkRoleSelectDialog.vue4
-rw-r--r--packages/frontend/src/components/MkSelect.vue35
-rw-r--r--packages/frontend/src/components/MkServerSetupWizard.vue23
-rw-r--r--packages/frontend/src/components/MkServerSetupWizardDialog.vue57
-rw-r--r--packages/frontend/src/components/MkSignin.password.vue2
-rw-r--r--packages/frontend/src/components/MkStreamingNotesTimeline.vue65
-rw-r--r--packages/frontend/src/components/MkStreamingNotificationsTimeline.vue10
-rw-r--r--packages/frontend/src/components/MkSuperMenu.vue34
-rw-r--r--packages/frontend/src/components/MkSwitch.vue3
-rw-r--r--packages/frontend/src/components/MkTabs.vue36
-rw-r--r--packages/frontend/src/components/MkTagItem.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkTooltip.vue4
-rw-r--r--packages/frontend/src/components/MkUpdated.vue9
-rw-r--r--packages/frontend/src/components/MkUrlPreviewPopup.vue8
-rw-r--r--packages/frontend/src/components/MkUsersTooltip.vue4
-rw-r--r--packages/frontend/src/components/MkVisibilityPicker.vue2
-rw-r--r--packages/frontend/src/components/MkVisitorDashboard.vue18
-rw-r--r--packages/frontend/src/components/MkWidgets.vue6
-rw-r--r--packages/frontend/src/components/form/suspense.vue2
-rw-r--r--packages/frontend/src/components/global/MkA.vue4
-rw-r--r--packages/frontend/src/components/global/MkAd.stories.impl.ts7
-rw-r--r--packages/frontend/src/components/global/MkAd.vue4
-rw-r--r--packages/frontend/src/components/global/MkAvatar.vue1
-rw-r--r--packages/frontend/src/components/global/MkError.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/global/MkPageHeader.stories.impl.ts4
-rw-r--r--packages/frontend/src/components/global/MkPageHeader.tabs.vue17
-rw-r--r--packages/frontend/src/components/global/MkPageHeader.vue3
-rw-r--r--packages/frontend/src/components/global/MkUrl.vue2
-rw-r--r--packages/frontend/src/components/global/PageWithHeader.vue25
-rw-r--r--packages/frontend/src/components/global/RouterView.vue20
-rw-r--r--packages/frontend/src/components/global/SearchText.vue (renamed from packages/frontend/src/components/global/SearchKeyword.vue)0
-rw-r--r--packages/frontend/src/components/global/StackingRouterView.vue4
-rw-r--r--packages/frontend/src/components/grid/MkCellTooltip.vue4
-rw-r--r--packages/frontend/src/components/grid/MkDataCell.vue2
-rw-r--r--packages/frontend/src/components/grid/MkGrid.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/index.ts6
-rw-r--r--packages/frontend/src/deck.ts2
-rw-r--r--packages/frontend/src/directives/appear.ts5
-rw-r--r--packages/frontend/src/directives/tooltip.ts2
-rw-r--r--packages/frontend/src/directives/user-preview.ts2
-rw-r--r--packages/frontend/src/i18n.ts3
-rw-r--r--packages/frontend/src/lib/nirax.ts140
-rw-r--r--packages/frontend/src/local-storage.ts4
-rw-r--r--packages/frontend/src/os.ts25
-rw-r--r--packages/frontend/src/pages/about-misskey.vue11
-rw-r--r--packages/frontend/src/pages/about.emojis.vue4
-rw-r--r--packages/frontend/src/pages/about.federation.vue55
-rw-r--r--packages/frontend/src/pages/about.overview.vue2
-rw-r--r--packages/frontend/src/pages/about.vue44
-rw-r--r--packages/frontend/src/pages/achievements.vue4
-rw-r--r--packages/frontend/src/pages/admin-file.vue6
-rw-r--r--packages/frontend/src/pages/admin-user.vue349
-rw-r--r--packages/frontend/src/pages/admin/ads.vue14
-rw-r--r--packages/frontend/src/pages/admin/bot-protection.vue280
-rw-r--r--packages/frontend/src/pages/admin/branding.vue241
-rw-r--r--packages/frontend/src/pages/admin/email-settings.vue115
-rw-r--r--packages/frontend/src/pages/admin/external-services.vue78
-rw-r--r--packages/frontend/src/pages/admin/index.vue11
-rw-r--r--packages/frontend/src/pages/admin/job-queue.job.vue13
-rw-r--r--packages/frontend/src/pages/admin/moderation.vue278
-rw-r--r--packages/frontend/src/pages/admin/modlog.vue4
-rw-r--r--packages/frontend/src/pages/admin/object-storage.vue161
-rw-r--r--packages/frontend/src/pages/admin/overview.ap-requests.stories.impl.ts2
-rw-r--r--packages/frontend/src/pages/admin/performance.vue244
-rw-r--r--packages/frontend/src/pages/admin/relays.vue24
-rw-r--r--packages/frontend/src/pages/admin/roles.edit.vue12
-rw-r--r--packages/frontend/src/pages/admin/roles.editor.vue20
-rw-r--r--packages/frontend/src/pages/admin/roles.role.vue6
-rw-r--r--packages/frontend/src/pages/admin/roles.vue8
-rw-r--r--packages/frontend/src/pages/admin/security.vue222
-rw-r--r--packages/frontend/src/pages/admin/server-rules.vue23
-rw-r--r--packages/frontend/src/pages/admin/settings.vue575
-rw-r--r--packages/frontend/src/pages/admin/system-webhook.vue24
-rw-r--r--packages/frontend/src/pages/announcement.vue6
-rw-r--r--packages/frontend/src/pages/antenna-timeline.vue23
-rw-r--r--packages/frontend/src/pages/api-console.vue5
-rw-r--r--packages/frontend/src/pages/auth.form.vue12
-rw-r--r--packages/frontend/src/pages/auth.vue7
-rw-r--r--packages/frontend/src/pages/channel-editor.vue43
-rw-r--r--packages/frontend/src/pages/channel.vue10
-rw-r--r--packages/frontend/src/pages/channels.vue5
-rw-r--r--packages/frontend/src/pages/chat/XMessage.vue9
-rw-r--r--packages/frontend/src/pages/chat/home.home.vue12
-rw-r--r--packages/frontend/src/pages/chat/home.invitations.vue6
-rw-r--r--packages/frontend/src/pages/custom-emojis-manager.vue1
-rw-r--r--packages/frontend/src/pages/drive.file.info.vue36
-rw-r--r--packages/frontend/src/pages/emoji-edit-dialog.vue12
-rw-r--r--packages/frontend/src/pages/explore.users.vue13
-rw-r--r--packages/frontend/src/pages/explore.vue6
-rw-r--r--packages/frontend/src/pages/flash/flash-edit.vue23
-rw-r--r--packages/frontend/src/pages/flash/flash.vue19
-rw-r--r--packages/frontend/src/pages/gallery/edit.vue14
-rw-r--r--packages/frontend/src/pages/gallery/post.vue6
-rw-r--r--packages/frontend/src/pages/install-extensions.vue6
-rw-r--r--packages/frontend/src/pages/instance-info.vue4
-rw-r--r--packages/frontend/src/pages/list.vue1
-rw-r--r--packages/frontend/src/pages/lookup.vue28
-rw-r--r--packages/frontend/src/pages/my-antennas/index.vue6
-rw-r--r--packages/frontend/src/pages/my-lists/index.vue16
-rw-r--r--packages/frontend/src/pages/my-lists/list.vue2
-rw-r--r--packages/frontend/src/pages/note.vue6
-rw-r--r--packages/frontend/src/pages/notifications.vue4
-rw-r--r--packages/frontend/src/pages/page-editor/page-editor.vue12
-rw-r--r--packages/frontend/src/pages/page.vue6
-rw-r--r--packages/frontend/src/pages/registry.value.vue4
-rw-r--r--packages/frontend/src/pages/registry.vue2
-rw-r--r--packages/frontend/src/pages/reset-password.vue1
-rw-r--r--packages/frontend/src/pages/reversi/index.vue6
-rw-r--r--packages/frontend/src/pages/scratchpad.vue10
-rw-r--r--packages/frontend/src/pages/search.note.vue20
-rw-r--r--packages/frontend/src/pages/search.user.vue20
-rw-r--r--packages/frontend/src/pages/search.vue10
-rw-r--r--packages/frontend/src/pages/settings/2fa.vue10
-rw-r--r--packages/frontend/src/pages/settings/account-data.vue2
-rw-r--r--packages/frontend/src/pages/settings/accounts.vue14
-rw-r--r--packages/frontend/src/pages/settings/apps.vue4
-rw-r--r--packages/frontend/src/pages/settings/avatar-decoration.vue26
-rw-r--r--packages/frontend/src/pages/settings/connect.vue2
-rw-r--r--packages/frontend/src/pages/settings/custom-css.vue3
-rw-r--r--packages/frontend/src/pages/settings/deck.vue6
-rw-r--r--packages/frontend/src/pages/settings/drive.WatermarkItem.vue2
-rw-r--r--packages/frontend/src/pages/settings/drive.vue6
-rw-r--r--packages/frontend/src/pages/settings/email.vue2
-rw-r--r--packages/frontend/src/pages/settings/index.vue12
-rw-r--r--packages/frontend/src/pages/settings/mute-block.vue10
-rw-r--r--packages/frontend/src/pages/settings/navbar.vue3
-rw-r--r--packages/frontend/src/pages/settings/notifications.vue19
-rw-r--r--packages/frontend/src/pages/settings/other.vue18
-rw-r--r--packages/frontend/src/pages/settings/plugin.install.vue2
-rw-r--r--packages/frontend/src/pages/settings/plugin.vue8
-rw-r--r--packages/frontend/src/pages/settings/preferences.vue41
-rw-r--r--packages/frontend/src/pages/settings/privacy.vue136
-rw-r--r--packages/frontend/src/pages/settings/profile.vue4
-rw-r--r--packages/frontend/src/pages/settings/security.vue54
-rw-r--r--packages/frontend/src/pages/settings/sounds.vue2
-rw-r--r--packages/frontend/src/pages/settings/statusbar.statusbar.vue6
-rw-r--r--packages/frontend/src/pages/settings/theme.install.vue6
-rw-r--r--packages/frontend/src/pages/settings/theme.manage.vue2
-rw-r--r--packages/frontend/src/pages/settings/theme.vue7
-rw-r--r--packages/frontend/src/pages/settings/webhook.edit.vue4
-rw-r--r--packages/frontend/src/pages/settings/webhook.new.vue3
-rw-r--r--packages/frontend/src/pages/signup-complete.vue2
-rw-r--r--packages/frontend/src/pages/tag.vue2
-rw-r--r--packages/frontend/src/pages/theme-editor.vue22
-rw-r--r--packages/frontend/src/pages/timeline.vue111
-rw-r--r--packages/frontend/src/pages/user-list-timeline.vue6
-rw-r--r--packages/frontend/src/pages/user/activity.following.vue4
-rw-r--r--packages/frontend/src/pages/user/activity.notes.vue4
-rw-r--r--packages/frontend/src/pages/user/activity.pv.vue4
-rw-r--r--packages/frontend/src/pages/user/follow-list.vue2
-rw-r--r--packages/frontend/src/pages/user/home.vue14
-rw-r--r--packages/frontend/src/pages/user/index.activity.vue2
-rw-r--r--packages/frontend/src/pages/user/raw.vue2
-rw-r--r--packages/frontend/src/pages/verify-email.vue122
-rw-r--r--packages/frontend/src/pages/welcome.entrance.classic.vue (renamed from packages/frontend/src/pages/welcome.entrance.a.vue)2
-rw-r--r--packages/frontend/src/pages/welcome.entrance.simple.vue69
-rw-r--r--packages/frontend/src/pages/welcome.setup.vue9
-rw-r--r--packages/frontend/src/pages/welcome.vue8
-rw-r--r--packages/frontend/src/plugin.ts47
-rw-r--r--packages/frontend/src/preferences/def.ts10
-rw-r--r--packages/frontend/src/preferences/manager.ts9
-rw-r--r--packages/frontend/src/router.definition.ts9
-rw-r--r--packages/frontend/src/router.ts2
-rw-r--r--packages/frontend/src/store.ts2
-rw-r--r--packages/frontend/src/style.scss15
-rw-r--r--packages/frontend/src/theme.ts41
-rw-r--r--packages/frontend/src/types/menu.ts16
-rw-r--r--packages/frontend/src/types/post-form.ts9
-rw-r--r--packages/frontend/src/ui/_common_/PreferenceRestore.vue4
-rw-r--r--packages/frontend/src/ui/_common_/ReloadSuggestion.vue71
-rw-r--r--packages/frontend/src/ui/_common_/announcements.vue2
-rw-r--r--packages/frontend/src/ui/_common_/common.ts34
-rw-r--r--packages/frontend/src/ui/_common_/common.vue22
-rw-r--r--packages/frontend/src/ui/_common_/mobile-footer-menu.vue2
-rw-r--r--packages/frontend/src/ui/_common_/navbar-h.vue12
-rw-r--r--packages/frontend/src/ui/_common_/navbar.vue2
-rw-r--r--packages/frontend/src/ui/_common_/statusbar-federation.vue6
-rw-r--r--packages/frontend/src/ui/_common_/statusbar-rss.vue6
-rw-r--r--packages/frontend/src/ui/_common_/statusbar-user-list.vue6
-rw-r--r--packages/frontend/src/ui/_common_/sw-inject.ts2
-rw-r--r--packages/frontend/src/ui/_common_/titlebar.vue2
-rw-r--r--packages/frontend/src/ui/deck.vue10
-rw-r--r--packages/frontend/src/ui/deck/antenna-column.vue13
-rw-r--r--packages/frontend/src/ui/deck/channel-column.vue12
-rw-r--r--packages/frontend/src/ui/deck/list-column.vue13
-rw-r--r--packages/frontend/src/ui/deck/role-timeline-column.vue12
-rw-r--r--packages/frontend/src/ui/universal.vue3
-rw-r--r--packages/frontend/src/unicode-emoji-indexes/en-US.json218
-rw-r--r--packages/frontend/src/unicode-emoji-indexes/ja-JP.json45
-rw-r--r--packages/frontend/src/unicode-emoji-indexes/ja-JP_hira.json53
-rw-r--r--packages/frontend/src/utility/admin-lookup.ts6
-rw-r--r--packages/frontend/src/utility/autocomplete.ts36
-rw-r--r--packages/frontend/src/utility/chart-legend.ts2
-rw-r--r--packages/frontend/src/utility/chart-vline.ts5
-rw-r--r--packages/frontend/src/utility/check-permissions.ts8
-rw-r--r--packages/frontend/src/utility/clear-cache.ts2
-rw-r--r--packages/frontend/src/utility/clicker-game.ts16
-rw-r--r--packages/frontend/src/utility/drive.ts8
-rw-r--r--packages/frontend/src/utility/extract-mentions.ts2
-rw-r--r--packages/frontend/src/utility/extract-url-from-mfm.ts2
-rw-r--r--packages/frontend/src/utility/get-note-menu.ts6
-rw-r--r--packages/frontend/src/utility/get-user-menu.ts13
-rw-r--r--packages/frontend/src/utility/haptic.ts13
-rw-r--r--packages/frontend/src/utility/image-effector/ImageEffector.ts84
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/blockNoise.ts20
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/checker.ts14
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/chromaticAberration.ts8
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/colorAdjust.ts17
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/colorClamp.ts8
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/colorClampAdvanced.ts20
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/distort.ts19
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/grayscale.ts2
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/invert.ts11
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/mirror.ts20
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/polkadot.ts27
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/stripe.ts18
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/tearing.ts17
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/threshold.ts11
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/watermarkPlacement.ts16
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/zoomLines.ts31
-rw-r--r--packages/frontend/src/utility/inapp-search.ts37
-rw-r--r--packages/frontend/src/utility/lookup.ts21
-rw-r--r--packages/frontend/src/utility/paginator.ts44
-rw-r--r--packages/frontend/src/utility/popout.ts4
-rw-r--r--packages/frontend/src/utility/popup-position.ts20
-rw-r--r--packages/frontend/src/utility/reload-ask.ts40
-rw-r--r--packages/frontend/src/utility/reload-suggest.ts12
-rw-r--r--packages/frontend/src/utility/settings-search-index.ts35
-rw-r--r--packages/frontend/src/utility/sticky-sidebar.ts2
-rw-r--r--packages/frontend/src/utility/virtual.d.ts29
-rw-r--r--packages/frontend/src/utility/watermark.ts1
-rw-r--r--packages/frontend/src/widgets/WidgetInstanceInfo.vue4
-rw-r--r--packages/frontend/src/workers/draw-blurhash.ts2
-rw-r--r--packages/frontend/test/aiscript/api.test.ts17
-rw-r--r--packages/frontend/test/home.test.ts2
-rw-r--r--packages/frontend/test/note.test.ts2
-rw-r--r--packages/frontend/tsconfig.json4
-rw-r--r--packages/frontend/vite.config.ts18
-rw-r--r--packages/icons-subsetter/package.json14
-rw-r--r--packages/misskey-bubble-game/package.json12
-rw-r--r--packages/misskey-bubble-game/src/game.ts4
-rw-r--r--packages/misskey-js/.swcrc23
-rw-r--r--packages/misskey-js/eslint.config.js2
-rw-r--r--packages/misskey-js/etc/misskey-js.api.md35
-rw-r--r--packages/misskey-js/generator/package.json14
-rw-r--r--packages/misskey-js/generator/src/generator.ts4
-rw-r--r--packages/misskey-js/jest.config.cjs207
-rw-r--r--packages/misskey-js/package.json33
-rw-r--r--packages/misskey-js/src/autogen/apiClientJSDoc.ts22
-rw-r--r--packages/misskey-js/src/autogen/endpoint.ts8
-rw-r--r--packages/misskey-js/src/autogen/entities.ts4
-rw-r--r--packages/misskey-js/src/autogen/models.ts2
-rw-r--r--packages/misskey-js/src/autogen/types.ts265
-rw-r--r--packages/misskey-js/src/consts.ts15
-rw-r--r--packages/misskey-js/src/entities.ts8
-rw-r--r--packages/misskey-js/src/note.ts4
-rw-r--r--packages/misskey-js/test-d/api.ts1
-rw-r--r--packages/misskey-js/test-d/streaming.ts1
-rw-r--r--packages/misskey-js/test/api.ts266
-rw-r--r--packages/misskey-js/test/streaming.ts3
-rw-r--r--packages/misskey-js/vitest.config.ts17
-rw-r--r--packages/misskey-reversi/package.json10
-rw-r--r--packages/sw/package.json6
-rw-r--r--pnpm-lock.yaml7180
-rw-r--r--pnpm-workspace.yaml1
-rw-r--r--scripts/changelog-checker/package-lock.json134
-rw-r--r--scripts/changelog-checker/package.json10
-rw-r--r--scripts/clean-all.js2
533 files changed, 17982 insertions, 9336 deletions
diff --git a/.github/min.node-version b/.github/min.node-version
index d4b7699d36..b8ffd70759 100644
--- a/.github/min.node-version
+++ b/.github/min.node-version
@@ -1 +1 @@
-20.18.1
+22.15.0
diff --git a/.github/workflows/api-misskey-js.yml b/.github/workflows/api-misskey-js.yml
index 6117e69c03..76a0bae8cc 100644
--- a/.github/workflows/api-misskey-js.yml
+++ b/.github/workflows/api-misskey-js.yml
@@ -16,7 +16,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v4.2.2
+ uses: actions/checkout@v4.3.0
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
diff --git a/.github/workflows/changelog-check.yml b/.github/workflows/changelog-check.yml
index 5ca27749bb..f4abedd960 100644
--- a/.github/workflows/changelog-check.yml
+++ b/.github/workflows/changelog-check.yml
@@ -12,7 +12,7 @@ jobs:
steps:
- name: Checkout head
- uses: actions/checkout@v4.2.2
+ uses: actions/checkout@v4.3.0
- name: Setup Node.js
uses: actions/setup-node@v4.4.0
with:
diff --git a/.github/workflows/check-misskey-js-autogen.yml b/.github/workflows/check-misskey-js-autogen.yml
index 22d500c306..05034ea0f4 100644
--- a/.github/workflows/check-misskey-js-autogen.yml
+++ b/.github/workflows/check-misskey-js-autogen.yml
@@ -18,7 +18,7 @@ jobs:
if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }}
steps:
- name: checkout
- uses: actions/checkout@v4.2.2
+ uses: actions/checkout@v4.3.0
with:
submodules: true
persist-credentials: false
@@ -66,7 +66,7 @@ jobs:
if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }}
steps:
- name: checkout
- uses: actions/checkout@v4.2.2
+ uses: actions/checkout@v4.3.0
with:
submodules: true
persist-credentials: false
diff --git a/.github/workflows/check-misskey-js-version.yml b/.github/workflows/check-misskey-js-version.yml
index 2b15cbee53..0e336a1551 100644
--- a/.github/workflows/check-misskey-js-version.yml
+++ b/.github/workflows/check-misskey-js-version.yml
@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v4.2.2
+ uses: actions/checkout@v4.3.0
- name: Check version
run: |
if [ "$(jq -r '.version' package.json)" != "$(jq -r '.version' packages/misskey-js/package.json)" ]; then
diff --git a/.github/workflows/check-spdx-license-id.yml b/.github/workflows/check-spdx-license-id.yml
index e40a4557df..d0b1be4991 100644
--- a/.github/workflows/check-spdx-license-id.yml
+++ b/.github/workflows/check-spdx-license-id.yml
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v4.2.2
+ uses: actions/checkout@v4.3.0
- name: Check
run: |
counter=0
@@ -50,6 +50,7 @@ jobs:
"packages/backend/test"
"packages/frontend-shared/@types"
"packages/frontend-shared/js"
+ "packages/frontend-builder"
"packages/frontend/.storybook"
"packages/frontend/@types"
"packages/frontend/lib"
diff --git a/.github/workflows/check_copyright_year.yml b/.github/workflows/check_copyright_year.yml
index eaf922d4bc..d891a538c6 100644
--- a/.github/workflows/check_copyright_year.yml
+++ b/.github/workflows/check_copyright_year.yml
@@ -10,7 +10,7 @@ jobs:
check_copyright_year:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
- run: |
if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then
echo "Please change copyright year!"
diff --git a/.github/workflows/deploy-test-environment.yml b/.github/workflows/deploy-test-environment.yml
index 46baf7421b..d838bc35e5 100644
--- a/.github/workflows/deploy-test-environment.yml
+++ b/.github/workflows/deploy-test-environment.yml
@@ -28,7 +28,7 @@ jobs:
wait_time: ${{ steps.get-wait-time.outputs.wait_time }}
steps:
- name: Checkout
- uses: actions/checkout@v4.2.2
+ uses: actions/checkout@v4.3.0
- name: Check allowed users
id: check-allowed-users
diff --git a/.github/workflows/docker-develop.yml b/.github/workflows/docker-develop.yml
index 56dedf273d..e24ef00d78 100644
--- a/.github/workflows/docker-develop.yml
+++ b/.github/workflows/docker-develop.yml
@@ -27,7 +27,7 @@ jobs:
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Check out the repo
- uses: actions/checkout@v4.2.2
+ uses: actions/checkout@v4.3.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index eb98273ba0..991fb85d85 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -32,7 +32,7 @@ jobs:
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Check out the repo
- uses: actions/checkout@v4.2.2
+ uses: actions/checkout@v4.3.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
diff --git a/.github/workflows/dockle.yml b/.github/workflows/dockle.yml
index 3054607913..1c0863e274 100644
--- a/.github/workflows/dockle.yml
+++ b/.github/workflows/dockle.yml
@@ -15,7 +15,7 @@ jobs:
DOCKER_CONTENT_TRUST: 1
DOCKLE_VERSION: 0.4.14
steps:
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
- name: Download and install dockle v${{ env.DOCKLE_VERSION }}
run: |
curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v${DOCKLE_VERSION}/dockle_${DOCKLE_VERSION}_Linux-64bit.deb"
@@ -25,7 +25,7 @@ jobs:
cp ./compose_example.yml ./compose.yml
- run: |
docker compose up -d web
- docker tag "$(docker compose images web | awk 'OFS=":" {print $4}' | tail -n +2)" misskey-web:latest
+ docker tag "$(docker compose images --format json web | jq -r '.[] | .ID')" misskey-web:latest
- run: |
cmd="dockle --exit-code 1 misskey-web:latest ${image_name}"
echo "> ${cmd}"
diff --git a/.github/workflows/get-api-diff.yml b/.github/workflows/get-api-diff.yml
index 933404dfa5..1cd1ef0ebd 100644
--- a/.github/workflows/get-api-diff.yml
+++ b/.github/workflows/get-api-diff.yml
@@ -25,7 +25,7 @@ jobs:
ref: refs/pull/${{ github.event.number }}/merge
steps:
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
with:
ref: ${{ matrix.ref }}
submodules: true
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 235faeb807..91813cebc3 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -9,6 +9,7 @@ on:
- packages/backend/**
- packages/frontend/**
- packages/frontend-shared/**
+ - packages/frontend-builder/**
- packages/frontend-embed/**
- packages/icons-subsetter/**
- packages/sw/**
@@ -22,6 +23,7 @@ on:
- packages/backend/**
- packages/frontend/**
- packages/frontend-shared/**
+ - packages/frontend-builder/**
- packages/frontend-embed/**
- packages/icons-subsetter/**
- packages/sw/**
@@ -34,7 +36,7 @@ jobs:
pnpm_install:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
with:
fetch-depth: 0
submodules: true
@@ -56,6 +58,7 @@ jobs:
- backend
- frontend
- frontend-shared
+ - frontend-builder
- frontend-embed
- icons-subsetter
- sw
@@ -66,7 +69,7 @@ jobs:
eslint-cache-version: v1
eslint-cache-path: ${{ github.workspace }}/node_modules/.cache/eslint-${{ matrix.workspace }}
steps:
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
with:
fetch-depth: 0
submodules: true
@@ -78,7 +81,7 @@ jobs:
cache: 'pnpm'
- run: pnpm i --frozen-lockfile
- name: Restore eslint cache
- uses: actions/cache@v4.2.3
+ uses: actions/cache@v4.2.4
with:
path: ${{ env.eslint-cache-path }}
key: eslint-${{ env.eslint-cache-version }}-${{ matrix.workspace }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.ref_name }}-${{ github.sha }}
@@ -96,7 +99,7 @@ jobs:
- sw
- misskey-js
steps:
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
with:
fetch-depth: 0
submodules: true
diff --git a/.github/workflows/locale.yml b/.github/workflows/locale.yml
index 68e45fdf61..63702c8bc7 100644
--- a/.github/workflows/locale.yml
+++ b/.github/workflows/locale.yml
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
with:
fetch-depth: 0
submodules: true
diff --git a/.github/workflows/on-release-created.yml b/.github/workflows/on-release-created.yml
index 7787d6055b..f36eb0037d 100644
--- a/.github/workflows/on-release-created.yml
+++ b/.github/workflows/on-release-created.yml
@@ -16,7 +16,7 @@ jobs:
id-token: write
steps:
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
with:
submodules: true
- name: Setup pnpm
diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml
index b1d95c1b33..7f964ef1d7 100644
--- a/.github/workflows/storybook.yml
+++ b/.github/workflows/storybook.yml
@@ -22,12 +22,12 @@ jobs:
NODE_OPTIONS: "--max_old_space_size=7168"
steps:
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
if: github.event_name != 'pull_request_target'
with:
fetch-depth: 0
submodules: true
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
if: github.event_name == 'pull_request_target'
with:
fetch-depth: 0
diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
index e4b139ef63..e9791ac95e 100644
--- a/.github/workflows/test-backend.yml
+++ b/.github/workflows/test-backend.yml
@@ -50,7 +50,7 @@ jobs:
- 56312:6379
steps:
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
with:
submodules: true
- name: Setup pnpm
@@ -109,6 +109,7 @@ jobs:
name: E2E tests (backend)
runs-on: ubuntu-latest
strategy:
+ fail-fast: false
matrix:
node-version-file:
- .node-version
@@ -128,7 +129,7 @@ jobs:
- 56312:6379
steps:
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
with:
submodules: true
- name: Setup pnpm
@@ -172,7 +173,7 @@ jobs:
POSTGRES_HOST_AUTH_METHOD: trust
steps:
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
with:
submodules: true
- name: Setup pnpm
diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml
index 94e43cf91e..da3c9255f2 100644
--- a/.github/workflows/test-frontend.yml
+++ b/.github/workflows/test-frontend.yml
@@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
with:
submodules: true
- name: Setup pnpm
@@ -76,7 +76,7 @@ jobs:
- 56312:6379
steps:
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
with:
submodules: true
# https://github.com/cypress-io/cypress-docker-images/issues/150
diff --git a/.github/workflows/test-misskey-js.yml b/.github/workflows/test-misskey-js.yml
index f6d16bbd76..0fad57f8da 100644
--- a/.github/workflows/test-misskey-js.yml
+++ b/.github/workflows/test-misskey-js.yml
@@ -22,7 +22,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v4.2.2
+ uses: actions/checkout@v4.3.0
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
diff --git a/.github/workflows/test-production.yml b/.github/workflows/test-production.yml
index 751c374608..08289dfd3b 100644
--- a/.github/workflows/test-production.yml
+++ b/.github/workflows/test-production.yml
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
with:
submodules: true
- name: Setup pnpm
diff --git a/.github/workflows/validate-api-json.yml b/.github/workflows/validate-api-json.yml
index edff7dbecb..25353e4d29 100644
--- a/.github/workflows/validate-api-json.yml
+++ b/.github/workflows/validate-api-json.yml
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4.2.2
+ - uses: actions/checkout@v4.3.0
with:
submodules: true
- name: Setup pnpm
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a31031830..a88ba3aca0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,85 @@
+## 2025.8.0
+
+### Note
+- サãƒãƒ¼ãƒˆã•れるNode.jsã®æœ€å°ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãŒ**22.15.0**ã«ãªã‚Šã¾ã—ãŸ
+
+### General
+- ノートを削除ã—ãŸéš›ã€é–¢é€£ã™ã‚‹ãƒŽãƒ¼ãƒˆãŒåŒæ™‚ã«å‰Šé™¤ã•れãªã„よã†ã«ãªã‚Šã¾ã—ãŸ
+ - APIã§ã€ã€ŒreplyIdãŒå­˜åœ¨ã—ã¦ã„ã‚‹ã®ã«replyãŒnullã€ã‚„「renoteIdãŒå­˜åœ¨ã—ã¦ã„ã‚‹ã®ã«renoteãŒnullã€ã§ã‚ã‚‹ã¨ã„ã†ã€ä»Šã¾ã§ã«ã¯ãªã‹ã£ãŸãƒ‘ターンãŒè¡¨ã‚Œã‚‹ã“ã¨ã«ãªã‚Šã¾ã™
+- 定期的ã«å¤ã„ãƒªãƒ¢ãƒ¼ãƒˆã®æŠ•ç¨¿ã‚’å‰Šé™¤ã™ã‚‹æ©Ÿèƒ½ãŒå®Ÿè£…ã•れã¾ã—ãŸ
+ - コントロールパãƒãƒ«â†’パフォーマンス→Remote Notes Cleaning ã§æœ‰åŠ¹åŒ–ã§ãã¾ã™
+ - データベースã®è‚¥å¤§åŒ–を防止ã™ã‚‹ã“ã¨ãŒå¯èƒ½ã§ã™
+ - 既存ã®ã‚µãƒ¼ãƒãƒ¼ã§å½“機能を有効化ã—ãŸå ´åˆã¯ã€å‡¦ç†é‡ãŒå¤šããªã‚‹ãŸã‚ã€ä¸€æ™‚çš„ã«ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ä½¿ç”¨é‡ãŒå¢—加ã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚
+ - 増加é‡ã‚’抑ãˆã‚‹ã«ã¯ã€æœ€å¤§å‡¦ç†ç¶™ç¶šæ™‚間をデフォルトより短ãã—ã¦ãã ã•ã„。
+ - データベースサイズã¸ã®åŠ¹æžœãŒè¦‹ã‚‰ã‚Œãªã„å ´åˆã¯autovacuumãŒæœ‰åйã«ãªã£ã¦ã„ã‚‹ã‹ç¢ºèªã—ã¦ãã ã•ã„
+- サーãƒãƒ¼ã®åˆæœŸè¨­å®šãŒå®Œäº†ã™ã‚‹ã¾ã§ã¯é€£åˆãŒã‚ªãƒ³ã«ãªã‚‰ãªã„よã†ã«ãªã‚Šã¾ã—ãŸ
+- 日本語ã«ãŠã‘る公開範囲åç§°ã®ã€Œãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆã€ãŒã€ŒæŒ‡åã€ã«æ”¹ç§°ã•れã¾ã—ãŸ
+ - 実際ã®å‹•作ã«å³ã—ãŸåç§°ã«ãªã‚Šã€é¦´æŸ“ã¿ã®ãªã„人ã§ã‚‚ç†è§£ã—ã‚„ã™ããªã‚Šã¾ã—ãŸ
+ - 他サービスã«ãŠã‘る「ダイレクトメッセージã€ã«ç›¸å½“ã™ã‚‹Misskeyã®æ©Ÿèƒ½ã¯ã€Œãƒãƒ£ãƒƒãƒˆã€ã§ã™ãŒ(éŽåŽ»ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®Misskeyã§ã‚‚ã€å½“該機能ã¯ã€Œãƒãƒ£ãƒƒãƒˆã€ã§ã¯ãªã「ダイレクトメッセージã€ã§ã—ãŸ)ã€ã€Œãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆæŠ•稿ã€ã¨ã„ã†åç§°ã®æ©Ÿèƒ½ãŒå­˜åœ¨ã™ã‚‹ã¨ãã¡ã‚‰ãŒãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆãƒ¡ãƒƒã‚»ãƒ¼ã‚¸æ©Ÿèƒ½ã§ã‚るよã†ãªèª¤è§£ã‚’生んã§ã„ã¾ã—ãŸ
+ - 今後ã€ã€Œãƒãƒ£ãƒƒãƒˆã€ã®å称を「ダイレクトメッセージã€ã«æˆ»ã™å¯èƒ½æ€§ãŒã‚りã¾ã™
+- mfm.jsをアップデートã—ã¾ã—ãŸ
+ - Enhance: Unicode 15.1 ãŠã‚ˆã³ 16.0 ã«åŽéŒ²ã•れã¦ã„る絵文字ã«å¯¾å¿œ
+ - Enhance: acctã« `.` ãŒå…¥ã£ã¦ã„るユーザーã®ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ã«å¯¾å¿œ
+ - Fix: Unicode絵文字ã«éš£æŽ¥ã™ã‚‹ç•°ä½“字セレクタ(`U+FE0F`)ãŒçµµæ–‡å­—ã¨ã—ã¦èªè­˜ã•れるå•題を修正
+- Enhance: ユーザー検索をロールãƒãƒªã‚·ãƒ¼ã§åˆ¶é™ã§ãるよã†ã«
+
+### Client
+- Feat: AiScriptãŒ1.1.0ã«æ›´æ–°ã•れã¾ã—ãŸ
+ - プラグインã¯1.xã«å¯¾å¿œã—ãŸã‚‚ã®ãŒå¿…è¦ã§ã™
+ - Playã¯ãã®ã¾ã¾å‹•作ã—ã¾ã™ãŒã€æ–°è¦ã«ä½œã‚‰ã‚Œã‚‹ãƒ—リセットã¯1.xã«ãªã‚Šã¾ã™
+ - 以å‰ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‹ã‚‰ç„¡åŠ¹åŒ–ã•れã¦ã„㟠note_view_interruptor ãŒæœ‰åйã«ãªã‚Šã¾ã—ãŸ
+ - ãƒãƒ³ãƒ‰ãƒ©ã¯åŒæœŸçš„ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™
+- Feat: セーフモード
+ - プラグイン・テーマ・カスタムCSSã®ä½¿ç”¨ã§ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆã®èµ·å‹•ã«å•題ãŒç™ºç”Ÿã—ãŸéš›ã«ã€ã“れらを無効ã«ã—ã¦èµ·å‹•ã§ãã¾ã™
+ - ä»¥ä¸‹ã®æ–¹æ³•ã§ã‚»ãƒ¼ãƒ•モードを起動ã§ãã¾ã™
+ - `g` キーを連打ã™ã‚‹
+ - URLã«`?safemode=true`を付ã‘ã‚‹
+ - PWAã®ã‚·ãƒ§ãƒ¼ãƒˆã‚«ãƒƒãƒˆã§ Safemode ã‚’é¸æŠžã—ã¦èµ·å‹•ã™ã‚‹
+- Feat: éžãƒ­ã‚°ã‚¤ãƒ³æ™‚ã«è¡¨ç¤ºã•れるトップページã®ã‚¹ã‚¿ã‚¤ãƒ«ã‚’é¸æŠžã§ãるよã†ã«
+ - コントロールパãƒãƒ«â†’ブランディング→エントランスページã®ã‚¹ã‚¿ã‚¤ãƒ«
+- Feat: ページã®ã‚¿ãƒ–ãƒãƒ¼ã‚’下部ã«è¡¨ç¤ºã§ãるよã†ã«
+- Feat: (実験的)iOSã§ã®è§¦è¦šãƒ•ィードãƒãƒƒã‚¯ã‚’有効ã«ã§ãるよã†ã«
+- Feat: コントロールパãƒãƒ«ã‚’検索ã§ãるよã†ã«
+- Enhance: 「自動ã§ã‚‚ã£ã¨è¦‹ã‚‹ã€ã‚ªãƒ—ã‚·ãƒ§ãƒ³ãŒæœ‰åйã«ãªã‚Šã€å®‰å®šæ€§ãŒå‘上ã—ã¾ã—ãŸ
+- Enhance: トルコ語 (tr-TR) ã«å¯¾å¿œ
+- Enhance: ä¸å¿…è¦ãªç¿»è¨³ãƒ‡ãƒ¼ã‚¿ã‚’読ã¿è¾¼ã¾ãªããªã‚Šã€ãƒ‘フォーマンスãŒå‘上ã—ã¾ã—ãŸ
+- Enhance: ç”»åƒã‚¨ãƒ•ェクトã®ãƒ‘ラメータåã®å¤šè¨€èªžå¯¾å¿œ
+- Enhance: ノートをéžè¡¨ç¤ºã«ã™ã‚‹ç›¸å¯¾æœŸé–“ã‚’1ヶ月å˜ä½ã§è‡ªç”±ã«æŒ‡å®šã§ãるよã†ã«
+- Enhance: メールアドレス確èªç”»é¢ã®UIを改善
+- Enhance: アイコンã®ã‚¹ã‚¯ãƒ­ãƒ¼ãƒ«è¿½å¾“を無効化ã™ã‚‹éš›ã®é©ç”¨ç¯„囲を強化
+- Enhance: レンダリングパフォーマンスã®å‘上
+- Enhance: ä¾å­˜ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã®æ›´æ–°
+- Fix: 投稿フォームã§ãƒ•ァイルã®ã‚¢ãƒƒãƒ—ロードãŒä¸­æ­¢ã¾ãŸã¯å¤±æ•—ã—ãŸéš›ã®ãƒãƒ³ãƒ‰ãƒªãƒ³ã‚°ã‚’修正
+- Fix: 一部ã®è¨­å®šæ¤œç´¢çµæžœãŒå­˜åœ¨ã—ãªã„パスã«ãªã‚‹å•題を修正
+ (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1171)
+- Fix: テーマエディタãŒå‹•作ã—ãªã„å•題を修正
+- Fix: ãƒãƒ£ãƒ³ãƒãƒ«ã®ãƒã‚¤ãƒ©ã‚¤ãƒˆãƒšãƒ¼ã‚¸ã«ãƒŽãƒ¼ãƒˆãŒè¡¨ç¤ºã•れãªã„å•題を修正
+- Fix: カラムã®åå‰ãŒæ­£ã—ãリスト/ãƒãƒ£ãƒ³ãƒãƒ«ã®åå‰ã«ãªã‚‰ãªã„å•題を修正
+- Fix: 複数ã®ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ã‚’1行ã«è¨˜è¿°ã—ãŸå ´åˆã«ã€ã‚µã‚¸ã‚§ã‚¹ãƒˆãŒæ­£ã—ã表示ã•れãªã„å•題を修正
+- Fix: メンションã¨ã—ã¦ã®æ¡ä»¶ã‚’満ãŸã—ã¦ã„ã¦ã‚‚ã€ç‰¹å®šã®æ¡ä»¶(`-`ãŒå«ã¾ã‚Œã‚‹å ´åˆãªã©)ã§æ­£ã—ãサジェストã•れãªã„å•題を一部修正
+- Fix: ユーザーã®å‰å¾ŒãƒŽãƒ¼ãƒˆã‚’閲覧ã™ã‚‹æ©Ÿèƒ½ãŒå‹•作ã—ãªã„å•題を修正
+- Fix: 照会ダイアログã§ap/showã§ãƒ­ãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’解決ã—ãŸéš›@username@nullã«é£›ã°ã•れるå•題を修正
+- Fix: アイコンã®ãƒ‡ã‚³ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã‚’付ã‘ã‚‹éš›ã«ãƒ‡ã‚³ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãŒè¡¨ç¤ºã•れãªããªã‚‹å•題を修正
+- Fix: ã‚¿ãƒƒãƒæ“作時ã«ãƒžã‚¦ã‚¹ãƒ›ãƒãƒ¼æ™‚ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãƒ—レビューãŒé–‹ãã“ã¨ãŒã‚ã‚‹å•題を修正
+- Fix: 管ç†ä¸­ã‚¢ã‚«ã‚¦ãƒ³ãƒˆä¸€è¦§ã§æ­£ã—ã„表示ãŒè¡Œã‚れãªã„å•題を修正
+- Fix: lookupページã§ãƒªãƒ¢ãƒ¼ãƒˆURLを指定ã—ãŸéš›ã«æ­£ã—ã動作ã—ãªã„å•題を修正
+
+### Server
+- Feat: サーãƒãƒ¼ç®¡ç†ã‚³ãƒžãƒ³ãƒ‰
+ - `pnpm cli foo` ã®å½¢å¼ã§å®Ÿè¡Œå¯èƒ½ã§ã™
+ - ç¾åœ¨ä»¥ä¸‹ã®ã‚³ãƒžãƒ³ãƒ‰ãŒåˆ©ç”¨å¯èƒ½ã§ã™
+ - `reset-captcha` - CAPTCHA設定をリセットã—ã¾ã™
+- Enhance: ノートã®å‰Šé™¤å‡¦ç†ã®åŠ¹çŽ‡åŒ–
+- Enhance: 全体的ãªãƒ‘フォーマンスã®å‘上
+- Enhance: ä¾å­˜ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã®æ›´æ–°
+- Enhance: `clips/list` APIãŒãƒšãƒ¼ã‚¸ãƒãƒ¼ã‚·ãƒ§ãƒ³ã«å¯¾å¿œã—ã¾ã—ãŸ
+- Fix: `notes/mentions` ã§å ´åˆã«ã‚ˆã£ã¦ã¯ä¸¦ã³é †ãŒæ­£ã—ãè¿”ã•れãªã„å•題を修正
+- Fix: SystemWebhook設定ã§secretを空ã«å‡ºæ¥ãªã„å•題を修正
+- Fix: 削除ã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒãƒãƒ£ãƒƒãƒˆãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã«ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã—ã¦ã„ã‚‹å ´åˆ`chat/history`ãªã©ã§ã‚¨ãƒ©ãƒ¼ã«ãªã‚‹å•題を修正
+- Fix: Pageã®ã‚¢ã‚¤ã‚­ãƒ£ãƒƒãƒç”»åƒã‚’ドライブã‹ã‚‰æ¶ˆã—ã¦ã‚‚Pageã”ã¨æ¶ˆãˆãªã„よã†ã«
+- Fix: タイムラインAPIã® withRenotes: false 時ã®ãƒ¬ã‚¹ãƒãƒ³ã‚¹ã‚’修正
+
+
## 2025.7.0
### Note
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8e10806686..95e6077d93 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -618,3 +618,23 @@ color: hsl(from var(--MI_THEME-accent) h s calc(l - 10));
color: color(from var(--MI_THEME-accent) srgb r g b / 0.5);
```
+## è€ƒãˆæ–¹
+### DRYã«å›šã‚れるãª
+å¿…è¦ãªã®ã¯ä¸€èˆ¬åŒ–ã§ã¯ãªã抽象化ã¨è€ƒãˆã¾ã™ã€‚
+盲信ã›ãšã€èª¤ã£ãŸãƒ»ä¸å¿…è¦ãªå…±é€šåŒ–ã¯é¿ã‘ã€ãれãŒè‡ªç„¶ã ã¨æ„Ÿã˜ã‚‹å ´åˆã¯é‡è¤‡ã•ã›ã‚‹å‹‡æ°—ã‚’æŒã¡ã¾ã—ょã†ã€‚
+
+### Misskeyを複雑ã«ã—ãªã„実装
+ãれãŒã„ãら複雑ã§ã‚ã£ã¦ã‚‚ã€Misskey固有ã®ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã¨é–¢å¿ƒãŒåˆ†é›¢ã•れã¦ã„ã‚‹(ã‚‚ã—ãã¯äº‹å®Ÿä¸Šåˆ†é›¢ã•れã¦ã„ã‚‹ã¨è¦‹åšã™ã“ã¨ãŒã§ãã‚‹)実装ã§ã‚れã°ã€ãれã¯Misskeyã®ã‚³ãƒ¼ãƒ‰ãƒ™ãƒ¼ã‚¹ã«å¯¾ã™ã‚‹è¤‡é›‘性ã«å½±éŸ¿ã‚’与ãˆãªã„ã¨è€ƒãˆã¾ã™ã€‚
+
+例ãˆã‚‹ãªã‚‰ã€Vueã‚„AiScriptã¨ã„ã£ãŸMisskeyãŒä½¿ç”¨ã—ã¦ã„るライブラリã®å†…部実装ãŒã„ãら複雑ã ã£ãŸã¨ã—ã¦ã‚‚ã€ã€Œãれを使用ã—ã¦ã„ã‚‹ã‹ã‚‰Misskeyã®å®Ÿè£…ã¯è¤‡é›‘ã§ã‚ã‚‹ã€ã¨ã„ã†ã“ã¨ã«ã¯ãªã‚‰ãªã„ã®ã¨åŒã˜ã§ã™ã€‚
+
+Misskeyã®ãƒ‰ãƒ¡ã‚¤ãƒ³çŸ¥è­˜ã‹ã‚‰é–¢å¿ƒãŒåˆ†é›¢ã•れã¦ã„ã‚‹ã¨ã„ã†ã“ã¨ã¯ã€Misskeyã®å®Ÿè£…ã«ã¤ã„ã¦è€ƒãˆã‚‹æ™‚ã«ãれらã®å†…部実装を考慮ã™ã‚‹å¿…è¦ãŒç„¡ãã€èªçŸ¥è² è·ã‚’増やã•ãªã„ã‹ã‚‰ã§ã™ã€‚
+
+ã¾ãŸé‡è¦ãªç‚¹ã¯ã€ãã®å®Ÿè£…ãŒã€Misskeyリãƒã‚¸ãƒˆãƒªã®å¤–部ã«ã‚ã‚‹ã‹ãƒ»å†…部ã«ã‚ã‚‹ã‹ã¨ã„ã†ã“ã¨ã‚„ã€MisskeyãŒãƒ¡ãƒ³ãƒ†ãƒŠãƒ³ã‚¹ã™ã‚‹ã‚‚ã®ã‹ãƒ»ç¬¬ä¸‰è€…ãŒãƒ¡ãƒ³ãƒ†ãƒŠãƒ³ã‚¹ã™ã‚‹ã‚‚ã®ã‹ã¨ã„ã£ãŸã“ã¨ã¯è¤‡é›‘性を考ãˆã‚‹ä¸Šã§ã¯ã»ã¨ã‚“ã©ç„¡è¦–ã§ãã‚‹ã¨ã„ã†ç‚¹ã§ã™ã€‚
+
+ã‚‚ã¡ã‚ã‚“ãã®å®Ÿè£…ãŒMisskeyリãƒã‚¸ãƒˆãƒªã«ã‚りã€MisskeyãŒãƒ¡ãƒ³ãƒ†ãƒŠãƒ³ã‚¹ã—ãªã‘れã°ãªã‚‰ãªã„ã‚‚ã®ã¯ã€ä¿å®ˆã®ã‚³ã‚¹ãƒˆã¯ã‹ã‹ã‚Šã¾ã™ã€‚
+ã—ã‹ã—ã€Misskeyã®æœ¬è³ªçš„ãªè¨­è¨ˆãƒ»å®Ÿè£…ã¨ã„ã†è¦³ç‚¹ã§è¦‹ãŸã¨ãã¯ã€ãã®å®Ÿè£…ã¯å®Ÿè³ªçš„ã«å¤–部ライブラリã®ã‚ˆã†ã«æŒ¯ã‚‹èˆžã„ã¾ã™ã€‚
+æ›è¨€ã™ã‚Œã°ã€ŒãŸã¾ãŸã¾Misskeyã®é–‹ç™ºè€…ã¨åŒã˜äººãŸã¡ãŒãƒ¡ãƒ³ãƒ†ãƒŠãƒ³ã‚¹ã—ã¦ã„ã‚‹ã—ã€ãŸã¾ãŸã¾Misskeyã®ãƒªãƒã‚¸ãƒˆãƒªå†…ã«ç½®ã„ã¦ã‚ã‚‹ã ã‘ã®å¤–部ライブラリã€ã§ã™ã€‚
+
+ãã®ãŸã‚ã€å®Ÿè£…ã‚’ãªã‚‹ã¹ãMisskeyã®ãƒ‰ãƒ¡ã‚¤ãƒ³çŸ¥è­˜ã‹ã‚‰ç‹¬ç«‹ã—ãŸã‚‚ã®ã«ã™ã‚Œã°ã€Misskeyã®ã‚³ãƒ¼ãƒ‰ãƒ™ãƒ¼ã‚¹ã®è¤‡é›‘性を上ã’ã‚‹ã“ã¨ãªã機能実装を行ã†ã“ã¨ãŒã§ãã€ãŠå¾—ã§ã‚ã‚‹ã¨è¨€ãˆã¾ã™ã€‚
+ã‚‚ã¡ã‚ã‚“ãれã«ã“ã ã‚ã£ã¦ã€äº›ç´°ãªå®Ÿè£…ã§ã‚‚ãã®ã‚ˆã†ã«åˆ†é›¢ã—ã¦ã—ã¾ã†ã¨ã‹ãˆã£ã¦èªçŸ¥è² è·ãŒå¢—ãˆãŸã‚Šã€å®Ÿè£…é‡ãŒå¢—ãˆã¦ãƒ¡ãƒªãƒƒãƒˆã‚’デメリットãŒä¸Šå›žã‚‹å ´åˆã‚‚ã‚ã‚‹ã®ã§ã€ã‚±ãƒ¼ã‚¹ãƒã‚¤ã‚±ãƒ¼ã‚¹ã§ã¯ã‚りã¾ã™ã€‚
diff --git a/Dockerfile b/Dockerfile
index 62b737c084..370bed5751 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -23,6 +23,7 @@ COPY --link ["packages/backend/package.json", "./packages/backend/"]
COPY --link ["packages/frontend-shared/package.json", "./packages/frontend-shared/"]
COPY --link ["packages/frontend/package.json", "./packages/frontend/"]
COPY --link ["packages/frontend-embed/package.json", "./packages/frontend-embed/"]
+COPY --link ["packages/frontend-builder/package.json", "./packages/frontend-builder/"]
COPY --link ["packages/icons-subsetter/package.json", "./packages/icons-subsetter/"]
COPY --link ["packages/sw/package.json", "./packages/sw/"]
COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
diff --git a/idea/MkAbuseReport.stories.impl.ts b/idea/MkAbuseReport.stories.impl.ts
index 717bceb23d..138e5cd0cd 100644
--- a/idea/MkAbuseReport.stories.impl.ts
+++ b/idea/MkAbuseReport.stories.impl.ts
@@ -4,8 +4,8 @@
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
-import { action } from '@storybook/addon-actions';
-import { StoryObj } from '@storybook/vue3';
+import { action } from 'storybook/actions';
+import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw';
import { abuseUserReport } from '../packages/frontend/.storybook/fakes.js';
import { commonHandlers } from '../packages/frontend/.storybook/mocks.js';
diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml
index 73f3870f18..24a2caea35 100644
--- a/locales/ar-SA.yml
+++ b/locales/ar-SA.yml
@@ -68,7 +68,7 @@ receiveFollowRequest: "تلقيت طلب متابعة"
followRequestAccepted: "Ù‚ÙØ¨Ù„ طلب المتابعة"
mention: "أشر الى"
mentions: "الإشارات"
-directNotes: "الملاحظات المباشرة"
+directNotes: "رسالة خاصة"
importAndExport: "إستورد / صدر"
import: "استيراد"
export: "تصدير"
@@ -1008,6 +1008,8 @@ lastNDays: "آخر {n} أيام"
surrender: "ألغÙ"
postForm: "أنشئ ملاحظة"
information: "عن"
+inMinutes: "د"
+inDays: "ÙŠ"
_chat:
invitations: "دعوة"
noHistory: "السجل ÙØ§Ø±Øº"
@@ -1597,3 +1599,9 @@ _watermarkEditor:
type: "نوع"
image: "صور"
advanced: "متقدم"
+_imageEffector:
+ _fxProps:
+ scale: "الحجم"
+ size: "الحجم"
+ color: "اللون"
+ opacity: "Ø§Ù„Ø´ÙØ§Ùية"
diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml
index 26902eb07f..2b0645b77d 100644
--- a/locales/bn-BD.yml
+++ b/locales/bn-BD.yml
@@ -848,6 +848,8 @@ sourceCode: "সোরà§à¦¸ কোড"
flip: "উলà§à¦Ÿà¦¾à¦¨"
postForm: "নোট লিখà§à¦¨"
information: "আপনার সমà§à¦ªà¦°à§à¦•ে"
+inMinutes: "মিনিট"
+inDays: "দিন"
_chat:
invitations: "আমনà§à¦¤à§à¦°à¦£"
noHistory: "কোনো ইতিহাস নেই"
@@ -1355,3 +1357,10 @@ _watermarkEditor:
text: "লেখা"
image: "ছবি"
advanced: "উনà§à¦¨à¦¤"
+_imageEffector:
+ _fxProps:
+ scale: "আকার"
+ size: "আকার"
+ color: "রং"
+ opacity: "অসà§à¦¬à¦šà§à¦›à¦¤à¦¾"
+ lightness: "উজà§à¦œà§à¦¬à¦² করà§à¦¨"
diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index 6e5882f087..4b6c8b97c3 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -896,7 +896,7 @@ searchResult: "Resultats de la cerca"
hashtags: "Etiquetes"
troubleshooting: "Solucionar problemes"
useBlurEffect: "Fes servir efectes de desenfocament a la interfície"
-learnMore: "Saber més "
+learnMore: "Saber-ne més "
misskeyUpdated: "Misskey s'ha actualitzat "
whatIsNew: "Mostra canvis"
translate: "Traduir "
@@ -1054,6 +1054,7 @@ permissionDeniedError: "Operació no permesa "
permissionDeniedErrorDescription: "Aquest compte no té suficients permisos per dur a terme aquesta acció "
preset: "Predefinit"
selectFromPresets: "Escull des dels predefinits"
+custom: "Personalitzat"
achievements: "Assoliments"
gotInvalidResponseError: "Resposta del servidor invàlida "
gotInvalidResponseErrorDescription: "No es pot contactar amb el servidor o potser es troba fora de línia per manteniment. Provar-ho de nou més tard."
@@ -1092,6 +1093,7 @@ prohibitedWordsDescription2: "Fent servir espais crearà expressions AND si l'ex
hiddenTags: "Etiquetes ocultes"
hiddenTagsDescription: "La visibilitat de totes les notes que continguin qualsevol de les paraules configurades seran, automàticament, afegides a \"Inici\". Pots llistar diferents paraules separant les per línies noves."
notesSearchNotAvailable: "La cerca de notes no es troba disponible."
+usersSearchNotAvailable: "La cerca d'usuaris no està disponible."
license: "Llicència"
unfavoriteConfirm: "Esborrar dels favorits?"
myClips: "Els meus retalls"
@@ -1243,7 +1245,7 @@ releaseToRefresh: "Deixar anar per actualitzar"
refreshing: "Recarregant..."
pullDownToRefresh: "Llisca cap a baix per recarregar"
useGroupedNotifications: "Mostrar les notificacions agrupades "
-signupPendingError: "Hi ha hagut un problema verificant l'adreça de correu electrònic. L'enllaç pot haver caducat."
+emailVerificationFailedError: "Hem tingut un problema en verificar la teva adreça de correu electrònic. És probable que l'enllaç estigui caducat."
cwNotationRequired: "Si està activat \"Amagar contingut\" s'ha d'escriure una descripció "
doReaction: "Afegeix una reacció "
code: "Codi"
@@ -1368,9 +1370,16 @@ redisplayAllTips: "Torna ha mostrat tots els trucs i consells"
hideAllTips: "Amagar tots els trucs i consells"
defaultImageCompressionLevel: "Nivell de comprensió de la imatge per defecte"
defaultImageCompressionLevel_description: "Baixa, conserva la qualitat de la imatge però la mida de l'arxiu és més gran. <br>Alta, redueix la mida de l'arxiu però també la qualitat de la imatge."
+inMinutes: "Minut(s)"
+inDays: "Di(a)(es)"
+safeModeEnabled: "Mode segur activat"
+pluginsAreDisabledBecauseSafeMode: "Els afegits no estan activats perquè el mode segur està activat."
+customCssIsDisabledBecauseSafeMode: "El CSS personalitzat no s'aplica perquè el mode segur es troba activat."
+themeIsDefaultBecauseSafeMode: "El tema predeterminat es farà servir mentre el mode segur estigui activat. Una vegada es desactivi el mode segur es restablirà el tema escollit."
+thankYouForTestingBeta: "Gràcies per ajudar-nos a provar la versió beta!"
_order:
newest: "Més recent"
- oldest: "Cronològic"
+ oldest: "Antigues primer"
_chat:
noMessagesYet: "Encara no tens missatges "
newMessage: "Missatge nou"
@@ -1459,6 +1468,7 @@ _settings:
contentsUpdateFrequency_description2: "Quan s'activa el mode en temps real, el contingut s'actualitza en temps real, independentment d'aquesta configuració."
showUrlPreview: "Mostrar vista prèvia d'URL"
showAvailableReactionsFirstInNote: "Mostra les reacciones que pots fer servir al damunt"
+ showPageTabBarBottom: "Mostrar les pestanyes de les línies de temps a la part inferior"
_chat:
showSenderName: "Mostrar el nom del remitent"
sendOnEnter: "Introdueix per enviar"
@@ -1632,6 +1642,10 @@ _serverSettings:
fanoutTimelineDbFallback: "Carregar de la base de dades"
fanoutTimelineDbFallbackDescription: "Quan s'activa, la línia de temps fa servir la base de dades per consultes adicionals si la línia de temps no es troba a la memòria cau. Si és desactiva la càrrega del servidor és veure reduïda, però també és reduirà el nombre de línies de temps que és poden obtenir."
reactionsBufferingDescription: "Quan s'activa aquesta opció millora bastant el rendiment en recuperar les línies de temps reduint la càrrega de la base. Com a contrapunt, augmentarà l'ús de memòria de Redís. Desactiva aquesta opció en cas de tenir un servidor amb poca memòria o si tens problemes d'inestabilitat."
+ remoteNotesCleaning: "Neteja automàtica de notes remotes"
+ remoteNotesCleaning_description: "Quan activis aquesta opció, periòdicament es netejaran les notes remotes que no es consultin, això evitarà que la base de dades se"
+ remoteNotesCleaningMaxProcessingDuration: "D'oració màxima del temps de funcionament del procés de neteja"
+ remoteNotesCleaningExpiryDaysForEachNotes: "Duració mínima de conservació de les notes"
inquiryUrl: "URL de consulta "
inquiryUrlDescription: "Escriu adreça URL per al formulari de consulta per al mantenidor del servidor o una pàgina web amb el contacte d'informació."
openRegistration: "Registres oberts"
@@ -1650,6 +1664,11 @@ _serverSettings:
userGeneratedContentsVisibilityForVisitor: "L'abast de la publicació del contingut generat per l'usuari"
userGeneratedContentsVisibilityForVisitor_description: "Això ajuda a evitar problemes com que continguts remots inadequats que no hagin estat moderats correctament es publiquin a internet mitjançant el teu servidor."
userGeneratedContentsVisibilityForVisitor_description2: "La publicació incondicional de tots els continguts del servidor a internet, incloent-hi els continguts remots rebuts pel servidor, comporta riscos. Això és extremadament important per els espectadors que desconeixen el caràcter descentralitzat dels continguts, ja que poden percebre erroneament els continguts remots com contingut generat per el propi servidor."
+ restartServerSetupWizardConfirm_title: "Vols tornar a executar l'assistent de configuració inicial del servidor?"
+ restartServerSetupWizardConfirm_text: "Algunes configuracions actuals seran restablertes."
+ entrancePageStyle: "Estil de la pàgina d'inici"
+ showTimelineForVisitor: "Mostrar la línia de temps"
+ showActivitiesForVisitor: "Mostrar activitat"
_userGeneratedContentsVisibilityForVisitor:
all: "Tot obert al públic "
localOnly: "Només es publiquen els continguts locals, el contingut remot es manté privat"
@@ -1986,6 +2005,7 @@ _role:
descriptionOfRateLimitFactor: "Límits baixos són menys restrictius, límits alts són més restrictius."
canHideAds: "Pot amagar la publicitat"
canSearchNotes: "Pot cercar notes"
+ canSearchUsers: "Pot cercar usuaris"
canUseTranslator: "Pot fer servir el traductor"
avatarDecorationLimit: "Nombre màxim de decoracions que es poden aplicar els avatars"
canImportAntennas: "Autoritza la importació d'antenes "
@@ -2258,6 +2278,7 @@ _time:
minute: "Minut(s)"
hour: "Hor(a)(es)"
day: "Di(a)(es)"
+ month: "Mes(os)"
_2fa:
alreadyRegistered: "J has registrat un dispositiu d'autenticació de doble factor."
registerTOTP: "Registrar una aplicació autenticadora"
@@ -3060,6 +3081,7 @@ _bootErrors:
otherOption1: "Esborrar la configuració i la memòria cau del client"
otherOption2: "Iniciar client senzill"
otherOption3: "Iniciar l'eina de reparació "
+ otherOption4: "Iniciar Misskey en mode segur"
_search:
searchScopeAll: "Tot"
searchScopeLocal: "Local"
@@ -3096,6 +3118,8 @@ _serverSetupWizard:
doYouConnectToFediverse_description1: "Quan es connecta amb una xarxa de servidors distribuïts (Fedivers), els continguts poden intercanviar-se amb altres servidors i entre ells."
doYouConnectToFediverse_description2: "La connexió amb el Fedivers també es coneix com a \"federació\"."
youCanConfigureMoreFederationSettingsLater: "Les configuracions avançades, com especificar els servidors amb els quals es pot federar, es poden fer més tard."
+ remoteContentsCleaning: "Neteja automàtica del contingut rebut"
+ remoteContentsCleaning_description: "Quan es comença a federar es rep un munt de contingut, quan s'activa la neteja automàtica el contingut antic que no es consulta serà eliminat del servidor, el que permet estalviar espai d'emmagatzematge."
adminInfo: "Informació de l'administrador "
adminInfo_description: "Estableix la informació de l'administrador que es farà servir per rebre consultes."
adminInfo_mustBeFilled: "Aquesta informació ha de ser omplerta si el servidor té els registres oberts o la federació es troba activada."
@@ -3148,10 +3172,10 @@ _watermarkEditor:
type: "Tipus"
image: "Imatges"
advanced: "Avançat"
+ angle: "Angle"
stripe: "Bandes"
stripeWidth: "Amplada de la banda"
stripeFrequency: "Freqüència de la banda"
- angle: "Angle"
polkadot: "Lunars"
checker: "Escacs"
polkadotMainDotOpacity: "Opacitat del lunar principal"
@@ -3163,6 +3187,7 @@ _imageEffector:
title: "Efecte"
addEffect: "Afegeix un efecte"
discardChangesConfirm: "Vols descartar els canvis i sortir?"
+ nothingToConfigure: "No hi ha opcions de configuració disponibles"
_fxs:
chromaticAberration: "Aberració cromàtica"
glitch: "Glitch"
@@ -3180,6 +3205,38 @@ _imageEffector:
checker: "Escacs"
blockNoise: "Bloqueig de soroll"
tearing: "Trencament d'imatge "
+ _fxProps:
+ angle: "Angle"
+ scale: "Mida"
+ size: "Mida"
+ color: "Color"
+ opacity: "Opacitat"
+ normalize: "Normalitzar"
+ amount: "Quantitat"
+ lightness: "Brillantor"
+ contrast: "Contrast"
+ hue: "Tonalitat"
+ brightness: "Brillantor"
+ saturation: "Saturació"
+ max: "Màxim"
+ min: "Mínim"
+ direction: "Direcció "
+ phase: "Fase"
+ frequency: "Freqüència "
+ strength: "Intensitat"
+ glitchChannelShift: "Canvi de canal "
+ seed: "Llindar"
+ redComponent: "Component vermell"
+ greenComponent: "Component verd"
+ blueComponent: "Component blau"
+ threshold: "Llindar"
+ centerX: "Centre de X"
+ centerY: "Centre de Y"
+ zoomLinesSmoothing: "Suavitzat"
+ zoomLinesSmoothingDescription: "Els paràmetres de suavitzat i amplada de línia en augmentar no es poden fer servir junts."
+ zoomLinesThreshold: "Amplada de línia a l'augmentar "
+ zoomLinesMaskSize: "Diàmetre del centre"
+ zoomLinesBlack: "Obscurir"
drafts: "Esborrany "
_drafts:
select: "Seleccionar esborrany"
diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml
index 5fc772a361..21be386e26 100644
--- a/locales/cs-CZ.yml
+++ b/locales/cs-CZ.yml
@@ -1107,6 +1107,8 @@ lastNDays: "Posledních {n} dnů"
surrender: "Zrušit"
postForm: "Formulář pro odeslání"
information: "Informace"
+inMinutes: "Minut"
+inDays: "Dnů"
_chat:
invitations: "Pozvat"
noHistory: "Žádná historie"
@@ -2002,7 +2004,7 @@ _deck:
list: "Seznamy"
channel: "Kanály"
mentions: "Zmínění"
- direct: "Přímý"
+ direct: "Přímé poznámky"
roleTimeline: "Časová osa role"
_dialog:
charactersExceeded: "PÅ™ekroÄili jste maximální poÄet znaků! V souÄasné dobÄ› je na hodnotÄ› {current} z {max}."
@@ -2051,3 +2053,10 @@ _watermarkEditor:
type: "Typ"
image: "Obrázky"
advanced: "PokroÄilé"
+_imageEffector:
+ _fxProps:
+ scale: "Velikost"
+ size: "Velikost"
+ color: "Barva"
+ opacity: "Průhlednost"
+ lightness: "Zesvětlit"
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index 202e512e63..2d7d41bfdd 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -1243,7 +1243,6 @@ releaseToRefresh: "Zum Aktualisieren loslassen"
refreshing: "Wird aktualisiert..."
pullDownToRefresh: "Zum Aktualisieren ziehen"
useGroupedNotifications: "Benachrichtigungen gruppieren"
-signupPendingError: "Beim Überprüfen der Mailadresse ist etwas schiefgelaufen. Der Link könnte abgelaufen sein."
cwNotationRequired: "Ist \"Inhaltswarnung verwenden\" aktiviert, muss eine Beschreibung gegeben werden."
doReaction: "Reagieren"
code: "Code"
@@ -1368,6 +1367,8 @@ redisplayAllTips: "Alle „Tipps und Tricks“ wieder anzeigen"
hideAllTips: "Alle „Tipps und Tricks“ ausblenden"
defaultImageCompressionLevel: "Standard-Bildkomprimierungsstufe"
defaultImageCompressionLevel_description: "Ein niedrigerer Wert erhält die Bildqualität, erhöht aber die Dateigröße. <br>Höhere Werte reduzieren die Dateigröße, verringern aber die Bildqualität."
+inMinutes: "Minute(n)"
+inDays: "Tag(en)"
_order:
newest: "Neueste zuerst"
oldest: "Älteste zuerst"
@@ -3145,10 +3146,10 @@ _watermarkEditor:
type: "Art"
image: "Bilder"
advanced: "Fortgeschritten"
+ angle: "Winkel"
stripe: "Streifen"
stripeWidth: "Linienbreite"
stripeFrequency: "Linienanzahl"
- angle: "Winkel"
polkadot: "Punktmuster"
polkadotMainDotOpacity: "Deckkraft des Hauptpunktes"
polkadotMainDotRadius: "Größe des Hauptpunktes"
@@ -3171,6 +3172,13 @@ _imageEffector:
distort: "Verzerrung"
stripe: "Streifen"
polkadot: "Punktmuster"
+ _fxProps:
+ angle: "Winkel"
+ scale: "Größe"
+ size: "Größe"
+ color: "Farbe"
+ opacity: "Transparenz"
+ lightness: "Erhellen"
drafts: "Entwurf"
_drafts:
select: "Entwurf auswählen"
diff --git a/locales/el-GR.yml b/locales/el-GR.yml
index ad146866be..5fc2bd7221 100644
--- a/locales/el-GR.yml
+++ b/locales/el-GR.yml
@@ -353,6 +353,7 @@ _visibility:
home: "ΚεντÏικό"
homeDescription: "Δημοσίευση στο κεντÏικό χÏονολόγιο μόνο"
followers: "ΑκολουθοÏν"
+ specified: "Απευθείας σημειώματα"
_profile:
name: "Όνομα"
username: "Όνομα μέλους"
@@ -395,6 +396,7 @@ _deck:
antenna: "Αντένες"
list: "Λίστα"
mentions: "Επισημάνσεις"
+ direct: "Απευθείας σημειώματα"
_webhookSettings:
name: "Όνομα"
_moderationLogTypes:
diff --git a/locales/en-US.yml b/locales/en-US.yml
index cc3b8f23ba..9c02e83021 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -81,7 +81,7 @@ import: "Import"
export: "Export"
files: "Files"
download: "Download"
-driveFileDeleteConfirm: "Do you want to remove the file \"{name}\"? Some content using this file will also be removed."
+driveFileDeleteConfirm: "Are you sure you want to delete \"{name}\"? All notes with this file attached will also be deleted."
unfollowConfirm: "Are you sure you want to unfollow {name}?"
exportRequested: "You've requested an export. This may take a while. It will be added to your Drive once completed."
importRequested: "You've requested an import. This may take a while."
@@ -1054,6 +1054,7 @@ permissionDeniedError: "Operation denied"
permissionDeniedErrorDescription: "This account does not have the permission to perform this action."
preset: "Preset"
selectFromPresets: "Choose from presets"
+custom: "Custom"
achievements: "Achievements"
gotInvalidResponseError: "Invalid server response"
gotInvalidResponseErrorDescription: "The server may be unreachable or undergoing maintenance. Please try again later."
@@ -1092,6 +1093,7 @@ prohibitedWordsDescription2: "Using spaces will create AND expressions and surro
hiddenTags: "Hidden hashtags"
hiddenTagsDescription: "Select tags which will not shown on trend list.\nMultiple tags could be registered by lines."
notesSearchNotAvailable: "Note search is unavailable."
+usersSearchNotAvailable: "User search is not available."
license: "License"
unfavoriteConfirm: "Really remove from favorites?"
myClips: "My clips"
@@ -1216,8 +1218,8 @@ showRepliesToOthersInTimeline: "Show replies to others in timeline"
hideRepliesToOthersInTimeline: "Hide replies to others from timeline"
showRepliesToOthersInTimelineAll: "Show replies to others from everyone you follow in timeline"
hideRepliesToOthersInTimelineAll: "Hide replies to others from everyone you follow in timeline"
-confirmShowRepliesAll: "This operation is irreversible. Would you really like to show replies to others from everyone you follow in your timeline?"
-confirmHideRepliesAll: "This operation is irreversible. Would you really like to hide replies to others from everyone you follow in your timeline?"
+confirmShowRepliesAll: "Are you sure you want to show replies from everyone you follow in your timeline? This action is irreversible."
+confirmHideRepliesAll: "Are you sure you want to hide replies from everyone you follow in your timeline? This action is irreversible."
externalServices: "External Services"
sourceCode: "Source code"
sourceCodeIsNotYetProvided: "Source code is not yet available. Contact the administrator to fix this problem."
@@ -1243,7 +1245,7 @@ releaseToRefresh: "Release to refresh"
refreshing: "Refreshing..."
pullDownToRefresh: "Pull down to refresh"
useGroupedNotifications: "Display grouped notifications"
-signupPendingError: "There was a problem verifying the email address. The link may have expired."
+emailVerificationFailedError: "A problem occurred while verifying your email address. The link may have expired."
cwNotationRequired: "If \"Hide content\" is enabled, a description must be provided."
doReaction: "Add reaction"
code: "Code"
@@ -1302,7 +1304,7 @@ passkeyVerificationSucceededButPasswordlessLoginDisabled: "Passkey verification
messageToFollower: "Message to followers"
target: "Target"
testCaptchaWarning: "This function is intended for CAPTCHA testing purposes.\n<strong>Do not use in a production environment.</strong>"
-prohibitedWordsForNameOfUser: "Prohibited words for user names"
+prohibitedWordsForNameOfUser: "Prohibited words for usernames"
prohibitedWordsForNameOfUserDescription: "If any of the strings in this list are included in the user's name, the name will be denied. Users with moderator privileges are not affected by this restriction."
yourNameContainsProhibitedWords: "Your name contains prohibited words"
yourNameContainsProhibitedWordsDescription: "If you wish to use this name, please contact your server administrator."
@@ -1368,6 +1370,13 @@ redisplayAllTips: "Show all “Tips & Tricks†again"
hideAllTips: "Hide all \"Tips & Tricks\""
defaultImageCompressionLevel: "Default image compression level"
defaultImageCompressionLevel_description: "Lower level preserves image quality but increases file size.<br>Higher level reduce file size, but reduce image quality."
+inMinutes: "Minute(s)"
+inDays: "Day(s)"
+safeModeEnabled: "Safe mode is enabled"
+pluginsAreDisabledBecauseSafeMode: "All plugins are disabled because safe mode is enabled."
+customCssIsDisabledBecauseSafeMode: "Custom CSS is not applied because safe mode is enabled."
+themeIsDefaultBecauseSafeMode: "While safe mode is active, the default theme is used. Disabling safe mode will revert these changes."
+thankYouForTestingBeta: "Thank you for helping us test the beta version!"
_order:
newest: "Newest First"
oldest: "Oldest First"
@@ -1400,7 +1409,7 @@ _chat:
muteThisRoom: "Mute room"
deleteRoom: "Delete room"
chatNotAvailableForThisAccountOrServer: "Chat is not enabled on this server or for this account."
- chatIsReadOnlyForThisAccountOrServer: "Chat is read-only on this instance or this account. You cannot write new messages or create/join chat rooms."
+ chatIsReadOnlyForThisAccountOrServer: "Chat is read-only on this server or this account. You cannot write new messages or create/join chat rooms."
chatNotAvailableInOtherAccount: "The chat function is disabled for the other user."
cannotChatWithTheUser: "Cannot start a chat with this user"
cannotChatWithTheUser_description: "Chat is either unavailable or the other party has not enabled chat."
@@ -1459,6 +1468,7 @@ _settings:
contentsUpdateFrequency_description2: "When real-time mode is on, content is updated in real time regardless of this setting."
showUrlPreview: "Show URL preview"
showAvailableReactionsFirstInNote: "Show available reactions at the top."
+ showPageTabBarBottom: "Show page tab bar at the bottom"
_chat:
showSenderName: "Show sender's name"
sendOnEnter: "Press Enter to send"
@@ -1498,7 +1508,7 @@ _abuseUserReport:
resolveTutorial: "If the report's content is legitimate, select \"Accept\" to mark it as resolved.\nIf the report's content is illegitimate, select \"Reject\" to ignore it."
_delivery:
status: "Delivery status"
- stop: "Suspended"
+ stop: "Suspend"
resume: "Delivery resume"
_type:
none: "Publishing"
@@ -1632,6 +1642,10 @@ _serverSettings:
fanoutTimelineDbFallback: "Fallback to database"
fanoutTimelineDbFallbackDescription: "When enabled, the timeline will fall back to the database for additional queries if the timeline is not cached. Disabling it further reduces the server load by eliminating the fallback process, but limits the range of timelines that can be retrieved."
reactionsBufferingDescription: "When enabled, performance during reaction creation will be greatly improved, reducing the load on the database. However, Redis memory usage will increase."
+ remoteNotesCleaning: "Automatic cleanup of remote notes"
+ remoteNotesCleaning_description: "When enabled, unused and outdated remote notes will be periodically cleaned up to prevent database bloat."
+ remoteNotesCleaningMaxProcessingDuration: "Maximum cleanup processing time"
+ remoteNotesCleaningExpiryDaysForEachNotes: "Minimum days to retain notes"
inquiryUrl: "Inquiry URL"
inquiryUrlDescription: "Specify a URL for the inquiry form to the server maintainer or a web page for the contact information."
openRegistration: "Make the account creation open"
@@ -1650,6 +1664,11 @@ _serverSettings:
userGeneratedContentsVisibilityForVisitor: "Visibility of user-generated content to guests"
userGeneratedContentsVisibilityForVisitor_description: "This is useful for preventing problems caused by inappropriate remote content that is not well moderated from being unintentionally published on the Internet via your own server."
userGeneratedContentsVisibilityForVisitor_description2: "Unconditionally publishing all content on the server to the Internet, including remote content received by the server is risky. This is especially important for guests who are unaware of the distributed nature of the content, as they may mistakenly believe that even remote content is content created by users on the server."
+ restartServerSetupWizardConfirm_title: "Restart server setup wizard?"
+ restartServerSetupWizardConfirm_text: "Some current settings will be reset."
+ entrancePageStyle: "Entrance page style"
+ showTimelineForVisitor: "Show timeline"
+ showActivitiesForVisitor: "Show activities"
_userGeneratedContentsVisibilityForVisitor:
all: "Everything is public"
localOnly: "Only local content is published, remote content is kept private"
@@ -1986,19 +2005,20 @@ _role:
descriptionOfRateLimitFactor: "Lower rate limits are less restrictive, higher ones more restrictive. "
canHideAds: "Can hide ads"
canSearchNotes: "Usage of note search"
+ canSearchUsers: "User search"
canUseTranslator: "Translator usage"
- avatarDecorationLimit: "Maximum number of avatar decorations that can be applied"
- canImportAntennas: "Allow importing antennas"
- canImportBlocking: "Allow importing blocking"
- canImportFollowing: "Allow importing following"
- canImportMuting: "Allow importing muting"
- canImportUserLists: "Allow importing lists"
- chatAvailability: "Allow Chat"
+ avatarDecorationLimit: "Maximum number of avatar decorations"
+ canImportAntennas: "Can import antennas"
+ canImportBlocking: "Can import blocking"
+ canImportFollowing: "Can import following"
+ canImportMuting: "Can import muting"
+ canImportUserLists: "Can import lists"
+ chatAvailability: "Chat"
uploadableFileTypes: "Uploadable file types"
uploadableFileTypes_caption: "Specifies the allowed MIME/file types. Multiple MIME types can be specified by separating them with a new line, and wildcards can be specified with an asterisk (*). (e.g., image/*)"
uploadableFileTypes_caption2: "Some files types might fail to be detected. To allow such files, add {x} to the specification."
noteDraftLimit: "Number of possible drafts of server notes"
- watermarkAvailable: "Availability of watermark function"
+ watermarkAvailable: "Watermark function"
_condition:
roleAssignedTo: "Assigned to manual roles"
isLocal: "Local user"
@@ -2258,6 +2278,7 @@ _time:
minute: "Minute(s)"
hour: "Hour(s)"
day: "Day(s)"
+ month: "Month(s)"
_2fa:
alreadyRegistered: "You have already registered a 2-factor authentication device."
registerTOTP: "Register authenticator app"
@@ -2330,7 +2351,7 @@ _permissions:
"read:admin:index-stats": "View database index stats"
"read:admin:table-stats": "View database table stats"
"read:admin:user-ips": "View user IP addresses"
- "read:admin:meta": "View instance metadata"
+ "read:admin:meta": "View server metadata"
"write:admin:reset-password": "Reset user password"
"write:admin:resolve-abuse-user-report": "Resolve user report"
"write:admin:send-email": "Send email"
@@ -2341,7 +2362,7 @@ _permissions:
"write:admin:unset-user-avatar": "Remove user avatar"
"write:admin:unset-user-banner": "Remove user banner"
"write:admin:unsuspend-user": "Unsuspend user"
- "write:admin:meta": "Manage instance metadata"
+ "write:admin:meta": "Manage server metadata"
"write:admin:user-note": "Manage moderation note"
"write:admin:roles": "Manage roles"
"read:admin:roles": "View roles"
@@ -2773,7 +2794,7 @@ _moderationLogTypes:
resetPassword: "Password reset"
suspendRemoteInstance: "Remote instance suspended"
unsuspendRemoteInstance: "Remote instance unsuspended"
- updateRemoteInstanceNote: "Moderation note updated for remote instance."
+ updateRemoteInstanceNote: "Updated moderation note for remote servers"
markSensitiveDriveFile: "File marked as sensitive"
unmarkSensitiveDriveFile: "File unmarked as sensitive"
resolveAbuseReport: "Report resolved"
@@ -3060,6 +3081,7 @@ _bootErrors:
otherOption1: "Delete client settings and cache"
otherOption2: "Start the simple client"
otherOption3: "Launch the repair tool"
+ otherOption4: "Launch Misskey in safe mode"
_search:
searchScopeAll: "All"
searchScopeLocal: "Local"
@@ -3096,6 +3118,8 @@ _serverSetupWizard:
doYouConnectToFediverse_description1: "When connected to a network of distributed servers (Fediverse) content can be exchanged with other servers."
doYouConnectToFediverse_description2: "Connecting with the Fediverse is also called \"federation\""
youCanConfigureMoreFederationSettingsLater: "Advanced settings such as specifying federated servers can be configured later."
+ remoteContentsCleaning: "Automatic cleanup of received contents"
+ remoteContentsCleaning_description: "Federation may result in a continuous inflow of content. Enabling automatic cleanup will remove outdated and unreferenced content from the server to save storage."
adminInfo: "Administrator information"
adminInfo_description: "Sets the administrator information used to receive inquiries."
adminInfo_mustBeFilled: "Must be entered if public server or federation is on."
@@ -3148,10 +3172,10 @@ _watermarkEditor:
type: "Type"
image: "Images"
advanced: "Advanced"
+ angle: "Angle"
stripe: "Stripes"
stripeWidth: "Line width"
stripeFrequency: "Lines count"
- angle: "Angle"
polkadot: "Polkadot"
checker: "Checker"
polkadotMainDotOpacity: "Opacity of the main dot"
@@ -3163,6 +3187,7 @@ _imageEffector:
title: "Effects"
addEffect: "Add Effects"
discardChangesConfirm: "Are you sure you want to leave? You have unsaved changes."
+ nothingToConfigure: "No configurable options available"
_fxs:
chromaticAberration: "Chromatic Aberration"
glitch: "Glitch"
@@ -3180,6 +3205,38 @@ _imageEffector:
checker: "Checker"
blockNoise: "Block Noise"
tearing: "Tearing"
+ _fxProps:
+ angle: "Angle"
+ scale: "Size"
+ size: "Size"
+ color: "Color"
+ opacity: "Opacity"
+ normalize: "Normalize"
+ amount: "Amount"
+ lightness: "Lighten"
+ contrast: "Contrast"
+ hue: "Hue"
+ brightness: "Brightness"
+ saturation: "Saturation"
+ max: "Maximum"
+ min: "Minimum"
+ direction: "Direction"
+ phase: "Phase"
+ frequency: "Frequency"
+ strength: "Strength"
+ glitchChannelShift: "Channel shift"
+ seed: "Seed value"
+ redComponent: "Red component"
+ greenComponent: "Green component"
+ blueComponent: "Blue component"
+ threshold: "Threshold"
+ centerX: "Center X"
+ centerY: "Center Y"
+ zoomLinesSmoothing: "Smoothing"
+ zoomLinesSmoothingDescription: "Smoothing and zoom line width cannot be used together."
+ zoomLinesThreshold: "Zoom line width"
+ zoomLinesMaskSize: "Center diameter"
+ zoomLinesBlack: "Make black"
drafts: "Drafts"
_drafts:
select: "Select Draft"
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index 9afebc6580..ac983aae37 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -280,8 +280,8 @@ featured: "Destacados"
usernameOrUserId: "Nombre o ID del usuario"
noSuchUser: "No se encuentra el usuario"
lookup: "Búsqueda"
-announcements: "Anuncios"
-imageUrl: "URL de la imágen"
+announcements: "Avisos"
+imageUrl: "URL de la imagen."
remove: "Borrar"
removed: "Borrado"
removeAreYouSure: "¿Desea borrar \"{x}\"?"
@@ -842,7 +842,7 @@ unlikeConfirm: "¿Quitar como favorito?"
fullView: "Vista completa"
quitFullView: "quitar vista completa"
addDescription: "Agregar descripción"
-userPagePinTip: "Puede mantener sus notas visibles aquí seleccionando Pin en el menú de notas individuales"
+userPagePinTip: "Puede mantener sus notas visibles aquí seleccionando 'Fijar al perfil' en el menú de notas individuales"
notSpecifiedMentionWarning: "Algunas menciones no están incluidas en el destino"
info: "Información"
userInfo: "Información del usuario"
@@ -877,7 +877,7 @@ popularPosts: "Más vistos"
shareWithNote: "Compartir con una nota"
ads: "Anuncios"
expiration: "Termina el"
-startingperiod: "periodo de inicio"
+startingperiod: "Comienzo"
memo: "Notas"
priority: "Prioridad"
high: "Alta"
@@ -1054,6 +1054,7 @@ permissionDeniedError: "Operación denegada"
permissionDeniedErrorDescription: "Esta cuenta no tiene permisos para hacer esa acción."
preset: "Predefinido"
selectFromPresets: "Escoger desde predefinidos"
+custom: "Personalizado"
achievements: "Logros"
gotInvalidResponseError: "Respuesta del servidor inválida"
gotInvalidResponseErrorDescription: "Puede que el servidor esté caído o en mantenimiento. Favor de intentar más tarde"
@@ -1092,6 +1093,7 @@ prohibitedWordsDescription2: "Si se usan espacios se crearán expresiones AND y
hiddenTags: "Hashtags ocultos"
hiddenTagsDescription: "Selecciona las etiquetas que no se mostrarán en tendencias. Una etiqueta por línea."
notesSearchNotAvailable: "No se puede buscar una nota"
+usersSearchNotAvailable: "La búsqueda de usuarios no está disponible."
license: "Licencia"
unfavoriteConfirm: "¿Desea quitar de favoritos?"
myClips: "Mis clips"
@@ -1143,7 +1145,7 @@ channelArchiveConfirmTitle: "¿Seguro de archivar {name}?"
channelArchiveConfirmDescription: "Un canal archivado no aparecerá en la lista de canales ni en los resultados. Las nuevas publicaciones tampoco serán añadidas."
thisChannelArchived: "El canal ha sido archivado."
displayOfNote: "Mostrar notas"
-initialAccountSetting: "Configración inicial de su cuenta\nã‹\nConfigración de inicio"
+initialAccountSetting: "Configración inicial de su cuenta"
youFollowing: "Siguiendo"
preventAiLearning: "Rechazar el uso en el Aprendizaje de Máquinas. (IA Generativa)"
preventAiLearningDescription: "Pedirle a las arañas (crawlers) no usar los textos publicados o imágenes en el aprendizaje automático (IA Predictiva / Generativa). Ésto se logra añadiendo una marca respuesta HTML con la cadena \"noai\" al cantenido. Una prevención total no podría lograrse sólo usando ésta marca, ya que puede ser simplemente ignorada."
@@ -1243,7 +1245,7 @@ releaseToRefresh: "Soltar para recargar"
refreshing: "Recargando..."
pullDownToRefresh: "Tira hacia abajo para recargar"
useGroupedNotifications: "Mostrar notificaciones agrupadas"
-signupPendingError: "Ha habido un problema al verificar tu dirección de correo electrónico. Es posible que el enlace haya caducado."
+emailVerificationFailedError: "Se ha producido un error al confirmar tu dirección de correo electrónico. Es posible que el enlace haya caducado."
cwNotationRequired: "Si se ha activado \"ocultar contenido\", es necesario proporcionar una descripción."
doReaction: "Añadir reacción"
code: "Código"
@@ -1358,8 +1360,8 @@ advice: "Consejos"
realtimeMode: "Modo en tiempo real"
turnItOn: "Activar"
turnItOff: "Desactivar"
-emojiMute: "Silenciar emojis"
-emojiUnmute: "No Silenciar emojis"
+emojiMute: "Silenciar emoji"
+emojiUnmute: "No silenciar emoji"
muteX: "Silenciar {x}"
unmuteX: "Dejar de silenciar {x}"
abort: "Abortar"
@@ -1368,6 +1370,13 @@ redisplayAllTips: "Volver a mostrar todos \"Trucos y consejos\""
hideAllTips: "Ocultar todos los \"Trucos y consejos\""
defaultImageCompressionLevel: "Nivel de compresión de la imagen por defecto"
defaultImageCompressionLevel_description: "Baja, conserva la calidad de la imagen pero la medida del archivo es más grande. <br>Alta, reduce la medida del archivo pero también la calidad de la imagen."
+inMinutes: "Minutos"
+inDays: "Días"
+safeModeEnabled: "El modo seguro está activado"
+pluginsAreDisabledBecauseSafeMode: "El modo seguro está activado, por lo que todos los plugins están desactivados."
+customCssIsDisabledBecauseSafeMode: "El modo seguro está activado, por lo que no se aplica el CSS personalizado."
+themeIsDefaultBecauseSafeMode: "Mientras el modo seguro esté activado, se utilizará el tema predeterminado. Cuando se desactive el modo seguro, se volverá al tema original."
+thankYouForTestingBeta: "¡Gracias por tu colaboración en la prueba de la versión beta!"
_order:
newest: "Los más recientes primero"
oldest: "Los más antiguos primero"
@@ -1459,6 +1468,7 @@ _settings:
contentsUpdateFrequency_description2: "Cuando el modo en tiempo real está activado, el contenido se actualiza en tiempo real independientemente de esta configuración."
showUrlPreview: "Mostrar la vista previa de la URL"
showAvailableReactionsFirstInNote: "Mostrar las reacciones disponibles en la parte superior."
+ showPageTabBarBottom: "Mostrar la barra de pestañas de la página en la parte inferior."
_chat:
showSenderName: "Mostrar el nombre del remitente"
sendOnEnter: "Intro para enviar"
@@ -1530,7 +1540,7 @@ _announcement:
tooManyActiveAnnouncementDescription: "Tener demasiados anuncios activos empeora la experiencia de usuario. Por favor, considera archivar aquellos anuncios que hayan quedado obsoletos."
readConfirmTitle: "¿Marcar como leído?"
readConfirmText: "Esto marcará el contenido de \"{title}\" como leído."
- shouldNotBeUsedToPresentPermanentInfo: "Dado que puede impactar en la experiencia de usuario de forma significativa, es recomendable usar notificaciones en el flujo de información en vez de información persistente."
+ shouldNotBeUsedToPresentPermanentInfo: "Se recomienda utilizar los avisos para publicar información que requiera inmediatez, en lugar de hacerlo constantemente, ya que esto perjudica especialmente la UX de los nuevos usuarios."
dialogAnnouncementUxWarn: "Mostrar dos o más notificaciones en formato diálogo a la vez puede impactar en la experiencia de usuario de forma significativa, úsalos con cuidado."
silence: "Silenciar notificaciones"
silenceDescription: "Si lo activas, no enviarás notificación sobre este anuncio y el usuario no tendrá que leerlo."
@@ -1632,6 +1642,10 @@ _serverSettings:
fanoutTimelineDbFallback: "Cargar desde la base de datos"
fanoutTimelineDbFallbackDescription: "Cuando esta opción está habilitada, la carga de peticiones adicionales de la línea de tiempo se hará desde la base de datos cuando éstas no se encuentren en la caché. Al deshabilitar esta opción se reduce la carga del servidor, pero limita el número de líneas de tiempo que pueden obtenerse."
reactionsBufferingDescription: "Cuando se activa, el rendimiento durante la creación de reacciones mejorará considerablemente, reduciendo la carga de la base de datos. Sin embargo, aumentará el uso de memoria de Redis."
+ remoteNotesCleaning: "Limpieza automática de notas (publicaciones) remotas"
+ remoteNotesCleaning_description: "Al habilitar esta opción, se limpiarán periódicamente las entradas remotas antiguas que no se consultan, lo que evitará que la base de datos se sature."
+ remoteNotesCleaningMaxProcessingDuration: "Tiempo máximo de funcionamiento continuo del proceso de limpieza"
+ remoteNotesCleaningExpiryDaysForEachNotes: "Días mínimos para conservar las notas"
inquiryUrl: "URL de consulta "
inquiryUrlDescription: "Especifica una URL para el formulario de consulta al responsable del servidor o una página web para la información de contacto."
openRegistration: "Registros Abiertos"
@@ -1650,6 +1664,11 @@ _serverSettings:
userGeneratedContentsVisibilityForVisitor: "Visibilidad de contenido generado por un usuario a invitados"
userGeneratedContentsVisibilityForVisitor_description: "Esto es útil para evitar problemas causados por contenidos remotos inapropiados que no estén bien moderados y que se publiquen involuntariamente en Internet a través de su propio servidor."
userGeneratedContentsVisibilityForVisitor_description2: "Publicar incondicionalmente todo el contenido del servidor en Internet, incluido el contenido remoto recibido por el servidor, es arriesgado. Esto es especialmente importante para los invitados que desconocen la naturaleza distribuida del contenido, ya que pueden creer erróneamente que incluso el contenido remoto es contenido creado por usuarios en el servidor."
+ restartServerSetupWizardConfirm_title: "¿Reiniciar el asistente de configuración del servidor?"
+ restartServerSetupWizardConfirm_text: "Algunas configuraciones actuales se restablecerán"
+ entrancePageStyle: "Estilo de la página de inicio"
+ showTimelineForVisitor: "Mostrar la línea de tiempo"
+ showActivitiesForVisitor: "Mostrar actividades"
_userGeneratedContentsVisibilityForVisitor:
all: "Todo es público."
localOnly: "Sólo se publica el contenido local, el remoto se mantiene privado"
@@ -1986,6 +2005,7 @@ _role:
descriptionOfRateLimitFactor: "Límites más bajos son menos restrictivos, más altos menos restrictivos"
canHideAds: "Puede ocultar anuncios"
canSearchNotes: "Uso de la búsqueda de notas"
+ canSearchUsers: "Uso de la búsqueda de usuarios"
canUseTranslator: "Uso de traductor"
avatarDecorationLimit: "Número máximo de decoraciones de avatar"
canImportAntennas: "Permitir la importación de antenas"
@@ -2258,6 +2278,7 @@ _time:
minute: "Minutos"
hour: "Horas"
day: "Días"
+ month: "Mes(es)"
_2fa:
alreadyRegistered: "Ya has completado la configuración."
registerTOTP: "Registrar aplicación autenticadora"
@@ -3060,6 +3081,7 @@ _bootErrors:
otherOption1: "Borra la configuración y la memoria caché del cliente"
otherOption2: "Iniciar el cliente simple"
otherOption3: "Iniciar la herramienta de reparación"
+ otherOption4: "Iniciar Misskey en modo seguro"
_search:
searchScopeAll: "Todo"
searchScopeLocal: "Local"
@@ -3096,6 +3118,8 @@ _serverSetupWizard:
doYouConnectToFediverse_description1: "Cuando se conecta a una red de servidores distribuidos (Fediverso), el contenido puede intercambiarse con otros servidores."
doYouConnectToFediverse_description2: "Conectarse con el Fediverso también se conoce como \"federación\"."
youCanConfigureMoreFederationSettingsLater: "Los ajustes avanzados, como la especificación de servidores federados, pueden configurarse más adelante."
+ remoteContentsCleaning: "Limpieza automática de los contenidos recibidos"
+ remoteContentsCleaning_description: "La federación puede dar lugar a un flujo continuo de contenido. Al habilitar la limpieza automática, se eliminará del servidor el contenido obsoleto y sin referencias para ahorrar espacio de almacenamiento."
adminInfo: "Información del administrador"
adminInfo_description: "Establece la información del administrador para recibir consultas."
adminInfo_mustBeFilled: "Esta información debe ser introducida en el caso de registros abiertos o la federación esté activada."
@@ -3121,7 +3145,7 @@ _uploader:
tip: "El archivo aún no se ha cargado, por lo que este cuadro de diálogo te permite confirmar, renombrar, comprimir y recortar el archivo antes de cargarlo. Cuando esté listo, puedes iniciar la carga pulsando el botón \"Cargar\"."
_clientPerformanceIssueTip:
title: "Si crees que el consumo de batería es demasiado alto"
- makeSureDisabledAdBlocker: "Por favor, desactive el bloqueador de publicidad."
+ makeSureDisabledAdBlocker: "Por favor, desactiva el bloqueador de publicidad."
makeSureDisabledAdBlocker_description: "Los bloqueadores de anuncios pueden afectar al rendimiento. Asegúrate de que no están activados en tu sistema o en las funciones/extensiones de tu navegador."
makeSureDisabledCustomCss: "Desactiva el CSS personalizado"
makeSureDisabledCustomCss_description: "Anular estilos puede afectar al rendimiento. Asegúrate de que el CSS personalizado o las extensiones que sobrescriben estilos no están activados."
@@ -3148,10 +3172,10 @@ _watermarkEditor:
type: "Tipo"
image: "Imágenes"
advanced: "Avanzado"
+ angle: "Ãngulo"
stripe: "Rayas"
stripeWidth: "Anchura de línea"
stripeFrequency: "Número de líneas."
- angle: "Ãngulo"
polkadot: "Lunares"
checker: "verificador"
polkadotMainDotOpacity: "Opacidad del círculo principal"
@@ -3163,6 +3187,7 @@ _imageEffector:
title: "Efecto"
addEffect: "Añadir Efecto"
discardChangesConfirm: "¿Ignorar cambios y salir?"
+ nothingToConfigure: "No hay opciones configurables disponibles."
_fxs:
chromaticAberration: "Aberración Cromática"
glitch: "Glitch"
@@ -3180,6 +3205,38 @@ _imageEffector:
checker: "Corrector"
blockNoise: "Bloquear Ruido"
tearing: "Rasgado de Imagen (Tearing)"
+ _fxProps:
+ angle: "Ãngulo"
+ scale: "Tamaño"
+ size: "Tamaño"
+ color: "Color"
+ opacity: "Opacidad"
+ normalize: "Normalización"
+ amount: "Cantidad"
+ lightness: "Brillo"
+ contrast: "Contraste"
+ hue: "Tonalidad"
+ brightness: "Brillo"
+ saturation: "Saturación"
+ max: "Valor máximo"
+ min: "Valor mínimo"
+ direction: "Dirección"
+ phase: "Fase"
+ frequency: "Frecuencia"
+ strength: "Intensidad"
+ glitchChannelShift: "cambio de canal de imagen"
+ seed: "Valor de la semilla"
+ redComponent: "Componente rojo"
+ greenComponent: "Componente Verde"
+ blueComponent: "Componente Azul"
+ threshold: "Umbral"
+ centerX: "Centrar X"
+ centerY: "Centrar Y"
+ zoomLinesSmoothing: "Suavizado"
+ zoomLinesSmoothingDescription: "El suavizado y el ancho de línea de zoom no se pueden utilizar juntos."
+ zoomLinesThreshold: "Ancho de línea del zoom"
+ zoomLinesMaskSize: "Diámetro del centro"
+ zoomLinesBlack: "Hacer oscuro"
drafts: "Borrador"
_drafts:
select: "Seleccionar borradores"
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index eba5a511de..8ea21bdb46 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -1208,7 +1208,6 @@ releaseToRefresh: "Relâcher pour rafraîchir"
refreshing: "Rafraîchissement..."
pullDownToRefresh: "Tirer vers le bas pour rafraîchir"
useGroupedNotifications: "Grouper les notifications"
-signupPendingError: "Un problème est survenu lors de la vérification de votre adresse e-mail. Le lien a peut-être expiré."
cwNotationRequired: "Si « Masquer le contenu » est activé, une description doit être fournie."
doReaction: "Réagir"
code: "Code"
@@ -1272,6 +1271,8 @@ pleaseSelectAccount: "Sélectionner un compte"
availableRoles: "Rôles disponibles"
postForm: "Formulaire de publication"
information: "Informations"
+inMinutes: "min"
+inDays: "j"
_chat:
invitations: "Inviter"
noHistory: "Pas d'historique"
@@ -2370,3 +2371,11 @@ _watermarkEditor:
image: "Images"
advanced: "Avancé"
angle: "Angle"
+_imageEffector:
+ _fxProps:
+ angle: "Angle"
+ scale: "Taille"
+ size: "Taille"
+ color: "Couleur"
+ opacity: "Transparence"
+ lightness: "Clair"
diff --git a/locales/generateDTS.js b/locales/generateDTS.js
index 49807144ec..ab0613cc82 100644
--- a/locales/generateDTS.js
+++ b/locales/generateDTS.js
@@ -73,7 +73,7 @@ export default function generateDTS() {
ts.NodeFlags.Const,
),
),
- ts.factory.createInterfaceDeclaration(
+ ts.factory.createTypeAliasDeclaration(
[ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
ts.factory.createIdentifier('ParameterizedString'),
[
@@ -84,20 +84,22 @@ export default function generateDTS() {
ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
),
],
- undefined,
- [
- ts.factory.createPropertySignature(
- undefined,
- ts.factory.createComputedPropertyName(
- ts.factory.createIdentifier('kParameters'),
- ),
- undefined,
- ts.factory.createTypeReferenceNode(
- ts.factory.createIdentifier('T'),
+ ts.factory.createIntersectionTypeNode([
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
+ ts.factory.createTypeLiteralNode([
+ ts.factory.createPropertySignature(
undefined,
+ ts.factory.createComputedPropertyName(
+ ts.factory.createIdentifier('kParameters'),
+ ),
+ undefined,
+ ts.factory.createTypeReferenceNode(
+ ts.factory.createIdentifier('T'),
+ undefined,
+ ),
),
- ),
- ],
+ ])
+ ]),
),
ts.factory.createInterfaceDeclaration(
[ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index fb8713d3b3..ac6aefa4b4 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -1212,7 +1212,6 @@ releaseToRefresh: "Lepaskan untuk memuat ulang"
refreshing: "Sedang memuat ulang..."
pullDownToRefresh: "Tarik ke bawah untuk memuat ulang"
useGroupedNotifications: "Tampilkan notifikasi secara dikelompokkan"
-signupPendingError: "Terdapat masalah ketika memverifikasi alamat surel. Tautan kemungkinan telah kedaluwarsa."
cwNotationRequired: "Jika \"Sembunyikan konten\" diaktifkan, deskripsi harus disediakan."
doReaction: "Tambahkan reaksi"
code: "Kode"
@@ -1263,6 +1262,8 @@ thereAreNChanges: "Ada {n} perubahan"
prohibitedWordsForNameOfUser: "Kata yang dilarang untuk nama pengguna"
postForm: "Buat catatan"
information: "Informasi"
+inMinutes: "menit"
+inDays: "hari"
_chat:
invitations: "Undang"
noHistory: "Tidak ada riwayat"
@@ -2625,3 +2626,11 @@ _watermarkEditor:
image: "Gambar"
advanced: "Tingkat lanjut"
angle: "Sudut"
+_imageEffector:
+ _fxProps:
+ angle: "Sudut"
+ scale: "Ukuran"
+ size: "Ukuran"
+ color: "Warna"
+ opacity: "Opasitas"
+ lightness: "Menerangkan"
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 8d757ff579..0cee5b27e5 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -2,9 +2,9 @@
// This file is generated by locales/generateDTS.js
// Do not edit this file directly.
declare const kParameters: unique symbol;
-export interface ParameterizedString<T extends string = string> {
+export type ParameterizedString<T extends string = string> = string & {
[kParameters]: T;
-}
+};
export interface ILocale {
[_: string]: string | ParameterizedString | ILocale;
}
@@ -315,11 +315,11 @@ export interface Locale extends ILocale {
*/
"mention": string;
/**
- * ã‚ãªãŸå®›ã¦
+ * メンション
*/
"mentions": string;
/**
- * ダイレクト投稿
+ * 指å
*/
"directNotes": string;
/**
@@ -2567,11 +2567,11 @@ export interface Locale extends ILocale {
*/
"serviceworkerInfo": string;
/**
- * 削除ã•ã‚ŒãŸæŠ•ç¨¿
+ * 削除ã•れãŸãƒŽãƒ¼ãƒˆ
*/
"deletedNote": string;
/**
- * éžå…¬é–‹ã®æŠ•稿
+ * éžå…¬é–‹ã®ãƒŽãƒ¼ãƒˆ
*/
"invisibleNote": string;
/**
@@ -4235,6 +4235,10 @@ export interface Locale extends ILocale {
*/
"selectFromPresets": string;
/**
+ * カスタム
+ */
+ "custom": string;
+ /**
* 実績
*/
"achievements": string;
@@ -4387,6 +4391,10 @@ export interface Locale extends ILocale {
*/
"notesSearchNotAvailable": string;
/**
+ * ユーザー検索ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。
+ */
+ "usersSearchNotAvailable": string;
+ /**
* ライセンス
*/
"license": string;
@@ -4993,7 +5001,7 @@ export interface Locale extends ILocale {
/**
* メールアドレスã®ç¢ºèªä¸­ã«å•題ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ãƒªãƒ³ã‚¯ã®æœ‰åŠ¹æœŸé™ãŒåˆ‡ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚
*/
- "signupPendingError": string;
+ "emailVerificationFailedError": string;
/**
* 「内容を隠ã™ã€ãŒã‚ªãƒ³ã®å ´åˆã¯æ³¨é‡ˆã®è¨˜è¿°ãŒå¿…è¦ã§ã™ã€‚
*/
@@ -5493,6 +5501,34 @@ export interface Locale extends ILocale {
* 低ãã™ã‚‹ã¨ç”»è³ªã‚’ä¿ã¦ã¾ã™ãŒã€ãƒ•ァイルサイズã¯å¢—加ã—ã¾ã™ã€‚<br>高ãã™ã‚‹ã¨ãƒ•ァイルサイズを減らã›ã¾ã™ãŒã€ç”»è³ªã¯ä½Žä¸‹ã—ã¾ã™ã€‚
*/
"defaultImageCompressionLevel_description": string;
+ /**
+ * 分
+ */
+ "inMinutes": string;
+ /**
+ * æ—¥
+ */
+ "inDays": string;
+ /**
+ * ã‚»ãƒ¼ãƒ•ãƒ¢ãƒ¼ãƒ‰ãŒæœ‰åйã§ã™
+ */
+ "safeModeEnabled": string;
+ /**
+ * ã‚»ãƒ¼ãƒ•ãƒ¢ãƒ¼ãƒ‰ãŒæœ‰åйãªãŸã‚ã€ãƒ—ラグインã¯ã™ã¹ã¦ç„¡åŠ¹åŒ–ã•れã¦ã„ã¾ã™ã€‚
+ */
+ "pluginsAreDisabledBecauseSafeMode": string;
+ /**
+ * ã‚»ãƒ¼ãƒ•ãƒ¢ãƒ¼ãƒ‰ãŒæœ‰åйãªãŸã‚ã€ã‚«ã‚¹ã‚¿ãƒ CSSã¯é©ç”¨ã•れã¦ã„ã¾ã›ã‚“。
+ */
+ "customCssIsDisabledBecauseSafeMode": string;
+ /**
+ * ã‚»ãƒ¼ãƒ•ãƒ¢ãƒ¼ãƒ‰ãŒæœ‰åйãªé–“ã¯ãƒ‡ãƒ•ォルトã®ãƒ†ãƒ¼ãƒžãŒä½¿ç”¨ã•れã¾ã™ã€‚セーフモードをオフã«ã™ã‚‹ã¨å…ƒã«æˆ»ã‚Šã¾ã™ã€‚
+ */
+ "themeIsDefaultBecauseSafeMode": string;
+ /**
+ * ãƒ™ãƒ¼ã‚¿ç‰ˆã®æ¤œè¨¼ã«ã”å”力ã„ãŸã ãã‚りãŒã¨ã†ã”ã–ã„ã¾ã™ï¼
+ */
+ "thankYouForTestingBeta": string;
"_order": {
/**
* æ–°ã—ã„é †
@@ -5847,6 +5883,10 @@ export interface Locale extends ILocale {
* 利用ã§ãるリアクションを先頭ã«è¡¨ç¤º
*/
"showAvailableReactionsFirstInNote": string;
+ /**
+ * ページã®ã‚¿ãƒ–ãƒãƒ¼ã‚’下部ã«è¡¨ç¤º
+ */
+ "showPageTabBarBottom": string;
"_chat": {
/**
* é€ä¿¡è€…ã®åå‰ã‚’表示
@@ -6329,7 +6369,7 @@ export interface Locale extends ILocale {
*/
"followers": string;
/**
- * 指定ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã®ã¿å…¬é–‹ã•れã€ã¾ãŸç›¸æ‰‹ã«é€šçŸ¥ãŒå…¥ã‚Šã¾ã™ã€‚ダイレクトメッセージã®ã‹ã‚りã«ãŠä½¿ã„ã„ãŸã ã‘ã¾ã™ã€‚
+ * 指定ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã®ã¿å…¬é–‹ã•れã€ã¾ãŸç›¸æ‰‹ã«é€šçŸ¥ãŒå…¥ã‚Šã¾ã™ã€‚
*/
"direct": string;
/**
@@ -6337,7 +6377,7 @@ export interface Locale extends ILocale {
*/
"doNotSendConfidencialOnDirect1": string;
/**
- * é€ä¿¡å…ˆã®ã‚µãƒ¼ãƒãƒ¼ã®ç®¡ç†è€…ã¯æŠ•ç¨¿å†…å®¹ã‚’è¦‹ã‚‹ã“ã¨ãŒå¯èƒ½ãªã®ã§ã€ä¿¡é ¼ã§ããªã„サーãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆæŠ•稿をé€ä¿¡ã™ã‚‹å ´åˆã¯ã€æ©Ÿå¯†æƒ…å ±ã®æ‰±ã„ã«æ³¨æ„ãŒå¿…è¦ã§ã™ã€‚
+ * é€ä¿¡å…ˆã®ã‚µãƒ¼ãƒãƒ¼ã®ç®¡ç†è€…ã¯æŠ•ç¨¿å†…å®¹ã‚’è¦‹ã‚‹ã“ã¨ãŒå¯èƒ½ãªã®ã§ã€ä¿¡é ¼ã§ããªã„サーãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒå«ã¾ã‚Œã‚‹é™å®šå…¬é–‹ã®ãƒŽãƒ¼ãƒˆã‚’作æˆã™ã‚‹éš›ã¯ã€æ©Ÿå¯†æƒ…å ±ã®æ‰±ã„ã«æ³¨æ„ãŒå¿…è¦ã§ã™ã€‚
*/
"doNotSendConfidencialOnDirect2": string;
/**
@@ -6487,6 +6527,22 @@ export interface Locale extends ILocale {
*/
"reactionsBufferingDescription": string;
/**
+ * リモート投稿ã®è‡ªå‹•クリーニング
+ */
+ "remoteNotesCleaning": string;
+ /**
+ * 有効ã«ã™ã‚‹ã¨ã€ä¸€å®šæœŸé–“経éŽã—ãŸãƒªãƒ¢ãƒ¼ãƒˆã®æŠ•稿を定期的ã«ã‚¯ãƒªãƒ¼ãƒ³ã‚¢ãƒƒãƒ—ã—ã¦ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã®è‚¥å¤§åŒ–を抑制ã—ã¾ã™ã€‚
+ */
+ "remoteNotesCleaning_description": string;
+ /**
+ * 最大クリーニング処ç†ç¶™ç¶šæ™‚é–“
+ */
+ "remoteNotesCleaningMaxProcessingDuration": string;
+ /**
+ * æœ€ä½ŽãƒŽãƒ¼ãƒˆä¿æŒæ—¥æ•°
+ */
+ "remoteNotesCleaningExpiryDaysForEachNotes": string;
+ /**
* å•ã„åˆã‚ã›å…ˆURL
*/
"inquiryUrl": string;
@@ -6558,6 +6614,26 @@ export interface Locale extends ILocale {
* サーãƒãƒ¼ã§å—ä¿¡ã—ãŸãƒªãƒ¢ãƒ¼ãƒˆã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã‚’å«ã‚ã€ã‚µãƒ¼ãƒãƒ¼å†…ã®å…¨ã¦ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã‚’ç„¡æ¡ä»¶ã§ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒƒãƒˆã«å…¬é–‹ã™ã‚‹ã“ã¨ã¯ãƒªã‚¹ã‚¯ãŒä¼´ã„ã¾ã™ã€‚特ã«ã€åˆ†æ•£åž‹ã®ç‰¹æ€§ã‚’知らãªã„閲覧者ã«ã¨ã£ã¦ã¯ã€ãƒªãƒ¢ãƒ¼ãƒˆã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã§ã‚ã£ã¦ã‚‚サーãƒãƒ¼å†…ã§ä½œæˆã•れãŸã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã§ã‚ã‚‹ã¨èª¤ã£ã¦èªè­˜ã—ã¦ã—ã¾ã†å¯èƒ½æ€§ãŒã‚ã‚‹ãŸã‚ã€æ³¨æ„ãŒå¿…è¦ã§ã™ã€‚
*/
"userGeneratedContentsVisibilityForVisitor_description2": string;
+ /**
+ * サーãƒãƒ¼ã®åˆæœŸè¨­å®šã‚¦ã‚£ã‚¶ãƒ¼ãƒ‰ã‚’やり直ã—ã¾ã™ã‹ï¼Ÿ
+ */
+ "restartServerSetupWizardConfirm_title": string;
+ /**
+ * ç¾åœ¨ã®ä¸€éƒ¨ã®è¨­å®šã¯ãƒªã‚»ãƒƒãƒˆã•れã¾ã™ã€‚
+ */
+ "restartServerSetupWizardConfirm_text": string;
+ /**
+ * エントランスページã®ã‚¹ã‚¿ã‚¤ãƒ«
+ */
+ "entrancePageStyle": string;
+ /**
+ * タイムラインを表示ã™ã‚‹
+ */
+ "showTimelineForVisitor": string;
+ /**
+ * アクティビティを表示ã™ã‚‹
+ */
+ "showActivitiesForVisitor": string;
"_userGeneratedContentsVisibilityForVisitor": {
/**
* å…¨ã¦å…¬é–‹
@@ -7748,6 +7824,10 @@ export interface Locale extends ILocale {
*/
"canSearchNotes": string;
/**
+ * ユーザー検索ã®åˆ©ç”¨
+ */
+ "canSearchUsers": string;
+ /**
* 翻訳機能ã®åˆ©ç”¨
*/
"canUseTranslator": string;
@@ -8776,6 +8856,10 @@ export interface Locale extends ILocale {
* æ—¥
*/
"day": string;
+ /**
+ * ヶ月
+ */
+ "month": string;
};
"_2fa": {
/**
@@ -9593,7 +9677,7 @@ export interface Locale extends ILocale {
*/
"followersDescription": string;
/**
- * ダイレクト
+ * 指å
*/
"specified": string;
/**
@@ -10482,11 +10566,11 @@ export interface Locale extends ILocale {
*/
"channel": string;
/**
- * ã‚ãªãŸå®›ã¦
+ * メンション
*/
"mentions": string;
/**
- * ダイレクト
+ * 指å
*/
"direct": string;
/**
@@ -11807,6 +11891,10 @@ export interface Locale extends ILocale {
* 修復ツールを起動
*/
"otherOption3": string;
+ /**
+ * Misskeyをセーフモードã§èµ·å‹•
+ */
+ "otherOption4": string;
};
"_search": {
/**
@@ -11944,6 +12032,14 @@ export interface Locale extends ILocale {
*/
"youCanConfigureMoreFederationSettingsLater": string;
/**
+ * リモートコンテンツã®è‡ªå‹•クリーニング
+ */
+ "remoteContentsCleaning": string;
+ /**
+ * 連åˆã‚’行ã†ã¨ã€ç¶™ç¶šã—ã¦å¤šãã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã‚’å—ä¿¡ã—ã¾ã™ã€‚自動クリーニングを有効ã«ã™ã‚‹ã¨ã€ä¸€å®šæœŸé–“経éŽã—ãŸãƒªãƒ¢ãƒ¼ãƒˆã‚³ãƒ³ãƒ†ãƒ³ãƒ„を自動ã§ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰å‰Šé™¤ã—ã€ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã‚’節約ã§ãã¾ã™ã€‚
+ */
+ "remoteContentsCleaning_description": string;
+ /**
* 管ç†è€…情報
*/
"adminInfo": string;
@@ -12140,6 +12236,10 @@ export interface Locale extends ILocale {
*/
"advanced": string;
/**
+ * 角度
+ */
+ "angle": string;
+ /**
* ストライプ
*/
"stripe": string;
@@ -12152,10 +12252,6 @@ export interface Locale extends ILocale {
*/
"stripeFrequency": string;
/**
- * 角度
- */
- "angle": string;
- /**
* ãƒãƒ«ã‚«ãƒ‰ãƒƒãƒˆ
*/
"polkadot": string;
@@ -12197,6 +12293,10 @@ export interface Locale extends ILocale {
* 変更を破棄ã—ã¦çµ‚了ã—ã¾ã™ã‹ï¼Ÿ
*/
"discardChangesConfirm": string;
+ /**
+ * 設定項目ã¯ã‚りã¾ã›ã‚“
+ */
+ "nothingToConfigure": string;
"_fxs": {
/**
* 色åŽå·®
@@ -12263,6 +12363,132 @@ export interface Locale extends ILocale {
*/
"tearing": string;
};
+ "_fxProps": {
+ /**
+ * 角度
+ */
+ "angle": string;
+ /**
+ * サイズ
+ */
+ "scale": string;
+ /**
+ * サイズ
+ */
+ "size": string;
+ /**
+ * 色
+ */
+ "color": string;
+ /**
+ * ä¸é€æ˜Žåº¦
+ */
+ "opacity": string;
+ /**
+ * æ­£è¦åŒ–
+ */
+ "normalize": string;
+ /**
+ * é‡
+ */
+ "amount": string;
+ /**
+ * 明るã•
+ */
+ "lightness": string;
+ /**
+ * コントラスト
+ */
+ "contrast": string;
+ /**
+ * 色相
+ */
+ "hue": string;
+ /**
+ * è¼åº¦
+ */
+ "brightness": string;
+ /**
+ * 彩度
+ */
+ "saturation": string;
+ /**
+ * 最大値
+ */
+ "max": string;
+ /**
+ * 最å°å€¤
+ */
+ "min": string;
+ /**
+ * æ–¹å‘
+ */
+ "direction": string;
+ /**
+ * ä½ç›¸
+ */
+ "phase": string;
+ /**
+ * 頻度
+ */
+ "frequency": string;
+ /**
+ * å¼·ã•
+ */
+ "strength": string;
+ /**
+ * ズレ
+ */
+ "glitchChannelShift": string;
+ /**
+ * シード値
+ */
+ "seed": string;
+ /**
+ * 赤色æˆåˆ†
+ */
+ "redComponent": string;
+ /**
+ * 緑色æˆåˆ†
+ */
+ "greenComponent": string;
+ /**
+ * é’色æˆåˆ†
+ */
+ "blueComponent": string;
+ /**
+ * ã—ãã„値
+ */
+ "threshold": string;
+ /**
+ * 中心X
+ */
+ "centerX": string;
+ /**
+ * 中心Y
+ */
+ "centerY": string;
+ /**
+ * スムージング
+ */
+ "zoomLinesSmoothing": string;
+ /**
+ * スムージングã¨é›†ä¸­ç·šã®å¹…ã®è¨­å®šã¯ä½µç”¨ã§ãã¾ã›ã‚“。
+ */
+ "zoomLinesSmoothingDescription": string;
+ /**
+ * 集中線ã®å¹…
+ */
+ "zoomLinesThreshold": string;
+ /**
+ * 中心径
+ */
+ "zoomLinesMaskSize": string;
+ /**
+ * 黒色ã«ã™ã‚‹
+ */
+ "zoomLinesBlack": string;
+ };
};
/**
* 下書ã
diff --git a/locales/index.js b/locales/index.js
index 091d216dee..6d9cf4796b 100644
--- a/locales/index.js
+++ b/locales/index.js
@@ -36,6 +36,7 @@ const languages = [
'ru-RU',
'sk-SK',
'th-TH',
+ 'tr-TR',
'ug-CN',
'uk-UA',
'vi-VN',
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index 23d46d6e22..8ea11f81c9 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -139,7 +139,7 @@ overwriteFromPinnedEmojis: "Sovrascrivi con le impostazioni globali"
reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere."
rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note"
attachCancel: "Rimuovi allegato"
-deleteFile: "File da Drive eliminato"
+deleteFile: "Elimina un file dal Drive"
markAsSensitive: "Segna come esplicito"
unmarkAsSensitive: "Non segnare come esplicito "
enterFileName: "Nome del file"
@@ -1054,6 +1054,7 @@ permissionDeniedError: "Errore, attività non autorizzata"
permissionDeniedErrorDescription: "Non si dispone dell'autorizzazione per eseguire questa operazione."
preset: "Preimpostato"
selectFromPresets: "Seleziona preimpostato"
+custom: "Personalizzato"
achievements: "Conquiste"
gotInvalidResponseError: "Risposta del server non valida"
gotInvalidResponseErrorDescription: "Il server potrebbe essere irraggiungibile o in manutenzione. Riprova più tardi."
@@ -1092,6 +1093,7 @@ prohibitedWordsDescription2: "Gli spazi creano la relazione \"E\" tra parole (qu
hiddenTags: "Hashtag nascosti"
hiddenTagsDescription: "Impedire la visualizzazione del tag impostato nei trend. Puoi impostare più valori, uno per riga."
notesSearchNotAvailable: "Non è possibile cercare tra le Note."
+usersSearchNotAvailable: "La ricerca profili non è disponibile."
license: "Licenza"
unfavoriteConfirm: "Vuoi davvero rimuovere la preferenza?"
myClips: "Le mie Clip"
@@ -1198,7 +1200,7 @@ replies: "Risposte"
renotes: "Rinota"
loadReplies: "Leggi le risposte"
loadConversation: "Leggi la conversazione"
-pinnedList: "Elenco in primo piano"
+pinnedList: "Lista in primo piano"
keepScreenOn: "Mantenere lo schermo acceso"
verifiedLink: "Abbiamo confermato la validità di questo collegamento"
notifyNotes: "Notifica nuove Note"
@@ -1243,7 +1245,7 @@ releaseToRefresh: "Rilascia per aggiornare"
refreshing: "Aggiornamento..."
pullDownToRefresh: "Trascinare per aggiornare"
useGroupedNotifications: "Mostra le notifiche raggruppate"
-signupPendingError: "Si è verificato un problema durante la verifica del tuo indirizzo email. Potrebbe essere scaduto il collegamento temporaneo."
+emailVerificationFailedError: "La verifica dell'indirizzo e-mail non è andata a buon fine. Il link potrebbe essere scaduto."
cwNotationRequired: "Devi indicare perché il contenuto è indicato come esplicito."
doReaction: "Reagisci"
code: "Codice"
@@ -1313,6 +1315,7 @@ availableRoles: "Ruoli disponibili"
acknowledgeNotesAndEnable: "Attivare dopo averne compreso il comportamento."
federationSpecified: "Questo server è federato solo con istanze specifiche del Fediverso. Puoi interagire solo con quelle scelte dall'amministrazione."
federationDisabled: "Questo server ha la federazione disabilitata. Non puoi interagire con profili provenienti da altri server."
+draft: "Bozza"
confirmOnReact: "Confermare le reazioni"
reactAreYouSure: "Vuoi davvero reagire con {emoji} ?"
markAsSensitiveConfirm: "Vuoi davvero indicare questo contenuto multimediale come esplicito?"
@@ -1367,6 +1370,16 @@ redisplayAllTips: "Mostra tutti i suggerimenti"
hideAllTips: "Nascondi tutti i suggerimenti"
defaultImageCompressionLevel: "Livello predefinito di compressione immagini"
defaultImageCompressionLevel_description: "La compressione diminuisce la qualità dell'immagine, poca compressione mantiene alta qualità delle immagini. Aumentandola, si riducono le dimensioni del file, a discapito della qualità dell'immagine."
+inMinutes: "min"
+inDays: "giorni"
+safeModeEnabled: "La modalità sicura è attiva"
+pluginsAreDisabledBecauseSafeMode: "Tutti i plugin sono disattivati, poiché la modalità sicura è attiva."
+customCssIsDisabledBecauseSafeMode: "Il CSS personalizzato non è stato applicato, poiché la modalità sicura è attiva."
+themeIsDefaultBecauseSafeMode: "Quando la modalità sicura è attiva, viene utilizzato il tema predefinito. Quando la modalità sicura viene disattivata, il tema torna a essere quello precedente."
+thankYouForTestingBeta: "Grazie per la tua collaborazione nella verifica delle versioni beta!"
+_order:
+ newest: "Prima i più recenti"
+ oldest: "Meno recenti prima"
_chat:
noMessagesYet: "Ancora nessun messaggio"
newMessage: "Nuovo messaggio"
@@ -1455,6 +1468,7 @@ _settings:
contentsUpdateFrequency_description2: "Quando la modalità è in tempo reale, arriveranno a prescindere."
showUrlPreview: "Mostra anteprima dell'URL"
showAvailableReactionsFirstInNote: "Mostra le reazioni disponibili in alto"
+ showPageTabBarBottom: "Visualizza le schede della pagina nella parte inferiore"
_chat:
showSenderName: "Mostra il nome del mittente"
sendOnEnter: "Invio spedisce"
@@ -1628,6 +1642,10 @@ _serverSettings:
fanoutTimelineDbFallback: "Elaborazione dati alternativa"
fanoutTimelineDbFallbackDescription: "Attivando l'elaborazione alternativa, verrà interrogato ulteriormente il database se la timeline non è nella cache. \nDisattivando, si può ridurre ulteriormente il carico del server, evitando l'elaborazione alternativa, ma limitando l'intervallo recuperabile delle timeline."
reactionsBufferingDescription: "Attivando questa opzione, puoi migliorare significativamente le prestazioni durante la creazione delle reazioni e ridurre il carico sul database. Tuttavia, aumenterà l'impiego di memoria Redis."
+ remoteNotesCleaning: "Pulizia automatica dei contenuti remoti"
+ remoteNotesCleaning_description: "Se abilitata, verranno periodicamente rimosse le vecchie Note remote senza relazioni, per ridurre il sovraccarico del sistema."
+ remoteNotesCleaningMaxProcessingDuration: "Durata massima del processo di pulizia"
+ remoteNotesCleaningExpiryDaysForEachNotes: "Periodo minimo di conservazione delle note"
inquiryUrl: "URL di contatto"
inquiryUrlDescription: "Specificare l'URL al modulo di contatto, oppure le informazioni con i dati di contatto dell'amministrazione."
openRegistration: "Registrazioni aperte"
@@ -1646,6 +1664,11 @@ _serverSettings:
userGeneratedContentsVisibilityForVisitor: "Visibilità dei contenuti generati dagli utenti ai non utenti"
userGeneratedContentsVisibilityForVisitor_description: "Questa funzionalità è utile per impedire che contenuti remoti inappropriati e difficili da moderare vengano inavvertitamente resi pubblici su Internet tramite il proprio server."
userGeneratedContentsVisibilityForVisitor_description2: "Esistono dei rischi nell'esporre incondizionatamente su internet tutto il contenuto del tuo server, incluso il contenuto remoto ricevuto da altri server. In particolare, occorre prestare attenzione, perché le persone non consapevoli della federazione potrebbero erroneamente credere che il contenuto remoto sia stato invece creato all'interno del proprio server."
+ restartServerSetupWizardConfirm_title: "Vuoi ripetere la procedura guidata di configurazione iniziale del server?"
+ restartServerSetupWizardConfirm_text: "Verranno ripristinate alcune tue impostazioni personalizzate."
+ entrancePageStyle: "Stile della pagina di ingresso"
+ showTimelineForVisitor: "Mostra la Timeline a visitatori non autenticati"
+ showActivitiesForVisitor: "Mostrare la propria attività"
_userGeneratedContentsVisibilityForVisitor:
all: "Tutto pubblico"
localOnly: "Pubblica solo contenuti locali, mantieni privati ​​i contenuti remoti"
@@ -1982,6 +2005,7 @@ _role:
descriptionOfRateLimitFactor: "I rapporti più bassi sono meno restrittivi, quelli più alti lo sono di più."
canHideAds: "Nascondere i banner"
canSearchNotes: "Ricercare nelle Note"
+ canSearchUsers: "Può cercare profili"
canUseTranslator: "Tradurre le Note"
avatarDecorationLimit: "Numero massimo di decorazioni foto profilo installabili"
canImportAntennas: "Può importare Antenne"
@@ -1993,6 +2017,8 @@ _role:
uploadableFileTypes: "Tipi di file caricabili"
uploadableFileTypes_caption: "Specifica il tipo MIME. Puoi specificare più valori separandoli andando a capo, oppure indicare caratteri jolly con un asterisco (*). Ad esempio: image/*"
uploadableFileTypes_caption2: "A seconda del file, il tipo potrebbe non essere determinato. Se si desidera consentire tali file, aggiungere {x} alla specifica."
+ noteDraftLimit: "Numero massimo di Note in bozza, lato server"
+ watermarkAvailable: "Disponibilità della funzione filigrana"
_condition:
roleAssignedTo: "Assegnato a ruoli manualmente"
isLocal: "Profilo locale"
@@ -2152,6 +2178,7 @@ _theme:
install: "Installa un tema"
manage: "Gestione dei temi"
code: "Codice tema"
+ copyThemeCode: "Copia il codice del Tema"
description: "Descrizione"
installed: "{name} è installato"
installedThemes: "Temi installati"
@@ -2195,7 +2222,7 @@ _theme:
hashtag: "Hashtag"
mention: "Menzioni"
mentionMe: "Menzioni (di me)"
- renote: "Renota"
+ renote: "Rinota"
modalBg: "Sfondo modale."
divider: "Interruzione di linea"
scrollbarHandle: "Maniglie della barra di scorrimento"
@@ -2251,6 +2278,7 @@ _time:
minute: "min"
hour: "ore"
day: "giorni"
+ month: "Mese"
_2fa:
alreadyRegistered: "La configurazione è stata già completata."
registerTOTP: "Registra una App di autenticazione a due fattori (2FA/MFA)"
@@ -2635,7 +2663,7 @@ _notification:
createToken: "È stato creato un token di accesso"
createTokenDescription: "In caso contrario, eliminare il token di accesso tramite ({text})."
_types:
- all: "Tutto"
+ all: "Tutte"
note: "Nuove Note"
follow: "Follower"
mention: "Menzioni"
@@ -2643,7 +2671,7 @@ _notification:
renote: "Rinota"
quote: "Cita"
reaction: "Reazioni"
- pollEnded: "Sondaggio chiuso."
+ pollEnded: "Sondaggio terminato"
receiveFollowRequest: "Richieste di follow in arrivo"
followRequestAccepted: "Richieste di follow accettate"
roleAssigned: "Ruolo concesso"
@@ -2651,7 +2679,7 @@ _notification:
achievementEarned: "Risultato raggiunto"
exportCompleted: "Esportazione completata"
login: "Accessi"
- createToken: "Creare un token di accesso"
+ createToken: "Aggiunto un token di accesso"
test: "Notifiche di test"
app: "Notifiche da applicazioni"
_actions:
@@ -2743,56 +2771,56 @@ _abuseReport:
notifiedWebhook: "Webhook da usare"
deleteConfirm: "Vuoi davvero rimuovere il destinatario della notifica?"
_moderationLogTypes:
- createRole: "Ruolo creato"
- deleteRole: "Ruolo eliminato"
- updateRole: "Ruolo aggiornato"
- assignRole: "Ruolo assegnato"
- unassignRole: "Ruolo disassegnato"
- suspend: "Sospensione"
- unsuspend: "Sospensione rimossa"
- addCustomEmoji: "Emoji personalizzata aggiunta"
- updateCustomEmoji: "Emoji personalizzata aggiornata"
- deleteCustomEmoji: "Emoji personalizzata eliminata"
- updateServerSettings: "Impostazioni del server aggiornate"
- updateUserNote: "Promemoria di moderazione aggiornato"
- deleteDriveFile: "File da Drive eliminato"
- deleteNote: "Nota eliminata"
- createGlobalAnnouncement: "Annuncio globale creato"
- createUserAnnouncement: "Annuncio ai profili iscritti creato"
- updateGlobalAnnouncement: "Annuncio globale aggiornato"
- updateUserAnnouncement: "Annuncio ai profili iscritti aggiornato"
- deleteGlobalAnnouncement: "Annuncio globale eliminato"
- deleteUserAnnouncement: "Annuncio ai profili iscritti eliminato"
- resetPassword: "Password azzerata"
- suspendRemoteInstance: "Istanza remota sospesa"
- unsuspendRemoteInstance: "Istanza remota riattivata"
- updateRemoteInstanceNote: "Aggiornamento del promemoria di moderazione per il server remoto"
- markSensitiveDriveFile: "File nel Drive segnato come esplicito"
- unmarkSensitiveDriveFile: "File nel Drive segnato come non esplicito"
- resolveAbuseReport: "Segnalazione risolta"
- forwardAbuseReport: "Segnalazione inoltrata"
- updateAbuseReportNote: "Ha aggiornato la segnalazione"
- createInvitation: "Genera codice di invito"
- createAd: "Banner creato"
- deleteAd: "Banner eliminato"
- updateAd: "Banner aggiornato"
- createAvatarDecoration: "Creazione decorazione della foto profilo"
- updateAvatarDecoration: "Aggiornamento decorazione foto profilo"
- deleteAvatarDecoration: "Eliminazione decorazione della foto profilo"
- unsetUserAvatar: "Rimossa foto profilo"
- unsetUserBanner: "Rimossa intestazione profilo"
- createSystemWebhook: "Crea un SystemWebhook"
- updateSystemWebhook: "Modifica SystemWebhook"
- deleteSystemWebhook: "Elimina SystemWebhook"
+ createRole: "Crea un Ruolo"
+ deleteRole: "Elimina un Ruolo"
+ updateRole: "Modifica un ruolo"
+ assignRole: "Assegna un Ruolo"
+ unassignRole: "Toglie un Ruolo al Profilo"
+ suspend: "Sospende"
+ unsuspend: "Solleva la sospensione"
+ addCustomEmoji: "Aggiunge Emoji personalizzata"
+ updateCustomEmoji: "Modifica Emoji personalizzata"
+ deleteCustomEmoji: "Elimina Emoji personalizzata"
+ updateServerSettings: "Modifica le impostazioni del server"
+ updateUserNote: "Modifica un promemoria di moderazione"
+ deleteDriveFile: "Elimina un file dal Drive"
+ deleteNote: "Elimina una Nota"
+ createGlobalAnnouncement: "Crea un annuncio globale"
+ createUserAnnouncement: "Crea un annuncio ai profili già iscritti"
+ updateGlobalAnnouncement: "Modifica un annuncio globale"
+ updateUserAnnouncement: "Modifica un annuncio ai profili già iscritti"
+ deleteGlobalAnnouncement: "Elimina un annuncio globale"
+ deleteUserAnnouncement: "Elimina un annuncio ai profili già iscritti"
+ resetPassword: "Azzera la password"
+ suspendRemoteInstance: "Sospende una istanza remota"
+ unsuspendRemoteInstance: "Riattiva una istanza remota"
+ updateRemoteInstanceNote: "Modifica il promemoria di moderazione per il server remoto"
+ markSensitiveDriveFile: "Aggiunge NSFW a un file nel Drive"
+ unmarkSensitiveDriveFile: "Toglie NSFW da un file nel Drive"
+ resolveAbuseReport: "Risolve una segnalazione"
+ forwardAbuseReport: "Inoltra una segnalazione"
+ updateAbuseReportNote: "Modifica una segnalazione"
+ createInvitation: "Genera un codice di invito"
+ createAd: "Aggiunge un Banner"
+ deleteAd: "Elimina un Banner"
+ updateAd: "Modifica un Banner"
+ createAvatarDecoration: "Crea una decorazione della foto profilo"
+ updateAvatarDecoration: "Modifica una decorazione della foto profilo"
+ deleteAvatarDecoration: "Elimina una decorazione della foto profilo"
+ unsetUserAvatar: "Toglie una foto profilo"
+ unsetUserBanner: "Toglie una immagine di intestazione profilo"
+ createSystemWebhook: "Aggiunge un System Webhook"
+ updateSystemWebhook: "Modifica un System Webhook"
+ deleteSystemWebhook: "Elimina un System Webhook"
createAbuseReportNotificationRecipient: "Crea destinatario per le notifiche di segnalazioni"
- updateAbuseReportNotificationRecipient: "Aggiorna destinatario notifiche di segnalazioni"
- deleteAbuseReportNotificationRecipient: "Elimina destinatario notifiche di segnalazioni"
- deleteAccount: "Quando viene eliminato un profilo"
- deletePage: "Pagina eliminata"
- deleteFlash: "Play eliminato"
- deleteGalleryPost: "Eliminazione pubblicazione nella Galleria"
- deleteChatRoom: "Elimina chat"
- updateProxyAccountDescription: "Aggiornata la descrizione del profilo proxy"
+ updateAbuseReportNotificationRecipient: "Modifica un destinatario per le notifiche di segnalazioni"
+ deleteAbuseReportNotificationRecipient: "Elimina un destinatario per le notifiche di segnalazioni"
+ deleteAccount: "Elimina un profilo"
+ deletePage: "Elimina una Pagina"
+ deleteFlash: "Elimina un Play"
+ deleteGalleryPost: "Elimina pubblicazione nella Galleria"
+ deleteChatRoom: "Elimina una Chat"
+ updateProxyAccountDescription: "Aggiorna la descrizione del profilo proxy"
_fileViewer:
title: "Dettagli del file"
type: "Tipo di file"
@@ -2800,6 +2828,7 @@ _fileViewer:
url: "URL"
uploadedAt: "Caricato il"
attachedNotes: "Note a cui è allegato"
+ usage: "In uso"
thisPageCanBeSeenFromTheAuthor: "Questa pagina può essere vista solo da chi ha caricato il file."
_externalResourceInstaller:
title: "Installa da sito esterno"
@@ -3052,6 +3081,7 @@ _bootErrors:
otherOption1: "Nelle impostazioni, cancellare le impostazioni del client e svuotare la cache"
otherOption2: "Avviare il client predefinito"
otherOption3: "Avviare lo strumento di riparazione"
+ otherOption4: "Avvia Misskey in modalità sicura"
_search:
searchScopeAll: "Tutte"
searchScopeLocal: "Locale"
@@ -3088,6 +3118,8 @@ _serverSetupWizard:
doYouConnectToFediverse_description1: "Collegandosi a una rete di server distribuiti, denominata Fediverso, potrai scambiare contenuti con altri server, tramite il protocollo di comunicazione ActivityPub."
doYouConnectToFediverse_description2: "Connettersi al Fediverso è anche detto \"federazione\"."
youCanConfigureMoreFederationSettingsLater: "Puoi svolgere la configurazione avanzata anche dopo. Ad esempio specificando quali server possono federarsi."
+ remoteContentsCleaning: "Pulizia automatica dei contenuti in arrivo"
+ remoteContentsCleaning_description: "Con la federazione funzionante, riceverai sempre più contenuti. Abilitando la pulizia automatica, i contenuti non referenziati e obsoleti verranno rimossi automaticamente dai tuoi server, risparmiando spazio di archiviazione."
adminInfo: "Informazioni sull'amministratore"
adminInfo_description: "Imposta le informazioni dell'amministratore utilizzate per accettare le richieste."
adminInfo_mustBeFilled: "Questa operazione è necessaria su un server aperto o se è attiva la federazione."
@@ -3103,6 +3135,7 @@ _serverSetupWizard:
text2: "Se puoi, ti preghiamo di prendere in considerazione l'idea di fare una donazione, così potremo continuare a sviluppare."
text3: "Sono previsti anche dei vantaggi speciali per i sostenitori!"
_uploader:
+ editImage: "Modifica immagine"
compressedToX: "Compresso in {x}"
savedXPercent: "{x}% risparmiati"
abortConfirm: "Alcuni file non sono stati caricati. Vuoi annullare l'operazione?"
@@ -3139,10 +3172,10 @@ _watermarkEditor:
type: "Tipo"
image: "Immagini"
advanced: "Avanzato"
+ angle: "Angolo"
stripe: "Strisce"
stripeWidth: "Larghezza della linea"
stripeFrequency: "Il numero di linee"
- angle: "Angolo"
polkadot: "A pallini"
checker: "revisore"
polkadotMainDotOpacity: "Opacità del punto principale"
@@ -3154,6 +3187,7 @@ _imageEffector:
title: "Effetto"
addEffect: "Aggiungi effetto"
discardChangesConfirm: "Scarta le modifiche ed esci?"
+ nothingToConfigure: "Nessuna impostazione configurabile."
_fxs:
chromaticAberration: "Aberrazione cromatica"
glitch: "Glitch"
@@ -3169,5 +3203,52 @@ _imageEffector:
stripe: "Strisce"
polkadot: "A pallini"
checker: "revisore"
+ blockNoise: "Attenua rumore"
+ tearing: "Strappa immagine"
+ _fxProps:
+ angle: "Angolo"
+ scale: "Dimensioni"
+ size: "Dimensioni"
+ color: "Colore"
+ opacity: "Opacità"
+ normalize: "Normalizza"
+ amount: "Quantità"
+ lightness: "Chiaro"
+ contrast: "Contrasto"
+ hue: "Tinta"
+ brightness: "Luminosità"
+ saturation: "Saturazione"
+ max: "Valore massimo"
+ min: "Valore minimo"
+ direction: "Orientamento"
+ phase: "Fasare"
+ frequency: "Frequenza"
+ strength: "Forza"
+ glitchChannelShift: "Glitch cambio canale"
+ seed: "Seme"
+ redComponent: "Rosso composito"
+ greenComponent: "Verde composito"
+ blueComponent: "Blu composito"
+ threshold: "Soglia"
+ centerX: "Centro orizzontale"
+ centerY: "Centro verticale"
+ zoomLinesSmoothing: "Levigatura"
+ zoomLinesSmoothingDescription: "Non si possono usare insieme la levigatura e la larghezza della linea centrale."
+ zoomLinesThreshold: "Limite delle linee zoom"
+ zoomLinesMaskSize: "Ampiezza del diametro"
+ zoomLinesBlack: "Bande nere"
+drafts: "Bozze"
_drafts:
+ select: "Selezionare bozza"
+ cannotCreateDraftAnymore: "Hai superato il numero massimo di bozze ammissibili."
+ cannotCreateDraft: "Impossibile creare una bozza di questo contenuto."
+ delete: "Elimina bozza"
+ deleteAreYouSure: "Vuoi davvero eliminare la bozza?"
+ noDrafts: "Non c'è nessuna bozza."
+ replyTo: "Rispondere a {user}"
+ quoteOf: "Citare la nota di {user}"
+ postTo: "Inserire in {channel}"
+ saveToDraft: "Salva come bozza"
+ restoreFromDraft: "Recuperare dalle bozze"
restore: "Ripristina"
+ listDrafts: "Elenco bozze"
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 161edfe8bb..3cb8248948 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -74,8 +74,8 @@ youGotNewFollower: "フォローã•れã¾ã—ãŸ"
receiveFollowRequest: "フォローリクエストã•れã¾ã—ãŸ"
followRequestAccepted: "ãƒ•ã‚©ãƒ­ãƒ¼ãŒæ‰¿èªã•れã¾ã—ãŸ"
mention: "メンション"
-mentions: "ã‚ãªãŸå®›ã¦"
-directNotes: "ダイレクト投稿"
+mentions: "メンション"
+directNotes: "指å"
importAndExport: "インãƒãƒ¼ãƒˆã¨ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ"
import: "インãƒãƒ¼ãƒˆ"
export: "エクスãƒãƒ¼ãƒˆ"
@@ -637,8 +637,8 @@ addRelay: "リレーã®è¿½åŠ "
inboxUrl: "inboxã®URL"
addedRelays: "追加済ã¿ã®ãƒªãƒ¬ãƒ¼"
serviceworkerInfo: "プッシュ通知を行ã†ã«ã¯æœ‰åйã«ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚"
-deletedNote: "削除ã•ã‚ŒãŸæŠ•ç¨¿"
-invisibleNote: "éžå…¬é–‹ã®æŠ•稿"
+deletedNote: "削除ã•れãŸãƒŽãƒ¼ãƒˆ"
+invisibleNote: "éžå…¬é–‹ã®ãƒŽãƒ¼ãƒˆ"
enableInfiniteScroll: "自動ã§ã‚‚ã£ã¨è¦‹ã‚‹"
visibility: "公開範囲"
poll: "アンケート"
@@ -1054,6 +1054,7 @@ permissionDeniedError: "æ“ä½œãŒæ‹’å¦ã•れã¾ã—ãŸ"
permissionDeniedErrorDescription: "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ã¯ã“ã®æ“作を行ã†ãŸã‚ã®æ¨©é™ãŒã‚りã¾ã›ã‚“。"
preset: "プリセット"
selectFromPresets: "プリセットã‹ã‚‰é¸æŠž"
+custom: "カスタム"
achievements: "実績"
gotInvalidResponseError: "サーãƒãƒ¼ã®å¿œç­”ãŒç„¡åйã§ã™"
gotInvalidResponseErrorDescription: "サーãƒãƒ¼ãŒãƒ€ã‚¦ãƒ³ã¾ãŸã¯ãƒ¡ãƒ³ãƒ†ãƒŠãƒ³ã‚¹ã—ã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚ã—ã°ã‚‰ãã—ã¦ã‹ã‚‰å†åº¦ãŠè©¦ã—ãã ã•ã„。"
@@ -1092,6 +1093,7 @@ prohibitedWordsDescription2: "スペースã§åŒºåˆ‡ã‚‹ã¨AND指定ã«ãªã‚Šã€ã‚
hiddenTags: "éžè¡¨ç¤ºãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°"
hiddenTagsDescription: "設定ã—ãŸã‚¿ã‚°ã‚’トレンドã«è¡¨ç¤ºã•ã›ãªã„よã†ã«ã—ã¾ã™ã€‚改行ã§åŒºåˆ‡ã£ã¦è¤‡æ•°è¨­å®šã§ãã¾ã™ã€‚"
notesSearchNotAvailable: "ノート検索ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。"
+usersSearchNotAvailable: "ユーザー検索ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。"
license: "ライセンス"
unfavoriteConfirm: "ãŠæ°—ã«å…¥ã‚Šè§£é™¤ã—ã¾ã™ã‹ï¼Ÿ"
myClips: "自分ã®ã‚¯ãƒªãƒƒãƒ—"
@@ -1243,7 +1245,7 @@ releaseToRefresh: "離ã—ã¦ãƒªãƒ­ãƒ¼ãƒ‰"
refreshing: "リロード中"
pullDownToRefresh: "引ã£å¼µã£ã¦ãƒªãƒ­ãƒ¼ãƒ‰"
useGroupedNotifications: "通知をグルーピング"
-signupPendingError: "メールアドレスã®ç¢ºèªä¸­ã«å•題ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ãƒªãƒ³ã‚¯ã®æœ‰åŠ¹æœŸé™ãŒåˆ‡ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚"
+emailVerificationFailedError: "メールアドレスã®ç¢ºèªä¸­ã«å•題ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ãƒªãƒ³ã‚¯ã®æœ‰åŠ¹æœŸé™ãŒåˆ‡ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚"
cwNotationRequired: "「内容を隠ã™ã€ãŒã‚ªãƒ³ã®å ´åˆã¯æ³¨é‡ˆã®è¨˜è¿°ãŒå¿…è¦ã§ã™ã€‚"
doReaction: "リアクションã™ã‚‹"
code: "コード"
@@ -1368,6 +1370,13 @@ redisplayAllTips: "å…¨ã¦ã®ã€Œãƒ’ントã¨ã‚³ãƒ„ã€ã‚’å†è¡¨ç¤º"
hideAllTips: "å…¨ã¦ã®ã€Œãƒ’ントã¨ã‚³ãƒ„ã€ã‚’éžè¡¨ç¤º"
defaultImageCompressionLevel: "デフォルトã®ç”»åƒåœ§ç¸®åº¦"
defaultImageCompressionLevel_description: "低ãã™ã‚‹ã¨ç”»è³ªã‚’ä¿ã¦ã¾ã™ãŒã€ãƒ•ァイルサイズã¯å¢—加ã—ã¾ã™ã€‚<br>高ãã™ã‚‹ã¨ãƒ•ァイルサイズを減らã›ã¾ã™ãŒã€ç”»è³ªã¯ä½Žä¸‹ã—ã¾ã™ã€‚"
+inMinutes: "分"
+inDays: "æ—¥"
+safeModeEnabled: "ã‚»ãƒ¼ãƒ•ãƒ¢ãƒ¼ãƒ‰ãŒæœ‰åйã§ã™"
+pluginsAreDisabledBecauseSafeMode: "ã‚»ãƒ¼ãƒ•ãƒ¢ãƒ¼ãƒ‰ãŒæœ‰åйãªãŸã‚ã€ãƒ—ラグインã¯ã™ã¹ã¦ç„¡åŠ¹åŒ–ã•れã¦ã„ã¾ã™ã€‚"
+customCssIsDisabledBecauseSafeMode: "ã‚»ãƒ¼ãƒ•ãƒ¢ãƒ¼ãƒ‰ãŒæœ‰åйãªãŸã‚ã€ã‚«ã‚¹ã‚¿ãƒ CSSã¯é©ç”¨ã•れã¦ã„ã¾ã›ã‚“。"
+themeIsDefaultBecauseSafeMode: "ã‚»ãƒ¼ãƒ•ãƒ¢ãƒ¼ãƒ‰ãŒæœ‰åйãªé–“ã¯ãƒ‡ãƒ•ォルトã®ãƒ†ãƒ¼ãƒžãŒä½¿ç”¨ã•れã¾ã™ã€‚セーフモードをオフã«ã™ã‚‹ã¨å…ƒã«æˆ»ã‚Šã¾ã™ã€‚"
+thankYouForTestingBeta: "ãƒ™ãƒ¼ã‚¿ç‰ˆã®æ¤œè¨¼ã«ã”å”力ã„ãŸã ãã‚りãŒã¨ã†ã”ã–ã„ã¾ã™ï¼"
_order:
newest: "æ–°ã—ã„é †"
@@ -1463,6 +1472,7 @@ _settings:
contentsUpdateFrequency_description2: "リアルタイムモードãŒã‚ªãƒ³ã®ã¨ãã¯ã€ã“ã®è¨­å®šã«é–¢ã‚らãšãƒªã‚¢ãƒ«ã‚¿ã‚¤ãƒ ã§ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ãŒæ›´æ–°ã•れã¾ã™ã€‚"
showUrlPreview: "URLプレビューを表示ã™ã‚‹"
showAvailableReactionsFirstInNote: "利用ã§ãるリアクションを先頭ã«è¡¨ç¤º"
+ showPageTabBarBottom: "ページã®ã‚¿ãƒ–ãƒãƒ¼ã‚’下部ã«è¡¨ç¤º"
_chat:
showSenderName: "é€ä¿¡è€…ã®åå‰ã‚’表示"
@@ -1603,9 +1613,9 @@ _initialTutorial:
public: "ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«å…¬é–‹ã€‚"
home: "ホームタイムラインã®ã¿ã«å…¬é–‹ã€‚ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãƒ»ãƒ—ãƒ­ãƒ•ã‚£ãƒ¼ãƒ«ã‚’è¦‹ã«æ¥ãŸäººãƒ»ãƒªãƒŽãƒ¼ãƒˆã‹ã‚‰ã€ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚‚見るã“ã¨ãŒã§ãã¾ã™ã€‚"
followers: "フォロワーã«ã®ã¿å…¬é–‹ã€‚本人以外ãŒãƒªãƒŽãƒ¼ãƒˆã™ã‚‹ã“ã¨ã¯ã§ããšã€ã¾ãŸãƒ•ォロワー以外ã¯é–²è¦§ã§ãã¾ã›ã‚“。"
- direct: "指定ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã®ã¿å…¬é–‹ã•れã€ã¾ãŸç›¸æ‰‹ã«é€šçŸ¥ãŒå…¥ã‚Šã¾ã™ã€‚ダイレクトメッセージã®ã‹ã‚りã«ãŠä½¿ã„ã„ãŸã ã‘ã¾ã™ã€‚"
+ direct: "指定ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã®ã¿å…¬é–‹ã•れã€ã¾ãŸç›¸æ‰‹ã«é€šçŸ¥ãŒå…¥ã‚Šã¾ã™ã€‚"
doNotSendConfidencialOnDirect1: "機密情報ã¯é€ä¿¡ã™ã‚‹éš›ã¯æ³¨æ„ã—ã¦ãã ã•ã„。"
- doNotSendConfidencialOnDirect2: "é€ä¿¡å…ˆã®ã‚µãƒ¼ãƒãƒ¼ã®ç®¡ç†è€…ã¯æŠ•ç¨¿å†…å®¹ã‚’è¦‹ã‚‹ã“ã¨ãŒå¯èƒ½ãªã®ã§ã€ä¿¡é ¼ã§ããªã„サーãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆæŠ•稿をé€ä¿¡ã™ã‚‹å ´åˆã¯ã€æ©Ÿå¯†æƒ…å ±ã®æ‰±ã„ã«æ³¨æ„ãŒå¿…è¦ã§ã™ã€‚"
+ doNotSendConfidencialOnDirect2: "é€ä¿¡å…ˆã®ã‚µãƒ¼ãƒãƒ¼ã®ç®¡ç†è€…ã¯æŠ•ç¨¿å†…å®¹ã‚’è¦‹ã‚‹ã“ã¨ãŒå¯èƒ½ãªã®ã§ã€ä¿¡é ¼ã§ããªã„サーãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒå«ã¾ã‚Œã‚‹é™å®šå…¬é–‹ã®ãƒŽãƒ¼ãƒˆã‚’作æˆã™ã‚‹éš›ã¯ã€æ©Ÿå¯†æƒ…å ±ã®æ‰±ã„ã«æ³¨æ„ãŒå¿…è¦ã§ã™ã€‚"
localOnly: "ä»–ã®ã‚µãƒ¼ãƒãƒ¼ã«æŠ•稿を連åˆã—ã¾ã›ã‚“。上記ã®å…¬é–‹ç¯„囲ã«é–¢ã‚らãšã€ä»–ã®ã‚µãƒ¼ãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã€ã“ã®è¨­å®šãŒã¤ã„ãŸãƒŽãƒ¼ãƒˆã‚’直接閲覧ã™ã‚‹ã“ã¨ãŒã§ããªããªã‚Šã¾ã™ã€‚"
_cw:
title: "内容を隠ã™ï¼ˆCW)"
@@ -1649,6 +1659,10 @@ _serverSettings:
fanoutTimelineDbFallback: "データベースã¸ã®ãƒ•ォールãƒãƒƒã‚¯"
fanoutTimelineDbFallbackDescription: "有効ã«ã™ã‚‹ã¨ã€ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ãŒã‚­ãƒ£ãƒƒã‚·ãƒ¥ã•れã¦ã„ãªã„å ´åˆã«DBã¸è¿½åŠ ã§å•ã„åˆã‚ã›ã‚’行ã†ãƒ•ォールãƒãƒƒã‚¯å‡¦ç†ã‚’行ã„ã¾ã™ã€‚無効ã«ã™ã‚‹ã¨ã€ãƒ•ォールãƒãƒƒã‚¯å‡¦ç†ã‚’行ã‚ãªã„ã“ã¨ã§ã•らã«ã‚µãƒ¼ãƒãƒ¼ã®è² è·ã‚’軽減ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ãŒã€ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ãŒå–å¾—ã§ãる範囲ã«åˆ¶é™ãŒç”Ÿã˜ã¾ã™ã€‚"
reactionsBufferingDescription: "有効ã«ã™ã‚‹ã¨ã€ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ä½œæˆæ™‚ã®ãƒ‘フォーマンスãŒå¤§å¹…ã«å‘上ã—ã€ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã¸ã®è² è·ã‚’軽減ã™ã‚‹ã“ã¨ãŒå¯èƒ½ã§ã™ã€‚ãŸã ã—ã€Redisã®ãƒ¡ãƒ¢ãƒªä½¿ç”¨é‡ã¯å¢—加ã—ã¾ã™ã€‚"
+ remoteNotesCleaning: "リモート投稿ã®è‡ªå‹•クリーニング"
+ remoteNotesCleaning_description: "有効ã«ã™ã‚‹ã¨ã€ä¸€å®šæœŸé–“経éŽã—ãŸãƒªãƒ¢ãƒ¼ãƒˆã®æŠ•稿を定期的ã«ã‚¯ãƒªãƒ¼ãƒ³ã‚¢ãƒƒãƒ—ã—ã¦ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã®è‚¥å¤§åŒ–を抑制ã—ã¾ã™ã€‚"
+ remoteNotesCleaningMaxProcessingDuration: "最大クリーニング処ç†ç¶™ç¶šæ™‚é–“"
+ remoteNotesCleaningExpiryDaysForEachNotes: "æœ€ä½ŽãƒŽãƒ¼ãƒˆä¿æŒæ—¥æ•°"
inquiryUrl: "å•ã„åˆã‚ã›å…ˆURL"
inquiryUrlDescription: "サーãƒãƒ¼é‹å–¶è€…ã¸ã®ãŠå•ã„åˆã‚ã›ãƒ•ォームã®URLã‚„ã€é‹å–¶è€…ã®é€£çµ¡å…ˆç­‰ãŒè¨˜è¼‰ã•れãŸWebページã®URLを指定ã—ã¾ã™ã€‚"
openRegistration: "アカウントã®ä½œæˆã‚’オープンã«ã™ã‚‹"
@@ -1667,6 +1681,11 @@ _serverSettings:
userGeneratedContentsVisibilityForVisitor: "éžåˆ©ç”¨è€…ã«å¯¾ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ä½œæˆã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã®å…¬é–‹ç¯„囲"
userGeneratedContentsVisibilityForVisitor_description: "モデレーションãŒè¡Œã届ãã«ãã„ä¸é©åˆ‡ãªãƒªãƒ¢ãƒ¼ãƒˆã‚³ãƒ³ãƒ†ãƒ³ãƒ„ãªã©ãŒã€è‡ªã‚µãƒ¼ãƒãƒ¼çµŒç”±ã§å›³ã‚‰ãšã‚‚インターãƒãƒƒãƒˆã«å…¬é–‹ã•れã¦ã—ã¾ã†ã“ã¨ã«ã‚ˆã‚‹ãƒˆãƒ©ãƒ–ル防止ãªã©ã«å½¹ç«‹ã¡ã¾ã™ã€‚"
userGeneratedContentsVisibilityForVisitor_description2: "サーãƒãƒ¼ã§å—ä¿¡ã—ãŸãƒªãƒ¢ãƒ¼ãƒˆã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã‚’å«ã‚ã€ã‚µãƒ¼ãƒãƒ¼å†…ã®å…¨ã¦ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã‚’ç„¡æ¡ä»¶ã§ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒƒãƒˆã«å…¬é–‹ã™ã‚‹ã“ã¨ã¯ãƒªã‚¹ã‚¯ãŒä¼´ã„ã¾ã™ã€‚特ã«ã€åˆ†æ•£åž‹ã®ç‰¹æ€§ã‚’知らãªã„閲覧者ã«ã¨ã£ã¦ã¯ã€ãƒªãƒ¢ãƒ¼ãƒˆã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã§ã‚ã£ã¦ã‚‚サーãƒãƒ¼å†…ã§ä½œæˆã•れãŸã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã§ã‚ã‚‹ã¨èª¤ã£ã¦èªè­˜ã—ã¦ã—ã¾ã†å¯èƒ½æ€§ãŒã‚ã‚‹ãŸã‚ã€æ³¨æ„ãŒå¿…è¦ã§ã™ã€‚"
+ restartServerSetupWizardConfirm_title: "サーãƒãƒ¼ã®åˆæœŸè¨­å®šã‚¦ã‚£ã‚¶ãƒ¼ãƒ‰ã‚’やり直ã—ã¾ã™ã‹ï¼Ÿ"
+ restartServerSetupWizardConfirm_text: "ç¾åœ¨ã®ä¸€éƒ¨ã®è¨­å®šã¯ãƒªã‚»ãƒƒãƒˆã•れã¾ã™ã€‚"
+ entrancePageStyle: "エントランスページã®ã‚¹ã‚¿ã‚¤ãƒ«"
+ showTimelineForVisitor: "タイムラインを表示ã™ã‚‹"
+ showActivitiesForVisitor: "アクティビティを表示ã™ã‚‹"
_userGeneratedContentsVisibilityForVisitor:
all: "å…¨ã¦å…¬é–‹"
@@ -2007,6 +2026,7 @@ _role:
descriptionOfRateLimitFactor: "å°ã•ã„ã»ã©åˆ¶é™ãŒç·©å’Œã•れã€å¤§ãã„ã»ã©åˆ¶é™ãŒå¼·åŒ–ã•れã¾ã™ã€‚"
canHideAds: "広告ã®éžè¡¨ç¤º"
canSearchNotes: "ノート検索ã®åˆ©ç”¨"
+ canSearchUsers: "ユーザー検索ã®åˆ©ç”¨"
canUseTranslator: "翻訳機能ã®åˆ©ç”¨"
avatarDecorationLimit: "ã‚¢ã‚¤ã‚³ãƒ³ãƒ‡ã‚³ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã®æœ€å¤§å–付個数"
canImportAntennas: "アンテナã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆã‚’許å¯"
@@ -2306,6 +2326,7 @@ _time:
minute: "分"
hour: "時間"
day: "æ—¥"
+ month: "ヶ月"
_2fa:
alreadyRegistered: "æ—¢ã«è¨­å®šã¯å®Œäº†ã—ã¦ã„ã¾ã™ã€‚"
@@ -2524,7 +2545,7 @@ _visibility:
homeDescription: "ホームタイムラインã®ã¿ã«å…¬é–‹"
followers: "フォロワー"
followersDescription: "自分ã®ãƒ•ォロワーã®ã¿ã«å…¬é–‹"
- specified: "ダイレクト"
+ specified: "指å"
specifiedDescription: "指定ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã¿ã«å…¬é–‹"
disableFederation: "連åˆãªã—"
disableFederationDescription: "他サーãƒãƒ¼ã¸ã®é…信を行ã„ã¾ã›ã‚“"
@@ -2770,8 +2791,8 @@ _deck:
antenna: "アンテナ"
list: "リスト"
channel: "ãƒãƒ£ãƒ³ãƒãƒ«"
- mentions: "ã‚ãªãŸå®›ã¦"
- direct: "ダイレクト"
+ mentions: "メンション"
+ direct: "指å"
roleTimeline: "ロールタイムライン"
chat: "ãƒãƒ£ãƒƒãƒˆ"
@@ -3156,6 +3177,7 @@ _bootErrors:
otherOption1: "クライアント設定ã¨ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã‚’削除"
otherOption2: "簡易クライアントを起動"
otherOption3: "修復ツールを起動"
+ otherOption4: "Misskeyをセーフモードã§èµ·å‹•"
_search:
searchScopeAll: "å…¨ã¦"
@@ -3194,6 +3216,8 @@ _serverSetupWizard:
doYouConnectToFediverse_description1: "分散型サーãƒãƒ¼ã§æ§‹æˆã•れるãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯(Fediverse)ã«æŽ¥ç¶šã™ã‚‹ã¨ã€ä»–ã®ã‚µãƒ¼ãƒãƒ¼ã¨ç›¸äº’ã«ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã®ã‚„りå–りãŒå¯èƒ½ã§ã™ã€‚"
doYouConnectToFediverse_description2: "Fediverseã¨æŽ¥ç¶šã™ã‚‹ã“ã¨ã¯ã€Œé€£åˆã€ã¨ã‚‚呼ã°ã‚Œã¾ã™ã€‚"
youCanConfigureMoreFederationSettingsLater: "連åˆå¯èƒ½ãªã‚µãƒ¼ãƒãƒ¼ã®æŒ‡å®šãªã©ã€é«˜åº¦ãªè¨­å®šã‚‚後ã»ã©å¯èƒ½ã§ã™ã€‚"
+ remoteContentsCleaning: "リモートコンテンツã®è‡ªå‹•クリーニング"
+ remoteContentsCleaning_description: "連åˆã‚’行ã†ã¨ã€ç¶™ç¶šã—ã¦å¤šãã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã‚’å—ä¿¡ã—ã¾ã™ã€‚自動クリーニングを有効ã«ã™ã‚‹ã¨ã€ä¸€å®šæœŸé–“経éŽã—ãŸãƒªãƒ¢ãƒ¼ãƒˆã‚³ãƒ³ãƒ†ãƒ³ãƒ„を自動ã§ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰å‰Šé™¤ã—ã€ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã‚’節約ã§ãã¾ã™ã€‚"
adminInfo: "管ç†è€…情報"
adminInfo_description: "å•ã„åˆã‚ã›ã‚’å—ã‘付ã‘ã‚‹ãŸã‚ã«ä½¿ç”¨ã•れる管ç†è€…情報を設定ã—ã¾ã™ã€‚"
adminInfo_mustBeFilled: "オープンサーãƒãƒ¼ã€ã¾ãŸã¯é€£åˆãŒã‚ªãƒ³ã®å ´åˆã¯å¿…ãšå…¥åŠ›ãŒå¿…è¦ã§ã™ã€‚"
@@ -3251,10 +3275,10 @@ _watermarkEditor:
type: "タイプ"
image: "ç”»åƒ"
advanced: "高度"
+ angle: "角度"
stripe: "ストライプ"
stripeWidth: "ラインã®å¹…"
stripeFrequency: "ãƒ©ã‚¤ãƒ³ã®æ•°"
- angle: "角度"
polkadot: "ãƒãƒ«ã‚«ãƒ‰ãƒƒãƒˆ"
checker: "ãƒã‚§ãƒƒã‚«ãƒ¼"
polkadotMainDotOpacity: "メインドットã®ä¸é€æ˜Žåº¦"
@@ -3267,6 +3291,7 @@ _imageEffector:
title: "エフェクト"
addEffect: "エフェクトを追加"
discardChangesConfirm: "変更を破棄ã—ã¦çµ‚了ã—ã¾ã™ã‹ï¼Ÿ"
+ nothingToConfigure: "設定項目ã¯ã‚りã¾ã›ã‚“"
_fxs:
chromaticAberration: "色åŽå·®"
@@ -3286,6 +3311,39 @@ _imageEffector:
blockNoise: "ブロックノイズ"
tearing: "ティアリング"
+ _fxProps:
+ angle: "角度"
+ scale: "サイズ"
+ size: "サイズ"
+ color: "色"
+ opacity: "ä¸é€æ˜Žåº¦"
+ normalize: "æ­£è¦åŒ–"
+ amount: "é‡"
+ lightness: "明るã•"
+ contrast: "コントラスト"
+ hue: "色相"
+ brightness: "è¼åº¦"
+ saturation: "彩度"
+ max: "最大値"
+ min: "最å°å€¤"
+ direction: "æ–¹å‘"
+ phase: "ä½ç›¸"
+ frequency: "頻度"
+ strength: "å¼·ã•"
+ glitchChannelShift: "ズレ"
+ seed: "シード値"
+ redComponent: "赤色æˆåˆ†"
+ greenComponent: "緑色æˆåˆ†"
+ blueComponent: "é’色æˆåˆ†"
+ threshold: "ã—ãã„値"
+ centerX: "中心X"
+ centerY: "中心Y"
+ zoomLinesSmoothing: "スムージング"
+ zoomLinesSmoothingDescription: "スムージングã¨é›†ä¸­ç·šã®å¹…ã®è¨­å®šã¯ä½µç”¨ã§ãã¾ã›ã‚“。"
+ zoomLinesThreshold: "集中線ã®å¹…"
+ zoomLinesMaskSize: "中心径"
+ zoomLinesBlack: "黒色ã«ã™ã‚‹"
+
drafts: "下書ã"
_drafts:
select: "下書ãã‚’é¸æŠž"
diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml
index 92f849efbb..d44ed1c3fc 100644
--- a/locales/ja-KS.yml
+++ b/locales/ja-KS.yml
@@ -300,6 +300,7 @@ uploadFromUrlMayTakeTime: "アップロード終ã‚ã‚‹ã‚“ã«ã¡ã‚‡ã„時間ã‹ã
explore: "ã¿ã¤ã‘ã‚‹"
messageRead: "ã‚‚ã†èª­ã‚“ã "
noMoreHistory: "ã“れより昔ã®ã‚“ã¯ã‚らã¸ã‚“ã§"
+startChat: "ãƒãƒ£ãƒƒãƒˆã‚’å§‹ã‚よã‹"
nUsersRead: "{n}人ãŒèª­ã‚“ã§ã‚‚ã†ãŸ"
agreeTo: "{0}ã«åŒæ„ã—ãŸã§"
agree: "ã›ã‚„ãª"
@@ -324,6 +325,7 @@ dark: "ダーク"
lightThemes: "デイゲーム"
darkThemes: "ナイトゲーム"
syncDeviceDarkMode: "デãƒã‚¤ã‚¹ã®ãƒ€ãƒ¼ã‚¯ãƒ¢ãƒ¼ãƒ‰ã¨ä¸€ç·’ã«ã™ã‚‹"
+switchDarkModeManuallyWhenSyncEnabledConfirm: "「{x}ã€ãŒã‚ªãƒ³ã«ãªã£ã¦ã‚‹ã§ã€‚åŒæœŸã‚’オフã«ã—ã¦æ‰‹å‹•ã§ãƒ¢ãƒ¼ãƒ‰ã‚’切り替ãˆã‚‹ã“ã¨ã«ã—ã¾ã™ï¼Ÿ"
drive: "ドライブ"
fileName: "ファイルå"
selectFile: "ファイルé¸ã‚“ã§ã‚„"
@@ -422,6 +424,7 @@ antennaExcludeBots: "Botアカウントを除外"
antennaKeywordsDescription: "スペースã§åŒºåˆ‡ã£ãŸã‚‹ã¨AND指定ã§ã€æ”¹è¡Œã§åŒºåˆ‡ã£ãŸã‚‹ã¨OR指定や"
notifyAntenna: "æ–°ã—ã„ノートを通知ã™ã‚“ã§"
withFileAntenna: "ãªã‚“ã‹æ·»ä»˜ã•れãŸãƒŽãƒ¼ãƒˆã ã‘"
+excludeNotesInSensitiveChannel: "センシティブãªãƒãƒ£ãƒ³ãƒãƒ«ã®ãƒŽãƒ¼ãƒˆã¯å…¥ã‚Œã‚“ã¨ãã‚"
enableServiceworker: "ブラウザã«ãƒ—ッシュ通知ãŒè¡Œãよã†ã«ã™ã‚‹"
antennaUsersDescription: "ユーザーåを改行ã§åŒºåˆ‡ã£ãŸã£ã¦ãª"
caseSensitive: "大文字ã¨å°æ–‡å­—ã¯åˆ¥ã‚‚ã‚“ã‚„"
@@ -693,6 +696,7 @@ userSaysSomethingAbout: "{name}ãŒã€Œ{word}ã€ã«ã¤ã„ã¦ãªã‚“ã‹è¨€ã†ã¦ãŸã
makeActive: "使ã†ã§"
display: "表示"
copy: "コピー"
+copiedToClipboard: "クリップボードã«ã‚³ãƒ”ーã•れãŸã§"
metrics: "メトリクス"
overview: "概è¦"
logs: "ログ"
@@ -787,6 +791,7 @@ wide: "広ã„"
narrow: "ç‹­ã„"
reloadToApplySetting: "設定ã¯ãƒšãƒ¼ã‚¸ãƒªãƒ­ãƒ¼ãƒ‰å¾Œã«å映ã•れるã§ã€‚今リロードã—ã¨ãã‹ï¼Ÿ"
needReloadToApply: "åæ˜ ã«ã¯å†èµ·å‹•ã›ãªã‚ã‹ã‚“ã§"
+needToRestartServerToApply: "åæ˜ ã«ã¯ã‚µãƒ¼ãƒãƒ¼ã‚’å†èµ·å‹•ã›ãªã‚ã‹ã‚“ã®ã‚ˆã€‚"
showTitlebar: "タイトルãƒãƒ¼ã‚’見ã›ã‚‹"
clearCache: "キャッシュをã»ã‹ã™"
onlineUsersCount: "{n}人ãŒèµ·ãã¨ã‚‹ã§"
@@ -974,6 +979,7 @@ document: "ドキュメント"
numberOfPageCache: "ページã€ã©ã‚“ã ã‘キャッシュã™ã‚“ã®ï¼Ÿ"
numberOfPageCacheDescription: "増やã™ã¨ä½¿ã„ã‚„ã™ããªã‚‹ã‘ã©ã€è² è·ã¨ãƒ¡ãƒ¢ãƒªä½¿ç”¨é‡ãŒå¢—ãˆã¦ãã§ã€‚一長一短やãªã€‚"
logoutConfirm: "ログアウトã—ã¾ã£ã‹ï¼Ÿ"
+logoutWillClearClientData: "ログアウトã™ã‚‹ã¨ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆã®è¨­å®šæƒ…å ±ãŒãƒ–ラウザã‹ã‚‰æ¶ˆã•れã¦ã¾ã†ã§ã€‚å†ãƒ­ã‚°ã‚¤ãƒ³æ™‚ã«è¨­å®šæƒ…報を復元ã§ãるよã†ã«ã™ã‚‹ãŸã‚ã«ã¯ã€è¨­å®šã®è‡ªå‹•ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—を有効ã«ã™ã‚‹ã¨ãˆãˆã§ã€‚"
lastActiveDate: "最後ã«ä½¿ã£ãŸæ—¥æ™‚"
statusbar: "ステータスãƒãƒ¼"
pleaseSelect: "é¸ã‚“ã ã£ã¦ã‚„ー"
@@ -992,6 +998,7 @@ failedToUpload: "アップロードã«å¤±æ•—ã—ã¦ã‚‚ã†ãŸã‚…"
cannotUploadBecauseInappropriate: "ãã‚ã©ã„内容をå«ã‚€ã‹ã‚‚ã—れã¸ã‚“ã£ã¦è¨€ã‚れãŸã‹ã‚‰ã‚¢ãƒƒãƒ—ロードã§ãã¸ã‚“ã‚。"
cannotUploadBecauseNoFreeSpace: "ドライブãŒã‚‚ã†ãƒ‘ンパンやã‹ã‚‰ã‚¢ãƒƒãƒ—ロードã§ãã¸ã‚“ã‚。"
cannotUploadBecauseExceedsFileSizeLimit: "ãƒ•ã‚¡ã‚¤ãƒ«ãŒæ€ã†ãŸã‚ˆã‚Šã‚‚大ãã„ã•ã‹ã„アップロードã§ãã¸ã‚“ã§ã“れ。"
+cannotUploadBecauseUnallowedFileType: "許å¯ã•れã¦ã¸ã‚“ファイル種別やã‹ã‚‰ã‚¢ãƒƒãƒ—ロードã§ãã¸ã‚“ã£ã½ã„。"
beta: "ベータ"
enableAutoSensitive: "自動ã§ãã‚ã©ã„ã‹åˆ¤æ–­ã™ã‚‹"
enableAutoSensitiveDescription: "使ãˆã‚‹æ™‚ã¯ã€æ©Ÿæ¢°å­¦ç¿’を使ã£ã¦è‡ªå‹•ã§ãƒ¡ãƒ‡ã‚£ã‚¢ã«NSFWフラグを設定ã™ã‚‹ã§ã€‚ã“ã®æ©Ÿèƒ½ã‚’オフã«ã—ã¦ã‚‚ã€ã‚µãƒ¼ãƒãƒ¼ã«ã‚ˆã£ã¦ã¯è‡ªå‹•ã§è¨­å®šã•れるã“ã¨ãŒã‚ã‚‹ã§ã€‚"
@@ -1232,7 +1239,6 @@ releaseToRefresh: "離ã—ãŸã‚‰ãƒªãƒ­ãƒ¼ãƒ‰"
refreshing: "リロードã—ã¨ã‚‹"
pullDownToRefresh: "引ã£å¼µã£ã¦ãƒªãƒ­ãƒ¼ãƒ‰ã™ã‚‹ã§"
useGroupedNotifications: "通知をグループ分ã‘ã—ã¦å‡ºã™ã§"
-signupPendingError: "メアド確èªã—ã¦ãŸã‚‰ãªã‚“ã‹å¤‰ãªã“ã¨ãªã£ãŸã‚ã€‚ãƒªãƒ³ã‚¯ã®æœŸé™åˆ‡ã‚Œã¦ã‚‹ã‹ã‚‚ã—れん。"
cwNotationRequired: "「内容を隠ã™ã€ã‚“ã‚„ã£ãŸã‚‰æ³¨é‡ˆæ›¸ã‹ãªã‚¢ã‚«ãƒ³ã§ã€‚"
doReaction: "ツッコむã§"
code: "コード"
@@ -1304,16 +1310,97 @@ federationSpecified: "ã“ã®ã‚µãƒ¼ãƒãƒ¼ã¯ãƒ›ãƒ¯ã‚¤ãƒˆãƒªã‚¹ãƒˆé€£åˆã§é‹ç”¨ã
federationDisabled: "ã“ã®ã‚µãƒ¼ãƒãƒ¼ã¯é€£åˆãŒç„¡åŠ¹åŒ–ã•れã¦ã‚‹ã§ã€‚ä»–ã®ã‚µãƒ¼ãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¨ã‚„りå–りã™ã‚‹ã“ã¨ã¯ã§ãã²ã‚“ã§ã€‚"
confirmOnReact: "ツッコむã¨ãã«ç¢ºèªã¨ã‚‹"
reactAreYouSure: "\" {emoji} \" ã§ãƒ„ッコむ?"
+markAsSensitiveConfirm: "ã“ã®ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’ãã‚ã©ã„扱ã„ã—ã¨ãã¾ã™ã‹ï¼Ÿ"
+unmarkAsSensitiveConfirm: "ã“ã®ãƒ¡ãƒ‡ã‚£ã‚¢ã¯ã‚„ã£ã±ãã‚ã©ããªã‹ã£ãŸã£ã¦ã“ã¨ã§ãˆãˆã‚“ã‹ï¼Ÿ"
+noName: "åå‰ã¯ã‚らã¸ã‚“ã§"
+preferenceSyncConflictTitle: "サーãƒãƒ¼ã«è¨­å®šå€¤ãŒã‚ã‚‹ã¿ãŸã„ã‚„ã‚"
+preferenceSyncConflictText: "åŒæœŸãŒæœ‰åйã«ã•れãŸè¨­å®šé …ç›®ã¯è¨­å®šå€¤ã‚’サーãƒãƒ¼ã«ä¿å­˜ã™ã‚‹ã­ã‚“ã‘ã©ã€ã“ã®è¨­å®šé …ç›®ã¯ã‚µãƒ¼ãƒãƒ¼ã«ä¿å­˜ã•れãŸã‚„ã¤ãŒã‚ã‚‹ã¿ãŸã„ã‚„ã‚。ã©ãªã„ã™ã‚‹ã‚“?"
+preferenceSyncConflictChoiceMerge: "ガッãƒãƒ£ãƒ³ã‚³ã—よã‹"
+preferenceSyncConflictChoiceCancel: "åŒæœŸã®æœ‰åŠ¹åŒ–ã¯ã‚„ã‚ã¨ãã‚"
postForm: "投稿フォーム"
information: "情報"
+migrateOldSettings: "旧設定情報をãŠå¼•ã£è¶Šã—"
+migrateOldSettings_description: "通常ã“れã¯è‡ªå‹•ã§è¡Œã‚れるã¯ãšãªã‚“ã‚„ã‘ã©ã€ãªã‚“ã‹ã®ç†ç”±ã§ä¸Šæ‰‹ã移行ã§ãã¸ã‚“ã‹ã£ãŸã¨ãã¯æ‰‹å‹•ã§ç§»è¡Œå‡¦ç†ã‚’ãƒãƒã£ã¨ã§ãã‚‹ã§ã€‚今ã®è¨­å®šæƒ…å ±ã¯ä¸Šæ›¸ãã•れるã§ã€‚"
+settingsMigrating: "設定を移行ã—ã¨ã‚‹ã§ã€‚ã¡ã‚‡ã£ã¨å¾…ã£ã¨ã£ã¦ãª... (後ã§ã€è¨­å®šâ†’ãã®ä»–→旧設定情報を移行 ã§æ‰‹å‹•ã§ç§»è¡Œã™ã‚‹ã“ã¨ã‚‚ã§ãã‚‹ã§)"
+driveAboutTip: "ドライブã§ã¯ã€ä»Šã¾ã§ã‚¢ãƒƒãƒ—ロードã—ãŸãƒ•ァイルãŒãšã‚‰ãƒ¼ã£ã¨è¡¨ç¤ºã•れるã§ã€‚<br>\nノートã«ãƒ•ァイルをもã£ã‹ã„ã®ã£ã‘ãŸã‚Šã€ã‚ã¨ã§æŠ•稿ã™ã‚‹ãƒ•ァイルをãã®è¾ºã«ç½®ã„ã¨ãã“ã¨ã‚‚ã§ãã‚‹ã­ã‚“。<br>\n<b>ファイルをã»ã‹ã™ã¨ã€å‰ã«ãã®ãƒ•ァイルをã®ã£ã‘ãŸå…¨éƒ¨ã®å ´æ‰€(ノートã€ãƒšãƒ¼ã‚¸ã€ã‚¢ãƒã‚¿ãƒ¼ã€ãƒãƒŠãƒ¼ç­‰)ã‹ã‚‰ã‚‚見ãˆã‚“ããªã‚‹ã‹ã‚‰æ°—ã„ã¤ã‘ã¦ãªã€‚</b><br>\nフォルダを作ã£ã¦æ•´ç†ã™ã‚‹ã“ã¨ã‚‚ã§ãã‚‹ã§ã€‚"
+turnItOn: "オンã«ã—ã¨ã“"
+turnItOff: "オフã§ãˆãˆã‚"
+emojiUnmute: "絵文字ミュートやã‚ãŸã‚‹"
+unmuteX: "{x}ã®ãƒŸãƒ¥ãƒ¼ãƒˆã‚„ã‚ãŸã‚‹"
+redisplayAllTips: "全部ã®ã€Œãƒ’ントã¨ã‚³ãƒ„ã€ã‚’ã‚‚ã£ã‹ã„見ã—ã¦"
+hideAllTips: "「ヒントã¨ã‚³ãƒ„ã€ã¯å…¨éƒ¨è¡¨ç¤ºã›ã‚“ã§ãˆãˆ"
+defaultImageCompressionLevel_description: "低ãã™ã‚‹ã¨ç”»è³ªã¯ä¿ã¦ã‚‹ã‚“ã‚„ã‘ã©ã€ãƒ•ァイルサイズãŒå¢—ãˆã‚‹ã§ã€‚<br>高ãã™ã‚‹ã¨ãƒ•ã‚¡ã‚¤ãƒ«ã‚µã‚¤ã‚ºã¯æ¸›ã‚‰ã›ã‚‹ã‚“ã‚„ã‘ã©ã€ç”»è³ªãŒè½ã¡ã‚‹ã§ã€‚"
+inMinutes: "分"
+inDays: "æ—¥"
+safeModeEnabled: "セーフモードãŒã‚ªãƒ³ã«ãªã£ã¦ã‚‹ã§"
+pluginsAreDisabledBecauseSafeMode: "セーフモードãŒã‚ªãƒ³ã‚„ã‹ã‚‰ã€ãƒ—ラグインã¯å…¨éƒ¨ç„¡åŠ¹åŒ–ã•れã¦ã‚‹ã§ã€‚"
+customCssIsDisabledBecauseSafeMode: "セーフモードãŒã‚ªãƒ³ã‚„ã‹ã‚‰ã€ã‚«ã‚¹ã‚¿ãƒ CSSã¯é©ç”¨ã•れã¦ã¸ã‚“ã§ã€‚"
+themeIsDefaultBecauseSafeMode: "セーフモードãŒã‚ªãƒ³ã®é–“ã¯ãƒ‡ãƒ•ォルトã®ãƒ†ãƒ¼ãƒžã‚’使ã†ã§ã€‚セーフモードをオフã«ã‚Œã°å…ƒã«æˆ»ã‚‹ã§ã€‚"
_chat:
+ noMessagesYet: "ã¾ã ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã¯ã‚らã¸ã‚“ã§"
+ individualChat_description: "特定ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¨ä¸€å¯¾ä¸€ã§ãƒãƒ£ãƒƒãƒˆãŒã§ãã‚‹ã§ã€‚"
+ roomChat_description: "複数人ã§ãƒãƒ£ãƒƒãƒˆã§ãã‚‹ã§ã€‚\nã‚ã¨ã€å€‹äººãƒãƒ£ãƒƒãƒˆã‚’許å¯ã—ã¦ã¸ã‚“ユーザーã¨ã§ã‚‚ã€ç›¸æ‰‹ãŒãˆãˆã£ã¦è¨€ã†ãªã‚‰ãƒãƒ£ãƒƒãƒˆã§ãã‚‹ã§ã€‚"
+ inviteUserToChat: "ユーザーを招待ã—ã¦ãƒãƒ£ãƒƒãƒˆã‚’å§‹ã‚ã¦ã¿"
invitations: "æ¥ã¦ã‚„"
+ noInvitations: "招待ã¯ã‚らã¸ã‚“ã§"
noHistory: "履歴ã¯ãªã„ã‚。"
+ noRooms: "ルームã¯ã‚らã¸ã‚“ã§"
members: "メンãƒãƒ¼ã¯ã‚“"
home: "ホーム"
send: "é€ä¿¡"
+ deleteRoom: "ルームをã»ã‹ã™"
+ chatNotAvailableForThisAccountOrServer: "ã“ã®ã‚µãƒ¼ãƒãƒ¼ã€ã‚‚ã—ãã¯ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ãƒãƒ£ãƒƒãƒˆãŒæœ‰åйã«ã•れã¦ã¸ã‚“ã§ã€‚"
+ chatIsReadOnlyForThisAccountOrServer: "ã“ã®ã‚µãƒ¼ãƒãƒ¼ã€ã‚‚ã—ãã¯ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ãƒãƒ£ãƒƒãƒˆãŒèª­ã¿å–り専用ã«ãªã£ã¨ã‚‹ã‚。新ã—ãæ›¸ã込んã ã‚Šã€ãƒãƒ£ãƒƒãƒˆãƒ«ãƒ¼ãƒ ã‚’作ã£ãŸã‚Šå‚加ã—ãŸã‚Šã¯ã§ãã¸ã‚“ã§ã€‚"
+ chatNotAvailableInOtherAccount: "相手ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ãƒãƒ£ãƒƒãƒˆæ©Ÿèƒ½ãŒä½¿ãˆã‚“ããªã£ã¨ã‚‹ã¿ãŸã„ã‚„ã‚。"
+ cannotChatWithTheUser: "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¨ã®ãƒãƒ£ãƒƒãƒˆã‚’é–‹å§‹ã§ãã¸ã‚“ã¿ãŸã„ã‚„ã‚"
+ cannotChatWithTheUser_description: "ãƒãƒ£ãƒƒãƒˆãŒä½¿ãˆã‚“状態ã«ãªã£ã¨ã‚‹ã‹ã€ç›¸æ‰‹ãŒãƒãƒ£ãƒƒãƒˆã‚’開放ã—ã¦ã¸ã‚“ã¿ãŸã„ã‚„ã‚。"
+ youAreNotAMemberOfThisRoomButInvited: "ã‚ã‚“ãŸã¯ã“ã®ãƒ«ãƒ¼ãƒ ã®å‚加者ã¡ã‚ƒã†ã‘ã©ã€æ‹›å¾…ãŒå±Šã„ã¨ã‚‹ã§ã€‚å‚加ã™ã‚‹ã‚“ã‚„ã£ãŸã‚‰ã€æ‹›å¾…を承èªã—ã¦ãªã€‚"
+ doYouAcceptInvitation: "招待を承èªã—ã¦ã‚‚ãˆãˆã‚“ã‹ï¼Ÿ"
+ chatWithThisUser: "ãƒãƒ£ãƒƒãƒˆã—よã‹"
+ thisUserAllowsChatOnlyFromFollowers: "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ãƒ•ォロワーã‹ã‚‰ã®ãƒãƒ£ãƒƒãƒˆã—ã‹å—ã‘付ã‘ã¨ã‚‰ã‚“ã¿ãŸã„ã‚„ã‚。"
+ thisUserAllowsChatOnlyFromFollowing: "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã€ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒãƒ•ォローã—ã¨ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹ã‚‰ã®ãƒãƒ£ãƒƒãƒˆã—ã‹å—ã‘付ã‘ã¨ã‚‰ã‚“ã¿ãŸã„ã‚„ã‚。"
+ thisUserAllowsChatOnlyFromMutualFollowing: "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ç›¸äº’フォローã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹ã‚‰ã®ãƒãƒ£ãƒƒãƒˆã—ã‹å—ã‘付ã‘ã¨ã‚‰ã‚“ã¿ãŸã„ã‚„ã‚。"
+ thisUserNotAllowedChatAnyone: "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯èª°ã‹ã‚‰ã®ãƒãƒ£ãƒƒãƒˆã‚‚å—ã‘付ã‘ã¨ã‚‰ã‚“ã¿ãŸã„ã‚„ã‚。"
+ chatAllowedUsers: "ãƒãƒ£ãƒƒãƒˆã—ã¦ã‚‚ãˆãˆç›¸æ‰‹"
+ chatAllowedUsers_note: "自分ã‹ã‚‰ãƒãƒ£ãƒƒãƒˆãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’é€ã£ãŸç›¸æ‰‹ã‚„ã£ãŸã‚‰ã“ã®è¨­å®šã«é–¢ã‚らãšãƒãƒ£ãƒƒãƒˆã§ãã‚‹ã§ã€‚"
+ _chatAllowedUsers:
+ followers: "自分ã®ãƒ•ォロワーã ã‘"
+ following: "自分ãŒãƒ•ォローã—ã¨ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã ã‘"
+ mutual: "相互フォローã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã ã‘"
+ none: "誰もã‹ã‚‚ã‚ã‹ã‚“"
+_emojiPalette:
+ enableSyncBetweenDevicesForPalettes: "パレットã®ãƒ‡ãƒã‚¤ã‚¹é–“åŒæœŸã‚’ã¤ã‘ã¨ã"
+ paletteForMain: "メインã§ä½¿ã†ãƒ‘レット"
+ paletteForReaction: "リアクションã§ä½¿ã†ãƒ‘レット"
_settings:
+ driveBanner: "ドライブã®ç®¡ç†ã¨è¨­å®šã€ä½¿ç”¨é‡ã®ç¢ºèªã€ãƒ•ァイルをアップロードã™ã‚‹ã¨ãã®è¨­å®šãŒã§ãã‚‹ã§ã€‚"
+ pluginBanner: "プラグインを使ã†ã¨ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆã®æ©Ÿèƒ½ã‚’æ‹¡å¼µã§ãã‚‹ã­ã‚“。プラグインã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã€å€‹åˆ¥ã®è¨­å®šã¨ç®¡ç†ãŒã§ãã‚‹ã§ã€‚"
+ notificationsBanner: "サーãƒãƒ¼ã‹ã‚‰å—ã‘å–る通知ã®ç¨®é¡žã¨ã‹ç¯„囲ã€ãƒ—ッシュ通知ã®è¨­å®šãŒã§ãã‚‹ã§ã€‚"
webhook: "Webhook"
+ serviceConnectionBanner: "外部ã®ã‚¢ãƒ—リ・サービスã¨é€£æºã™ã‚‹ã®ã«ä½¿ã†ã¨ã‚‹ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã¨ã‹Webhookã®ç®¡ç†ã¨è¨­å®šãŒã§ãã‚‹ã§ã€‚"
+ accountDataBanner: "アカウントデータã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–をエクスãƒãƒ¼ãƒˆ/インãƒãƒ¼ãƒˆã—ã¦ç®¡ç†ã§ãã‚‹ã§ã€‚"
+ muteAndBlockBanner: "見ã›ã‚“ã§ãˆãˆã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã®è¨­å®šã¨ã‹ã€ç‰¹å®šã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹ã‚‰ã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’制é™ã™ã‚‹è¨­å®šã¨ç®¡ç†ãŒã§ãã‚‹ã§ã€‚"
+ accessibilityBanner: "クライアントã®è¦–覚や動作ã«é–¢ã‚るパーソナライズをã—ã¦ã€ã‚ˆã‚Šãˆãˆæ„Ÿã˜ã«ä½¿ãˆã‚‹ã‚ˆã†ã«è¨­å®šã§ãã‚‹ã§ã€‚"
+ privacyBanner: "コンテンツã®å…¬é–‹ç¯„囲ã€è¦‹ã¤ã‘ã‚„ã™ã•ã€ãƒ•ã‚©ãƒ­ãƒ¼ã®æ‰¿èªåˆ¶ã¨ã‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ãƒ—ライãƒã‚·ãƒ¼ã«é–¢ã‚る設定ãŒã§ãã‚‹ã§ã€‚"
+ securityBanner: "パスワードã€ãƒ­ã‚°ã‚¤ãƒ³æ–¹æ³•ã€èªè¨¼ã‚¢ãƒ—リã€ãƒ‘スキーã¨ã‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ã«é–¢ã‚る設定ãŒã§ãã‚‹ã§ã€‚"
+ preferencesBanner: "好ã¿ã«å¿œã˜ãŸã€ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆã®å…¨ä½“çš„ãªå‹•作ã®è¨­å®šãŒã§ãã‚‹ã§ã€‚"
+ appearanceBanner: "好ã¿ã«å¿œã˜ãŸã€ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆã®è¦‹ãŸç›®ãƒ»è¡¨ç¤ºæ–¹æ³•ã«é–¢ã‚る設定ãŒã§ãã‚‹ã§ã€‚"
+ soundsBanner: "ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆã§æµã™ã‚µã‚¦ãƒ³ãƒ‰ã®è¨­å®šãŒã§ãã‚‹ã§ã€‚"
+ makeEveryTextElementsSelectable: "全部ã®ãƒ†ã‚­ã‚¹ãƒˆè¦ç´ ã‚’é¸æŠžã§ãるよã†ã«ã™ã‚‹"
+ makeEveryTextElementsSelectable_description: "ã“れをã¤ã‘ã‚‹ã¨ã€ä¸€éƒ¨ã®ã‚·ãƒãƒ¥ã‚¨ãƒ¼ã‚·ãƒ§ãƒ³ã§ãƒ¦ãƒ¼ã‚¶ãƒ“リティãŒä½Žä¸‹ã™ã‚‹ã‹ã‚‚ã—れん。"
+ enablePullToRefresh_description: "マウスやã£ãŸã‚‰ã€ãƒ›ã‚¤ãƒ¼ãƒ«ã‚’押ã—è¾¼ã¿ãªãŒã‚‰ãƒ‰ãƒ©ãƒƒã‚°ã—ã¦ãªã€‚"
+ realtimeMode_description: "サーãƒãƒ¼ã¨æŽ¥ç¶šã‚’確立ã—ã¦ã€ãƒªã‚¢ãƒ«ã‚¿ã‚¤ãƒ ã§ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã‚’æ›´æ–°ã™ã‚‹ã§ã€‚通信é‡ã¨ãƒãƒƒãƒ†ãƒªãƒ¼ã®æ¶ˆè²»ãŒå¤šããªã‚‹ã‹ã‚‚ã—れã¸ã‚“。"
+ contentsUpdateFrequency_description: "高ã„ã»ã©ãƒªã‚¢ãƒ«ã‚¿ã‚¤ãƒ ã«ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ãŒæ›´æ–°ã•れるんやã‘ã©ã€ãã®ã¶ã‚“パフォーマンスãŒä½Žããªã‚‹ã—ã€é€šä¿¡é‡ã¨ãƒãƒƒãƒ†ãƒªãƒ¼ã®æ¶ˆè²»ã‚‚増ãˆã‚‹ã­ã‚“。"
+ contentsUpdateFrequency_description2: "リアルタイムモードをã¤ã‘ã¦ã‚‹ã‚“ã‚„ã£ãŸã‚‰ã€ã“ã®è¨­å®šãŒã©ã†ã§ã‚れリアルタイムã§ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ãŒæ›´æ–°ã•れるã§ã€‚"
+_preferencesProfile:
+ profileNameDescription: "ã“ã®ãƒ‡ãƒã‚¤ã‚¹ã¯ãªã‚“ã¦å‘¼ã‚“ã ã‚‰ãˆãˆã‚“や?"
+_preferencesBackup:
+ noBackupsFoundTitle: "ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ãŒè¦‹ã¤ã‹ã‚‰ã¸ã‚“ã­"
+ noBackupsFoundDescription: "自動ã§ä½œã‚‰ã‚ŒãŸãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã¯è¦‹ã¤ã‹ã‚‰ã‚“ã‹ã£ãŸã‘ã©ã€ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ファイルを手動ã§ä¿å­˜ã—ã¦ã‚‹ã‚“ã‚„ã£ãŸã‚‰ã€ãれをインãƒãƒ¼ãƒˆã—ã¦å¾©å…ƒã§ãã‚‹ã§ã€‚"
+ selectBackupToRestore: "復元ã™ã‚‹ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã‚’é¸ã‚“ã§ã‚„"
+ youNeedToNameYourProfileToEnableAutoBackup: "自動ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—を有効ã™ã‚‹ã‚“ã‚„ã£ãŸã‚‰ãƒ—ロファイルåã®è¨­å®šãŒå¿…è¦ã‚„ãªã€‚"
+ autoPreferencesBackupIsNotEnabledForThisDevice: "ã“ã®ãƒ‡ãƒã‚¤ã‚¹ã§è¨­å®šã®è‡ªå‹•ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã¯æœ‰åйã«ãªã£ã¦ã¸ã‚“ã§ã€‚"
+ backupFound: "設定ã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ãŒã‚ã‚‹ã¿ãŸã„ã‚„ã‚"
_accountSettings:
requireSigninToViewContents: "ログインã—ã¦ã‚‚らã£ã¦ã‹ã‚‰ã‚³ãƒ³ãƒ†ãƒ³ãƒ„見ã¦ã‚‚らã†"
requireSigninToViewContentsDescription1: "ã‚ãªãŸãŒä½œæˆã—ãŸå…¨éƒ¨ã®ãƒŽãƒ¼ãƒˆã¨ã‹ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„を見れるよã†ã«ã™ã‚‹ã®ã«ãƒ­ã‚°ã‚¤ãƒ³ãŒã„るよã†ã«ã™ã‚‹ã§ã€‚クローラーã«ã„ã‚ã„ã‚åŽé›†ã•れるんを防ã’ã‚‹ã‹ã‚‚ã—れん。"
@@ -1324,6 +1411,7 @@ _accountSettings:
makeNotesHiddenBefore: "昔ã®ãƒŽãƒ¼ãƒˆã‚’見れんよã†ã«ã™ã‚‹"
makeNotesHiddenBeforeDescription: "ã“ã®æ©Ÿèƒ½ãŒæœ‰åйã«ãªã£ã¦ã‚‹é–“ã¯ã€è¨­å®šã•ã‚ŒãŸæ—¥æ™‚よりå‰ã€ãれã‹è¨­å®šã•ã‚ŒãŸæ™‚é–“ãŒçµŒã£ãŸãƒŽãƒ¼ãƒˆãŒãƒ•ォロワーã®ã¿è¦‹ã‚Œã‚‹ã‚ˆã†ã«ãªã‚‹ã§ã€‚ç„¡åŠ¹ã«æˆ»ã™ã¨ã€ãƒŽãƒ¼ãƒˆã®å…¬é–‹çŠ¶æ…‹ã‚‚æˆ»ã‚‹ã§ã€‚"
mayNotEffectForFederatedNotes: "リモートサーãƒãƒ¼ã«é€£åˆã•れãŸãƒŽãƒ¼ãƒˆã«ã¯åŠ¹æžœãŒåŠã°ã‚“ã‹ã‚‚ã—れん。"
+ mayNotEffectSomeSituations: "ã“れらã®åˆ¶é™ã¯ç°¡æ˜“çš„ãªã‚‚ã®ã‚„ã§ã€‚リモートサーãƒãƒ¼ã§ã®é–²è¦§ã¨ã‹ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³æ™‚ã¨ã‹ã€ä¸€éƒ¨ã®ã‚·ãƒãƒ¥ã‚¨ãƒ¼ã‚·ãƒ§ãƒ³ã§ã¯é©ç”¨ã•れã¸ã‚“ã‹ã‚‚ã—れん。"
notesHavePassedSpecifiedPeriod: "決ã‚ãŸæ™‚é–“ãŒçµŒã£ãŸãƒŽãƒ¼ãƒˆ"
notesOlderThanSpecifiedDateAndTime: "決ã‚ãŸæ—¥æ™‚よりå‰ã®ãƒŽãƒ¼ãƒˆ"
_abuseUserReport:
@@ -1342,6 +1430,7 @@ _delivery:
manuallySuspended: "æ‰‹å‹•åœæ­¢ä¸­"
goneSuspended: "サーãƒãƒ¼å‰Šé™¤ã®ãŸã‚åœæ­¢ä¸­"
autoSuspendedForNotResponding: "サーãƒãƒ¼å¿œç­”ã›ãˆã¸ã‚“ã‹ã‚‰åœæ­¢ä¸­"
+ softwareSuspended: "é…ä¿¡åœæ­¢ä¸­ã®ã‚½ãƒ•トウェアやã‹ã‚‰åœæ­¢ä¸­"
_bubbleGame:
howToPlay: "éŠã³æ–¹"
hold: "ホールド"
@@ -1468,11 +1557,21 @@ _serverSettings:
fanoutTimelineDbFallback: "データベースã«ãƒ•ォールãƒãƒƒã‚¯ã™ã‚‹"
fanoutTimelineDbFallbackDescription: "有効ã«ã—ãŸã‚‰ã€ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ãŒã‚­ãƒ£ãƒƒã‚·ãƒ¥ã‚“中ã«å…¥ã£ã¦ãªã„ã¨ãã«DBã«ã‚‚ã£ã‹ã„å•ã„åˆã‚ã›ã‚‹ãƒ•ォールãƒãƒƒã‚¯å‡¦ç†ã£ã¦ã®ã‚’ã‚„ã£ã¨ãã§ã€‚切ã£ãŸã‚‰ãƒ•ォールãƒãƒƒã‚¯å‡¦ç†ã‚’やらんã‹ã‚‰ã‚µãƒ¼ãƒãƒ¼ã¯ã‚‚ã£ã¨è»½ããªã‚“ã­ã‚“ã‘ã©ã€ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã®å–得範囲ãŒã¡ã‚‡ã£ã¨æ¸›ã‚‹ã§ã€‚"
reactionsBufferingDescription: "有効ã«ã—ãŸã‚‰ã€ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ä½œã‚‹ã¨ãã®ãƒ‘フォーマンスãŒã™ã£ã”ã„上ãŒã£ã¦ã€ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã¸ã®è² è·ãŒæ¸›ã‚‹ã§ã€‚代ã‚りã«ã€Redisã®ãƒ¡ãƒ¢ãƒªä½¿ç”¨ã¯å¢—ãˆã‚‹ã§ã€‚"
+ remoteNotesCleaning_description: "ã¤ã‘ã‚‹ã¨ã€å‚ç…§ã•れã¦ã¸ã‚“å¤ã„ãƒªãƒ¢ãƒ¼ãƒˆã®æŠ•ç¨¿ã‚’å®šæœŸçš„ã«ã‚¯ãƒªãƒ¼ãƒ³ã‚¢ãƒƒãƒ—ã—ã¦ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã®è‚¥å¤§åŒ–を抑ãˆã¦ãれるã§ã€‚"
inquiryUrl: "å•ã„åˆã‚ã›å…ˆURL"
inquiryUrlDescription: "サーãƒãƒ¼é‹å–¶è€…ã¸ã®ãŠå•ã„åˆã‚ã›ãƒ•ォームã®URLã‚„ã€é‹å–¶è€…ã®é€£çµ¡å…ˆç­‰ãŒè¨˜è¼‰ã•れãŸWebページã®URLを指定ã™ã‚‹ã§ã€‚"
openRegistration: "アカウントã®ä½œæˆã‚’オープンã«ã™ã‚‹"
openRegistrationWarning: "登録を解放ã™ã‚‹ã®ã¯ãƒªã‚¹ã‚¯ãŒä¼´ã†ã§ã€‚サーãƒãƒ¼ã‚’ã„ã£ã¤ã‚‚監視ã—ã¦ã€ãªã‚“ã‹èµ·ããŸã‚‰ã™ãã«å¯¾å¿œã§ãã‚‹ã‚“ã‚„ã£ãŸã‚‰ã€ã‚ªãƒ³ã«ã—ã¦ã‚‚ãˆãˆã¨æ€ã†ã€‚"
thisSettingWillAutomaticallyOffWhenModeratorsInactive: "一定期間モデレーターãŒãŠã‚‰ã‚“ã‹ã£ãŸã‚‰ã€ã‚¹ãƒ‘ムを防ããŸã‚ã«ã“ã®è¨­å®šã¯å‹æ‰‹ã«åˆ‡ã‚‰ã‚Œã‚‹ã§ã€‚"
+ deliverSuspendedSoftwareDescription: "脆弱性ã¨ã‹ã®ç†ç”±ã§ã€ã‚µãƒ¼ãƒãƒ¼ã®ã‚½ãƒ•トウェアã®åå‰ã¨ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ç¯„囲を決ã‚ã¦é…ä¿¡ã‚’æ­¢ã‚られるã§ã€‚ã“ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³æƒ…å ±ã¯ã‚µãƒ¼ãƒãƒ¼ãŒæä¾›ã—ãŸã‚‚ã®ã‚„ã‹ã‚‰ã€ä¿¡é ¼æ€§ã¯ä¿è¨¼ã•れã¸ã‚“。ãƒãƒ¼ã‚¸ãƒ§ãƒ³æŒ‡å®šã«ã¯ semver ã®ç¯„囲指定ãŒä½¿ãˆã‚‹ã­ã‚“ã‘ã©ã€>= 2024.3.1ã¨æŒ‡å®šã™ã‚‹ã¨ 2024.3.1-custom.0 ã¿ãŸã„ãªã‚«ã‚¹ã‚¿ãƒ ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãŒå«ã¾ã‚Œã¸ã‚“ã‹ã‚‰ã€>= 2024.3.1-0 ã¿ãŸã„ã« prerelease を指定ã™ã‚‹ã¨ãˆãˆã‹ã‚‚ã—れã¸ã‚“ãªã€‚"
+ singleUserMode_description: "ã“ã®ã‚µãƒ¼ãƒãƒ¼ã‚’使ã†ã¨ã‚‹ã‚“ãŒè‡ªåˆ†ã ã‘ãªã‚“ã‚„ã£ãŸã‚‰ã€ã“ã®ãƒ¢ãƒ¼ãƒ‰ã‚’有効ã«ã™ã‚‹ã¨å‹•作ãŒãˆãˆæ„Ÿã˜ã«ãªã‚‹ã§ã€‚"
+ signToActivityPubGet_description: "通常ã¯ã¤ã‘ã¨ã„ã¦ãªã€‚連åˆã®é€šä¿¡ã«é–¢ã‚ã‚‹å•題ãŒã‚ã‚‹ã‚“ã‚„ã£ãŸã‚‰ã€ç„¡åйã«ã™ã‚‹ã¨æ”¹å–„ã™ã‚‹ã‹ã‚‚ã—れã¸ã‚“ã‘ã©ã€é€†ã«ã‚µãƒ¼ãƒãƒ¼ã«ã‚ˆã£ã¦ã¯é€šä¿¡ãŒã§ãã‚“ããªã‚‹ã“ã¨ãŒã‚ã‚‹ã§ã€‚"
+ proxyRemoteFiles_description: "ã¤ã‘ã‚‹ã¨ã€ãƒªãƒ¢ãƒ¼ãƒˆã®ãƒ•ァイルをプロキシã—ã¦æä¾›ã™ã‚‹ã§ã€‚ç”»åƒã®ã‚µãƒ ãƒã‚¤ãƒ«ç”Ÿæˆã¨ã‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ—ライãƒã‚·ãƒ¼ä¿è­·ã«ãˆãˆãªã€‚"
+ allowExternalApRedirect_description: "ã¤ã‘ã‚‹ã¨ã€ä»–ã®ã‚µãƒ¼ãƒãƒ¼ãŒã†ã¡ã®ã‚µãƒ¼ãƒãƒ¼ã‚’通ã—ã¦ç¬¬ä¸‰è€…ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„を照会ã§ãるよã†ã«ãªã‚‹ã‚“ã‚„ã‘ã©ã€ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã®ãªã‚Šã™ã¾ã—ãŒç™ºç”Ÿã™ã‚‹ã‹ã‚‚ã—れã¸ã‚“。"
+ userGeneratedContentsVisibilityForVisitor_description: "モデレーションãŒè¡Œã届ãã«ãã„ä¸é©åˆ‡ãªãƒªãƒ¢ãƒ¼ãƒˆã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã¨ã‹ãŒã€ã†ã¡ã®ã‚µãƒ¼ãƒãƒ¼çµŒç”±ã§å›³ã‚‰ãšã‚‚インターãƒãƒƒãƒˆã«å…¬é–‹ã•れã¦ã¾ã†ã“ã¨ã«ã‚ˆã‚‹ãƒˆãƒ©ãƒ–ルを防止ã§ããŸã‚Šã™ã‚‹ã§ã€‚"
+ userGeneratedContentsVisibilityForVisitor_description2: "サーãƒãƒ¼ã§å—ã‘å–ã£ãŸãƒªãƒ¢ãƒ¼ãƒˆã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã‚’å«ã‚ã€ã‚µãƒ¼ãƒãƒ¼å†…ã®å…¨éƒ¨ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„を何ã§ã‚‚ã‹ã‚“ã§ã‚‚インターãƒãƒƒãƒˆã«å…¬é–‹ã™ã‚‹ã®ã¯ãƒªã‚¹ã‚¯ã‚’ä¼´ã†ã­ã‚“。特ã«ã€åˆ†æ•£åž‹ã®ç‰¹æ€§ã‚’知らん閲覧者ã«ã¨ã£ã¦ã¯ã€ãƒªãƒ¢ãƒ¼ãƒˆã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã‚„ã£ãŸã¨ã—ã¦ã‚‚サーãƒãƒ¼å†…ã§ä½œã‚‰ã‚ŒãŸã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã‚„ã¨èª¤èªã—ã¦ã¾ã†ã‹ã‚‚ã—れã¸ã‚“ã‹ã‚‰ã€æ³¨æ„ãŒå¿…è¦ã‚„ãªã€‚"
+ restartServerSetupWizardConfirm_title: "サーãƒãƒ¼ã®åˆæœŸè¨­å®šã‚¦ã‚£ã‚¶ãƒ¼ãƒ‰ã‚’やり直ã™ã‚“?"
+ restartServerSetupWizardConfirm_text: "ç¾åœ¨ã®ä¸€éƒ¨ã®è¨­å®šã¯ãƒªã‚»ãƒƒãƒˆã•れるã§ã€‚"
_accountMigration:
moveFrom: "別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«å¼•ã£è¶Šã™"
moveFromSub: "別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¸ã‚¨ã‚¤ãƒªã‚¢ã‚¹ã‚’作る"
@@ -1769,6 +1868,7 @@ _role:
descriptionOfIsExplorable: "オンã«ã—ãŸã‚‰ãƒ­ãƒ¼ãƒ«ã®é¢å­ä¸€è¦§ãŒã€Œã¿ã¤ã‘ã‚‹ã€ã§å…¬é–‹ã•れるã—ã€ãƒ­ãƒ¼ãƒ«ã®ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ãŒä½¿ãˆã‚‹ã‚ˆã†ã«ãªã‚‹ã§ã€‚"
displayOrder: "表示順"
descriptionOfDisplayOrder: "æ•°ãŒã§ã‹ã„ã»ã©ã€UI上ã§å…ˆã«è¡¨ç¤ºã•れるã§ã€‚"
+ preserveAssignmentOnMoveAccount_description: "ã¤ã‘ã‚‹ã¨ã€ã“ã®ãƒ­ãƒ¼ãƒ«ãŒã®ã£ã‹ã£ãŸã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒå¼•ã£è¶Šã—ãŸã¨ãã«ã€å¼•ã£è¶Šã—先アカウントã«ã‚‚ã“ã®ãƒ­ãƒ¼ãƒ«ãŒã®ã£ã‹ã‚‹ã‚ˆã†ã«ãªã‚‹ã§ã€‚"
canEditMembersByModerator: "モデレーターãŒãƒ¡ãƒ³ãƒãƒ¼ã„ã˜ã‚‹ã®ã‚’許ã™"
descriptionOfCanEditMembersByModerator: "オンã«ã™ã‚‹ã¨ã€ç®¡ç†è€…ã ã‘ã‚„ãªãã¦ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚¿ãƒ¼ã‚‚ã“ã®ãƒ­ãƒ¼ãƒ«ã«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’入れãŸã‚ŠæŠœã„ãŸã‚Šã§ãã‚‹ã§ã€‚オフã«ã™ã‚‹ã¨ç®¡ç†è€…ã ã‘ã—ã‹ã‚„れã¸ã‚“ããªã‚‹ã§ã€‚"
priority: "優先度"
@@ -1809,6 +1909,8 @@ _role:
canImportFollowing: "フォローã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆã‚’許ã™"
canImportMuting: "ミュートã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆã‚’許ã™"
canImportUserLists: "リストã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆã‚’許ã™"
+ uploadableFileTypes_caption: "MIMEタイプを指定ã—ã¦ã‚„。改行ã§åŒºåˆ‡ã£ã¦è¤‡æ•°æŒ‡å®šã‚‚ã§ãã‚‹ã—ã€ã‚¢ã‚¹ã‚¿ãƒªã‚¹ã‚¯(*)ã§ãƒ¯ã‚¤ãƒ«ãƒ‰ã‚«ãƒ¼ãƒ‰æŒ‡å®šã‚‚ã§ãã‚‹ã§ã€‚(例: image/*)"
+ uploadableFileTypes_caption2: "ファイルã«ã‚ˆã£ã¦ã¯ç¨®åˆ¥ãŒã‚ã‹ã‚‰ã‚“ã“ã¨ã‚‚ã‚ã‚‹ã§ã€‚ããªã„ãªãƒ•ァイルを許å¯ã™ã‚‹ã‚“ã‚„ã£ãŸã‚‰ {x} を指定ã«è¿½åŠ ã—ã¦ãªã€‚"
_condition:
roleAssignedTo: "マニュアルロールã«ã‚¢ã‚µã‚¤ãƒ³æ¸ˆã¿"
isLocal: "ローカルユーザー"
@@ -2008,7 +2110,7 @@ _theme:
navIndicator: "サイドãƒãƒ¼ã®ã‚¤ãƒ³ã‚¸ã‚±ãƒ¼ã‚¿ãƒ¼"
link: "リンク"
hashtag: "ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°"
- mention: "メンション"
+ mention: "ã‚ã‚“ãŸå®›ã¦"
mentionMe: "ã†ã¡å®›ã¦ã®ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³"
renote: "Renote"
modalBg: "モーダルã®èƒŒæ™¯"
@@ -2277,6 +2379,8 @@ _visibility:
disableFederation: "連åˆãªã—"
disableFederationDescription: "他サーãƒãƒ¼ã¸ã¯é€ã‚‰ã‚“ã¨ãã‚"
_postForm:
+ quitInspiteOfThereAreUnuploadedFilesConfirm: "アップロードã•れã¦ã¸ã‚“ファイルãŒã‚ã‚‹ã‚“ã‚„ã‘ã©ã€ã»ã‹ã—ã¦ãƒ•ォームを閉ã˜ã¦ã‚‚ãˆãˆã‚“ã‹ï¼Ÿ"
+ uploaderTip: "ファイルã¯ã¾ã ã‚¢ãƒƒãƒ—ロードã•れã¦ã¸ã‚“ã§ã€‚ファイルã®ãƒ¡ãƒ‹ãƒ¥ãƒ¼ã‹ã‚‰ã€ãƒªãƒãƒ¼ãƒ ã¨ã‹ç”»åƒã®ã‚¯ãƒ­ãƒƒãƒ—ã€ã‚¦ã‚©ãƒ¼ã‚¿ãƒ¼ãƒžãƒ¼ã‚¯ã‚’ã®ã£ã‘ã‚‹ã€åœ§ç¸®ã™ã‚‹ã‹ã©ã†ã‹ãªã‚“ã‹ã‚’設定ã§ãã‚‹ã§ã€‚ファイルã¯ãƒŽãƒ¼ãƒˆã‚’投稿ã™ã‚‹ã¨ãã«è‡ªå‹•ã§ã‚¢ãƒƒãƒ—ロードã•れるã§ã€‚"
replyPlaceholder: "ã“ã®ãƒŽãƒ¼ãƒˆã«è¿”ä¿¡..."
quotePlaceholder: "ã“ã®ãƒŽãƒ¼ãƒˆã‚’引用..."
channelPlaceholder: "ãƒãƒ£ãƒ³ãƒãƒ«ã«æŠ•稿..."
@@ -2428,6 +2532,7 @@ _notification:
newNote: "ã•ã‚‰ã®æŠ•ç¨¿"
unreadAntennaNote: "アンテナ {name}"
roleAssigned: "ロールãŒä»˜ä¸Žã•れãŸã§"
+ chatRoomInvitationReceived: "ãƒãƒ£ãƒƒãƒˆãƒ«ãƒ¼ãƒ ã¸æ‹›å¾…ã•れãŸã§"
emptyPushNotificationMessage: "ãƒ—ãƒƒã‚·ãƒ¥é€šçŸ¥ã®æ›´æ–°ã‚’ã—ã¨ã„ãŸã§"
achievementEarned: "実績をç²å¾—ã—ã¨ã‚‹ã§"
testNotification: "通知テスト"
@@ -2447,7 +2552,7 @@ _notification:
all: "ã™ã¹ã¦"
note: "ã‚ã‚“ãŸã‚‰ã®æ–°è¦æŠ•稿"
follow: "フォロー"
- mention: "メンション"
+ mention: "ã‚ã‚“ãŸå®›ã¦"
reply: "リプライ"
renote: "リノート"
quote: "引用"
@@ -2617,7 +2722,7 @@ _externalResourceInstaller:
_errors:
_invalidParams:
title: ""
- description: ""
+ description: "外部サイトã‹ã‚‰ãƒ‡ãƒ¼ã‚¿ã‚’æŒã£ã¦ãã‚‹ã®ã«æ¬²ã—ã„æƒ…å ±ãŒè¶³ã‚‰ã¸ã‚“ã¿ãŸã„ã‚„ã‚。URLã¯åˆã£ã¨ã‚‹ï¼Ÿ"
_resourceTypeNotSupported:
title: ""
description: ""
@@ -2647,8 +2752,12 @@ _dataSaver:
_avatar:
title: "アイコンã®çµµ"
description: "アイコン画åƒã®ã‚¢ãƒ‹ãƒ¡ãŒæ­¢ã¾ã‚‹ã§ã€‚普通ã®ç”»åƒã‚ˆã‚Šã‚‚データé‡ãŒã§ã‹ã„ã‹ã‚‰ã€ã‚‚ã£ã¨é€šä¿¡é‡ã‚’節約ã§ãã‚‹ã­ã‚“。"
+ _urlPreviewThumbnail:
+ description: "URLプレビューã®ã‚µãƒ ãƒã‚¤ãƒ«ç”»åƒãŒèª­ã¿è¾¼ã¾ã‚Œã¸ã‚“ããªã‚‹ã§ã€‚"
+ _disableUrlPreview:
+ description: "URLプレビュー機能を切るã§ã€‚サムãƒã‚¤ãƒ«ç”»åƒã ã‘ã¨é•ã£ã¦ã€ãƒªãƒ³ã‚¯å…ˆã®æƒ…å ±ã®èª­ã¿è¾¼ã¿è‡ªä½“を削減ã§ãã‚‹ã§ã€‚"
_code:
- title: "コードãƒã‚¤ãƒ©ã‚¤ãƒˆ"
+ title: "コードãƒã‚¤ãƒ©ã‚¤ãƒˆã¯è¡¨ç¤ºã›ã‚“ã§ãˆãˆ"
description: "MFMã¨ã‹ã§ã‚³ãƒ¼ãƒ‰ãƒã‚¤ãƒ©ã‚¤ãƒˆè¨˜æ³•ãŒä½¿ã‚れã¦ã‚‹ã¨ãã€ã‚¿ãƒƒãƒ—ã™ã‚‹ã¾ã§èª­ã¿è¾¼ã¾ã‚Œã¸ã‚“ããªã‚‹ã§ã€‚コードãƒã‚¤ãƒ©ã‚¤ãƒˆã§ã¯ãƒã‚¤ãƒ©ã‚¤ãƒˆã™ã‚‹è¨€èªžã”ã¨ã«ãã®æ±ºã‚ã¦ã‚‹ãƒ•ァイルを読む必è¦ã¯ã‚ã‚“ã­ã‚“ãªã€‚ã‘ã©ãªã€ãれã¯è‡ªå‹•ã§èª­ã¿è¾¼ã¾ã‚Œãªããªã‚‹ã‹ã‚‰ã€é€šä¿¡é‡ã‚’å°‘ãªãã§ãã‚‹ã“ã¨ãŒã§ãã‚‹ã­ã‚“。"
_hemisphere:
N: "北åŠçƒ"
@@ -2704,6 +2813,7 @@ _offlineScreen:
_urlPreviewSetting:
title: "URLプレビューã®è¨­å®š"
enable: "URLプレビューを有効ã«ã™ã‚‹"
+ allowRedirectDescription: "入力ã•れãŸURLãŒãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆã•れるã¨ãã€ãã®ãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆå…ˆã‚’ãŸã©ã£ã¦ãƒ—レビューを表示ã™ã‚‹ã‹ã©ã†ã‹ã‚’設定ã§ãã‚‹ã§ã€‚無効ã«ã™ã‚‹ã¨ã‚µãƒ¼ãƒãƒ¼ãƒªã‚½ãƒ¼ã‚¹ã‚’節約ã§ãã‚‹ã‚“ã‚„ã‘ã©ã€ãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆå…ˆã®å†…容ã¯è¡¨ç¤ºã•れã¸ã‚“ããªã‚‹ã§ã€‚"
timeout: "プレビューå–得時ã®ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆ(ms)"
timeoutDescription: "プレビューå–å¾—ã®æ‰€è¦æ™‚é–“ãŒã“ã®å€¤ã‚’è¶…ãˆãŸå ´åˆã€ãƒ—レビューã¯ç”Ÿæˆã•れã¸ã‚“ã§ã€‚"
maximumContentLength: "Content-Lengthã®æœ€å¤§å€¤(byte)"
@@ -2848,8 +2958,57 @@ _search:
searchScopeAll: "ã¿ã‚“ãª"
searchScopeLocal: "ローカル"
searchScopeUser: "ユーザー指定"
+ pleaseEnterServerHost: "サーãƒãƒ¼ã®ãƒ›ã‚¹ãƒˆã¯ã©ãªã„ã™ã‚‹ã‚“?"
+ pleaseSelectUser: "ユーザーをé¸ã‚“ã§ã‚„"
+_serverSetupWizard:
+ installCompleted: "Misskeyã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ãŒçµ‚ã‚ã£ãŸã§ï¼"
+ firstCreateAccount: "最åˆã¯ã€ç®¡ç†è€…アカウントを作æˆã—よã‹ã€‚"
+ accountCreated: "管ç†è€…アカウントãŒã§ããŸã§ï¼"
+ youCanEasilyConfigureOptimalServerSettingsWithThisWizard: "ã“ã®ã‚¦ã‚£ã‚¶ãƒ¼ãƒ‰ã§ç°¡å˜ã«ãˆãˆæ„Ÿã˜ã®ã‚µãƒ¼ãƒãƒ¼ã®è¨­å®šãŒã§ãã‚‹ã§ã€‚"
+ settingsYouMakeHereCanBeChangedLater: "ã“ã“ã§ã®è¨­å®šã¯ã€ã‚ã¨ã‹ã‚‰ã§ã‚‚変ãˆã‚‰ã‚Œã‚‹ã§ã€‚"
+ howWillYouUseMisskey: "Misskeyã‚’ã©ã‚“ãªæ„Ÿã˜ã«ä½¿ã†ã‚“?"
+ _use:
+ single_youCanCreateMultipleAccounts: "ãŠä¸€äººæ§˜ã‚µãƒ¼ãƒãƒ¼ã¨ã—ã¦é‹ç”¨ã™ã‚‹ã¨ã—ã¦ã‚‚ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯å¿…è¦ã«å¿œã˜ã¦è¤‡æ•°ä½œã‚Œã‚‹ã§ã€‚"
+ openServerAdvice: "ä¸ç‰¹å®šå¤šæ•°ã®åˆ©ç”¨è€…ã‚’å—ã‘入れるã«ã¯ç›¸å¿œã®ãƒªã‚¹ã‚¯ãŒã‚ã‚‹ã§ã€‚トラブルã«å¯¾å‡¦ã§ãるよã†ã€ã¡ã‚ƒã‚“ã¨ã—ãŸãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ä½“制ã§é‹å–¶ã—ã„や。"
+ openServerAntiSpamAdvice: "ã†ã¡ã®ã‚µãƒ¼ãƒãƒ¼ãŒã‚¹ãƒ‘ムã®è¸ã¿å°ã«ãªã‚‰ã¸ã‚“よã†ã«ã€reCAPTCHAã¨ã‹ã®ã‚¢ãƒ³ãƒãƒœãƒƒãƒˆæ©Ÿèƒ½ã‚’使ã†ã€ã¿ãŸã„ãªã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£å¯¾ç­–ã‚‚ã—ã£ã‹ã‚Šè€ƒãˆã¦ãªã€‚"
+ howManyUsersDoYouExpect: "ã©ã‚Œãらã„ã®äººæ•°ã‚’考ãˆã¨ã‚‹ã‚“?"
+ largeScaleServerAdvice: "å¤§è¦æ¨¡ãªã‚µãƒ¼ãƒãƒ¼ã‚„ã£ãŸã‚‰ã€ãƒ­ãƒ¼ãƒ‰ãƒãƒ©ãƒ³ã‚·ãƒ³ã‚°ã¨ã‹ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã®ãƒ¬ãƒ—リケーションã¿ãŸã„ãªã€é«˜åº¦ãªã‚¤ãƒ³ãƒ•ラストラクãƒãƒ£ãƒ¼ã®çŸ¥è­˜ãŒå¿…è¦ã«ãªã‚‹ã‹ã‚‚ã—れã¸ã‚“ã‚。"
+ doYouConnectToFediverse: "Fediverseã¨æŽ¥ç¶šã™ã‚‹ã‚“ã‚„ã£ã‘?"
+ doYouConnectToFediverse_description1: "分散型サーãƒãƒ¼ã§ã§ããŸãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯(Fediverse)ã«ç¹‹ã’ã‚‹ã¨ã€ä»–ã®ã‚µãƒ¼ãƒãƒ¼ã¨ç›¸äº’ã«ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã®ã‚„りå–りãŒã§ãるよã†ã«ãªã‚‹ã§ã€‚"
+ doYouConnectToFediverse_description2: "Fediverseã¨æŽ¥ç¶šã™ã‚‹ã“ã¨ã¯ã€Œé€£åˆã€ã¨ã‚‚呼ã°ã‚Œã‚‹ãªã€‚"
+ youCanConfigureMoreFederationSettingsLater: "連åˆã—ã¦ã‚‚ãˆãˆã‚µãƒ¼ãƒãƒ¼ã®æŒ‡å®šã¨ã‹ã€é«˜åº¦ãªè¨­å®šã‚‚後ã§ã§ãã‚‹ã§ã€‚"
+ remoteContentsCleaning_description: "連åˆã™ã‚‹ã¨ã€ãŽã‚‡ã†ã•んコンテンツをå—ã‘å–り続ã‘ã‚‹ã“ã¨ã«ãªã‚‹ã­ã‚“。自動クリーニングをã¤ã‘ã‚‹ã¨ã€å‚ç…§ã•れã¦ãªã„å¤ã„コンテンツを自動ã§ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰ã»ã‹ã—ã¦ã€ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã‚’節約ã§ãã‚‹ã§ã€‚"
+ adminInfo_description: "å•ã„åˆã‚ã›ã‚’å—ã‘付ã‘ã‚‹ã®ã«ä½¿ã†ç®¡ç†è€…情報を設定ã—よã‹ã€‚"
+ adminInfo_mustBeFilled: "オープンサーãƒãƒ¼ã€ã‚‚ã—ãã¯é€£åˆã‚’入れã¨ã‚‹ã‚“ã‚„ã£ãŸã‚‰å¿…ãšå…¥åŠ›ã›ãªã‚ã‹ã‚“ã§ã€‚"
+ followingSettingsAreRecommended: "ã“ã†ã„ã†è¨­å®šãŒãˆãˆã‹ã‚‚ãª"
+ settingsCompleted: "設定ãŒçµ‚ã‚ã£ãŸã§ï¼"
+ settingsCompleted_description: "ãŠç–²ã‚Œã•ん。準備ãŒã§ããŸã‹ã‚‰ã€ã•ã£ããサーãƒãƒ¼ã‚’使ã„å§‹ã‚られるã§ã€‚"
+ settingsCompleted_description2: "ç´°ã‹ã„サーãƒãƒ¼è¨­å®šã¯ã€ã€Œã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ãƒ‘ãƒãƒ«ã€ã‚’見ã¦ã¿ã¦ãªã€‚"
+ _donationRequest:
+ text1: "Misskeyã¯æœ‰å¿—ã§é–‹ç™ºã•れã¨ã‚‹ç„¡æ–™ã®ã‚½ãƒ•トウェアやã§ã€‚"
+ text2: "今後も開発を続ã‘られるよã†ã«ã€ã‚ˆã‹ã£ãŸã‚‰ãœã²ã‚«ãƒ³ãƒ‘ã‚’ãŠé¡˜ã„ã™ã‚‹ã‚。"
+ text3: "支æ´è€…å‘ã‘特典もã‚ã‚‹ã§ï¼"
+_uploader:
+ abortConfirm: "アップロードã•れã¦ã¸ã‚“ファイルãŒã‚ã‚‹ã‚“ã‚„ã‘ã©ã€ã‚„ã‚ã¦ã‚‚ãˆãˆã‚“ã‹ï¼Ÿ"
+ doneConfirm: "アップロードã•れã¦ã¸ã‚“ファイルãŒã‚ã‚‹ã‚“ã‚„ã‘ã©ã€å®Œäº†ã—ã¦ã‚‚ãˆãˆã‚“ã‹ï¼Ÿ"
+ maxFileSizeIsX: "アップロードã§ãるファイルサイズã¯{x}ã¾ã§ã‚„ã§ã€‚"
+ tip: "ファイルã¯ã¾ã ã‚¢ãƒƒãƒ—ロードã•れã¦ã¸ã‚“ã§ã€‚ã“ã®ãƒ€ã‚¤ã‚¢ãƒ­ã‚°ã§ã€ã‚¢ãƒƒãƒ—ロードã™ã‚‹å‰ã«ç¢ºèªãƒ»ãƒªãƒãƒ¼ãƒ ãƒ»åœ§ç¸®ãƒ»ã‚¯ãƒ­ãƒƒãƒ”ングã¨ã‹ã‚’ã§ãã‚‹ã§ã€‚準備ãŒå‡ºæ¥ãŸã‚‰ã€ã€Œã‚¢ãƒƒãƒ—ロードã€ãƒœã‚¿ãƒ³ã‚’押ã—ã¦ã‚¢ãƒƒãƒ—ロードã—ã¦ãªã€‚"
+_clientPerformanceIssueTip:
+ makeSureDisabledAdBlocker: "アドブロッカーを切ã£ã¦ã¿ã¦ã‚„"
+ makeSureDisabledAdBlocker_description: "アドブロッカーã¯ãƒ‘フォーマンスã«å½±éŸ¿ãŒã‚ã‚‹ã‹ã‚‚ã—れã¸ã‚“。OSã®æ©Ÿèƒ½ã¨ã‹ãƒ–ãƒ©ã‚¦ã‚¶ã®æ©Ÿèƒ½ãƒ»ã‚¢ãƒ‰ã‚ªãƒ³ã¨ã‹ã§ã‚¢ãƒ‰ãƒ–ãƒ­ãƒƒã‚«ãƒ¼ãŒæœ‰åйã«ãªã£ã¦ãªã„ã‹ç¢ºèªã—ã¦ã‚„。"
+ makeSureDisabledCustomCss: "カスタムCSSを無効ã«ã—ã¦ã¿ã¦ã‚„"
+ makeSureDisabledCustomCss_description: "スタイルを上書ãã™ã‚‹ã¨ãƒ‘フォーマンスã«å½±éŸ¿ãŒã‚ã‚‹ã‹ã‚‚ã—れã¸ã‚“。カスタムCSSã¨ã‹ã€ã‚¹ã‚¿ã‚¤ãƒ«ã‚’上書ãã™ã‚‹æ‹¡å¼µæ©Ÿèƒ½ãŒæœ‰åйã«ãªã£ã¦ãªã„ã‹ç¢ºèªã—ã¦ã‚„。"
+ makeSureDisabledAddons: "拡張機能を無効ã«ã—ã¦ã¿ã¦ã‚„"
+ makeSureDisabledAddons_description: "ãªã‚“ã‹ã®æ‹¡å¼µæ©Ÿèƒ½ãŒã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆã®å‹•作ã«ã¡ã‚‡ã£ã‹ã„ã‚’ã‹ã‘ã¦ãƒ‘フォーマンスã«å½±éŸ¿ã‚’与ãˆã¦ã‚‹ã‹ã‚‚ã—れã¸ã‚“ã€‚ãƒ–ãƒ©ã‚¦ã‚¶ã®æ‹¡å¼µæ©Ÿèƒ½ã‚’無効ã«ã—ã¦è‰¯ããªã‚‹ã‹ç¢ºèªã—ã¦ã‚„。"
+_clip:
+ tip: "クリップã¯ã€ãƒŽãƒ¼ãƒˆã‚’ã¾ã¨ã‚られる機能やã§ã€‚"
+_userLists:
+ tip: "好ããªãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’å«ã‚€ãƒªã‚¹ãƒˆã‚’作れるã­ã‚“。作ã£ãŸãƒªã‚¹ãƒˆã¯ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã¨ã—ã¦è¡¨ç¤ºã§ãã‚‹ã§ã€‚"
_watermarkEditor:
+ tip: "ç”»åƒã«ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆæƒ…å ±ã¨ã‹ã®ã‚¦ã‚©ãƒ¼ã‚¿ãƒ¼ãƒžãƒ¼ã‚¯ã‚’ã®ã£ã‘られるã§ã€‚"
+ quitWithoutSaveConfirm: "ä¿å­˜ã›ãšã«çµ‚ã‚ã£ã¦ã‚‚ãˆãˆã‚“ã‹ï¼Ÿ"
driveFileTypeWarn: "ã“ã®ãƒ•ァイルã¯å¯¾å¿œã—ã¨ã‚‰ã¸ã‚“"
+ driveFileTypeWarnDescription: "ç”»åƒãƒ•ァイルをé¸ã‚“ã§ã‚„"
opacity: "ä¸é€æ˜Žåº¦"
scale: "大ãã•"
text: "テキスト"
@@ -2858,3 +3017,18 @@ _watermarkEditor:
image: "ç”»åƒ"
advanced: "高度"
angle: "角度"
+_imageEffector:
+ discardChangesConfirm: "変更をã›ã‚“ã§çµ‚ã‚ã‚‹ã‹ï¼Ÿ"
+ _fxProps:
+ angle: "角度"
+ scale: "大ãã•"
+ size: "大ãã•"
+ color: "色"
+ opacity: "ä¸é€æ˜Žåº¦"
+ lightness: "明るã•"
+_drafts:
+ cannotCreateDraftAnymore: "下書ãã¯ã“れ以上ã¯ä½œã‚Œã¸ã‚“ãªã€‚"
+ cannotCreateDraft: "ã“ã®å†…容ã§ä¸‹æ›¸ãã¯ä½œã‚Œã¸ã‚“ãªã€‚"
+ delete: "下書ãã‚’ã»ã‹ã™"
+ deleteAreYouSure: "下書ãã‚’ã»ã‹ã—ã¦ã‚‚ãˆãˆã‹ï¼Ÿ"
+ noDrafts: "下書ãã¯ã‚らã¸ã‚“"
diff --git a/locales/kn-IN.yml b/locales/kn-IN.yml
index 222599572a..cdd9a5b2ae 100644
--- a/locales/kn-IN.yml
+++ b/locales/kn-IN.yml
@@ -44,6 +44,7 @@ showMore: "ಇನà³à²¨à²·à³à²Ÿà³ ನೋಡà³"
youGotNewFollower: "ಹಿಂಬಾಲಿಸಿದರà³"
receiveFollowRequest: "ಹಿಂಬಾಲನೆ ವಿನಂತಿ ಬಂದಿದೆ"
followRequestAccepted: "ಹಿಂಬಾಲನೆ ವಿನಂತಿ ಸà³à²µà³€à²•ರಿಸಲಾಯಿತà³"
+mention: "ಹೆಸರಿಸಿದ"
mentions: "ಹೆಸರಿಸಿದ"
directNotes: "ನೇರ ಟಿಪà³à²ªà²£à²¿à²—ಳà³"
importAndExport: "ಆಮದà³/ರಫà³à²¤à³"
@@ -65,6 +66,9 @@ replies: "ಉತà³à²¤à²°à²¿à²¸à³"
_email:
_follow:
title: "ಹಿಂಬಾಲಿಸಿದರà³"
+_theme:
+ keys:
+ mention: "ಹೆಸರಿಸಿದ"
_sfx:
notification: "ಅಧಿಸೂಚನೆಗಳà³"
_widgets:
@@ -73,11 +77,14 @@ _widgets:
timeline: "ಸಮಯಸಾಲà³"
_cw:
show: "ಇನà³à²¨à²·à³à²Ÿà³ ನೋಡà³"
+_visibility:
+ specified: "ನೇರ ಟಿಪà³à²ªà²£à²¿à²—ಳà³"
_profile:
username: "ಬಳಕೆಹೆಸರà³"
_notification:
youWereFollowed: "ಹಿಂಬಾಲಿಸಿದರà³"
_types:
+ mention: "ಹೆಸರಿಸಿದ"
login: "ಪà³à²°à²µà³‡à²¶"
_actions:
reply: "ಉತà³à²¤à²°à²¿à²¸à³"
@@ -86,3 +93,4 @@ _deck:
notifications: "ಅಧಿಸೂಚನೆಗಳà³"
tl: "ಸಮಯಸಾಲà³"
mentions: "ಹೆಸರಿಸಿದ"
+ direct: "ನೇರ ಟಿಪà³à²ªà²£à²¿à²—ಳà³"
diff --git a/locales/ko-GS.yml b/locales/ko-GS.yml
index 554eb42694..59fe63be6c 100644
--- a/locales/ko-GS.yml
+++ b/locales/ko-GS.yml
@@ -745,7 +745,7 @@ _menuDisplay:
_theme:
description: "설멩"
keys:
- mention: "멘션"
+ mention: "받언 멘션"
renote: "리노트"
_sfx:
note: "새 노트"
@@ -775,6 +775,7 @@ _cw:
_visibility:
home: "ëœë¨¸ë¦¬"
followers: "팔로워"
+ specified: "쪽지 서기"
_postForm:
_placeholders:
e: "옇다 서 주ì´ì†Œ"
@@ -809,7 +810,7 @@ _notification:
newNote: "새 걸"
_types:
follow: "팔로잉"
- mention: "멘션"
+ mention: "받언 멘션"
renote: "리노트"
quote: "따오기"
reaction: "반엉"
@@ -824,6 +825,7 @@ _deck:
antenna: "안테나"
list: "리스트"
mentions: "받언 멘션"
+ direct: "쪽지 서기"
_webhookSettings:
name: "ì´ëŸ¼"
_abuseReport:
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index 00a470a718..8809d3b4d3 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -1054,6 +1054,7 @@ permissionDeniedError: "ìž‘ì—…ì´ ê±°ë¶€ë˜ì—ˆìŠµë‹ˆë‹¤"
permissionDeniedErrorDescription: "ì´ ìž‘ì—…ì„ ìˆ˜í–‰í•  ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤."
preset: "프리셋"
selectFromPresets: "프리셋ì—서 ì„ íƒ"
+custom: "커스텀"
achievements: "ë„ì „ 과제"
gotInvalidResponseError: "ì„œë²„ì˜ ì‘ë‹µì´ ì˜¬ë°”ë¥´ì§€ 않습니다"
gotInvalidResponseErrorDescription: " 서버가 다운ë˜ì—ˆê±°ë‚˜ ì ê²€ì¤‘ì¼ ê°€ëŠ¥ì„±ì´ ìžˆìŠµë‹ˆë‹¤. ìž ì‹œí›„ì— ë‹¤ì‹œ 시ë„í•´ 주십시오."
@@ -1092,6 +1093,7 @@ prohibitedWordsDescription2: "공백으로 구분하면 AND ì§€ì •ì´ ë˜ë©°, í‚
hiddenTags: "숨긴 해시태그"
hiddenTagsDescription: "설정한 태그를 íŠ¸ë Œë“œì— í‘œì‹œí•˜ì§€ 않ë„ë¡ í•©ë‹ˆë‹¤. 줄 바꿈으로 하나씩 나눠서 설정할 수 있습니다."
notesSearchNotAvailable: "노트 ê²€ìƒ‰ì„ ì´ìš©í•˜ì‹¤ 수 없습니다."
+usersSearchNotAvailable: "유저 ê²€ìƒ‰ì„ ì´ìš©í•˜ì‹¤ 수 없습니다."
license: "ë¼ì´ì„ ìФ"
unfavoriteConfirm: "ì¦ê²¨ì°¾ê¸°ë¥¼ 해제하시겠습니까?"
myClips: "ë‚´ í´ë¦½"
@@ -1243,7 +1245,7 @@ releaseToRefresh: "놓아서 새로고침"
refreshing: "새로고침 중"
pullDownToRefresh: "아래로 내려서 새로고침"
useGroupedNotifications: "ì•Œë¦¼ì„ ê·¸ë£¹í™”í•˜ê³  표시"
-signupPendingError: "ë©”ì¼ ì£¼ì†Œ 확ì¸ì¤‘ì— ë¬¸ì œê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤. ë§í¬ì˜ ìœ íš¨ê¸°ê°„ì´ ì§€ë‚¬ì„ ê°€ëŠ¥ì„±ì´ ìžˆìŠµë‹ˆë‹¤."
+emailVerificationFailedError: "ë©”ì¼ ì£¼ì†Œ 확ì¸ì— 실패했습니다. 확ì¸ì— 필요한 URLì˜ ìœ íš¨ê¸°ê°„ì´ ì§€ë‚¬ì„ ê°€ëŠ¥ì„±ì´ ìžˆìŠµë‹ˆë‹¤."
cwNotationRequired: "'ë‚´ìš©ì„ ìˆ¨ê¸°ê¸°'를 ì²´í¬í•œ 경우 주ì„ì„ ì¨ì•¼ 합니다."
doReaction: "리액션 추가"
code: "문ìžì—´"
@@ -1368,6 +1370,13 @@ redisplayAllTips: "모든 'íŒê³¼ 유용한 ì •ë³´'를 재표시"
hideAllTips: "모든 'íŒê³¼ 유용한 ì •ë³´'를 비표시"
defaultImageCompressionLevel: "기본 ì´ë¯¸ì§€ ì••ì¶• ì •ë„"
defaultImageCompressionLevel_description: "낮추면 í™”ì§ˆì„ ìœ ì§€í•©ë‹ˆë‹¤ë§Œ íŒŒì¼ í¬ê¸°ëŠ” ì¦ê°€í•©ë‹ˆë‹¤. <br>높ì´ë©´ íŒŒì¼ í¬ê¸°ë¥¼ ì¤„ì¼ ìˆ˜ 있습니다만 í™”ì§ˆì€ ì €í•˜ë©ë‹ˆë‹¤."
+inMinutes: "ë¶„"
+inDays: "ì¼"
+safeModeEnabled: "세ì´í”„ 모드가 활성화ë¼ìžˆìŠµë‹ˆë‹¤"
+pluginsAreDisabledBecauseSafeMode: "세ì´í”„ 모드가 활성화ë¼ìžˆê¸°ì— 플러그ì¸ì€ ì „ë¶€ 비활성화ë©ë‹ˆë‹¤."
+customCssIsDisabledBecauseSafeMode: "세ì´í”„ 모드가 활성화ë¼ìžˆê¸°ì— 커스텀 CSS는 ì ìš©ë˜ì§€ 않습니다."
+themeIsDefaultBecauseSafeMode: "세ì´í”„ 모드가 활성화ë¼ìžˆëŠ” ë™ì•ˆì—는 기본 테마가 사용ë©ë‹ˆë‹¤. 세ì´í”„ 모드를 ë„ë©´ ì›ëž˜ëŒ€ë¡œ ëŒì•„옵니다."
+thankYouForTestingBeta: "베타 ë²„ì „ì˜ ê²€ì¦ì— 협력해 주셔서 ê°ì‚¬í•©ë‹ˆë‹¤!"
_order:
newest: "최신 순"
oldest: "ì˜¤ëž˜ëœ ìˆœ"
@@ -1459,6 +1468,7 @@ _settings:
contentsUpdateFrequency_description2: "실시간 모드가 켜져 ìžˆì„ ë•ŒëŠ” ì´ ì„¤ì •ê³¼ ìƒê´€ì—†ì´ 실시간으로 콘í…츠가 ì—…ë°ì´íЏë©ë‹ˆë‹¤."
showUrlPreview: "URL 미리보기 표시"
showAvailableReactionsFirstInNote: "ì´ìš© 가능한 ë¦¬ì•¡ì…˜ì„ ì„ ë‘로 표시"
+ showPageTabBarBottom: "페ì´ì§€ì˜ 탭 바를 ì•„ëž˜ìª½ì— í‘œì‹œ"
_chat:
showSenderName: "ë°œì‹ ìž ì´ë¦„ 표시"
sendOnEnter: "엔터로 보내기"
@@ -1632,6 +1642,10 @@ _serverSettings:
fanoutTimelineDbFallback: "ë°ì´í„°ë² ì´ìŠ¤ë¥¼ 예비로 사용하기"
fanoutTimelineDbFallbackDescription: "활성화하면 타임ë¼ì¸ì˜ ìºì‹œë˜ì–´ 있지 ì•Šì€ ë¶€ë¶„ì— ëŒ€í•´ DBì— ì§ˆì˜í•˜ì—¬ 정보를 가져옵니다. 비활성화하면 ì´ë¥¼ 실행하지 않ìŒìœ¼ë¡œì¨ ì„œë²„ì˜ ë¶€í•˜ë¥¼ ì¤„ì¼ ìˆ˜ 있지만, 타임ë¼ì¸ì—서 가져올 수 있는 게시물 범위가 한정ë©ë‹ˆë‹¤."
reactionsBufferingDescription: "활성화 한 경우, 리액션 작성 í¼í¬ë¨¼ìŠ¤ê°€ ëŒ€í­ í–¥ìƒë˜ì–´ DBì˜ ë¶€í•˜ë¥¼ ì¤„ì¼ ìˆ˜ 있으나, Redisì˜ ë©”ëª¨ë¦¬ ì‚¬ìš©ëŸ‰ì´ ë§Žì•„ì§‘ë‹ˆë‹¤."
+ remoteNotesCleaning: "리모트 서버 노트 ìžë™ 정리 "
+ remoteNotesCleaning_description: "ë” ì´ìƒ 사용ë˜ì§€ 않는 ì˜¤ëž˜ëœ ë¦¬ëª¨íŠ¸ 노트를 정기ì ìœ¼ë¡œ 정리하여, ë°ì´í„° ë² ì´ìŠ¤ì˜ ì‚¬ìš©ëŸ‰ì„ ì ˆì•½í•  수 있습니다."
+ remoteNotesCleaningMaxProcessingDuration: "리모트 노트 ìžë™ 정리 최대 실행 시간"
+ remoteNotesCleaningExpiryDaysForEachNotes: "리모트 노트 저장 최소 ì¼ìˆ˜"
inquiryUrl: "문ì˜ì²˜ URL"
inquiryUrlDescription: "서버 ìš´ì˜ìžì—게 보내는 ë¬¸ì˜ ì–‘ì‹ì˜ URLì´ë‚˜ ìš´ì˜ìžì˜ ì—°ë½ì²˜ ë“±ì´ ì ížŒ 웹 페ì´ì§€ì˜ URLì„ ì„¤ì •í•©ë‹ˆë‹¤."
openRegistration: "íšŒì› ê°€ìž…ì„ í™œì„±í™” 하기"
@@ -1650,6 +1664,11 @@ _serverSettings:
userGeneratedContentsVisibilityForVisitor: "비ì´ìš©ìžì— 대한 유저 작성 콘í…ì¸ ì˜ ê³µê°œ 범위"
userGeneratedContentsVisibilityForVisitor_description: "ì¡°ì •ì„ í•˜ê¸° 힘든 ë¶€ì ì ˆí•œ 리모트 콘í…츠 ë“±ì´ ìžì‹ ì˜ 서버 경유로 ì˜ë„치 않게 ì¸í„°ë„·ì— 공개ë˜ëŠ” ë¬¸ì œì˜ ë°©ì§€ ë“±ì— ë„ì›€ì„ ì¤ë‹ˆë‹¤."
userGeneratedContentsVisibilityForVisitor_description2: "서버ì—서 ë°›ì€ ë¦¬ëª¨íŠ¸ 콘í…츠를 í¬í•¨í•´ 서버 ë‚´ì˜ ëª¨ë“  콘í…츠를 무조건 ì¸í„°ë„·ì— 공개하는 것ì—는 ìœ„í—˜ì´ ë”°ë¦…ë‹ˆë‹¤. 특히, 분산형 íŠ¹ì„±ì— ëŒ€í•´ 모르는 열람ìžì—게는 리모트 콘í…ì¸ ì—¬ë„ ì„œë²„ ë‚´ì—서 ìž‘ì„±ëœ ì½˜í…츠ë¼ê³  잘못 ì¸ì‹í•  수 ìžˆê¸°ì— ì£¼ì˜ê°€ 필요합니다."
+ restartServerSetupWizardConfirm_title: "ì„œë²„ì˜ ì´ˆê¸° 설정 위ìžë“œë¥¼ 재시ë„하시겠습니까?"
+ restartServerSetupWizardConfirm_text: "현재 ì¼ë¶€ ì„¤ì •ì€ ë¦¬ì…‹ë©ë‹ˆë‹¤."
+ entrancePageStyle: "입구 페ì´ì§€ì˜ 스타ì¼"
+ showTimelineForVisitor: "타임ë¼ì¸ 표시"
+ showActivitiesForVisitor: "액티비티 표시하기"
_userGeneratedContentsVisibilityForVisitor:
all: "ëª¨ë‘ ê³µê°œ"
localOnly: "로컬 콘í…츠만 공개하고 리모트 콘í…츠는 비공개"
@@ -1986,6 +2005,7 @@ _role:
descriptionOfRateLimitFactor: "ìž‘ì„ìˆ˜ë¡ ì œí•œì´ ì™„í™”ë˜ê³ , í´ìˆ˜ë¡ ì œí•œì´ ê°•í™”ë©ë‹ˆë‹¤."
canHideAds: "광고 숨기기"
canSearchNotes: "노트 검색 ì´ìš© 가능 여부"
+ canSearchUsers: "유저 검색 ì´ìš©"
canUseTranslator: "번역 ê¸°ëŠ¥ì˜ ì‚¬ìš©"
avatarDecorationLimit: "아바타 장ì‹ì˜ 최대 붙임 개수"
canImportAntennas: "안테나 가져오기 허용"
@@ -2258,6 +2278,7 @@ _time:
minute: "ë¶„"
hour: "시간"
day: "ì¼"
+ month: "개월"
_2fa:
alreadyRegistered: "ì´ë¯¸ ì„¤ì •ì´ ì™„ë£Œë˜ì—ˆìŠµë‹ˆë‹¤."
registerTOTP: "ì¸ì¦ 앱 설정 시작"
@@ -3060,6 +3081,7 @@ _bootErrors:
otherOption1: "í´ë¼ì´ì–¸íЏ 설정 ë° ìºì‹œ ì‚­ì œ"
otherOption2: "간편 í´ë¼ì´ì–¸íЏ 실행"
otherOption3: "복구 툴 실행"
+ otherOption4: "Misskey를 세ì´í”„ 모드로 열기"
_search:
searchScopeAll: "ì „ì²´"
searchScopeLocal: "로컬"
@@ -3096,6 +3118,8 @@ _serverSetupWizard:
doYouConnectToFediverse_description1: "분산형 서버로 êµ¬ì„±ëœ ë„¤íŠ¸ì›Œí¬(Fediverse)ì— ì ‘ì†í•˜ë©´ 다른 서버와 서로 콘í…ì¸ ì˜ ì£¼ê³ ë°›ê¸°ë¥¼ í•  수 있습니다."
doYouConnectToFediverse_description2: "Fediverseì— ì ‘ì†í•˜ëŠ” ê²ƒì„ 'ì—°í•©'ì´ë¼ê³ ë„ 부릅니다."
youCanConfigureMoreFederationSettingsLater: "ë‚˜ì¤‘ì— ì—°í•© 가능한 ì„œë²„ì˜ ì§€ì • 등 고급 ì„¤ì •ì„ í•  수 있습니다."
+ remoteContentsCleaning: "리모트 콘í…츠 ìžë™ 정리"
+ remoteContentsCleaning_description: "ì—°í•© ì¤‘ì¸ ì„œë²„ê°€ 있는 경우, 리모트 서버ì—서 대단히 ë§Žì€ ì½˜í…츠를 받아오게 ë©ë‹ˆë‹¤. ìžë™ 정리 ê¸°ëŠ¥ì„ í™œì„±í™”í•˜ë©´, 오래ë˜ê³  서버ì—서 ë” ì´ìƒ 조회ë˜ì§€ 않는 콘í…츠를 ìžë™ìœ¼ë¡œ 서버ì—서 삭제하여, 스토리지를 절약할 수 있습니다."
adminInfo: "ê´€ë¦¬ìž ì •ë³´"
adminInfo_description: "ë¬¸ì˜ ì ‘ìˆ˜ë¥¼ 위해 사용ë˜ëŠ” ê´€ë¦¬ìž ì •ë³´ë¥¼ 설정합니다."
adminInfo_mustBeFilled: "오픈 서버 í˜¹ì€ ì—°í•©ì´ ì¼œì ¸ 있는 경우 반드시 입력해야 합니다."
@@ -3148,10 +3172,10 @@ _watermarkEditor:
type: "종류"
image: "ì´ë¯¸ì§€"
advanced: "고급"
+ angle: "ê°ë„"
stripe: "줄무늬"
stripeWidth: "ë¼ì¸ì˜ í­"
stripeFrequency: "ë¼ì¸ì˜ 수"
- angle: "ê°ë„"
polkadot: "물방울 무늬"
checker: "ì²´í¬ ë¬´ëŠ¬"
polkadotMainDotOpacity: "주요 ë¬¼ë°©ìš¸ì˜ ë¶ˆíˆ¬ëª…ë„"
@@ -3163,6 +3187,7 @@ _imageEffector:
title: "ì´íŽ™íŠ¸"
addEffect: "ì´íŽ™íŠ¸ë¥¼ 추가"
discardChangesConfirm: "ë³€ê²½ì„ ì·¨ì†Œí•˜ê³  종료하시겠습니까?"
+ nothingToConfigure: "설정 í•­ëª©ì´ ì—†ìŠµë‹ˆë‹¤."
_fxs:
chromaticAberration: "색수차"
glitch: "글리치"
@@ -3180,6 +3205,38 @@ _imageEffector:
checker: "ì²´í¬ ë¬´ëŠ¬"
blockNoise: "ë…¸ì´ì¦ˆ ë°©ì§€"
tearing: "í‹°ì–´ë§"
+ _fxProps:
+ angle: "ê°ë„"
+ scale: "í¬ê¸°"
+ size: "í¬ê¸°"
+ color: "색"
+ opacity: "불투명ë„"
+ normalize: "노멀ë¼ì´ì¦ˆ"
+ amount: "ì–‘"
+ lightness: "ë°ìŒ"
+ contrast: "대비"
+ hue: "색조"
+ brightness: "ë°ê¸°"
+ saturation: "채ë„"
+ max: "최대 값"
+ min: "최소 값"
+ direction: "ë°©í–¥"
+ phase: "위ìƒ"
+ frequency: "빈ë„"
+ strength: "ê°•ë„"
+ glitchChannelShift: "글리치"
+ seed: "시드 값"
+ redComponent: "빨간색 요소"
+ greenComponent: "녹색 요소"
+ blueComponent: "파란색 요소"
+ threshold: "한계 값"
+ centerX: "X축 중심"
+ centerY: "Y축 중심"
+ zoomLinesSmoothing: "다듬기"
+ zoomLinesSmoothingDescription: "다듬기와 집중선 í­ ì„¤ì •ì€ ê°™ì´ ì“¸ 수 없습니다."
+ zoomLinesThreshold: "집중선 í­"
+ zoomLinesMaskSize: "중앙 값"
+ zoomLinesBlack: "ê²€ì€ìƒ‰ìœ¼ë¡œ 하기"
drafts: "초안"
_drafts:
select: "초안 ì„ íƒ"
diff --git a/locales/lo-LA.yml b/locales/lo-LA.yml
index 49270119b3..aec49a2965 100644
--- a/locales/lo-LA.yml
+++ b/locales/lo-LA.yml
@@ -433,6 +433,7 @@ _cw:
_visibility:
home: "ໜ້າຫຼັàº"
followers: "ຜູ້ຕິດຕາມ"
+ specified: "ໂພສ Direct note"
_profile:
name: "ຊື່"
username: "ຊື່ຜູ້ໃຊ້"
@@ -470,6 +471,7 @@ _deck:
list: "ລາàºàºàº²àº™"
channel: "ຊ່ອງ"
mentions: "àºà»ˆàº²àº§à»€àº–ິງເຈົ້າ"
+ direct: "ໂພສ Direct note"
_webhookSettings:
name: "ຊື່"
_abuseReport:
diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml
index 62f31dcced..e4efbc7e39 100644
--- a/locales/nl-NL.yml
+++ b/locales/nl-NL.yml
@@ -1019,6 +1019,7 @@ _cw:
_visibility:
home: "Startpagina"
followers: "Volgers"
+ specified: "Directe notities"
_profile:
name: "Naam"
username: "Gebruikersnaam"
@@ -1061,6 +1062,7 @@ _deck:
list: "Lijsten"
channel: "Kanalen"
mentions: "Vermeldingen"
+ direct: "Directe notities"
_webhookSettings:
name: "Naam"
active: "Ingeschakeld"
diff --git a/locales/no-NO.yml b/locales/no-NO.yml
index b161ce19b6..1eafd31c4f 100644
--- a/locales/no-NO.yml
+++ b/locales/no-NO.yml
@@ -461,6 +461,8 @@ replies: "Svar"
renotes: "Renote"
surrender: "Avbryt"
information: "Informasjon"
+inMinutes: "Minutter"
+inDays: "Dager"
_chat:
invitations: "Inviter"
members: "Medlemmer"
@@ -740,3 +742,8 @@ _watermarkEditor:
text: "Tekst"
type: "Type"
image: "Bilder"
+_imageEffector:
+ _fxProps:
+ scale: "Størrelse"
+ size: "Størrelse"
+ color: "Farge"
diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml
index a72b9f2c7d..fbd898016e 100644
--- a/locales/pl-PL.yml
+++ b/locales/pl-PL.yml
@@ -1040,6 +1040,8 @@ surrender: "Odrzuć"
gameRetry: "Spróbuj ponownie"
postForm: "Formularz tworzenia wpisu"
information: "Informacje"
+inMinutes: "minuta"
+inDays: "dzień"
_chat:
invitations: "ZaproÅ›"
noHistory: "Brak historii"
@@ -1591,3 +1593,10 @@ _watermarkEditor:
type: "Typ"
image: "Zdjęcia"
advanced: "Zaawansowane"
+_imageEffector:
+ _fxProps:
+ scale: "Rozmiar"
+ size: "Rozmiar"
+ color: "Kolor"
+ opacity: "Przezroczystość"
+ lightness: "Rozjaśnij"
diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml
index 978f81b626..013d2ef549 100644
--- a/locales/pt-PT.yml
+++ b/locales/pt-PT.yml
@@ -1243,7 +1243,6 @@ releaseToRefresh: "Solte para atualizar"
refreshing: "Atualizando..."
pullDownToRefresh: "Puxe para baixo para atualizar"
useGroupedNotifications: "Agrupar notificações"
-signupPendingError: "Houve um problema ao verificar o endereço de email. O link pode ter expirado."
cwNotationRequired: "Se \"Esconder conteúdo\" está habilitado, uma descrição deve ser adicionada."
doReaction: "Adicionar reação"
code: "Código"
@@ -1368,6 +1367,8 @@ redisplayAllTips: "Mostrar todas as \"Dicas e Truques\" novamente"
hideAllTips: "Ocultas todas as \"Dicas e Truques\""
defaultImageCompressionLevel: "Nível de compressão de imagem padrão"
defaultImageCompressionLevel_description: "Alto, reduz o tamanho do arquivo mas, também, a qualidade da imagem.<br>Alto, reduz o tamanho do arquivo mas, também, a qualidade da imagem."
+inMinutes: "Minuto(s)"
+inDays: "Dia(s)"
_order:
newest: "Priorizar Mais Novos"
oldest: "Priorizar Mais Antigos"
@@ -3148,10 +3149,10 @@ _watermarkEditor:
type: "Tipo"
image: "imagem"
advanced: "Avançado"
+ angle: "Ângulo"
stripe: "Listras"
stripeWidth: "Largura da linha"
stripeFrequency: "Número de linhas"
- angle: "Ângulo"
polkadot: "Bolinhas"
checker: "Xadrez"
polkadotMainDotOpacity: "Opacidade da bolinha principal"
@@ -3180,6 +3181,13 @@ _imageEffector:
checker: "Xadrez"
blockNoise: "Bloquear Ruído"
tearing: "Descontinuidade"
+ _fxProps:
+ angle: "Ângulo"
+ scale: "Tamanho"
+ size: "Tamanho"
+ color: "Cor"
+ opacity: "Opacidade"
+ lightness: "Esclarecer"
drafts: "Rascunhos"
_drafts:
select: "Selecionar Rascunho"
diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml
index de3e17fce3..b08341711a 100644
--- a/locales/ro-RO.yml
+++ b/locales/ro-RO.yml
@@ -1302,6 +1302,7 @@ _cw:
_visibility:
home: "Acasă"
followers: "Urmăritori"
+ specified: "Note directe"
_postForm:
replyPlaceholder: "Răspunde la această notă..."
quotePlaceholder: "Citează aceasta nota..."
@@ -1356,6 +1357,7 @@ _deck:
list: "Liste"
channel: "Canale"
mentions: "Mențiuni"
+ direct: "Note directe"
roleTimeline: "Cronologia rolului"
_webhookSettings:
name: "Nume"
@@ -1398,3 +1400,7 @@ _watermarkEditor:
type: "Tip"
image: "Imagini"
advanced: "Avansat"
+_imageEffector:
+ _fxProps:
+ scale: "Dimensiune"
+ size: "Dimensiune"
diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml
index 1e34af9351..aea69f6f24 100644
--- a/locales/ru-RU.yml
+++ b/locales/ru-RU.yml
@@ -2,7 +2,7 @@
_lang_: "РуÑÑкий"
headlineMisskey: "Сеть, ÑÐ¿Ð»ÐµÑ‚Ñ‘Ð½Ð½Ð°Ñ Ð¸Ð· заметок"
introMisskey: "Добро пожаловать! Misskey — Ñто децентрализованный ÑÐµÑ€Ð²Ð¸Ñ Ð¼Ð¸ÐºÑ€Ð¾Ð±Ð»Ð¾Ð³Ð¾Ð² Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ñ‹Ð¼ иÑходным кодом.\nПишите «заметки» — делитеÑÑŒ Ñо вÑеми проиÑходÑщим вокруг или раÑÑказывайте о Ñебе 📡\nСтавьте «реакции» — выражайте Ñвои чувÑтва и Ñмоции от заметок других ðŸ‘\nОткройте Ð´Ð»Ñ ÑÐµÐ±Ñ Ð½Ð¾Ð²Ñ‹Ð¹ мир 🚀"
-poweredByMisskeyDescription: "{name} – ÑÐµÑ€Ð²Ð¸Ñ Ð½Ð° платформе Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ñ‹Ð¼ иÑходным кодом <b>Misskey</b>, называемый ÑкземплÑром Misskey."
+poweredByMisskeyDescription: "{name} – один из инÑтанÑов (также называемый ÑкземплÑром Misskey), иÑпользующий платформу Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ñ‹Ð¼ иÑходным кодом <b>Misskey</b>."
monthAndDay: "{day}.{month}"
search: "ПоиÑк"
reset: "СброÑ"
@@ -82,7 +82,7 @@ export: "ЭкÑпорт"
files: "Файлы"
download: "Скачать"
driveFileDeleteConfirm: "Удалить файл «{name}»? Заметки Ñ Ð½Ð¸Ð¼ также будут удалены."
-unfollowConfirm: "Удалить из подпиÑок Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ {name}?"
+unfollowConfirm: "ОтпиÑатьÑÑ Ð¾Ñ‚ {name} ?"
exportRequested: "Ð’Ñ‹ запроÑили ÑкÑпорт. Это может занÑть некоторое времÑ. Результат будет добавлен на «ДиÑк»."
importRequested: "Ð’Ñ‹ запроÑили импорт. Это может занÑть некоторое времÑ."
lists: "СпиÑки"
@@ -298,6 +298,7 @@ uploadFromUrl: "Загрузить по ÑÑылке"
uploadFromUrlDescription: "СÑылка на файл, который хотите загрузить"
uploadFromUrlRequested: "Загрузка выбранного"
uploadFromUrlMayTakeTime: "Загрузка может занÑть некоторое времÑ."
+uploadNFiles: "Загрузить {n} файл"
explore: "Обзор"
messageRead: "Прочитали"
noMoreHistory: "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð·Ð°ÐºÐ¾Ð½Ñ‡Ð¸Ð»Ð°ÑÑŒ"
@@ -575,8 +576,10 @@ showFixedPostForm: "Показывать поле Ð´Ð»Ñ Ð²Ð²Ð¾Ð´Ð° новой Ð
showFixedPostFormInChannel: "Показывать поле Ð´Ð»Ñ Ð²Ð²Ð¾Ð´Ð° новой заметки наверху ленты (каналы)"
withRepliesByDefaultForNewlyFollowed: "По умолчанию включайте ответы новых пользователей, на которых вы подпиÑалиÑÑŒ, во временную шкалу"
newNoteRecived: "ПоÑвилаÑÑŒ Ð½Ð¾Ð²Ð°Ñ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ°"
+newNote: "ÐÐ¾Ð²Ð°Ñ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ°"
sounds: "Звуки"
sound: "Звуки"
+notificationSoundSettings: "ÐаÑтройки звука уведомлений"
listen: "Слушать"
none: "Ðичего"
showInPage: "Показать Ñтраницу"
@@ -791,6 +794,7 @@ wide: "ТолÑтый"
narrow: "Тонкий"
reloadToApplySetting: "Это наÑтройка вÑтупает в Ñилу при загрузке Ñтраницы. Перезагрузить ÑейчаÑ?"
needReloadToApply: "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð²ÑтупÑÑ‚ в Ñилу поÑле перезагрузки Ñтраницы."
+needToRestartServerToApply: "Ð”Ð»Ñ Ð²ÑÑ‚ÑƒÐ¿Ð»ÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹ в Ñилу необходимо перезапуÑтить Ñервер."
showTitlebar: "Показать заголовок"
clearCache: "ОчиÑтить кÑш"
onlineUsersCount: "Пользователей ÑÐµÐ¹Ñ‡Ð°Ñ Ð² Ñети: {n}"
@@ -1176,13 +1180,25 @@ unused: "ÐеиÑпользованное"
used: "ИÑпользован"
expired: "Срок дейÑÑ‚Ð²Ð¸Ñ Ð¿Ñ€Ð¸Ð³Ð»Ð°ÑˆÐµÐ½Ð¸Ñ Ð¸Ñтёк"
doYouAgree: "СоглаÑны?"
+beSureToReadThisAsItIsImportant: "Это важно, поÑтому, пожалуйÑта, прочтите Ñто."
+iHaveReadXCarefullyAndAgree: "Я прочитал(а) и ÑоглаÑен(Ñна) Ñ ÑƒÑловиÑми \"{x}"
+dialog: "Диалог"
icon: "Ðватар"
+currentAnnouncements: "Текущие новоÑти"
+pastAnnouncements: "Предыдущие новоÑти"
+youHaveUnreadAnnouncements: "У Ð²Ð°Ñ ÐµÑть непрочитанные уведомлениÑ"
replies: "Ответы"
renotes: "РепоÑÑ‚"
loadReplies: "Показать ответы"
+loadConversation: "Загрузить беÑеду"
pinnedList: "Закреплённый ÑпиÑок"
keepScreenOn: "Держать Ñкран включённым"
+unnotifyNotes: "ОтпиÑатьÑÑ Ð¾Ñ‚ Ñообщений"
+authentication: "ÐутентификациÑ"
+authenticationRequiredToContinue: "ПожалуйÑта, пройдите аутентификацию, чтобы продолжить"
+dateAndTime: "Дата и времÑ"
showRenotes: "Показывать репоÑты"
+edited: "Изменено"
mutualFollow: "Взаимные подпиÑки"
followingOrFollower: "ПодпиÑки или подпиÑчики"
fileAttachedOnly: "Только заметки Ñ Ñ„Ð°Ð¹Ð»Ð°Ð¼Ð¸"
@@ -1193,30 +1209,70 @@ sourceCode: "ИÑходный код"
sourceCodeIsNotYetProvided: "ИÑходный код пока не доÑтупен. СвÑжитеÑÑŒ Ñ Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñтратором, чтобы иÑправить Ñту проблему."
repositoryUrl: "СÑылка на репозиторий"
repositoryUrlDescription: "ЕÑли вы иÑпользуете Misskey как еÑть (без изменений в иÑходном коде), введите https://github.com/misskey-dev/misskey"
+feedback: "ÐžÐ±Ñ€Ð°Ñ‚Ð½Ð°Ñ ÑвÑзь"
privacyPolicy: "Политика КонфиденциальноÑти"
privacyPolicyUrl: "СÑылка на Политику КонфиденциальноÑти"
+tosAndPrivacyPolicy: "УÑÐ»Ð¾Ð²Ð¸Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸ политика конфиденциальноÑти"
+avatarDecorations: "Ð£ÐºÑ€Ð°ÑˆÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð°Ð²Ð°Ñ‚Ð°Ñ€Ð°"
attach: "Прикрепить"
angle: "Угол"
flip: "Переворот"
+showAvatarDecorations: "Показать ÑƒÐºÑ€Ð°ÑˆÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð°Ð²Ð°Ñ‚Ð°Ñ€Ð°"
+pullDownToRefresh: "ОпуÑтите что бы обновить"
useGroupedNotifications: "Отображать ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ñгруппировано"
+cwNotationRequired: "ЕÑли включена Ð¾Ð¿Ñ†Ð¸Ñ Â«Ð¡ÐºÑ€Ñ‹Ñ‚ÑŒ Ñодержимое», необходимо напиÑать аннотацию."
doReaction: "Добавить реакцию"
code: "Код"
+reloadRequiredToApplySettings: "Ð”Ð»Ñ Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð½Ð°Ñтроек необходима обновить Ñтраницу."
remainingN: "ОÑтаётÑÑ: {n}"
+overwriteContentConfirm: "Текущее Ñодержимое будет перезапиÑано. Ð’Ñ‹ уверены?"
seasonalScreenEffect: "Эффект времени года на Ñкране"
decorate: "УкраÑить"
addMfmFunction: "Добавить MFM"
+bubbleGame: "BubbleGame"
+sfx: "Звуковые Ñффекты"
+soundWillBePlayed: "Будет воÑпроизведен звук"
+showReplay: "Показать повтор"
+endReplay: "Конец повтора"
lastNDays: "ПоÑледние {n} Ñут"
hemisphere: "МеÑто проживаниÑ"
+userSaysSomethingSensitive: "Сообщение, Ñодержит конфиденциальные файлы от {name}"
enableHorizontalSwipe: "Смахните в Ñторону, чтобы Ñменить вкладки"
surrender: "Этот поÑÑ‚ не может быть отменен."
+gameRetry: "Повторить попытку"
+notUsePleaseLeaveBlank: "ЕÑли не иÑпользуетÑÑ, оÑтавьте пуÑтым"
useNativeUIForVideoAudioPlayer: "ИÑпользовать Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð±Ñ€Ð°ÑƒÐ·ÐµÑ€Ð° при проигрывании видео и звука"
keepOriginalFilename: "СохранÑть иÑходное Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°"
keepOriginalFilenameDescription: "ЕÑли вы выключите данную наÑтройку, имена файлов будут автоматичеÑки заменены Ñлучайной Ñтрокой при загрузке."
alwaysConfirmFollow: "Ð’Ñегда подтверждать подпиÑку"
inquiry: "СвÑзатьÑÑ"
+fromX: "Из {x}"
+genEmbedCode: "Сгенерировать код Ð´Ð»Ñ "
+noteOfThisUser: "СпиÑок заметок Ñтого пользователÑ"
+clipNoteLimitExceeded: "К Ñтому клипу больше Ð½ÐµÐ»ÑŒÐ·Ñ Ð´Ð¾Ð±Ð°Ð²Ð¸Ñ‚ÑŒ заметки"
+performance: "ПроизводительноÑть"
+modified: "Изменено"
+signinWithPasskey: "Войдите в ÑиÑтему, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ñвой пароль"
+unknownWebAuthnKey: "Ðе извеÑтный ключ "
+passkeyVerificationFailed: "Ошибка проверка ключа доÑтупа "
messageToFollower: "Сообщение подпиÑчикам"
+testCaptchaWarning: "Эта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¿Ñ€ÐµÐ´Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð° Ð´Ð»Ñ Ñ‚ÐµÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ CAPTCHA. <strong>Ðе иÑпользовать Ñто в рабочей Ñреде</strong>"
+prohibitedWordsForNameOfUser: "Запрещенные Ñлова (Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ)"
+prohibitedWordsForNameOfUserDescription: "ЕÑли Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ñодержит Ñтроку из Ñтого ÑпиÑка, изменение имени Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð±ÑƒÐ´ÐµÑ‚ запрещено. Ðа пользователей Ñ Ð¿Ñ€Ð°Ð²Ð°Ð¼Ð¸ модератора Ñто ограничение не раÑпроÑтранÑетÑÑ. Имена пользователей также проверÑÑŽÑ‚ÑÑ Ð¿ÑƒÑ‚Ñ‘Ð¼ замены вÑех букв в нижнем региÑтре"
+yourNameContainsProhibitedWords: "ИмÑ, которое вы пытаетеÑÑŒ изменить, Ñодержит запрещенную Ñтроку Ñимволов"
+yourNameContainsProhibitedWordsDescription: "Ð˜Ð¼Ñ Ñодержит запрещённую Ñтроку Ñимволов. ЕÑли вы хотите иÑпользовать Ñто имÑ, обратитеÑÑŒ к админиÑтратору Ñервера"
+thisContentsAreMarkedAsSigninRequiredByAuthor: "Ðвтор ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ ÑƒÑтановил требование в виде авторизации Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра"
+lockdown: "ДоÑтуп ограничен"
+pleaseSelectAccount: "Выберите Ñвой аккаунт"
+availableRoles: "ДоÑтупные роли"
+federationDisabled: "Ð¤ÐµÐ´ÐµÑ€Ð°Ñ†Ð¸Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½Ð° Ð´Ð»Ñ Ñтого Ñервера. Ð’Ñ‹ не можете взаимодейÑтвовать Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñми на других Ñерверах."
+draft: "Черновик"
+markAsSensitiveConfirm: "Отметить контент как чувÑтвительный?"
+resetToDefaultValue: "СброÑить наÑтройки до Ñтандартных"
postForm: "Форма отправки"
information: "ОпиÑание"
+inMinutes: "мин"
+inDays: "Ñут"
_chat:
invitations: "ПриглаÑить"
noHistory: "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð¿Ð¾ÐºÐ° пуÑта"
@@ -2200,3 +2256,12 @@ _watermarkEditor:
image: "ИзображениÑ"
advanced: "Ð”Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð²Ð¸Ð½ÑƒÑ‚Ñ‹Ñ…"
angle: "Угол"
+_imageEffector:
+ _fxProps:
+ angle: "Угол"
+ scale: "Размер"
+ size: "Размер"
+ color: "Цвет"
+ opacity: "ÐепрозрачноÑть"
+ lightness: "ОÑветление"
+drafts: "Черновик"
diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml
index 651e91fa0a..c9fd8e6ae9 100644
--- a/locales/sk-SK.yml
+++ b/locales/sk-SK.yml
@@ -913,6 +913,8 @@ flip: "Preklopiť"
lastNDays: "Posledných {n} dní"
postForm: "Napísať poznámku"
information: "Informácie"
+inMinutes: "min"
+inDays: "dní"
_chat:
invitations: "Pozvať"
noHistory: "Žiadna história"
@@ -1457,3 +1459,10 @@ _watermarkEditor:
type: "Typ"
image: "Obrázky"
advanced: "Rozšírené"
+_imageEffector:
+ _fxProps:
+ scale: "Veľkosť"
+ size: "Veľkosť"
+ color: "Farba"
+ opacity: "Priehľadnosť"
+ lightness: "Zosvetliť"
diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml
index 4d1708c7d1..96944706e9 100644
--- a/locales/sv-SE.yml
+++ b/locales/sv-SE.yml
@@ -646,6 +646,7 @@ _poll:
_visibility:
home: "Hem"
followers: "Följare"
+ specified: "Direktnoter"
_profile:
name: "Namn"
username: "Användarnamn"
@@ -692,6 +693,7 @@ _deck:
list: "Listor"
channel: "kanal"
mentions: "Omnämningar"
+ direct: "Direktnoter"
_webhookSettings:
name: "Namn"
active: "Aktiverad"
@@ -714,3 +716,8 @@ _search:
_watermarkEditor:
scale: "Storlek"
image: "Bilder"
+_imageEffector:
+ _fxProps:
+ scale: "Storlek"
+ size: "Storlek"
+ color: "Färg"
diff --git a/locales/th-TH.yml b/locales/th-TH.yml
index d6b0ef4de4..f70b0d5be8 100644
--- a/locales/th-TH.yml
+++ b/locales/th-TH.yml
@@ -776,7 +776,7 @@ highlightSensitiveMedia: "ไฮไลท์สื่อที่มีเนื
verificationEmailSent: "ได้ส่งอีเมลยืนยันà¹à¸¥à¹‰à¸§ à¸à¸£à¸¸à¸“าเข้าลิงà¸à¹Œà¸—ี่ระบุในอีเมลเพื่อทำà¸à¸²à¸£à¸•ั้งค่าให้เสร็จสิ้น"
notSet: "ไม่ได้ตั้งค่า"
emailVerified: "อีเมลได้รับà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¹à¸¥à¹‰à¸§"
-noteFavoritesCount: "จำนวนโน้ตที่ชื่นชอบ"
+noteFavoritesCount: "จำนวนโน้ตโปรด"
pageLikesCount: "จำนวนเพจที่ถูà¸à¹ƒà¸ˆ"
pageLikedCount: "จำนวนà¸à¸²à¸£à¸à¸”ถูà¸à¹ƒà¸ˆà¹€à¸žà¸ˆà¸—ี่ได้รับà¹à¸¥à¹‰à¸§"
contact: "ติดต่อ"
@@ -1054,6 +1054,7 @@ permissionDeniedError: "à¸à¸²à¸£à¸”ำเนินถูà¸à¸›à¸à¸´à¹€à¸ªà¸˜"
permissionDeniedErrorDescription: "บัà¸à¸Šà¸µà¸™à¸µà¹‰à¹„ม่มีสิทธิ์อนุà¸à¸²à¸•ในà¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰"
preset: "พรีเซ็ต"
selectFromPresets: "เลือà¸à¸ˆà¸²à¸à¸à¸²à¸£à¸žà¸£à¸µà¹€à¸‹à¹‡à¸•"
+custom: "à¹à¸šà¸šà¸à¸³à¸«à¸™à¸”เอง"
achievements: "ความสำเร็จ"
gotInvalidResponseError: "à¸à¸²à¸£à¸•อบสนองเซิร์ฟเวอร์ไม่ถูà¸à¸•้อง"
gotInvalidResponseErrorDescription: "เซิร์ฟเวอร์อาจไม่สามารถเข้าถึงได้หรืออาจจะà¸à¸³à¸¥à¸±à¸‡à¸­à¸¢à¸¹à¹ˆà¹ƒà¸™à¸£à¸°à¸«à¸§à¹ˆà¸²à¸‡à¸›à¸£à¸±à¸šà¸›à¸£à¸¸à¸‡ à¸à¸£à¸¸à¸“าลองใหม่อีà¸à¸„รั้งในภายหลังนะคะ"
@@ -1092,6 +1093,7 @@ prohibitedWordsDescription2: "ถ้าà¹à¸¢à¸à¸”้วยเว้นวรà¸
hiddenTags: "à¹à¸®à¸Šà¹à¸—็à¸à¸—ี่ซ่อนอยู่"
hiddenTagsDescription: "เลือà¸à¹à¸—็à¸à¸—ี่จะไม่à¹à¸ªà¸”งในรายà¸à¸²à¸£à¹€à¸—รนด์ สามารถลงทะเบียนหลายà¹à¸—็à¸à¹„ด้โดยขึ้นบรรทัดใหม่"
notesSearchNotAvailable: "à¸à¸²à¸£à¸„้นหาโน้ตไม่พร้อมใช้งาน"
+usersSearchNotAvailable: "à¸à¸²à¸£à¸„้นหาผู้ใช้ไม่พร้อมใช้งาน"
license: "ใบอนุà¸à¸²à¸•"
unfavoriteConfirm: "ลบออà¸à¸ˆà¸²à¸à¸£à¸²à¸¢à¸à¸²à¸£à¹‚ปรดà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸­?"
myClips: "คลิปของฉัน"
@@ -1243,7 +1245,6 @@ releaseToRefresh: "ปล่อยเพื่อรีเฟรช"
refreshing: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸µà¹€à¸Ÿà¸£à¸Š..."
pullDownToRefresh: "ดึงลงเพื่อรีเฟรช"
useGroupedNotifications: "à¹à¸ªà¸”งผลà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือนà¹à¸šà¸šà¸à¸¥à¸¸à¹ˆà¸¡à¹à¸¥à¹‰à¸§"
-signupPendingError: "มีปัà¸à¸«à¸²à¹ƒà¸™à¸à¸²à¸£à¸•รวจสอบที่อยู่อีเมลลิงà¸à¹Œà¸­à¸²à¸ˆà¸«à¸¡à¸”อายุà¹à¸¥à¹‰à¸§"
cwNotationRequired: "หาà¸à¹€à¸›à¸´à¸”ใช้งาน “ซ่อนเนื้อหา†จะต้องระบุคำอธิบาย"
doReaction: "เพิ่มรีà¹à¸­à¸„ชั่น"
code: "โค้ด"
@@ -1368,6 +1369,13 @@ redisplayAllTips: "à¹à¸ªà¸”งคำà¹à¸™à¸°à¸™à¸³à¹à¸¥à¸°à¹€à¸„ล็ดลà
hideAllTips: "ซ่อนคำà¹à¸™à¸°à¸™à¸³à¹à¸¥à¸°à¹€à¸„ล็ดลับทั้งหมด"
defaultImageCompressionLevel: "ความละเอียดเริ่มต้นสำหรับà¸à¸²à¸£à¸šà¸µà¸šà¸­à¸±à¸”ภาพ"
defaultImageCompressionLevel_description: "หาà¸à¸•ั้งค่าต่ำ จะรัà¸à¸©à¸²à¸„ุณภาพภาพได้ดีขึ้นà¹à¸•่ขนาดไฟล์จะเพิ่มขึ้น<br>หาà¸à¸•ั้งค่าสูง จะลดขนาดไฟล์ได้ à¹à¸•่คุณภาพภาพจะลดลง"
+inMinutes: "นาที"
+inDays: "วัน"
+safeModeEnabled: "โหมดปลอดภัยถูà¸à¹€à¸›à¸´à¸”ใช้งาน"
+pluginsAreDisabledBecauseSafeMode: "เนื่องจาà¸à¹‚หมดปลอดภัยถูà¸à¹€à¸›à¸´à¸”ใช้งาน ปลั๊à¸à¸­à¸´à¸™à¸—ั้งหมดจึงถูà¸à¸›à¸´à¸”ใช้งาน"
+customCssIsDisabledBecauseSafeMode: "เนื่องจาà¸à¹‚หมดปลอดภัยถูà¸à¹€à¸›à¸´à¸”ใช้งาน CSS à¹à¸šà¸šà¸à¸³à¸«à¸™à¸”เองจึงไม่ได้ถูà¸à¸™à¸³à¸¡à¸²à¹ƒà¸Šà¹‰"
+themeIsDefaultBecauseSafeMode: "ในระหว่างที่โหมดปลอดภัยถูà¸à¹€à¸›à¸´à¸”ใช้งาน จะใช้ธีมเริ่มต้น เมื่อปิดโหมดปลอดภัยจะà¸à¸¥à¸±à¸šà¸„ืนดังเดิม"
+thankYouForTestingBeta: "ขอบคุณที่ให้ความร่วมมือในà¸à¸²à¸£à¸—ดสอบเวอร์ชันเบต้า!"
_order:
newest: "เรียงจาà¸à¹ƒà¸«à¸¡à¹ˆà¹„ปเà¸à¹ˆà¸²"
oldest: "เรียงจาà¸à¹€à¸à¹ˆà¸²à¹„ปใหม่"
@@ -1431,7 +1439,7 @@ _settings:
api: "API"
webhook: "Webhook"
serviceConnection: "à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•่อà¸à¸±à¸šà¸šà¸£à¸´à¸à¸²à¸£"
- serviceConnectionBanner: "สามารถจัดà¸à¸²à¸£à¹à¸¥à¸°à¸•ั้งค่า Access Token à¹à¸¥à¸° Webhook เพื่อเชื่อมต่อà¸à¸±à¸šà¹à¸­à¸›à¸«à¸£à¸·à¸­à¸šà¸£à¸´à¸à¸²à¸£à¸ à¸²à¸¢à¸™à¸­à¸à¹„ด้"
+ serviceConnectionBanner: "สามารถจัดà¸à¸²à¸£à¹à¸¥à¸°à¸•ั้งค่าโทเค็นà¸à¸²à¸£à¹€à¸‚้าถึงà¹à¸¥à¸° Webhook เพื่อเชื่อมต่อà¸à¸±à¸šà¹à¸­à¸›à¸«à¸£à¸·à¸­à¸šà¸£à¸´à¸à¸²à¸£à¸ à¸²à¸¢à¸™à¸­à¸à¹„ด้"
accountData: "ข้อมูลบัà¸à¸Šà¸µ"
accountDataBanner: "สามารถจัดà¸à¸²à¸£à¸‚้อมูลบัà¸à¸Šà¸µà¹„ด้โดยส่งออà¸à¸«à¸£à¸·à¸­à¸™à¸³à¹€à¸‚้าไฟล์เà¸à¹‡à¸šà¸–าวร"
muteAndBlockBanner: "สามารถตั้งค่าà¸à¸²à¸£à¸‹à¹ˆà¸­à¸™à¹€à¸™à¸·à¹‰à¸­à¸«à¸² à¹à¸¥à¸°à¸ˆà¸³à¸à¸±à¸”à¸à¸²à¸£à¸à¸£à¸°à¸—ำจาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¹€à¸‰à¸žà¸²à¸°à¸£à¸²à¸¢à¹„ด้"
@@ -1459,6 +1467,7 @@ _settings:
contentsUpdateFrequency_description2: "เมื่อโหมดเรียลไทม์เปิดอยู่ เนื้อหาจะอัปเดตà¹à¸šà¸šà¹€à¸£à¸µà¸¢à¸¥à¹„ทม์โดยไม่ขึ้นà¸à¸±à¸šà¸à¸²à¸£à¸•ั้งค่านี้"
showUrlPreview: "à¹à¸ªà¸”งตัวอย่าง URL"
showAvailableReactionsFirstInNote: "à¹à¸ªà¸”งรีà¹à¸­à¸„ชั่นที่ใช้ได้ไว้หน้าสุด"
+ showPageTabBarBottom: "à¹à¸ªà¸”งà¹à¸—็บบาร์ของเพจที่ด้านล่าง"
_chat:
showSenderName: "à¹à¸ªà¸”งชื่อผู้ส่ง"
sendOnEnter: "à¸à¸” Enter เพื่อส่ง"
@@ -1632,6 +1641,10 @@ _serverSettings:
fanoutTimelineDbFallback: "ฟอลà¹à¸šà¹Šà¸à¸à¸¥à¸±à¸šà¸à¸²à¸™à¸‚้อมูล"
fanoutTimelineDbFallbackDescription: "เมื่อเปิดใช้งาน หาà¸à¹„ม่ได้à¹à¸„ชไทม์ไลน์ ไทม์ไลน์จะฟอลà¹à¸šà¹Šà¸à¹„ปยังà¸à¸²à¸™à¸‚้อมูลสำหรับà¸à¸²à¸£ query เพิ่มเติม à¸à¸²à¸£à¸›à¸´à¸”ใช้งานจะช่วยลดภาระของเซิร์ฟเวอร์ด้วยà¸à¸²à¸£à¸à¸³à¸ˆà¸±à¸”à¸à¸£à¸°à¸šà¸§à¸™à¸Ÿà¸­à¸¥à¹à¸šà¹Šà¸ à¹à¸•่มันà¸à¹‡à¸ˆà¸°à¸ˆà¸³à¸à¸±à¸”ช่วงเวลาไทม์ไลน์ที่สามารถดึงข้อมูลได้"
reactionsBufferingDescription: "เมื่อเปิดใช้งานฟังà¸à¹Œà¸Šà¸±à¸™à¸™à¸µà¹‰à¸à¹‡à¸ˆà¸°à¸Šà¹ˆà¸§à¸¢à¸¥à¸” latency ในà¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¸›à¸à¸´à¸à¸´à¸£à¸´à¸¢à¸² à¹à¸•่อาจจะส่งผลให้ memory footprint ของ Redis เพิ่มขึ้นนะ"
+ remoteNotesCleaning: "à¸à¸²à¸£à¸¥à¹‰à¸²à¸‡à¸‚้อมูลโพสต์จาà¸à¸£à¸°à¸¢à¸°à¹„à¸à¸¥à¹‚ดยอัตโนมัติ"
+ remoteNotesCleaning_description: "เมื่อเปิดใช้งาน จะทำà¸à¸²à¸£à¸¥à¹‰à¸²à¸‡à¹‚พสต์จาà¸à¸£à¸°à¸¢à¸°à¹„à¸à¸¥à¹€à¸à¹ˆà¸²à¸—ี่ไม่ถูà¸à¸­à¹‰à¸²à¸‡à¸­à¸´à¸‡ เป็นระยะ เพื่อลดà¸à¸²à¸£à¸‚ยายตัวของà¸à¸²à¸™à¸‚้อมูล"
+ remoteNotesCleaningMaxProcessingDuration: "ระยะเวลาสูงสุดของà¸à¸²à¸£à¸›à¸£à¸°à¸¡à¸§à¸¥à¸œà¸¥à¸à¸²à¸£à¸¥à¹‰à¸²à¸‡à¸‚้อมูล"
+ remoteNotesCleaningExpiryDaysForEachNotes: "จำนวนวันที่ต้องเà¸à¹‡à¸šà¹‚น้ตไว้อย่างน้อย"
inquiryUrl: "URL สำหรับà¸à¸²à¸£à¸•ิดต่อสอบถาม"
inquiryUrlDescription: "ระบุ URL ของหน้าเว็บที่มีà¹à¸šà¸šà¸Ÿà¸­à¸£à¹Œà¸¡à¸ªà¸³à¸«à¸£à¸±à¸šà¸•ิดต่อผู้ดูà¹à¸¥à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œ หรือข้อมูลà¸à¸²à¸£à¸•ิดต่อของผู้ดูà¹à¸¥à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œ"
openRegistration: "เปิดให้สร้างบัà¸à¸Šà¸µà¹„ด้"
@@ -1650,6 +1663,8 @@ _serverSettings:
userGeneratedContentsVisibilityForVisitor: "ขอบเขตà¸à¸²à¸£à¹€à¸›à¸´à¸”เผยเนื้อหาที่ผู้ใช้สร้างต่อบุคคลที่ไม่ได้เข้าร่วม (à¹à¸‚à¸)"
userGeneratedContentsVisibilityForVisitor_description: "ช่วยป้องà¸à¸±à¸™à¸›à¸±à¸à¸«à¸²à¸—ี่อาจเà¸à¸´à¸”ขึ้นจาà¸à¹€à¸™à¸·à¹‰à¸­à¸«à¸²à¸£à¸°à¸¢à¸°à¹„à¸à¸¥à¸—ี่ไม่เหมาะสม ซึ่งอาจถูà¸à¹€à¸œà¸¢à¹à¸žà¸£à¹ˆà¸­à¸­à¸à¸ªà¸¹à¹ˆà¸­à¸´à¸™à¹€à¸—อร์เน็ตโดยไม่ตั้งใจผ่านเซิร์ฟเวอร์ของตนเอง โดยเฉพาะในà¸à¸£à¸“ีที่à¸à¸²à¸£à¸”ูà¹à¸¥à¸„วบคุมไม่ทั่วถึง"
userGeneratedContentsVisibilityForVisitor_description2: "à¸à¸²à¸£à¹€à¸›à¸´à¸”เผยเนื้อหาทั้งหมดในเซิร์ฟเวอร์รวมทั้งเนื้อหาที่รับมาจาà¸à¸£à¸°à¸¢à¸°à¹„à¸à¸¥à¸ªà¸¹à¹ˆà¸ªà¸²à¸˜à¸²à¸£à¸“ะบนอินเทอร์เน็ตโดยไม่มีข้อจำà¸à¸±à¸”ใดๆ มีความเสี่ยงโดยเฉพาะอย่างยิ่งสำหรับผู้ชมที่ไม่เข้าใจลัà¸à¸©à¸“ะของระบบà¹à¸šà¸šà¸à¸£à¸°à¸ˆà¸²à¸¢ อาจทำให้เà¸à¸´à¸”ความเข้าใจผิดคิดว่าเนื้อหาที่มาจาà¸à¸£à¸°à¸¢à¸°à¹„à¸à¸¥à¸™à¸±à¹‰à¸™à¹€à¸›à¹‡à¸™à¹€à¸™à¸·à¹‰à¸­à¸«à¸²à¸—ี่สร้างขึ้นภายในเซิร์ฟเวอร์นี้ จึงควรใช้ความระมัดระวังอย่างมาà¸"
+ restartServerSetupWizardConfirm_title: "ต้องà¸à¸²à¸£à¹€à¸£à¸´à¹ˆà¸¡à¸§à¸´à¸‹à¸²à¸£à¹Œà¸”à¸à¸²à¸£à¸•ั้งค่าเซิร์ฟเวอร์ใหม่หรือไม่?"
+ restartServerSetupWizardConfirm_text: "à¸à¸²à¸£à¸•ั้งค่าบางส่วนในปัจจุบันจะถูà¸à¸£à¸µà¹€à¸‹à¹‡à¸•"
_userGeneratedContentsVisibilityForVisitor:
all: "ทั้งหมดสาธารณะ"
localOnly: "เผยà¹à¸žà¸£à¹ˆà¹€à¸›à¹‡à¸™à¸ªà¸²à¸˜à¸²à¸£à¸“ะเฉพาะเนื้อหาท้องถิ่น เนื้อหาระยะไà¸à¸¥à¹ƒà¸«à¹‰à¹€à¸›à¹‡à¸™à¸ªà¹ˆà¸§à¸™à¸•ัว"
@@ -1986,6 +2001,7 @@ _role:
descriptionOfRateLimitFactor: "ยิ่งตัวเลขน้อยà¸à¹‡à¸¢à¸´à¹ˆà¸‡à¸ˆà¸³à¸à¸±à¸”น้อย ยิ่งมาà¸à¸à¹‡à¸¢à¸´à¹ˆà¸‡à¹€à¸‚้มงวดมาà¸à¸‚ึ้น"
canHideAds: "ซ่อนโฆษณา"
canSearchNotes: "à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸à¸²à¸£à¸„้นหาโน้ต"
+ canSearchUsers: "ค้นหาผู้ใช้"
canUseTranslator: "à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¹à¸›à¸¥"
avatarDecorationLimit: "จำนวนของตà¸à¹à¸•่งไอคอนสูงสุดที่สามารถติดตั้งได้"
canImportAntennas: "อนุà¸à¸²à¸•ให้นำเข้าเสาอาà¸à¸²à¸¨"
@@ -2258,6 +2274,7 @@ _time:
minute: "นาที"
hour: "ชั่วโมง"
day: "วัน"
+ month: "เดือน"
_2fa:
alreadyRegistered: "คุณได้ลงทะเบียนอุปà¸à¸£à¸“์ยืนยันตัวตนà¹à¸šà¸š 2 ชั้นà¹à¸¥à¹‰à¸§"
registerTOTP: "ลงทะเบียนà¹à¸­à¸žà¸•ัวตรวจสอบสิทธิ์"
@@ -3060,6 +3077,7 @@ _bootErrors:
otherOption1: "ลบà¸à¸²à¸£à¸•ั้งค่าà¹à¸¥à¸°à¹à¸„ชของไคลเอนต์"
otherOption2: "เริ่มใช้งานไคลเอนต์à¹à¸šà¸šà¸‡à¹ˆà¸²à¸¢"
otherOption3: "เปิดเครื่องมือซ่อมà¹à¸‹à¸¡"
+ otherOption4: "เริ่มทำงาน Misskey ในโหมดปลอดภัย"
_search:
searchScopeAll: "ทั้งหมด"
searchScopeLocal: "ท้องถิ่น"
@@ -3096,6 +3114,8 @@ _serverSetupWizard:
doYouConnectToFediverse_description1: "หาà¸à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•่อà¸à¸±à¸šà¹€à¸„รือข่ายที่ประà¸à¸­à¸šà¸”้วยเซิร์ฟเวอร์à¹à¸šà¸šà¸à¸£à¸°à¸ˆà¸²à¸¢ (Fediverse) จะสามารถà¹à¸¥à¸à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹€à¸™à¸·à¹‰à¸­à¸«à¸²à¸à¸±à¸šà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œà¸­à¸·à¹ˆà¸™à¹† ได้"
doYouConnectToFediverse_description2: "à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•่อà¸à¸±à¸š Fediverse เรียà¸à¸§à¹ˆà¸² “สหพันธ์â€"
youCanConfigureMoreFederationSettingsLater: "หลังจาà¸à¸™à¸µà¹‰à¸¢à¸±à¸‡à¸ªà¸²à¸¡à¸²à¸£à¸–ตั้งค่าà¹à¸šà¸šà¸‚ั้นสูง เช่น à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”เซิร์ฟเวอร์ที่อนุà¸à¸²à¸•ให้สหพันธ์ต่อà¸à¸±à¸™à¹„ด้เพิ่มเติม"
+ remoteContentsCleaning: "à¸à¸²à¸£à¸¥à¹‰à¸²à¸‡à¸‚้อมูลเนื้อหาที่ได้รับโดยอัตโนมัติ"
+ remoteContentsCleaning_description: "เมื่อมีà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¹‚ยงสหพันธ์ จะได้รับเนื้อหาเป็นจำนวนมาà¸à¸­à¸¢à¹ˆà¸²à¸‡à¸•่อเนื่อง เมื่อเปิดใช้งานà¸à¸²à¸£à¸¥à¹‰à¸²à¸‡à¸‚้อมูลอัตโนมัติ จะทำà¸à¸²à¸£à¸¥à¸šà¹€à¸™à¸·à¹‰à¸­à¸«à¸²à¹€à¸à¹ˆà¸²à¸—ี่ไม่ถูà¸à¸­à¹‰à¸²à¸‡à¸­à¸´à¸‡ ไปจาà¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œà¹‚ดยอัตโนมัติ เพื่อประหยัดพื้นที่จัดเà¸à¹‡à¸šà¸‚้อมูล"
adminInfo: "ข้อมูลผู้ดูà¹à¸¥à¸£à¸°à¸š"
adminInfo_description: "ตั้งค่าข้อมูลผู้ดูà¹à¸¥à¸£à¸°à¸šà¸šà¸—ี่จะใช้รับคำถามà¹à¸¥à¸°à¸•ิดต่อ"
adminInfo_mustBeFilled: "หาà¸à¹€à¸›à¸´à¸”ใช้เซิร์ฟเวอร์สาธารณะ หรือเปิดใช้งานสหพันธ์ จะต้องà¸à¸£à¸­à¸à¸‚้อมูลนี้"
@@ -3148,10 +3168,10 @@ _watermarkEditor:
type: "รูปà¹à¸šà¸š"
image: "รูปภาพ"
advanced: "ขั้นสูง"
+ angle: "à¹à¸­à¸‡à¹€à¸à¸´à¸¥"
stripe: "ริ้ว"
stripeWidth: "ความà¸à¸§à¹‰à¸²à¸‡à¹€à¸ªà¹‰à¸™"
stripeFrequency: "จำนวนเส้น"
- angle: "à¹à¸­à¸‡à¹€à¸à¸´à¸¥"
polkadot: "ลายจุด"
checker: "ช่องตาราง"
polkadotMainDotOpacity: "ความทึบของจุดหลัà¸"
@@ -3163,6 +3183,7 @@ _imageEffector:
title: "เอฟเฟà¸à¸•์"
addEffect: "เพิ่มเอฟเฟà¸à¸•์"
discardChangesConfirm: "ต้องà¸à¸²à¸£à¸—ิ้งà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¹à¸¥à¹‰à¸§à¸­à¸­à¸à¸«à¸£à¸·à¸­à¹„ม่?"
+ nothingToConfigure: "ไม่มีอะไรให้ตั้งค่า"
_fxs:
chromaticAberration: "ความคลาดสี"
glitch: "à¸à¸¥à¸´à¸•ช์"
@@ -3180,6 +3201,38 @@ _imageEffector:
checker: "ช่องตาราง"
blockNoise: "บล็อà¸à¸—ี่มีà¸à¸²à¸£à¸£à¸šà¸à¸§à¸™"
tearing: "ฉีà¸à¸‚าด"
+ _fxProps:
+ angle: "à¹à¸­à¸‡à¹€à¸à¸´à¸¥"
+ scale: "ขนาด"
+ size: "ขนาด"
+ color: "สี"
+ opacity: "ความทึบà¹à¸ªà¸‡"
+ normalize: "นอร์มัลไลซ์"
+ amount: "จำนวน"
+ lightness: "สว่าง"
+ contrast: "คอนทราสต์"
+ hue: "HUE"
+ brightness: "ความสว่าง"
+ saturation: "ความอิ่มตัว"
+ max: "สูงสุด"
+ min: "ต่ำสุด"
+ direction: "ทิศทาง"
+ phase: "ระยะ"
+ frequency: "ความถี่"
+ strength: "ความà¹à¸£à¸‡"
+ glitchChannelShift: "ความเคลื่อน"
+ seed: "ซีด"
+ redComponent: "ส่วนสีà¹à¸”ง"
+ greenComponent: "ส่วนสีเขียว"
+ blueComponent: "ส่วนสีน้ำเงิน"
+ threshold: "เทรชโฮลด์"
+ centerX: "à¸à¸¥à¸²à¸‡ X"
+ centerY: "à¸à¸¥à¸²à¸‡ Y"
+ zoomLinesSmoothing: "ทำให้สมูธ"
+ zoomLinesSmoothingDescription: "ตั้งให้สมูธไม่สามารถใช้ร่วมà¸à¸±à¸šà¸•ั้งความà¸à¸§à¹‰à¸²à¸‡à¹€à¸ªà¹‰à¸™à¸£à¸§à¸¡à¸¨à¸¹à¸™à¸¢à¹Œà¹„ด้"
+ zoomLinesThreshold: "ความà¸à¸§à¹‰à¸²à¸‡à¹€à¸ªà¹‰à¸™à¸£à¸§à¸¡à¸¨à¸¹à¸™à¸¢à¹Œ"
+ zoomLinesMaskSize: "ขนาดพื้นที่ตรงà¸à¸¥à¸²à¸‡"
+ zoomLinesBlack: "ทำให้ดำ"
drafts: "ร่าง"
_drafts:
select: "เลือà¸à¸‰à¸šà¸±à¸šà¸£à¹ˆà¸²à¸‡"
diff --git a/locales/tr-TR.yml b/locales/tr-TR.yml
index 2f20c02105..bde027ba69 100644
--- a/locales/tr-TR.yml
+++ b/locales/tr-TR.yml
@@ -1,150 +1,166 @@
---
_lang_: "Türkçe"
-headlineMisskey: "Notlarla bağlanmış bir ağ"
-introMisskey: "Açık kaynaklı bir dağıtılmış mikroblog hizmeti olan Misskey'e hoÅŸ geldiniz.\nMisskey, neler olup bittiÄŸini paylaÅŸmak ve herkese sizden bahsetmek için \"notlar\" oluÅŸturmanıza olanak tanıyan, açık kaynaklı, dağıtılmış bir mikroblog hizmetidir.\nHerkesin notlarına kendi tepkilerinizi hızlıca eklemek için \"Tepkiler\" özelliÄŸini de kullanabilirsinizðŸ‘.\nYeni bir dünyayı keÅŸfedin🚀."
-poweredByMisskeyDescription: "name}Açık kaynak bir platform\n<b>Misskey</b>Dünya'nın en sunucularında biri。"
-monthAndDay: "{month}Ay {day}Gün"
-search: "Arama"
-notifications: "Bildirim"
+headlineMisskey: "Notlarla birbirine bağlı bir ağ"
+introMisskey: "HoÅŸ geldiniz! Misskey, merkezi olmayan bir açık kaynaklı mikroblog platformudur.\n“Notlar†yazarak ÅŸu anda neler olduÄŸunu anlatabilir veya olayları baÅŸkalarıyla paylaÅŸabilirsiniz. 📡\n“Tepkiler†ile diÄŸer kullanıcıların notları hakkındaki duygularınızı hızlı bir ÅŸekilde ifade edebilirsiniz. ðŸ‘\nYeni bir dünya sizi bekliyor! 🚀"
+poweredByMisskeyDescription: "{name}, açık kaynak platformu <b>Misskey</b> (kısaca “Misskey örneği†olarak anılır) tarafından desteklenen hizmetlerden biridir."
+monthAndDay: "{day}.{month}."
+search: "Ara"
+reset: "Sıfırla"
+notifications: "Bildirimler"
username: "Kullanıcı Adı"
password: "Åžifre"
-initialPasswordForSetup: ""
-forgotPassword: "ÅŸifremi unuttum"
-fetchingAsApObject: "從è¯é‚¦å®‡å®™å–得中..."
-ok: "TAMAM"
-gotIt: "Anladım"
-cancel: "İptal"
-noThankYou: "Hayır, teşekkürler"
-enterUsername: "Kullanıcı adınızı giriniz"
-renotedBy: "{user} tarafından Renotelandı"
-noNotes: "Notlar mevcut deÄŸil."
-noNotifications: "Bildirim bulunmuyor"
+initialPasswordForSetup: "Kurulum için ilk şifre"
+initialPasswordIsIncorrect: "Kurulum için ilk şifre yanlış"
+initialPasswordForSetupDescription: "Misskey'i kendiniz kurduysan, yapılandırma dosyasında belirtilen şifreyi kullan.\nMisskey barındırma hizmeti veya benzeri bir hizmet kullanıyorsan, orada belirtilen şifreyi kullan.\nŞifre belirlemediysen, devam etmek için boş bırak."
+forgotPassword: "Åžifremi unuttum"
+fetchingAsApObject: "Fediverse'den talep ediliyor..."
+ok: "Tamam"
+gotIt: "Anladım!"
+cancel: "Vazgeç"
+noThankYou: "Hayır, teşekkürler."
+enterUsername: "Kullanıcı adı gir"
+renotedBy: "{user} renote etti"
+noNotes: "Not yok"
+noNotifications: "Bildirim yok"
instance: "Sunucu"
settings: "Ayarlar"
notificationSettings: "Bildirim Ayarları"
basicSettings: "Temel Ayarlar"
otherSettings: "DiÄŸer Ayarlar"
-openInWindow: "Bir pencere ile aç"
+openInWindow: "Pencerede aç"
profile: "Profil"
-timeline: "Zaman çizelgesi"
-noAccountDescription: "Bu kullanıcı henüz biyografisini yazmadı"
-login: "GiriÅŸ Yap "
-loggingIn: "Oturum aç"
+timeline: "Pano"
+noAccountDescription: "Bu kullanıcı henüz biyografisini yazmamış."
+login: "Oturum Aç"
+loggingIn: "Giriş Yapılıyor..."
logout: "Çıkış Yap"
-signup: "Kayıt Ol"
-uploading: "Yükleniyor"
+signup: "Kaydol"
+uploading: "Yükleniyor..."
save: "Kaydet"
-users: "Kullanıcı"
-addUser: "Kullanıcı Ekle"
-favorite: "Favoriler"
+users: "Kullanıcılar"
+addUser: "Kullanıcı ekle"
+favorite: "Favori"
favorites: "Favoriler"
-unfavorite: "Favorilerden Kaldır"
-favorited: "Favorilerime eklendi."
-alreadyFavorited: "Zaten favorilerinizde kayıtlı."
-cantFavorite: "Favorilere kayıt yapılamadı"
-pin: "SabitlenmiÅŸ"
-unpin: "Sabitlemeyi kaldır"
+unfavorite: "Favoriden kaldır"
+favorited: "Favoriye eklendi."
+alreadyFavorited: "Zaten favoride"
+cantFavorite: "Favoriye eklenemedi"
+pin: "Profiline sabitle"
+unpin: "Profilden sabitlemeyi kaldır"
copyContent: "İçeriği kopyala"
-copyLink: "Bağlantıyı Kopyala"
-copyLinkRenote: "Turkish"
+copyLink: "Link kopyala"
+copyRemoteLink: "Uzak linki kopyala"
+copyLinkRenote: "Renote linkini kopyala"
delete: "Sil"
deleteAndEdit: "Sil ve yeniden düzenle"
-deleteAndEditConfirm: "Bu notu silip yeniden düzenlemek istiyor musunuz? Bu nota ilişkin tüm Tepkiler, Yeniden Notlar ve Yanıtlar da silinecektir."
+deleteAndEditConfirm: "Bu notu silip yeniden düzenlemek ister misin? Bu notla ilgili tüm Tepki, Renote ve Yanıtlar da silinecek."
addToList: "Listeye ekle"
-addToAntenna: "Antene ekle"
-sendMessage: "Mesaj Gönder"
-copyRSS: "RSSKopyala"
-copyUsername: "Kullanıcı Adını Kopyala"
-copyUserId: "KullanıcıyıKopyala"
-copyNoteId: "Kimlik notunu kopyala"
-copyFileId: "Dosya ID'sini kopyala"
-copyFolderId: "Klasör ID'sini kopyala"
-copyProfileUrl: "Profil URL'sini kopyala"
-searchUser: "Kullanıcıları ara"
-reply: "yanıt"
-loadMore: "Devamını yükle"
-showMore: "Devamını yükle"
+addToAntenna: "Anten'e ekle"
+sendMessage: "Mesaj gönder"
+copyRSS: "RSS kopyala"
+copyUsername: "Kullanıcı adını kopyala"
+copyUserId: "Kullanıcı ID'yi kopyala"
+copyNoteId: "Not ID'yi kopyala"
+copyFileId: "Dosya ID'yi kopyala"
+copyFolderId: "Klasör ID'yi kopyala"
+copyProfileUrl: "Profil URL kopyala"
+searchUser: "Kullanıcı ara"
+searchThisUsersNotes: "Kullanıcının notlarını ara"
+reply: "Yanıtla"
+loadMore: "Daha fazla yükle"
+showMore: "Daha fazlasını göster"
showLess: "Kapat"
youGotNewFollower: "seni takip etti"
receiveFollowRequest: "Takip isteği alındı"
followRequestAccepted: "Takip isteÄŸi kabul edildi"
mention: "Bahset"
mentions: "Bahsetmeler"
-directNotes: "KiÅŸisel mesajlar"
+directNotes: "DoÄŸrudan notlar"
importAndExport: "İçeri/Dışarı aktar"
import: "İçeri aktar"
export: "Dışa aktar"
files: "Dosyalar"
download: "İndir"
-driveFileDeleteConfirm: "\"{name}\" dosyası silinsin mi? Dosya kullanıldığı tüm notlardan kaybolacaktır."
-unfollowConfirm: "{name} takipten çıkarılsın mı?"
-exportRequested: "Dışa aktarım talep ettiniz. Bu biraz zaman alabilir. İşlem bitince Sürücünüze eklenecektir."
-importRequested: "Dışa aktarım talep ettiniz. Bu işlem biraz zaman alabilir."
+driveFileDeleteConfirm: "“{name}†dosyasını silmek istediğinden emin misin? Bu dosyaya ekli tüm notlar da silinecek."
+unfollowConfirm: "{name} kullanıcısını cidden takipden çıkmak istiyor musun?"
+exportRequested: "Dışa aktarma işlemi talep ettin. Bu işlem biraz zaman alabilir. İşlem tamamlandığında Drive'ına eklenecek."
+importRequested: "İçe aktarma talebinde bulundun. Bu işlem biraz zaman alabilir."
lists: "Listeler"
-noLists: "Liste yok"
-note: "not"
-notes: "notlar"
-following: "takipçi"
-followers: "takipçi"
-followsYou: "seni takip ediyor"
+noLists: "Hiç liste yok"
+note: "Not"
+notes: "Notlar"
+following: "Takip"
+followers: "Takipçi"
+followsYou: "Sizi takip ediyor"
createList: "Liste oluÅŸtur"
-manageLists: "Yönetici Listeleri"
-error: "hata"
+manageLists: "Listeleri yönet"
+error: "Hata"
somethingHappened: "Bir hata oluÅŸtu"
retry: "Tekrar dene"
-pageLoadError: "Sayfa yüklenemedi."
-pageLoadErrorDescription: "Bu genelde ağ veya tarayıcı ön belleği hatalarından olur. Lütfen ön belleği temizlemeyi veya birkaç dakika beklemeyi ve sayfayı yenilemeyi deneyin."
-serverIsDead: "Sunucu yanıt vermiyor. Birkaç dakika sonra tekrar deneyin."
-youShouldUpgradeClient: "Sayfayı görüntülemek için yenileyin."
-enterListName: "Liste ismi"
+pageLoadError: "Sayfa yüklenirken bir hata oluştu."
+pageLoadErrorDescription: "Bu durum genellikle ağ hataları veya tarayıcının önbelleği nedeniyle oluşur. Önbelleği temizleyin ve bir süre bekledikten sonra tekrar dene."
+serverIsDead: "Bu sunucu yanıt vermiyor. Lütfen bir süre bekleyin ve tekrar dene."
+youShouldUpgradeClient: "Bu sayfayı görüntülemek için lütfen yenileyerek istemcini güncelle."
+enterListName: "Listeye bir ad girin"
privacy: "Gizlilik"
-makeFollowManuallyApprove: "Takip istekleri elle onaylansın"
+makeFollowManuallyApprove: "Takip istekleri onay gerektirir"
defaultNoteVisibility: "Varsayılan görünürlük"
-follow: "takipçi"
-followRequest: "Takip isteÄŸi"
+follow: "Takip et"
+followRequest: "Takip isteği gönder"
followRequests: "Takip istekleri"
-unfollow: "takip etmeyi bırak"
-followRequestPending: "Bekleyen Takip Etme Talebi"
-enterEmoji: "Emoji Giriniz"
-renote: "vazgeçme"
-unrenote: "not alma"
-renoted: "yeniden adlandırılmış"
-cantRenote: "Ayrılamama"
-cantReRenote: "not alabilirmiyim"
-quote: "alıntı"
-inChannelRenote: "Kanal içi Renote"
-inChannelQuote: "Kanal içi Alıntı"
-pinnedNote: "Sabitlenen"
-pinned: "SabitlenmiÅŸ"
-you: "sen"
-clickToShow: "Görüntülemek için tıkla"
-sensitive: "Hassas içerik"
+unfollow: "Takibi bırak"
+followRequestPending: "Takip isteÄŸi beklemede"
+enterEmoji: "Bir emoji gir"
+renote: "Renote"
+unrenote: "Renote geri al"
+renoted: "Renote yapıldı."
+renotedToX: "{name} adresine Renote"
+cantRenote: "Bu not renote edilemez."
+cantReRenote: "Renote yeniden Renote edilemez."
+quote: "Alıntı"
+inChannelRenote: "Kanal içi renote"
+inChannelQuote: "Kanal içi alıntı"
+renoteToChannel: "Kanala Renote"
+renoteToOtherChannel: "DiÄŸer kanala Renote\n"
+pinnedNote: "Sabit not"
+pinned: "Profiline sabitle"
+you: "Sen"
+clickToShow: "Göstermek için tıklayın"
+sensitive: "Hassas"
add: "Ekle"
-reaction: "Tepkiler"
+reaction: "Tepki"
reactions: "Tepkiler"
-reactionSettingDescription2: "Sıralamak için sürükleyin, silmek için tıklayın, eklemek için \"+\" tuşuna tıklayın."
-rememberNoteVisibility: "Görünürlük ayarlarını hatırla"
-attachCancel: "Eki sil"
+emojiPicker: "Emoji seçici"
+pinnedEmojisForReactionSettingDescription: "Tepki verirken sabitlenecek ve görüntülenecek emojileri ayarlayın."
+pinnedEmojisSettingDescription: "Emoji seçiciyi görüntülerken sabitlenecek ve görüntülenecek emojileri ayarlayın"
+emojiPickerDisplay: "Emoji seçici ekranı"
+overwriteFromPinnedEmojisForReaction: "Tepki ayarlarından geçersiz kılma"
+overwriteFromPinnedEmojis: "Genel ayarlardan geçersiz kılma"
+reactionSettingDescription2: "Sıralamayı değiştirmek için sürükle, silmek için tıkla, eklemek için “+†tuşuna bas."
+rememberNoteVisibility: "Not görünürlük ayarlarını hatırla"
+attachCancel: "Eki kaldır"
+deleteFile: "Dosyayı sil"
markAsSensitive: "Hassas içerik olarak işaretle"
unmarkAsSensitive: "Hassas içerik işaretini kaldır"
enterFileName: "Dosya ismini gir"
mute: "Gizle"
unmute: "sesi aç"
renoteMute: "sesi kapat"
-renoteUnmute: "sesi açmayı iptal et"
+renoteUnmute: "Renote sessiz modunu kaldır"
block: "engelle"
unblock: "engellemeyi kaldır"
suspend: "askıya al"
-unsuspend: "askıya alma"
-blockConfirm: "Onayı engelle"
-unblockConfirm: "engellemeyi kaldır onayla"
+unsuspend: "askıya almayı kaldır"
+blockConfirm: "Engeli onayla"
+unblockConfirm: "Engel kaldırmayı onayla"
suspendConfirm: "Hesap askıya alınsın mı?"
-unsuspendConfirm: "Hesap askıdan kaldırılsın mı"
+unsuspendConfirm: "Hesap askıdan kaldırılsın mı?"
selectList: "Bir liste seç"
editList: "Listeyi düzenle"
selectChannel: "Kanal seç"
selectAntenna: "Bir anten seç"
editAntenna: "Anteni düzenle"
+createAntenna: "Bir anten oluÅŸturun"
selectWidget: "Araç seç"
editWidgets: "Araçları düzenle"
editWidgetsExit: "Tamam"
@@ -156,309 +172,3082 @@ emojiUrl: "Emoji URL'si"
addEmoji: "Emoji ekle"
settingGuide: "Önerilen ayarlar"
cacheRemoteFiles: "Uzak dosyalar ön belleğe alınsın"
-cacheRemoteFilesDescription: "Bu ayar açık olduğunda diğer sitelerin dosyaları doğrudan uzak sunucudan yüklenecektir. Bu ayarı kapatmak depolama kullanımını azaltacak ama küçük resimler oluşturulmadığından trafiği arttıracaktır."
-youCanCleanRemoteFilesCache: ""
+cacheRemoteFilesDescription: "Bu ayar açık olduğunda diğer sitelerin dosyaları doğrudan uzak sunucudan yüklenece. Bu ayarı kapatmak depolama kullanımını azaltacak ama küçük resimler oluşturulmadığından trafiği arttıracak."
+youCanCleanRemoteFilesCache: "Dosya yönetimi görünümünde ðŸ—‘ï¸ düğmesine tıklayarak önbelleÄŸi temizleyebilirsin."
cacheRemoteSensitiveFiles: "Hassas uzak dosyalar ön belleğe alınsın"
-cacheRemoteSensitiveFilesDescription: "Bu ayar kapalı olduğunda hassas uzak dosyalar ön belleğe alınmadan doğrudan uzak sunucudan yüklenecektir."
+cacheRemoteSensitiveFilesDescription: "Bu ayar kapalı olduğunda hassas uzak dosyalar ön belleğe alınmadan doğrudan uzak sunucudan yüklenecek."
flagAsBot: "Bot olarak iÅŸaretle"
-flagAsBotDescription: "Bu seçeneği hesap bir program tarafından kontrol ediliyorsa işaretleyin. Bu, diğer geliştiricilerin sonsuz etkileşim zincirleri oluşturmasını engellemeye yardımcı olur ve Misskey'in iç sisteminin hesaba bir bot gibi davranmasını sağlar."
+flagAsBotDescription: "Bu hesap bir program tarafından kontrol ediliyorsa bu seçeneği etkinleştir. Etkinleştirildiğinde, diğer geliştiriciler için bir işaret görevi görerek diğer botlarla sonsuz etkileşim zincirlerini önleyecek ve Misskey'in iç sistemlerini bu hesabı bir bot olarak ele alacak şekilde ayarlayacak."
flagAsCat: "Kedi hesabı"
flagAsCatDescription: "Kedi hesabı"
-flagShowTimelineReplies: "Zaman akışında notlara gelen cevapları göster"
-flagShowTimelineRepliesDescription: "Açık olduğu durumda, zaman akışında kullanıcıların başkalarına verdiği cevaplar gözükür."
+flagShowTimelineReplies: "Pano'da notlara gelen cevapları göster"
+flagShowTimelineRepliesDescription: "Açık olduğu durumda, Pano'da kullanıcıların başkalarına verdiği cevaplar gözükür."
autoAcceptFollowed: "Takip edilen hesapların takip isteklerini kabul et"
addAccount: "Hesap ekle"
reloadAccountsList: "Hesap listesini güncelle"
loginFailed: "Giriş başarısız oldu"
showOnRemote: "Uzak sunucuda görüntüle"
+continueOnRemote: "Uzak bir sunucuda devam edin"
+chooseServerOnMisskeyHub: "Misskey Hub'dan bir sunucu seçin."
+specifyServerHost: "Doğrudan bir sunucu ana bilgisayarı belirtin"
+inputHostName: "Alan adını girin"
general: "Genel"
wallpaper: "Duvar kağıdı"
setWallpaper: "Duvar kağıdını ayarla"
-removeWallpaper: "Duvar kağıdını sil"
-searchWith: "Arama: {q}"
-youHaveNoLists: "Hiç listeniz yok"
-followConfirm: "{name} takip edilsin mi?"
-proxyAccount: "Vekil hesabı"
-proxyAccountDescription: "Proxy hesabı, belirli koşullar altında kullanıcılar için uzaktan takipçi işlevi gören bir hesaptır. Örneğin, bir kullanıcı listeye bir uzak kullanıcı eklediğinde, o kullanıcıyı takip eden yerel bir kullanıcı yoksa uzak kullanıcının etkinliği örneğe teslim edilmeyecektir, dolayısıyla bunun yerine proxy hesabı takip edilecektir."
-host: "Sağlayıcı"
+removeWallpaper: "Duvar kağıdını kaldır"
+searchWith: "Ara: {q}"
+youHaveNoLists: "Hiç listeniz yok."
+followConfirm: "{name} kullanıcısını takip etmek istediğinden emin misin?"
+proxyAccount: "Proxy hesabı"
+proxyAccountDescription: "Proxy hesabı, belirli koşullar altında kullanıcılar için uzaktan takipçi görevi gören bir hesap. Örneğin, bir kullanıcı listeye uzaktan bir kullanıcı eklediğinde, o kullanıcıyı takip eden yerel kullanıcı yoksa uzaktan kullanıcının etkinliği örneğe iletilmez, bunun yerine proxy hesabı takip eder."
+host: "Host"
+selectSelf: "Kendimi seç"
selectUser: "Kullanıcı seç"
-recipient: "Kime"
-annotation: "Açıklamalar"
+recipient: "Alıcı"
+annotation: "Yorumlar"
federation: "Federasyon"
-instances: "Sunucu"
-registeredAt: "Katılma tarihi"
-latestRequestReceivedAt: "Alınan son talep"
-latestStatus: "En son durum"
+instances: "Sunucular"
+registeredAt: "Kayıtlı"
+latestRequestReceivedAt: "Son talep alındı"
+latestStatus: "Son durum"
storageUsage: "Depolama kullanımı"
-charts: "Çizelgeler"
+charts: "Grafikler"
perHour: "Saatlik"
perDay: "Günlük"
-stopActivityDelivery: "Durum güncellemelerini gönderme"
+stopActivityDelivery: "Etkinlik göndermeyi durdur"
blockThisInstance: "Bu sunucuyu engelle"
-silenceThisInstance: ""
-operations: "İşlemler"
-software: "Yazılımlar"
+silenceThisInstance: "Bu sunucuyu sustur"
+mediaSilenceThisInstance: "Medya bu sunucuyu sustursun"
+operations: "Operasyonlar"
+software: "Yazılım"
+softwareName: "Yazılım"
version: "Sürüm"
-metadata: "Meta Verileri"
-withNFiles: "{n} tane dosya"
+metadata: "Meta veri"
+withNFiles: "{n} dosya"
monitor: "Monitör"
jobQueue: "İşlem sırası"
-cpuAndMemory: "İşlemci ve Hafıza"
+cpuAndMemory: "CPU ve Bellek"
network: "AÄŸ"
disk: "Disk"
instanceInfo: "Sunucu Bilgisi"
statistics: "İstatistikler"
-clearQueue: "Sırayı temizle"
-clearQueueConfirmTitle: "Sıra silinsin mi?"
-clearQueueConfirmText: "Sırada kalan hiçbir şey iletilmeyecek. Genelde bu işlem gerekli değildir."
-clearCachedFiles: "Ön belleği temizle"
-clearCachedFilesConfirm: "Ön belleğe alınmış tüm uzak sunucu dosyaları silinsin mi?"
-blockedInstances: "Engellenen sunucular"
-blockedInstancesDescription: "Engellemek istediğiniz sunucuların alan adlarını satır sonlarıyla ayırarak yazın. Yazılan sunucular bu sunucuyla iletişime geçemeyecek."
-silencedInstances: "Turkısh"
-silencedInstancesDescription: ""
-muteAndBlock: "Susturma ve Engelleme"
-mutedUsers: "Susturulan kullanıcılar"
+clearQueue: "KuyruÄŸu temizle"
+clearQueueConfirmTitle: "KuyruÄŸu silmek istediÄŸinden emin misin?"
+clearQueueConfirmText: "Kuyrukta kalan teslim edilmemiÅŸ notlar birleÅŸtirilmeyecek. Genellikle bu iÅŸlem gerekli deÄŸildir."
+clearCachedFiles: "Önbelleği temizle"
+clearCachedFilesConfirm: "Tüm önbelleğe alınmış uzak dosyaları silmek istediğinden emin misin?"
+blockedInstances: "Engellenen Sunucu"
+blockedInstancesDescription: "Engellemek istediğin sunucuların ana bilgisayar adlarını satır sonlarıyla ayırarak liste. Listelenen örnekler artık bu örnekle iletişim kuramayacaktır."
+silencedInstances: "SusturulmuÅŸ sunucular"
+silencedInstancesDescription: "Sessize almak istediğin sunucuların ana bilgisayar adlarını yeni bir satırla ayırarak listele. Listelenen sunuculara ait tüm hesaplar sessize alınmış olarak kabul edilecek ve yalnızca takip isteklerinde bulunabilecek, takip edilmedikleri takdirde yerel hesapları etiketleyemeyeceklerdir. Bu, engellenen sunucuları etkilemeyecek."
+mediaSilencedInstances: "Medya susturulmuÅŸ sunucular"
+mediaSilencedInstancesDescription: "Medya sessize almak istediğin sunucuların ana bilgisayar adlarını yeni bir satırla ayırarak liste. Listelenen sunuculara ait tüm hesaplar hassas hesap olarak değerlendirilecek ve özel emojiler kullanılamayacaktır. Bu durum, engellenen sunucuları etkilemeyecek."
+federationAllowedHosts: "Federasyona izin verilen sunucular"
+federationAllowedHostsDescription: "Federasyona izin vermek istediğiniz sunucuların ana bilgisayar adlarını satır sonlarıyla ayırın."
+muteAndBlock: "Sessize Alma ve Engelleme"
+mutedUsers: "Sessize alınan kullanıcılar"
blockedUsers: "Engellenen kullanıcılar"
noUsers: "Kullanıcı yok"
editProfile: "Profili düzenle"
-noteDeleteConfirm: "Bu notu silmek istediÄŸinizden emin misiniz?"
-pinLimitExceeded: "Daha fazla not sabitlenemez"
-done: "Tamamlandı"
+noteDeleteConfirm: "Bu notu silmek istediÄŸinden emin misin?"
+pinLimitExceeded: "Artık daha fazla not sabitleyemezsin"
+done: "Tamam"
+processing: "İşleniyor..."
preview: "Önizleme"
default: "Varsayılan"
defaultValueIs: "Varsayılan: {value}"
-noCustomEmojis: "Emoji bulunamadı"
-noJobs: "Hiç işlem yok"
-federating: "Federe ediliyor"
+noCustomEmojis: "Emoji yok"
+noJobs: "Hiç ş yok"
+federating: "BirleÅŸtirme"
blocked: "EngellenmiÅŸ"
suspended: "Askıya alınmış"
all: "Tümü"
subscribing: "Abonelik"
publishing: "Paylaşım"
-notResponding: "Cevap yok"
-instanceFollowing: "Sunucuda takip edenler"
+notResponding: "Yanıt vermiyor"
+instanceFollowing: "Sunucuda takip"
instanceFollowers: "Sunucu takipçileri"
-instanceUsers: "Sunucu kullanıcıları"
+instanceUsers: "Bu sunucunun kullanıcıları"
changePassword: "Åžifreyi deÄŸiÅŸtir"
security: "Güvenlik"
-retypedNotMatch: "GiriÅŸler uyuÅŸmuyor."
-currentPassword: "Geçerli şifre"
+retypedNotMatch: "GiriÅŸler eÅŸleÅŸmiyor."
+currentPassword: "Mevcut ÅŸifre"
newPassword: "Yeni ÅŸifre"
-newPasswordRetype: "Yeni ÅŸifre (tekrar)"
-attachFile: "Dosya ekle"
-more: "Daha!"
-featured: "Öne Çıkan"
-usernameOrUserId: "Kullanıcı adı veya ID'si"
+newPasswordRetype: "Yeni ÅŸifreyi tekrar girin"
+attachFile: "Dosyaları ekle"
+more: "Daha fazlası!"
+featured: "Öne çıkan"
+usernameOrUserId: "Kullanıcı adı veya ID"
noSuchUser: "Kullanıcı bulunamadı"
lookup: "Sorgu"
announcements: "Duyurular"
-imageUrl: "Görsel URL'si"
+imageUrl: "Görsel URL"
remove: "Sil"
removed: "Silindi"
-removeAreYouSure: "\"{x}\" silmek istediÄŸinizden emin misiniz?"
-deleteAreYouSure: "\"{x}\" silmek istediÄŸinizden emin misiniz?"
-resetAreYouSure: "Sıfırlansın mı?"
+removeAreYouSure: "“{x}†öğesini kaldırmak istediğinizden emin misin?"
+deleteAreYouSure: "“{x}†öğesini silmek istediğinizden emin misin?"
+resetAreYouSure: "Cidden sıfırlansın mı?"
+areYouSure: "Emin misin?"
saved: "Kaydedildi"
upload: "Yükle"
-keepOriginalUploading: "Orijinal görseli koru"
-keepOriginalUploadingDescription: "Orijinal olarak yüklenen görüntüyü olduğu gibi kaydeder. Kapatılırsa, yükleme sırasında web'de görüntülenecek bir sürüm oluşturulur."
-fromDrive: "Drive Dosyasından"
-fromUrl: "Bağlantıdan"
-uploadFromUrl: "Bağlantıdan yükle"
-uploadFromUrlDescription: "Yüklemek istediğiniz dosyanın bağlantısı"
-uploadFromUrlRequested: "Yükleme talep edildi"
-uploadFromUrlMayTakeTime: "Yüklemenin tamamlanması biraz süre alabilir."
+keepOriginalUploading: "Orijinal görüntüyü koru"
+keepOriginalUploadingDescription: "Orijinal olarak yüklenen görüntüyü olduğu gibi kaydeder. Kapalıysa, yükleme sırasında web'de görüntülenecek bir sürüm oluşturulur."
+fromDrive: "Drive'den"
+fromUrl: "URL'den"
+uploadFromUrl: "URL'den yükle"
+uploadFromUrlDescription: "Yüklemek istediğiniz dosyanın URL'si"
+uploadFromUrlRequested: "Yükleme istendi"
+uploadFromUrlMayTakeTime: "Yükleme işleminin tamamlanması biraz zaman alabilir."
+uploadNFiles: "{n} dosya yükle"
explore: "KeÅŸfet"
-messageRead: "Okundu"
-noMoreHistory: "Bundan öncesi yok"
-nUsersRead: "{n} kiÅŸi okudu"
-agreeTo: "Kabul Ediyorum: {0}"
-agree: "Kabul Et"
-agreeBelow: "Aşağıdakileri kabul ederim"
+messageRead: "Oku"
+noMoreHistory: "Daha fazla geçmiş bilgisi yok."
+startChat: "Sohbete baÅŸla"
+nUsersRead: "{n} tarafından okundu"
+agreeTo: "{0}'ı kabul ediyorum."
+agree: "Kabul ediyorum"
+agreeBelow: "Aşağıdakileri kabul ediyorum"
basicNotesBeforeCreateAccount: "Önemli notlar"
-termsOfService: "Åžartlar ve KoÅŸullar"
+termsOfService: "Hizmet Şartları"
start: "BaÅŸla"
-home: "Ana sayfa"
-remoteUserCaution: "Bu kullanıcı bir uzak sunucudan olduğu için alınan bilgiler tam olmayabilir."
+home: "Pano"
+remoteUserCaution: "Bu kullanıcı uzak bir sunucudan geldiği için, gösterilen bilgiler eksik olabilir."
activity: "Etkinlik"
images: "Görseller"
-image: "Görseller"
+image: "Görsel"
birthday: "Doğum günü"
yearsOld: "{age} yaşında"
-registeredDate: "Kayıt tarihi"
+registeredDate: "Katılma tarihi"
location: "Konum"
-theme: "Temalar"
-themeForLightMode: "Aydınlık Tema"
-themeForDarkMode: "Karanlık Tema"
+theme: "Tema"
+themeForLightMode: "Aydınlık Mod'da kullanılacak tema"
+themeForDarkMode: "Karanlık Mod'da kullanılacak tema"
light: "Aydınlık"
dark: "Karanlık"
-lightThemes: "Aydınlık Temalar"
-darkThemes: "Karanlık Temalar"
-syncDeviceDarkMode: "Sistem Koyu Modu ile senkronize et"
-drive: "Sürücü"
+lightThemes: "Aydınlık temalar"
+darkThemes: "Karanlık temalar"
+syncDeviceDarkMode: "Karanlık Modu cihaz ayarlarınızla senkronize et"
+switchDarkModeManuallyWhenSyncEnabledConfirm: "\"{x}\" açık. Senkronizasyonu kapatıp modları manuel olarak değiştirmek ister misin?"
+drive: "Drive"
fileName: "Dosya adı"
-selectFile: "Dosya seç"
-selectFiles: "Dosya seç"
-selectFolder: "Klasör seç"
-selectFolders: "Klasör seç"
+selectFile: "Dosya seçin"
+selectFiles: "Dosyaları seçin"
+selectFolder: "Klasör seçin"
+selectFolders: "Klasörleri seçin"
+fileNotSelected: "Hiç dosya seçilmedi"
renameFile: "Dosyayı yeniden adlandır"
folderName: "Klasör adı"
-createFolder: "Klasör oluştur"
-renameFolder: "Klasörü Yeniden Adlandır"
-deleteFolder: "Klasörü sil"
-addFile: "Dosya ekle"
-emptyDrive: "Sürücü boş"
+createFolder: "Bir klasör oluşturun"
+renameFolder: "Bu klasörü yeniden adlandır"
+deleteFolder: "Bu klasörü sil"
+folder: "Dosya"
+addFile: "Bir dosya ekle"
+showFile: "Dosyaları göster"
+emptyDrive: "Drive boÅŸ"
emptyFolder: "Bu klasör boş"
-unableToDelete: "Silme mümkün değil"
-inputNewFileName: "Yeni dosya ismini girin"
-inputNewDescription: "Yeni bir başlık gir"
-inputNewFolderName: "Yeni klasör ismini girin"
-circularReferenceFolder: "Hedef klasör taşınan klasörün bir alt klasörü."
-hasChildFilesOrFolders: "Klasör boş olmadığından silinemiyor"
-copyUrl: "URL'yi kopyala"
+unableToDelete: "Silinemiyor"
+inputNewFileName: "Yeni bir dosya adı girin"
+inputNewDescription: "Yeni alternatif metin girin"
+inputNewFolderName: "Yeni bir klasör adı girin"
+circularReferenceFolder: "Hedef klasör, taşımak istediğiniz klasörün bir alt klasörü."
+hasChildFilesOrFolders: "Bu klasör boş olmadığı için silinemez."
+copyUrl: "URL kopyala"
rename: "Yeniden adlandır"
avatar: "Avatar"
banner: "Banner"
-displayOfSensitiveMedia: "Hassas içerik gösterimi"
-whenServerDisconnected: "Sunucu bağlantısı kesildiğinde"
-disconnectedFromServer: "Sunucu bağlantısı koptu"
+displayOfSensitiveMedia: "Hassas ortamların görüntülenmesi"
+whenServerDisconnected: "Sunucu ile bağlantı kesildiğinde"
+disconnectedFromServer: "Sunucu bağlantısı kesildi"
reload: "Yenile"
-doNothing: "Bir ÅŸey yapma"
-reloadConfirm: "Zaman akışı yenilensin mi?"
+doNothing: "Yoksay"
+reloadConfirm: "Zaman çizelgesini yenilemek ister misin?"
watch: "İzle"
unwatch: "İzlemeyi bırak"
accept: "Kabul et"
reject: "Reddet"
normal: "Normal"
-instanceName: "Sunucu ismi"
+instanceName: "Sunucu adı"
instanceDescription: "Sunucu açıklaması"
-maintainerName: "Yönetici ismi"
-maintainerEmail: "Yöneticinin e-postası"
-tosUrl: "Hizmet Koşulları Bağlantısı"
-thisYear: "Bu yıl"
-thisMonth: "Bu ay"
+maintainerName: "Bakım sorumlusu"
+maintainerEmail: "Bakım sorumlusu E-Posta adresi"
+tosUrl: "Hizmet Şartları URL'si"
+thisYear: "Yıl"
+thisMonth: "Ay"
today: "Bugün"
-monthX: "{month} ay"
+dayX: "{day}"
+monthX: "{month}"
+yearX: "{year}"
pages: "Sayfalar"
integration: "Entegrasyon"
+connectService: "BaÄŸlan"
+disconnectService: "Bağlantıyı kes"
+enableLocalTimeline: "Yerel Pano'yu etkinleÅŸtir"
+enableGlobalTimeline: "Global Pano'yu etkinleÅŸtir"
+disablingTimelinesInfo: "Yöneticiler ve Moderatörler, etkinleştirilmemiş olsalar bile her zaman tüm Pano'ya erişebilecekler."
+registration: "Kaydol"
+invite: "Davet et"
+driveCapacityPerLocalAccount: "Yerel kullanıcı başına Drive kapasitesi"
+driveCapacityPerRemoteAccount: "Uzak kullanıcı başına Drive kapasitesi"
+inMb: "Megabayt cinsinden"
+bannerUrl: "Banner görseli URL"
+backgroundImageUrl: "Arka plan görseli URL"
basicInfo: "Temel bilgiler"
pinnedUsers: "Sabitlenmiş kullanıcılar"
-pinnedNotes: "Sabitlenen"
-manageAntennas: "Anten ayarları"
+pinnedUsersDescription: "“Keşfet†sekmesinde sabitlenecek kullanıcı adlarını satır sonlarıyla ayırarak liste."
+pinnedPages: "SabitlenmiÅŸ Sayfalar"
+pinnedPagesDescription: "Bu örneğin üst sayfasına sabitlemek istediğin Sayfaların yollarını satır sonlarıyla ayırarak gir."
+pinnedClipId: "Sabitlenecek klibin ID"
+pinnedNotes: "SabitlenmiÅŸ notlar"
+hcaptcha: "hCaptcha"
+enableHcaptcha: "hCaptcha etkinleÅŸtir"
+hcaptchaSiteKey: "Site anahtar"
+hcaptchaSecretKey: "Gizli anahtar"
+mcaptcha: "mCaptcha"
+enableMcaptcha: "mCaptcha etkinleÅŸtir"
+mcaptchaSiteKey: "Site anahtarı"
+mcaptchaSecretKey: "Gizli anahtar"
+mcaptchaInstanceUrl: "mCaptcha sunucu URL'si"
+recaptcha: "reCAPTCHA"
+enableRecaptcha: "reCAPTCHA etkinleÅŸtir"
+recaptchaSiteKey: "Site anahtar"
+recaptchaSecretKey: "Gizli anahtar"
+turnstile: "Turnstile"
+enableTurnstile: "Turnstile etkinleÅŸtir"
+turnstileSiteKey: "Site anahtar"
+turnstileSecretKey: "Gizli anahtar"
+avoidMultiCaptchaConfirm: "Birden fazla Captcha sistemi kullanmak, aralarında çakışmaya neden olabilir. Şu anda etkin olan diğer Captcha sistemlerini devre dışı bırakmak ister misiniz? Etkin kalmalarını istiyorsan, iptal düğmesine bas."
+antennas: "Antenler"
+manageAntennas: "Antenleri Yönet"
+name: "İsim"
+antennaSource: "Anten kaynağı"
+antennaKeywords: "Dinlenecek anahtar kelimeler"
+antennaExcludeKeywords: "Hariç tutulacak anahtar kelimeler"
+antennaExcludeBots: "Bot hesaplarını hariç tut"
+antennaKeywordsDescription: "VE koşulu için boşluklarla, VEYA koşulu için satır sonlarıyla ayırın."
+notifyAntenna: "Yeni notlar hakkında bildirimde bulunun"
+withFileAntenna: "Sadece dosyalı notlar"
+excludeNotesInSensitiveChannel: "Hassas kanallardan gelen notları hariç tutun"
+enableServiceworker: "Tarayıcınız için Push Bildirimlerini Etkinleştir"
+antennaUsersDescription: "Satır başına bir kullanıcı adı listele"
+caseSensitive: "Harfe duyarlı"
+withReplies: "Yanıtları ekle"
+connectedTo: "Aşağıdaki hesap(lar) bağlı"
+notesAndReplies: "Notlar ve yanıtlar"
+withFiles: "Dosyalar dahil"
+silence: "Sessize al"
+silenceConfirm: "Bu kullanıcıyı susturmak istediğinden emin misin?"
+unsilence: "Sessize almayı geri al"
+unsilenceConfirm: "Bu kullanıcının sessize alınmasını geri almak istediğinden emin misin?"
+popularUsers: "Popüler kullanıcılar"
+recentlyUpdatedUsers: "Son zamanlarda aktif olan kullanıcılar"
+recentlyRegisteredUsers: "Yeni katılan kullanıcılar"
+recentlyDiscoveredUsers: "Yeni keşfedilen kullanıcılar"
+exploreUsersCount: "{count} kullanıcı var"
+exploreFediverse: "Fediverse'i keÅŸfedin"
+popularTags: "Popüler etiketler"
userList: "Listeler"
-resetPassword: "Şifre sıfırlama"
-details: "Detaylar"
-deck: "Güverte"
-smtpHost: "Sağlayıcı"
-smtpUser: "Kullanıcı Adı"
+about: "Hakkında"
+aboutMisskey: "Misskey Hakkında"
+administrator: "Yönetici"
+token: "Token"
+2fa: "İki faktörlü kimlik doğrulama"
+setupOf2fa: "İki faktörlü kimlik doğrulamayı ayarlayın"
+totp: "Authenticator Uygulaması"
+totpDescription: "Tek seferlik şifreleri girmek için bir kimlik doğrulama uygulaması kullanın"
+moderator: "Moderatör"
+moderation: "Moderasyon"
+moderationNote: "Moderasyon notu"
+moderationNoteDescription: "Moderatörler arasında paylaşılacak notları girebilirsin."
+addModerationNote: "Moderasyon notu ekle"
+moderationLogs: "Moderasyon günlükleri"
+nUsersMentioned: "{n} kullanıcı bahsetti"
+securityKeyAndPasskey: "Güvenlik ve geçiş anahtarları"
+securityKey: "Güvenlik anahtarı"
+lastUsed: "Son kullanılan"
+lastUsedAt: "Son kullanım: {t}"
+unregister: "Kayıttan çık"
+passwordLessLogin: "Åžifresiz giriÅŸ"
+passwordLessLoginDescription: "Yalnızca güvenlik anahtarı veya şifre anahtarı kullanarak şifresiz oturum açmaya izin verir."
+resetPassword: "Şifreyi sıfırla"
+newPasswordIs: "Yeni ÅŸifre \"{password}\""
+reduceUiAnimation: "UI animasyonlarını azaltın."
+share: "PaylaÅŸ"
+notFound: "Bulunamadı"
+notFoundDescription: "Bu URL'ye karşılık gelen sayfa bulunamadı."
+uploadFolder: "Yüklemeler için varsayılan klasör"
+markAsReadAllNotifications: "Tüm bildirimleri okundu olarak işaretle"
+markAsReadAllUnreadNotes: "Tüm notları okundu olarak işaretle"
+markAsReadAllTalkMessages: "Tüm mesajları okundu olarak işaretle"
+help: "Yardım"
+inputMessageHere: "Mesajınızı buraya girin"
+close: "Kapat"
+invites: "Davetler"
+members: "Üyeler"
+transfer: "Transfer"
+title: "Başlık"
+text: "Metin"
+enable: "Etkin"
+next: "Sonraki"
+retype: "Tekrar girin"
+noteOf: "{user} not'u"
+quoteAttached: "Alıntı"
+quoteQuestion: "Alıntı olarak ekle?"
+attachAsFileQuestion: "Panodaki metin uzun. Metin dosyası olarak eklemek ister misin?"
+onlyOneFileCanBeAttached: "Bir mesaja yalnızca bir dosya ekleyebilirsin."
+signinRequired: "Devam etmeden önce lütfen kayıt olun veya giriş yapın."
+signinOrContinueOnRemote: "Devam etmek için sunucunuzu taşıyın veya bu sunucuya kaydolun / giriş yapın."
+invitations: "Davetler"
+invitationCode: "Davet kodu"
+checking: "Kontrol ediliyor..."
+available: "Kullanılabilir"
+unavailable: "Kullanılamaz"
+usernameInvalidFormat: "Büyük ve küçük harfler, rakamlar ve alt çizgi kullanabilirsin. (a~zã€A~Zã€0~9)"
+tooShort: "Çok kısa"
+tooLong: "Çok uzun"
+weakPassword: "Zayıf şifre"
+normalPassword: "Ortalama ÅŸifre"
+strongPassword: "Güçlü şifre"
+passwordMatched: "EÅŸleÅŸti"
+passwordNotMatched: "EÅŸleÅŸmedi"
+signinWith: "{x} ile giriş yapın"
+signinFailed: "Giriş yapılamıyor. Girilen kullanıcı adı veya şifre yanlış."
+or: "veya"
+language: "Dil"
+uiLanguage: "Kullanıcı arayüzü dili"
+aboutX: "{x} hakkında"
+emojiStyle: "Emoji stili"
+native: "Yerli"
+menuStyle: "Menü stili"
+style: "Stil"
+drawer: "Çekmece"
+popup: "Pop-up"
+showNoteActionsOnlyHover: "Not eylemlerini yalnızca üzerine gelindiğinde göster"
+showReactionsCount: "Notlardaki tepki sayısını gör"
+noHistory: "Geçmiş mevcut değil"
+signinHistory: "Giriş geçmişi"
+enableAdvancedMfm: "GeliÅŸmiÅŸ MFM'yi etkinleÅŸtir"
+enableAnimatedMfm: "Animasyonlu MFM'yi etkinleÅŸtir"
+doing: "İşleniyor..."
+category: "Kategori"
+tags: "Takma adlar"
+docSource: "Bu belgenin kaynağı"
+createAccount: "Hesap oluÅŸtur"
+existingAccount: "Mevcut hesap"
+regenerate: "Yeniden oluÅŸtur"
+fontSize: "Yazı tipi boyutu"
+mediaListWithOneImageAppearance: "Tek bir resim içeren medya listelerinin yüksekliği"
+limitTo: "{x} ile sınırlandır"
+noFollowRequests: "Bekleyen takip istekleri yok."
+openImageInNewTab: "Görüntüleri yeni sekmede aç"
+dashboard: "Gösterge paneli"
+local: "Yerel"
+remote: "Uzak"
+total: "Toplam"
+weekOverWeekChanges: "Geçen haftadan beri yapılan değişiklikler"
+dayOverDayChanges: "Dünkü değişiklikler"
+appearance: "Görünüm"
+clientSettings: "İstemci Ayarları"
+accountSettings: "Hesap Ayarları"
+promotion: "Tanıtım"
+promote: "Tanıtıldı"
+numberOfDays: "Gün sayısı"
+hideThisNote: "Bu notu gizle"
+showFeaturedNotesInTimeline: "Pano'da öne çıkan notları göster"
+objectStorage: "Nesne Depolama"
+useObjectStorage: "Nesne depolamayı kullanın"
+objectStorageBaseUrl: "Temel URL"
+objectStorageBaseUrlDesc: "Referans olarak kullanılan URL. CDN veya Proxy kullanıyorsanız, bunların URL'sini belirtin.\nS3 için ‘https://<bucket>.s3.amazonaws.com’ ve GCS veya eşdeğer hizmetler için ‘https://storage.googleapis.com/<bucket>’ vb. kullanın."
+objectStorageBucket: "Kova"
+objectStorageBucketDesc: "Lütfen sağlayıcınızda kullanılan kova adını belirtin."
+objectStoragePrefix: "Ön ek"
+objectStoragePrefixDesc: "Dosyalar bu öneke sahip dizinler altında saklanacaktır."
+objectStorageEndpoint: "Uç nokta"
+objectStorageEndpointDesc: "AWS S3 kullanıyorsanız bu alanı boş bırakın, aksi takdirde kullandığınız hizmete bağlı olarak uç noktayı ‘<host>’ veya ‘<host>:<port>’ olarak belirtin."
+objectStorageRegion: "Bölge"
+objectStorageRegionDesc: "'xx-east-1' gibi bir bölge belirt. Hizmetin bölgeler arasında ayrım yapmıyorsa, ‘us-east-1’ girin. AWS yapılandırma dosyalarını veya ortam değişkenlerini kullanıyorsan boş bırak."
+objectStorageUseSSL: "SSL kullanın"
+objectStorageUseSSLDesc: "API bağlantıları için HTTPS kullanmayacaksanız bunu kapatın."
+objectStorageUseProxy: "Proxy üzerinden bağlan"
+objectStorageUseProxyDesc: "API bağlantıları için Proxy kullanmayacaksanız bunu kapatın."
+objectStorageSetPublicRead: "Yükleme sırasında \"genel-okuma\" ayarını yapın"
+s3ForcePathStyleDesc: "s3ForcePathStyle etkinleştirilirse, kova adı URL'nin ana bilgisayar adı yerine URL yoluna eklenmelidir. Kendi kendine barındırılan bir Minio örneği gibi hizmetleri kullanırken bu ayarı etkinleştirmen gerekebilir."
+serverLogs: "Sunucu log kayıtları"
+deleteAll: "Tümünü sil"
+showFixedPostForm: "Gönderi formunu zaman çizelgesinin en üstünde görüntüle"
+showFixedPostFormInChannel: "Gönderi formunu zaman çizelgesinin en üstünde görüntüle (Kanallar)"
+withRepliesByDefaultForNewlyFollowed: "Yeni takip edilen kullanıcıların yanıtlarını varsayılan olarak zaman çizelgesine dahil et"
+newNoteRecived: "Yeni Not'lar var"
+newNote: "Yeni Not"
+sounds: "Sesler"
+sound: "Ses"
+notificationSoundSettings: "Bildirim sesi ayarları"
+listen: "Dinle"
+none: "Hiçbiri"
+showInPage: "Sayfada göster"
+popout: "Açılır pencere"
+volume: "Ses hacmi"
+masterVolume: "Ana ses seviyesi"
+notUseSound: "Sesi kapat"
+useSoundOnlyWhenActive: "Misskey etkin olduğunda ses çıkarılır."
+details: "Ayrıntılar"
+renoteDetails: "Renote detayları"
+chooseEmoji: "Bir emoji seçin"
+unableToProcess: "İşlem tamamlanamadı."
+recentUsed: "Son kullanılan"
+install: "Yükle"
+uninstall: "Kaldır"
+installedApps: "Yetkili Uygulamalar"
+nothing: "Burada görülecek bir şey yok."
+installedDate: "Yetkili"
+lastUsedDate: "En son kullanıldığı tarih"
+state: "Durum"
+sort: "Sıralama düzeni"
+ascendingOrder: "Artan"
+descendingOrder: "Azalan"
+scratchpad: "Not defteri"
+scratchpadDescription: "Scratchpad, AiScript deneyleri için bir ortam sağlar. Misskey ile etkileşim halindeyken yazabilir, çalıştırabilir ve sonuçlarını kontrol edebilirsin."
+uiInspector: "UI denetçisi"
+uiInspectorDescription: "Bellekteki UI bileşeni sunucu listesini görebilirsin. UI bileşeni, Ui:C: işlevi tarafından oluşturulacak."
+output: "Çıktı"
+script: "Script"
+disablePagesScript: "Sayfalarda AiScript'i devre dışı bırak"
+updateRemoteUser: "Uzak kullanıcı bilgilerini güncelle"
+unsetUserAvatar: "Avatar'ı kaldır"
+unsetUserAvatarConfirm: "Avatarı silmek istediğinden emin misin?"
+unsetUserBanner: "Banner'ı kaldır"
+unsetUserBannerConfirm: "Banner'ı kaldırmak istediğinden emin misin?"
+deleteAllFiles: "Tüm dosyaları sil"
+deleteAllFilesConfirm: "Tüm dosyaları silmek istediğinden emin misin?"
+removeAllFollowing: "Takip ettiğin tüm kullanıcıları takipten çıkar"
+removeAllFollowingDescription: "Bu komutu çalıştırmak, {host} adresindeki tüm hesapları takipten çıkarır. Örneğin, sunucu artık mevcut değilse bu komutu çalıştırın."
+userSuspended: "Bu kullanıcı askıya alınmıştır."
+userSilenced: "Bu kullanıcı susturuluyor."
+yourAccountSuspendedTitle: "Bu hesap askıya alınmıştır."
+yourAccountSuspendedDescription: "Bu hesap, sunucunun hizmet şartlarını veya benzerlerini ihlal ettiği için askıya alınmıştır. Daha ayrıntılı bir neden öğrenmek istersen yöneticiyle iletişime geç. Lütfen yeni bir hesap oluşturma."
+tokenRevoked: "Geçersiz jeton"
+tokenRevokedDescription: "Bu jetonun süresi doldu. Lütfen tekrar giriş yapın."
+accountDeleted: "Hesap silindi"
+accountDeletedDescription: "Bu hesap silinmiÅŸ."
+menu: "Menü"
+divider: "Bölücü"
+addItem: "Öğe Ekle"
+rearrange: "Yeniden düzenle"
+relays: "Röleler"
+addRelay: "Röle ekle"
+inboxUrl: "Gelen Kutusu URL"
+addedRelays: "Eklenen Röleler"
+serviceworkerInfo: "Push bildirimleri için etkinleştirilmeli."
+deletedNote: "Silinen not"
+invisibleNote: "Görünmez not"
+enableInfiniteScroll: "Otomatik olarak daha fazlasını yükle"
+visibility: "Görünürlük"
+poll: "Anket"
+useCw: "İçeriği gizle"
+enablePlayer: "Video oynatıcıyı aç"
+disablePlayer: "Video oynatıcıyı kapat"
+expandTweet: "Notu geniÅŸlet"
+themeEditor: "Tema düzenleyici"
+description: "Açıklama"
+describeFile: "Alternatif metin ekle"
+enterFileDescription: "Alternatif metin girin"
+author: "Yazar"
+leaveConfirm: "Kaydedilmemiş değişiklikler var. Bunları silmek istiyor musunuz?"
+manage: "Yönetim"
+plugins: "Eklentiler"
+preferencesBackups: "Tercih yedeklemeleri"
+deck: "Deck"
+undeck: "Güverteden Ayrıl"
+useBlurEffectForModal: "Modaller için bulanıklaştırma efekti kullanın"
+useFullReactionPicker: "Tam boy tepki seçiciyi kullanın"
+width: "GeniÅŸlik"
+height: "Yükseklik"
+large: "Büyük"
+medium: "Orta"
+small: "Küçük"
+generateAccessToken: "EriÅŸim jetonu oluÅŸtur"
+permission: "İzinler"
+adminPermission: "Yönetici İzinleri"
+enableAll: "Tümünü etkinleştir"
+disableAll: "Tümünü devre dışı bırak"
+tokenRequested: "Hesaba eriÅŸim izni ver"
+pluginTokenRequestedDescription: "Bu eklenti, burada ayarlanan izinleri kullanabilecek."
+notificationType: "Bildirim türü"
+edit: "Düzenle"
+emailServer: "E-posta sunucusu"
+enableEmail: "E-posta dağıtımını etkinleştir"
+emailConfigInfo: "Kayıt sırasında veya şifreni unuttuğunda E-postanı doğrulamak için kullanılır."
+email: "E-Posta"
+emailAddress: "E-Posta adresi"
+smtpConfig: "SMTP Sunucu yapılandırması"
+smtpHost: "Host"
+smtpPort: "Port"
+smtpUser: "Kullanıcı adı"
smtpPass: "Åžifre"
+emptyToDisableSmtpAuth: "SMTP kimlik doğrulamasını devre dışı bırakmak için kullanıcı adı ve şifre alanlarını boş bırakın."
+smtpSecure: "SMTP bağlantıları için örtük SSL/TLS kullanın"
+smtpSecureInfo: "STARTTLS kullanırken bunu kapatın."
+testEmail: "Test E-postası gönderimi"
+wordMute: "Kelime sustur"
+wordMuteDescription: "Belirtilen kelime veya kelime öbeğini içeren notları küçültün. Küçültülmüş notlar, üzerlerine tıklanarak görüntülenebilir."
+hardWordMute: "Zorla kelime sustur"
+showMutedWord: "Sessize alınan kelimeleri göster"
+hardWordMuteDescription: "Belirtilen kelime veya kelime öbeğini içeren notları gizle. Kelime sessize alma özelliğinden farklı olarak, not tamamen görünmez hale gelir."
+regexpError: "Düzenli ifade hatası"
+regexpErrorDescription: "{tab} kelimesinin {line} satırındaki düzenli ifadede bir hata oluştu:"
+instanceMute: "Sunucu SessizleÅŸtirme"
+userSaysSomething: "{name} bir şey söyledi."
+userSaysSomethingAbout: "{name} “{word}†hakkında bir şey söyledi."
+makeActive: "EtkinleÅŸtir"
+display: "Ekran"
+copy: "Kopyala"
+copiedToClipboard: "Panoya kopyalandı"
+metrics: "Metrikler"
+overview: "Genel Bakış"
+logs: "Günlükler"
+delayed: "Gecikmeli"
+database: "Veritabanı"
+channel: "Kanallar"
+create: "OluÅŸtur"
notificationSetting: "Bildirim ayarları"
+notificationSettingDesc: "Görüntülemek istediğiniz bildirim türlerini seçin."
+useGlobalSetting: "Genel ayarları kullan"
+useGlobalSettingDesc: "Etkinleştirildiğinde, hesabınızın bildirim ayarları kullanılır. Devre dışı bırakıldığında, bireysel yapılandırmalar yapılabilir."
+other: "DiÄŸer"
+regenerateLoginToken: "GiriÅŸ jetonunu yeniden oluÅŸtur"
+regenerateLoginTokenDescription: "Giriş sırasında dahili olarak kullanılan jetonu yeniden oluşturur. Normalde bu işlem gerekli değildir. Yeniden oluşturulursa, tüm cihazlar oturumu kapatılır."
+theKeywordWhenSearchingForCustomEmoji: "Bu, kendi emojilerini ararken kullanılan anahtar kelimedir."
+setMultipleBySeparatingWithSpace: "Birden fazla girişi boşluklarla ayırın."
+fileIdOrUrl: "Dosya ID veya URL"
+behavior: "Davranış"
+sample: "Örnek"
+abuseReports: "Raporlar"
+reportAbuse: "Rapor"
+reportAbuseRenote: "Raporu yeniden gönder"
+reportAbuseOf: "{name} raporu"
+fillAbuseReportDescription: "Bu raporla ilgili ayrıntıları lütfen doldur. Belirli bir notla ilgiliyse, lütfen URL'sini de ekle."
+abuseReported: "Raporunuz gönderildi. Çok teşekkür ederiz."
+reporter: "Raporlayan"
+reporteeOrigin: "Bildirim Kaynağı"
+reporterOrigin: "Bildirenin Kaynağı"
+send: "Gönder"
+openInNewTab: "Yeni sekmede aç"
+openInSideView: "Yan görünümde aç"
+defaultNavigationBehaviour: "Varsayılan gezinme davranışı"
+editTheseSettingsMayBreakAccount: "Bu ayarları düzenlemek hesabınıza zarar verebilir."
instanceTicker: "Notların sunucu bilgileri"
+waitingFor: "{x} bekleniyor"
+random: "Rastgele"
+system: "Sistem"
+switchUi: "UI deÄŸiÅŸtir"
+desktop: "Masaüstü "
+clip: "Klip"
+createNew: "Yeni oluÅŸtur"
+optional: "Opsiyonel"
+createNewClip: "Klip oluÅŸtur"
+unclip: "Klip kaldır"
+confirmToUnclipAlreadyClippedNote: "Bu not zaten “{name}†klibinin bir parçası. Bu klipten silmek ister misin?"
+public: "Herkese açık"
+private: "Özel"
+i18nInfo: "Misskey, gönüllüler tarafından çeşitli dillere çevrilmektedir. {link} adresinden yardımcı olabilirsin."
+manageAccessTokens: "Acces Tokens yönet"
+accountInfo: "Hesap bilgileri"
+notesCount: "Not sayısı"
+repliesCount: "Yanıt sayısı"
+renotesCount: "Renote sayısı"
+repliedCount: "Alınan yanıt sayısı"
+renotedCount: "Alınan Renote sayısı"
+followingCount: "Takip sayısı"
+followersCount: "Takipçi sayısı"
+sentReactionsCount: "Tepki sayısı"
+receivedReactionsCount: "Alınan tepki sayısı"
+pollVotesCount: "Anket oy sayısı"
+pollVotedCount: "Alınan anket oy sayısı"
+yes: "Evet"
+no: "Hayır"
+driveFilesCount: "Drive dosya sayısı"
+driveUsage: "Drive alanı kullanımı"
+noCrawle: "Tarayıcı indekslemesini reddet"
noCrawleDescription: "Arama motorlarından profilinde, notlarında, sayfalarında vb. dolaşılmamasını ve dizine eklememesini talep et."
-clearCache: "Ön belleği temizle"
+lockedAccountInfo: "Notunuzun görünürlüğünü “Yalnızca takipçiler†olarak ayarlamadığınız sürece, takipçilerin manuel olarak onaylanmasını gerektirse bile notlarınız herkes tarafından görülebilir."
+alwaysMarkSensitive: "Varsayılan olarak hassas olarak işaretle"
+loadRawImages: "Küçük resimleri göstermek yerine orijinal resimleri yükle"
+disableShowingAnimatedImages: "Animasyonlu görüntüleri oynatmayın"
+highlightSensitiveMedia: "Hassas medyayı vurgulayın"
+verificationEmailSent: "Doğrulama e-postası gönderildi. Doğrulamayı tamamlamak için e-postadaki bağlantıyı takip edin."
+notSet: "Ayarlı değil"
+emailVerified: "E-posta adresi doğrulandı."
+noteFavoritesCount: "Favori not sayısı"
+pageLikesCount: "Beğenilen sayfa sayısı"
+pageLikedCount: "Alınan sayfa beğen sayısı"
+contact: "İletişim"
+useSystemFont: "Sistemin varsayılan yazı tipini kullanın"
+clips: "Klipler"
+experimentalFeatures: "Deneysel özellikler"
+experimental: "Deneysel"
+thisIsExperimentalFeature: "Bu deneysel bir özellik. İşlevselliği değişebilir ve amaçlandığı gibi çalışmayabilir."
+developer: "GeliÅŸtirici"
+makeExplorable: "Hesabı “Keşfet†bölümünde görünür hale getir"
+makeExplorableDescription: "Bunu kapatırsanız, hesabınız “Keşfet†bölümünde görünmez."
+duplicate: "Çoğalt"
+left: "Sol"
+center: "Merkez"
+wide: "GeniÅŸ"
+narrow: "Dar"
+reloadToApplySetting: "Bu ayar, sayfa yeniden yüklendikten sonra geçerli olacaktır. Şimdi yeniden yüklemek ister misin?"
+needReloadToApply: "Bunun yansıtılması için yeniden yükleme yapılması gerekir."
+needToRestartServerToApply: "Değişikliğin yansıtılması için Misskey'in yeniden başlatılması gerekir."
+showTitlebar: "Başlık çubuğunu göster"
+clearCache: "Önbellek temizle"
onlineUsersCount: "{n} kullanıcı çevrim içi"
+nUsers: "{n} Kullanıcı"
+nNotes: "{n} Not"
+sendErrorReports: "Hata raporları gönder"
+sendErrorReportsDescription: "Etkinleştirildiğinde, bir sorun oluştuğunda ayrıntılı hata bilgileri Misskey ile paylaşılacak ve bu da Misskey'in kalitesinin iyileştirilmesine yardımcı olacak.\nBu bilgiler arasında işletim sisteminizin sürümü, kullandığınız tarayıcı, Misskey'deki faaliyetlerin vb. yer alacaktır."
+myTheme: "Benim temam"
+backgroundColor: "Arka plan rengi"
+accentColor: "Vurgu rengi"
+textColor: "Metin rengi"
+saveAs: "Farklı kaydet"
+advanced: "GeliÅŸmiÅŸ"
+advancedSettings: "GeliÅŸmiÅŸ ayarlar"
+value: "DeÄŸer"
+createdAt: "OluÅŸturuldu"
+updatedAt: "Güncellendi"
+saveConfirm: "DeÄŸiÅŸiklikleri kaydetmek ister misin?"
+deleteConfirm: "Cidden silmek istiyor musunuz?"
+invalidValue: "Geçersiz değer."
+registry: "Kayıt Defteri"
+closeAccount: "Hesabı kapat"
+currentVersion: "Şu anki sürüm"
+latestVersion: "En yeni sürüm"
+youAreRunningUpToDateClient: "İstemci yazılımınızın en yeni sürümünü kullanıyorsunuz."
+newVersionOfClientAvailable: "İstemcinin daha yeni bir sürümü var."
+usageAmount: "Kullanım"
+capacity: "Kapasite"
+inUse: "Kullanılıyor"
+editCode: "Kodu düzenle"
+apply: "Uygula"
+receiveAnnouncementFromInstance: "Bu sunucudan bildirimler alın"
+emailNotification: "E-posta bildirimi"
+publish: "Yayınla"
+inChannelSearch: "Kanalda ara"
+useReactionPickerForContextMenu: "Sağ tıklama ile tepki seçiciyi aç"
+typingUsers: "{users} yazıyor..."
+jumpToSpecifiedDate: "Belirli bir tarihe atla"
+showingPastTimeline: "Şu anda eski bir Pano görüntüleniyor."
+clear: "Temizle"
+markAllAsRead: "Tümünü okundu olarak işaretle"
+goBack: "Geri"
+unlikeConfirm: "Cidden beğenini kaldırmak mı istiyorsun?"
+fullView: "Tam görünüm"
+quitFullView: "Tam ekranı kapat"
+addDescription: "Açıklama ekle"
+userPagePinTip: "Bireysel notların menüsünden “Profiline sabitle†seçeneğini seçerek notları burada görüntüleyebilirsin."
+notSpecifiedMentionWarning: "Bu notta, alıcılar arasında yer almayan kullanıcılar hakkında bilgiler bulunmaktadır."
+info: "Hakkında"
+userInfo: "Kullanıcı hakkında"
+unknown: "Bilinmiyor"
+onlineStatus: "Çevrimiçi durumu"
+hideOnlineStatus: "Çevrimiçi durumunu gizle"
+hideOnlineStatusDescription: "Çevrimiçi durumunuzu gizlemek, arama gibi bazı özelliklerin kullanışlılığını azaltır."
+online: "Online"
+active: "Aktif"
+offline: "Offline"
+notRecommended: "Tavsiye edilmez"
+botProtection: "Bot Koruması"
+instanceBlocking: "Blocked/Silenced Instances"
+selectAccount: "Hesap seç"
+switchAccount: "Hesap deÄŸiÅŸtir"
+enabled: "Aktif"
+disabled: "Devre Dışı"
+quickAction: "Hızlı eylemler"
user: "Kullanıcı"
-global: "Küresel"
+administration: "Yönetim"
+accounts: "Hesaplar"
+switch: "Anahtar"
+noMaintainerInformationWarning: "Bakımcı bilgileri yapılandırılmamıştır."
+noInquiryUrlWarning: "Sorgu URL'si ayarlanmadı"
+noBotProtectionWarning: "Bot koruması yapılandırılmamıştır."
+configure: "Yeniden Yapılandır"
+postToGallery: "Yeni galeri gönderisi oluştur"
+postToHashtag: "Bu hashtag'e gönder"
+gallery: "Galeri"
+recentPosts: "Son gönderiler"
+popularPosts: "Popüler gönderiler"
+shareWithNote: "Notla paylaÅŸ"
+ads: "Reklamlar"
+expiration: "Son tarih"
+startingperiod: "BaÅŸla"
+memo: "Hatırlatıcı"
+priority: "Öncelik"
+high: "Yüksek"
+middle: "Orta"
+low: "Düşük"
+emailNotConfiguredWarning: "E-posta adresi ayarlanmamış."
+ratio: "Oran"
+previewNoteText: "Önizlemeyi göster"
+customCss: "Özel CSS"
+customCssWarn: "Bu ayar, yalnızca ne işe yaradığını biliyorsanız kullanılmalıdır. Yanlış değerler girilmesi, istemcinin normal şekilde çalışmamasına neden olabilir."
+global: "Global"
squareAvatars: "Kare avatarlar"
+sent: "Gönderilen"
+received: "Alınan"
+searchResult: "Arama sonuçları"
+hashtags: "Hashtag'ler"
+troubleshooting: "Sorun Giderme"
+useBlurEffect: "UI'da bulanıklık efektleri kullanın"
+learnMore: "Daha fazla bilgi edinin"
+misskeyUpdated: "Misskey güncellendi!"
+whatIsNew: "Değişiklikleri göster"
+translate: "Çevir"
+translatedFrom: "{x}'ten çevrilmiştir."
+accountDeletionInProgress: "Hesap silme iÅŸlemi ÅŸu anda devam ediyor."
+usernameInfo: "Bu sunucudaki diğer hesaplardan hesabını ayıran bir isim. Alfabe (a~z, A~Z), rakamlar (0~9) veya alt çizgi (_) kullanabilirsin. Kullanıcı adları daha sonra değiştirilemez."
+aiChanMode: "Ai Modu"
+devMode: "GeliÅŸtirici modu"
+keepCw: "İçerik uyarılarını sakla"
+pubSub: "Yayın/Abonelik Hesapları"
+lastCommunication: "Son iletiÅŸim"
+resolved: "Çözülmüş"
+unresolved: "Çözülmemiş"
+breakFollow: "Takipçiyi kaldır"
+breakFollowConfirm: "Bu takipçiyi ciddden silmek istiyor musun?"
+itsOn: "Etkin"
+itsOff: "Devre Dışı"
+on: "Açık"
+off: "Kapalı"
+emailRequiredForSignup: "Kayıt için E-posta adresi gereklidir."
+unread: "Okunmamış"
+filter: "Filtre"
+controlPanel: "Kontrol Paneli"
+manageAccounts: "Hesapları Yönet"
+makeReactionsPublic: "Tepki geçmişini herkese açık olarak ayarla"
+makeReactionsPublicDescription: "Bu, geçmişteki tüm tepkilerinin listesini herkese açık hale getirecek."
+classic: "Klasik"
+muteThread: "Konuyu sessize al"
+unmuteThread: "Konuyu sessizden çıkar"
+followingVisibility: "Takip edilenlerin görünürlüğü"
+followersVisibility: "Takipçilerin görünürlüğü"
+continueThread: "Konunun devamını görüntüle"
+deleteAccountConfirm: "Bu, hesabını geri dönüşü olmayan bir şekilde silecek. Devam etmek istiyor musun?"
+incorrectPassword: "Yanlış şifre."
+incorrectTotp: "Tek kullanımlık şifre yanlış veya süresi dolmuş."
+voteConfirm: "\"{choice}\" için oyunuzu onaylıyor musunuz?"
+hide: "Gizle"
+useDrawerReactionPickerForMobile: "Mobil cihazlarda tepki seçiciyi çekmece olarak göster"
+welcomeBackWithName: "HoÅŸ geldin, {name}"
+clickToFinishEmailVerification: "E-posta doğrulamasını tamamlamak için lütfen [{ok}] düğmesine tıklayın."
+overridedDeviceKind: "Cihaz türü"
+smartphone: "Akıllı telefon"
+tablet: "Tablet"
+auto: "Otomatik"
+themeColor: "Örnek Ticker Rengi"
+size: "Boyut"
+numberOfColumn: "Sütun sayısı"
searchByGoogle: "Arama"
+instanceDefaultLightTheme: "Sunucu genelinde varsayılan açık tema"
+instanceDefaultDarkTheme: "Sunucu genelinde varsayılan koyu tema"
+instanceDefaultThemeDescription: "Tema kodunu nesne biçiminde girin."
+mutePeriod: "Sessiz kalma süresi"
+period: "Zaman sınırı"
+indefinitely: "Kalıcı olarak"
+tenMinutes: "10 dakika"
+oneHour: "1 saat"
+oneDay: "1 gün"
+oneWeek: "1 hafta"
+oneMonth: "1 ay"
+threeMonths: "3 ay"
+oneYear: "1 yıl"
+threeDays: "3 gün"
+reflectMayTakeTime: "Bunun yansıtılması biraz zaman alabilir."
+failedToFetchAccountInformation: "Hesap bilgileri alınamadı"
+rateLimitExceeded: "Hız sınırı aşıldı"
+cropImage: "Görüntüyü kırp"
+cropImageAsk: "Bu görüntüyü kırpmak ister misin?"
+cropYes: "Kırp"
+cropNo: "Olduğu gibi kullanın"
file: "Dosyalar"
+recentNHours: "Son {n} saat"
+recentNDays: "Son {n} gün"
+noEmailServerWarning: "E-posta sunucusu yapılandırılmamış."
+thereIsUnresolvedAbuseReportWarning: "Çözülmemiş raporlar var."
+recommended: "Önerilen"
+check: "Kontrol"
+driveCapOverrideLabel: "Bu kullanıcının Drive kapasitesini değiştir"
+driveCapOverrideCaption: "Kapasiteyi varsayılan değere sıfırlamak için 0 veya daha düşük bir değer girin."
+requireAdminForView: "Bunu görüntülemek için yönetici hesabıyla oturum açmanız gerekir."
+isSystemAccount: "Sistem tarafından oluşturulan ve otomatik olarak işletilen bir hesap."
+typeToConfirm: "Onaylamak için lütfen {x} girin."
+deleteAccount: "Hesabı sil"
+document: "Dokümantasyon"
+numberOfPageCache: "Önbelleğe alınmış sayfa sayısı"
+numberOfPageCacheDescription: "Bu sayıyı artırmak, kullanıcının cihazında daha fazla bellek kullanımı nedeniyle daha fazla yük oluşturmakla birlikte, kullanıcının rahatlığını artıracaktır."
+logoutConfirm: "Çıkmak istediğinden emin misin?"
+logoutWillClearClientData: "Oturumu kapatmak, tarayıcıdan istemcinin ayarlarını siler. Tekrar oturum açtığında ayarları geri yükleyebilmek için, ayarlarının otomatik yedeklenmesini etkinleştirmen gerekir."
+lastActiveDate: "Son kullanımı"
+statusbar: "Durum çubuğu"
+pleaseSelect: "Bir seçenek seçin"
+reverse: "Tersine"
+colored: "Renkli"
+refreshInterval: "Güncelleme aralığı"
+label: "Etiket"
+type: "Tür"
+speed: "Hız"
+slow: "YavaÅŸ"
+fast: "Hızlı"
+sensitiveMediaDetection: "Hassas ortamların tespiti"
+localOnly: "Yalnızca yerel"
+remoteOnly: "Sadece uzaktan"
+failedToUpload: "Yükleme başarısız"
+cannotUploadBecauseInappropriate: "Bu dosya, dosyanın bazı kısımlarının uygunsuz olabileceği tespit edildiği için yüklenemiyor."
+cannotUploadBecauseNoFreeSpace: "Drive kapasitesi yetersiz olduğu için yükleme başarısız oldu."
+cannotUploadBecauseExceedsFileSizeLimit: "Bu dosya, dosya boyutu sınırını aştığı için yüklenemiyor."
+cannotUploadBecauseUnallowedFileType: "Yetkisiz dosya türü nedeniyle yükleme yapılamıyor."
+beta: "Beta"
+enableAutoSensitive: "Otomatik olarak hassas olarak iÅŸaretleme"
+enableAutoSensitiveDescription: "Mümkün olduğunda, Makine Öğrenimi yoluyla hassas ortamların otomatik olarak algılanmasını ve işaretlenmesini sağlar. Bu seçenek devre dışı bırakılmış olsa bile, örnek genelinde etkinleştirilebilir."
+activeEmailValidationDescription: "E-posta adreslerinin daha sıkı bir şekilde doğrulanmasını sağlar. Bu, tek kullanımlık adreslerin kontrol edilmesini ve adresin gerçekten iletişim kurulabilir olup olmadığının kontrol edilmesini içerir. İşaretlenmediğinde, yalnızca e-postanın biçimi doğrulanır."
+navbar: "Gezinti çubuğu"
+shuffle: "Karıştır"
+account: "Hesap"
+move: "Taşı"
pushNotification: "Push bildirimleri"
subscribePushNotification: "Push bildirimlerini etkinleÅŸtir"
unsubscribePushNotification: "Push bildirimlerini kapat"
pushNotificationAlreadySubscribed: "Push bildirimleri zaten açık"
pushNotificationNotSupported: "Push bildirimleri sunucu veya tarayıcı tarafından desteklenmiyor"
+sendPushNotificationReadMessage: "Okunduktan sonra push bildirimlerini silin"
+sendPushNotificationReadMessageCaption: "Bu, cihazınızın güç tüketimini artırabilir."
+windowMaximize: "Maksimize et"
+windowMinimize: "Minimize et"
+windowRestore: "Geri yükle"
+caption: "Alternatif metin"
+loggedInAsBot: "Şu anda bot olarak oturum açmış durumdasınız."
+tools: "Araçlar"
+cannotLoad: "Yüklenemiyor"
+numberOfProfileView: "Profil görüntülemeleri"
+like: "BeÄŸen"
+unlike: "BeÄŸenme"
+numberOfLikes: "BeÄŸeniler"
+show: "Göster"
+neverShow: "Bir daha gösterme"
+remindMeLater: "Belki daha sonra"
+didYouLikeMisskey: "Misskey'i sevdin mi?"
+pleaseDonate: "{host} ücretsiz yazılım Misskey kullanmaktadır. Misskey'in geliştirilmesinin devam edebilmesi için bağışlarınızı çok takdir ederiz!"
+correspondingSourceIsAvailable: "İlgili kaynak kodu {anchor} adresinde mevcuttur."
+roles: "Roller"
+role: "Rol"
noRole: "Rol bulunamadı"
+normalUser: "Normal kullanıcı"
+undefined: "Tanımlanmamış"
+assign: "Atama"
+unassign: "Atamayı kaldır"
color: "Renk"
+manageCustomEmojis: "Özel Emojileri Yönet"
+manageAvatarDecorations: "Avatar süslerini yönet"
+youCannotCreateAnymore: "Oluşturma sınırına ulaştınız."
+cannotPerformTemporary: "Geçici olarak kullanılamıyor"
+cannotPerformTemporaryDescription: "Bu işlem, yürütme sınırını aştığı için geçici olarak gerçekleştirilememekte. Lütfen bir süre bekle ve tekrar dene."
+invalidParamError: "Geçersiz parametreler"
+invalidParamErrorDescription: "İstek parametreleri geçersiz. Bu durum genellikle bir hata nedeniyle oluşur, ancak boyut sınırlarını aşan girdiler veya benzer nedenlerden de kaynaklanabilir."
+permissionDeniedError: "İşlem reddedildi"
+permissionDeniedErrorDescription: "Bu hesap bu işlemi gerçekleştirmek için gerekli izne sahip değildir."
+preset: "Ön ayar"
+selectFromPresets: "Ön ayarlardan seçim yapın"
+custom: "Özel"
+achievements: "Başarılar"
+gotInvalidResponseError: "Geçersiz sunucu yanıtı"
+gotInvalidResponseErrorDescription: "Sunucu erişilemez durumda olabilir veya bakım çalışması yapılmaktadır. Lütfen daha sonra tekrar dene."
+thisPostMayBeAnnoying: "Bu not başkalarını rahatsız edebilir."
+thisPostMayBeAnnoyingHome: "Ana zaman çizelgesine gönder"
+thisPostMayBeAnnoyingCancel: "İptal"
+thisPostMayBeAnnoyingIgnore: "Yine de gönder"
+collapseRenotes: "Daha önce görüntülenen Renote'lari kısaltılmış olarak göster"
+collapseRenotesDescription: "Zaten yanıtladığın veya renote aldığın notları kapat."
+internalServerError: "İç Sunucu Hatası"
+internalServerErrorDescription: "Sunucu beklenmedik bir hatayla karşılaştı."
+copyErrorInfo: "Hata ayrıntılarını kopyala"
+joinThisServer: "Kaydol"
+exploreOtherServers: "Diğer sunucuları keşfet"
+letsLookAtTimeline: "Pano'ya bir göz atın"
+disableFederationConfirm: "Federasyonu cidden devre dışı bırakmak istiyor musun?"
+disableFederationConfirmWarn: "Federasyondan ayrılsa bile, aksi belirtilmedikçe gönderiler herkese açık olmaya devam edecek. Genellikle bunu yapmanız gerekmez."
+disableFederationOk: "Devre Dışı"
+invitationRequiredToRegister: "Bu etkinlik davetle katılımlıdır. Geçerli bir davet kodu girerek kaydolmanız gerekir."
+emailNotSupported: "Bu sunucu, E-Posta göndermeyi desteklemiyor."
+postToTheChannel: "Kanalına gönder"
+cannotBeChangedLater: "Bu daha sonra deÄŸiÅŸtirilemez."
+reactionAcceptance: "Tepki Kabulü"
+likeOnly: "Sadece beÄŸeniler"
+likeOnlyForRemote: "Tüm (Yalnızca uzak sunucu için beğeniler)"
+nonSensitiveOnly: "Hassas olmayanlar için"
+nonSensitiveOnlyForLocalLikeOnlyForRemote: "Yalnızca hassas olmayanlar (Yalnızca uzaktan beğeniler)"
+rolesAssignedToMe: "Bana atanan roller"
+resetPasswordConfirm: "Şifreni gerçekten sıfırlamak istiyor musun?"
+sensitiveWords: "Hassas kelimeler"
+sensitiveWordsDescription: "Yapılandırılan kelimelerden herhangi birini içeren tüm notların görünürlüğü otomatik olarak “Ana Sayfa†olarak ayarlanacaktır. Satır sonları ile ayırarak birden fazla not listeleyebilirsin."
+sensitiveWordsDescription2: "Boşluk kullanmak AND ifadeleri oluşturur ve anahtar kelimeleri eğik çizgi ile çevrelemek bunları düzenli ifadeye dönüştürür."
+prohibitedWords: "Yasaklanmış kelimeler"
+prohibitedWordsDescription: "Belirlenen kelime(ler)i içeren bir not göndermeye çalışıldığında hata verir. Birden fazla kelime, yeni satırla ayrılmış olarak ayarlanabilir."
+prohibitedWordsDescription2: "Boşluk kullanmak AND ifadeleri oluşturur ve anahtar kelimeleri eğik çizgi ile çevrelemek bunları düzenli ifadeye dönüştürür."
+hiddenTags: "Gizli hashtag'ler"
+hiddenTagsDescription: "Trend listesinde gösterilmeyecek etiketleri seçin.\nSatırlarla birden fazla etiket kaydedilebilir."
+notesSearchNotAvailable: "Not arama özelliği kullanılamıyor."
+usersSearchNotAvailable: "Kullanıcı araması mevcut değildir."
+license: "Lisans"
+unfavoriteConfirm: "Cidden favorilerden kaldırmak istiyor musunuz?"
+myClips: "Kliplerim"
+drivecleaner: "Drive Temizleyici"
+retryAllQueuesNow: "Tüm kuyrukları yeniden çalıştırmayı deneyin"
+retryAllQueuesConfirmTitle: "Cidden hepsini tekrar denemek istiyor musunuz?"
+retryAllQueuesConfirmText: "Bu, sunucu yükünü geçici olarak artıracaktır."
+enableChartsForRemoteUser: "Uzak kullanıcı veri grafikleri oluşturun"
+enableChartsForFederatedInstances: "Uzak sunucu veri grafikleri oluÅŸturun"
+enableStatsForFederatedInstances: "Uzak sunucu istatistiklerini alın"
+showClipButtonInNoteFooter: "Not eylem menüsüne “Klip†ekle"
+reactionsDisplaySize: "Tepki ekran boyutu"
+limitWidthOfReaction: "Tepkilerin maksimum genişliğini sınırla ve bunları küçültülmüş boyutta görüntüle."
+noteIdOrUrl: "Not ID veya URL"
+video: "Video"
+videos: "Videolar"
+audio: "Ses"
+audioFiles: "Ses Dosyası"
+dataSaver: "Veri Tasarrufu"
+accountMigration: "Hesap Taşıma"
+accountMoved: "Bu kullanıcı yeni bir hesaba taşındı:"
+accountMovedShort: "Bu hesap taşınmıştır."
+operationForbidden: "İşlem yasak"
+forceShowAds: "Her zaman reklamları göster"
addMemo: "Kısa not ekle"
+editMemo: "Kısa not düzenle"
+reactionsList: "Tepkiler"
+renotesList: "Renote'lar"
+notificationDisplay: "Bildirimler"
+leftTop: "Sol üst"
+rightTop: "Sağ üst"
+leftBottom: "Sol alt"
+rightBottom: "SaÄŸ alt"
+stackAxis: "Yığınlama yönü"
+vertical: "Dikey"
+horizontal: "Yatay"
+position: "Pozisyon"
+serverRules: "Sunucu kuralları"
+pleaseConfirmBelowBeforeSignup: "Bu sunucuya kaydolmak için aşağıdakileri gözden geçirip kabul etmelisin:"
+pleaseAgreeAllToContinue: "Devam etmek için yukarıdaki tüm alanları kabul etmelisin."
+continue: "Devam et"
+preservedUsernames: "Rezerve edilmiş kullanıcı adları"
+preservedUsernamesDescription: "Rezervasyon yapmak için kullanıcı adlarını satır sonlarıyla ayırarak listele. Bu kullanıcı adları normal hesap oluşturma sırasında kullanılamaz hale gelir, ancak yöneticiler tarafından manuel olarak hesap oluşturmak için kullanılabilir. Bu kullanıcı adlarını kullanan mevcut hesaplar etkilenmez."
+createNoteFromTheFile: "Bu dosyadan not oluÅŸturun"
+archive: "ArÅŸiv"
+archived: "ArÅŸivle"
+unarchive: "Arşivden çıkar"
+channelArchiveConfirmTitle: "Cidden {name} arÅŸivlemek mi istiyorsun?"
+channelArchiveConfirmDescription: "Arşivlenmiş bir kanal artık kanal listesinde veya arama sonuçlarında görünmeyecektir. Ayrıca, bu kanala yeni gönderiler eklenemeyecek."
+thisChannelArchived: "Bu kanal arÅŸivlenmiÅŸ."
+displayOfNote: "Not ekranı"
+initialAccountSetting: "Profil ayarları"
+youFollowing: "Takip edildi"
+preventAiLearning: "Makine Öğreniminde (Üretken Ai) kullanımını reddet"
+preventAiLearningDescription: "Tarayıcılardan, makine öğrenimi (Tahminsel / Üretken Ai) veri kümelerinde yayınlanan metin veya görsel materyalleri vb. kullanmamalarını talep eder. Bu, ilgili içeriğe “noai†HTML-Response bayrağı eklenerek gerçekleştirilir. Ancak, bu bayrakla tam bir önleme sağlanamaz, çünkü bu bayrak basitçe göz ardı edilebilir."
+options: "Seçenekler"
+specifyUser: "Belirli kullanıcı"
+lookupConfirm: "Yukarı bakmak ister misin?"
+openTagPageConfirm: "Bir hashtag sayfası açmak ister misin?"
+specifyHost: "Belirli ana bilgisayar"
+failedToPreviewUrl: "Önizleme yapılamadı"
+update: "Güncelle"
+rolesThatCanBeUsedThisEmojiAsReaction: "Bu emojiyi tepki olarak kullanabileceÄŸin roller"
+rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "Herhangi bir rol belirtilmezse, herkes bu emojiyi tepki olarak kullanabilir."
+rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "Bu roller herkese açık olmalıdır."
+cancelReactionConfirm: "Tepkini cidden silmek istiyor musun?"
+changeReactionConfirm: "Tepkini cidden deÄŸiÅŸtirmek istiyor musun?"
+later: "Daha sonra"
+goToMisskey: "Misskey'e"
+additionalEmojiDictionary: "Ek emoji sözlükleri"
+installed: "Yüklendi"
+branding: "MarkalaÅŸma"
+enableServerMachineStats: "Sunucu donanım istatistiklerini yayınla"
+enableIdenticonGeneration: "Kullanıcı identicon oluşturmayı etkinleştir"
+turnOffToImprovePerformance: "Devre dışı bırakma, daha yüksek performansa yol açabilir."
+createInviteCode: "Davet Kodu oluÅŸtur"
+createWithOptions: "Seçeneklerle oluştur"
+createCount: "Davet sayısı"
+inviteCodeCreated: "Davet oluÅŸturuldu"
+inviteLimitExceeded: "Oluşturulabilecek davetiyelerin maksimum sayısına ulaştın."
+createLimitRemaining: "{limit} Davet limiti kaldı"
+inviteLimitResetCycle: "Bu limit {time} tarihinde {limit} değerine sıfırlanacaktır."
+expirationDate: "Son kullanma tarihi"
+noExpirationDate: "Son kullanma tarihi yok"
+inviteCodeUsedAt: "Kullanılan davet kodu"
+registeredUserUsingInviteCode: "Kullanılan davet"
+waitingForMailAuth: "E-Posta doÄŸrulama beklemede"
+inviteCodeCreator: "Davet oluÅŸturuldu"
+usedAt: "Kullanıldığı yer"
+unused: "Kullanılmamış"
+used: "Kullanılmış"
+expired: "Süresi dolmuş"
+doYouAgree: "Katılıyor musunuz?"
+beSureToReadThisAsItIsImportant: "Lütfen bu önemli bilgileri okuyun."
+iHaveReadXCarefullyAndAgree: "“{x}†metnini okudum ve kabul ediyorum."
+dialog: "Diyalog"
icon: "Avatar"
-replies: "yanıt"
-renotes: "vazgeçme"
+forYou: "Senin için"
+currentAnnouncements: "Güncel duyurular"
+pastAnnouncements: "Geçmiş duyurular"
+youHaveUnreadAnnouncements: "Okunmamış duyurular var."
+useSecurityKey: "Güvenlik anahtarını veya şifreni kullanmak için lütfen tarayıcının veya cihazının talimatlarını izle."
+replies: "Yanıtla"
+renotes: "Renote'lar"
+loadReplies: "Yanıtları göster"
+loadConversation: "Konuşmayı göster"
+pinnedList: "SabitlenmiÅŸ liste"
+keepScreenOn: "Ekranı açık tut"
+verifiedLink: "Bağlantı sahipliği doğrulanmıştır."
+notifyNotes: "Yeni notlar hakkında bildirimde bulun"
+unnotifyNotes: "Yeni notlar hakkında bildirim almayı durdur"
+authentication: "Kimlik doÄŸrulama"
+authenticationRequiredToContinue: "Devam etmek için lütfen kimlik doğrulaması yapın."
+dateAndTime: "Zaman damgası"
+showRenotes: "Renote'ları göster"
+edited: "Düzenlendi"
+notificationRecieveConfig: "Bildirim Ayarları"
+mutualFollow: "Karşılıklı takip"
+followingOrFollower: "Takip eden veya takipçi"
+fileAttachedOnly: "Yalnızca dosya içeren notlar"
+showRepliesToOthersInTimeline: "Pano'da diğer kişilere verilen yanıtları göster"
+hideRepliesToOthersInTimeline: "Pano'dan diğer kişilerin yanıtlarını gizle"
+showRepliesToOthersInTimelineAll: "Pano'da takip ettiğin herkesin diğerlerine verdiği yanıtları göster"
+hideRepliesToOthersInTimelineAll: "Pano'da takip ettiğin herkesten diğer kişilere verilen yanıtları gizle"
+confirmShowRepliesAll: "Bu işlem geri alınamaz. Takip ettiğin herkesin yanıtlarını zaman çizelgende diğer kullanıcılara göstermek istiyor musun?"
+confirmHideRepliesAll: "Bu işlem geri alınamaz. Şu anda takip ettiğin tüm kullanıcıların yanıtlarını zaman tünelinde cidden göstermeyecek misin?"
+externalServices: "Dış Hizmetler"
+sourceCode: "Kaynak kodu"
+sourceCodeIsNotYetProvided: "Kaynak kodu henüz mevcut değildir. Bu sorunu gidermek için yöneticiyle iletişime geçin."
+repositoryUrl: "Depo URL'si"
+repositoryUrlDescription: "Misskey'i olduğu gibi kullanıyorsanız (kaynak kodunda herhangi bir değişiklik yapmadan), https://github.com/misskey-dev/misskey adresini girin."
+repositoryUrlOrTarballRequired: "Bir depo yayınlamadıysanız, bunun yerine bir tarball sağlamalısınız. Daha fazla bilgi için .config/example.yml dosyasına bakın."
+feedback: "Feedback"
+feedbackUrl: "Geri Bildirim URL'si"
+impressum: "Yayıncı Bilgileri"
+impressumUrl: "Yayıncı Bilgileri URL'si"
+impressumDescription: "Almanya gibi bazı ülkelerde, ticari web sitelerinde işletmeci iletişim bilgilerinin (Yayıncı) yer alması yasal olarak zorunludur."
+privacyPolicy: "Gizlilik Politikası"
+privacyPolicyUrl: "Gizlilik Politikası URL'si"
+tosAndPrivacyPolicy: "Hizmet Şartları ve Gizlilik Politikası"
+avatarDecorations: "Avatar süsleri"
+attach: "Ek"
+detach: "Kaldır"
+detachAll: "Tümünü Kaldır"
+angle: "Açı"
+flip: "Çevir"
+showAvatarDecorations: "Avatar süslerini göster"
+releaseToRefresh: "Yenilemek için serbest bırak"
+refreshing: "Yenileniyor..."
+pullDownToRefresh: "Yenilemek için aşağı çekin"
+useGroupedNotifications: "Gruplandırılmış bildirimleri göster"
+emailVerificationFailedError: "E-posta adresi doğrulanırken bir sorun oluştu. Bağlantının geçerlilik süresi dolmuş olabilir."
+cwNotationRequired: "“İçeriği gizle†seçeneği etkinleştirilirse, bir açıklama sağlanmalı."
+doReaction: "Tepki ekle"
+code: "Kod"
+reloadRequiredToApplySettings: "Ayarları uygulamak için yeniden yükleme gereklidir."
+remainingN: "Kalan: {n}"
+overwriteContentConfirm: "Mevcut içeriği üzerine yazmak istediğinden emin misin?"
+seasonalScreenEffect: "Mevsimsel Ekran Efekti"
+decorate: "Süsle"
+addMfmFunction: "MFM ekle"
+enableQuickAddMfmFunction: "Gelişmiş MFM seçiciyi göster"
+bubbleGame: "Kabarcık Oyunu"
+sfx: "Ses Efektleri"
+soundWillBePlayed: "Ses çalınacaktır"
+showReplay: "Tekrarı izle"
+replay: "Tekrar oynat"
+replaying: "Tekrar gösteriliyor"
+endReplay: "Tekrardan çık"
+copyReplayData: "Tekrar oynatma verilerini kopyala"
+ranking: "Sıralama"
+lastNDays: "Son {n} gün"
+backToTitle: "Başlığa geri dön"
+hemisphere: "Yaşadığınız yer"
+withSensitive: "Hassas dosyalara notlar ekle"
+userSaysSomethingSensitive: "{name} tarafından gönderilen mesaj hassas içerik barındırmaktadır."
+enableHorizontalSwipe: "Kaydırarak sekmeler arasında geçiş yapın"
+loading: "Yükleniyor"
+surrender: "İptal"
+gameRetry: "Tekrar dene"
+notUsePleaseLeaveBlank: "Kullanılmıyorsa boş bırakın."
+useTotp: "Tek Kullanımlık Şifreyi Girin"
+useBackupCode: "Yedek kodları kullanın"
+launchApp: "Uygulamayı başlatın"
+useNativeUIForVideoAudioPlayer: "Video ve ses oynatımı için tarayıcı kullanıcı arayüzünü kullan"
+keepOriginalFilename: "Orijinal dosya adını koru"
+keepOriginalFilenameDescription: "Bu ayarı kapatırsan, dosya yüklediğinde dosya adları otomatik olarak rastgele bir dizeyle değiştirilecek."
+noDescription: "Açıklama yok"
+alwaysConfirmFollow: "Takip ederken her zaman onaylayın"
+inquiry: "İletişim"
+tryAgain: "Lütfen daha sonra tekrar dene."
+confirmWhenRevealingSensitiveMedia: "Confirm when revealing sensitive media"
+sensitiveMediaRevealConfirm: "Bu hassas bir medya olabilir. Açıklamakta emin misin?"
+createdLists: "OluÅŸturulan listeler"
+createdAntennas: "OluÅŸturulan antenler"
+fromX: "{x}'den"
+genEmbedCode: "Gömme kodu oluştur"
+noteOfThisUser: "Bu kullanıcının notları"
+clipNoteLimitExceeded: "Bu klibe daha fazla not eklenemez."
+performance: "Başarım"
+modified: "DeÄŸiÅŸtirilmiÅŸ"
+discard: "At"
+thereAreNChanges: "{n} deÄŸiÅŸiklik var."
+signinWithPasskey: "Geçiş Anahtarı ile giriş yapın"
+unknownWebAuthnKey: "Bilinmeyen Geçiş Anahtarı"
+passkeyVerificationFailed: "Geçiş Anahtarı doğrulama başarısız oldu."
+passkeyVerificationSucceededButPasswordlessLoginDisabled: "Geçiş anahtarı doğrulaması başarılı oldu ancak şifresiz oturum açma devre dışıdır."
+messageToFollower: "Takipçilere mesaj"
+target: "Hedef"
+testCaptchaWarning: "Bu işlev CAPTCHA testi amacıyla tasarlanmıştır.\n<strong>Üretim ortamında kullanmayın.</strong>"
+prohibitedWordsForNameOfUser: "Kullanıcı adları için yasaklanmış kelimeler"
+prohibitedWordsForNameOfUserDescription: "Bu listedeki dizilerden herhangi biri kullanıcının adında yer alıyorsa, ad reddedilecektir. Moderatör ayrıcalıklarına sahip kullanıcılar bu kısıtlamadan etkilenmez."
+yourNameContainsProhibitedWords: "Adınız yasaklanmış kelimeler içeriyor"
+yourNameContainsProhibitedWordsDescription: "Bu adı kullanmak istiyorsan, lütfen sunucu yöneticinizle iletişime geç."
+thisContentsAreMarkedAsSigninRequiredByAuthor: "Yazar tarafından görüntülemek için oturum açma gerektirir."
+lockdown: "Karantina"
+pleaseSelectAccount: "Hesap seçin"
+availableRoles: "Mevcut roller"
+acknowledgeNotesAndEnable: "Önlemleri anladıktan sonra açın."
+federationSpecified: "Bu sunucu, beyaz liste federasyonunda çalıştırılmaktadır. Yönetici tarafından belirlenen sunucular dışında diğer sunucularla etkileşim kurmak yasaktır."
+federationDisabled: "Bu sunucuda federasyon devre dışıdır. Diğer sunuculardaki kullanıcılarla etkileşim kuramazsınız."
+draft: "Taslaklar"
+confirmOnReact: "Tepki verirken onaylayın"
+reactAreYouSure: "“{emoji}†tepkisini eklemek ister misin?"
+markAsSensitiveConfirm: "Bu medyayı hassas olarak ayarlamak ister misin?"
+unmarkAsSensitiveConfirm: "Bu medya için hassas işaretini kaldırmak ister misin?"
+preferences: "Tercihler"
+accessibility: "EriÅŸilebilirlik"
+preferencesProfile: "Tercihler profili"
+copyPreferenceId: "Tercih ID kopyala"
+resetToDefaultValue: "Varsayılana dön"
+overrideByAccount: "Hesap tarafından geçersiz kılma"
+untitled: "İsimsiz"
+noName: "İsim yok"
+skip: "Atla"
+restore: "Geri yükle"
+syncBetweenDevices: "Cihazlar arasında senkronizasyon"
+preferenceSyncConflictTitle: "Yapılandırılan değer sunucuda mevcuttur."
+preferenceSyncConflictText: "Senkronizasyon etkin ayarlar, değerlerini sunucuya kaydeder. Ancak, sunucuda mevcut değerler bulunmaktadır. Hangi değerleri üzerine yazmak istersin?"
+preferenceSyncConflictChoiceMerge: "BirleÅŸtir"
+preferenceSyncConflictChoiceServer: "Sunucuda yapılandırılan değer"
+preferenceSyncConflictChoiceDevice: "Cihazda yapılandırılan değer"
+preferenceSyncConflictChoiceCancel: "Senkronizasyonu etkinleÅŸtirmeyi iptal et"
+paste: "Yapıştır"
+emojiPalette: "Emoji paleti"
+postForm: "Gönderim formu"
+textCount: "Karakter sayısı"
+information: "Hakkında"
+chat: "Sohbet"
+migrateOldSettings: "Eski istemci ayarlarını taşıma"
+migrateOldSettings_description: "Bu işlem otomatik olarak yapılmalıdır, ancak herhangi bir nedenle geçiş başarısız olursa, geçiş işlemini manuel olarak kendin başlatabilirsin. Mevcut yapılandırma bilgileri üzerine yazılacaktır."
+compress: "Sıkıştır"
+right: "SaÄŸ"
+bottom: "Alt"
+top: "Üst"
+embed: "Göm"
+settingsMigrating: "Ayarlar taşınıyor, lütfen bir dakika bekle... (Daha sonra Ayarlar→Diğerler→Eski ayarları taşı seçeneğine giderek manuel olarak da taşıyabilirsin)"
+readonly: "Sadece okuma"
+goToDeck: "Güverteye Dön"
+federationJobs: "Federasyon İşleri"
+driveAboutTip: "Drive'da, geçmişte yüklediğin dosyaların bir listesi görüntülenir. <br>\nBu dosyaları notlara eklerken yeniden kullanabilir veya daha sonra paylaşmak üzere önceden yükleyebilirsin. <br>\n<b>Bir dosyayı silerken dikkatli ol, çünkü kullanıldığı her yerde (notlar, sayfalar, avatarlar, afişler vb.) mevcut olmayacakt.</b> <br>\nAyrıca dosyalarını düzenlemek için klasörler oluşturabilirsin."
+scrollToClose: "Kaydırarak kapatın"
+advice: "Tavsiye"
+realtimeMode: "Gerçek zamanlı mod"
+turnItOn: "Aç"
+turnItOff: "Kapat"
+emojiMute: "Emoji ses kapat"
+emojiUnmute: "Emoji ses aç"
+muteX: "Sessiz {x}"
+unmuteX: "Sesi aç {x}"
+abort: "İptal"
+tip: "İpucu & Püf Nokta"
+redisplayAllTips: "Tüm “İpucu & Püf Nokta†tekrar göster"
+hideAllTips: "Tüm “İpucu & Püf Nokta†gizle"
+defaultImageCompressionLevel: "Varsayılan görüntü sıkıştırma düzeyi"
+defaultImageCompressionLevel_description: "Düşük seviye görüntü kalitesini korur ancak dosya boyutunu artırır.<br>Yüksek seviye dosya boyutunu azaltır ancak görüntü kalitesini düşürür."
+inMinutes: "Dakika(lar)"
+inDays: "Gün(ler)"
+safeModeEnabled: "Güvenli mod etkinleştirildi"
+pluginsAreDisabledBecauseSafeMode: "Güvenli mod etkinleştirildiği için tüm eklentiler devre dışı bırakılmıştır."
+customCssIsDisabledBecauseSafeMode: "Güvenli mod etkin olduğu için özel CSS uygulanmıyor."
+themeIsDefaultBecauseSafeMode: "Güvenli mod etkinken, varsayılan tema kullanılır. Güvenli modu devre dışı bırakmak bu değişiklikleri geri alır."
+thankYouForTestingBeta: "Beta sürümünü test ettiğin için teşekkür ederiz!"
+_order:
+ newest: "Önce yeni"
+ oldest: "Önce eski"
_chat:
- home: "Ana sayfa"
+ noMessagesYet: "Henüz mesaj yok"
+ newMessage: "Yeni mesaj"
+ individualChat: "Özel Sohbet"
+ individualChat_description: "Başka bir kişiyle özel sohbet edin."
+ roomChat: "Sohbet Odası"
+ roomChat_description: "Birden fazla kişinin katılabileceği bir sohbet odası.\nÖzel sohbetlere izin vermeyen kişileri de davet edebilirsin, ancak davetini kabul etmeleri gerekir."
+ createRoom: "Oda OluÅŸtur"
+ inviteUserToChat: "Kullanıcıları sohbete davet edin"
+ yourRooms: "OluÅŸturulan odalar"
+ joiningRooms: "Katıldığı odalar"
+ invitations: "Davet"
+ noInvitations: "Davet yok"
+ history: "Tarih"
+ noHistory: "Geçmiş bilgisi mevcut değil"
+ noRooms: "Oda bulunamadı"
+ inviteUser: "Kullanıcıları Davet Et"
+ sentInvitations: "Gönderilen Davetler"
+ join: "Katıl"
+ ignore: "Yoksay"
+ leave: "Odadan çık"
+ members: "Üyeler"
+ searchMessages: "Mesajları ara"
+ home: "Ana Sayfa"
+ send: "Gönder"
+ newline: "Yeni satır"
+ muteThisRoom: "Sessiz oda"
+ deleteRoom: "Odayı sil"
+ chatNotAvailableForThisAccountOrServer: "Bu sunucuda veya bu hesapta sohbet özelliği etkin değildir."
+ chatIsReadOnlyForThisAccountOrServer: "Bu sunucuda veya bu hesapta sohbet okunur modundadır. Yeni mesaj yazamaz veya sohbet odası oluşturamaz/katılamazsınız."
+ chatNotAvailableInOtherAccount: "Sohbet işlevi diğer kullanıcı için devre dışı bırakılmıştır."
+ cannotChatWithTheUser: "Bu kullanıcıyla sohbet başlatılamıyor."
+ cannotChatWithTheUser_description: "Sohbet kullanılamıyor veya karşı taraf sohbeti etkinleştirmedi."
+ youAreNotAMemberOfThisRoomButInvited: "Bu odanın katılımcısı değilsin, ancak bir davet aldın. Lütfen daveti kabul ederek katıl."
+ doYouAcceptInvitation: "Daveti kabul ediyor musunuz?"
+ chatWithThisUser: "Kullanıcıyla sohbet et"
+ thisUserAllowsChatOnlyFromFollowers: "Bu kullanıcı yalnızca takipçilerinden gelen sohbetleri kabul eder."
+ thisUserAllowsChatOnlyFromFollowing: "Bu kullanıcı, yalnızca takip ettiği kullanıcılardan gelen sohbetleri kabul eder."
+ thisUserAllowsChatOnlyFromMutualFollowing: "Bu kullanıcı, yalnızca karşılıklı takip eden kullanıcıların sohbetlerini kabul eder."
+ thisUserNotAllowedChatAnyone: "Bu kullanıcı kimseyle sohbet etmiyor."
+ chatAllowedUsers: "Sohbet etmesine izin verilecek kiÅŸiler"
+ chatAllowedUsers_note: "Bu ayardan bağımsız olarak, sohbet mesajı gönderdiğin herkesle sohbet edebilirsin."
+ _chatAllowedUsers:
+ everyone: "Herkes"
+ followers: "Sadece takipçilerin"
+ following: "Only users you are following"
+ mutual: "Sadece takiplerin"
+ none: "Kimse"
+_emojiPalette:
+ palettes: "Palet"
+ enableSyncBetweenDevicesForPalettes: "Cihazlar arasında palet senkronizasyonunu etkinleştir"
+ paletteForMain: "Ana palet"
+ paletteForReaction: "Reaksiyon paleti"
+_settings:
+ driveBanner: "Drive'ı yönetebilir ve yapılandırabilir, kullanımı kontrol edebilir ve dosya yükleme ayarlarını yapılandırabilirsin."
+ pluginBanner: "Eklentilerle istemci özelliklerini genişletebilirsin. Eklentileri yükleyebilir, ayrı ayrı yapılandırabilir ve yönetebilirsin."
+ notificationsBanner: "Sunucudan gelen bildirimlerin türlerini ve kapsamını ve push bildirimlerini yapılandırabilirsin."
+ api: "API"
+ webhook: "Webhook"
+ serviceConnection: "Hizmet entegrasyonu"
+ serviceConnectionBanner: "Dış uygulamalar veya hizmetlerle entegrasyon sağlamak için erişim belirteçlerini ve Webhook'ları yönetin ve yapılandırın."
+ accountData: "Hesap verileri"
+ accountDataBanner: "Hesap verilerini yönetmek için dışa ve içe aktarma."
+ muteAndBlockBanner: "İçeriği gizlemek ve belirli kullanıcıların eylemlerini kısıtlamak için ayarları yapılandırabilir ve yönetebilirsin."
+ accessibilityBanner: "İstemci, görünüm ve davranışları açısından en iyi şekilde kullanılmak üzere kişiselleştirilebilir ve ayarlanabilir."
+ privacyBanner: "Hesap gizliliği ile ilgili ayarları, örneğin içerik görünürlüğü, bulunabilirlik ve takip onayı gibi ayarları yapılandırabilirsin."
+ securityBanner: "Şifre, oturum açma yöntemleri, kimlik doğrulama uygulamaları ve Passkeys gibi hesap güvenliği ile ilgili ayarları yapılandırabilirsin."
+ preferencesBanner: "İstediğin şekilde istemcinin genel davranışını yapılandırabilirsin."
+ appearanceBanner: "İstemcinin görünüm ve ekran ayarlarını tercihlerini göre yapılandırabilirsin."
+ soundsBanner: "İstemcide oynatma için ses ayarlarını yapılandırabilirsin."
+ timelineAndNote: "Pano ve not"
+ makeEveryTextElementsSelectable: "Tüm metin öğelerini seçilebilir hale getir"
+ makeEveryTextElementsSelectable_description: "Bunu etkinleştirmek bazı durumlarda kullanılabilirliği azaltabilir."
+ useStickyIcons: "Kaydırma sırasında simgeleri takip et"
+ enableHighQualityImagePlaceholders: "Yüksek kaliteli görüntüler için yer tutucuları göster"
+ uiAnimations: "UI Animasyonları"
+ showNavbarSubButtons: "Navigasyon çubuğunda alt düğmeleri göster"
+ ifOn: "Açıkken"
+ ifOff: "Kapalıyken"
+ enableSyncThemesBetweenDevices: "Yüklü temaları cihazlar arasında senkronize edin"
+ enablePullToRefresh: "Yenilemek için çekin"
+ enablePullToRefresh_description: "Fareyi kullanırken, kaydırma tekerleğini basılı tutarken sürükle."
+ realtimeMode_description: "Sunucu ile bağlantı kurar ve içeriği gerçek zamanlı olarak günceller. Bu, trafik ve bellek tüketimini artırabilir."
+ contentsUpdateFrequency: "İçerik erişim sıklığı"
+ contentsUpdateFrequency_description: "Değer ne kadar yüksek olursa içerik o kadar sık güncellenir, ancak bu durum performansı düşürür ve trafik ile bellek tüketimini artırır."
+ contentsUpdateFrequency_description2: "Gerçek zamanlı mod açık olduğunda, bu ayardan bağımsız olarak içerik gerçek zamanlı olarak güncellenir."
+ showUrlPreview: "URL önizlemesi"
+ showAvailableReactionsFirstInNote: "Mevcut tepkileri en üstte göster."
+ showPageTabBarBottom: "Sayfa sekme çubuğunu aşağıda göster"
+ _chat:
+ showSenderName: "Gönderenin adını göster"
+ sendOnEnter: "Enter tuşuna basarak gönderin"
+_preferencesProfile:
+ profileName: "Profil adı"
+ profileNameDescription: "Bu cihazı tanımlayan bir ad belirle."
+ profileNameDescription2: "Örnek: “Ana bilgisayarâ€, “Akıllı telefonâ€"
+ manageProfiles: "Profilleri Yönet"
+_preferencesBackup:
+ autoBackup: "Otomatik yedekleme"
+ restoreFromBackup: "Yedeklemeden geri yükle"
+ noBackupsFoundTitle: "Yedekleme bulunamadı"
+ noBackupsFoundDescription: "Otomatik olarak oluşturulan yedekleme bulunamadı, ancak manuel olarak bir yedekleme dosyası kaydettiysen, bunu içe aktarabilir ve geri yükleyebilirsin."
+ selectBackupToRestore: "Geri yüklemek için bir yedekleme seçin"
+ youNeedToNameYourProfileToEnableAutoBackup: "Otomatik yedeklemeyi etkinleştirmek için bir profil adı ayarlanmalıdır."
+ autoPreferencesBackupIsNotEnabledForThisDevice: "Bu cihazda ayarların otomatik yedeklemesi etkinleştirilmemiş."
+ backupFound: "Ayarların yedeği bulundu"
+_accountSettings:
+ requireSigninToViewContents: "İçeriği görüntülemek için oturum açmanız gerekir."
+ requireSigninToViewContentsDescription1: "Oluşturduğun tüm notları ve diğer içeriği görüntülemek için oturum açman gerekir. Bu, tarayıcıların bilgilerini toplamasına engel olacaktır."
+ requireSigninToViewContentsDescription2: "İçerik, URL önizlemelerinde (OGP), web sayfalarına gömülü olarak veya not alıntıları desteklemeyen sunucularda görüntülenmeyecek."
+ requireSigninToViewContentsDescription3: "Bu kısıtlamalar, diğer uzak sunuculardan gelen birleştirilmiş içerik için geçerli olmayabilir."
+ makeNotesFollowersOnlyBefore: "Geçmiş notların yalnızca takipçilere gösterilmesini sağlayın"
+ makeNotesFollowersOnlyBeforeDescription: "Bu özellik etkinleştirildiğinde, yalnızca takipçiler belirlenen tarih ve saatten sonra veya belirlenen süre boyunca görünür olan notları görebilir. Bu özellik devre dışı bırakıldığında, notun yayın durumu da geri yüklenir."
+ makeNotesHiddenBefore: "Geçmiş notları gizli yap"
+ makeNotesHiddenBeforeDescription: "Bu özellik etkinleştirildiğinde, belirlenen tarih ve saatten geçmiş olan veya yalnızca sizin görebildiğiniz notlar. Bu özellik devre dışı bırakıldığında, notun yayın durumu da geri yüklenecek."
+ mayNotEffectForFederatedNotes: "Uzak sunucuya bağlı notlar etkilenmeyebilir."
+ mayNotEffectSomeSituations: "Bu kısıtlamalar basitleştirilmiştir. Uzaktaki bir sunucuda görüntüleme veya moderasyon sırasında gibi bazı durumlarda geçerli olmayabilir."
+ notesHavePassedSpecifiedPeriod: "Belirtilen sürenin geçtiğini unutmayın."
+ notesOlderThanSpecifiedDateAndTime: "Belirtilen tarih ve saatten önceki notlar"
+_abuseUserReport:
+ forward: "İleri"
+ forwardDescription: "Raporu, anonim bir sistem hesabı olarak uzak bir sunucuya iletin."
+ resolve: "Çözüm"
+ accept: "Kabul et"
+ reject: "Reddet"
+ resolveTutorial: "Raporun içeriği meşruysa, “Kabul Et†seçeneğini seçerek sorunu çözülmüş olarak işaretle.\nRaporun içeriği meşru değilse, “Reddet†seçeneğini seçerek raporu yok say."
_delivery:
- stop: "Askıya alınmış"
+ status: "Teslimat durumu"
+ stop: "Askıya al"
+ resume: "Teslimat özgeçmişi"
_type:
none: "Paylaşım"
+ manuallySuspended: "Manuel olarak askıya alınmış"
+ goneSuspended: "Sunucu, sunucunun silinmesi nedeniyle askıya alınmıştır."
+ autoSuspendedForNotResponding: "Sunucu yanıt vermediği için askıya alınmıştır."
+ softwareSuspended: "Bu yazılım artık dağıtılmadığı için askıya alınmıştır."
+_bubbleGame:
+ howToPlay: "Nasıl oynanır"
+ hold: "Tut"
+ _score:
+ score: "Skor"
+ scoreYen: "Kazanılan para miktarı"
+ highScore: "Yüksek puan"
+ maxChain: "Maksimum zincir sayısı"
+ yen: "{yen} Yen"
+ estimatedQty: "{qty} Adet"
+ scoreSweets: "{onigiriQtyWithUnit} Onigiri"
+ _howToPlay:
+ section1: "Konumu ayarlayın ve nesneyi kutuya bırakın."
+ section2: "Aynı türden iki nesne birbirine dokunduğunda, farklı bir nesneye dönüşür ve puan kazanırsınız."
+ section3: "Kutu dolduğunda oyun biter. Kutuyu doldurmadan nesneleri birleştirerek yüksek puan almaya çalış!"
+_announcement:
+ forExistingUsers: "Sadece mevcut kullanıcılar"
+ forExistingUsersDescription: "Bu duyuru, etkinleştirildiğinde yalnızca yayınlandığı anda mevcut olan kullanıcılara gösterilecek. Devre dışı bırakıldığında, yayınlandıktan sonra yeni kaydolan kullanıcılar da bu duyuruyu görecek."
+ needConfirmationToRead: "Ayrı okuma onayı gerektirir"
+ needConfirmationToReadDescription: "Etkinleştirildiğinde, bu duyuruyu okundu olarak işaretlemek için ayrı bir onay mesajı görüntülenir. Bu duyuru, “Tümünü okundu olarak işaretle†işlevinden de hariç tutulur."
+ end: "ArÅŸiv duyurusu"
+ tooManyActiveAnnouncementDescription: "Çok fazla aktif duyuru olması kullanıcı deneyimini kötüleştirebilir. Artık geçerliliğini yitirmiş duyuruları arşivlemeyi düşün."
+ readConfirmTitle: "Okundu olarak iÅŸaretle?"
+ readConfirmText: "Bu, “{title}†içeriğini okundu olarak işaretleyecek."
+ shouldNotBeUsedToPresentPermanentInfo: "Duyuruları, uzun vadede geçerli olacak bilgiler için değil, güncel ve zaman sınırlı bilgileri yayınlamak için kullanmak en iyisidir."
+ dialogAnnouncementUxWarn: "Aynı anda iki veya daha fazla diyalog tarzı bildirim olması, kullanıcı deneyimini önemli ölçüde etkileyebilir, bu nedenle lütfen bunları dikkatli kullanın."
+ silence: "Bildirim yok"
+ silenceDescription: "Bu seçeneği etkinleştirdiğinde, bu duyurunun bildirimi atlanacak ve kullanıcı bunu okumak zorunda kalmayacak."
+_initialAccountSetting:
+ accountCreated: "Hesabınız başarıyla oluşturuldu!"
+ letsStartAccountSetup: "Şimdi hesabını oluşturalım."
+ letsFillYourProfile: "Önce profilini oluşturalım."
+ profileSetting: "Profil ayarları"
+ privacySetting: "Gizlilik ayarları"
+ theseSettingsCanEditLater: "Bu ayarları daha sonra istediğin zaman değiştirebilirsin."
+ youCanEditMoreSettingsInSettingsPageLater: "“Ayarlar†sayfasından yapılandırabileceğin daha birçok ayar bulunmaktadır. Daha sonra mutlaka ziyaret et."
+ followUsers: "İlgilendiğiniz bazı kullanıcıları takip ederek zaman akışını oluşturmaya çalış."
+ pushNotificationDescription: "Push bildirimlerini etkinleştirdiğinde, {name} adresinden gelen bildirimleri doğrudan cihazınıza alabilirsin."
+ initialAccountSettingCompleted: "Profil kurulumu tamamlandı!"
+ haveFun: "{name} ile iyi eÄŸlenceler!"
+ youCanContinueTutorial: "{name} (Misskey) öğreticisine geçebilir veya buradan kurulumu sonlandırıp hemen kullanabilirsin."
+ startTutorial: "Öğreticiye başla"
+ skipAreYouSure: "Profil kurulumunu cidden atlamak mı istiyorsun?"
+ laterAreYouSure: "Profil ayarlarını cidden daha sonra mı yapacaksın?"
+_initialTutorial:
+ launchTutorial: "Öğreticiyi izle"
+ title: "Öğretici"
+ wellDone: "Tebrikler!"
+ skipAreYouSure: "Öğreticiyi kapatmak mı istiyorsunuz?"
+ _landing:
+ title: "Öğreticiye hoş geldin"
+ description: "Burada, Misskey'i kullanmanın temellerini ve özelliklerini öğrenebilirsin."
+ _note:
+ title: "Not nedir?"
+ description: "Misskey'deki gönderiler “Notlar†olarak adlandırılır. Notlar zaman çizelgesinde kronolojik olarak düzenlenir ve gerçek zamanlı olarak güncellenir."
+ reply: "Bir mesaja yanıt vermek için bu düğmeye tıklayın. Yanıtlara yanıt vermek de mümkündür, böylece konuşma bir konu başlığı gibi devam eder."
+ renote: "Bu notu kendi zaman çizelgende paylaşabilirsiniz. Ayrıca yorumlarınızla birlikte alıntı da yapabilirsin."
+ reaction: "Not'a tepkiler ekleyebilirsin. Daha fazla ayrıntı bir sonraki sayfada açıklanacak."
+ menu: "Not ayrıntılarını görüntüleyebilir, bağlantıları kopyalayabilir ve çeşitli diğer işlemleri gerçekleştirebilirsin."
+ _reaction:
+ title: "Reaksiyonlar nedir?"
+ description: "Notlara çeşitli emojilerle tepki verilebilir. Tepkiler, sadece bir ‘beğeni’ ile ifade edilemeyen nüansları ifade etmeni sağlar."
+ letsTryReacting: "Notun üzerindeki ‘+’ düğmesine tıklayarak tepkiler eklenebilir. Bu örnek nota tepki verin!"
+ reactToContinue: "Devam etmek için bir tepki ekle."
+ reactNotification: "Biri notunuza tepki verdiğinde gerçek zamanlı bildirimler alacaksınız."
+ reactDone: "“-†düğmesine basarak bir tepkiyi geri alabilirsin."
+ _timeline:
+ title: "Pano Kavramı"
+ description1: "Misskey, kullanıma göre birden fazla Pano sunar (Bazı Pano'lar sunucunun politikalarına bağlı olarak kullanılamayabilir)."
+ home: "Takip ettiğin hesapların notlarını görüntüleyebilirsin."
+ local: "Bu sunucudaki tüm kullanıcıların notlarını görüntüleyebilirsin."
+ social: "Ev ve Yerel Pano'dan notlar görüntülenecek."
+ global: "Bağlı tüm sunuculardan gelen notları görüntüleyebilirsin."
+ description2: "Ekranın üst kısmındaki Pano'lar arasında istediğin zaman geçiş yapabilirsin."
+ description3: "Ayrıca, Liste Pano ve Kanal Pano da bulunmaktadır. Daha fazla ayrıntı için lütfen {link} adresine bakın."
+ _postNote:
+ title: "Not Yayınlama Ayarları"
+ description1: "Misskey'de not yayınlarken çeşitli seçenekler mevcuttur. Yayınlama formu şu şekildedir."
+ _visibility:
+ description: "Notunu kimlerin görüntüleyebileceğini sınırlayabilirsin."
+ public: "Notunuz tüm kullanıcılar tarafından görülebilir olacaktır."
+ home: "Yalnızca Ana zaman akışında herkese açık. Profilinizi ziyaret edenler, takipçilerin ve yeniden notlar aracılığıyla bunu görebilirler."
+ followers: "Sadece takipçiler tarafından görülebilir. Sadece takipçiler görebilir, başkaları göremez ve başkaları tarafından yeniden not edilemez."
+ direct: "Yalnızca belirli kullanıcılar tarafından görülebilir ve alıcıya bildirim gönderilir. Doğrudan mesajlaşma yerine alternatif olarak kullanılabilir."
+ doNotSendConfidencialOnDirect1: "Hassas bilgileri gönderirken dikkatli olun!"
+ doNotSendConfidencialOnDirect2: "Sunucu yöneticileri yazdıklarınızı görebilir. Güvenilir olmayan sunuculardaki kullanıcılara doğrudan not gönderirken hassas bilgilere dikkat edin."
+ localOnly: "Bu bayrakla yayınlamak, notu diğer sunuculara aktarmaz. Diğer sunuculardaki kullanıcılar, yukarıdaki görüntüleme ayarlarından bağımsız olarak bu notları doğrudan görüntüleyemezler."
+ _cw:
+ title: "İçerik Uyarısı"
+ description: "Gövde yerine, “Yorumlar†alanına yazılan içerik görüntülenecek. “Devamını oku†düğmesine basıldığında gövde görüntülenecek."
+ _exampleNote:
+ cw: "Bu kesinlikle sizi acıktıracak!"
+ note: "Az önce çikolata kaplı bir donut yedim ðŸ©ðŸ˜‹"
+ useCases: "Bu, sunucu kurallarına uyulurken, gerekli notlar için veya spoiler veya hassas metinlerin kendi kendine kısıtlanması için kullanılır."
+ _howToMakeAttachmentsSensitive:
+ title: "Ekleri Hassas Olarak İşaretleme"
+ description: "Sunucu kuralları gereği gerekli olan veya bozulmaması gereken ekler için “hassas†bayrağı ekle."
+ tryThisFile: "Bu forma ekli resmi hassas olarak iÅŸaretlemeyi dene!"
+ _exampleNote:
+ note: "Oops, natto kapağını açarken berbat ettim..."
+ method: "Bir eki hassas olarak işaretlemek için, dosya küçük resmini tıklayın, menüyü açın ve “Hassas Olarak İşaretle†seçeneğini tıklayın."
+ sensitiveSucceeded: "Dosya eklerken, lütfen sunucu kurallarına uygun olarak hassasiyet ayarlarını yapın."
+ doItToContinue: "Devam etmek için ek dosyayı hassas olarak işaretle."
+ _done:
+ title: "Eğitimi tamamladınız! 🎉"
+ description: "Burada tanıtılan işlevler sadece küçük bir kısmıdır. Misskey'i kullanma konusunda daha ayrıntılı bilgi için lütfen şu kaynağa bakın: {link}."
+_timelineDescription:
+ home: "Ana Pano'da, takip ettiğin hesapların notlarını görebilirsin."
+ local: "Yerel Pano'da, bu sunucudaki tüm kullanıcıların notlarını görebilirsin."
+ social: "Pano, Sosyal Pano ve Yerel Pano'dan gelen notları görüntüler."
+ global: "Global Pano'da, bağlı tüm sunuculardan gelen notları görebilirsin."
+_serverRules:
+ description: "Kayıt öncesinde gösterilecek bir dizi kural. Hizmet Şartlarının özetini belirlemen önerilir."
+_serverSettings:
+ iconUrl: "Simge URL'si"
+ appIconDescription: " {host} bir uygulama olarak görüntülendiğinde kullanılacak simgeyi belirtir."
+ appIconUsageExample: "Örneğin, PWA olarak veya bir telefonda ana ekran yer imi olarak görüntülendiğinde"
+ appIconStyleRecommendation: "Simge kare veya daire şeklinde kırpılabileceğinden, içeriğin etrafında renkli kenar boşluğu bulunan bir simge kullanılması önerilir."
+ appIconResolutionMustBe: "Minimum çözünürlük {resolution}'tür."
+ manifestJsonOverride: "manifest.json Geçersiz Kılma"
+ shortName: "Kısa ad"
+ shortNameDescription: "Resmi adın uzun olması durumunda görüntülenebilen, örneğin adının kısaltması."
+ fanoutTimelineDescription: "Etkinleştirildiğinde Pano alma performansını büyük ölçüde artırır ve veritabanı yükünü azaltır. Bunun karşılığında Redis'in bellek kullanımı artacaktır. Sunucu belleği düşükse veya sunucu kararsızsa bunu devre dışı bırakmayı düşün."
+ fanoutTimelineDbFallback: "Veritabanına geri dön"
+ fanoutTimelineDbFallbackDescription: "Etkinleştirildiğinde, Pano önbelleğe alınmamışsa ek sorgular için veritabanına geri döner. Bu özelliği devre dışı bırakmak, geri dönüş sürecini ortadan kaldırarak sunucu yükünü daha da azaltır, ancak alınabilecek zaman çizelgelerinin aralığını sınırlar."
+ reactionsBufferingDescription: "Etkinleştirildiğinde, reaksiyon oluşturma sırasında performans büyük ölçüde artacak ve veritabanı üzerindeki yük azalacaktır. Ancak, Redis bellek kullanımı artacakt."
+ remoteNotesCleaning: "Uzak notların otomatik olarak temizlenmesi"
+ remoteNotesCleaning_description: "Etkinleştirildiğinde, kullanılmayan ve güncelliğini yitirmiş uzak notlar, veritabanının şişmesini önlemek için periyodik olarak temizlenecek."
+ remoteNotesCleaningMaxProcessingDuration: "Maksimum temizleme işlem süresi"
+ remoteNotesCleaningExpiryDaysForEachNotes: "Notları saklamak için minimum gün sayısı"
+ inquiryUrl: "Sorgu URL'si"
+ inquiryUrlDescription: "Sorgu formu için sunucu yöneticisine bir URL veya iletişim bilgileri için bir web sayfası belirtin."
+ openRegistration: "Hesap oluşturmayı açık hale getir"
+ openRegistrationWarning: "Kayıt açma işlemi riskler içerir. Sunucuyu sürekli olarak izleyen ve herhangi bir sorun durumunda hemen müdahale edebilen bir sistemin varsa, bu işlemi etkinleştirmen önerilir."
+ thisSettingWillAutomaticallyOffWhenModeratorsInactive: "Bir süre boyunca moderatör etkinliği algılanmazsa, spam'ı önlemek için bu ayar otomatik olarak kapatılır."
+ deliverSuspendedSoftware: "Askıya Alınan Yazılım"
+ deliverSuspendedSoftwareDescription: "Güvenlik açığı veya diğer nedenlerle sunucunun yazılımının belirli bir isim ve sürüm aralığı için teslimatı durdurabilirsiniz. Bu sürüm bilgileri sunucu tarafından sağlanır ve güvenilirliği garanti edilmez. Sürümü belirtmek için semver aralığı belirtilebilir, ancak >= 2024.3.1 belirtildiğinde 2024.3.1-custom.0 gibi özel sürümler dahil edilmez, bu nedenle >= 2024.3.1-0 gibi ön sürüm belirtimi kullanılması önerilir."
+ singleUserMode: "Tek kullanıcı modu"
+ singleUserMode_description: "Bu sunucunun tek kullanıcısıysanız, bu modu etkinleştirerek performansını optimize edebilirsin."
+ signToActivityPubGet: "ActivityPub GET isteklerini imzalayın"
+ signToActivityPubGet_description: "Normalde bu özellik etkinleştirilmiş olmalıdır. Bu özelliği devre dışı bırakmak federasyonla ilgili sorunları iyileştirebilir, ancak diğer yandan bazı diğer sunuculara yönelik federasyonu devre dışı bırakabilir."
+ proxyRemoteFiles: "Proxy uzak dosyalar"
+ proxyRemoteFiles_description: "Etkinleştirildiğinde, sunucu uzak dosyaları proxy olarak kullanır ve sunar. Bu, resim küçük resimleri oluşturmak ve kullanıcı gizliliğini korumak için kullanışlıdır."
+ allowExternalApRedirect: "ActivityPub aracılığıyla yapılan sorgular için yönlendirmelere izin ver"
+ allowExternalApRedirect_description: "Etkinleştirildiğinde, diğer sunucular bu sunucu aracılığıyla üçüncü taraf içeriğini sorgulayabilir, ancak bu durum içerik sahteciliğine yol açabilir."
+ userGeneratedContentsVisibilityForVisitor: "Kullanıcılar tarafından oluşturulan içeriğin misafirlere görünürlüğü"
+ userGeneratedContentsVisibilityForVisitor_description: "Bu, uygunsuz ve iyi denetlenmemiş uzaktaki içeriğin kendi sunucunuz aracılığıyla istemeden internette yayınlanmasını önlemek için yararlıdır."
+ userGeneratedContentsVisibilityForVisitor_description2: "Sunucu tarafından alınan uzak içerik dahil olmak üzere sunucudaki tüm içeriği koşulsuz olarak İnternet'e yayınlamak risklidir. Bu, içeriğin dağıtılmış yapısından haberdar olmayan misafirler için özellikle önemlidir, çünkü onlar yanlışlıkla uzak içeriğin bile sunucudaki kullanıcılar tarafından oluşturulan içerik olduğunu düşünebilirler."
+ restartServerSetupWizardConfirm_title: "Sunucu kurulum sihirbazını yeniden başlatmak ister misin?"
+ restartServerSetupWizardConfirm_text: "Bazı mevcut ayarlar sıfırlanacaktır."
+ entrancePageStyle: "Giriş sayfası stili"
+ showTimelineForVisitor: "Panoyu göster"
+ _userGeneratedContentsVisibilityForVisitor:
+ all: "Her şey halka açıktır."
+ localOnly: "Yalnızca yerel içerik yayınlanır, uzak içerik gizli tutulur."
+ none: "Her ÅŸey gizlidir."
+_accountMigration:
+ moveFrom: "Başka bir hesabı bu hesaba taşıyın"
+ moveFromSub: "BaÅŸka bir hesaba takma ad oluÅŸturun"
+ moveFromLabel: "Orijinal Hesap #{n}"
+ moveFromDescription: "Bu hesaptan taşınacak hesap için bir takma ad oluşturmalısınız.\nTaşınacak hesabı aşağıdaki biçimde girin: @username@server.example.com\nTakma adı silmek için alanı boş bırakın (önerilmez)."
+ moveTo: "Bu hesabı başka bir hesaba taşıyın"
+ moveToLabel: "Taşınacak hesap:"
+ moveCannotBeUndone: "Hesap taşıma işlemi geri alınamaz."
+ moveAccountDescription: "Bu işlem, hesabını farklı bir hesaba taşıyacaktır.\n・Bu hesabın takipçileri otomatik olarak yeni hesaba taşınacak.\n・Bu hesap, şu anda takip ettiği tüm kullanıcıları takipten çıkaracak.\n・Bu hesapta yeni notlar vb. oluşturamayacaksın.\n\nTakipçilerin taşınması otomatik olarak gerçekleşirken, takip ettiğin kullanıcıların listesini taşımak için bazı adımları manuel olarak hazırlaman gerekir. Bunu yapmak için, ayarlar menüsünden takipçilerini dışa aktar ve daha sonra yeni hesaba içe aktar. Aynı prosedür, listelerinin yanı sıra sessize aldığın ve engellediğin kullanıcılar için de geçerli.\n\n(Bu açıklama Misskey v13.12.0 ve sonraki sürümler için geçerlidir. Mastodon gibi diğer ActivityPub yazılımları farklı şekilde çalışabilir.)"
+ moveAccountHowTo: "Geçiş yapmak için, önce taşınacak hesapta bu hesap için bir takma ad oluşturun.\nTakma adı oluşturduktan sonra, taşınacak hesabı aşağıdaki biçimde girin: @username@server.example.com"
+ startMigration: "Taşın"
+ migrationConfirm: "Bu hesabı {account} hesabına gerçekten taşımak istiyor musun? Bu işlem başlatıldıktan sonra durdurulamaz veya geri alınamaz ve bu hesabı artık orijinal haliyle kullanamazsın."
+ movedAndCannotBeUndone: "\nBu hesap taşınmıştır.\nTaşıma işlemi geri alınamaz."
+ postMigrationNote: "Bu hesap, geçiş işlemi tamamlandıktan 24 saat sonra şu anda takip ettiği tüm hesapları takipten çıkaracak.\nHem takipçi sayısı hem de takip edilenler sayısı sıfır olacak. Takipçilerinin bu hesabın yalnızca takipçilere açık gönderilerini görememesi durumunu önlemek için, takipçilerin bu hesabı takip etmeye devam edecek."
+ movedTo: "Yeni hesap:"
+_achievements:
+ earnedAt: "Şurada açıldı"
+ _types:
+ _notes1:
+ title: "msky'ımı kuruyorum"
+ description: "İlk notunuzu yayınlayın"
+ flavor: "Misskey ile iyi vakit geçirin!"
+ _notes10:
+ title: "Bazı notlar"
+ description: "10 not gönder"
+ _notes100:
+ title: "Çok sayıda not"
+ description: "100 notu yayınla"
+ _notes500:
+ title: "Notlarla kaplı"
+ description: "500 notu yayınla"
+ _notes1000:
+ title: "Notlardan oluÅŸan bir daÄŸ"
+ description: "1.000 not yayınla"
+ _notes5000:
+ title: "TaÅŸan notlar"
+ description: "5.000 not yayınla"
+ _notes10000:
+ title: "Süper not"
+ description: "10.000 not yayınla"
+ _notes20000:
+ title: "Daha... fazla... not... lazım..."
+ description: "20.000 not yayınla"
+ _notes30000:
+ title: "Notlar notlar notlar!"
+ description: "30.000 not yayınla"
+ _notes40000:
+ title: "Not fabrikası"
+ description: "40.000 not yayınla"
+ _notes50000:
+ title: "Notların gezegeni"
+ description: "50.000 not yayınla"
+ _notes60000:
+ title: "Not kuasar"
+ description: "60.000 not yayınla"
+ _notes70000:
+ title: "Not kara deliÄŸi"
+ description: "70.000 not yayınla"
+ _notes80000:
+ title: "Not galaksisi"
+ description: "80.000 not yayınla"
+ _notes90000:
+ title: "Not evreni"
+ description: "90.000 not yayınla"
+ _notes100000:
+ title: "TÜM NOTLARINIZ BİZE AİTTİR"
+ description: "100.000 yayınlanmış not"
+ flavor: "Gerçekten söyleyecek çok şeyin var."
+ _login3:
+ title: "Başlangıç I"
+ description: "Log in for a total of 3 days"
+ flavor: "Toplam 3 gün boyunca oturum açın"
+ _login7:
+ title: "Başlangıç II"
+ description: "Toplam 7 gün boyunca oturum açın"
+ flavor: "Henüz işlerin nasıl yürüdüğünü anladığını hissediyor musun?"
+ _login15:
+ title: "Başlangıç III"
+ description: "Toplam 15 gün boyunca oturum açın"
+ _login30:
+ title: "Misskist I"
+ description: "Toplam 30 gün boyunca oturum açın"
+ _login60:
+ title: "Misskist II"
+ description: "Toplam 60 gün boyunca oturum açın"
+ _login100:
+ title: "Misskist III"
+ description: "Toplam 100 gün boyunca oturum açın"
+ flavor: "Åžiddetli Misskist"
+ _login200:
+ title: "Düzenli I"
+ description: "Toplam 200 gün boyunca oturum açın."
+ _login300:
+ title: "Düzenli II"
+ description: "Toplam 300 gün boyunca oturum açın"
+ _login400:
+ title: "Düzenli III"
+ description: "Toplam 400 gün boyunca oturum açın"
+ _login500:
+ title: "Uzman I"
+ description: "Toplam 500 gün boyunca oturum açın"
+ flavor: "Arkadaşlar, sık sık not almayı sevdiğim söylenir."
+ _login600:
+ title: "Uzman II"
+ description: "Toplam 600 gün boyunca oturum açın"
+ _login700:
+ title: "Uzman III"
+ description: "Toplam 700 gün boyunca oturum açın"
+ _login800:
+ title: "Notların Ustası I"
+ description: "Toplam 800 gün boyunca oturum açın"
+ _login900:
+ title: "Notların Ustası II"
+ description: "Toplam 900 gün boyunca oturum açın"
+ _login1000:
+ title: "Notların Ustası III"
+ description: "Toplam 1.000 gün boyunca oturum açın."
+ flavor: "Misskey'i kullandığınız için teşekkür ederiz!"
+ _noteClipped1:
+ title: "Kesinlikle... kesmeliyim..."
+ description: "İlk notunu ekle"
+ _noteFavorited1:
+ title: "Yıldız gözlemcisi"
+ description: "İlk notunu favorilerine ekle"
+ _myNoteFavorited1:
+ title: "Yıldızları Arayış"
+ description: "Başka birinin notlarınızdan birini favorilerine eklemesini sağlayın"
+ _profileFilled:
+ title: "İyi hazırlanmış"
+ description: "Profilini oluÅŸtur"
+ _markedAsCat:
+ title: "Ben bir kediyim."
+ description: "Hesabını kedi olarak işaretle"
+ flavor: "Sana daha sonra bir isim vereceÄŸim."
+ _following1:
+ title: "İlk kullanıcınızı takip edin"
+ description: "Bir kullanıcıyı takip et"
+ _following10:
+ title: "Devam et... devam et..."
+ description: "10 kullanıcıyı takip et"
+ _following50:
+ title: "Bir sürü arkadaş"
+ description: "50 hesabı takip et"
+ _following100:
+ title: "100 ArkadaÅŸ"
+ description: "100 hesabı takip et"
+ _following300:
+ title: "Arkadaş yüklemesi"
+ description: "300 hesabı takip et"
+ _followers1:
+ title: "İlk takipçi"
+ description: "1 takipçi kazanın"
+ _followers10:
+ title: "Beni takip edin!"
+ description: "10 takipçi kazanın"
+ _followers50:
+ title: "Kalabalıklar halinde gelmek"
+ description: "50 takipçi kazanın"
+ _followers100:
+ title: "Popüler"
+ description: "100 takipçi kazanın"
+ _followers300:
+ title: "Lütfen tek sıra halinde dizilin."
+ description: "300 takipçi kazanın"
+ _followers500:
+ title: "Radyo Kulesi"
+ description: "500 takipçi kazanın"
+ _followers1000:
+ title: "Etkileyici"
+ description: "1.000 takipçi kazanın"
+ _collectAchievements30:
+ title: "Başarı Koleksiyoncusu"
+ description: "30 başarı kazan"
+ _viewAchievements3min:
+ title: "Beğeniler Başarılar"
+ description: "Likes Achievements"
+ _iLoveMisskey:
+ title: "Misskey'i seviyorum"
+ description: "“I ⤠#Misskey†yazısını paylaş"
+ flavor: "Misskey geliştirme ekibi desteğin için çok teşekkür eder!"
+ _foundTreasure:
+ title: "Hazine Avı"
+ description: "Gizli hazineyi buldunuz."
+ _client30min:
+ title: "Kısa mola"
+ description: "Misskey'i en az 30 dakika açık tutun."
+ _client60min:
+ title: "Misskey'de “Miss†yok"
+ description: "Misskey'i en az 60 dakika açık tutun."
+ _noteDeletedWithin1min:
+ title: "Nevermind"
+ description: "BoÅŸ ver"
+ _postedAtLateNight:
+ title: "Gececi"
+ description: "Gece geç saatlerde bir not yayınlayın"
+ flavor: "Yatma vakti geldi."
+ _postedAt0min0sec:
+ title: "KonuÅŸan Saat"
+ description: "00:00'da bir not yayınlayın."
+ flavor: "Tık tık tık, güm!"
+ _selfQuote:
+ title: "Öz Referans"
+ description: "Kendi notunuzu alıntı yapın"
+ _htl20npm:
+ title: "Akış Zaman Çizelgesi"
+ description: "Ev zaman çizelgenizin hızı 20 npm'yi (dakika başına not sayısı) aşıyor mu?"
+ _viewInstanceChart:
+ title: "Analist"
+ description: "Sunucunun grafiklerini görüntüle"
+ _outputHelloWorldOnScratchpad:
+ title: "Merhaba, dünya!"
+ description: "Scratchpad'de “hello world†yazdırın."
+ _open3windows:
+ title: "Çoklu Pencere"
+ description: "Aynı anda en az 3 pencere açık olsun."
+ _driveFolderCircularReference:
+ title: "Döngüsel Referans"
+ description: "Drive'da yinelemeli olarak iç içe geçmiş bir klasör oluşturmaya çalış."
+ _reactWithoutRead:
+ title: "Cidden okudun mu?"
+ description: "100 karakterden uzun bir notun yayınlanmasından itibaren 3 saniye içinde yanıt verin."
+ _clickedClickHere:
+ title: "Buraya tıklayın"
+ description: "Buraya tıkladınız"
+ _justPlainLucky:
+ title: "Sadece Şanslı"
+ description: "Her 10 saniyede bir %0,005 olasılıkla elde edilme şansı vardır."
+ _setNameToSyuilo:
+ title: "Tanrı Kompleksi"
+ description: "Adınızı “syuilo†olarak ayarlayın."
+ _passedSinceAccountCreated1:
+ title: "Birinci Yıl Dönümü"
+ description: "Hesabınızın oluşturulmasından bu yana bir yıl geçti."
+ _passedSinceAccountCreated2:
+ title: "İki Yıllık Yıldönümü"
+ description: "Hesabınızın oluşturulmasından bu yana iki yıl geçti."
+ _passedSinceAccountCreated3:
+ title: "Üçüncü Yıl Dönümü"
+ description: "Hesabınızın oluşturulmasından bu yana üç yıl geçti."
+ _loggedInOnBirthday:
+ title: "Doğum günün kutlu olsun"
+ description: "Doğum gününüzde giriş yapın"
+ _loggedInOnNewYearsDay:
+ title: "Yeni yılınız kutlu olsun!"
+ description: "Yılın ilk gününde oturum açıldı"
+ flavor: "Bu sunucuda bir başka harika yıla"
+ _cookieClicked:
+ title: "Çerezleri tıklayarak oynanan bir oyun"
+ description: "Çerezi tıkladı"
+ flavor: "Wait, are you on the correct website?"
+ _brainDiver:
+ title: "Brain Diver"
+ description: "Brain Diver bağlantısını paylaşın"
+ flavor: "Misskey-Misskey La-Tu-Ma"
+ _smashTestNotificationButton:
+ title: "Test taşması"
+ description: "Bildirim testini çok kısa bir süre içinde tekrar tekrar tetikle."
+ _tutorialCompleted:
+ title: "Misskey Temel Kurs Diploması"
+ description: "Eğitim tamamlandı"
+ _bubbleGameExplodingHead:
+ title: "🤯"
+ description: "Kabarcık oyunundaki en büyük nesne"
+ _bubbleGameDoubleExplodingHead:
+ title: "Çift🤯"
+ description: "Aynı anda balon oyunundaki en büyük iki nesne"
+ flavor: "Öğle yemeği kutunu şöyle doldurabilirsin 🤯 🤯 biraz."
+_role:
+ new: "Yeni rol"
+ edit: "Rolü düzenle"
+ name: "Rol adı"
+ description: "Rol tanımı"
+ permission: "Rol izinleri"
+ descriptionOfPermission: "<b>Moderators</b> temel moderasyon işlemlerini gerçekleştirebilir.\n<b>Administrators</b> örneğin tüm ayarlarını değiştirebilir."
+ assignTarget: "Görev türü"
+ descriptionOfAssignTarget: "Bu rolün parçası olan ve olmayan kişileri manuel olarak değiştirmek için </b>manuel</b>.\nKullanıcıların bir koşula bağlı olarak bu role otomatik olarak atanmasını ve bu rolden çıkarılmasını sağlamak için <b>koşullu.</b>"
+ manual: "Kılavuz"
+ manualRoles: "Manuel roller"
+ conditional: "KoÅŸullu"
+ conditionalRoles: "KoÅŸullu roller"
+ condition: "Durum"
+ isConditionalRole: "Bu, koşullu bir roldür."
+ isPublic: "Kamu rolü"
+ descriptionOfIsPublic: "Bu rol, atanan kullanıcıların profillerinde görüntülenecek."
+ options: "Seçenekler"
+ policies: "Politikalar"
+ baseRole: "Rol ÅŸablonu"
+ useBaseValue: "Rol ÅŸablonu deÄŸerini kullan"
+ chooseRoleToAssign: "Atamak istediğin rolü seç"
+ iconUrl: "Simge URL'si"
+ asBadge: "Rozet olarak göster"
+ descriptionOfAsBadge: "This role's icon will be displayed next to the username of users with this role if turned on."
+ isExplorable: "Rolü keşfedilebilir hale getir"
+ descriptionOfIsExplorable: "Bu rolün zaman çizelgesi ve bu role sahip kullanıcıların listesi, etkinleştirilirse kamuya açık hale getirilecek."
+ displayOrder: "Pozisyon"
+ descriptionOfDisplayOrder: "Sayı ne kadar yüksekse, UI pozisyonu da o kadar yüksek olur."
+ preserveAssignmentOnMoveAccount: "Geçiş sırasında rol atamalarını koruyun"
+ preserveAssignmentOnMoveAccount_description: "Etkinleştirildiğinde, bu rol, bu role sahip bir hesap taşındığında hedef hesaba aktarılacak."
+ canEditMembersByModerator: "Moderatörlerin bu rol için üye listesini düzenlemesine izin ver"
+ descriptionOfCanEditMembersByModerator: "Etkinleştirildiğinde, moderatörler ve yöneticiler bu role kullanıcıları atayabilir ve atamalarını kaldırabilir. Devre dışı bırakıldığında, yalnızca yöneticiler kullanıcıları atayabilir."
+ priority: "Öncelik"
+ _priority:
+ low: "Düşük"
+ middle: "Orta"
+ high: "Yüksek"
+ _options:
+ gtlAvailable: "Global Pano'yu görüntüleyebilir"
+ ltlAvailable: "Yerel zaman çizelgesini görüntüleyebilir"
+ canPublicNote: "Halka açık notlar gönderebilir"
+ mentionMax: "Bir notta maksimum bahsetme sayısı"
+ canInvite: "Sunucu davet kodları oluşturabilir"
+ inviteLimit: "Davet sınırı"
+ inviteLimitCycle: "Davet sınırı bekleme süresi"
+ inviteExpirationTime: "Davet süresi dolma aralığı"
+ canManageCustomEmojis: "Özel emojileri yönetebilir"
+ canManageAvatarDecorations: "Avatar süslerini yönet"
+ driveCapacity: "Drive kapasitesi"
+ maxFileSize: "Yükleyebileceğin maksimum dosya boyutu"
+ alwaysMarkNsfw: "Dosyaları her zaman NSFW olarak işaretle"
+ canUpdateBioMedia: "Bir simge veya banner görüntüsünü düzenleyebilir"
+ pinMax: "Sabitlenmiş notların maksimum sayısı"
+ antennaMax: "Maksimum anten sayısı"
+ wordMuteMax: "Kelime sessizlerinde izin verilen maksimum karakter sayısı"
+ webhookMax: "Maksimum Webhook sayısı"
+ clipMax: "Maksimum klip sayısı"
+ noteEachClipsMax: "Bir klip içindeki maksimum not sayısı"
+ userListMax: "Maksimum kullanıcı listesi sayısı"
+ userEachUserListsMax: "Kullanıcı listesindeki maksimum kullanıcı sayısı"
+ rateLimitFactor: "Hız Sınırı"
+ descriptionOfRateLimitFactor: "Daha düşük oran sınırları daha az kısıtlayıcıdır, daha yüksek olanlar ise daha kısıtlayıcıdır."
+ canHideAds: "Reklamları gizleyebilir"
+ canSearchNotes: "Not arama kullanımı"
+ canSearchUsers: "Kullanıcı arama"
+ canUseTranslator: "Çevirmen kullanımı"
+ avatarDecorationLimit: "Maksimum avatar süsü sayısı"
+ canImportAntennas: "Antenlerin içe aktarılmasına izin ver"
+ canImportBlocking: "Engellemeyi içe aktarmaya izin ver"
+ canImportFollowing: "Aşağıdakilerin içe aktarılmasına izin ver"
+ canImportMuting: "Sessize alma özelliğini içe aktarmaya izin ver"
+ canImportUserLists: "Listelerin içe aktarılmasına izin ver"
+ chatAvailability: "Sohbeti İzin Ver"
+ uploadableFileTypes: "Yüklenebilir dosya türleri"
+ uploadableFileTypes_caption: "İzin verilen MIME/dosya türlerini belirtir. Birden fazla MIME türü, yeni bir satırla ayırarak belirtilebilir ve joker karakterler yıldız işareti (*) ile belirtilebilir. (örneğin, image/*)"
+ uploadableFileTypes_caption2: "Bazı dosya türleri algılanamayabilir. Bu tür dosyalara izin vermek için, spesifikasyona {x} ekle."
+ noteDraftLimit: "Sunucu notlarının olası taslak sayısı"
+ watermarkAvailable: "Filigran işlevinin kullanılabilirliği"
+ _condition:
+ roleAssignedTo: "Manuel rollere atanmış"
+ isLocal: "Yerel kullanıcı"
+ isRemote: "Uzak kullanıcı"
+ isCat: "Kedi Kullanıcıları"
+ isBot: "Bot Kullanıcıları"
+ isSuspended: "Askıya alınmış kullanıcı"
+ isLocked: "Özel hesaplar"
+ isExplorable: "“Hesabı bulunabilir hale getir†özelliğini etkili bir şekilde kullanan kullanıcı"
+ createdLessThan: "Hesap oluşturulduktan sonra X'ten az zaman geçti."
+ createdMoreThan: "Hesap oluşturulmasından bu yana X'ten fazla zaman geçti."
+ followersLessThanOrEq: "X veya daha az takipçisi var"
+ followersMoreThanOrEq: "X veya daha fazla takipçisi var"
+ followingLessThanOrEq: "X veya daha az sayıda hesabı takip ediyor"
+ followingMoreThanOrEq: "X veya daha fazla hesabı takip ediyor"
+ notesLessThanOrEq: "Gönderi sayısı şundan az/eşit"
+ notesMoreThanOrEq: "Gönderi sayısı şundan büyük/eşit"
+ and: "KoÅŸul-AND"
+ or: "KoÅŸul-QR"
+ not: "KoÅŸul-NOT"
+_sensitiveMediaDetection:
+ description: "Makine öğrenimi yoluyla hassas medyayı otomatik olarak tanıyarak sunucu moderasyonunun yükünü azaltır. Bu, sunucu üzerindeki yükü biraz artıracaktır."
+ sensitivity: "Algılama hassasiyeti"
+ sensitivityDescription: "Hassasiyeti azaltmak, yanlış algılamaların (yanlış pozitifler) azalmasına neden olurken, hassasiyeti artırmak ise algılamaların kaçırılmasının (yanlış negatifler) azalmasına neden olur."
+ setSensitiveFlagAutomatically: "Hassas olarak iÅŸaretle"
+ setSensitiveFlagAutomaticallyDescription: "Bu seçenek kapatılsa bile, dahili algılama sonuçları korunacaktır."
+ analyzeVideos: "Videoların analizini etkinleştir"
+ analyzeVideosDescription: "Görüntülerin yanı sıra videoları da analiz eder. Bu, sunucu üzerindeki yükü biraz artıracaktır."
+_emailUnavailable:
+ used: "Bu E-Posta adresi zaten kullanılıyor."
+ format: "Bu E-Posta adresinin biçimi geçersizdir."
+ disposable: "Tek kullanımlık E-Posta adresleri kullanılamaz."
+ mx: "Bu E-Posta sunucusu geçersizdir."
+ smtp: "Bu E-Posta sunucusu yanıt vermiyor."
+ banned: "Bu E-Posta adresiyle kayıt olamazsınız."
+_ffVisibility:
+ public: "Bu e-posta adresiyle kayıt olamazsınız."
+ followers: "Sadece takipçiler tarafından görülebilir"
+ private: "Özel"
+_signup:
+ almostThere: "Neredeyse vardık"
+ emailAddressInfo: "Lütfen E-Posta adresini gir. Bu adres kamuya açık hale getirilmeyecek."
+ emailSent: "Onay e-postası E-Posta adresine ({email}) gönderilmiştir. Hesap oluşturma işlemini tamamlamak için e-postadaki bağlantıya tıkla."
_accountDelete:
- started: "Silme işlemi başlatıldı"
+ accountDelete: "Hesabı sil"
+ mayTakeTime: "Hesap silme işlemi kaynak yoğun bir işlem olduğundan, oluşturduğun içerik miktarına ve yüklediğin dosya sayısına bağlı olarak tamamlanması biraz zaman alabilir."
+ sendEmail: "Hesap silme işlemi tamamlandıktan sonra, bu hesaba kayıtlı E-Posta adresine bir e-posta gönderilecek."
+ requestAccountDelete: "Hesap silme talebi"
+ started: "Silme işlemi başlatıldı."
+ inProgress: "Silme iÅŸlemi ÅŸu anda devam ediyor."
+_ad:
+ back: "Geri"
+ reduceFrequencyOfThisAd: "Bu reklamı daha az göster"
+ hide: "Gizle"
+ timezoneinfo: "Haftanın günü, sunucunun saat diliminden belirlenir."
+ adsSettings: "Reklam ayarları"
+ notesPerOneAd: "Gerçek zamanlı güncelleme reklam yerleşim aralığı (Reklam başına notlar)"
+ setZeroToDisable: "Bu değeri 0 olarak ayarlayarak gerçek zamanlı güncelleme reklamlarını devre dışı bırakın."
+ adsTooClose: "Mevcut reklam aralığı çok düşük olduğu için kullanıcı deneyimini önemli ölçüde kötüleştirebilir."
+_forgotPassword:
+ enterEmail: "Kayıt olurken kullandığın E-Posta adresini gir. Şifreni sıfırlayabileceğin bir bağlantı bu adrese gönderilecek."
+ ifNoEmail: "Kayıt sırasında E-Posta kullanmadıysanız, lütfen bunun yerine sunucu yöneticisiyle iletişime geçin."
+ contactAdmin: "This instance does not support using email addresses, please contact the instance administrator to reset your password instead."
+_gallery:
+ my: "Benim Galerim"
+ liked: "Beğenilen Gönderiler"
+ like: "BeÄŸen"
+ unlike: "Benzerlerini kaldır"
_email:
_follow:
- title: "seni takip etti"
+ title: "Yeni bir takipçin var."
+ _receiveFollowRequest:
+ title: "Bir takip isteği aldınız."
+_plugin:
+ install: "Eklentileri yükle takip isteği aldınız"
+ installWarn: "Güvenilir olmayan eklentileri yükleme."
+ manage: "Eklentileri yönet"
+ viewSource: "Kaynak görüntüle"
+ viewLog: "Günlüğü göster"
+_preferencesBackups:
+ list: "Created backups"
+ saveNew: "Yeni yedeklemeyi kaydet"
+ loadFile: "Dosyadan yükle"
+ apply: "Bu cihaza baÅŸvur"
+ save: "DeÄŸiÅŸiklikleri kaydet"
+ inputName: "Lütfen bu yedekleme için bir ad girin."
+ cannotSave: "Kaydetme başarısız oldu"
+ nameAlreadyExists: "“{name}†adlı bir yedekleme zaten mevcut. Lütfen farklı bir ad girin."
+ applyConfirm: "Bu cihaza “{name}†yedeklemesini cidden uygulamak istiyor musun? Bu cihazın mevcut ayarları üzerine yazılacaktır."
+ saveConfirm: "Yedeklemeyi {name} olarak kaydedin?"
+ deleteConfirm: "{name} yedeklemesini silmek ister misin?"
+ renameConfirm: "Bu yedeğin adını “{old}†den “{new}†ye değiştirmek ister misin?"
+ noBackups: "Yedekleme mevcut değil. “Yeni yedekleme oluştur†seçeneğini kullanarak bu sunucudaki istemci ayarlarınızı yedekleyebilirsin."
+ createdAt: "OluÅŸturulma tarihi: {date} {time}"
+ updatedAt: "Güncelleme tarihi: {date} {time}"
+ cannotLoad: "Yükleme başarısız"
+ invalidFile: "Geçersiz dosya biçimi"
+_registry:
+ scope: "Kapsam"
+ key: "Anahtar"
+ keys: "Anahtarlar"
+ domain: "Alan adı"
+ createKey: "Anahtar oluÅŸtur"
+_aboutMisskey:
+ about: "Misskey, 2014 yılından beri syuilo tarafından geliştirilen açık kaynaklı bir yazılımdır."
+ contributors: "Başlıca katkıda bulunanlar"
+ allContributors: "Tüm katkıda bulunanlar"
+ source: "Kaynak kodu"
+ original: "Orijinal"
+ thisIsModifiedVersion: "{name} orijinal Misskey'in değiştirilmiş bir sürümünü kullanır."
+ translation: "Misskey'i çevir"
+ donate: "Misskey'e bağış yapın"
+ morePatrons: "Burada adı geçmeyen diğer birçok yardımseverin desteğine de teşekkür ederiz. Teşekkürler! 🥰"
+ patrons: "Müşteriler"
+ projectMembers: "Proje üyeleri"
+_displayOfSensitiveMedia:
+ respect: "Hassas olarak işaretlenmiş medyayı gizle"
+ ignore: "Hassas olarak işaretlenmiş medya görüntüleme"
+ force: "Hide all media"
+_instanceTicker:
+ none: "Asla gösterme"
+ remote: "Uzak kullanıcılar için göster"
+ always: "Her zaman göster"
+_serverDisconnectedBehavior:
+ reload: "Otomatik olarak yeniden yükle"
+ dialog: "Otomatik olarak yeniden yükle"
+ quiet: "Göze batmayan uyarı göster"
+_channel:
+ create: "Kanal oluÅŸtur"
+ edit: "Kanalı düzenle"
+ setBanner: "AfiÅŸ ayarla"
+ removeBanner: "Afişi kaldır"
+ featured: "Trend olan"
+ owned: "Sahip olunan"
+ following: "Takip edildi"
+ usersCount: "{n} Katılımcılar"
+ notesCount: "{n} Notlar"
+ nameAndDescription: "Adı ve açıklaması"
+ nameOnly: "Sadece isim"
+ allowRenoteToExternal: "Kanal dışında yeniden not alma ve alıntı yapmaya izin ver"
+_menuDisplay:
+ sideFull: "Yan"
+ sideIcon: "Yan (Simgeler)"
+ top: "En üst"
+ hide: "Gizle"
+_wordMute:
+ muteWords: "Sessiz kelimeler"
+ muteWordsDescription: "AND koşulu için boşluklarla, OR koşulu için satır sonlarıyla ayırın."
+ muteWordsDescription2: "Surround keywords with slashes to use regular expressions."
+_instanceMute:
+ instanceMuteDescription: "Bu, listelenen sunuculardan gelen tüm notları/yeniden notları sessize alır, sessize alınan bir sunucudan bir kullanıcıya yanıt veren kullanıcıların notları da dahil olmak üzere."
+ instanceMuteDescription2: "Yeni satırlarla ayırın"
+ title: "Listelenen sunuculardan notları gizler."
+ heading: "Sessize alınacak sunucuların listesi"
_theme:
+ explore: "Temaları Keşfedin"
+ install: "Bir tema yükle"
+ manage: "Temaları yönet"
+ code: "Tema kodu"
+ copyThemeCode: "Tema kodunu kopyala"
+ description: "Açıklama"
+ installed: "{name} kuruldu"
+ installedThemes: "Yüklü temalar"
+ builtinThemes: "YerleÅŸik temalar"
+ instanceTheme: "Sunucu teması"
+ alreadyInstalled: "Bu tema zaten yüklenmiş."
+ invalid: "Bu temanın biçimi geçersizdir."
+ make: "Bir tema oluÅŸturun"
+ base: "Base"
+ addConstant: "Sabit ekle"
+ constant: "Sabit"
+ defaultValue: "Varsayılan değer"
color: "Renk"
+ refProp: "Bir özelliği referans al"
+ refConst: "Sabiti referans al"
+ key: "Anahtar"
+ func: "İşlevler"
+ funcKind: "İşlev türü"
+ argument: "Tartışma"
+ basedProp: "Referans verilen mülk"
+ alpha: "Opaklık"
+ darken: "Koyulaştır"
+ lighten: "Hafiflet"
+ inputConstantName: "Bu sabit için bir ad girin"
+ importInfo: "Buraya tema kodunu girersen, onu tema düzenleyicisine aktarabilirsin."
+ deleteConstantConfirm: "{const} sabitini cidden silmek istiyor musun?"
keys:
- mention: "Bahset"
- renote: "vazgeçme"
+ accent: "Aksan"
+ bg: "Arka plan"
+ fg: "Metin"
+ focus: "Odak"
+ indicator: "Gösterge"
+ panel: "Panel"
+ shadow: "Gölge"
+ header: "Başlık"
+ navBg: "Kenar çubuğu arka planı"
+ navFg: "Kenar çubuğu metni"
+ navActive: "Kenar çubuğu metni (Etkin)"
+ navIndicator: "Kenar çubuğu göstergesi"
+ link: "Link"
+ hashtag: "Hashtag"
+ mention: "Bahsetmeler"
+ mentionMe: "Bahsetmeler (Ben)"
+ renote: "Renote"
+ modalBg: "Modal arka plan"
+ divider: "Bölücü"
+ scrollbarHandle: "Kaydırma çubuğu"
+ scrollbarHandleHover: "Kaydırma çubuğu (Fareyi üzerine getir)"
+ dateLabelFg: "Tarih etiketi metni"
+ infoBg: "Bilgi arka planı"
+ infoFg: "Bilgi metni"
+ infoWarnBg: "Uyarı arka planı"
+ infoWarnFg: "Uyarı metni"
+ toastBg: "Bildirim arka planı"
+ toastFg: "Bildirim metni"
+ buttonBg: "Düğme arka planı"
+ buttonHoverBg: "Button background (Hover)"
+ inputBorder: "Giriş alanı kenarlığı"
+ badge: "Rozet"
+ messageBg: "Sohbet arka planı"
+ fgHighlighted: "Vurgulanan Metin"
_sfx:
- note: "notlar"
- notification: "Bildirim"
+ note: "Yeni not"
+ noteMy: "Kendi notu"
+ notification: "Bildirimler"
+ reaction: "Reaksiyon seçimi hakkında"
+ chatMessage: "Sohbet Mesajları"
+_soundSettings:
+ driveFile: "Drive'da bir ses dosyası kullanın."
+ driveFileWarn: "Drive'dan bir ses dosyası seçin."
+ driveFileTypeWarn: "Bu dosya desteklenmiyor"
+ driveFileTypeWarnDescription: "Bir ses dosyası seçin"
+ driveFileDurationWarn: "Ses kaydı çok uzun."
+ driveFileDurationWarnDescription: "Uzun sesli mesajlar Misskey'in kullanımını engelleyebilir. Devam etmek istiyor musunuz?"
+ driveFileError: "Ses yüklenemedi. Lütfen ayarları değiştir."
+_ago:
+ future: "Gelecek"
+ justNow: "Åžimdi"
+ secondsAgo: "{n} sn"
+ minutesAgo: "{n} dk"
+ hoursAgo: "{n} sa"
+ daysAgo: "{n} gün"
+ weeksAgo: "{n} hafta"
+ monthsAgo: "{n} ay"
+ yearsAgo: "{n} yıl"
+ invalid: "Geçersiz"
+_timeIn:
+ seconds: "{n} saniye içinde"
+ minutes: "{n} dakika içinde"
+ hours: "{n} saat içinde"
+ days: "{n} gün içinde"
+ weeks: "{n} hafta içinde"
+ months: "{n} ay içinde"
+ years: "{n} yıl içinde"
+_time:
+ second: "Saniye(ler)"
+ minute: "Dakika(lar)"
+ hour: "Saat(ler)"
+ day: "Gün(ler)"
+ month: "Ay"
_2fa:
- renewTOTPCancel: "Hayır, teşekkürler"
+ alreadyRegistered: "2fa kimlik doğrulama cihazını zaten kaydettin."
+ registerTOTP: "Kimlik doğrulama uygulamasını kaydet"
+ step1: "Öncelikle, cihazınıza bir kimlik doğrulama uygulaması (örneğin {a} veya {b}) yükleyin."
+ step2: "Ardından, bu ekranda görüntülenen QR kodunu tarayın."
+ step2Uri: "Masaüstü programı kullanıyorsanız aşağıdaki URI'yi girin"
+ step3Title: "DoÄŸrulama kodunu girin"
+ step3: "Uygulamanız tarafından sağlanan kimlik doğrulama kodunu (token) girerek kurulumu tamamlayın."
+ setupCompleted: "Kurulum tamamlandı"
+ step4: "Bundan sonra, gelecekteki tüm oturum açma girişimlerinde bu tür bir oturum açma jetonu istenecek."
+ securityKeyNotSupported: "Tarayıcınız güvenlik anahtarlarını desteklemiyor."
+ registerTOTPBeforeKey: "Güvenlik veya geçiş anahtarını kaydetmek için bir kimlik doğrulama uygulaması kurun."
+ securityKeyInfo: "Parmak izi veya PIN kimlik doğrulamasının yanı sıra, hesabını daha da güvenli hale getirmek için FIDO2'yi destekleyen donanım güvenlik anahtarları aracılığıyla kimlik doğrulama da ayarlayabilirsin."
+ registerSecurityKey: "Güvenlik veya geçiş anahtarını kaydedin"
+ securityKeyName: "Bir anahtar adı girin"
+ tapSecurityKey: "Güvenlik veya geçiş anahtarını kaydetmek için lütfen tarayıcınızı takip edin."
+ removeKey: "Güvenlik anahtarını kaldır"
+ removeKeyConfirm: "{name} anahtarını cidden silmek istiyor musun?"
+ whyTOTPOnlyRenew: "Güvenlik anahtarı kayıtlı olduğu sürece kimlik doğrulama uygulaması kaldırılamaz."
+ renewTOTP: "Kimlik doğrulama uygulamasını yeniden yapılandırın"
+ renewTOTPConfirm: "Bu, önceki uygulamanızdaki doğrulama kodlarının çalışmamasına neden olacaktır."
+ renewTOTPOk: "Yeniden yapılandır"
+ renewTOTPCancel: "İptal"
+ checkBackupCodesBeforeCloseThisWizard: "Bu pencereyi kapatmadan önce, lütfen aşağıdaki yedek kodları not edin."
+ backupCodes: "Yedek kodlar"
+ backupCodesDescription: "İki faktörlü kimlik doğrulama uygulamasını kullanamaz hale gelmen durumunda, bu kodları kullanarak hesabınıza erişebilirsin. Her kod yalnızca bir kez kullanılabilir. Lütfen bu kodları güvenli bir yerde sakla."
+ backupCodeUsedWarning: "Yedek kod kullanıldı. Artık kullanamıyorsanız, lütfen iki faktörlü kimlik doğrulamayı mümkün olan en kısa sürede yeniden yapılandırın."
+ backupCodesExhaustedWarning: "Tüm yedek kodlar kullanıldı. İki faktörlü kimlik doğrulama uygulamana erişimini kaybedersen, bu hesaba erişemezsin. Lütfen iki faktörlü kimlik doğrulamayı yeniden yapılandır."
+ moreDetailedGuideHere: "İşte ayrıntılı kılavuz"
_permissions:
- "read:blocks": "Engellenen hesapları gör"
- "write:blocks": "Engellenen hesap listesini düzenle"
+ "read:account": "Hesap bilgilerini gör"
+ "write:account": "Hesap bilgilerini düzenle"
+ "read:blocks": "Engellenen kullanıcıların listesini görüntüle"
+ "write:blocks": "Engellenen kullanıcılar listeni düzenle"
+ "read:drive": "Drive dosyalarına ve klasörlerine eriş"
+ "write:drive": "Drive dosyalarını ve klasörlerini düzenle veya sil"
+ "read:favorites": "Favoriler listeni görüntüle"
+ "write:favorites": "Favoriler listeni düzenle"
+ "read:following": "Takip ettiğin kişilerle ilgili bilgileri görüntüle"
+ "write:following": "Diğer hesapları takip et veya takipten çıkar"
+ "read:messaging": "Sohbetlerini görüntüle"
+ "write:messaging": "Sohbet mesajlarını oluşturun veya silin"
+ "read:mutes": "Sessize alınan kullanıcıların listesini görüntüle"
+ "write:mutes": "Sessize alınan kullanıcıların listesini düzenle"
+ "write:notes": "Notlar oluÅŸturun veya silin"
+ "read:notifications": "Bildirimlerini görüntüle"
+ "write:notifications": "Bildirimlerini yönet"
+ "read:reactions": "Tepkilerini görüntüle"
+ "write:reactions": "Tepkilerini düzenle"
+ "write:votes": "Ankete oy verin"
+ "read:pages": "Sayfalarını görüntüle"
+ "write:pages": "Sayfalarını düzenle veya sil"
+ "read:page-likes": "Beğenilen sayfaların listesini görüntüle"
+ "write:page-likes": "Beğenilen sayfaların listesini düzenle"
+ "read:user-groups": "Kullanıcı gruplarını görüntüle"
+ "write:user-groups": "Kullanıcı gruplarını düzenle veya sil"
+ "read:channels": "Kanallarını görüntüle"
+ "write:channels": "Kanallarını düzenle"
+ "read:gallery": "Galeriyi görüntüle"
+ "write:gallery": "Galeri düzenle"
+ "read:gallery-likes": "Beğendiğin galeri gönderilerinin listesini görüntüle"
+ "write:gallery-likes": "Beğendiğin galeri gönderilerinin listesini düzenle"
+ "read:flash": "Oynat"
+ "write:flash": "Oyunları Düzenle"
+ "read:flash-likes": "Beğenilen Oyunların listesini görüntüle"
+ "write:flash-likes": "Beğenilen Oyunlar listesini düzenle"
+ "read:admin:abuse-user-reports": "Kullanıcı raporlarını görüntüle"
+ "write:admin:delete-account": "Kullanıcı hesabını sil"
+ "write:admin:delete-all-files-of-a-user": "Bir kullanıcının tüm dosyalarını sil"
+ "read:admin:index-stats": "Veritabanı dizin istatistiklerini görüntüle"
+ "read:admin:table-stats": "Veritabanı tablosu istatistiklerini görüntüle"
+ "read:admin:user-ips": "Kullanıcı IP adreslerini görüntüle"
+ "read:admin:meta": "Sunucu meta verilerini görüntüle"
+ "write:admin:reset-password": "Kullanıcı şifresini sıfırla"
+ "write:admin:resolve-abuse-user-report": "Kullanıcı raporunu çözme"
+ "write:admin:send-email": "E-Posta gönder"
+ "read:admin:server-info": "Sunucu bilgilerini görüntüle"
+ "read:admin:show-moderation-log": "Moderasyon günlüğünü görüntüle"
+ "read:admin:show-user": "Özel kullanıcı bilgilerini görüntüle"
+ "write:admin:suspend-user": "Kullanıcıyı askıya al"
+ "write:admin:unset-user-avatar": "Kullanıcı avatarını kaldır"
+ "write:admin:unset-user-banner": "Kullanıcı afişini kaldır"
+ "write:admin:unsuspend-user": "Kullanıcı askıya alma işlemini kaldır"
+ "write:admin:meta": "Sunucu meta verilerini yönetme"
+ "write:admin:user-note": "Moderasyon notunu yönet"
+ "write:admin:roles": "Rolleri yönet"
+ "read:admin:roles": "Rolü görüntüle"
+ "write:admin:relays": "Röleleri yönetme"
+ "read:admin:relays": "Röleleri görüntüle"
+ "write:admin:invite-codes": "Davet kodlarını yönet"
+ "read:admin:invite-codes": "Davet kodlarını görüntüle"
+ "write:admin:announcements": "Duyuruları yönet"
+ "read:admin:announcements": "Duyuruları görüntüle"
+ "write:admin:avatar-decorations": "Avatar süslerini yönetebilir"
+ "read:admin:avatar-decorations": "Avatar süslerini görüntüle"
+ "write:admin:federation": "Federasyon verilerini yönetme"
+ "write:admin:account": "Kullanıcı hesabını yönet"
+ "read:admin:account": "Kullanıcı hesabını görüntüle"
+ "write:admin:emoji": "Emoji'leri yönet"
+ "read:admin:emoji": "Emojiyi görüntüle"
+ "write:admin:queue": "İş kuyruğunu yönet"
+ "read:admin:queue": "İş kuyruğu bilgilerini görüntüle"
+ "write:admin:promo": "Promosyon notlarını yönet"
+ "write:admin:drive": "Kullanıcı Drive'ını yönet"
+ "read:admin:drive": "Kullanıcı Drive bilgilerini görüntüle"
+ "read:admin:stream": "Yönetici için WebSocket API'sını kullanın"
+ "write:admin:ad": "Reklamları yönet"
+ "read:admin:ad": "Reklamları görüntüle"
+ "write:invite-codes": "Davet kodları oluşturun"
+ "read:invite-codes": "Davet kodlarını alın"
+ "write:clip-favorite": "Favorilere eklenen klipleri yönet"
+ "read:clip-favorite": "Favorilere eklenen klipleri görüntüle"
+ "read:federation": "Federasyon verilerini alın"
+ "write:report-abuse": "İhlali bildir"
+ "write:chat": "Sohbet mesajlarını oluşturun veya silin"
+ "read:chat": "Sohbeti Gözat"
+_auth:
+ shareAccessTitle: "Uygulama izinlerinin verilmesi"
+ shareAccess: "“{name}â€nin bu hesaba eriÅŸmesine izin vermek ister misiniz?"
+ shareAccessAsk: "Bu uygulamanın hesabınıza erişmesine izin vermek istediğinden emin misin?"
+ permission: "{name} aşağıdaki izinleri talep etmektedir."
+ permissionAsk: "Bu uygulama aşağıdaki izinleri talep etmektedir"
+ pleaseGoBack: "Lütfen uygulamaya geri dönün."
+ callback: "Uygulamaya geri dönmek"
+ accepted: "EriÅŸim izni verildi"
+ denied: "EriÅŸim reddedildi"
+ scopeUser: "Aşağıdaki kullanıcı olarak çalıştırın"
+ pleaseLogin: "Uygulamaları yetkilendirmek için lütfen giriş yapın."
+ byClickingYouWillBeRedirectedToThisUrl: "Erişim izni verildiğinde, otomatik olarak aşağıdaki URL'ye yönlendirileceksin."
+_antennaSources:
+ all: "Tüm notlar"
+ homeTimeline: "Takip edilen kullanıcıların notları"
+ users: "Belirli kullanıcılardan gelen notlar"
+ userList: "Belirtilen kullanıcı listesinden notlar"
+ userBlacklist: "Bir veya daha fazla belirli kullanıcıya ait olanlar hariç tüm notlar"
+_weekday:
+ sunday: "Pazar"
+ monday: "Pazartesi"
+ tuesday: "Salı"
+ wednesday: "Çarşamba"
+ thursday: "PerÅŸembe"
+ friday: "Cuma"
+ saturday: "Cumartesi"
_widgets:
profile: "Profil"
instanceInfo: "Sunucu Bilgisi"
- notifications: "Bildirim"
- timeline: "Zaman çizelgesi"
+ memo: "Yapışkan notlar"
+ notifications: "Bildirimler"
+ timeline: "Pano"
calendar: "Takvim"
+ trends: "Trend olan"
clock: "Saat"
+ rss: "RSS okuyucu"
+ rssTicker: "RSS-Ticker"
activity: "Etkinlik"
+ photos: "FotoÄŸraflar"
+ digitalClock: "Dijital saat"
+ unixClock: "UNIX saati"
federation: "Federasyon"
- jobQueue: "İşlem sırası"
+ instanceCloud: "Bulut sunucu"
+ postForm: "Gönderim formu"
+ slideshow: "Slayt gösterisi"
+ button: "Düğme"
+ onlineUsers: "Çevrimiçi kullanıcılar"
+ jobQueue: "İş Kuyruğu"
+ serverMetric: "Sunucu ölçümleri"
+ aiscript: "AiScript konsolu"
+ aiscriptApp: "AiScript Uygulaması"
+ aichan: "Ai"
+ userList: "Kullanıcı listesi"
_userList:
- chooseList: "Bir liste seç"
+ chooseList: "Bir liste seçin"
+ clicker: "Tıklayıcı"
+ birthdayFollowings: "Bugünün Doğum Günleri"
+ chat: "Sohbet"
_cw:
- show: "Devamını yükle"
+ hide: "Gizle"
+ show: "İçeriği göster"
+ chars: "{count} karakter"
+ files: "{count} dosya(lar)"
_poll:
- vote: "Oy kullan"
+ noOnlyOneChoice: "En az iki seçenek gereklidir."
+ choiceN: "Seçim {n}"
+ noMore: "Daha fazla seçenek ekleyemezsin."
+ canMultipleVote: "Birden fazla seçenek seçilmesine izin ver"
+ expiration: "Anketi sonlandır"
+ infinite: "Asla"
+ at: "Åžurada bitir..."
+ after: "Sonrasında bitir..."
+ deadlineDate: "BitiÅŸ tarihi"
+ deadlineTime: "Zaman"
+ duration: "Süre"
+ votesCount: "{n} oy"
+ totalVotes: "Toplam {n} oy"
+ vote: "Oy ver"
+ showResult: "Sonuçları görüntüle"
+ voted: "Oylandı"
+ closed: "Sona erdi"
+ remainingDays: "{d} gün {h} saat kaldı"
+ remainingHours: "{h} saat {m} dakika kaldı"
+ remainingMinutes: "{m} dakika {s} saniye kaldı"
+ remainingSeconds: "{s} saniye kaldı"
_visibility:
- publicDescription: "Herkese açık"
- home: "Ana sayfa"
- followers: "takipçi"
+ public: "Halka açık"
+ publicDescription: "Notunuz tüm kullanıcılar tarafından görülebilir olacaktır."
+ home: "Pano"
+ homeDescription: "Yalnızca ana zaman çizelgesine gönder"
+ followers: "Takipçiler"
+ followersDescription: "Sadece takipçilerine görünür hale getir"
+ specified: "DoÄŸrudan"
+ specifiedDescription: "Yalnızca belirli kullanıcılar için görünür hale getir"
+ disableFederation: "Federasyon olmadan"
+ disableFederationDescription: "DiÄŸer sunuculara aktarma"
+_postForm:
+ quitInspiteOfThereAreUnuploadedFilesConfirm: "Yüklenmemiş dosyalar var, bunları silip formu kapatmak ister misin?"
+ uploaderTip: "Dosya henüz yüklenmemiş. Dosya menüsünden dosyayı yeniden adlandırabilir, görüntüleri kırpabilir, filigran ekleyebilir ve dosyayı sıkıştırabilir veya sıkıştırmayı kaldırabilirsin. Notu yayınladığında dosyalar otomatik olarak yüklenir."
+ replyPlaceholder: "Bu notu yanıtla..."
+ quotePlaceholder: "Bu notu alıntı yap..."
+ channelPlaceholder: "Bir kanala gönder..."
+ _placeholders:
+ a: "Ne yapıyorsun?"
+ b: "Çevrende neler oluyor?"
+ c: "Aklında ne var?"
+ d: "Ne söylemek istiyorsun?"
+ e: "Yazmaya başlayın..."
+ f: "Yazmanızı bekliyoruz..."
_profile:
- username: "Kullanıcı Adı"
+ name: "Ad"
+ username: "Kullanıcı adı"
+ description: "Biyografi"
+ youCanIncludeHashtags: "Biyografinize hashtag'ler de ekleyebilirsiniz."
+ metadata: "Ek Bilgiler"
+ metadataEdit: "Ek bilgileri düzenle"
+ metadataDescription: "Bunları kullanarak profilinde ek bilgi alanları görüntüleyebilirsin."
+ metadataLabel: "Etiket"
+ metadataContent: "İçerik"
+ changeAvatar: "Avatar deÄŸiÅŸtir"
+ changeBanner: "Banner deÄŸiÅŸtir"
+ verifiedLinkDescription: "Buraya profiline bağlantı içeren bir URL girerek, alanın yanında bir sahiplik doğrulama simgesi görüntülenebilir."
+ avatarDecorationMax: "En fazla {max} süs ekleyebilirsin."
+ followedMessage: "Takip edildiğinizde gönderilen mesaj"
+ followedMessageDescription: "Abonelerin seni takip ettiklerinde görüntülenmesini istediğin kısa bir mesaj ayarlayabilirsin."
+ followedMessageDescriptionForLockedAccount: "Takip isteklerinin onay gerektirmesini ayarladıysan, bir takip isteğini kabul ettiğinde bu mesaj görüntülenir."
_exportOrImport:
- followingList: "takipçi"
- muteList: "Gizle"
- blockingList: "engelle"
- userLists: "Listeler"
+ allNotes: "Tüm notlar"
+ favoritedNotes: "Favori notlar"
+ clips: "Klip"
+ followingList: "Takip edilen kullanıcılar"
+ muteList: "Sessize alınan kullanıcılar"
+ blockingList: "Engellenen kullanıcılar"
+ userLists: "Kullanıcı listeleri"
+ excludeMutingUsers: "Sessize alınan kullanıcıları hariç tut"
+ excludeInactiveUsers: "Etkin olmayan kullanıcıları hariç tut"
+ withReplies: "İçe aktarılan kullanıcıların yanıtlarını zaman çizelgesine dahil edin"
_charts:
federation: "Federasyon"
+ apRequest: "Talepler"
+ usersIncDec: "Kullanıcı sayısındaki fark"
+ usersTotal: "Toplam kullanıcı sayısı"
+ activeUsers: "Aktif kullanıcılar"
+ notesIncDec: "Not sayısındaki fark"
+ localNotesIncDec: "Yerel notaların sayısındaki fark"
+ remoteNotesIncDec: "Uzak notların sayısındaki fark"
+ notesTotal: "Toplam not sayısı"
+ filesIncDec: "Dosya sayısındaki fark"
+ filesTotal: "Toplam dosya sayısı"
+ storageUsageIncDec: "Depolama kullanımı farkı"
+ storageUsageTotal: "Total storage usage"
+_instanceCharts:
+ requests: "Talepler"
+ users: "Kullanıcı sayısındaki fark"
+ usersTotal: "Toplam kullanıcı sayısı"
+ notes: "Not sayısındaki fark"
+ notesTotal: "Toplam not sayısı"
+ ff: "Takip / Takipçi sayısı farkı"
+ ffTotal: "Takip / Takipçi toplam sayısı"
+ cacheSize: "Önbellek boyutundaki fark"
+ cacheSizeTotal: "Önbelleğin toplam boyutu"
+ files: "Dosya sayısındaki fark"
+ filesTotal: "Toplam dosya sayısı"
_timelines:
- home: "Ana sayfa"
- global: "Küresel"
+ home: "Pano"
+ local: "Yerel"
+ social: "Sosyal"
+ global: "Global"
+_play:
+ new: "Oyun OluÅŸtur"
+ edit: "Düzenle Oynat"
+ created: "Oyun oluÅŸturuldu"
+ updated: "Düzenlenmiş oynat"
+ deleted: "Oyun silindi"
+ pageSetting: "Oyun ayarları"
+ editThisPage: "Bu Oyunu Düzenle"
+ viewSource: "Kaynak görüntüle"
+ my: "Oyunlarım"
+ liked: "BeÄŸenilen Oyunlar"
+ featured: "Popüler"
+ title: "Başlık"
+ script: "Senaryo"
+ summary: "Açıklama"
+ visibilityDescription: "Özel olarak ayarlamak, profilinde görünmeyeceği anlamına gelir, ancak URL'ye sahip olan herkes yine de erişebilir."
_pages:
+ newPage: "Yeni bir Sayfa oluÅŸturun"
+ editPage: "Bu sayfayı düzenle"
+ readPage: "Bu Sayfanın Kaynağını Görüntüleme"
+ pageSetting: "Sayfa ayarları"
+ nameAlreadyExists: "Belirtilen Sayfa URL'si zaten mevcut."
+ invalidNameTitle: "Belirtilen Sayfa URL geçersiz"
+ invalidNameText: "Sayfa başlığının boş olmadığından emin olun."
+ editThisPage: "Bu sayfayı düzenle"
+ viewSource: "Kaynak görüntüle"
+ viewPage: "Sayfalarını görüntüle"
+ like: "BeÄŸen"
+ unlike: "Benzerlerini kaldır"
+ my: "Benzerlerini kaldır"
+ liked: "BeÄŸenilen Sayfalar"
+ featured: "Popüler"
+ inspector: "Müfettiş"
+ contents: "İçindekiler"
+ content: "Sayfa bloÄŸu"
+ variables: "DeÄŸiÅŸkenler"
+ title: "Başlık"
+ url: "Sayfa URL'si"
+ summary: "Sayfa özeti"
+ alignCenter: "Merkez öğeleri"
+ hideTitleWhenPinned: "Profiline sabitlendiğinde sayfa başlığını gizle"
+ font: "Yazı tipi"
+ fontSerif: "Serif"
+ fontSansSerif: "Sans Serif"
+ eyeCatchingImageSet: "Küçük resmi ayarla"
+ eyeCatchingImageRemove: "Küçük resmi sil"
+ chooseBlock: "Blok ekle"
+ enterSectionTitle: "Bölüm başlığını girin"
+ selectType: "Bir tür seçin"
+ contentBlocks: "İçerik"
+ inputBlocks: "GiriÅŸ"
+ specialBlocks: "Özel"
blocks:
+ text: "Metin"
+ textarea: "Metin alanı"
+ section: "Bölüm"
image: "Görseller"
+ button: "Düğme"
+ dynamic: "Dinamik Bloklar"
+ dynamicDescription: "Bu blok kaldırılmıştır. Bundan sonra lütfen {play} kullanın."
+ note: "Gömülü not"
+ _note:
+ id: "Not KimliÄŸi"
+ idDescription: "Alternatif olarak notun URL buraya yapıştırabilirsin."
+ detailed: "Ayrıntılı görünüm"
+_relayStatus:
+ requesting: "Beklemede"
+ accepted: "Accepted"
+ rejected: "Reddedildi"
_notification:
+ fileUploaded: "Dosya başarıyla yüklendi"
+ youGotMention: "{name} sizden bahsetti."
+ youGotReply: "{name} size yanıt verdi"
+ youGotQuote: "{name} sizden alıntı yaptı"
+ youRenoted: "{name}'den Renote"
youWereFollowed: "seni takip etti"
+ youReceivedFollowRequest: "Bir takip isteği aldınız."
+ yourFollowRequestAccepted: "Takip isteÄŸin kabul edildi."
+ pollEnded: "Anket sonuçları açıklandı."
+ newNote: "Yeni not"
unreadAntennaNote: "{name} anteni"
+ roleAssigned: "Verilen rol"
+ chatRoomInvitationReceived: "Sohbet odasına davet edildin."
+ emptyPushNotificationMessage: "Push bildirimleri güncellendi"
+ achievementEarned: "Achievement unlocked"
+ testNotification: "Test bildirimi"
+ checkNotificationBehavior: "Bildirim görünümünü kontrol edin"
+ sendTestNotification: "Test bildirimi gönder"
+ notificationWillBeDisplayedLikeThis: "Bildirimler şöyle görünür"
+ reactedBySomeUsers: "{n} kullanıcı tepki gösterdi"
+ likedBySomeUsers: "{n} kullanıcı notunuzu beğendi."
+ renotedBySomeUsers: "{n} kullanıcıdan gelen hatırlatma"
+ followedBySomeUsers: "{n} kullanıcı tarafından takip ediliyor"
+ flushNotification: "Bildirimleri temizle"
+ exportOfXCompleted: "{x} ihracatı tamamlandı."
+ login: "Biri oturum açtı"
+ createToken: "Bir eriÅŸim jetonu oluÅŸturuldu."
+ createTokenDescription: "Eğer bilmiyorsanız, “{text}†aracılığıyla erişim jetonunu silin."
_types:
- follow: "takipçi"
- mention: "Bahset"
- renote: "vazgeçme"
- quote: "alıntı"
- reaction: "Tepkiler"
- receiveFollowRequest: "Takip isteği alındı"
- followRequestAccepted: "Takip isteÄŸi kabul edildi"
- login: "GiriÅŸ Yap "
+ all: "Tümü"
+ note: "Yeni notlar"
+ follow: "Yeni takipçiler"
+ mention: "Bahsetmeler"
+ reply: "Yanıtlar"
+ renote: "Renote"
+ quote: "Alıntılar"
+ reaction: "Tepki"
+ pollEnded: "Anketler sona eriyor"
+ receiveFollowRequest: "Takip istekleri alındı"
+ followRequestAccepted: "Kabul edilen takip istekleri"
+ roleAssigned: "Verilen rol"
+ chatRoomInvitationReceived: "Sohbet odasına davet edildi"
+ achievementEarned: "Başarı kilidi açıldı"
+ exportCompleted: "İhracat işlemi tamamlandı."
+ login: "Oturum Aç"
+ createToken: "EriÅŸim jetonu oluÅŸtur"
+ test: "Bildirim testi"
+ app: "Bağlı uygulamalardan gelen bildirimler"
_actions:
- reply: "yanıt"
- renote: "vazgeçme"
+ followBack: "seni takip ettim"
+ reply: "Yanıtla"
+ renote: "Renote"
_deck:
- configureColumn: "Sütun seçenekleri"
+ alwaysShowMainColumn: "Ana sütunu her zaman göster"
+ columnAlign: "Sütunları hizala"
+ columnGap: "Sütunlar arasındaki kenar boşluğu"
+ deckMenuPosition: "Sütunlar arasındaki kenar boşluğu"
+ navbarPosition: "Gezinti çubuğu konumu"
+ addColumn: "Sütun ekle"
+ newNoteNotificationSettings: "Notification setting for new notes"
+ configureColumn: "Sütun ayarları"
+ swapLeft: "Sol sütunla değiştir"
+ swapRight: "Sağ sütunla değiştir"
+ swapUp: "Yukarıdaki sütunla değiştir"
+ swapDown: "Aşağıdaki sütunla değiştir"
+ stackLeft: "Sol sütunda yığın"
+ popRight: "Sağdaki pop sütunu"
+ profile: "Profil"
+ newProfile: "Yeni profil"
+ deleteProfile: "Profili sil"
+ introduction: "Sütunları serbestçe düzenleyerek size en uygun arayüzü oluşturun!"
+ introduction2: "Ekranın sağındaki + işaretine tıklayarak istediğin zaman yeni sütunlar ekleyebilirsin."
+ widgetsIntroduction: "Lütfen sütun menüsünden “Widget'ları düzenle†seçeneğini seç ve bir widget ekle."
+ useSimpleUiForNonRootPages: "Gezinilen sayfalar için basit kullanıcı arayüzü kullanın"
+ usedAsMinWidthWhenFlexible: "“Otomatik genişlik ayarı†seçeneği etkinleştirildiğinde, bunun için minimum genişlik kullanılacak."
+ flexible: "Otomatik genişlik ayarı"
+ enableSyncBetweenDevicesForProfiles: "Cihazlar arasında profil bilgilerinin senkronizasyonunu etkinleştir"
_columns:
- notifications: "Bildirim"
- tl: "Zaman çizelgesi"
- list: "Listeler"
+ main: "Ana"
+ widgets: "Widget'lar"
+ notifications: "Bildirimler"
+ tl: "Pano"
+ antenna: "Antenler"
+ list: "Liste"
+ channel: "Kanal"
mentions: "Bahsetmeler"
+ direct: "DoÄŸrudan notlar"
+ roleTimeline: "Rol Pano"
+ chat: "Sohbet"
+_dialog:
+ charactersExceeded: "Maksimum karakter sınırını aştınız! Şu anda {current} karakterde {max} karakterlik sınırın {current} karakterinde bulunuyorsunuz."
+ charactersBelow: "You're below the minimum character limit! Currently at {current} of {min}."
+_disabledTimeline:
+ title: "Pano devre dışı bırakıldı"
+ description: "Mevcut rollerinle bu Pano kullanılamaz."
+_drivecleaner:
+ orderBySizeDesc: "Azalan Dosya Boyutları"
+ orderByCreatedAtAsc: "Yükselen Tarihler"
+_webhookSettings:
+ createWebhook: "Webhook oluÅŸtur"
+ modifyWebhook: "Webhook'u deÄŸiÅŸtir"
+ name: "Webhook'u deÄŸiÅŸtir"
+ secret: "Gizli"
+ trigger: "Tetikleyici"
+ active: "Etkin"
+ _events:
+ follow: "Bir kullanıcıyı takip ederken"
+ followed: "Takip edildiÄŸinde"
+ note: "Not gönderirken"
+ reply: "Yanıt alındığında"
+ renote: "Yeniden not edildiÄŸinde"
+ reaction: "Tepki aldığınızda"
+ mention: "BahsedildiÄŸinde"
+ _systemEvents:
+ abuseReport: "Yeni bir rapor alındığında"
+ abuseReportResolved: "Çözüldüğünde rapor"
+ userCreated: "Kullanıcı oluşturulduğunda"
+ inactiveModeratorsWarning: "Moderatörler bir süredir aktif olmadıklarında"
+ inactiveModeratorsInvitationOnlyChanged: "Bir moderatör bir süre aktif olmadığında ve sunucu davetle erişilebilir hale getirildiğinde"
+ deleteConfirm: "Webhook'u silmek istediÄŸinden emin misin?"
+ testRemarks: "Anahtarın sağındaki düğmeyi tıklayarak sahte verilerle bir test Webhook gönderin."
+_abuseReport:
+ _notificationRecipient:
+ createRecipient: "Raporlar için alıcı ekle"
+ modifyRecipient: "Raporlar için alıcıyı düzenle"
+ recipientType: "Bildirim türü"
+ _recipientType:
+ mail: "E-Posta"
+ webhook: "Webhook"
+ _captions:
+ mail: "Raporları aldığınızda, E-Postayı moderatörlerin e-posta adreslerine gönderin."
+ webhook: "Raporları aldığınızda veya çözdüğünüzde Sistem Webhook'una bir bildirim gönderin."
+ keywords: "Anahtar kelimeler"
+ notifiedUser: "Bildirilecek kullanıcılar"
+ notifiedWebhook: "Kullanılacak webhook"
+ deleteConfirm: "Bildirim alıcısını silmek istediğinden emin misin?"
_moderationLogTypes:
- suspend: "askıya al"
- resetPassword: "Şifre sıfırlama"
+ createRole: "Rol oluÅŸturuldu"
+ deleteRole: "Rol silindi"
+ updateRole: "Rol güncellendi"
+ assignRole: "Rol atandı"
+ unassignRole: "Görevinden alınmış"
+ suspend: "Askıya alınmış"
+ unsuspend: "Askıya alınmamış"
+ addCustomEmoji: "Özel emoji eklendi"
+ updateCustomEmoji: "Özel emoji güncellendi"
+ deleteCustomEmoji: "Özel emoji silindi"
+ updateServerSettings: "Sunucu ayarları güncellendi"
+ updateUserNote: "Moderasyon notu güncellendi"
+ deleteDriveFile: "Dosya silindi"
+ deleteNote: "Not silindi"
+ createGlobalAnnouncement: "Global duyuru oluÅŸturuldu"
+ createUserAnnouncement: "Kullanıcı duyurusu oluşturuldu"
+ updateGlobalAnnouncement: "Global duyuru güncellendi"
+ updateUserAnnouncement: "Kullanıcı duyurusu güncellendi"
+ deleteGlobalAnnouncement: "Global duyuru silindi"
+ deleteUserAnnouncement: "Kullanıcı duyurusu silindi"
+ resetPassword: "Şifreyi sıfırla"
+ suspendRemoteInstance: "Uzak sunucu askıya alındı"
+ unsuspendRemoteInstance: "Uzak sunucu askıya alınmadı"
+ updateRemoteInstanceNote: "Uzak sunucular için güncellenmiş moderasyon notu"
+ markSensitiveDriveFile: "Hassas olarak iÅŸaretlenmiÅŸ dosya"
+ unmarkSensitiveDriveFile: "Dosya hassas olarak iÅŸaretlenmemiÅŸ"
+ resolveAbuseReport: "Rapor çözüldü"
+ forwardAbuseReport: "Rapor iletildi"
+ updateAbuseReportNote: "Güncellenen raporun moderasyon notu"
+ createInvitation: "Davet oluÅŸturuldu"
+ createAd: "Reklam oluÅŸturuldu"
+ deleteAd: "Reklam silindi"
+ updateAd: "Reklam güncellendi"
+ createAvatarDecoration: "Avatar dekorasyonu oluÅŸturuldu"
+ updateAvatarDecoration: "Avatar dekorasyonu güncellendi"
+ deleteAvatarDecoration: "Avatar süsü silindi"
+ unsetUserAvatar: "Kullanıcı avatarı ayarlanmamış"
+ unsetUserBanner: "Kullanıcı başlığı ayarlanmamış"
+ createSystemWebhook: "Sistem Webhook oluÅŸturuldu"
+ updateSystemWebhook: "Sistem Webhook güncellendi"
+ deleteSystemWebhook: "Sistem Webhook silindi"
+ createAbuseReportNotificationRecipient: "Oluşturulan raporlar için alıcı"
+ updateAbuseReportNotificationRecipient: "Raporlar için alıcı güncellendi"
+ deleteAbuseReportNotificationRecipient: "Silinen raporlar için alıcı"
+ deleteAccount: "Hesap silindi"
+ deletePage: "Sayfa silindi"
+ deleteFlash: "Oyun silindi"
+ deleteGalleryPost: "Galeri gönderisi silindi"
+ deleteChatRoom: "Deleted Chat Room"
+ updateProxyAccountDescription: "Proxy hesabının açıklamasını güncelle"
+_fileViewer:
+ title: "Dosya ayrıntıları"
+ type: "Dosya türü"
+ size: "Dosya boyutu"
+ url: "URL"
+ uploadedAt: "Yüklendiği tarih"
+ attachedNotes: "Ekli notlar"
+ usage: "Kullanılmış"
+ thisPageCanBeSeenFromTheAuthor: "Bu sayfa, bu dosyayı yükleyen kullanıcı tarafından görülebilir."
+_externalResourceInstaller:
+ title: "Harici siteden yükle"
+ checkVendorBeforeInstall: "Yüklemeden önce bu kaynağın dağıtımcısının güvenilir olduğundan emin olun."
+ _plugin:
+ title: "Bu eklentiyi yüklemek ister misin?"
+ _theme:
+ title: "Bu temayı yüklemek ister misin?"
+ _meta:
+ base: "Temel renk şeması"
+ _vendorInfo:
+ title: "Dağıtıcı bilgileri"
+ endpoint: "Referans uç nokta"
+ hashVerify: "Hash doÄŸrulama"
+ _errors:
+ _invalidParams:
+ title: "Geçersiz parametreler"
+ description: "Harici bir siteden veri yüklemek için yeterli bilgi yok. Lütfen girdiğin URL'yi kontrol et."
+ _resourceTypeNotSupported:
+ title: "Bu harici kaynak desteklenmemektedir."
+ description: "Bu harici kaynağın türü desteklenmemektedir. Lütfen site yöneticisiyle iletişime geç."
+ _failedToFetch:
+ title: "Veriler alınamadı"
+ fetchErrorDescription: "Harici siteyle iletişim sırasında bir hata oluştu. Tekrar denemen sorunu çözmezse, lütfen site yöneticisine başvur."
+ parseErrorDescription: "Harici siteden yüklenen veriler işlenirken bir hata oluştu. Lütfen site yöneticisiyle iletişime geçin."
+ _hashUnmatched:
+ title: "Veri doğrulama başarısız oldu"
+ description: "Alınan verilerin bütünlüğünü doğrularken bir hata oluştu. Güvenlik önlemi olarak, kurulum devam edemez. Lütfen site yöneticisiyle iletişime geçin."
+ _pluginParseFailed:
+ title: "AiScript Hatası"
+ description: "İstenen veriler başarıyla alındı, ancak AiScript ayrıştırma sırasında bir hata oluştu. Lütfen eklenti yazarına başvurun. Hata ayrıntıları Javascript konsolunda görüntülenebilir."
+ _pluginInstallFailed:
+ title: "Eklenti kurulumu başarısız oldu"
+ description: "Eklenti yükleme sırasında bir sorun oluştu. Lütfen tekrar dene. Hata ayrıntıları Javascript konsolunda görüntülenebilir."
+ _themeParseFailed:
+ title: "Tema ayrıştırma başarısız oldu"
+ description: "İstenen veriler başarıyla alındı, ancak tema ayrıştırma sırasında bir hata oluştu. Lütfen tema yazarıyla iletişime geçin. Hata ayrıntıları Javascript konsolunda görüntülenebilir."
+ _themeInstallFailed:
+ title: "Tema yüklenemedi"
+ description: "Tema yükleme sırasında bir sorun oluştu. Lütfen tekrar dene. Hata ayrıntıları Javascript konsolunda görüntülenebilir."
+_dataSaver:
+ _media:
+ title: "Medya yükleniyor"
+ description: "Görüntülerin/videoların otomatik olarak yüklenmesini engeller. Gizli görüntüler/videolar dokunulduğunda yüklenir."
+ _avatar:
+ title: "Avatar resmi"
+ description: "Avatar görüntüsünün animasyonunu durdurun. Animasyonlu görüntüler normal görüntülere göre dosya boyutu açısından daha büyük olabilir ve bu da veri trafiğinde daha fazla azalmaya yol açabilir."
+ _urlPreviewThumbnail:
+ title: "URL önizleme küçük resimlerini gizle"
+ description: "URL önizleme küçük resimleri artık yüklenmeyecek."
+ _disableUrlPreview:
+ title: "URL önizlemesini devre dışı bırak"
+ description: "URL önizleme işlevini devre dışı bırakır. Küçük resimler aksine, bu işlev bağlantılı bilgilerin kendisinin yüklenmesini azaltır."
+ _code:
+ title: "Kod vurgulama"
+ description: "MFM vb. programlarda kod vurgulama notasyonları kullanılıyorsa, bunlar dokunulana kadar yüklenmez. Sözdizimi vurgulama, her programlama dili için vurgu tanım dosyalarının indirilmesini gerektirir. Bu nedenle, bu dosyaların otomatik olarak yüklenmesinin devre dışı bırakılması, iletişim verisi miktarını azaltması beklenir."
+_hemisphere:
+ N: "Kuzey Yarımküre"
+ S: "Güney Yarımküre"
+ caption: "Bazı istemci ayarlarında mevsimi belirlemek için kullanılır."
+_reversi:
+ reversi: "Tersine çevirme"
+ gameSettings: "Oyun ayarları"
+ chooseBoard: "Bir tahta seçin"
+ blackOrWhite: "Siyah/Beyaz"
+ blackIs: "{name} siyah oynuyor."
+ rules: "Kurallar"
+ thisGameIsStartedSoon: "Oyun kısa süre içinde başlayacak."
+ waitingForOther: "Rakibin sırasını bekle"
+ waitingForMe: "Sıranı bekliyorsun"
+ waitingBoth: "Hazır olun"
+ ready: "Hazır"
+ cancelReady: "Hazır değil"
+ opponentTurn: "Rakibin sırası"
+ myTurn: "Sıra sende"
+ turnOf: "Sıra {name}'de."
+ pastTurnOf: "{name}'nin sırası"
+ surrender: "Teslimiyet"
+ surrendered: "Teslim oldu"
+ timeout: "Zaman doldu"
+ drawn: "Çiz"
+ won: "{name} kazandı"
+ black: "Siyah"
+ white: "Beyaz"
+ total: "Toplam"
+ turnCount: "{count} döndür"
+ myGames: "Benim turlarım"
+ allGames: "Tüm turlar"
+ ended: "Sona erdi"
+ playing: "Şu anda oynatılıyor"
+ isLlotheo: "Taş sayısı daha az olan kazanır (Llotheo)"
+ loopedMap: "Döngüsel harita"
+ canPutEverywhere: "Fayanslar her yere yerleÅŸtirilebilir."
+ timeLimitForEachTurn: "Sıra için zaman sınırı"
+ freeMatch: "Ücretsiz Eşleştirme"
+ lookingForPlayer: "Rakip aranıyor..."
+ gameCanceled: "Oyun iptal edildi."
+ shareToTlTheGameWhenStart: "Oyun başlatıldığında zaman çizelgesinde paylaş"
+ iStartedAGame: "Oyun başladı! #MisskeyReversi"
+ opponentHasSettingsChanged: "Rakip ayarlarını değiştirmiş."
+ allowIrregularRules: "Düzensiz kurallar (tamamen ücretsiz)"
+ disallowIrregularRules: "Düzensiz kurallar yok"
+ showBoardLabels: "Tahtada satır ve sütun numaralarını göster"
+ useAvatarAsStone: "Taşları kullanıcı avatarlarına dönüştürün"
+_offlineScreen:
+ title: "Çevrimdışı - sunucuya bağlanılamıyor"
+ header: "Sunucuya bağlanılamıyor"
+_urlPreviewSetting:
+ title: "URL önizleme ayarları"
+ enable: "URL önizlemesini etkinleştir"
+ allowRedirect: "URL önizleme yönlendirmesine izin ver"
+ allowRedirectDescription: "Bir URL'de yönlendirme ayarlanmışsa, bu özelliği etkinleştirerek yönlendirmeyi takip edebilir ve yönlendirilen içeriğin önizlemesini görüntüleyebilirsin. Bu özelliği devre dışı bırakmak sunucu kaynaklarından tasarruf sağlar, ancak yönlendirilen içerik görüntülenmez."
+ timeout: "Önizleme alırken zaman aşımı (ms)"
+ timeoutDescription: "Önizlemeyi almak bu değerden daha uzun sürerse, önizleme oluşturulmaz."
+ maximumContentLength: "Maksimum İçerik Uzunluğu (bayt)"
+ maximumContentLengthDescription: "Content-Length bu değerden yüksekse, önizleme oluşturulmaz."
+ requireContentLength: "Yalnızca Content-Length değerini alabiliyorsanız önizlemeyi oluşturun."
+ requireContentLengthDescription: "Diğer sunucu Content-Length değerini döndürmezse, önizleme oluşturulmaz."
+ userAgent: "Kullanıcı Aracısı"
+ userAgentDescription: "Önizlemeleri alırken kullanılacak Kullanıcı Aracısını ayarlar. Boş bırakılırsa, varsayılan Kullanıcı Aracısı kullanılır."
+ summaryProxy: "Önizlemeler oluşturan proxy uç noktaları"
+ summaryProxyDescription: "Misskey'in kendisi değil, Summaly Proxy kullanarak önizlemeler oluştur."
+ summaryProxyDescription2: "Aşağıdaki parametreler, sorgu dizesi olarak proxy'ye bağlanır. Proxy bunları desteklemiyorsa, değerler yok sayılır."
+_mediaControls:
+ pip: "Resim içinde resim"
+ playbackRate: "Oynatma Hızı"
+ loop: "Döngüsel oynatma"
+_contextMenu:
+ title: "Bağlam menüsü"
+ app: "Uygulama"
+ appWithShift: "Shift tuÅŸuyla uygulama"
+ native: "DoÄŸal"
+_gridComponent:
+ _error:
+ requiredValue: "Bu deÄŸer gereklidir."
+ columnTypeNotSupport: "Düzenli ifade ile doğrulama yalnızca type:text sütunları için desteklenir."
+ patternNotMatch: "Bu değer {pattern} içindeki desenle eşleşmiyor."
+ notUnique: "Bu değer benzersiz olmalıdır."
+_roleSelectDialog:
+ notSelected: "Seçilmedi"
+_customEmojisManager:
+ _gridCommon:
+ copySelectionRows: "Seçili satırları kopyala"
+ copySelectionRanges: "Seçimi kopyala"
+ deleteSelectionRows: "Seçili satırları sil"
+ deleteSelectionRanges: "Seçimdeki satırları sil"
+ searchSettings: "Arama ayarları"
+ searchSettingCaption: "Ayrıntılı arama kriterleri belirle."
+ searchLimit: "Sonuç sayısı"
+ sortOrder: "Sıralama düzeni"
+ registrationLogs: "Kayıt günlüğü"
+ registrationLogsCaption: "Emojileri güncellerken veya silerken günlükler görüntülenecek. Güncelleme veya silme işleminden sonra, yeni bir sayfaya geçildiğinde veya yeniden yüklendiğinde günlükler kaybolacak."
+ alertEmojisRegisterFailedDescription: "Emojileri güncelleyemedi veya silemedi. Ayrıntılar için kayıt günlüğünü kontrol edin."
+ _logs:
+ showSuccessLogSwitch: "Başarı günlüğünü göster"
+ failureLogNothing: "Hata günlüğü yoktur."
+ logNothing: "Günlük kaydı yok."
+ _remote:
+ selectionRowDetail: "Seçilen satırın ayrıntıları"
+ importSelectionRows: "Seçilen satırları içe aktar"
+ importSelectionRangesRows: "Seçimdeki satırları içe aktar"
+ importEmojisButton: "Kontrol edilen Emojileri içe aktar"
+ confirmImportEmojisTitle: "Emoji'leri İçe Aktar"
+ confirmImportEmojisDescription: "Uzak sunucudan alınan {count} Emoji(ler)i içe aktarın. Emoji lisansına dikkat edin. Devam etmek istediğinizden emin misiniz?"
+ _local:
+ tabTitleList: "Kayıtlı emojiler"
+ tabTitleRegister: "Emoji kaydı"
+ _list:
+ emojisNothing: "Kayıtlı Emoji yok."
+ markAsDeleteTargetRows: "Silinecek hedef olarak seçilen satırları işaretle"
+ markAsDeleteTargetRanges: "Seçimdeki satırları silinecek hedef olarak işaretle"
+ alertUpdateEmojisNothingDescription: "Güncellenmiş Emoji yok."
+ alertDeleteEmojisNothingDescription: "Silinecek Emoji yok."
+ confirmMovePage: "Sayfaları taşımak ister misin?"
+ confirmChangeView: "Görüntüleme şeklini değiştirmek ister misn?"
+ confirmUpdateEmojisDescription: "{count} Emoji'yi güncelle. Devam etmek istediğinden emin misin?"
+ confirmDeleteEmojisDescription: "İşaretli {count} Emoji(leri) silin. Devam etmek istediğinden emin misin?"
+ confirmResetDescription: "Şimdiye kadar yapılan tüm değişiklikler geri alınacaktır."
+ confirmMovePageDesciption: "Bu sayfadaki Emojilerde değişiklikler yapılmış.\nSayfayı kaydetmeden terk ederseniz, bu sayfada yapılan tüm değişiklikler silinecek."
+ dialogSelectRoleTitle: "Emojilerde rol setine göre arama yapın"
+ _register:
+ uploadSettingTitle: "Yükleme ayarları"
+ uploadSettingDescription: "Bu ekranda, Emoji yüklerken davranışı yapılandırabilirsin."
+ directoryToCategoryLabel: "“Kategori†alanına dizin adını girin."
+ directoryToCategoryCaption: "Bir dizini sürükleyip bıraktığınızda, “kategori†alanına dizin adını girin."
+ confirmRegisterEmojisDescription: "Listeden Emojileri yeni özel Emojiler olarak kaydet. Devam etmek istediğinden emin misin? (Aşırı yüklemeyi önlemek için, tek bir işlemde yalnızca {count} Emoji kaydedilebilir)"
+ confirmClearEmojisDescription: "Düzenlemeleri sil ve listeden Emojileri temizle. Devam etmek istediğinden emin misiniz?"
+ confirmUploadEmojisDescription: "Drive'a sürüklenip bırakılan {count} dosyayı yükle. Devam etmek istediğinden emin misin?"
+_embedCodeGen:
+ title: "Gömme kodunu özelleştir"
+ header: "Başlığı göster"
+ autoload: "Otomatik olarak daha fazlasını yükle (kullanımdan kaldırıldı)"
+ maxHeight: "Maksimum yükseklik"
+ maxHeightDescription: "0 olarak ayarlandığında maksimum yükseklik ayarı devre dışı bırakılır. Widget'ın dikey olarak genişlemeye devam etmesini önlemek için bir değer belirt."
+ maxHeightWarn: "Maksimum yükseklik sınırı devre dışıdır (0). Bu istenmeyen bir durumsa, maksimum yüksekliği bir değer olarak ayarla."
+ previewIsNotActual: "Ekran, önizleme ekranında görüntülenen aralığı aştığı için gerçek gömme işleminden farklıdır."
+ rounded: "Yuvarlak hale getir"
+ border: "Dış çerçeveye kenarlık ekle"
+ applyToPreview: "Önizlemeye başvur"
+ generateCode: "Gömme kodu oluştur"
+ codeGenerated: "Kod oluÅŸturuldu"
+ codeGeneratedDescription: "Oluşturulan kodu web sitene yapıştırarak içeriği göm."
+_selfXssPrevention:
+ warning: "UYARI"
+ title: "“Bu ekrana bir şey yapıştırın†tamamen bir aldatmaca."
+ description1: "Buraya bir şey yapıştırırsan, kötü niyetli bir kullanıcı hesabını ele geçirebilir veya kişisel bilgilerini çalabilir."
+ description2: "Yapıştırmaya çalıştığınız şeyi tam olarak anlamıyorsanız, %c hemen çalışmayı bırakın ve bu pencereyi kapatın."
+ description3: "Daha fazla bilgi için lütfen buraya bakın. {link}"
+_followRequest:
+ recieved: "Talep alındı"
+ sent: "İstek gönderildi"
+_remoteLookupErrors:
+ _federationNotAllowed:
+ title: "Bu sunucuyla iletişim kurulamıyor"
+ description: "Bu sunucu ile iletişim devre dışı bırakılmış olabilir veya bu sunucu engellenmiş olabilir.\nLütfen sunucu yöneticisi ile iletişime geçin."
+ _uriInvalid:
+ title: "URI geçersiz"
+ description: "Girdiğin URI ile ilgili bir sorun var. Lütfen URI'da kullanılamayan karakterler girip girmediğini kontrol et."
+ _requestFailed:
+ title: "İstek başarısız oldu"
+ description: "Bu sunucuyla iletişim kurulamadı. Sunucu kapalı olabilir. Ayrıca, geçersiz veya mevcut olmayan bir URI girmediğinizden emin ol."
+ _responseInvalid:
+ title: "Yanıt geçersiz"
+ description: "Bu sunucuyla iletişim kurabildi, ancak elde edilen veriler yanlıştı."
+ _noSuchObject:
+ title: "Bulunamadı"
+ description: "İstenen kaynak bulunamadı, lütfen URI'yi tekrar kontrol edin."
+_captcha:
+ verify: "Lütfen CAPTCHA'yı doğrulayın"
+ testSiteKeyMessage: "Site ve gizli anahtarlar için test değerlerini girerek önizlemeyi kontrol edebilirsin.\nAyrıntılar için lütfen aşağıdaki sayfaya bak."
+ _error:
+ _requestFailed:
+ title: "CAPTCHA isteği başarısız oldu"
+ text: "Lütfen bir süre sonra tekrar çalıştırın veya ayarları tekrar kontrol edin."
+ _verificationFailed:
+ title: "CAPTCHA doğrulaması başarısız oldu"
+ text: "Ayarların doğru olup olmadığını lütfen tekrar kontrol edin."
+ _unknown:
+ title: "CAPTCHA hatası"
+ text: "Beklenmedik bir hata oluÅŸtu."
+_bootErrors:
+ title: "Yükleme başarısız"
+ serverError: "Bir süre bekledikten ve yeniden yükledikten sonra sorun devam ederse, lütfen aşağıdaki Hata ID ile sunucu yöneticisine başvurun."
+ solution: "Aşağıdakiler sorunu çözebilir."
+ solution1: "Tarayıcını ve işletim sistemini en son sürüme güncelle."
+ solution2: "Reklam engelleyiciyi devre dışı bırak"
+ solution3: "Tarayıcı önbelleğini temizle"
+ solution4: "Tor Tarayıcı için dom.webaudio.enabled değerini true olarak ayarlayın."
+ otherOption: "Diğer seçenekler"
+ otherOption1: "İstemci ayarlarını ve önbelleği sil"
+ otherOption2: "Basit istemciyi başlatın"
+ otherOption3: "Onarım aracını başlatın"
+ otherOption4: "Misskey'i güvenli modda başlatın"
_search:
searchScopeAll: "Tümü"
+ searchScopeLocal: "Yerel"
+ searchScopeServer: "Spesifik sunucu"
+ searchScopeUser: "Spesifik kullanıcı"
+ pleaseEnterServerHost: "Sunucu ana bilgisayarını girin"
+ pleaseSelectUser: "Kullanıcı seç"
+ serverHostPlaceholder: "Örnek: misskey.example.com"
+_serverSetupWizard:
+ installCompleted: "Misskey kurulumu tamamlandı!"
+ firstCreateAccount: "Başlamak için bir yönetici hesabı oluşturun."
+ accountCreated: "Yönetici hesabı oluşturuldu!"
+ serverSetting: "Sunucu Ayarları"
+ youCanEasilyConfigureOptimalServerSettingsWithThisWizard: "Bu sihirbaz, sunucu ayarlarını yapılandırmayı kolaylaştırır."
+ settingsYouMakeHereCanBeChangedLater: "Bu sihirbaz aracılığıyla değiştirilen ayarlar daha sonra yeniden düzenlenebilir."
+ howWillYouUseMisskey: "Misskey'i nasıl kullanacaksınız?"
+ _use:
+ single: "Tek Kullanıcı Sunucusu"
+ single_description: "Kendi sunucunuz olarak tek başına kullanın."
+ single_youCanCreateMultipleAccounts: "Tek kullanıcı sunucusu olarak çalıştırıldığında bile, gerektiğinde birden fazla hesap oluşturulabilir."
+ group: "Grup sunucusu"
+ group_description: "Diğer güvenilir kullanıcıları birden fazla kullanıcıyla birlikte kullanmaya davet edin."
+ open: "Genel sunucu"
+ open_description: "Herkesin kayıt olmasına izin verin."
+ openServerAdvice: "Çok sayıda bilinmeyen kullanıcıyı kabul etmek risklidir. Herhangi bir sorunu çözmek için güvenilir bir moderasyon sistemi kullanmanızı öneririz."
+ openServerAntiSpamAdvice: "Sunucunuzun spam için bir basamak haline gelmesini önlemek için, reCAPTCHA gibi anti-bot işlevlerini etkinleştirerek güvenliğe de özen göstermelisin."
+ howManyUsersDoYouExpect: "Kaç kullanıcı bekliyorsunuz?"
+ _scale:
+ small: "100'den az (küçük ölçekli)"
+ medium: "100'den fazla ve 1000'den az kullanıcı (orta büyüklükte)"
+ large: "1000'den fazla (Büyük ölçekli)"
+ largeScaleServerAdvice: "Büyük sunucular, yük dengeleme ve veritabanı çoğaltma gibi gelişmiş altyapı bilgisi gerektirebilir."
+ doYouConnectToFediverse: "Fediverse'e baÄŸlanmak ister misin?"
+ doYouConnectToFediverse_description1: "Dağıtılmış sunucular ağına (Fediverse) bağlandığında, içerik diğer sunucularla paylaşılabilir."
+ doYouConnectToFediverse_description2: "Fediverse ile bağlantı kurmak “federasyon†olarak da adlandırılır."
+ youCanConfigureMoreFederationSettingsLater: "Birleştirilmiş sunucuları belirtme gibi gelişmiş ayarlar daha sonra yapılandırılabilir."
+ remoteContentsCleaning: "Alınan içeriklerin otomatik olarak temizlenmesi"
+ remoteContentsCleaning_description: "Federasyon, sürekli içerik akışına neden olabilir. Otomatik temizleme özelliğini etkinleştirmek, depolama alanından tasarruf etmek için sunucudan eski ve referanslanmamış içeriği kaldıracak."
+ adminInfo: "Yönetici bilgileri"
+ adminInfo_description: "Sorguları almak için kullanılan yönetici bilgilerini ayarlar."
+ adminInfo_mustBeFilled: "Genel sunucu veya federasyon açıksa girilmelidir."
+ followingSettingsAreRecommended: "Aşağıdaki ayarlar önerilir"
+ applyTheseSettings: "Bu ayarları uygulayın"
+ skipSettings: "Ayarları atla"
+ settingsCompleted: "Kurulum tamamlandı!"
+ settingsCompleted_description: "Zaman ayırdığınız için teşekkür ederiz. Artık her şey hazır olduğuna göre, sunucuyu hemen kullanmaya başlayabilirsin."
+ settingsCompleted_description2: "Sunucu ayarları “Kontrol Paneliâ€nden deÄŸiÅŸtirilebilir."
+ donationRequest: "Bağış Talebi"
+ _donationRequest:
+ text1: "Misskey, gönüllüler tarafından geliştirilen ücretsiz bir yazılımdır."
+ text2: "Bu yazılımı gelecekte de geliştirmeye devam edebilmemiz için desteğini rica ederiz."
+ text3: "Destekçilere özel avantajlar da var!"
+_uploader:
+ editImage: "Resmi Düzenle"
+ compressedToX: "{x} boyutuna sıkıştırıldı"
+ savedXPercent: "{x}% tasarruf"
+ abortConfirm: "Bazı dosyalar yüklenmedi, iptal etmek ister misin?"
+ doneConfirm: "Bazı dosyalar yüklenmedi, yine de devam etmek istiyor musun?"
+ maxFileSizeIsX: "Yükleyebileceğin maksimum dosya boyutu {x}"
+ allowedTypes: "Yüklenebilir dosya türleri"
+ tip: "Dosya henüz yüklenmediğinden, bu iletişim kutusu yüklemeden önce dosyayı onaylamanıza, yeniden adlandırmana, sıkıştırmana ve kırpmana olanak tanır. Hazır olduğunda, “Yükle†düğmesine basarak yüklemeyi başlatabilirsin."
+_clientPerformanceIssueTip:
+ title: "Performans ipuçları"
+ makeSureDisabledAdBlocker: "Reklam engelleyicini devre dışı bırak"
+ makeSureDisabledAdBlocker_description: "Reklam engelleyiciler performansı etkileyebilir, lütfen sisteminde veya tarayıcının özelliklerinde/uzantılarında reklam engelleyicilerin etkinleştirilmediğinden emin ol."
+ makeSureDisabledCustomCss: "Özel CSS'yi devre dışı bırak"
+ makeSureDisabledCustomCss_description: "Stil geçersiz kılma, performansı etkileyebilir. Stil geçersiz kılan özel CSS veya uzantıların etkinleştirilmediğinden emin ol."
+ makeSureDisabledAddons: "Uzantıları devre dışı bırak"
+ makeSureDisabledAddons_description: "Bazı uzantılar istemci davranışını engelleyebilir ve performansı etkileyebilir. Lütfen tarayıcı uzantılarınızı devre dışı bırakın ve durumun düzelip düzelmediğini kontrol edin."
+_clip:
+ tip: "Klip, notları gruplandırmanıza olanak tanıyan bir özelliktir."
+_userLists:
+ tip: "Listeler, oluşturulurken belirttiğin herhangi bir kullanıcıyı içerebilir. Oluşturulan liste, yalnızca belirtilen kullanıcıları gösteren bir zaman çizelgesi olarak görüntülenebilir."
+watermark: "Filigran"
+defaultPreset: "Varsayılan Ön Ayar"
_watermarkEditor:
+ tip: "Kredi bilgileri gibi bir filigran görüntüye eklenebilir."
+ quitWithoutSaveConfirm: "KaydedilmemiÅŸ deÄŸiÅŸiklikleri silmek ister misin?"
+ driveFileTypeWarn: "Bu dosya desteklenmiyor"
+ driveFileTypeWarnDescription: "Bir görüntü dosyası seçin"
+ title: "Filigranı Düzenle"
+ cover: "Her şeyi örtün"
+ repeat: "her yere yayılmış"
+ opacity: "Opaklık"
+ scale: "Boyut"
+ text: "Metin"
+ position: "Pozisyon"
+ type: "Tür"
image: "Görseller"
+ advanced: "GeliÅŸmiÅŸ"
+ angle: "Açı"
+ stripe: "Çizgiler"
+ stripeWidth: "Çizgi genişliği"
+ stripeFrequency: "Satır sayısı"
+ polkadot: "Nokta deseni"
+ checker: "Kontrolcü"
+ polkadotMainDotOpacity: "Ana noktanın opaklığı"
+ polkadotMainDotRadius: "Ana noktanın boyutu"
+ polkadotSubDotOpacity: "İkincil noktanın opaklığı"
+ polkadotSubDotRadius: "İkincil noktanın boyutu"
+ polkadotSubDotDivisions: "Alt nokta sayısı."
+_imageEffector:
+ title: "Effektler"
+ addEffect: "Efektler Ekle"
+ discardChangesConfirm: "Cidden çıkmak istiyor musun? Kaydedilmemiş değişikliklerin var."
+ nothingToConfigure: "Yapılandırılabilir seçenekler mevcut değildir."
+ _fxs:
+ chromaticAberration: "Renk Sapması"
+ glitch: "Bozulma"
+ mirror: "Ayna"
+ invert: "Renkleri Ters Çevir"
+ grayscale: "Gri tonlama"
+ colorAdjust: "Renk Düzeltme"
+ colorClamp: "Renk Sıkıştırma"
+ colorClampAdvanced: "Renk Sıkıştırma (Gelişmiş)"
+ distort: "Bozulma"
+ threshold: "Binarize"
+ zoomLines: "DoymuÅŸ hatlar"
+ stripe: "Çizgiler"
+ polkadot: "Nokta deseni"
+ checker: "Denetleyici"
+ blockNoise: "Gürültüyü Engelle"
+ tearing: "Yırtılma"
+ _fxProps:
+ angle: "Açı"
+ scale: "Boyut"
+ size: "Boyut"
+ color: "Renk"
+ opacity: "Opaklık"
+ normalize: "Normalize"
+ amount: "Miktar"
+ lightness: "Hafiflet"
+ contrast: "Kontrast"
+ hue: "Hue"
+ brightness: "Parlaklık"
+ saturation: "Doygunluk"
+ max: "Maksimum"
+ min: "Minimum"
+ direction: "Yön"
+ phase: "AÅŸama"
+ frequency: "Sıklık"
+ strength: "Güç"
+ glitchChannelShift: "Kanal deÄŸiÅŸimi"
+ seed: "Tohum deÄŸeri"
+ redComponent: "Kırmızı bileşen"
+ greenComponent: "YeÅŸil bileÅŸen"
+ blueComponent: "Mavi bileÅŸen"
+ threshold: "EÅŸik"
+ centerX: "Merkez X"
+ centerY: "Merkez Y"
+ zoomLinesSmoothing: "Düzeltme"
+ zoomLinesSmoothingDescription: "Düzeltme ve yakınlaştırma çizgi genişliği birlikte kullanılamaz."
+ zoomLinesThreshold: "Zoom çizgi genişliği"
+ zoomLinesMaskSize: "Merkez çapı"
+ zoomLinesBlack: "Siyah yap"
+drafts: "Taslaklar"
+_drafts:
+ select: "Taslak Seç"
+ cannotCreateDraftAnymore: "Oluşturulabilecek taslak sayısı aşılmıştır."
+ cannotCreateDraft: "Bu içerikle taslak oluşturamazsınız."
+ delete: "Taslak Sil"
+ deleteAreYouSure: "Taslağı silmek ister misin?"
+ noDrafts: "Taslak yok"
+ replyTo: "{user} notunu yanıtla"
+ quoteOf: "{user} notuna alıntı"
+ postTo: "{channel}'a gönder"
+ saveToDraft: "Taslak olarak kaydet"
+ restoreFromDraft: "Taslaktan geri yükle"
+ restore: "Geri yükle"
+ listDrafts: "Taslaklar Listesi"
diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml
index c6708ec0e2..46d9ab95ff 100644
--- a/locales/uk-UA.yml
+++ b/locales/uk-UA.yml
@@ -919,6 +919,8 @@ flip: "Перевернути"
lastNDays: "ОÑтанні {n} днів"
postForm: "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð½Ð¾Ñ‚Ð°Ñ‚ÐºÐ¸"
information: "ІнформаціÑ"
+inMinutes: "Ñ…"
+inDays: "д"
_chat:
invitations: "ЗапроÑити"
noHistory: "ІÑÑ‚Ð¾Ñ€Ñ–Ñ Ð¿Ð¾Ñ€Ð¾Ð¶Ð½Ñ"
@@ -1646,3 +1648,10 @@ _watermarkEditor:
type: "Тип"
image: "ЗображеннÑ"
advanced: "Розширені"
+_imageEffector:
+ _fxProps:
+ scale: "Розмір"
+ size: "Розмір"
+ color: "Колір"
+ opacity: "ÐепрозоріÑть"
+ lightness: "ЯÑкравіÑть"
diff --git a/locales/uz-UZ.yml b/locales/uz-UZ.yml
index b8375302ba..cfa2e26fd5 100644
--- a/locales/uz-UZ.yml
+++ b/locales/uz-UZ.yml
@@ -903,7 +903,7 @@ _theme:
header: "Sarlavha"
navBg: "Yon panel foni"
navFg: "Yon panel matni"
- mention: "Murojat"
+ mention: "Eslatib o'tish"
renote: "Qayta qayd etish"
divider: "Ajratrmoq"
fgHighlighted: "Belgilangan matn"
@@ -1045,7 +1045,7 @@ _notification:
_types:
all: "Barchasi"
follow: "Obuna bo‘lish"
- mention: "Murojat"
+ mention: "Eslatib o'tish"
renote: "Qayta qayd etish"
quote: "Iqtibos keltirish"
reaction: "Reaktsiyalar"
@@ -1102,3 +1102,7 @@ _watermarkEditor:
type: "turi"
image: "Rasmlar"
advanced: "Murakkab"
+_imageEffector:
+ _fxProps:
+ color: "Rang"
+ lightness: "Yoritish"
diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml
index 47ea715058..cb2f37bed7 100644
--- a/locales/vi-VN.yml
+++ b/locales/vi-VN.yml
@@ -1196,7 +1196,6 @@ showAvatarDecorations: "Hiển thị trang trí ảnh đại diện"
releaseToRefresh: "Thả để làm mới"
refreshing: "Äang làm má»›i"
pullDownToRefresh: "Kéo xuống để làm mới"
-signupPendingError: "Äã xảy ra sá»± cố khi xác minh địa chỉ email cá»§a bạn. Liên kết có thể đã hết hạn."
cwNotationRequired: "Nếu \"Ẩn nội dung\" được bật thì cần phải có chú thích."
decorate: "Trang trí"
lastNDays: "{n} ngày trước"
@@ -1221,6 +1220,8 @@ information: "Giới thiệu"
chat: "Trò chuyện"
migrateOldSettings: "Di chuyển cài đặt cũ"
migrateOldSettings_description: "Thông thưá»ng, quá trình này diá»…n ra tá»± động, nhưng nếu vì lý do nào đó mà quá trình di chuyển không thành công, bạn có thể kích hoạt thá»§ công quy trình di chuyển, quá trình này sẽ ghi đè lên thông tin cấu hình hiện tại cá»§a bạn."
+inMinutes: "phút"
+inDays: "ngày"
_chat:
invitations: "Má»i"
noHistory: "Không có dữ liệu"
@@ -2089,3 +2090,11 @@ _watermarkEditor:
image: "Hình ảnh"
advanced: "Nâng cao"
angle: "Góc"
+_imageEffector:
+ _fxProps:
+ angle: "Góc"
+ scale: "Kích thước"
+ size: "Kích thước"
+ color: "Màu sắc"
+ opacity: "Äá»™ trong suốt"
+ lightness: "Äộ saÌng"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index 0958158e5e..6c62c80f0d 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -1054,6 +1054,7 @@ permissionDeniedError: "æ“作被拒ç»"
permissionDeniedErrorDescription: "本账户没有执行该æ“作的æƒé™ã€‚"
preset: "预设值"
selectFromPresets: "从预设值中选择"
+custom: "自定义"
achievements: "æˆå°±"
gotInvalidResponseError: "æœåŠ¡å™¨æ— åº”ç­”"
gotInvalidResponseErrorDescription: "您的网络连接å¯èƒ½å‡ºçŽ°äº†é—®é¢˜, 或是远程æœåŠ¡å™¨æš‚æ—¶ä¸å¯ç”¨. 请ç¨åŽé‡è¯•。"
@@ -1092,6 +1093,7 @@ prohibitedWordsDescription2: "AND æ¡ä»¶ç”¨ç©ºæ ¼åˆ†éš”,正则表达å¼ç”¨æ–œç
hiddenTags: "éšè—标签"
hiddenTagsDescription: "设定的标签将ä¸ä¼šåœ¨æ—¶é—´çº¿ä¸Šæ˜¾ç¤ºã€‚å¯ä½¿ç”¨æ¢è¡Œæ¥è®¾ç½®å¤šä¸ªæ ‡ç­¾ã€‚"
notesSearchNotAvailable: "取孿£€ç´¢ä¸å¯ç”¨"
+usersSearchNotAvailable: "用户检索ä¸å¯ç”¨"
license: "许å¯ä¿¡æ¯"
unfavoriteConfirm: "确定è¦å–消收è—å—?"
myClips: "我的便签"
@@ -1143,7 +1145,7 @@ channelArchiveConfirmTitle: "è¦å°† {name} å½’æ¡£å—?"
channelArchiveConfirmDescription: "å½’æ¡£åŽï¼Œåœ¨é¢‘é“列表与æœç´¢ç»“果中ä¸ä¼šæ˜¾ç¤ºï¼Œä¹Ÿæ— æ³•å‘布新的贴文。"
thisChannelArchived: "该频é“已被归档。"
displayOfNote: "显示帖å­"
-initialAccountSetting: "åˆå§‹è®¾ç½®"
+initialAccountSetting: "åˆå§‹è®¾å®š"
youFollowing: "正在关注"
preventAiLearning: "æ‹’ç»æŽ¥å—生æˆå¼ AI 的学习"
preventAiLearningDescription: "è¦æ±‚æ–‡ç« ç”Ÿæˆ AI 或图åƒç”Ÿæˆ AI ä¸èƒ½å¤Ÿä»¥å‘布的帖å­å’Œå›¾åƒç­‰å†…容作为学习对象。这是通过在 HTML å“åº”ä¸­åŒ…å« noai 标志æ¥å®žçŽ°çš„ï¼Œè¿™ä¸èƒ½å®Œå…¨é˜»æ­¢ AI 学习你的å‘å¸ƒå†…å®¹ï¼Œå¹¶ä¸æ˜¯æ‰€æœ‰ AI 都会éµå®ˆè¿™ç±»è¯·æ±‚。"
@@ -1243,7 +1245,7 @@ releaseToRefresh: "æ¾å¼€ä»¥åˆ·æ–°"
refreshing: "刷新中"
pullDownToRefresh: "下拉以刷新"
useGroupedNotifications: "分组显示通知"
-signupPendingError: "确认电å­é‚®ä»¶æ—¶å‡ºçŽ°é”™è¯¯ã€‚é“¾æŽ¥å¯èƒ½å·²è¿‡æœŸã€‚"
+emailVerificationFailedError: "确认电å­é‚®ä»¶æ—¶å‡ºçŽ°é”™è¯¯ã€‚é“¾æŽ¥å¯èƒ½å·²è¿‡æœŸã€‚"
cwNotationRequired: "在å¯ç”¨ã€Œéšè—å†…å®¹ã€æ—¶å¿…须输入注释"
doReaction: "回应"
code: "代ç "
@@ -1318,7 +1320,7 @@ confirmOnReact: "å‘é€å›žåº”å‰éœ€è¦ç¡®è®¤"
reactAreYouSure: "è¦ç”¨ã€Œ{emoji}ã€è¿›è¡Œå›žåº”å—?"
markAsSensitiveConfirm: "è¦å°†æ­¤åª’ä½“æ ‡è®°ä¸ºæ•æ„Ÿå—?"
unmarkAsSensitiveConfirm: "è¦å°†æ­¤åª’ä½“è§£é™¤æ•æ„Ÿæ ‡è®°å—?"
-preferences: "设置"
+preferences: "å好设置"
accessibility: "辅助功能"
preferencesProfile: "设置的é…ç½®"
copyPreferenceId: "å¤åˆ¶è®¾ç½® ID"
@@ -1368,6 +1370,13 @@ redisplayAllTips: "釿–°æ˜¾ç¤ºæ‰€æœ‰çš„æç¤ºå’ŒæŠ€å·§"
hideAllTips: "éšè—所有的æç¤ºå’ŒæŠ€å·§"
defaultImageCompressionLevel: "默认图åƒåŽ‹ç¼©ç­‰çº§"
defaultImageCompressionLevel_description: "较低的等级å¯ä»¥ä¿æŒç”»è´¨ï¼Œä½†ä¼šå¢žåŠ æ–‡ä»¶å¤§å°ã€‚<br>较高的等级å¯ä»¥å‡å°‘文件大å°ï¼Œä½†ç›¸å¯¹åº”的画质将会é™ä½Žã€‚"
+inMinutes: "分"
+inDays: "æ—¥"
+safeModeEnabled: "å·²å¯ç”¨å®‰å…¨æ¨¡å¼"
+pluginsAreDisabledBecauseSafeMode: "å› å¯ç”¨äº†å®‰å…¨æ¨¡å¼ï¼Œæ‰€æœ‰æ’ä»¶å‡å·²è¢«ç¦ç”¨ã€‚"
+customCssIsDisabledBecauseSafeMode: "å› å¯ç”¨äº†å®‰å…¨æ¨¡å¼ï¼Œæ— æ³•应用自定义 CSS。"
+themeIsDefaultBecauseSafeMode: "å¯ç”¨å®‰å…¨æ¨¡å¼æ—¶å°†ä½¿ç”¨é»˜è®¤ä¸»é¢˜ã€‚关闭安全模å¼åŽå°†è¿˜åŽŸã€‚"
+thankYouForTestingBeta: "感谢您å助测试 beta 版ï¼"
_order:
newest: "从新到旧"
oldest: "从旧到新"
@@ -1459,6 +1468,7 @@ _settings:
contentsUpdateFrequency_description2: "当实时模å¼å¼€å¯æ—¶ï¼Œæ— è®ºæ­¤è®¾ç½®å¦‚何,内容都会实时更新。"
showUrlPreview: "显示 URL 预览"
showAvailableReactionsFirstInNote: "在顶部显示å¯ç”¨çš„回应"
+ showPageTabBarBottom: "åœ¨ä¸‹æ–¹æ˜¾ç¤ºé¡µé¢æ ‡ç­¾æ "
_chat:
showSenderName: "显示å‘é€è€…çš„åå­—"
sendOnEnter: "回车键å‘é€"
@@ -1536,7 +1546,7 @@ _announcement:
silenceDescription: "å¼€å¯åŽï¼Œæ­¤æ¡å…¬å‘Šå°†ä¸ä¼šå‘é€é€šçŸ¥ï¼Œä¹Ÿä¸å¼ºåˆ¶ç”¨æˆ·é˜…读。"
_initialAccountSetting:
accountCreated: "账户创建完æˆäº†ï¼"
- letsStartAccountSetup: "æ¥è¿›è¡Œå¸æˆ·çš„åˆå§‹è®¾ç½®å§ã€‚"
+ letsStartAccountSetup: "马上æ¥è¿›è¡Œè´¦æˆ·çš„åˆå§‹è®¾å®šå§ã€‚"
letsFillYourProfile: "首先,æ¥è®¾å®šä½ çš„个人档案å§ï¼"
profileSetting: "个人资料设置"
privacySetting: "éšç§è®¾ç½®"
@@ -1548,7 +1558,7 @@ _initialAccountSetting:
haveFun: "希望 {name} 在这里玩得开心ï¼"
youCanContinueTutorial: "您å¯ä»¥ç»§ç»­äº†è§£ {name}(Misskey) 的使用教程,也å¯ä»¥åœ¨æ­¤åœæ­¢æ•™ç¨‹å¹¶ç«‹å³å¼€å§‹ä½¿ç”¨å®ƒã€‚\n"
startTutorial: "开始教学"
- skipAreYouSure: "è¦è·³è¿‡åˆå§‹è®¾ç½®å—?"
+ skipAreYouSure: "è¦è·³è¿‡åˆå§‹è®¾å®šå—?"
laterAreYouSure: "è¦ç¨åŽå†è¿›è¡Œåˆå§‹è®¾å®šå—?"
_initialTutorial:
launchTutorial: "观看教学"
@@ -1632,6 +1642,10 @@ _serverSettings:
fanoutTimelineDbFallback: "回退到数æ®åº“"
fanoutTimelineDbFallbackDescription: "当å¯ç”¨æ—¶ï¼Œè‹¥æ—¶é—´çº¿æœªè¢«ç¼“存,则将é¢å¤–查询数æ®åº“。ç¦ç”¨è¯¥åŠŸèƒ½å¯é€šè¿‡ä¸æ‰§è¡Œå›žé€€å¤„ç†è¿›ä¸€æ­¥å‡å°‘æœåŠ¡å™¨è´Ÿè½½ï¼Œä½†ä¼šé™åˆ¶å¯æ£€ç´¢çš„æ—¶é—´çº¿èŒƒå›´ã€‚"
reactionsBufferingDescription: "开坿—¶å¯æ˜¾è‘—æé«˜å‘é€å›žåº”时的性能,åŠå‡è½»æ•°æ®åº“è´Ÿè·ã€‚但 Redis 的内存用é‡ä¼šç›¸åº”增加。"
+ remoteNotesCleaning: "自动清ç†è¿œç¨‹æŠ•稿"
+ remoteNotesCleaning_description: "å¯ç”¨åŽï¼Œå°†è‡ªåŠ¨æ¸…ç†å·²æ— æ³•找到的旧的远程投稿,å¯å‡ç¼“æ•°æ®åº“的增长。"
+ remoteNotesCleaningMaxProcessingDuration: "æœ€é•¿æ¸…ç†æŒç»­æ—¶é—´"
+ remoteNotesCleaningExpiryDaysForEachNotes: "最短帖å­ä¿ç•™æœŸé™"
inquiryUrl: "è”络地å€"
inquiryUrlDescription: "ç”¨æ¥æŒ‡å®šè¯¸å¦‚呿œåŠ¡è¿è¥å•†å’¨è¯¢çš„论å›åœ°å€ï¼Œæˆ–记载了è¿è¥å•†è”系方å¼ä¹‹ç±»çš„网页地å€ã€‚"
openRegistration: "开放注册"
@@ -1650,6 +1664,11 @@ _serverSettings:
userGeneratedContentsVisibilityForVisitor: "用户生æˆå†…容对éžç”¨æˆ·çš„å¯è§æ€§"
userGeneratedContentsVisibilityForVisitor_description: "对于防止难以审核的ä¸é€‚当的远程内容等,通过自己的æœåŠ¡å™¨æ— æ„中在互è”网上公开等问题很有用。"
userGeneratedContentsVisibilityForVisitor_description2: "åŒ…å«æœåŠ¡å™¨æŽ¥æ”¶åˆ°çš„è¿œç¨‹å†…å®¹åœ¨å†…ï¼Œæ— æ¡ä»¶å°†æœåŠ¡å™¨ä¸Šçš„æ‰€æœ‰å†…å®¹å…¬å¼€åœ¨äº’è”ç½‘ä¸Šå­˜åœ¨é£Žé™©ã€‚ç‰¹åˆ«æ˜¯å¯¹åŽ»ä¸­å¿ƒåŒ–çš„ç‰¹æ€§ä¸æ˜¯å¾ˆäº†è§£çš„访问者有å¯èƒ½å°†è¿œç¨‹æœåŠ¡å™¨ä¸Šçš„å†…å®¹è¯¯è®¤ä¸ºæ˜¯åœ¨æ­¤æœåŠ¡å™¨å†…ç”Ÿæˆçš„,需è¦ç‰¹åˆ«ç•™æ„。"
+ restartServerSetupWizardConfirm_title: "è¦é‡æ–°å¼€å§‹æœåС噍åˆå§‹è®¾å®šå‘导å—?"
+ restartServerSetupWizardConfirm_text: "现有的部分设定将é‡ç½®ã€‚"
+ entrancePageStyle: "å…¥å£é¡µé¢æ ·å¼"
+ showTimelineForVisitor: "显示时间线"
+ showActivitiesForVisitor: "显示活动"
_userGeneratedContentsVisibilityForVisitor:
all: "全部公开"
localOnly: "仅公开本地内容,éšè—远程内容"
@@ -1927,7 +1946,7 @@ _role:
name: "角色åç§°"
description: "角色æè¿°"
permission: "角色æƒé™"
- descriptionOfPermission: "<b>监察员</b>å¯ä»¥æ‰§è¡ŒåŸºæœ¬åœ°å®¡æ ¸æ“作。\n<b>管ç†å‘˜</b>å¯ä»¥æ›´æ”¹æœåŠ¡å™¨çš„æ‰€æœ‰è®¾ç½®ã€‚"
+ descriptionOfPermission: "<b>监察员</b>å¯ä»¥æ‰§è¡ŒåŸºæœ¬çš„审核æ“作。\n<b>管ç†å‘˜</b>å¯ä»¥æ›´æ”¹å®žä¾‹çš„æ‰€æœ‰è®¾ç½®ã€‚"
assignTarget: "授æƒå¯¹è±¡"
descriptionOfAssignTarget: "<b>手动</b>指手动选择è°è¢«åŒ…括在这个角色中。\n<b>ç¬¦åˆæ¡ä»¶</b>指设置æ¡ä»¶ä»¥è‡ªåŠ¨åŒ…æ‹¬ç¬¦åˆæ¡ä»¶çš„用户。"
manual: "手动"
@@ -1986,6 +2005,7 @@ _role:
descriptionOfRateLimitFactor: "值越å°é™åˆ¶è¶Šå°‘,值越大é™åˆ¶è¶Šå¤šã€‚"
canHideAds: "å¯ä»¥éšè—广告"
canSearchNotes: "是å¦å¯ä»¥æœç´¢å¸–å­"
+ canSearchUsers: "使用用户检索"
canUseTranslator: "使用翻译功能"
avatarDecorationLimit: "坿·»åŠ å¤´åƒæŒ‚件的最大个数"
canImportAntennas: "å…许导入天线"
@@ -2258,6 +2278,7 @@ _time:
minute: "分"
hour: "å°æ—¶"
day: "æ—¥"
+ month: "个月"
_2fa:
alreadyRegistered: "此设备已被注册"
registerTOTP: "开始设置验è¯å™¨"
@@ -3060,6 +3081,7 @@ _bootErrors:
otherOption1: "清除客户端设定与缓存"
otherOption2: "使用简易客户端"
otherOption3: "å¯åŠ¨ä¿®å¤å·¥å…·"
+ otherOption4: "以安全模å¼å¯åЍ Misskey"
_search:
searchScopeAll: "全部"
searchScopeLocal: "本地"
@@ -3096,6 +3118,8 @@ _serverSetupWizard:
doYouConnectToFediverse_description1: "若加入由分散性æœåŠ¡å™¨æ‰€æž„æˆçš„网络(Fediverse),将能与其它æœåŠ¡å™¨äº¤æ¢å†…容。"
doYouConnectToFediverse_description2: "加入 Fediverse 在这里被称为「è”åˆã€ã€‚"
youCanConfigureMoreFederationSettingsLater: "å¯åœ¨ä¹‹åŽè¿›è¡Œå¦‚哪些æœåС噍å¯ä»¥è¿›è¡Œè”åˆç­‰é«˜çº§è®¾ç½®ã€‚"
+ remoteContentsCleaning: "自动清ç†ä¼ å…¥å†…容"
+ remoteContentsCleaning_description: "加入è”åˆåŽï¼ŒæœåС噍将æŒç»­æŽ¥æ”¶å¤§é‡å†…容。打开自动清ç†åŽï¼Œå°†è‡ªåŠ¨åˆ é™¤æ— æ³•æ‰¾åˆ°çš„æ—§å†…å®¹ï¼Œå¯èŠ‚çœå­˜å‚¨ç©ºé—´ã€‚"
adminInfo: "管ç†å‘˜ä¿¡æ¯"
adminInfo_description: "设置用于接å—询问的管ç†å‘˜ä¿¡æ¯ã€‚"
adminInfo_mustBeFilled: "开放æœåŠ¡å™¨æˆ–å¼€å¯äº†è”åˆçš„æƒ…况下必须输入。"
@@ -3148,10 +3172,10 @@ _watermarkEditor:
type: "类型"
image: "图片"
advanced: "高级"
+ angle: "角度"
stripe: "æ¡çº¹"
stripeWidth: "线æ¡å®½åº¦"
stripeFrequency: "çº¿æ¡æ•°é‡"
- angle: "角度"
polkadot: "波点"
checker: "检查"
polkadotMainDotOpacity: "主波点的ä¸é€æ˜Žåº¦"
@@ -3163,6 +3187,7 @@ _imageEffector:
title: "效果"
addEffect: "添加效果"
discardChangesConfirm: "丢弃当å‰è®¾ç½®å¹¶é€€å‡ºï¼Ÿ"
+ nothingToConfigure: "还没有设置"
_fxs:
chromaticAberration: "色差"
glitch: "故障"
@@ -3180,6 +3205,38 @@ _imageEffector:
checker: "检查"
blockNoise: "å—状噪点"
tearing: "撕裂"
+ _fxProps:
+ angle: "角度"
+ scale: "大å°"
+ size: "大å°"
+ color: "颜色"
+ opacity: "ä¸é€æ˜Žåº¦"
+ normalize: "标准化"
+ amount: "æ•°é‡"
+ lightness: "浅色"
+ contrast: "对比度"
+ hue: "色调"
+ brightness: "亮度"
+ saturation: "饱和度"
+ max: "最大值"
+ min: "最å°å€¼"
+ direction: "æ–¹å‘"
+ phase: "相ä½"
+ frequency: "频率"
+ strength: "强度"
+ glitchChannelShift: "é”™ä½"
+ seed: "ç§å­"
+ redComponent: "红色æˆåˆ†"
+ greenComponent: "绿色æˆåˆ†"
+ blueComponent: "è“色æˆåˆ†"
+ threshold: "阈值"
+ centerX: "中心 X "
+ centerY: "中心 Y"
+ zoomLinesSmoothing: "平滑"
+ zoomLinesSmoothingDescription: "平滑和集中线宽度设置ä¸èƒ½åŒæ—¶ä½¿ç”¨ã€‚"
+ zoomLinesThreshold: "集中线宽度"
+ zoomLinesMaskSize: "中心直径"
+ zoomLinesBlack: "å˜æˆé»‘色"
drafts: "è‰ç¨¿"
_drafts:
select: "选择è‰ç¨¿"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index d0dbecfcba..65b7f9bfba 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -638,7 +638,7 @@ inboxUrl: "收件夾 URL"
addedRelays: "已加入的中繼器"
serviceworkerInfo: "如è¦ä½¿ç”¨æŽ¨æ’­é€šçŸ¥ï¼Œéœ€è¦å•Ÿç”¨æ­¤é¸é …並設定金鑰。"
deletedNote: "已刪除的貼文"
-invisibleNote: "ç§äººè²¼æ–‡"
+invisibleNote: "ç§å¯†çš„貼文"
enableInfiniteScroll: "啟用自動滾動é é¢æ¨¡å¼"
visibility: "å¯è¦‹æ€§"
poll: "ç¥¨é¸æ´»å‹•"
@@ -1054,6 +1054,7 @@ permissionDeniedError: "æ“作被拒絕"
permissionDeniedErrorDescription: "此帳戶沒有執行這個æ“作的權é™ã€‚"
preset: "é è¨­å€¼"
selectFromPresets: "從é è¨­å€¼ä¸­é¸æ“‡"
+custom: "自訂"
achievements: "æˆå°±"
gotInvalidResponseError: "伺æœå™¨çš„回應無效"
gotInvalidResponseErrorDescription: "伺æœå™¨å¯èƒ½å·²é—œé–‰æˆ–者在維護中,請ç¨å¾Œå†è©¦ã€‚"
@@ -1092,6 +1093,7 @@ prohibitedWordsDescription2: "空格代表「以åŠã€ï¼ˆAND),斜線包åœé—
hiddenTags: "éš±è—æ¨™ç±¤"
hiddenTagsDescription: "è¨­å®šçš„æ¨™ç±¤ä¸æœƒåœ¨è¶¨å‹¢ä¸­é¡¯ç¤ºï¼Œæ›è¡Œå¯ä»¥è¨­å®šå¤šå€‹æ¨™ç±¤ã€‚"
notesSearchNotAvailable: "無法使用æœå°‹è²¼æ–‡åŠŸèƒ½ã€‚"
+usersSearchNotAvailable: "無法使用使用者æœå°‹åŠŸèƒ½ã€‚"
license: "授權"
unfavoriteConfirm: "è¦å–消收錄我的最愛嗎?"
myClips: "我的摘錄"
@@ -1243,7 +1245,7 @@ releaseToRefresh: "放開以更新內容"
refreshing: "載入更新中"
pullDownToRefresh: "往下拉來更新內容"
useGroupedNotifications: "分組顯示通知訊æ¯"
-signupPendingError: "驗證您的電å­éƒµä»¶åœ°å€æ™‚出ç¾å•題。連çµå¯èƒ½å·²éŽæœŸã€‚"
+emailVerificationFailedError: "驗證您的電å­éƒµä»¶åœ°å€æ™‚出ç¾å•題。連çµå¯èƒ½å·²éŽæœŸã€‚"
cwNotationRequired: "如果開啟「隱è—內容ã€ï¼Œå‰‡éœ€è¦è¨»è§£èªªæ˜Žã€‚"
doReaction: "åšå‡ºå應"
code: "程å¼ç¢¼"
@@ -1368,6 +1370,13 @@ redisplayAllTips: "釿–°é¡¯ç¤ºæ‰€æœ‰ã€Œæç¤ºèˆ‡æŠ€å·§ã€"
hideAllTips: "éš±è—æ‰€æœ‰ã€Œæç¤ºèˆ‡æŠ€å·§ã€"
defaultImageCompressionLevel: "é è¨­çš„å½±åƒå£“縮程度"
defaultImageCompressionLevel_description: "低的話å¯ä»¥ä¿ç•™ç•«è³ªï¼Œä½†æ˜¯æœƒå¢žåŠ æª”æ¡ˆçš„å¤§å°ã€‚<br>高的話å¯ä»¥æ¸›å°‘檔案大å°ï¼Œä½†æ˜¯æœƒé™ä½Žç•«è³ªã€‚"
+inMinutes: "分é˜"
+inDays: "æ—¥"
+safeModeEnabled: "啟用安全模å¼"
+pluginsAreDisabledBecauseSafeMode: "由於啟用安全模å¼ï¼Œæ‰€æœ‰çš„外掛都被åœç”¨ã€‚"
+customCssIsDisabledBecauseSafeMode: "由於啟用安全模å¼ï¼Œæ‰€æœ‰çš„客製 CSS 都被åœç”¨ã€‚"
+themeIsDefaultBecauseSafeMode: "在安全模å¼å•Ÿç”¨æœŸé–“將使用é è¨­ä¸»é¡Œã€‚關閉安全模å¼å¾Œæœƒæ¢å¾©åŽŸæœ¬çš„è¨­å®šã€‚"
+thankYouForTestingBeta: "æ„Ÿè¬æ‚¨å”助驗證 beta 版ï¼"
_order:
newest: "最新的在å‰"
oldest: "最舊的在å‰"
@@ -1459,6 +1468,7 @@ _settings:
contentsUpdateFrequency_description2: "ç•¶å³æ™‚模å¼é–‹å•Ÿæ™‚,ä¸è«–æ­¤è¨­å®šç‚ºä½•ï¼Œå…§å®¹éƒ½æœƒå³æ™‚更新。"
showUrlPreview: "顯示網å€é è¦½"
showAvailableReactionsFirstInNote: "å°‡å¯ç”¨çš„忇‰é¡¯ç¤ºåœ¨é ‚部"
+ showPageTabBarBottom: "在底部顯示é é¢çš„æ¨™ç±¤åˆ—"
_chat:
showSenderName: "顯示發é€è€…çš„å稱"
sendOnEnter: "按下 Enter 發é€è¨Šæ¯"
@@ -1543,7 +1553,7 @@ _initialAccountSetting:
theseSettingsCanEditLater: "這裡的設定å¯ä»¥åœ¨ä¹‹å¾Œè®Šæ›´ã€‚"
youCanEditMoreSettingsInSettingsPageLater: "除此之外,還å¯ä»¥åœ¨ã€Œè¨­å®šã€é é¢é€²è¡Œå„種設定。之後請確èªçœ‹çœ‹ã€‚"
followUsers: "為了構築時間軸,試著追隨您感興趣的使用者å§ã€‚"
- pushNotificationDescription: "啟用推é€é€šçŸ¥å¾Œï¼Œå°±å¯ä»¥åœ¨è£ç½®ä¸ŠæŽ¥æ”¶ä¾†è‡ª{name}的通知了。"
+ pushNotificationDescription: "啟用推é€é€šçŸ¥å¾Œï¼Œå°±å¯ä»¥åœ¨è£ç½®ä¸ŠæŽ¥æ”¶ä¾†è‡ª {name} 的通知了。"
initialAccountSettingCompleted: "åˆå§‹è¨­å®šå®Œæˆäº†ï¼"
haveFun: "盡情享å—{name}å§ï¼"
youCanContinueTutorial: "您å¯ä»¥ç¹¼çºŒå­¸ç¿’如何使用{name}(Misskey),也å¯ä»¥å°±æ­¤æ‰“ä½ï¼Œç«‹å³é–‹å§‹ä½¿ç”¨ã€‚"
@@ -1632,6 +1642,10 @@ _serverSettings:
fanoutTimelineDbFallback: "資料庫的回退"
fanoutTimelineDbFallbackDescription: "若啟用,在時間軸沒有快å–的情æ³ä¸‹å°‡åŸ·è¡Œå›žé€€è™•ç†ä»¥é¡å¤–查詢資料庫。若åœç”¨ï¼Œå¯ä»¥é€éŽä¸åŸ·è¡Œå›žé€€è™•ç†ä¾†é€²ä¸€æ­¥æ¸›å°‘伺æœå™¨çš„è² è·ï¼Œä½†æœƒé™åˆ¶å¯å–得的時間軸範åœã€‚"
reactionsBufferingDescription: "啟用時,å¯ä»¥é¡¯è‘—æé«˜å»ºç«‹å應時的效能並減少資料庫的負載。 但是,Redis è¨˜æ†¶é«”ä½¿ç”¨é‡æœƒå¢žåŠ ã€‚"
+ remoteNotesCleaning: "自動清除é ç«¯ç™¼ä½ˆå…§å®¹"
+ remoteNotesCleaning_description: "å•Ÿç”¨å¾Œï¼Œç³»çµ±æœƒå®šæœŸæ¸…ç†æœªè¢«åƒç…§çš„舊é ç«¯è²¼æ–‡ï¼Œä»¥æŠ‘制資料庫的膨脹。"
+ remoteNotesCleaningMaxProcessingDuration: "清ç†ä½œæ¥­çš„æœ€é•·æŒçºŒæ™‚é–“"
+ remoteNotesCleaningExpiryDaysForEachNotes: "貼文最短ä¿ç•™å¤©æ•¸"
inquiryUrl: "è¯çµ¡è¡¨å–®ç¶²å€"
inquiryUrlDescription: "指定伺æœå™¨é‹ç‡Ÿè€…çš„è¯çµ¡è¡¨å–®ç¶²å€ï¼Œæˆ–包å«é‹ç‡Ÿè€…è¯çµ¡è³‡è¨Šç¶²é çš„ç¶²å€ã€‚"
openRegistration: "å…許建立帳戶"
@@ -1650,6 +1664,11 @@ _serverSettings:
userGeneratedContentsVisibilityForVisitor: "使用者建立的內容å°è¨ªå®¢çš„公開範åœ"
userGeneratedContentsVisibilityForVisitor_description: "這有助於防止一些å•題的發生,例如未經é©ç•¶å¯©æ ¸çš„ä¸é©ç•¶é ç«¯å…§å®¹ç„¡æ„中é€éŽæ‚¨è‡ªå·±çš„伺æœå™¨ç™¼ä½ˆåˆ°ç¶²éš›ç¶²è·¯ä¸Šã€‚"
userGeneratedContentsVisibilityForVisitor_description2: "包括伺æœå™¨æŽ¥æ”¶åˆ°çš„é ç«¯å…§å®¹åœ¨å…§ï¼Œç„¡æ¢ä»¶åœ°å°‡ä¼ºæœå™¨å…§æ‰€æœ‰å…§å®¹å…¬é–‹åˆ°ç¶²éš›ç¶²è·¯ä¸Šæ˜¯å…·æœ‰é¢¨éšªçš„ã€‚ç‰¹åˆ¥æ˜¯å°æ–¼ä¸äº†è§£åˆ†æ•£å¼æž¶æ§‹ç‰¹æ€§çš„ç€è¦½è€…來說,他們å¯èƒ½æœƒèª¤ä»¥ç‚ºé€™äº›é ç«¯å…§å®¹æ˜¯ç”±è©²ä¼ºæœå™¨æ‰€å‰µå»ºçš„,因此需è¦ç‰¹åˆ¥ç•™æ„。"
+ restartServerSetupWizardConfirm_title: "è¦é‡æ–°åŸ·è¡Œä¼ºæœå™¨çš„åˆå§‹è¨­å®šç²¾éˆå—Žï¼Ÿ"
+ restartServerSetupWizardConfirm_text: "ç•¶å‰çš„部分設定將會被é‡è¨­ã€‚"
+ entrancePageStyle: "å…¥å£é é¢çš„æ¨£å¼"
+ showTimelineForVisitor: "顯示時間軸"
+ showActivitiesForVisitor: "顯示活動"
_userGeneratedContentsVisibilityForVisitor:
all: "全部公開\n"
localOnly: "僅公開本地內容,é ç«¯å…§å®¹å‰‡ä¸å…¬é–‹\n"
@@ -1986,6 +2005,7 @@ _role:
descriptionOfRateLimitFactor: "值越å°é™åˆ¶è¶Šå°‘,值越大é™åˆ¶è¶Šå¤šã€‚"
canHideAds: "ä¸é¡¯ç¤ºå»£å‘Š"
canSearchNotes: "å¯å¦æœå°‹è²¼æ–‡"
+ canSearchUsers: "å¯ä½¿ç”¨ä½¿ç”¨è€…æœå°‹åŠŸèƒ½"
canUseTranslator: "使用翻譯功能"
avatarDecorationLimit: "é ­åƒå¯æŽ›ä¸Šçš„æœ€å¤§è£é£¾æ•¸é‡"
canImportAntennas: "å…許匯入天線"
@@ -2258,6 +2278,7 @@ _time:
minute: "分é˜"
hour: "å°æ™‚"
day: "æ—¥"
+ month: "個月"
_2fa:
alreadyRegistered: "æ­¤è£ç½®å·²è¢«è¨»å†ŠéŽäº†"
registerTOTP: "開始設定驗證應用程å¼"
@@ -3060,6 +3081,7 @@ _bootErrors:
otherOption1: "刪除用戶端設定和快å–"
otherOption2: "啟動簡易用戶端"
otherOption3: "啟動修復工具"
+ otherOption4: "以安全模å¼å•Ÿå‹• Misskey"
_search:
searchScopeAll: "全部"
searchScopeLocal: "本地"
@@ -3096,6 +3118,8 @@ _serverSetupWizard:
doYouConnectToFediverse_description1: "連接到由分散型伺æœå™¨æ§‹æˆçš„網絡(è¯é‚¦å®‡å®™ï¼‰å¾Œï¼Œæ‚¨å¯ä»¥èˆ‡å…¶ä»–伺æœå™¨é€²è¡Œå…§å®¹çš„互相交æµã€‚\n"
doYouConnectToFediverse_description2: "連接到è¯é‚¦å®‡å®™è¢«ç¨±ç‚ºã€Œè¯é‚¦ã€ã€‚\n"
youCanConfigureMoreFederationSettingsLater: "您å¯ä»¥åœ¨ç¨å¾Œé€²è¡Œæ›´é«˜ç´šçš„設定,例如指定å¯ä»¥è¯ç¹«çš„伺æœå™¨ç­‰ã€‚\n"
+ remoteContentsCleaning: "è‡ªå‹•æ¸…ç†æŽ¥æ”¶çš„å…§å®¹"
+ remoteContentsCleaning_description: "進行è¯é‚¦å¾Œï¼ŒæœƒæŒçºŒæŽ¥æ”¶å¤§é‡å…§å®¹ã€‚啟用自動清ç†åŠŸèƒ½å¾Œï¼Œç³»çµ±æœƒè‡ªå‹•å¾žä¼ºæœå™¨ä¸­åˆªé™¤æœªè¢«åƒç…§çš„éŽæ™‚內容,以節çœå„²å­˜ç©ºé–“。"
adminInfo: "管ç†å“¡è³‡è¨Š"
adminInfo_description: "設定用於接收查詢的管ç†è€…資訊。\n"
adminInfo_mustBeFilled: "當設置為開放伺æœå™¨æˆ–啟用è¯é‚¦æ™‚,必須填寫此資訊。\n"
@@ -3148,10 +3172,10 @@ _watermarkEditor:
type: "類型"
image: "圖片"
advanced: "進階"
+ angle: "角度"
stripe: "æ¢ç´‹"
stripeWidth: "ç·šæ¢å¯¬åº¦"
stripeFrequency: "ç·šæ¢æ•¸é‡"
- angle: "角度"
polkadot: "æ³¢å¡åœ“點"
checker: "棋盤格"
polkadotMainDotOpacity: "主圓點的ä¸é€æ˜Žåº¦"
@@ -3163,6 +3187,7 @@ _imageEffector:
title: "特效"
addEffect: "新增特效"
discardChangesConfirm: "æ¨æ£„更改並退出嗎?"
+ nothingToConfigure: "ç„¡å¯è¨­å®šçš„é …ç›®"
_fxs:
chromaticAberration: "色差"
glitch: "異常雜訊效果"
@@ -3180,6 +3205,38 @@ _imageEffector:
checker: "棋盤格"
blockNoise: "阻擋雜訊"
tearing: "撕裂"
+ _fxProps:
+ angle: "角度"
+ scale: "大å°"
+ size: "大å°"
+ color: "é¡è‰²"
+ opacity: "逿˜Žåº¦"
+ normalize: "æ­£è¦åŒ–"
+ amount: "數é‡"
+ lightness: "亮度"
+ contrast: "å°æ¯”度"
+ hue: "色相"
+ brightness: "亮度"
+ saturation: "彩度"
+ max: "最大值"
+ min: "最å°å€¼"
+ direction: "æ–¹å‘"
+ phase: "相ä½"
+ frequency: "頻率"
+ strength: "強度"
+ glitchChannelShift: "åç§»"
+ seed: "種å­å€¼"
+ redComponent: "紅色æˆåˆ†"
+ greenComponent: "綠色æˆåˆ†"
+ blueComponent: "é’色æˆåˆ†"
+ threshold: "閾值"
+ centerX: "X中心座標"
+ centerY: "Y中心座標"
+ zoomLinesSmoothing: "平滑化"
+ zoomLinesSmoothingDescription: "平滑化與集中線寬度設定ä¸èƒ½åŒæ™‚使用。"
+ zoomLinesThreshold: "集中線的寬度"
+ zoomLinesMaskSize: "中心直徑"
+ zoomLinesBlack: "變æˆé»‘色"
drafts: "è‰ç¨¿\n"
_drafts:
select: "鏿“‡è‰æ§"
diff --git a/package.json b/package.json
index 7bfca56065..e4cec6aea6 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,12 @@
{
"name": "misskey",
- "version": "2025.7.0",
+ "version": "2025.8.0",
"codename": "nasubi",
"repository": {
"type": "git",
"url": "https://github.com/misskey-dev/misskey.git"
},
- "packageManager": "pnpm@10.13.1",
+ "packageManager": "pnpm@10.15.0",
"workspaces": [
"packages/frontend-shared",
"packages/frontend",
@@ -27,6 +27,7 @@
"build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json --no-build && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
"start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js",
"start:test": "ncp ./.github/misskey/test.yml ./.config/test.yml && cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js",
+ "cli": "cd packages/backend && pnpm cli",
"init": "pnpm migrate",
"migrate": "cd packages/backend && pnpm migrate",
"revert": "cd packages/backend && pnpm revert",
@@ -52,8 +53,8 @@
"lodash": "4.17.21"
},
"dependencies": {
- "cssnano": "7.1.0",
- "esbuild": "0.25.6",
+ "cssnano": "7.1.1",
+ "esbuild": "0.25.9",
"execa": "9.6.0",
"fast-glob": "3.3.3",
"glob": "11.0.3",
@@ -62,20 +63,20 @@
"postcss": "8.5.6",
"tar": "7.4.3",
"terser": "5.43.1",
- "typescript": "5.8.3"
+ "typescript": "5.9.2"
},
"devDependencies": {
"@misskey-dev/eslint-plugin": "2.1.0",
- "@types/node": "22.16.4",
- "@typescript-eslint/eslint-plugin": "8.37.0",
- "@typescript-eslint/parser": "8.37.0",
+ "@types/node": "22.17.2",
+ "@typescript-eslint/eslint-plugin": "8.40.0",
+ "@typescript-eslint/parser": "8.40.0",
"cross-env": "7.0.3",
- "cypress": "14.5.2",
- "eslint": "9.31.0",
+ "cypress": "14.5.4",
+ "eslint": "9.34.0",
"globals": "16.3.0",
"ncp": "2.0.0",
- "pnpm": "10.13.1",
- "start-server-and-test": "2.0.12"
+ "pnpm": "10.15.0",
+ "start-server-and-test": "2.0.13"
},
"optionalDependencies": {
"@tensorflow/tfjs-core": "4.22.0"
diff --git a/packages/backend/migration/1753863104203-remoteNotesCleaning.js b/packages/backend/migration/1753863104203-remoteNotesCleaning.js
new file mode 100644
index 0000000000..8250d8d0df
--- /dev/null
+++ b/packages/backend/migration/1753863104203-remoteNotesCleaning.js
@@ -0,0 +1,20 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class RemoteNotesCleaning1753863104203 {
+ name = 'RemoteNotesCleaning1753863104203'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" ADD "enableRemoteNotesCleaning" boolean NOT NULL DEFAULT false`);
+ await queryRunner.query('ALTER TABLE "meta" ADD "remoteNotesCleaningMaxProcessingDurationInMinutes" integer NOT NULL DEFAULT \'60\'');
+ await queryRunner.query('ALTER TABLE "meta" ADD "remoteNotesCleaningExpiryDaysForEachNotes" integer NOT NULL DEFAULT \'90\'');
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query('ALTER TABLE "meta" DROP COLUMN "remoteNotesCleaningExpiryDaysForEachNotes"');
+ await queryRunner.query('ALTER TABLE "meta" DROP COLUMN "remoteNotesCleaningMaxProcessingDurationInMinutes"');
+ await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableRemoteNotesCleaning"`);
+ }
+}
diff --git a/packages/backend/migration/1753868431598-remove_note_constraints.js b/packages/backend/migration/1753868431598-remove_note_constraints.js
new file mode 100644
index 0000000000..29540cf9de
--- /dev/null
+++ b/packages/backend/migration/1753868431598-remove_note_constraints.js
@@ -0,0 +1,18 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class RemoveNoteConstraints1753868431598 {
+ name = 'RemoveNoteConstraints1753868431598'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_52ccc804d7c69037d558bac4c96"`);
+ await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_17cb3553c700a4985dff5a30ff5"`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_17cb3553c700a4985dff5a30ff5" FOREIGN KEY ("replyId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
+ await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_52ccc804d7c69037d558bac4c96" FOREIGN KEY ("renoteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
+ }
+}
diff --git a/packages/backend/migration/1754019326356-tweakDefaultFederationSettings.js b/packages/backend/migration/1754019326356-tweakDefaultFederationSettings.js
new file mode 100644
index 0000000000..1051b18e1b
--- /dev/null
+++ b/packages/backend/migration/1754019326356-tweakDefaultFederationSettings.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class TweakDefaultFederationSettings1754019326356 {
+ name = 'TweakDefaultFederationSettings1754019326356'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "federation" SET DEFAULT 'none'`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "federation" SET DEFAULT 'all'`);
+ }
+}
diff --git a/packages/backend/migration/1755168347001-PageCountInNote.js b/packages/backend/migration/1755168347001-PageCountInNote.js
new file mode 100644
index 0000000000..9f1894ab2f
--- /dev/null
+++ b/packages/backend/migration/1755168347001-PageCountInNote.js
@@ -0,0 +1,58 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class PageCountInNote1755168347001 {
+ name = 'PageCountInNote1755168347001'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "note" ADD "pageCount" smallint NOT NULL DEFAULT '0'`);
+
+ // Update existing notes
+ // block_list CTE collects all page blocks on the pages including child blocks in the section blocks.
+ // The clipped_notes CTE counts how many distinct pages each note block is referenced in.
+ // Finally, we update the note table with the count of pages for each referenced note.
+ await queryRunner.query(`
+ WITH RECURSIVE block_list AS (
+ (
+ SELECT
+ page.id as page_id,
+ block as block
+ FROM page
+ CROSS JOIN LATERAL jsonb_array_elements(page.content) block
+ WHERE block->>'type' = 'note' OR block->>'type' = 'section'
+ )
+ UNION ALL
+ (
+ SELECT
+ block_list.page_id,
+ child_block AS block
+ FROM LATERAL (
+ SELECT page_id, block
+ FROM block_list
+ WHERE block_list.block->>'type' = 'section'
+ ) block_list
+ CROSS JOIN LATERAL jsonb_array_elements(block_list.block->'children') child_block
+ WHERE child_block->>'type' = 'note' OR child_block->>'type' = 'section'
+ )
+ ),
+ clipped_notes AS (
+ SELECT
+ (block->>'note') AS note_id,
+ COUNT(distinct block_list.page_id) AS count
+ FROM block_list
+ WHERE block_list.block->>'type' = 'note'
+ GROUP BY block->>'note'
+ )
+ UPDATE note
+ SET "pageCount" = clipped_notes.count
+ FROM clipped_notes
+ WHERE note.id = clipped_notes.note_id;
+ `);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "pageCount"`);
+ }
+}
diff --git a/packages/backend/migration/1755574887486-entrancePageStyle.js b/packages/backend/migration/1755574887486-entrancePageStyle.js
new file mode 100644
index 0000000000..ba40764b94
--- /dev/null
+++ b/packages/backend/migration/1755574887486-entrancePageStyle.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class EntrancePageStyle1755574887486 {
+ name = 'EntrancePageStyle1755574887486'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" ADD "clientOptions" jsonb NOT NULL DEFAULT '{}'`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "clientOptions"`);
+ }
+}
diff --git a/packages/backend/migration/1756062689648-NonCascadingPageEyeCatching.js b/packages/backend/migration/1756062689648-NonCascadingPageEyeCatching.js
new file mode 100644
index 0000000000..8554cc4304
--- /dev/null
+++ b/packages/backend/migration/1756062689648-NonCascadingPageEyeCatching.js
@@ -0,0 +1,18 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class NonCascadingPageEyeCatching1756062689648 {
+ name = 'NonCascadingPageEyeCatching1756062689648'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "page" DROP CONSTRAINT "FK_a9ca79ad939bf06066b81c9d3aa"`);
+ await queryRunner.query(`ALTER TABLE "page" ADD CONSTRAINT "FK_a9ca79ad939bf06066b81c9d3aa" FOREIGN KEY ("eyeCatchingImageId") REFERENCES "drive_file"("id") ON DELETE SET NULL ON UPDATE NO ACTION`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "page" DROP CONSTRAINT "FK_a9ca79ad939bf06066b81c9d3aa"`);
+ await queryRunner.query(`ALTER TABLE "page" ADD CONSTRAINT "FK_a9ca79ad939bf06066b81c9d3aa" FOREIGN KEY ("eyeCatchingImageId") REFERENCES "drive_file"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
+ }
+}
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 1a25f63b83..89e04f4da4 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -4,13 +4,14 @@
"private": true,
"type": "module",
"engines": {
- "node": "^20.18.1 || ^22.0.0"
+ "node": "^22.15.0"
},
"scripts": {
"start": "node ./built/boot/entry.js",
"start:test": "cross-env NODE_ENV=test node ./built/boot/entry.js",
"migrate": "pnpm typeorm migration:run -d ormconfig.js",
"revert": "pnpm typeorm migration:revert -d ormconfig.js",
+ "cli": "node ./built/boot/cli.js",
"check:connect": "node ./scripts/check_connect.js",
"build": "swc src -d built -D --strip-leading-paths",
"build:test": "swc test-server -d built-test -D --config-file test-server/.swcrc --strip-leading-paths",
@@ -38,17 +39,17 @@
},
"optionalDependencies": {
"@swc/core-android-arm64": "1.3.11",
- "@swc/core-darwin-arm64": "1.12.0",
- "@swc/core-darwin-x64": "1.12.0",
+ "@swc/core-darwin-arm64": "1.13.4",
+ "@swc/core-darwin-x64": "1.13.4",
"@swc/core-freebsd-x64": "1.3.11",
- "@swc/core-linux-arm-gnueabihf": "1.12.0",
- "@swc/core-linux-arm64-gnu": "1.12.0",
- "@swc/core-linux-arm64-musl": "1.12.0",
- "@swc/core-linux-x64-gnu": "1.12.0",
- "@swc/core-linux-x64-musl": "1.12.0",
- "@swc/core-win32-arm64-msvc": "1.12.0",
- "@swc/core-win32-ia32-msvc": "1.12.0",
- "@swc/core-win32-x64-msvc": "1.12.0",
+ "@swc/core-linux-arm-gnueabihf": "1.13.4",
+ "@swc/core-linux-arm64-gnu": "1.13.4",
+ "@swc/core-linux-arm64-musl": "1.13.4",
+ "@swc/core-linux-x64-gnu": "1.13.4",
+ "@swc/core-linux-x64-musl": "1.13.4",
+ "@swc/core-win32-arm64-msvc": "1.13.4",
+ "@swc/core-win32-ia32-msvc": "1.13.4",
+ "@swc/core-win32-x64-msvc": "1.13.4",
"@tensorflow/tfjs": "4.22.0",
"@tensorflow/tfjs-node": "4.22.0",
"bufferutil": "4.0.9",
@@ -68,9 +69,9 @@
"utf-8-validate": "6.0.5"
},
"dependencies": {
- "@aws-sdk/client-s3": "3.826.0",
- "@aws-sdk/lib-storage": "3.826.0",
- "@discordapp/twemoji": "15.1.0",
+ "@aws-sdk/client-s3": "3.873.0",
+ "@aws-sdk/lib-storage": "3.873.0",
+ "@discordapp/twemoji": "16.0.1",
"@fastify/accepts": "5.0.2",
"@fastify/cookie": "11.0.2",
"@fastify/cors": "10.1.0",
@@ -80,20 +81,20 @@
"@fastify/static": "8.2.0",
"@fastify/view": "10.0.2",
"@misskey-dev/sharp-read-bmp": "1.2.0",
- "@misskey-dev/summaly": "5.2.1",
- "@napi-rs/canvas": "0.1.71",
- "@nestjs/common": "11.1.3",
- "@nestjs/core": "11.1.3",
- "@nestjs/testing": "11.1.3",
+ "@misskey-dev/summaly": "5.2.3",
+ "@napi-rs/canvas": "0.1.77",
+ "@nestjs/common": "11.1.6",
+ "@nestjs/core": "11.1.6",
+ "@nestjs/testing": "11.1.6",
"@peertube/http-signature": "1.7.0",
"@sentry/node": "8.55.0",
"@sentry/profiling-node": "8.55.0",
"@simplewebauthn/server": "12.0.0",
"@sinonjs/fake-timers": "11.3.1",
"@smithy/node-http-handler": "2.5.0",
- "@swc/cli": "0.7.7",
- "@swc/core": "1.12.0",
- "@twemoji/parser": "15.1.1",
+ "@swc/cli": "0.7.8",
+ "@swc/core": "1.13.4",
+ "@twemoji/parser": "16.0.0",
"@types/redis-info": "3.0.3",
"accepts": "1.3.8",
"ajv": "8.17.1",
@@ -102,10 +103,10 @@
"bcryptjs": "2.4.3",
"blurhash": "2.0.5",
"body-parser": "1.20.3",
- "bullmq": "5.53.2",
+ "bullmq": "5.58.1",
"cacheable-lookup": "7.0.0",
"cbor": "9.0.2",
- "chalk": "5.4.1",
+ "chalk": "5.6.0",
"chalk-template": "1.1.0",
"chokidar": "4.0.3",
"cli-highlight": "2.1.11",
@@ -113,18 +114,18 @@
"content-disposition": "0.5.4",
"date-fns": "2.30.0",
"deep-email-validator": "0.1.21",
- "fastify": "5.3.3",
+ "fastify": "5.5.0",
"fastify-raw-body": "5.0.0",
"feed": "4.2.2",
"file-type": "19.6.0",
"fluent-ffmpeg": "2.1.3",
- "form-data": "4.0.3",
+ "form-data": "4.0.4",
"got": "14.4.7",
"happy-dom": "16.8.1",
"hpagent": "1.2.0",
"htmlescape": "1.1.1",
"http-link-header": "1.1.3",
- "ioredis": "5.6.1",
+ "ioredis": "5.7.0",
"ip-cidr": "4.0.2",
"ipaddr.js": "2.2.0",
"is-svg": "5.1.0",
@@ -134,9 +135,9 @@
"jsonld": "8.3.3",
"jsrsasign": "11.1.0",
"juice": "11.0.1",
- "meilisearch": "0.51.0",
- "mfm-js": "0.24.0",
- "microformats-parser": "2.0.3",
+ "meilisearch": "0.52.0",
+ "mfm-js": "0.25.0",
+ "microformats-parser": "2.0.4",
"mime-types": "2.1.35",
"misskey-js": "workspace:*",
"misskey-reversi": "workspace:*",
@@ -150,9 +151,9 @@
"oauth2orize": "1.12.0",
"oauth2orize-pkce": "0.1.2",
"os-utils": "0.0.14",
- "otpauth": "9.4.0",
+ "otpauth": "9.4.1",
"parse5": "7.3.0",
- "pg": "8.16.0",
+ "pg": "8.16.3",
"pkce-challenge": "4.1.0",
"probe-image-size": "7.2.3",
"promise-limit": "2.7.0",
@@ -174,25 +175,25 @@
"slacc": "0.0.10",
"strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0",
- "systeminformation": "5.27.1",
+ "systeminformation": "5.27.7",
"tinycolor2": "1.6.0",
- "tmp": "0.2.3",
+ "tmp": "0.2.5",
"tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0",
- "typeorm": "0.3.24",
- "typescript": "5.8.3",
+ "typeorm": "0.3.26",
+ "typescript": "5.9.2",
"ulid": "2.4.0",
"vary": "1.1.2",
"web-push": "3.6.7",
- "ws": "8.18.2",
+ "ws": "8.18.3",
"xev": "3.0.2"
},
"devDependencies": {
"@jest/globals": "29.7.0",
- "@nestjs/platform-express": "10.4.19",
- "@sentry/vue": "9.28.0",
+ "@nestjs/platform-express": "10.4.20",
+ "@sentry/vue": "9.46.0",
"@simplewebauthn/types": "12.0.0",
- "@swc/jest": "0.2.38",
+ "@swc/jest": "0.2.39",
"@types/accepts": "1.3.7",
"@types/archiver": "6.0.3",
"@types/bcryptjs": "2.4.6",
@@ -209,12 +210,12 @@
"@types/jsrsasign": "10.5.15",
"@types/mime-types": "2.1.4",
"@types/ms": "0.7.34",
- "@types/node": "22.15.31",
- "@types/nodemailer": "6.4.17",
+ "@types/node": "22.17.2",
+ "@types/nodemailer": "6.4.19",
"@types/oauth": "0.9.6",
"@types/oauth2orize": "1.11.5",
"@types/oauth2orize-pkce": "0.1.2",
- "@types/pg": "8.15.4",
+ "@types/pg": "8.15.5",
"@types/pug": "2.0.10",
"@types/qrcode": "1.5.5",
"@types/random-seed": "0.3.5",
@@ -230,11 +231,11 @@
"@types/vary": "1.1.3",
"@types/web-push": "3.6.4",
"@types/ws": "8.18.1",
- "@typescript-eslint/eslint-plugin": "8.34.0",
- "@typescript-eslint/parser": "8.34.0",
+ "@typescript-eslint/eslint-plugin": "8.40.0",
+ "@typescript-eslint/parser": "8.40.0",
"aws-sdk-client-mock": "4.1.0",
"cross-env": "7.0.3",
- "eslint-plugin-import": "2.31.0",
+ "eslint-plugin-import": "2.32.0",
"execa": "8.0.1",
"fkill": "9.0.0",
"jest": "29.7.0",
@@ -242,6 +243,6 @@
"nodemon": "3.1.10",
"pid-port": "1.0.2",
"simple-oauth2": "5.1.0",
- "supertest": "7.1.1"
+ "supertest": "7.1.4"
}
}
diff --git a/packages/backend/scripts/dev.mjs b/packages/backend/scripts/dev.mjs
index a3e0558abd..023eb7eae6 100644
--- a/packages/backend/scripts/dev.mjs
+++ b/packages/backend/scripts/dev.mjs
@@ -42,7 +42,7 @@ async function killProc() {
'./node_modules/nodemon/bin/nodemon.js',
[
'-w', 'src',
- '-e', 'ts,js,mjs,cjs,json',
+ '-e', 'ts,js,mjs,cjs,json,pug',
'--exec', 'pnpm', 'run', 'build',
],
{
diff --git a/packages/backend/src/boot/cli.ts b/packages/backend/src/boot/cli.ts
new file mode 100644
index 0000000000..a5618f8152
--- /dev/null
+++ b/packages/backend/src/boot/cli.ts
@@ -0,0 +1,49 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import 'reflect-metadata';
+import { EventEmitter } from 'node:events';
+import { NestFactory } from '@nestjs/core';
+import { CommandModule } from '@/cli/CommandModule.js';
+import { NestLogger } from '@/NestLogger.js';
+import { CommandService } from '@/cli/CommandService.js';
+
+process.title = 'Misskey Cli';
+
+Error.stackTraceLimit = Infinity;
+EventEmitter.defaultMaxListeners = 128;
+
+const app = await NestFactory.createApplicationContext(CommandModule, {
+ logger: new NestLogger(),
+});
+
+const commandService = app.get(CommandService);
+
+const command = process.argv[2] ?? 'help';
+
+switch (command) {
+ case 'help': {
+ console.log('Available commands:');
+ console.log(' help - Displays this help message');
+ console.log(' reset-captcha - Resets the captcha');
+ break;
+ }
+ case 'ping': {
+ await commandService.ping();
+ break;
+ }
+ case 'reset-captcha': {
+ await commandService.resetCaptcha();
+ console.log('Captcha has been reset.');
+ break;
+ }
+ default: {
+ console.error(`Unrecognized command: ${command}`);
+ console.error('Use "help" to see available commands.');
+ process.exit(1);
+ }
+}
+
+process.exit(0);
diff --git a/packages/backend/src/cli/CommandModule.ts b/packages/backend/src/cli/CommandModule.ts
new file mode 100644
index 0000000000..f4b1d25c18
--- /dev/null
+++ b/packages/backend/src/cli/CommandModule.ts
@@ -0,0 +1,23 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Module } from '@nestjs/common';
+import { CoreModule } from '@/core/CoreModule.js';
+import { GlobalModule } from '@/GlobalModule.js';
+import { CommandService } from './CommandService.js';
+
+@Module({
+ imports: [
+ GlobalModule,
+ CoreModule,
+ ],
+ providers: [
+ CommandService,
+ ],
+ exports: [
+ CommandService,
+ ],
+})
+export class CommandModule {}
diff --git a/packages/backend/src/cli/CommandService.ts b/packages/backend/src/cli/CommandService.ts
new file mode 100644
index 0000000000..cdb2a9f382
--- /dev/null
+++ b/packages/backend/src/cli/CommandService.ts
@@ -0,0 +1,49 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import type { Config } from '@/config.js';
+import { DI } from '@/di-symbols.js';
+import type Logger from '@/logger.js';
+import { bindThis } from '@/decorators.js';
+import { MetaService } from '@/core/MetaService.js';
+
+@Injectable()
+export class CommandService {
+ private logger: Logger;
+
+ constructor(
+ @Inject(DI.config)
+ private config: Config,
+
+ private metaService: MetaService,
+ ) {
+ }
+
+ @bindThis
+ public async ping() {
+ console.log('pong');
+ }
+
+ @bindThis
+ public async resetCaptcha() {
+ await this.metaService.update({
+ enableHcaptcha: false,
+ hcaptchaSiteKey: null,
+ hcaptchaSecretKey: null,
+ enableMcaptcha: false,
+ mcaptchaSitekey: null,
+ mcaptchaSecretKey: null,
+ mcaptchaInstanceUrl: null,
+ enableRecaptcha: false,
+ recaptchaSiteKey: null,
+ recaptchaSecretKey: null,
+ enableTurnstile: false,
+ turnstileSiteKey: null,
+ turnstileSecretKey: null,
+ enableTestcaptcha: false,
+ });
+ }
+}
diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts
index 9031096745..f71f1d7e34 100644
--- a/packages/backend/src/config.ts
+++ b/packages/backend/src/config.ts
@@ -184,9 +184,9 @@ export type Config = {
authUrl: string;
driveUrl: string;
userAgent: string;
- frontendEntry: string;
+ frontendEntry: { file: string | null };
frontendManifestExists: boolean;
- frontendEmbedEntry: string;
+ frontendEmbedEntry: { file: string | null };
frontendEmbedManifestExists: boolean;
mediaProxy: string;
externalMediaProxyEnabled: boolean;
@@ -235,10 +235,10 @@ export function loadConfig(): Config {
const frontendEmbedManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_embed_vite_/manifest.json');
const frontendManifest = frontendManifestExists ?
JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_vite_/manifest.json`, 'utf-8'))
- : { 'src/_boot_.ts': { file: 'src/_boot_.ts' } };
+ : { 'src/_boot_.ts': { file: null } };
const frontendEmbedManifest = frontendEmbedManifestExists ?
JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_embed_vite_/manifest.json`, 'utf-8'))
- : { 'src/boot.ts': { file: 'src/boot.ts' } };
+ : { 'src/boot.ts': { file: null } };
const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source;
diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts
index 0c0c5d3a39..a30bff0fe4 100644
--- a/packages/backend/src/core/CoreModule.ts
+++ b/packages/backend/src/core/CoreModule.ts
@@ -78,6 +78,7 @@ import { ChannelFollowingService } from './ChannelFollowingService.js';
import { ChatService } from './ChatService.js';
import { RegistryApiService } from './RegistryApiService.js';
import { ReversiService } from './ReversiService.js';
+import { PageService } from './PageService.js';
import { ChartLoggerService } from './chart/ChartLoggerService.js';
import FederationChart from './chart/charts/federation.js';
@@ -227,6 +228,7 @@ const $ChannelFollowingService: Provider = { provide: 'ChannelFollowingService',
const $ChatService: Provider = { provide: 'ChatService', useExisting: ChatService };
const $RegistryApiService: Provider = { provide: 'RegistryApiService', useExisting: RegistryApiService };
const $ReversiService: Provider = { provide: 'ReversiService', useExisting: ReversiService };
+const $PageService: Provider = { provide: 'PageService', useExisting: PageService };
const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService };
const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart };
@@ -379,6 +381,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
ChatService,
RegistryApiService,
ReversiService,
+ PageService,
ChartLoggerService,
FederationChart,
@@ -527,6 +530,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$ChatService,
$RegistryApiService,
$ReversiService,
+ $PageService,
$ChartLoggerService,
$FederationChart,
@@ -676,6 +680,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
ChatService,
RegistryApiService,
ReversiService,
+ PageService,
FederationChart,
NotesChart,
@@ -822,6 +827,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$ChatService,
$RegistryApiService,
$ReversiService,
+ $PageService,
$FederationChart,
$NotesChart,
diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts
index 97b617096a..94c5691bf4 100644
--- a/packages/backend/src/core/FanoutTimelineEndpointService.ts
+++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts
@@ -20,6 +20,8 @@ import { CacheService } from '@/core/CacheService.js';
import { isReply } from '@/misc/is-reply.js';
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
+type NoteFilter = (note: MiNote) => boolean;
+
type TimelineOptions = {
untilId: string | null,
sinceId: string | null,
@@ -28,7 +30,7 @@ type TimelineOptions = {
me?: { id: MiUser['id'] } | undefined | null,
useDbFallback: boolean,
redisTimelines: FanoutTimelineName[],
- noteFilter?: (note: MiNote) => boolean,
+ noteFilter?: NoteFilter,
alwaysIncludeMyNotes?: boolean;
ignoreAuthorFromBlock?: boolean;
ignoreAuthorFromMute?: boolean;
@@ -79,7 +81,7 @@ export class FanoutTimelineEndpointService {
const shouldFallbackToDb = noteIds.length === 0 || ps.sinceId != null && ps.sinceId < oldestNoteId;
if (!shouldFallbackToDb) {
- let filter = ps.noteFilter ?? (_note => true);
+ let filter = ps.noteFilter ?? (_note => true) as NoteFilter;
if (ps.alwaysIncludeMyNotes && ps.me) {
const me = ps.me;
@@ -145,15 +147,11 @@ export class FanoutTimelineEndpointService {
{
const parentFilter = filter;
filter = (note) => {
- const noteJoined = note as MiNote & {
- renoteUser: MiUser | null;
- replyUser: MiUser | null;
- };
if (!ps.ignoreAuthorFromUserSuspension) {
if (note.user!.isSuspended) return false;
}
- if (note.userId !== note.renoteUserId && noteJoined.renoteUser?.isSuspended) return false;
- if (note.userId !== note.replyUserId && noteJoined.replyUser?.isSuspended) return false;
+ if (note.userId !== note.renoteUserId && note.renote?.user?.isSuspended) return false;
+ if (note.userId !== note.replyUserId && note.reply?.user?.isSuspended) return false;
return parentFilter(note);
};
@@ -200,7 +198,7 @@ export class FanoutTimelineEndpointService {
return await ps.dbFallback(ps.untilId, ps.sinceId, ps.limit);
}
- private async getAndFilterFromDb(noteIds: string[], noteFilter: (note: MiNote) => boolean, idCompare: (a: string, b: string) => number): Promise<MiNote[]> {
+ private async getAndFilterFromDb(noteIds: string[], noteFilter: NoteFilter, idCompare: (a: string, b: string) => number): Promise<MiNote[]> {
const query = this.notesRepository.createQueryBuilder('note')
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
.innerJoinAndSelect('note.user', 'user')
diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts
index 3ddfe52045..f7973cbb66 100644
--- a/packages/backend/src/core/HttpRequestService.ts
+++ b/packages/backend/src/core/HttpRequestService.ts
@@ -6,6 +6,7 @@
import * as http from 'node:http';
import * as https from 'node:https';
import * as net from 'node:net';
+import * as stream from 'node:stream';
import ipaddr from 'ipaddr.js';
import CacheableLookup from 'cacheable-lookup';
import fetch from 'node-fetch';
@@ -26,12 +27,6 @@ export type HttpRequestSendOptions = {
validators?: ((res: Response) => void)[];
};
-declare module 'node:http' {
- interface Agent {
- createConnection(options: net.NetConnectOpts, callback?: (err: unknown, stream: net.Socket) => void): net.Socket;
- }
-}
-
class HttpRequestServiceAgent extends http.Agent {
constructor(
private config: Config,
@@ -41,11 +36,11 @@ class HttpRequestServiceAgent extends http.Agent {
}
@bindThis
- public createConnection(options: net.NetConnectOpts, callback?: (err: unknown, stream: net.Socket) => void): net.Socket {
+ public createConnection(options: http.ClientRequestArgs, callback?: (err: Error | null, stream: stream.Duplex) => void): stream.Duplex {
const socket = super.createConnection(options, callback)
.on('connect', () => {
- const address = socket.remoteAddress;
- if (process.env.NODE_ENV === 'production') {
+ if (socket instanceof net.Socket && process.env.NODE_ENV === 'production') {
+ const address = socket.remoteAddress;
if (address && ipaddr.isValid(address)) {
if (this.isPrivateIp(address)) {
socket.destroy(new Error(`Blocked address: ${address}`));
@@ -80,11 +75,11 @@ class HttpsRequestServiceAgent extends https.Agent {
}
@bindThis
- public createConnection(options: net.NetConnectOpts, callback?: (err: unknown, stream: net.Socket) => void): net.Socket {
+ public createConnection(options: http.ClientRequestArgs, callback?: (err: Error | null, stream: stream.Duplex) => void): stream.Duplex {
const socket = super.createConnection(options, callback)
.on('connect', () => {
- const address = socket.remoteAddress;
- if (process.env.NODE_ENV === 'production') {
+ if (socket instanceof net.Socket && process.env.NODE_ENV === 'production') {
+ const address = socket.remoteAddress;
if (address && ipaddr.isValid(address)) {
if (this.isPrivateIp(address)) {
socket.destroy(new Error(`Blocked address: ${address}`));
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 469426f87e..1eefcfa054 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -421,7 +421,7 @@ export class NoteCreateService implements OnApplicationShutdown {
emojis,
userId: user.id,
localOnly: data.localOnly!,
- reactionAcceptance: data.reactionAcceptance,
+ reactionAcceptance: data.reactionAcceptance ?? null,
visibility: data.visibility as any,
visibleUserIds: data.visibility === 'specified'
? data.visibleUsers
@@ -483,7 +483,11 @@ export class NoteCreateService implements OnApplicationShutdown {
await this.notesRepository.insert(insert);
}
- return insert;
+ return {
+ ...insert,
+ reply: data.reply ?? null,
+ renote: data.renote ?? null,
+ };
} catch (e) {
// duplicate key error
if (isDuplicateKeyValueError(e)) {
diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts
index e394506a44..af1f0eda9a 100644
--- a/packages/backend/src/core/NoteDeleteService.ts
+++ b/packages/backend/src/core/NoteDeleteService.ts
@@ -62,7 +62,6 @@ export class NoteDeleteService {
*/
async delete(user: { id: MiUser['id']; uri: MiUser['uri']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, quiet = false, deleter?: MiUser) {
const deletedAt = new Date();
- const cascadingNotes = await this.findCascadingNotes(note);
if (note.replyId) {
await this.notesRepository.decrement({ id: note.replyId }, 'repliesCount', 1);
@@ -90,15 +89,6 @@ export class NoteDeleteService {
this.deliverToConcerned(user, note, content);
}
-
- // also deliver delete activity to cascaded notes
- const federatedLocalCascadingNotes = (cascadingNotes).filter(note => !note.localOnly && note.userHost == null); // filter out local-only notes
- for (const cascadingNote of federatedLocalCascadingNotes) {
- if (!cascadingNote.user) continue;
- if (!this.userEntityService.isLocalUser(cascadingNote.user)) continue;
- const content = this.apRendererService.addContext(this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${cascadingNote.id}`), cascadingNote.user));
- this.deliverToConcerned(cascadingNote.user, cascadingNote, content);
- }
//#endregion
this.notesChart.update(note, false);
@@ -118,9 +108,6 @@ export class NoteDeleteService {
}
}
- for (const cascadingNote of cascadingNotes) {
- this.searchService.unindexNote(cascadingNote);
- }
this.searchService.unindexNote(note);
await this.notesRepository.delete({
@@ -141,29 +128,6 @@ export class NoteDeleteService {
}
@bindThis
- private async findCascadingNotes(note: MiNote): Promise<MiNote[]> {
- const recursive = async (noteId: string): Promise<MiNote[]> => {
- const query = this.notesRepository.createQueryBuilder('note')
- .where('note.replyId = :noteId', { noteId })
- .orWhere(new Brackets(q => {
- q.where('note.renoteId = :noteId', { noteId })
- .andWhere('note.text IS NOT NULL');
- }))
- .leftJoinAndSelect('note.user', 'user');
- const replies = await query.getMany();
-
- return [
- replies,
- ...await Promise.all(replies.map(reply => recursive(reply.id))),
- ].flat();
- };
-
- const cascadingNotes: MiNote[] = await recursive(note.id);
-
- return cascadingNotes;
- }
-
- @bindThis
private async getMentionedRemoteUsers(note: MiNote) {
const where = [] as any[];
diff --git a/packages/backend/src/core/PageService.ts b/packages/backend/src/core/PageService.ts
new file mode 100644
index 0000000000..7f0e5c7ccc
--- /dev/null
+++ b/packages/backend/src/core/PageService.ts
@@ -0,0 +1,223 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { DataSource, In, Not } from 'typeorm';
+import { DI } from '@/di-symbols.js';
+import {
+ type NotesRepository,
+ MiPage,
+ type PagesRepository,
+ MiDriveFile,
+ type UsersRepository,
+ MiNote,
+} from '@/models/_.js';
+import { bindThis } from '@/decorators.js';
+import { RoleService } from '@/core/RoleService.js';
+import { IdService } from '@/core/IdService.js';
+import type { MiUser } from '@/models/User.js';
+import { IdentifiableError } from '@/misc/identifiable-error.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
+
+export interface PageBody {
+ title: string;
+ name: string;
+ summary: string | null;
+ content: Array<Record<string, any>>;
+ variables: Array<Record<string, any>>;
+ script: string;
+ eyeCatchingImage?: MiDriveFile | null;
+ font: string;
+ alignCenter: boolean;
+ hideTitleWhenPinned: boolean;
+}
+
+@Injectable()
+export class PageService {
+ constructor(
+ @Inject(DI.db)
+ private db: DataSource,
+
+ @Inject(DI.pagesRepository)
+ private pagesRepository: PagesRepository,
+
+ @Inject(DI.notesRepository)
+ private notesRepository: NotesRepository,
+
+ @Inject(DI.usersRepository)
+ private usersRepository: UsersRepository,
+
+ private roleService: RoleService,
+ private moderationLogService: ModerationLogService,
+ private idService: IdService,
+ ) {
+ }
+
+ @bindThis
+ public async create(
+ me: MiUser,
+ body: PageBody,
+ ): Promise<MiPage> {
+ await this.pagesRepository.findBy({
+ userId: me.id,
+ name: body.name,
+ }).then(result => {
+ if (result.length > 0) {
+ throw new IdentifiableError('1a79e38e-3d83-4423-845b-a9d83ff93b61');
+ }
+ });
+
+ const page = await this.pagesRepository.insertOne(new MiPage({
+ id: this.idService.gen(),
+ updatedAt: new Date(),
+ title: body.title,
+ name: body.name,
+ summary: body.summary,
+ content: body.content,
+ variables: body.variables,
+ script: body.script,
+ eyeCatchingImageId: body.eyeCatchingImage ? body.eyeCatchingImage.id : null,
+ userId: me.id,
+ visibility: 'public',
+ alignCenter: body.alignCenter,
+ hideTitleWhenPinned: body.hideTitleWhenPinned,
+ font: body.font,
+ }));
+
+ const referencedNotes = this.collectReferencedNotes(page.content);
+ if (referencedNotes.length > 0) {
+ await this.notesRepository.increment({ id: In(referencedNotes) }, 'pageCount', 1);
+ }
+
+ return page;
+ }
+
+ @bindThis
+ public async update(
+ me: MiUser,
+ pageId: MiPage['id'],
+ body: Partial<PageBody>,
+ ): Promise<void> {
+ await this.db.transaction(async (transaction) => {
+ const page = await transaction.findOne(MiPage, {
+ where: {
+ id: pageId,
+ },
+ lock: { mode: 'for_no_key_update' },
+ });
+
+ if (page == null) {
+ throw new IdentifiableError('66aefd3c-fdb2-4a71-85ae-cc18bea85d3f');
+ }
+ if (page.userId !== me.id) {
+ throw new IdentifiableError('d0017699-8256-46f1-aed4-bc03bed73616');
+ }
+
+ if (body.name != null) {
+ await transaction.findBy(MiPage, {
+ id: Not(pageId),
+ userId: me.id,
+ name: body.name,
+ }).then(result => {
+ if (result.length > 0) {
+ throw new IdentifiableError('d05bfe24-24b6-4ea2-a3ec-87cc9bf4daa4');
+ }
+ });
+ }
+
+ await transaction.update(MiPage, page.id, {
+ updatedAt: new Date(),
+ title: body.title,
+ name: body.name,
+ summary: body.summary === undefined ? page.summary : body.summary,
+ content: body.content,
+ variables: body.variables,
+ script: body.script,
+ alignCenter: body.alignCenter,
+ hideTitleWhenPinned: body.hideTitleWhenPinned,
+ font: body.font,
+ eyeCatchingImageId: body.eyeCatchingImage === undefined ? undefined : (body.eyeCatchingImage?.id ?? null),
+ });
+
+ console.log("page.content", page.content);
+
+ if (body.content != null) {
+ const beforeReferencedNotes = this.collectReferencedNotes(page.content);
+ const afterReferencedNotes = this.collectReferencedNotes(body.content);
+
+ const removedNotes = beforeReferencedNotes.filter(noteId => !afterReferencedNotes.includes(noteId));
+ const addedNotes = afterReferencedNotes.filter(noteId => !beforeReferencedNotes.includes(noteId));
+
+ if (removedNotes.length > 0) {
+ await transaction.decrement(MiNote, { id: In(removedNotes) }, 'pageCount', 1);
+ }
+ if (addedNotes.length > 0) {
+ await transaction.increment(MiNote, { id: In(addedNotes) }, 'pageCount', 1);
+ }
+ }
+ });
+ }
+
+ @bindThis
+ public async delete(me: MiUser, pageId: MiPage['id']): Promise<void> {
+ await this.db.transaction(async (transaction) => {
+ const page = await transaction.findOne(MiPage, {
+ where: {
+ id: pageId,
+ },
+ lock: { mode: 'pessimistic_write' }, // same lock level as DELETE
+ });
+
+ if (page == null) {
+ throw new IdentifiableError('66aefd3c-fdb2-4a71-85ae-cc18bea85d3f');
+ }
+
+ if (!await this.roleService.isModerator(me) && page.userId !== me.id) {
+ throw new IdentifiableError('d0017699-8256-46f1-aed4-bc03bed73616');
+ }
+
+ await transaction.delete(MiPage, page.id);
+
+ if (page.userId !== me.id) {
+ const user = await this.usersRepository.findOneByOrFail({ id: page.userId });
+ this.moderationLogService.log(me, 'deletePage', {
+ pageId: page.id,
+ pageUserId: page.userId,
+ pageUserUsername: user.username,
+ page,
+ });
+ }
+
+ const referencedNotes = this.collectReferencedNotes(page.content);
+ if (referencedNotes.length > 0) {
+ await transaction.decrement(MiNote, { id: In(referencedNotes) }, 'pageCount', 1);
+ }
+ });
+ }
+
+ collectReferencedNotes(content: MiPage['content']): string[] {
+ const referencingNotes = new Set<string>();
+ const recursiveCollect = (content: unknown[]) => {
+ for (const contentElement of content) {
+ if (typeof contentElement === 'object'
+ && contentElement !== null
+ && 'type' in contentElement) {
+ if (contentElement.type === 'note'
+ && 'note' in contentElement
+ && typeof contentElement.note === 'string') {
+ referencingNotes.add(contentElement.note);
+ }
+ if (contentElement.type === 'section'
+ && 'children' in contentElement
+ && Array.isArray(contentElement.children)) {
+ recursiveCollect(contentElement.children);
+ }
+ }
+ }
+ };
+ recursiveCollect(content);
+ return [...referencingNotes];
+ }
+}
diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts
index d398e83230..49f93ad108 100644
--- a/packages/backend/src/core/QueryService.ts
+++ b/packages/backend/src/core/QueryService.ts
@@ -360,7 +360,7 @@ export class QueryService {
public generateSuspendedUserQueryForNote(q: SelectQueryBuilder<any>, excludeAuthor?: boolean): void {
if (excludeAuthor) {
const brakets = (user: string) => new Brackets(qb => qb
- .where(`note.${user}Id IS NULL`)
+ .where(`${user}.id IS NULL`) // ãã‚‚ãã‚‚replyã‚„renoteã§ã¯ãªã„ã€ã‚‚ã—ãã¯leftjoinãªã©ã§userãŒå­˜åœ¨ã—ãªã‹ã£ãŸå ´åˆã‚’考慮
.orWhere(`user.id = ${user}.id`)
.orWhere(`${user}.isSuspended = FALSE`));
q
@@ -368,7 +368,7 @@ export class QueryService {
.andWhere(brakets('renoteUser'));
} else {
const brakets = (user: string) => new Brackets(qb => qb
- .where(`note.${user}Id IS NULL`)
+ .where(`${user}.id IS NULL`) // ãã‚‚ãã‚‚replyã‚„renoteã§ã¯ãªã„ã€ã‚‚ã—ãã¯leftjoinãªã©ã§userãŒå­˜åœ¨ã—ãªã‹ã£ãŸå ´åˆã‚’考慮
.orWhere(`${user}.isSuspended = FALSE`));
q
.andWhere('user.isSuspended = FALSE')
diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts
index 04bbc7e38a..0f225a8242 100644
--- a/packages/backend/src/core/QueueService.ts
+++ b/packages/backend/src/core/QueueService.ts
@@ -17,6 +17,7 @@ import { bindThis } from '@/decorators.js';
import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js';
import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js';
import { type SystemWebhookPayload } from '@/core/SystemWebhookService.js';
+import type { Packed } from '@/misc/json-schema.js';
import { type UserWebhookPayload } from './UserWebhookService.js';
import type {
DbJobData,
@@ -39,7 +40,6 @@ import type {
} from './QueueModule.js';
import type httpSignature from '@peertube/http-signature';
import type * as Bull from 'bullmq';
-import type { Packed } from '@/misc/json-schema.js';
export const QUEUE_TYPES = [
'system',
@@ -53,6 +53,37 @@ export const QUEUE_TYPES = [
'systemWebhookDeliver',
] as const;
+const REPEATABLE_SYSTEM_JOB_DEF = [{
+ name: 'tickCharts',
+ pattern: '55 * * * *',
+}, {
+ name: 'resyncCharts',
+ pattern: '0 0 * * *',
+}, {
+ name: 'cleanCharts',
+ pattern: '0 0 * * *',
+}, {
+ name: 'aggregateRetention',
+ pattern: '0 0 * * *',
+}, {
+ name: 'clean',
+ pattern: '0 0 * * *',
+}, {
+ name: 'checkExpiredMutings',
+ pattern: '*/5 * * * *',
+}, {
+ name: 'bakeBufferedReactions',
+ pattern: '0 0 * * *',
+}, {
+ name: 'checkModeratorsActivity',
+ // 毎時30分ã«èµ·å‹•
+ pattern: '30 * * * *',
+}, {
+ name: 'cleanRemoteNotes',
+ // 毎日åˆå‰4時ã«èµ·å‹•(最も人ã®å°‘ãªã„時間帯)
+ pattern: '0 4 * * *',
+}];
+
@Injectable()
export class QueueService {
constructor(
@@ -69,61 +100,31 @@ export class QueueService {
@Inject('queue:userWebhookDeliver') public userWebhookDeliverQueue: UserWebhookDeliverQueue,
@Inject('queue:systemWebhookDeliver') public systemWebhookDeliverQueue: SystemWebhookDeliverQueue,
) {
- this.systemQueue.add('tickCharts', {
- }, {
- repeat: { pattern: '55 * * * *' },
- removeOnComplete: 10,
- removeOnFail: 30,
- });
-
- this.systemQueue.add('resyncCharts', {
- }, {
- repeat: { pattern: '0 0 * * *' },
- removeOnComplete: 10,
- removeOnFail: 30,
- });
-
- this.systemQueue.add('cleanCharts', {
- }, {
- repeat: { pattern: '0 0 * * *' },
- removeOnComplete: 10,
- removeOnFail: 30,
- });
-
- this.systemQueue.add('aggregateRetention', {
- }, {
- repeat: { pattern: '0 0 * * *' },
- removeOnComplete: 10,
- removeOnFail: 30,
- });
-
- this.systemQueue.add('clean', {
- }, {
- repeat: { pattern: '0 0 * * *' },
- removeOnComplete: 10,
- removeOnFail: 30,
- });
-
- this.systemQueue.add('checkExpiredMutings', {
- }, {
- repeat: { pattern: '*/5 * * * *' },
- removeOnComplete: 10,
- removeOnFail: 30,
- });
-
- this.systemQueue.add('bakeBufferedReactions', {
- }, {
- repeat: { pattern: '0 0 * * *' },
- removeOnComplete: 10,
- removeOnFail: 30,
- });
+ for (const def of REPEATABLE_SYSTEM_JOB_DEF) {
+ this.systemQueue.upsertJobScheduler(def.name, {
+ pattern: def.pattern,
+ immediately: false,
+ }, {
+ name: def.name,
+ opts: {
+ // 期é™ã§ã¯ãªãcountã§è¨­å®šã—ãŸã„ãŒã€ã‚¸ãƒ§ãƒ–ã”ã¨ã§ã¯ãªãキュー全体ã§ã‚«ã‚¦ãƒ³ãƒˆã•れるãŸã‚ã€é«˜é »åº¦ã§å®Ÿè¡Œã•れるジョブã«ã‚ˆã£ã¦ä½Žé »åº¦ã§å®Ÿè¡Œã•れるジョブã®ãƒ­ã‚°ãŒæ¶ˆãˆã‚‹ã“ã¨ã«ãªã‚‹
+ removeOnComplete: {
+ age: 3600 * 24 * 7, // keep up to 7 days
+ },
+ removeOnFail: {
+ age: 3600 * 24 * 7, // keep up to 7 days
+ },
+ },
+ });
+ }
- this.systemQueue.add('checkModeratorsActivity', {
- }, {
- // 毎時30分ã«èµ·å‹•
- repeat: { pattern: '30 * * * *' },
- removeOnComplete: 10,
- removeOnFail: 30,
+ // å¤ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã§ä½œæˆã•れç¾åœ¨ä½¿ã‚れãªããªã£ãŸrepeatableジョブをクリーンアップ
+ this.systemQueue.getJobSchedulers().then(schedulers => {
+ for (const scheduler of schedulers) {
+ if (!REPEATABLE_SYSTEM_JOB_DEF.some(def => def.name === scheduler.key)) {
+ this.systemQueue.removeJobScheduler(scheduler.key);
+ }
+ }
});
}
@@ -811,6 +812,13 @@ export class QueueService {
}
@bindThis
+ public async queueGetJobLogs(queueType: typeof QUEUE_TYPES[number], jobId: string) {
+ const queue = this.getQueue(queueType);
+ const result = await queue.getJobLogs(jobId);
+ return result.logs;
+ }
+
+ @bindThis
public async queueGetJobs(queueType: typeof QUEUE_TYPES[number], jobTypes: JobType[], search?: string) {
const RETURN_LIMIT = 100;
const queue = this.getQueue(queueType);
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index cddfc0094e..3df7ee69ee 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -43,6 +43,7 @@ export type RolePolicies = {
canManageCustomEmojis: boolean;
canManageAvatarDecorations: boolean;
canSearchNotes: boolean;
+ canSearchUsers: boolean;
canUseTranslator: boolean;
canHideAds: boolean;
driveCapacityMb: number;
@@ -82,6 +83,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
canManageCustomEmojis: false,
canManageAvatarDecorations: false,
canSearchNotes: false,
+ canSearchUsers: true,
canUseTranslator: true,
canHideAds: false,
driveCapacityMb: 100,
@@ -402,6 +404,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
canManageCustomEmojis: calc('canManageCustomEmojis', vs => vs.some(v => v === true)),
canManageAvatarDecorations: calc('canManageAvatarDecorations', vs => vs.some(v => v === true)),
canSearchNotes: calc('canSearchNotes', vs => vs.some(v => v === true)),
+ canSearchUsers: calc('canSearchUsers', vs => vs.some(v => v === true)),
canUseTranslator: calc('canUseTranslator', vs => vs.some(v => v === true)),
canHideAds: calc('canHideAds', vs => vs.some(v => v === true)),
driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)),
diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts
index 643a5f525d..71dc718916 100644
--- a/packages/backend/src/core/SearchService.ts
+++ b/packages/backend/src/core/SearchService.ts
@@ -227,9 +227,9 @@ export class SearchService {
if (opts.host) {
if (opts.host === '.') {
- query.andWhere('user.host IS NULL');
+ query.andWhere('note.userHost IS NULL');
} else {
- query.andWhere('user.host = :host', { host: opts.host });
+ query.andWhere('note.userHost = :host', { host: opts.host });
}
}
diff --git a/packages/backend/src/core/WebhookTestService.ts b/packages/backend/src/core/WebhookTestService.ts
index 9cf985b688..6714bda9a9 100644
--- a/packages/backend/src/core/WebhookTestService.ts
+++ b/packages/backend/src/core/WebhookTestService.ts
@@ -85,6 +85,7 @@ function generateDummyNote(override?: Partial<MiNote>): MiNote {
renoteCount: 10,
repliesCount: 5,
clippedCount: 0,
+ pageCount: 0,
reactions: {},
visibility: 'public',
uri: null,
@@ -243,7 +244,6 @@ export class WebhookTestService {
case 'reaction':
return;
default: {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
const _exhaustiveAssertion: never = params.type;
return;
}
@@ -326,7 +326,6 @@ export class WebhookTestService {
break;
}
default: {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
const _exhaustiveAssertion: never = params.type;
return;
}
@@ -411,7 +410,7 @@ export class WebhookTestService {
name: user.name,
username: user.username,
host: user.host,
- avatarUrl: user.avatarId == null ? null : user.avatarUrl,
+ avatarUrl: (user.avatarId == null ? null : user.avatarUrl) ?? '',
avatarBlurhash: user.avatarId == null ? null : user.avatarBlurhash,
avatarDecorations: user.avatarDecorations.map(it => ({
id: it.id,
diff --git a/packages/backend/src/core/entities/ChatEntityService.ts b/packages/backend/src/core/entities/ChatEntityService.ts
index 6bce2413fd..cfa983e766 100644
--- a/packages/backend/src/core/entities/ChatEntityService.ts
+++ b/packages/backend/src/core/entities/ChatEntityService.ts
@@ -54,12 +54,13 @@ export class ChatEntityService {
const message = typeof src === 'object' ? src : await this.chatMessagesRepository.findOneByOrFail({ id: src });
- const reactions: { user: Packed<'UserLite'>; reaction: string; }[] = [];
+ // userã¯å‰Šé™¤ã•れã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚‹ã®ã§null許容
+ const reactions: { user: Packed<'UserLite'> | null; reaction: string; }[] = [];
for (const record of message.reactions) {
const [userId, reaction] = record.split('/');
reactions.push({
- user: packedUsers?.get(userId) ?? await this.userEntityService.pack(userId),
+ user: packedUsers?.get(userId) ?? await this.userEntityService.pack(userId).catch(() => null),
reaction,
});
}
@@ -76,7 +77,7 @@ export class ChatEntityService {
toRoom: message.toRoomId ? (packedRooms?.get(message.toRoomId) ?? await this.packRoom(message.toRoom ?? message.toRoomId, me)) : undefined,
fileId: message.fileId,
file: message.fileId ? (packedFiles?.get(message.fileId) ?? await this.driveFileEntityService.pack(message.file ?? message.fileId)) : null,
- reactions,
+ reactions: reactions.filter((r): r is { user: Packed<'UserLite'>; reaction: string; } => r.user != null),
};
}
@@ -108,6 +109,7 @@ export class ChatEntityService {
}
}
+ // TODO: packedUsersã«å‰Šé™¤ã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚‚nullã¨ã—ã¦å«ã‚ã‚‹
const [packedUsers, packedFiles, packedRooms] = await Promise.all([
this.userEntityService.packMany(users, me)
.then(users => new Map(users.map(u => [u.id, u]))),
@@ -183,12 +185,13 @@ export class ChatEntityService {
const message = typeof src === 'object' ? src : await this.chatMessagesRepository.findOneByOrFail({ id: src });
- const reactions: { user: Packed<'UserLite'>; reaction: string; }[] = [];
+ // userã¯å‰Šé™¤ã•れã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚‹ã®ã§null許容
+ const reactions: { user: Packed<'UserLite'> | null; reaction: string; }[] = [];
for (const record of message.reactions) {
const [userId, reaction] = record.split('/');
reactions.push({
- user: packedUsers?.get(userId) ?? await this.userEntityService.pack(userId),
+ user: packedUsers?.get(userId) ?? await this.userEntityService.pack(userId).catch(() => null),
reaction,
});
}
@@ -202,7 +205,7 @@ export class ChatEntityService {
toRoomId: message.toRoomId!,
fileId: message.fileId,
file: message.fileId ? (packedFiles?.get(message.fileId) ?? await this.driveFileEntityService.pack(message.file ?? message.fileId)) : null,
- reactions,
+ reactions: reactions.filter((r): r is { user: Packed<'UserLite'>; reaction: string; } => r.user != null),
};
}
diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts
index 02783dc450..f8abfb2f98 100644
--- a/packages/backend/src/core/entities/MetaEntityService.ts
+++ b/packages/backend/src/core/entities/MetaEntityService.ts
@@ -109,6 +109,7 @@ export class MetaEntityService {
maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
defaultLightTheme,
defaultDarkTheme,
+ clientOptions: instance.clientOptions,
ads: ads.map(ad => ({
id: ad.id,
url: ad.url,
diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts
index 92caad908c..6871ba2c72 100644
--- a/packages/backend/src/core/entities/NoteEntityService.ts
+++ b/packages/backend/src/core/entities/NoteEntityService.ts
@@ -4,7 +4,7 @@
*/
import { Inject, Injectable } from '@nestjs/common';
-import { In } from 'typeorm';
+import { EntityNotFoundError, In } from 'typeorm';
import { ModuleRef } from '@nestjs/core';
import { DI } from '@/di-symbols.js';
import type { Packed } from '@/misc/json-schema.js';
@@ -46,6 +46,17 @@ function getAppearNoteIds(notes: MiNote[]): Set<string> {
return appearNoteIds;
}
+async function nullIfEntityNotFound<T>(promise: Promise<T>): Promise<T | null> {
+ try {
+ return await promise;
+ } catch (err) {
+ if (err instanceof EntityNotFoundError) {
+ return null;
+ }
+ throw err;
+ }
+}
+
@Injectable()
export class NoteEntityService implements OnModuleInit {
private userEntityService: UserEntityService;
@@ -436,19 +447,21 @@ export class NoteEntityService implements OnModuleInit {
...(opts.detail ? {
clippedCount: note.clippedCount,
- reply: note.replyId ? this.pack(note.reply ?? note.replyId, me, {
+ // ãã‚‚ãã‚‚JOINã—ã¦ã„ãªã„å ´åˆã¯undefinedã€JOINã—ãŸã‘ã©å­˜åœ¨ã—ã¦ã„ãªã‹ã£ãŸå ´åˆã¯nullã§åŒºåˆ¥ã•れる
+ reply: (note.replyId && note.reply === null) ? null : note.replyId ? nullIfEntityNotFound(this.pack(note.reply ?? note.replyId, me, {
detail: false,
skipHide: opts.skipHide,
withReactionAndUserPairCache: opts.withReactionAndUserPairCache,
_hint_: options?._hint_,
- }) : undefined,
+ })) : undefined,
- renote: note.renoteId ? this.pack(note.renote ?? note.renoteId, me, {
+ // ãã‚‚ãã‚‚JOINã—ã¦ã„ãªã„å ´åˆã¯undefinedã€JOINã—ãŸã‘ã©å­˜åœ¨ã—ã¦ã„ãªã‹ã£ãŸå ´åˆã¯nullã§åŒºåˆ¥ã•れる
+ renote: (note.renoteId && note.renote === null) ? null : note.renoteId ? nullIfEntityNotFound(this.pack(note.renote ?? note.renoteId, me, {
detail: true,
skipHide: opts.skipHide,
withReactionAndUserPairCache: opts.withReactionAndUserPairCache,
_hint_: options?._hint_,
- }) : undefined,
+ })) : undefined,
poll: note.hasPoll ? this.populatePoll(note, meId) : undefined,
@@ -591,7 +604,7 @@ export class NoteEntityService implements OnModuleInit {
private findNoteOrFail(id: string): Promise<MiNote> {
return this.notesRepository.findOneOrFail({
where: { id },
- relations: ['user'],
+ relations: ['user', 'renote', 'reply'],
});
}
diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts
index 46ec13704c..54ce4d472a 100644
--- a/packages/backend/src/core/entities/NoteReactionEntityService.ts
+++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts
@@ -49,15 +49,12 @@ export class NoteReactionEntityService implements OnModuleInit {
public async pack(
src: MiNoteReaction['id'] | MiNoteReaction,
me?: { id: MiUser['id'] } | null | undefined,
- options?: {
- withNote: boolean;
- },
+ options?: object,
hints?: {
packedUser?: Packed<'UserLite'>
},
): Promise<Packed<'NoteReaction'>> {
const opts = Object.assign({
- withNote: false,
}, options);
const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src });
@@ -67,9 +64,6 @@ export class NoteReactionEntityService implements OnModuleInit {
createdAt: this.idService.parse(reaction.id).date.toISOString(),
user: hints?.packedUser ?? await this.userEntityService.pack(reaction.user ?? reaction.userId, me),
type: this.reactionService.convertLegacyReaction(reaction.reaction),
- ...(opts.withNote ? {
- note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me),
- } : {}),
};
}
@@ -77,16 +71,50 @@ export class NoteReactionEntityService implements OnModuleInit {
public async packMany(
reactions: MiNoteReaction[],
me?: { id: MiUser['id'] } | null | undefined,
- options?: {
- withNote: boolean;
- },
+ options?: object,
): Promise<Packed<'NoteReaction'>[]> {
const opts = Object.assign({
- withNote: false,
}, options);
const _users = reactions.map(({ user, userId }) => user ?? userId);
const _userMap = await this.userEntityService.packMany(_users, me)
.then(users => new Map(users.map(u => [u.id, u])));
return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts, { packedUser: _userMap.get(reaction.userId) })));
}
+
+ @bindThis
+ public async packWithNote(
+ src: MiNoteReaction['id'] | MiNoteReaction,
+ me?: { id: MiUser['id'] } | null | undefined,
+ options?: object,
+ hints?: {
+ packedUser?: Packed<'UserLite'>
+ },
+ ): Promise<Packed<'NoteReactionWithNote'>> {
+ const opts = Object.assign({
+ }, options);
+
+ const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src });
+
+ return {
+ id: reaction.id,
+ createdAt: this.idService.parse(reaction.id).date.toISOString(),
+ user: hints?.packedUser ?? await this.userEntityService.pack(reaction.user ?? reaction.userId, me),
+ type: this.reactionService.convertLegacyReaction(reaction.reaction),
+ note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me),
+ };
+ }
+
+ @bindThis
+ public async packManyWithNote(
+ reactions: MiNoteReaction[],
+ me?: { id: MiUser['id'] } | null | undefined,
+ options?: object,
+ ): Promise<Packed<'NoteReactionWithNote'>[]> {
+ const opts = Object.assign({
+ }, options);
+ const _users = reactions.map(({ user, userId }) => user ?? userId);
+ const _userMap = await this.userEntityService.packMany(_users, me)
+ .then(users => new Map(users.map(u => [u.id, u])));
+ return Promise.all(reactions.map(reaction => this.packWithNote(reaction, me, opts, { packedUser: _userMap.get(reaction.userId) })));
+ }
}
diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index d4769d24d4..47021359e1 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -471,8 +471,8 @@ export class UserEntityService implements OnModuleInit {
(profile.followersVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount :
null;
- const isModerator = isMe && isDetailed ? this.roleService.isModerator(user) : null;
- const isAdmin = isMe && isDetailed ? this.roleService.isAdministrator(user) : null;
+ const isModerator = isMe && isDetailed ? this.roleService.isModerator(user) : undefined;
+ const isAdmin = isMe && isDetailed ? this.roleService.isAdministrator(user) : undefined;
const unreadAnnouncements = isMe && isDetailed ?
(await this.announcementService.getUnreadAnnouncements(user)).map((announcement) => ({
createdAt: this.idService.parse(announcement.id).date.toISOString(),
@@ -481,6 +481,7 @@ export class UserEntityService implements OnModuleInit {
const notificationsInfo = isMe && isDetailed ? await this.getNotificationsInfo(user.id) : null;
+ // TODO: 例ãˆã° avatarUrl: true ãªã©é–“é•ã£ãŸåž‹ã‚’設定ã—ã¦ã‚‚型エラーã«ãªã‚‰ãªã„ã®ã‚’ã©ã†ã«ã‹ã™ã‚‹(ジェãƒãƒªã‚¯ã‚¹ä½¿ã‚ãªã„方法ã§å®Ÿè£…ã™ã‚‹ã—ã‹ãªã•ãã†ï¼Ÿ)
const packed = {
id: user.id,
name: user.name,
diff --git a/packages/backend/src/misc/emoji-regex.ts b/packages/backend/src/misc/emoji-regex.ts
index 6d03b433ba..fabbdc335f 100644
--- a/packages/backend/src/misc/emoji-regex.ts
+++ b/packages/backend/src/misc/emoji-regex.ts
@@ -4,6 +4,6 @@
*/
// taken from @twemoji/parser/dist/lib/regex.js
-const twemojiRegex = /(?:\ud83d\udc68\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffc-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb\udffd-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb-\udffd\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb-\udffe]|\ud83d\udc68\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc68\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc68\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffc-\udfff]|\ud83e\uddd1\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb\udffd-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb-\udffd\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d[\udc68\udc69]|\ud83e\udef1\ud83c\udffb\u200d\ud83e\udef2\ud83c[\udffc-\udfff]|\ud83e\udef1\ud83c\udffc\u200d\ud83e\udef2\ud83c[\udffb\udffd-\udfff]|\ud83e\udef1\ud83c\udffd\u200d\ud83e\udef2\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\udef1\ud83c\udffe\u200d\ud83e\udef2\ud83c[\udffb-\udffd\udfff]|\ud83e\udef1\ud83c\udfff\u200d\ud83e\udef2\ud83c[\udffb-\udffe]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc68|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d[\udc68\udc69]|\ud83e\uddd1\u200d\ud83e\udd1d\u200d\ud83e\uddd1|\ud83d\udc6b\ud83c[\udffb-\udfff]|\ud83d\udc6c\ud83c[\udffb-\udfff]|\ud83d\udc6d\ud83c[\udffb-\udfff]|\ud83d\udc8f\ud83c[\udffb-\udfff]|\ud83d\udc91\ud83c[\udffb-\udfff]|\ud83e\udd1d\ud83c[\udffb-\udfff]|\ud83d[\udc6b-\udc6d\udc8f\udc91]|\ud83e\udd1d)|(?:\ud83d[\udc68\udc69]|\ud83e\uddd1)(?:\ud83c[\udffb-\udfff])?\u200d(?:\u2695\ufe0f|\u2696\ufe0f|\u2708\ufe0f|\ud83c[\udf3e\udf73\udf7c\udf84\udf93\udfa4\udfa8\udfeb\udfed]|\ud83d[\udcbb\udcbc\udd27\udd2c\ude80\ude92]|\ud83e[\uddaf-\uddb3\uddbc\uddbd])|(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75]|\u26f9)((?:\ud83c[\udffb-\udfff]|\ufe0f)\u200d[\u2640\u2642]\ufe0f)|(?:\ud83c[\udfc3\udfc4\udfca]|\ud83d[\udc6e\udc70\udc71\udc73\udc77\udc81\udc82\udc86\udc87\ude45-\ude47\ude4b\ude4d\ude4e\udea3\udeb4-\udeb6]|\ud83e[\udd26\udd35\udd37-\udd39\udd3d\udd3e\uddb8\uddb9\uddcd-\uddcf\uddd4\uddd6-\udddd])(?:\ud83c[\udffb-\udfff])?\u200d[\u2640\u2642]\ufe0f|(?:\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f|\ud83c\udff3\ufe0f\u200d\ud83c\udf08|\ud83d\ude36\u200d\ud83c\udf2b\ufe0f|\u2764\ufe0f\u200d\ud83d\udd25|\u2764\ufe0f\u200d\ud83e\ude79|\ud83c\udff4\u200d\u2620\ufe0f|\ud83d\udc15\u200d\ud83e\uddba|\ud83d\udc3b\u200d\u2744\ufe0f|\ud83d\udc41\u200d\ud83d\udde8|\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc6f\u200d\u2640\ufe0f|\ud83d\udc6f\u200d\u2642\ufe0f|\ud83d\ude2e\u200d\ud83d\udca8|\ud83d\ude35\u200d\ud83d\udcab|\ud83e\udd3c\u200d\u2640\ufe0f|\ud83e\udd3c\u200d\u2642\ufe0f|\ud83e\uddde\u200d\u2640\ufe0f|\ud83e\uddde\u200d\u2642\ufe0f|\ud83e\udddf\u200d\u2640\ufe0f|\ud83e\udddf\u200d\u2642\ufe0f|\ud83d\udc08\u200d\u2b1b|\ud83d\udc26\u200d\u2b1b)|[#*0-9]\ufe0f?\u20e3|(?:[©®\u2122\u265f]\ufe0f)|(?:\ud83c[\udc04\udd70\udd71\udd7e\udd7f\ude02\ude1a\ude2f\ude37\udf21\udf24-\udf2c\udf36\udf7d\udf96\udf97\udf99-\udf9b\udf9e\udf9f\udfcd\udfce\udfd4-\udfdf\udff3\udff5\udff7]|\ud83d[\udc3f\udc41\udcfd\udd49\udd4a\udd6f\udd70\udd73\udd76-\udd79\udd87\udd8a-\udd8d\udda5\udda8\uddb1\uddb2\uddbc\uddc2-\uddc4\uddd1-\uddd3\udddc-\uddde\udde1\udde3\udde8\uddef\uddf3\uddfa\udecb\udecd-\udecf\udee0-\udee5\udee9\udef0\udef3]|[\u203c\u2049\u2139\u2194-\u2199\u21a9\u21aa\u231a\u231b\u2328\u23cf\u23ed-\u23ef\u23f1\u23f2\u23f8-\u23fa\u24c2\u25aa\u25ab\u25b6\u25c0\u25fb-\u25fe\u2600-\u2604\u260e\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262a\u262e\u262f\u2638-\u263a\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267b\u267f\u2692-\u2697\u2699\u269b\u269c\u26a0\u26a1\u26a7\u26aa\u26ab\u26b0\u26b1\u26bd\u26be\u26c4\u26c5\u26c8\u26cf\u26d1\u26d3\u26d4\u26e9\u26ea\u26f0-\u26f5\u26f8\u26fa\u26fd\u2702\u2708\u2709\u270f\u2712\u2714\u2716\u271d\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u2764\u27a1\u2934\u2935\u2b05-\u2b07\u2b1b\u2b1c\u2b50\u2b55\u3030\u303d\u3297\u3299])(?:\ufe0f|(?!\ufe0e))|(?:(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75\udd90]|\ud83e\udef0|[\u261d\u26f7\u26f9\u270c\u270d])(?:\ufe0f|(?!\ufe0e))|(?:\ud83c[\udf85\udfc2-\udfc4\udfc7\udfca]|\ud83d[\udc42\udc43\udc46-\udc50\udc66-\udc69\udc6e\udc70-\udc78\udc7c\udc81-\udc83\udc85-\udc87\udcaa\udd7a\udd95\udd96\ude45-\ude47\ude4b-\ude4f\udea3\udeb4-\udeb6\udec0\udecc]|\ud83e[\udd0c\udd0f\udd18-\udd1c\udd1e\udd1f\udd26\udd30-\udd39\udd3d\udd3e\udd77\uddb5\uddb6\uddb8\uddb9\uddbb\uddcd-\uddcf\uddd1-\udddd\udec3-\udec5\udef1-\udef8]|[\u270a\u270b]))(?:\ud83c[\udffb-\udfff])?|(?:\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc73\udb40\udc63\udb40\udc74\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f|\ud83c\udde6\ud83c[\udde8-\uddec\uddee\uddf1\uddf2\uddf4\uddf6-\uddfa\uddfc\uddfd\uddff]|\ud83c\udde7\ud83c[\udde6\udde7\udde9-\uddef\uddf1-\uddf4\uddf6-\uddf9\uddfb\uddfc\uddfe\uddff]|\ud83c\udde8\ud83c[\udde6\udde8\udde9\uddeb-\uddee\uddf0-\uddf5\uddf7\uddfa-\uddff]|\ud83c\udde9\ud83c[\uddea\uddec\uddef\uddf0\uddf2\uddf4\uddff]|\ud83c\uddea\ud83c[\udde6\udde8\uddea\uddec\udded\uddf7-\uddfa]|\ud83c\uddeb\ud83c[\uddee-\uddf0\uddf2\uddf4\uddf7]|\ud83c\uddec\ud83c[\udde6\udde7\udde9-\uddee\uddf1-\uddf3\uddf5-\uddfa\uddfc\uddfe]|\ud83c\udded\ud83c[\uddf0\uddf2\uddf3\uddf7\uddf9\uddfa]|\ud83c\uddee\ud83c[\udde8-\uddea\uddf1-\uddf4\uddf6-\uddf9]|\ud83c\uddef\ud83c[\uddea\uddf2\uddf4\uddf5]|\ud83c\uddf0\ud83c[\uddea\uddec-\uddee\uddf2\uddf3\uddf5\uddf7\uddfc\uddfe\uddff]|\ud83c\uddf1\ud83c[\udde6-\udde8\uddee\uddf0\uddf7-\uddfb\uddfe]|\ud83c\uddf2\ud83c[\udde6\udde8-\udded\uddf0-\uddff]|\ud83c\uddf3\ud83c[\udde6\udde8\uddea-\uddec\uddee\uddf1\uddf4\uddf5\uddf7\uddfa\uddff]|\ud83c\uddf4\ud83c\uddf2|\ud83c\uddf5\ud83c[\udde6\uddea-\udded\uddf0-\uddf3\uddf7-\uddf9\uddfc\uddfe]|\ud83c\uddf6\ud83c\udde6|\ud83c\uddf7\ud83c[\uddea\uddf4\uddf8\uddfa\uddfc]|\ud83c\uddf8\ud83c[\udde6-\uddea\uddec-\uddf4\uddf7-\uddf9\uddfb\uddfd-\uddff]|\ud83c\uddf9\ud83c[\udde6\udde8\udde9\uddeb-\udded\uddef-\uddf4\uddf7\uddf9\uddfb\uddfc\uddff]|\ud83c\uddfa\ud83c[\udde6\uddec\uddf2\uddf3\uddf8\uddfe\uddff]|\ud83c\uddfb\ud83c[\udde6\udde8\uddea\uddec\uddee\uddf3\uddfa]|\ud83c\uddfc\ud83c[\uddeb\uddf8]|\ud83c\uddfd\ud83c\uddf0|\ud83c\uddfe\ud83c[\uddea\uddf9]|\ud83c\uddff\ud83c[\udde6\uddf2\uddfc]|\ud83c[\udccf\udd8e\udd91-\udd9a\udde6-\uddff\ude01\ude32-\ude36\ude38-\ude3a\ude50\ude51\udf00-\udf20\udf2d-\udf35\udf37-\udf7c\udf7e-\udf84\udf86-\udf93\udfa0-\udfc1\udfc5\udfc6\udfc8\udfc9\udfcf-\udfd3\udfe0-\udff0\udff4\udff8-\udfff]|\ud83d[\udc00-\udc3e\udc40\udc44\udc45\udc51-\udc65\udc6a\udc6f\udc79-\udc7b\udc7d-\udc80\udc84\udc88-\udc8e\udc90\udc92-\udca9\udcab-\udcfc\udcff-\udd3d\udd4b-\udd4e\udd50-\udd67\udda4\uddfb-\ude44\ude48-\ude4a\ude80-\udea2\udea4-\udeb3\udeb7-\udebf\udec1-\udec5\uded0-\uded2\uded5-\uded7\udedc-\udedf\udeeb\udeec\udef4-\udefc\udfe0-\udfeb\udff0]|\ud83e[\udd0d\udd0e\udd10-\udd17\udd20-\udd25\udd27-\udd2f\udd3a\udd3c\udd3f-\udd45\udd47-\udd76\udd78-\uddb4\uddb7\uddba\uddbc-\uddcc\uddd0\uddde-\uddff\ude70-\ude7c\ude80-\ude88\ude90-\udebd\udebf-\udec2\udece-\udedb\udee0-\udee8]|[\u23e9-\u23ec\u23f0\u23f3\u267e\u26ce\u2705\u2728\u274c\u274e\u2753-\u2755\u2795-\u2797\u27b0\u27bf\ue50a])|\ufe0f/g;
+const twemojiRegex = /(?:\ud83d\udc68\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffc-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb\udffd-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb-\udffd\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb-\udffe]|\ud83d\udc68\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc68\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc68\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffc-\udfff]|\ud83e\uddd1\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb\udffd-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb-\udffd\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d[\udc68\udc69]|\ud83e\udef1\ud83c\udffb\u200d\ud83e\udef2\ud83c[\udffc-\udfff]|\ud83e\udef1\ud83c\udffc\u200d\ud83e\udef2\ud83c[\udffb\udffd-\udfff]|\ud83e\udef1\ud83c\udffd\u200d\ud83e\udef2\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\udef1\ud83c\udffe\u200d\ud83e\udef2\ud83c[\udffb-\udffd\udfff]|\ud83e\udef1\ud83c\udfff\u200d\ud83e\udef2\ud83c[\udffb-\udffe]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc68|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d[\udc68\udc69]|\ud83e\uddd1\u200d\ud83e\udd1d\u200d\ud83e\uddd1|\ud83d\udc6b\ud83c[\udffb-\udfff]|\ud83d\udc6c\ud83c[\udffb-\udfff]|\ud83d\udc6d\ud83c[\udffb-\udfff]|\ud83d\udc8f\ud83c[\udffb-\udfff]|\ud83d\udc91\ud83c[\udffb-\udfff]|\ud83e\udd1d\ud83c[\udffb-\udfff]|\ud83d[\udc6b-\udc6d\udc8f\udc91]|\ud83e\udd1d)|(?:\ud83d[\udc68\udc69]|\ud83e\uddd1)(?:\ud83c[\udffb-\udfff])?\u200d(?:\u2695\ufe0f|\u2696\ufe0f|\u2708\ufe0f|\ud83c[\udf3e\udf73\udf7c\udf84\udf93\udfa4\udfa8\udfeb\udfed]|\ud83d[\udcbb\udcbc\udd27\udd2c\ude80\ude92]|\ud83e[\uddaf-\uddb3\uddbc\uddbd])(?:\u200d\u27a1\ufe0f)?|(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75]|\u26f9)((?:\ud83c[\udffb-\udfff]|\ufe0f)\u200d[\u2640\u2642]\ufe0f(?:\u200d\u27a1\ufe0f)?)|(?:\ud83c[\udfc3\udfc4\udfca]|\ud83d[\udc6e\udc70\udc71\udc73\udc77\udc81\udc82\udc86\udc87\ude45-\ude47\ude4b\ude4d\ude4e\udea3\udeb4-\udeb6]|\ud83e[\udd26\udd35\udd37-\udd39\udd3d\udd3e\uddb8\uddb9\uddcd-\uddcf\uddd4\uddd6-\udddd])(?:\ud83c[\udffb-\udfff])?\u200d[\u2640\u2642]\ufe0f(?:\u200d\u27a1\ufe0f)?|(?:\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83e\uddd1\u200d\ud83e\uddd1\u200d\ud83e\uddd2\u200d\ud83e\uddd2|\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83e\uddd1\u200d\ud83e\uddd1\u200d\ud83e\uddd2|\ud83e\uddd1\u200d\ud83e\uddd2\u200d\ud83e\uddd2|\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f|\ud83c\udff3\ufe0f\u200d\ud83c\udf08|\ud83d\ude36\u200d\ud83c\udf2b\ufe0f|\u26d3\ufe0f\u200d\ud83d\udca5|\u2764\ufe0f\u200d\ud83d\udd25|\u2764\ufe0f\u200d\ud83e\ude79|\ud83c\udf44\u200d\ud83d\udfeb|\ud83c\udf4b\u200d\ud83d\udfe9|\ud83c\udff4\u200d\u2620\ufe0f|\ud83d\udc15\u200d\ud83e\uddba|\ud83d\udc26\u200d\ud83d\udd25|\ud83d\udc3b\u200d\u2744\ufe0f|\ud83d\udc41\u200d\ud83d\udde8|\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc6f\u200d\u2640\ufe0f|\ud83d\udc6f\u200d\u2642\ufe0f|\ud83d\ude2e\u200d\ud83d\udca8|\ud83d\ude35\u200d\ud83d\udcab|\ud83d\ude42\u200d\u2194\ufe0f|\ud83d\ude42\u200d\u2195\ufe0f|\ud83e\udd3c\u200d\u2640\ufe0f|\ud83e\udd3c\u200d\u2642\ufe0f|\ud83e\uddd1\u200d\ud83e\uddd2|\ud83e\uddde\u200d\u2640\ufe0f|\ud83e\uddde\u200d\u2642\ufe0f|\ud83e\udddf\u200d\u2640\ufe0f|\ud83e\udddf\u200d\u2642\ufe0f|\ud83d\udc08\u200d\u2b1b|\ud83d\udc26\u200d\u2b1b)|[#*0-9]\ufe0f?\u20e3|(?:[©®\u2122\u265f]\ufe0f)|(?:\ud83c[\udc04\udd70\udd71\udd7e\udd7f\ude02\ude1a\ude2f\ude37\udf21\udf24-\udf2c\udf36\udf7d\udf96\udf97\udf99-\udf9b\udf9e\udf9f\udfcd\udfce\udfd4-\udfdf\udff3\udff5\udff7]|\ud83d[\udc3f\udc41\udcfd\udd49\udd4a\udd6f\udd70\udd73\udd76-\udd79\udd87\udd8a-\udd8d\udda5\udda8\uddb1\uddb2\uddbc\uddc2-\uddc4\uddd1-\uddd3\udddc-\uddde\udde1\udde3\udde8\uddef\uddf3\uddfa\udecb\udecd-\udecf\udee0-\udee5\udee9\udef0\udef3]|[\u203c\u2049\u2139\u2194-\u2199\u21a9\u21aa\u231a\u231b\u2328\u23cf\u23ed-\u23ef\u23f1\u23f2\u23f8-\u23fa\u24c2\u25aa\u25ab\u25b6\u25c0\u25fb-\u25fe\u2600-\u2604\u260e\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262a\u262e\u262f\u2638-\u263a\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267b\u267f\u2692-\u2697\u2699\u269b\u269c\u26a0\u26a1\u26a7\u26aa\u26ab\u26b0\u26b1\u26bd\u26be\u26c4\u26c5\u26c8\u26cf\u26d1\u26d3\u26d4\u26e9\u26ea\u26f0-\u26f5\u26f8\u26fa\u26fd\u2702\u2708\u2709\u270f\u2712\u2714\u2716\u271d\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u2764\u27a1\u2934\u2935\u2b05-\u2b07\u2b1b\u2b1c\u2b50\u2b55\u3030\u303d\u3297\u3299])(?:\ufe0f|(?!\ufe0e))|(?:(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75\udd90]|\ud83e\udef0|[\u261d\u26f7\u26f9\u270c\u270d])(?:\ufe0f|(?!\ufe0e))|(?:\ud83c\udfc3|\ud83d\udeb6|\ud83e\uddce)(?:\ud83c[\udffb-\udfff])?(?:\u200d\u27a1\ufe0f)?|(?:\ud83c[\udf85\udfc2\udfc4\udfc7\udfca]|\ud83d[\udc42\udc43\udc46-\udc50\udc66-\udc69\udc6e\udc70-\udc78\udc7c\udc81-\udc83\udc85-\udc87\udcaa\udd7a\udd95\udd96\ude45-\ude47\ude4b-\ude4f\udea3\udeb4\udeb5\udec0\udecc]|\ud83e[\udd0c\udd0f\udd18-\udd1c\udd1e\udd1f\udd26\udd30-\udd39\udd3d\udd3e\udd77\uddb5\uddb6\uddb8\uddb9\uddbb\uddcd\uddcf\uddd1-\udddd\udec3-\udec5\udef1-\udef8]|[\u270a\u270b]))(?:\ud83c[\udffb-\udfff])?|(?:\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc73\udb40\udc63\udb40\udc74\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f|\ud83c\udde6\ud83c[\udde8-\uddec\uddee\uddf1\uddf2\uddf4\uddf6-\uddfa\uddfc\uddfd\uddff]|\ud83c\udde7\ud83c[\udde6\udde7\udde9-\uddef\uddf1-\uddf4\uddf6-\uddf9\uddfb\uddfc\uddfe\uddff]|\ud83c\udde8\ud83c[\udde6\udde8\udde9\uddeb-\uddee\uddf0-\uddf7\uddfa-\uddff]|\ud83c\udde9\ud83c[\uddea\uddec\uddef\uddf0\uddf2\uddf4\uddff]|\ud83c\uddea\ud83c[\udde6\udde8\uddea\uddec\udded\uddf7-\uddfa]|\ud83c\uddeb\ud83c[\uddee-\uddf0\uddf2\uddf4\uddf7]|\ud83c\uddec\ud83c[\udde6\udde7\udde9-\uddee\uddf1-\uddf3\uddf5-\uddfa\uddfc\uddfe]|\ud83c\udded\ud83c[\uddf0\uddf2\uddf3\uddf7\uddf9\uddfa]|\ud83c\uddee\ud83c[\udde8-\uddea\uddf1-\uddf4\uddf6-\uddf9]|\ud83c\uddef\ud83c[\uddea\uddf2\uddf4\uddf5]|\ud83c\uddf0\ud83c[\uddea\uddec-\uddee\uddf2\uddf3\uddf5\uddf7\uddfc\uddfe\uddff]|\ud83c\uddf1\ud83c[\udde6-\udde8\uddee\uddf0\uddf7-\uddfb\uddfe]|\ud83c\uddf2\ud83c[\udde6\udde8-\udded\uddf0-\uddff]|\ud83c\uddf3\ud83c[\udde6\udde8\uddea-\uddec\uddee\uddf1\uddf4\uddf5\uddf7\uddfa\uddff]|\ud83c\uddf4\ud83c\uddf2|\ud83c\uddf5\ud83c[\udde6\uddea-\udded\uddf0-\uddf3\uddf7-\uddf9\uddfc\uddfe]|\ud83c\uddf6\ud83c\udde6|\ud83c\uddf7\ud83c[\uddea\uddf4\uddf8\uddfa\uddfc]|\ud83c\uddf8\ud83c[\udde6-\uddea\uddec-\uddf4\uddf7-\uddf9\uddfb\uddfd-\uddff]|\ud83c\uddf9\ud83c[\udde6\udde8\udde9\uddeb-\udded\uddef-\uddf4\uddf7\uddf9\uddfb\uddfc\uddff]|\ud83c\uddfa\ud83c[\udde6\uddec\uddf2\uddf3\uddf8\uddfe\uddff]|\ud83c\uddfb\ud83c[\udde6\udde8\uddea\uddec\uddee\uddf3\uddfa]|\ud83c\uddfc\ud83c[\uddeb\uddf8]|\ud83c\uddfd\ud83c\uddf0|\ud83c\uddfe\ud83c[\uddea\uddf9]|\ud83c\uddff\ud83c[\udde6\uddf2\uddfc]|\ud83c[\udccf\udd8e\udd91-\udd9a\udde6-\uddff\ude01\ude32-\ude36\ude38-\ude3a\ude50\ude51\udf00-\udf20\udf2d-\udf35\udf37-\udf7c\udf7e-\udf84\udf86-\udf93\udfa0-\udfc1\udfc5\udfc6\udfc8\udfc9\udfcf-\udfd3\udfe0-\udff0\udff4\udff8-\udfff]|\ud83d[\udc00-\udc3e\udc40\udc44\udc45\udc51-\udc65\udc6a\udc6f\udc79-\udc7b\udc7d-\udc80\udc84\udc88-\udc8e\udc90\udc92-\udca9\udcab-\udcfc\udcff-\udd3d\udd4b-\udd4e\udd50-\udd67\udda4\uddfb-\ude44\ude48-\ude4a\ude80-\udea2\udea4-\udeb3\udeb7-\udebf\udec1-\udec5\uded0-\uded2\uded5-\uded7\udedc-\udedf\udeeb\udeec\udef4-\udefc\udfe0-\udfeb\udff0]|\ud83e[\udd0d\udd0e\udd10-\udd17\udd20-\udd25\udd27-\udd2f\udd3a\udd3c\udd3f-\udd45\udd47-\udd76\udd78-\uddb4\uddb7\uddba\uddbc-\uddcc\uddd0\uddde-\uddff\ude70-\ude7c\ude80-\ude89\ude8f-\udec2\udec6\udece-\udedc\udedf-\udee9]|[\u23e9-\u23ec\u23f0\u23f3\u267e\u26ce\u2705\u2728\u274c\u274e\u2753-\u2755\u2795-\u2797\u27b0\u27bf\ue50a])|\ufe0f/g;
export const emojiRegex = new RegExp(`(${twemojiRegex.source})`);
diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts
index ed47edff9b..ed7d5bfc3a 100644
--- a/packages/backend/src/misc/json-schema.ts
+++ b/packages/backend/src/misc/json-schema.ts
@@ -22,7 +22,7 @@ import { packedFollowingSchema } from '@/models/json-schema/following.js';
import { packedMutingSchema } from '@/models/json-schema/muting.js';
import { packedRenoteMutingSchema } from '@/models/json-schema/renote-muting.js';
import { packedBlockingSchema } from '@/models/json-schema/blocking.js';
-import { packedNoteReactionSchema } from '@/models/json-schema/note-reaction.js';
+import { packedNoteReactionSchema, packedNoteReactionWithNoteSchema } from '@/models/json-schema/note-reaction.js';
import { packedHashtagSchema } from '@/models/json-schema/hashtag.js';
import { packedInviteCodeSchema } from '@/models/json-schema/invite-code.js';
import { packedPageBlockSchema, packedPageSchema } from '@/models/json-schema/page.js';
@@ -65,6 +65,7 @@ import {
packedMetaDetailedSchema,
packedMetaLiteSchema,
} from '@/models/json-schema/meta.js';
+import { packedUserWebhookSchema } from '@/models/json-schema/user-webhook.js';
import { packedSystemWebhookSchema } from '@/models/json-schema/system-webhook.js';
import { packedAbuseReportNotificationRecipientSchema } from '@/models/json-schema/abuse-report-notification-recipient.js';
import { packedChatMessageSchema, packedChatMessageLiteSchema, packedChatMessageLiteForRoomSchema, packedChatMessageLiteFor1on1Schema } from '@/models/json-schema/chat-message.js';
@@ -92,6 +93,7 @@ export const refs = {
Note: packedNoteSchema,
NoteDraft: packedNoteDraftSchema,
NoteReaction: packedNoteReactionSchema,
+ NoteReactionWithNote: packedNoteReactionWithNoteSchema,
NoteFavorite: packedNoteFavoriteSchema,
Notification: packedNotificationSchema,
DriveFile: packedDriveFileSchema,
@@ -133,6 +135,7 @@ export const refs = {
MetaLite: packedMetaLiteSchema,
MetaDetailedOnly: packedMetaDetailedOnlySchema,
MetaDetailed: packedMetaDetailedSchema,
+ UserWebhook: packedUserWebhookSchema,
SystemWebhook: packedSystemWebhookSchema,
AbuseReportNotificationRecipient: packedAbuseReportNotificationRecipientSchema,
ChatMessage: packedChatMessageSchema,
diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts
index 85c10ab666..f8021a7a84 100644
--- a/packages/backend/src/models/Meta.ts
+++ b/packages/backend/src/models/Meta.ts
@@ -654,7 +654,7 @@ export class MiMeta {
@Column('varchar', {
length: 128,
- default: 'all',
+ default: 'none',
})
public federation: 'all' | 'specified' | 'none';
@@ -701,6 +701,26 @@ export class MiMeta {
default: true,
})
public allowExternalApRedirect: boolean;
+
+ @Column('boolean', {
+ default: false,
+ })
+ public enableRemoteNotesCleaning: boolean;
+
+ @Column('integer', {
+ default: 60, // minutes
+ })
+ public remoteNotesCleaningMaxProcessingDurationInMinutes: number;
+
+ @Column('integer', {
+ default: 90, // days
+ })
+ public remoteNotesCleaningExpiryDaysForEachNotes: number;
+
+ @Column('jsonb', {
+ default: { },
+ })
+ public clientOptions: Record<string, any>;
}
export type SoftwareSuspension = {
diff --git a/packages/backend/src/models/Note.ts b/packages/backend/src/models/Note.ts
index 9822ec94e4..26d5c1d535 100644
--- a/packages/backend/src/models/Note.ts
+++ b/packages/backend/src/models/Note.ts
@@ -36,7 +36,7 @@ export class MiNote {
public replyId: MiNote['id'] | null;
@ManyToOne(type => MiNote, {
- onDelete: 'CASCADE',
+ createForeignKeyConstraints: false,
})
@JoinColumn()
public reply: MiNote | null;
@@ -50,7 +50,7 @@ export class MiNote {
public renoteId: MiNote['id'] | null;
@ManyToOne(type => MiNote, {
- onDelete: 'CASCADE',
+ createForeignKeyConstraints: false,
})
@JoinColumn()
public renote: MiNote | null;
@@ -114,6 +114,13 @@ export class MiNote {
})
public clippedCount: number;
+ // The number of note page blocks referencing this note.
+ // This column is used by Remote Note Cleaning and manually updated rather than automatically with triggers.
+ @Column('smallint', {
+ default: 0,
+ })
+ public pageCount: number;
+
@Column('jsonb', {
default: {},
})
diff --git a/packages/backend/src/models/Page.ts b/packages/backend/src/models/Page.ts
index 0b59e7a92c..d112a66c04 100644
--- a/packages/backend/src/models/Page.ts
+++ b/packages/backend/src/models/Page.ts
@@ -69,7 +69,7 @@ export class MiPage {
public eyeCatchingImageId: MiDriveFile['id'] | null;
@ManyToOne(type => MiDriveFile, {
- onDelete: 'CASCADE',
+ onDelete: 'SET NULL',
})
@JoinColumn()
public eyeCatchingImage: MiDriveFile | null;
diff --git a/packages/backend/src/models/json-schema/meta.ts b/packages/backend/src/models/json-schema/meta.ts
index 2cd7620af0..357ff26041 100644
--- a/packages/backend/src/models/json-schema/meta.ts
+++ b/packages/backend/src/models/json-schema/meta.ts
@@ -71,6 +71,10 @@ export const packedMetaLiteSchema = {
type: 'string',
optional: false, nullable: true,
},
+ clientOptions: {
+ type: 'object',
+ optional: false, nullable: false,
+ },
disableRegistration: {
type: 'boolean',
optional: false, nullable: false,
diff --git a/packages/backend/src/models/json-schema/note-reaction.ts b/packages/backend/src/models/json-schema/note-reaction.ts
index 95658ace1f..04c9f34232 100644
--- a/packages/backend/src/models/json-schema/note-reaction.ts
+++ b/packages/backend/src/models/json-schema/note-reaction.ts
@@ -10,7 +10,6 @@ export const packedNoteReactionSchema = {
type: 'string',
optional: false, nullable: false,
format: 'id',
- example: 'xxxxxxxxxx',
},
createdAt: {
type: 'string',
@@ -28,3 +27,33 @@ export const packedNoteReactionSchema = {
},
},
} as const;
+
+export const packedNoteReactionWithNoteSchema = {
+ type: 'object',
+ properties: {
+ id: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ },
+ createdAt: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'date-time',
+ },
+ user: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'UserLite',
+ },
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ note: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'Note',
+ },
+ },
+} as const;
diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts
index c9cdbd5d89..0b9234cb81 100644
--- a/packages/backend/src/models/json-schema/role.ts
+++ b/packages/backend/src/models/json-schema/role.ts
@@ -212,6 +212,10 @@ export const packedRolePoliciesSchema = {
type: 'boolean',
optional: false, nullable: false,
},
+ canSearchUsers: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
canUseTranslator: {
type: 'boolean',
optional: false, nullable: false,
diff --git a/packages/backend/src/models/json-schema/user-webhook.ts b/packages/backend/src/models/json-schema/user-webhook.ts
new file mode 100644
index 0000000000..8ea0991716
--- /dev/null
+++ b/packages/backend/src/models/json-schema/user-webhook.ts
@@ -0,0 +1,55 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { webhookEventTypes } from '@/models/Webhook.js';
+
+export const packedUserWebhookSchema = {
+ type: 'object',
+ properties: {
+ id: {
+ type: 'string',
+ format: 'id',
+ optional: false, nullable: false,
+ },
+ userId: {
+ type: 'string',
+ format: 'id',
+ optional: false, nullable: false,
+ },
+ name: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ on: {
+ type: 'array',
+ items: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: webhookEventTypes,
+ },
+ },
+ url: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ secret: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ active: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ latestSentAt: {
+ type: 'string',
+ format: 'date-time',
+ optional: false, nullable: true,
+ },
+ latestStatus: {
+ type: 'integer',
+ optional: false, nullable: true,
+ },
+ },
+} as const;
diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index 2b5f706ff9..c507d8d5c6 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -65,7 +65,7 @@ export const packedUserLiteSchema = {
avatarUrl: {
type: 'string',
format: 'url',
- nullable: true, optional: false,
+ nullable: false, optional: false,
},
avatarBlurhash: {
type: 'string',
@@ -465,11 +465,11 @@ export const packedMeDetailedOnlySchema = {
},
isModerator: {
type: 'boolean',
- nullable: true, optional: false,
+ nullable: false, optional: false,
},
isAdmin: {
type: 'boolean',
- nullable: true, optional: false,
+ nullable: false, optional: false,
},
injectFeaturedNote: {
type: 'boolean',
@@ -591,7 +591,7 @@ export const packedMeDetailedOnlySchema = {
},
mutedInstances: {
type: 'array',
- nullable: true, optional: false,
+ nullable: false, optional: false,
items: {
type: 'string',
nullable: false, optional: false,
diff --git a/packages/backend/src/queue/QueueProcessorModule.ts b/packages/backend/src/queue/QueueProcessorModule.ts
index 9044285bf6..e01414cd53 100644
--- a/packages/backend/src/queue/QueueProcessorModule.ts
+++ b/packages/backend/src/queue/QueueProcessorModule.ts
@@ -6,7 +6,6 @@
import { Module } from '@nestjs/common';
import { CoreModule } from '@/core/CoreModule.js';
import { GlobalModule } from '@/GlobalModule.js';
-import { CheckModeratorsActivityProcessorService } from '@/queue/processors/CheckModeratorsActivityProcessorService.js';
import { QueueLoggerService } from './QueueLoggerService.js';
import { QueueProcessorService } from './QueueProcessorService.js';
import { DeliverProcessorService } from './processors/DeliverProcessorService.js';
@@ -18,6 +17,8 @@ import { CheckExpiredMutingsProcessorService } from './processors/CheckExpiredMu
import { BakeBufferedReactionsProcessorService } from './processors/BakeBufferedReactionsProcessorService.js';
import { CleanChartsProcessorService } from './processors/CleanChartsProcessorService.js';
import { CleanProcessorService } from './processors/CleanProcessorService.js';
+import { CheckModeratorsActivityProcessorService } from './processors/CheckModeratorsActivityProcessorService.js';
+import { CleanRemoteNotesProcessorService } from './processors/CleanRemoteNotesProcessorService.js';
import { CleanRemoteFilesProcessorService } from './processors/CleanRemoteFilesProcessorService.js';
import { DeleteAccountProcessorService } from './processors/DeleteAccountProcessorService.js';
import { DeleteDriveFilesProcessorService } from './processors/DeleteDriveFilesProcessorService.js';
@@ -83,6 +84,7 @@ import { RelationshipProcessorService } from './processors/RelationshipProcessor
AggregateRetentionProcessorService,
CheckExpiredMutingsProcessorService,
CheckModeratorsActivityProcessorService,
+ CleanRemoteNotesProcessorService,
QueueProcessorService,
],
exports: [
diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts
index c98ebcdcd9..7b64182754 100644
--- a/packages/backend/src/queue/QueueProcessorService.ts
+++ b/packages/backend/src/queue/QueueProcessorService.ts
@@ -43,6 +43,7 @@ import { CheckExpiredMutingsProcessorService } from './processors/CheckExpiredMu
import { BakeBufferedReactionsProcessorService } from './processors/BakeBufferedReactionsProcessorService.js';
import { CleanProcessorService } from './processors/CleanProcessorService.js';
import { AggregateRetentionProcessorService } from './processors/AggregateRetentionProcessorService.js';
+import { CleanRemoteNotesProcessorService } from './processors/CleanRemoteNotesProcessorService.js';
import { QueueLoggerService } from './QueueLoggerService.js';
import { QUEUE, baseWorkerOptions } from './const.js';
@@ -123,6 +124,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
private bakeBufferedReactionsProcessorService: BakeBufferedReactionsProcessorService,
private checkModeratorsActivityProcessorService: CheckModeratorsActivityProcessorService,
private cleanProcessorService: CleanProcessorService,
+ private cleanRemoteNotesProcessorService: CleanRemoteNotesProcessorService,
) {
this.logger = this.queueLoggerService.logger;
@@ -164,6 +166,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
case 'bakeBufferedReactions': return this.bakeBufferedReactionsProcessorService.process();
case 'checkModeratorsActivity': return this.checkModeratorsActivityProcessorService.process();
case 'clean': return this.cleanProcessorService.process();
+ case 'cleanRemoteNotes': return this.cleanRemoteNotesProcessorService.process(job);
default: throw new Error(`unrecognized job type ${job.name} for system`);
}
};
diff --git a/packages/backend/src/queue/processors/CleanRemoteNotesProcessorService.ts b/packages/backend/src/queue/processors/CleanRemoteNotesProcessorService.ts
new file mode 100644
index 0000000000..36c34c753c
--- /dev/null
+++ b/packages/backend/src/queue/processors/CleanRemoteNotesProcessorService.ts
@@ -0,0 +1,289 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { setTimeout } from 'node:timers/promises';
+import { Inject, Injectable } from '@nestjs/common';
+import { DataSource, IsNull, LessThan, QueryFailedError, Not } from 'typeorm';
+import { DI } from '@/di-symbols.js';
+import type { MiMeta, MiNote, NotesRepository } from '@/models/_.js';
+import type Logger from '@/logger.js';
+import { bindThis } from '@/decorators.js';
+import { IdService } from '@/core/IdService.js';
+import { QueueLoggerService } from '../QueueLoggerService.js';
+import type * as Bull from 'bullmq';
+
+@Injectable()
+export class CleanRemoteNotesProcessorService {
+ private logger: Logger;
+
+ constructor(
+ @Inject(DI.meta)
+ private meta: MiMeta,
+
+ @Inject(DI.notesRepository)
+ private notesRepository: NotesRepository,
+
+ @Inject(DI.db)
+ private db: DataSource,
+
+ private idService: IdService,
+ private queueLoggerService: QueueLoggerService,
+ ) {
+ this.logger = this.queueLoggerService.logger.createSubLogger('clean-remote-notes');
+ }
+
+ @bindThis
+ private computeProgress(minId: string, maxId: string, cursorLeft: string) {
+ const minTs = this.idService.parse(minId).date.getTime();
+ const maxTs = this.idService.parse(maxId).date.getTime();
+ const cursorTs = this.idService.parse(cursorLeft).date.getTime();
+
+ return ((cursorTs - minTs) / (maxTs - minTs)) * 100;
+ }
+
+ @bindThis
+ public async process(job: Bull.Job<Record<string, unknown>>): Promise<{
+ deletedCount: number;
+ oldest: number | null;
+ newest: number | null;
+ skipped: boolean;
+ transientErrors: number;
+ }> {
+ if (!this.meta.enableRemoteNotesCleaning) {
+ this.logger.info('Remote notes cleaning is disabled, skipping...');
+ return {
+ deletedCount: 0,
+ oldest: null,
+ newest: null,
+ skipped: true,
+ transientErrors: 0,
+ };
+ }
+
+ this.logger.info('cleaning remote notes...');
+
+ const maxDuration = this.meta.remoteNotesCleaningMaxProcessingDurationInMinutes * 60 * 1000; // Convert minutes to milliseconds
+ const startAt = Date.now();
+
+ //#region queries
+ // The date limit for the newest note to be considered for deletion.
+ // All notes newer than this limit will always be retained.
+ const newestLimit = this.idService.gen(Date.now() - (1000 * 60 * 60 * 24 * this.meta.remoteNotesCleaningExpiryDaysForEachNotes));
+
+ // The condition for removing the notes.
+ // The note must be:
+ // - old enough (older than the newestLimit)
+ // - a remote note (userHost is not null).
+ // - not have clipped
+ // - not have pinned on the user profile
+ // - not has been favorite by any user
+ const removalCriteria = [
+ 'note."id" < :newestLimit',
+ 'note."clippedCount" = 0',
+ 'note."pageCount" = 0',
+ 'note."userHost" IS NOT NULL',
+ 'NOT EXISTS (SELECT 1 FROM user_note_pining WHERE "noteId" = note."id")',
+ 'NOT EXISTS (SELECT 1 FROM note_favorite WHERE "noteId" = note."id")',
+ 'NOT EXISTS (SELECT 1 FROM note_reaction INNER JOIN "user" ON note_reaction."userId" = "user".id WHERE note_reaction."noteId" = note."id" AND "user"."host" IS NULL)',
+ ].join(' AND ');
+
+ const minId = (await this.notesRepository.createQueryBuilder('note')
+ .select('MIN(note.id)', 'minId')
+ .where({
+ id: LessThan(newestLimit),
+ userHost: Not(IsNull()),
+ replyId: IsNull(),
+ renoteId: IsNull(),
+ })
+ .getRawOne<{ minId?: MiNote['id'] }>())?.minId;
+
+ if (!minId) {
+ this.logger.info('No notes can possibly be deleted, skipping...');
+ return {
+ deletedCount: 0,
+ oldest: null,
+ newest: null,
+ skipped: false,
+ transientErrors: 0,
+ };
+ }
+
+ // start with a conservative limit and adjust it based on the query duration
+ const minimumLimit = 10;
+ let currentLimit = 100;
+ let cursorLeft = '0';
+
+ const candidateNotesCteName = 'candidate_notes';
+
+ // tree walk down all root notes, short-circuit when the first unremovable note is found
+ const candidateNotesQueryBase = this.notesRepository.createQueryBuilder('note')
+ .select('note."id"', 'id')
+ .addSelect('note."replyId"', 'replyId')
+ .addSelect('note."renoteId"', 'renoteId')
+ .addSelect('note."id"', 'rootId')
+ .addSelect('TRUE', 'isRemovable')
+ .addSelect('TRUE', 'isBase')
+ .where('note."id" > :cursorLeft')
+ .andWhere(removalCriteria)
+ .andWhere({ replyId: IsNull(), renoteId: IsNull() });
+
+ const candidateNotesQueryInductive = this.notesRepository.createQueryBuilder('note')
+ .select('note.id', 'id')
+ .addSelect('note."replyId"', 'replyId')
+ .addSelect('note."renoteId"', 'renoteId')
+ .addSelect('parent."rootId"', 'rootId')
+ .addSelect(removalCriteria, 'isRemovable')
+ .addSelect('FALSE', 'isBase')
+ .innerJoin(candidateNotesCteName, 'parent', 'parent."id" = note."replyId" OR parent."id" = note."renoteId"')
+ .where('parent."isRemovable" = TRUE');
+
+ // A note tree can be deleted if there are no unremovable rows with the same rootId.
+ //
+ // `candidate_notes` will have the following structure after recursive query (some columns omitted):
+ // After performing a LEFT JOIN with `candidate_notes` as `unremovable`,
+ // the note tree containing unremovable notes will be anti-joined.
+ // For removable rows, the `unremovable` columns will have `NULL` values.
+ // | id | rootId | isRemovable |
+ // |-----|--------|-------------|
+ // | aaa | aaa | TRUE |
+ // | bbb | aaa | FALSE |
+ // | ccc | aaa | FALSE |
+ // | ddd | ddd | TRUE |
+ // | eee | ddd | TRUE |
+ // | fff | fff | TRUE |
+ // | ggg | ggg | FALSE |
+ //
+ const candidateNotesQuery = this.db.createQueryBuilder()
+ .select(`"${candidateNotesCteName}"."id"`, 'id')
+ .addSelect('unremovable."id" IS NULL', 'isRemovable')
+ .addSelect(`BOOL_OR("${candidateNotesCteName}"."isBase")`, 'isBase')
+ .addCommonTableExpression(
+ `((SELECT "base".* FROM (${candidateNotesQueryBase.orderBy('note.id', 'ASC').limit(currentLimit).getQuery()}) AS "base") UNION ${candidateNotesQueryInductive.getQuery()})`,
+ candidateNotesCteName,
+ { recursive: true },
+ )
+ .from(candidateNotesCteName, candidateNotesCteName)
+ .leftJoin(candidateNotesCteName, 'unremovable', `unremovable."rootId" = "${candidateNotesCteName}"."rootId" AND unremovable."isRemovable" = FALSE`)
+ .groupBy(`"${candidateNotesCteName}"."id"`)
+ .addGroupBy('unremovable."id" IS NULL');
+
+ const stats = {
+ deletedCount: 0,
+ oldest: null as number | null,
+ newest: null as number | null,
+ };
+
+ let lowThroughputWarned = false;
+ let transientErrors = 0;
+ for (;;) {
+ //#region check time
+ const batchBeginAt = Date.now();
+
+ const elapsed = batchBeginAt - startAt;
+
+ const progress = this.computeProgress(minId, newestLimit, cursorLeft > minId ? cursorLeft : minId);
+
+ if (elapsed >= maxDuration) {
+ job.log(`Reached maximum duration of ${maxDuration}ms, stopping... (last cursor: ${cursorLeft}, final progress ${progress}%)`);
+ job.updateProgress(100);
+ break;
+ }
+
+ const wallClockUsage = elapsed / maxDuration;
+ if (wallClockUsage > 0.5 && progress < 50 && !lowThroughputWarned) {
+ const msg = `Not projected to finish in time! (wall clock usage ${wallClockUsage * 100}% at ${progress}%, current limit ${currentLimit})`;
+ this.logger.warn(msg);
+ job.log(msg);
+ lowThroughputWarned = true;
+ }
+ job.updateProgress(progress);
+ //#endregion
+
+ const queryBegin = performance.now();
+ let noteIds = null;
+
+ try {
+ noteIds = await candidateNotesQuery.setParameters(
+ { newestLimit, cursorLeft },
+ ).getRawMany<{ id: MiNote['id'], isRemovable: boolean, isBase: boolean }>();
+ } catch (e) {
+ if (currentLimit > minimumLimit && e instanceof QueryFailedError && e.driverError?.code === '57014') {
+ // Statement timeout (maybe suddenly hit a large note tree), reduce the limit and try again
+ // continuous failures will eventually converge to currentLimit == minimumLimit and then throw
+ currentLimit = Math.max(minimumLimit, Math.floor(currentLimit * 0.25));
+ continue;
+ }
+ throw e;
+ }
+
+ if (noteIds.length === 0) {
+ job.log('No more notes to clean.');
+ break;
+ }
+
+ const queryDuration = performance.now() - queryBegin;
+ // try to adjust such that each query takes about 1~5 seconds and reasonable NodeJS heap so the task stays responsive
+ // this should not oscillate..
+ if (queryDuration > 5000 || noteIds.length > 5000) {
+ currentLimit = Math.floor(currentLimit * 0.5);
+ } else if (queryDuration < 1000 && noteIds.length < 1000) {
+ currentLimit = Math.floor(currentLimit * 1.5);
+ }
+ // clamp to a sane range
+ currentLimit = Math.min(Math.max(currentLimit, minimumLimit), 5000);
+
+ const deletableNoteIds = noteIds.filter(result => result.isRemovable).map(result => result.id);
+ if (deletableNoteIds.length > 0) {
+ try {
+ await this.notesRepository.delete(deletableNoteIds);
+
+ for (const id of deletableNoteIds) {
+ const t = this.idService.parse(id).date.getTime();
+ if (stats.oldest === null || t < stats.oldest) {
+ stats.oldest = t;
+ }
+ if (stats.newest === null || t > stats.newest) {
+ stats.newest = t;
+ }
+ }
+
+ stats.deletedCount += deletableNoteIds.length;
+ } catch (e) {
+ // check for integrity violation errors (class 23) that might have occurred between the check and the delete
+ // we can safely continue to the next batch
+ if (e instanceof QueryFailedError && e.driverError?.code?.startsWith('23')) {
+ transientErrors++;
+ job.log(`Error deleting notes: ${e} (transient race condition?)`);
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ cursorLeft = noteIds.filter(result => result.isBase).reduce((max, { id }) => id > max ? id : max, cursorLeft);
+
+ job.log(`Deleted ${noteIds.length} notes; ${Date.now() - batchBeginAt}ms`);
+
+ if (process.env.NODE_ENV !== 'test') {
+ await setTimeout(Math.min(1000 * 5, queryDuration)); // Wait a moment to avoid overwhelming the db
+ }
+ };
+
+ if (transientErrors > 0) {
+ const msg = `${transientErrors} transient errors occurred while cleaning remote notes. You may need a second pass to complete the cleaning.`;
+ this.logger.warn(msg);
+ job.log(msg);
+ }
+ this.logger.succ('cleaning of remote notes completed.');
+
+ return {
+ deletedCount: stats.deletedCount,
+ oldest: stats.oldest,
+ newest: stats.newest,
+ skipped: false,
+ transientErrors,
+ };
+ }
+}
diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts
index 14a53e0c42..b643c2a6d0 100644
--- a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts
+++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts
@@ -6,7 +6,7 @@
import { Inject, Injectable } from '@nestjs/common';
import { MoreThan } from 'typeorm';
import { DI } from '@/di-symbols.js';
-import type { DriveFilesRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
+import type { DriveFilesRepository, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
import type Logger from '@/logger.js';
import { DriveService } from '@/core/DriveService.js';
import type { MiDriveFile } from '@/models/DriveFile.js';
@@ -14,6 +14,7 @@ import type { MiNote } from '@/models/Note.js';
import { EmailService } from '@/core/EmailService.js';
import { bindThis } from '@/decorators.js';
import { SearchService } from '@/core/SearchService.js';
+import { PageService } from '@/core/PageService.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
import type { DbUserDeleteJobData } from '../types.js';
@@ -35,7 +36,11 @@ export class DeleteAccountProcessorService {
@Inject(DI.driveFilesRepository)
private driveFilesRepository: DriveFilesRepository,
+ @Inject(DI.pagesRepository)
+ private pagesRepository: PagesRepository,
+
private driveService: DriveService,
+ private pageService: PageService,
private emailService: EmailService,
private queueLoggerService: QueueLoggerService,
private searchService: SearchService,
@@ -112,6 +117,28 @@ export class DeleteAccountProcessorService {
this.logger.succ('All of files deleted');
}
+ {
+ // delete pages. Necessary for decrementing pageCount of notes.
+ while (true) {
+ const pages = await this.pagesRepository.find({
+ where: {
+ userId: user.id,
+ },
+ take: 100,
+ order: {
+ id: 1,
+ },
+ });
+
+ if (pages.length === 0) {
+ break;
+ }
+ for (const page of pages) {
+ await this.pageService.delete(user, page.id);
+ }
+ }
+ }
+
{ // Send email notification
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
if (profile.email && profile.emailVerified) {
diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts
index 23c085ee27..7325c53df0 100644
--- a/packages/backend/src/server/ServerService.ts
+++ b/packages/backend/src/server/ServerService.ts
@@ -238,30 +238,6 @@ export class ServerService implements OnApplicationShutdown {
}
});
- fastify.get<{ Params: { code: string } }>('/verify-email/:code', async (request, reply) => {
- const profile = await this.userProfilesRepository.findOneBy({
- emailVerifyCode: request.params.code,
- });
-
- if (profile != null) {
- await this.userProfilesRepository.update({ userId: profile.userId }, {
- emailVerified: true,
- emailVerifyCode: null,
- });
-
- this.globalEventService.publishMainStream(profile.userId, 'meUpdated', await this.userEntityService.pack(profile.userId, { id: profile.userId }, {
- schema: 'MeDetailed',
- includeSecrets: true,
- }));
-
- reply.code(200).send('Verification succeeded! メールアドレスã®èªè¨¼ã«æˆåŠŸã—ã¾ã—ãŸã€‚');
- return;
- } else {
- reply.code(404).send('Verification failed. Please try again. メールアドレスã®èªè¨¼ã«å¤±æ•—ã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„');
- return;
- }
- });
-
fastify.register(this.clientServerService.createServer);
this.streamingApiServerService.attach(fastify.server);
diff --git a/packages/backend/src/server/api/GetterService.ts b/packages/backend/src/server/api/GetterService.ts
index 444e6db744..8f4213dfb6 100644
--- a/packages/backend/src/server/api/GetterService.ts
+++ b/packages/backend/src/server/api/GetterService.ts
@@ -40,8 +40,8 @@ export class GetterService {
}
@bindThis
- public async getNoteWithUser(noteId: MiNote['id']) {
- const note = await this.notesRepository.findOne({ where: { id: noteId }, relations: ['user'] });
+ public async getNoteWithRelations(noteId: MiNote['id']) {
+ const note = await this.notesRepository.findOne({ where: { id: noteId }, relations: ['user', 'reply', 'renote', 'reply.user', 'renote.user'] });
if (note == null) {
throw new IdentifiableError('9725d0ce-ba28-4dde-95a7-2cbb2c15de24', 'No such note.');
diff --git a/packages/backend/src/server/api/endpoint-list.ts b/packages/backend/src/server/api/endpoint-list.ts
index 5c4a58a6fc..f4aa07875d 100644
--- a/packages/backend/src/server/api/endpoint-list.ts
+++ b/packages/backend/src/server/api/endpoint-list.ts
@@ -70,6 +70,7 @@ export * as 'admin/queue/inbox-delayed' from './endpoints/admin/queue/inbox-dela
export * as 'admin/queue/retry-job' from './endpoints/admin/queue/retry-job.js';
export * as 'admin/queue/remove-job' from './endpoints/admin/queue/remove-job.js';
export * as 'admin/queue/show-job' from './endpoints/admin/queue/show-job.js';
+export * as 'admin/queue/show-job-logs' from './endpoints/admin/queue/show-job-logs.js';
export * as 'admin/queue/promote-jobs' from './endpoints/admin/queue/promote-jobs.js';
export * as 'admin/queue/jobs' from './endpoints/admin/queue/jobs.js';
export * as 'admin/queue/stats' from './endpoints/admin/queue/stats.js';
@@ -411,6 +412,7 @@ export * as 'users/search' from './endpoints/users/search.js';
export * as 'users/search-by-username-and-host' from './endpoints/users/search-by-username-and-host.js';
export * as 'users/show' from './endpoints/users/show.js';
export * as 'users/update-memo' from './endpoints/users/update-memo.js';
+export * as 'verify-email' from './endpoints/verify-email.js';
export * as 'chat/messages/create-to-user' from './endpoints/chat/messages/create-to-user.js';
export * as 'chat/messages/create-to-room' from './endpoints/chat/messages/create-to-room.js';
export * as 'chat/messages/delete' from './endpoints/chat/messages/delete.js';
diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
index 81a788de2b..804bd5d9b9 100644
--- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
@@ -49,6 +49,34 @@ export const meta = {
type: 'string',
optional: false, nullable: false,
},
+ icon: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ display: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ isActive: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ forExistingUsers: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ silence: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ needConfirmationToRead: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ userId: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
imageUrl: {
type: 'string',
optional: false, nullable: true,
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 b84a5c73f9..e7a70d0762 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
@@ -157,6 +157,22 @@ export const meta = {
type: 'boolean',
optional: false, nullable: false,
},
+ maybeSensitive: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ maybePorn: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ requestIp: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ requestHeaders: {
+ type: 'object',
+ optional: false, nullable: true,
+ },
},
},
} as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index 924163afbb..21099c0a8c 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -223,10 +223,12 @@ export const meta = {
sensitiveMediaDetection: {
type: 'string',
optional: false, nullable: false,
+ enum: ['none', 'all', 'local', 'remote'],
},
sensitiveMediaDetectionSensitivity: {
type: 'string',
optional: false, nullable: false,
+ enum: ['medium', 'low', 'high', 'veryLow', 'veryHigh'],
},
setSensitiveFlagAutomatically: {
type: 'boolean',
@@ -425,6 +427,10 @@ export const meta = {
type: 'string',
optional: false, nullable: true,
},
+ clientOptions: {
+ type: 'object',
+ optional: false, nullable: false,
+ },
description: {
type: 'string',
optional: false, nullable: true,
@@ -469,6 +475,10 @@ export const meta = {
type: 'string',
optional: false, nullable: true,
},
+ feedbackUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
summalyProxy: {
type: 'string',
optional: false, nullable: true,
@@ -571,6 +581,18 @@ export const meta = {
type: 'boolean',
optional: false, nullable: false,
},
+ enableRemoteNotesCleaning: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ remoteNotesCleaningExpiryDaysForEachNotes: {
+ type: 'number',
+ optional: false, nullable: false,
+ },
+ remoteNotesCleaningMaxProcessingDurationInMinutes: {
+ type: 'number',
+ optional: false, nullable: false,
+ },
},
},
} as const;
@@ -638,6 +660,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
logoImageUrl: instance.logoImageUrl,
defaultLightTheme: instance.defaultLightTheme,
defaultDarkTheme: instance.defaultDarkTheme,
+ clientOptions: instance.clientOptions,
enableEmail: instance.enableEmail,
enableServiceWorker: instance.enableServiceWorker,
translatorAvailable: instance.deeplAuthKey != null,
@@ -722,6 +745,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
proxyRemoteFiles: instance.proxyRemoteFiles,
signToActivityPubGet: instance.signToActivityPubGet,
allowExternalApRedirect: instance.allowExternalApRedirect,
+ enableRemoteNotesCleaning: instance.enableRemoteNotesCleaning,
+ remoteNotesCleaningExpiryDaysForEachNotes: instance.remoteNotesCleaningExpiryDaysForEachNotes,
+ remoteNotesCleaningMaxProcessingDurationInMinutes: instance.remoteNotesCleaningMaxProcessingDurationInMinutes,
};
});
}
diff --git a/packages/backend/src/server/api/endpoints/admin/queue/show-job-logs.ts b/packages/backend/src/server/api/endpoints/admin/queue/show-job-logs.ts
new file mode 100644
index 0000000000..b9292ed12a
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/queue/show-job-logs.ts
@@ -0,0 +1,45 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { QUEUE_TYPES, QueueService } from '@/core/QueueService.js';
+
+export const meta = {
+ tags: ['admin'],
+
+ requireCredential: true,
+ requireModerator: true,
+ kind: 'read:admin:queue',
+
+ res: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ optional: false, nullable: false,
+ type: 'string',
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ queue: { type: 'string', enum: QUEUE_TYPES },
+ jobId: { type: 'string' },
+ },
+ required: ['queue', 'jobId'],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+ constructor(
+ private queueService: QueueService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ return this.queueService.queueGetJobLogs(ps.queue, ps.jobId);
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/system-webhook/create.ts b/packages/backend/src/server/api/endpoints/admin/system-webhook/create.ts
index 28071e7a33..93d293ed41 100644
--- a/packages/backend/src/server/api/endpoints/admin/system-webhook/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/system-webhook/create.ts
@@ -48,8 +48,8 @@ export const paramDef = {
},
secret: {
type: 'string',
- minLength: 1,
maxLength: 1024,
+ default: '',
},
},
required: [
@@ -57,7 +57,6 @@ export const paramDef = {
'name',
'on',
'url',
- 'secret',
],
} as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/system-webhook/update.ts b/packages/backend/src/server/api/endpoints/admin/system-webhook/update.ts
index 8d68bb8f87..e021806398 100644
--- a/packages/backend/src/server/api/endpoints/admin/system-webhook/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/system-webhook/update.ts
@@ -52,8 +52,8 @@ export const paramDef = {
},
secret: {
type: 'string',
- minLength: 1,
maxLength: 1024,
+ default: '',
},
},
required: [
@@ -62,7 +62,6 @@ export const paramDef = {
'name',
'on',
'url',
- 'secret',
],
} as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
index 578aa2b662..a1a2a99d6e 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -67,6 +67,7 @@ export const paramDef = {
description: { type: 'string', nullable: true },
defaultLightTheme: { type: 'string', nullable: true },
defaultDarkTheme: { type: 'string', nullable: true },
+ clientOptions: { type: 'object', nullable: false },
cacheRemoteFiles: { type: 'boolean' },
cacheRemoteSensitiveFiles: { type: 'boolean' },
emailRequiredForSignup: { type: 'boolean' },
@@ -205,6 +206,9 @@ export const paramDef = {
proxyRemoteFiles: { type: 'boolean' },
signToActivityPubGet: { type: 'boolean' },
allowExternalApRedirect: { type: 'boolean' },
+ enableRemoteNotesCleaning: { type: 'boolean' },
+ remoteNotesCleaningExpiryDaysForEachNotes: { type: 'number' },
+ remoteNotesCleaningMaxProcessingDurationInMinutes: { type: 'number' },
},
required: [],
} as const;
@@ -323,6 +327,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
set.defaultDarkTheme = ps.defaultDarkTheme;
}
+ if (ps.clientOptions !== undefined) {
+ set.clientOptions = ps.clientOptions;
+ }
+
if (ps.cacheRemoteFiles !== undefined) {
set.cacheRemoteFiles = ps.cacheRemoteFiles;
}
@@ -723,6 +731,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
set.allowExternalApRedirect = ps.allowExternalApRedirect;
}
+ if (ps.enableRemoteNotesCleaning !== undefined) {
+ set.enableRemoteNotesCleaning = ps.enableRemoteNotesCleaning;
+ }
+
+ if (ps.remoteNotesCleaningExpiryDaysForEachNotes !== undefined) {
+ set.remoteNotesCleaningExpiryDaysForEachNotes = ps.remoteNotesCleaningExpiryDaysForEachNotes;
+ }
+
+ if (ps.remoteNotesCleaningMaxProcessingDurationInMinutes !== undefined) {
+ set.remoteNotesCleaningMaxProcessingDurationInMinutes = ps.remoteNotesCleaningMaxProcessingDurationInMinutes;
+ }
+
const before = await this.metaService.fetch(true);
await this.metaService.update(set);
diff --git a/packages/backend/src/server/api/endpoints/chat/read-all.ts b/packages/backend/src/server/api/endpoints/chat/read-all.ts
index 2ed9497eef..e2d9601aa6 100644
--- a/packages/backend/src/server/api/endpoints/chat/read-all.ts
+++ b/packages/backend/src/server/api/endpoints/chat/read-all.ts
@@ -32,6 +32,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private chatService: ChatService,
) {
super(meta, paramDef, async (ps, me) => {
+ await this.chatService.checkChatAvailability(me.id, 'read');
+
await this.chatService.readAllChatMessages(me.id);
});
}
diff --git a/packages/backend/src/server/api/endpoints/clips/list.ts b/packages/backend/src/server/api/endpoints/clips/list.ts
index 2e4a3ff820..af20ea9f8d 100644
--- a/packages/backend/src/server/api/endpoints/clips/list.ts
+++ b/packages/backend/src/server/api/endpoints/clips/list.ts
@@ -5,6 +5,7 @@
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
+import { QueryService } from '@/core/QueryService.js';
import type { ClipsRepository } from '@/models/_.js';
import { ClipEntityService } from '@/core/entities/ClipEntityService.js';
import { DI } from '@/di-symbols.js';
@@ -29,7 +30,13 @@ export const meta = {
export const paramDef = {
type: 'object',
- properties: {},
+ properties: {
+ limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
+ sinceId: { type: 'string', format: 'misskey:id' },
+ untilId: { type: 'string', format: 'misskey:id' },
+ sinceDate: { type: 'integer' },
+ untilDate: { type: 'integer' },
+ },
required: [],
} as const;
@@ -39,12 +46,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.clipsRepository)
private clipsRepository: ClipsRepository,
+ private queryService: QueryService,
private clipEntityService: ClipEntityService,
) {
super(meta, paramDef, async (ps, me) => {
- const clips = await this.clipsRepository.findBy({
- userId: me.id,
- });
+ const query = this.queryService.makePaginationQuery(this.clipsRepository.createQueryBuilder('clip'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
+ .andWhere('clip.userId = :userId', { userId: me.id });
+
+ const clips = await query.limit(ps.limit).getMany();
return await this.clipEntityService.packMany(clips, me);
});
diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-chat-messages.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-chat-messages.ts
index 5be477f468..b34ac4abd1 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/attached-chat-messages.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/attached-chat-messages.ts
@@ -10,6 +10,7 @@ import { QueryService } from '@/core/QueryService.js';
import { DI } from '@/di-symbols.js';
import { RoleService } from '@/core/RoleService.js';
import { ChatEntityService } from '@/core/entities/ChatEntityService.js';
+import { ChatService } from '@/core/ChatService.js';
import { ApiError } from '../../../error.js';
export const meta = {
@@ -60,14 +61,21 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.chatMessagesRepository)
private chatMessagesRepository: ChatMessagesRepository,
+ private chatService: ChatService,
private chatEntityService: ChatEntityService,
private queryService: QueryService,
private roleService: RoleService,
) {
super(meta, paramDef, async (ps, me) => {
+ const isModerator = await this.roleService.isModerator(me);
+
+ if (!isModerator) {
+ await this.chatService.checkChatAvailability(me.id, 'read');
+ }
+
const file = await this.driveFilesRepository.findOneBy({
id: ps.fileId,
- userId: await this.roleService.isModerator(me) ? undefined : me.id,
+ userId: isModerator ? undefined : me.id,
});
if (file == null) {
diff --git a/packages/backend/src/server/api/endpoints/flash/update.ts b/packages/backend/src/server/api/endpoints/flash/update.ts
index e378669f0a..8696c6f6e8 100644
--- a/packages/backend/src/server/api/endpoints/flash/update.ts
+++ b/packages/backend/src/server/api/endpoints/flash/update.ts
@@ -73,8 +73,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
updatedAt: new Date(),
...Object.fromEntries(
Object.entries(ps).filter(
- ([key, val]) => (key !== 'flashId') && Object.hasOwn(paramDef.properties, key)
- )
+ ([key, val]) => (key !== 'flashId') && Object.hasOwn(paramDef.properties, key),
+ ),
),
});
});
diff --git a/packages/backend/src/server/api/endpoints/i/apps.ts b/packages/backend/src/server/api/endpoints/i/apps.ts
index 055b5cc061..523d81ac73 100644
--- a/packages/backend/src/server/api/endpoints/i/apps.ts
+++ b/packages/backend/src/server/api/endpoints/i/apps.ts
@@ -46,6 +46,14 @@ export const meta = {
type: 'string',
},
},
+ iconUrl: {
+ type: 'string',
+ optional: true, nullable: true,
+ },
+ description: {
+ type: 'string',
+ optional: true, nullable: true,
+ },
},
},
},
@@ -88,6 +96,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
createdAt: this.idService.parse(token.id).date.toISOString(),
lastUsedAt: token.lastUsedAt?.toISOString(),
permission: token.app ? token.app.permission : token.permission,
+ iconUrl: token.iconUrl,
+ description: token.description ?? token.app?.description ?? null,
})));
});
}
diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts
index 394c178f2a..8a3ba9e026 100644
--- a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts
+++ b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts
@@ -21,29 +21,7 @@ export const meta = {
type: 'array',
items: {
type: 'object',
- properties: {
- id: {
- type: 'string',
- format: 'misskey:id',
- },
- userId: {
- type: 'string',
- format: 'misskey:id',
- },
- name: { type: 'string' },
- on: {
- type: 'array',
- items: {
- type: 'string',
- enum: webhookEventTypes,
- },
- },
- url: { type: 'string' },
- secret: { type: 'string' },
- active: { type: 'boolean' },
- latestSentAt: { type: 'string', format: 'date-time', nullable: true },
- latestStatus: { type: 'integer', nullable: true },
- },
+ ref: 'UserWebhook',
},
},
} as const;
@@ -65,19 +43,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
userId: me.id,
});
- return webhooks.map(webhook => (
- {
- id: webhook.id,
- userId: webhook.userId,
- name: webhook.name,
- on: webhook.on,
- url: webhook.url,
- secret: webhook.secret,
- active: webhook.active,
- latestSentAt: webhook.latestSentAt ? webhook.latestSentAt.toISOString() : null,
- latestStatus: webhook.latestStatus,
- }
- ));
+ return webhooks.map(webhook => ({
+ id: webhook.id,
+ userId: webhook.userId,
+ name: webhook.name,
+ on: webhook.on,
+ url: webhook.url,
+ secret: webhook.secret,
+ active: webhook.active,
+ latestSentAt: webhook.latestSentAt ? webhook.latestSentAt.toISOString() : null,
+ latestStatus: webhook.latestStatus,
+ }));
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts
index 4a0c09ff0c..1c19081c98 100644
--- a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts
+++ b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts
@@ -28,29 +28,7 @@ export const meta = {
res: {
type: 'object',
- properties: {
- id: {
- type: 'string',
- format: 'misskey:id',
- },
- userId: {
- type: 'string',
- format: 'misskey:id',
- },
- name: { type: 'string' },
- on: {
- type: 'array',
- items: {
- type: 'string',
- enum: webhookEventTypes,
- },
- },
- url: { type: 'string' },
- secret: { type: 'string' },
- active: { type: 'boolean' },
- latestSentAt: { type: 'string', format: 'date-time', nullable: true },
- latestStatus: { type: 'integer', nullable: true },
- },
+ ref: 'UserWebhook',
},
} as const;
diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts
index 253a360815..7caea8eedc 100644
--- a/packages/backend/src/server/api/endpoints/notes/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/create.ts
@@ -269,7 +269,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
let renote: MiNote | null = null;
if (ps.renoteId != null) {
// Fetch renote to note
- renote = await this.notesRepository.findOneBy({ id: ps.renoteId });
+ renote = await this.notesRepository.findOne({
+ where: { id: ps.renoteId },
+ relations: ['user', 'renote', 'reply'],
+ });
if (renote == null) {
throw new ApiError(meta.errors.noSuchRenoteTarget);
@@ -315,7 +318,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
let reply: MiNote | null = null;
if (ps.replyId != null) {
// Fetch reply
- reply = await this.notesRepository.findOneBy({ id: ps.replyId });
+ reply = await this.notesRepository.findOne({
+ where: { id: ps.replyId },
+ relations: ['user'],
+ });
if (reply == null) {
throw new ApiError(meta.errors.noSuchReplyTarget);
diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
index 1c73edf08e..7fa8004209 100644
--- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
@@ -91,6 +91,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
qb.orWhere(new Brackets(qb => {
qb.where('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
+ qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
}));
}));
}
diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts
index 05ffdc1f97..e775bdb7fd 100644
--- a/packages/backend/src/server/api/endpoints/notes/mentions.ts
+++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts
@@ -66,7 +66,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.orWhere(':meIdAsList <@ note.visibleUserIds');
}))
// Avoid scanning primary key index
- .orderBy('CONCAT(note.id)', 'DESC')
+ .orderBy('CONCAT(note.id)', (ps.sinceDate || ps.sinceId) ? 'ASC' : 'DESC')
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts
index b93c73b0c5..cae0e752da 100644
--- a/packages/backend/src/server/api/endpoints/notes/show.ts
+++ b/packages/backend/src/server/api/endpoints/notes/show.ts
@@ -55,7 +55,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private getterService: GetterService,
) {
super(meta, paramDef, async (ps, me) => {
- const note = await this.getterService.getNoteWithUser(ps.noteId).catch(err => {
+ const note = await this.getterService.getNoteWithRelations(ps.noteId).catch(err => {
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
throw err;
});
diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts
index c76cca1518..eeeb797efc 100644
--- a/packages/backend/src/server/api/endpoints/notes/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts
@@ -237,7 +237,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
if (ps.withRenotes === false) {
- query.andWhere('note.renoteId IS NULL');
+ query.andWhere(new Brackets(qb => {
+ qb.orWhere('note.renoteId IS NULL');
+ qb.orWhere(new Brackets(qb => {
+ qb.orWhere('note.text IS NOT NULL');
+ qb.orWhere('note.fileIds != \'{}\'');
+ qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
+ }));
+ }));
}
//#endregion
diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
index 614cd9204d..42e80c6ae1 100644
--- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -223,6 +223,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
qb.orWhere(new Brackets(qb => {
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
+ qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
}));
}));
}
diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts
index 6de5fe3d44..96bc2a953a 100644
--- a/packages/backend/src/server/api/endpoints/pages/create.ts
+++ b/packages/backend/src/server/api/endpoints/pages/create.ts
@@ -5,12 +5,13 @@
import ms from 'ms';
import { Inject, Injectable } from '@nestjs/common';
-import type { DriveFilesRepository, PagesRepository } from '@/models/_.js';
-import { IdService } from '@/core/IdService.js';
-import { MiPage, pageNameSchema } from '@/models/Page.js';
+import type { DriveFilesRepository, MiDriveFile, PagesRepository } from '@/models/_.js';
+import { pageNameSchema } from '@/models/Page.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { PageEntityService } from '@/core/entities/PageEntityService.js';
import { DI } from '@/di-symbols.js';
+import { PageService } from '@/core/PageService.js';
+import { IdentifiableError } from '@/misc/identifiable-error.js';
import { ApiError } from '../../error.js';
export const meta = {
@@ -77,11 +78,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.driveFilesRepository)
private driveFilesRepository: DriveFilesRepository,
+ private pageService: PageService,
private pageEntityService: PageEntityService,
- private idService: IdService,
) {
super(meta, paramDef, async (ps, me) => {
- let eyeCatchingImage = null;
+ let eyeCatchingImage: MiDriveFile | null = null;
if (ps.eyeCatchingImageId != null) {
eyeCatchingImage = await this.driveFilesRepository.findOneBy({
id: ps.eyeCatchingImageId,
@@ -102,24 +103,20 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
});
- const page = await this.pagesRepository.insertOne(new MiPage({
- id: this.idService.gen(),
- updatedAt: new Date(),
- title: ps.title,
- name: ps.name,
- summary: ps.summary,
- content: ps.content,
- variables: ps.variables,
- script: ps.script,
- eyeCatchingImageId: eyeCatchingImage ? eyeCatchingImage.id : null,
- userId: me.id,
- visibility: 'public',
- alignCenter: ps.alignCenter,
- hideTitleWhenPinned: ps.hideTitleWhenPinned,
- font: ps.font,
- }));
+ try {
+ const page = await this.pageService.create(me, {
+ ...ps,
+ eyeCatchingImage,
+ summary: ps.summary ?? null,
+ });
- return await this.pageEntityService.pack(page);
+ return await this.pageEntityService.pack(page);
+ } catch (err) {
+ if (err instanceof IdentifiableError && err.id === '1a79e38e-3d83-4423-845b-a9d83ff93b61') {
+ throw new ApiError(meta.errors.nameAlreadyExists);
+ }
+ throw err;
+ }
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/pages/delete.ts b/packages/backend/src/server/api/endpoints/pages/delete.ts
index f2bc946788..a33868552d 100644
--- a/packages/backend/src/server/api/endpoints/pages/delete.ts
+++ b/packages/backend/src/server/api/endpoints/pages/delete.ts
@@ -4,12 +4,14 @@
*/
import { Inject, Injectable } from '@nestjs/common';
-import type { PagesRepository, UsersRepository } from '@/models/_.js';
+import type { MiDriveFile, PagesRepository, UsersRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { DI } from '@/di-symbols.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { RoleService } from '@/core/RoleService.js';
import { ApiError } from '../../error.js';
+import { IdentifiableError } from '@/misc/identifiable-error.js';
+import { PageService } from '@/core/PageService.js';
export const meta = {
tags: ['pages'],
@@ -44,36 +46,17 @@ export const paramDef = {
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
- @Inject(DI.pagesRepository)
- private pagesRepository: PagesRepository,
-
- @Inject(DI.usersRepository)
- private usersRepository: UsersRepository,
-
- private moderationLogService: ModerationLogService,
- private roleService: RoleService,
+ private pageService: PageService,
) {
super(meta, paramDef, async (ps, me) => {
- const page = await this.pagesRepository.findOneBy({ id: ps.pageId });
-
- if (page == null) {
- throw new ApiError(meta.errors.noSuchPage);
- }
-
- if (!await this.roleService.isModerator(me) && page.userId !== me.id) {
- throw new ApiError(meta.errors.accessDenied);
- }
-
- await this.pagesRepository.delete(page.id);
-
- if (page.userId !== me.id) {
- const user = await this.usersRepository.findOneByOrFail({ id: page.userId });
- this.moderationLogService.log(me, 'deletePage', {
- pageId: page.id,
- pageUserId: page.userId,
- pageUserUsername: user.username,
- page,
- });
+ try {
+ await this.pageService.delete(me, ps.pageId);
+ } catch (err) {
+ if (err instanceof IdentifiableError) {
+ if (err.id === '66aefd3c-fdb2-4a71-85ae-cc18bea85d3f') throw new ApiError(meta.errors.noSuchPage);
+ if (err.id === 'd0017699-8256-46f1-aed4-bc03bed73616') throw new ApiError(meta.errors.accessDenied);
+ }
+ throw err;
}
});
}
diff --git a/packages/backend/src/server/api/endpoints/pages/update.ts b/packages/backend/src/server/api/endpoints/pages/update.ts
index a6aeb6002e..6fa5c1d75c 100644
--- a/packages/backend/src/server/api/endpoints/pages/update.ts
+++ b/packages/backend/src/server/api/endpoints/pages/update.ts
@@ -4,13 +4,14 @@
*/
import ms from 'ms';
-import { Not } from 'typeorm';
import { Inject, Injectable } from '@nestjs/common';
-import type { PagesRepository, DriveFilesRepository } from '@/models/_.js';
+import type { DriveFilesRepository, MiDriveFile } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../../error.js';
import { pageNameSchema } from '@/models/Page.js';
+import { IdentifiableError } from '@/misc/identifiable-error.js';
+import { PageService } from '@/core/PageService.js';
export const meta = {
tags: ['pages'],
@@ -75,57 +76,37 @@ export const paramDef = {
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
- @Inject(DI.pagesRepository)
- private pagesRepository: PagesRepository,
-
@Inject(DI.driveFilesRepository)
private driveFilesRepository: DriveFilesRepository,
+
+ private pageService: PageService,
) {
super(meta, paramDef, async (ps, me) => {
- const page = await this.pagesRepository.findOneBy({ id: ps.pageId });
- if (page == null) {
- throw new ApiError(meta.errors.noSuchPage);
- }
- if (page.userId !== me.id) {
- throw new ApiError(meta.errors.accessDenied);
- }
-
- if (ps.eyeCatchingImageId != null) {
- const eyeCatchingImage = await this.driveFilesRepository.findOneBy({
- id: ps.eyeCatchingImageId,
- userId: me.id,
- });
+ try {
+ let eyeCatchingImage: MiDriveFile | null | undefined | string = ps.eyeCatchingImageId;
+ if (eyeCatchingImage != null) {
+ eyeCatchingImage = await this.driveFilesRepository.findOneBy({
+ id: eyeCatchingImage,
+ userId: me.id,
+ });
- if (eyeCatchingImage == null) {
- throw new ApiError(meta.errors.noSuchFile);
+ if (eyeCatchingImage == null) {
+ throw new ApiError(meta.errors.noSuchFile);
+ }
}
- }
- if (ps.name != null) {
- await this.pagesRepository.findBy({
- id: Not(ps.pageId),
- userId: me.id,
- name: ps.name,
- }).then(result => {
- if (result.length > 0) {
- throw new ApiError(meta.errors.nameAlreadyExists);
- }
+ await this.pageService.update(me, ps.pageId, {
+ ...ps,
+ eyeCatchingImage,
});
+ } catch (err) {
+ if (err instanceof IdentifiableError) {
+ if (err.id === '66aefd3c-fdb2-4a71-85ae-cc18bea85d3f') throw new ApiError(meta.errors.noSuchPage);
+ if (err.id === 'd0017699-8256-46f1-aed4-bc03bed73616') throw new ApiError(meta.errors.accessDenied);
+ if (err.id === 'd05bfe24-24b6-4ea2-a3ec-87cc9bf4daa4') throw new ApiError(meta.errors.nameAlreadyExists);
+ }
+ throw err;
}
-
- await this.pagesRepository.update(page.id, {
- updatedAt: new Date(),
- title: ps.title,
- name: ps.name,
- summary: ps.summary === undefined ? page.summary : ps.summary,
- content: ps.content,
- variables: ps.variables,
- script: ps.script,
- alignCenter: ps.alignCenter,
- hideTitleWhenPinned: ps.hideTitleWhenPinned,
- font: ps.font,
- eyeCatchingImageId: ps.eyeCatchingImageId,
- });
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/users/lists/show.ts b/packages/backend/src/server/api/endpoints/users/lists/show.ts
index 8756801fe4..ed5952d4c5 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/show.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/show.ts
@@ -23,6 +23,16 @@ export const meta = {
type: 'object',
optional: false, nullable: false,
ref: 'UserList',
+ properties: {
+ likedCount: {
+ type: 'number',
+ optional: true, nullable: false,
+ },
+ isLiked: {
+ type: 'boolean',
+ optional: true, nullable: false,
+ },
+ },
},
errors: {
diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts
index d6f1ecd8ed..d84a191f7a 100644
--- a/packages/backend/src/server/api/endpoints/users/reactions.ts
+++ b/packages/backend/src/server/api/endpoints/users/reactions.ts
@@ -28,7 +28,7 @@ export const meta = {
items: {
type: 'object',
optional: false, nullable: false,
- ref: 'NoteReaction',
+ ref: 'NoteReactionWithNote',
},
},
@@ -120,7 +120,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
return true;
});
- return await this.noteReactionEntityService.packMany(reactions, me, { withNote: true });
+ return await this.noteReactionEntityService.packManyWithNote(reactions, me);
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts
index 5d36847e03..c422286152 100644
--- a/packages/backend/src/server/api/endpoints/users/search.ts
+++ b/packages/backend/src/server/api/endpoints/users/search.ts
@@ -13,6 +13,7 @@ export const meta = {
tags: ['users'],
requireCredential: false,
+ requiredRolePolicy: 'canSearchUsers',
description: 'Search for users.',
diff --git a/packages/backend/src/server/api/endpoints/verify-email.ts b/packages/backend/src/server/api/endpoints/verify-email.ts
new file mode 100644
index 0000000000..e069ed59f2
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/verify-email.ts
@@ -0,0 +1,66 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import type { UserProfilesRepository } from '@/models/_.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
+import { DI } from '@/di-symbols.js';
+import { GlobalEventService } from '@/core/GlobalEventService.js';
+import { ApiError } from '../error.js';
+
+export const meta = {
+ requireCredential: false,
+
+ tags: ['account'],
+
+ errors: {
+ noSuchCode: {
+ message: 'No such code.',
+ code: 'NO_SUCH_CODE',
+ id: '97c1f576-e4b8-4b8a-a6dc-9cb65e7f6f85',
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ code: { type: 'string' },
+ },
+ required: ['code'],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+ constructor(
+ @Inject(DI.userProfilesRepository)
+ private userProfilesRepository: UserProfilesRepository,
+
+ private userEntityService: UserEntityService,
+ private globalEventService: GlobalEventService,
+ ) {
+ super(meta, paramDef, async (ps) => {
+ const profile = await this.userProfilesRepository.findOneBy({
+ emailVerifyCode: ps.code,
+ });
+
+ if (profile == null) {
+ throw new ApiError(meta.errors.noSuchCode);
+ }
+
+ await this.userProfilesRepository.update({ userId: profile.userId }, {
+ emailVerified: true,
+ emailVerifyCode: null,
+ });
+
+ this.globalEventService.publishMainStream(profile.userId, 'meUpdated', await this.userEntityService.pack(profile.userId, { id: profile.userId }, {
+ schema: 'MeDetailed',
+ includeSecrets: true,
+ }));
+ });
+ }
+}
+
diff --git a/packages/backend/src/server/api/stream/Connection.ts b/packages/backend/src/server/api/stream/Connection.ts
index c9801d8314..8e28ab263b 100644
--- a/packages/backend/src/server/api/stream/Connection.ts
+++ b/packages/backend/src/server/api/stream/Connection.ts
@@ -32,7 +32,6 @@ export default class Connection {
public subscriber: StreamEventEmitter;
private channels: Channel[] = [];
private subscribingNotes: Partial<Record<string, number>> = {};
- private cachedNotes: Packed<'Note'>[] = [];
public userProfile: MiUserProfile | null = null;
public following: Record<string, Pick<MiFollowing, 'withReplies'> | undefined> = {};
public followingChannels: Set<string> = new Set();
@@ -133,26 +132,6 @@ export default class Connection {
}
@bindThis
- public cacheNote(note: Packed<'Note'>) {
- const add = (note: Packed<'Note'>) => {
- const existIndex = this.cachedNotes.findIndex(n => n.id === note.id);
- if (existIndex > -1) {
- this.cachedNotes[existIndex] = note;
- return;
- }
-
- this.cachedNotes.unshift(note);
- if (this.cachedNotes.length > 32) {
- this.cachedNotes.splice(32);
- }
- };
-
- add(note);
- if (note.reply) add(note.reply);
- if (note.renote) add(note.renote);
- }
-
- @bindThis
private onReadNotification(payload: JsonValue | undefined) {
this.notificationService.readAllNotification(this.user!.id);
}
diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts
index 53dc7f18b6..e08562fdf9 100644
--- a/packages/backend/src/server/api/stream/channels/antenna.ts
+++ b/packages/backend/src/server/api/stream/channels/antenna.ts
@@ -43,8 +43,6 @@ class AntennaChannel extends Channel {
if (this.isNoteMutedOrBlocked(note)) return;
- this.connection.cacheNote(note);
-
this.send('note', note);
} else {
this.send(data.type, data.body);
diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts
index 7108e0cd6e..ac79c31854 100644
--- a/packages/backend/src/server/api/stream/channels/channel.ts
+++ b/packages/backend/src/server/api/stream/channels/channel.ts
@@ -49,8 +49,6 @@ class ChannelChannel extends Channel {
}
}
- this.connection.cacheNote(note);
-
this.send('note', note);
}
diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts
index 795980821b..d7c781ad12 100644
--- a/packages/backend/src/server/api/stream/channels/global-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts
@@ -65,8 +65,6 @@ class GlobalTimelineChannel extends Channel {
}
}
- this.connection.cacheNote(note);
-
this.send('note', note);
}
diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts
index 8105f15cb1..c911d63642 100644
--- a/packages/backend/src/server/api/stream/channels/hashtag.ts
+++ b/packages/backend/src/server/api/stream/channels/hashtag.ts
@@ -53,8 +53,6 @@ class HashtagChannel extends Channel {
}
}
- this.connection.cacheNote(note);
-
this.send('note', note);
}
diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts
index 66644ed58c..157d9fc279 100644
--- a/packages/backend/src/server/api/stream/channels/home-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts
@@ -86,8 +86,6 @@ class HomeTimelineChannel extends Channel {
}
}
- this.connection.cacheNote(note);
-
this.send('note', note);
}
diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
index 5681311493..db5b4576be 100644
--- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
@@ -100,8 +100,6 @@ class HybridTimelineChannel extends Channel {
}
}
- this.connection.cacheNote(note);
-
this.send('note', note);
}
diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts
index 2984e18774..3d7ed6acdb 100644
--- a/packages/backend/src/server/api/stream/channels/local-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts
@@ -75,8 +75,6 @@ class LocalTimelineChannel extends Channel {
}
}
- this.connection.cacheNote(note);
-
this.send('note', note);
}
diff --git a/packages/backend/src/server/api/stream/channels/main.ts b/packages/backend/src/server/api/stream/channels/main.ts
index 863d7f4c4e..525f24c105 100644
--- a/packages/backend/src/server/api/stream/channels/main.ts
+++ b/packages/backend/src/server/api/stream/channels/main.ts
@@ -39,7 +39,6 @@ class MainChannel extends Channel {
const note = await this.noteEntityService.pack(data.body.note.id, this.user, {
detail: true,
});
- this.connection.cacheNote(note);
data.body.note = note;
}
break;
@@ -52,7 +51,6 @@ class MainChannel extends Channel {
const note = await this.noteEntityService.pack(data.body.id, this.user, {
detail: true,
});
- this.connection.cacheNote(note);
data.body = note;
}
break;
diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts
index 4f38351e94..5bfd8fa68c 100644
--- a/packages/backend/src/server/api/stream/channels/user-list.ts
+++ b/packages/backend/src/server/api/stream/channels/user-list.ts
@@ -118,8 +118,6 @@ class UserListChannel extends Channel {
}
}
- this.connection.cacheNote(note);
-
this.send('note', note);
}
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index 8ca61a497d..b515a0c0c8 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -20,17 +20,6 @@ import type { Config } from '@/config.js';
import { getNoteSummary } from '@/misc/get-note-summary.js';
import { DI } from '@/di-symbols.js';
import * as Acct from '@/misc/acct.js';
-import type {
- DbQueue,
- DeliverQueue,
- EndedPollNotificationQueue,
- InboxQueue,
- ObjectStorageQueue,
- RelationshipQueue,
- SystemQueue,
- UserWebhookDeliverQueue,
- SystemWebhookDeliverQueue,
-} from '@/core/QueueModule.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { PageEntityService } from '@/core/entities/PageEntityService.js';
@@ -129,16 +118,6 @@ export class ClientServerService {
private feedService: FeedService,
private roleService: RoleService,
private clientLoggerService: ClientLoggerService,
-
- @Inject('queue:system') public systemQueue: SystemQueue,
- @Inject('queue:endedPollNotification') public endedPollNotificationQueue: EndedPollNotificationQueue,
- @Inject('queue:deliver') public deliverQueue: DeliverQueue,
- @Inject('queue:inbox') public inboxQueue: InboxQueue,
- @Inject('queue:db') public dbQueue: DbQueue,
- @Inject('queue:relationship') public relationshipQueue: RelationshipQueue,
- @Inject('queue:objectStorage') public objectStorageQueue: ObjectStorageQueue,
- @Inject('queue:userWebhookDeliver') public userWebhookDeliverQueue: UserWebhookDeliverQueue,
- @Inject('queue:systemWebhookDeliver') public systemWebhookDeliverQueue: SystemWebhookDeliverQueue,
) {
//this.createServer = this.createServer.bind(this);
}
@@ -188,6 +167,10 @@ export class ClientServerService {
'url': 'url',
},
},
+ 'shortcuts': [{
+ 'name': 'Safemode',
+ 'url': '/?safemode=true',
+ }],
};
manifest = {
@@ -580,7 +563,7 @@ export class ClientServerService {
id: request.params.note,
visibility: In(['public', 'home']),
},
- relations: ['user'],
+ relations: ['user', 'reply', 'renote'],
});
if (
@@ -821,8 +804,11 @@ export class ClientServerService {
fastify.get<{ Params: { note: string; } }>('/embed/notes/:note', async (request, reply) => {
reply.removeHeader('X-Frame-Options');
- const note = await this.notesRepository.findOneBy({
- id: request.params.note,
+ const note = await this.notesRepository.findOne({
+ where: {
+ id: request.params.note,
+ },
+ relations: ['user', 'reply', 'renote'],
});
if (note == null) return;
diff --git a/packages/backend/src/server/web/boot.embed.js b/packages/backend/src/server/web/boot.embed.js
index 9de1275380..022ff064ad 100644
--- a/packages/backend/src/server/web/boot.embed.js
+++ b/packages/backend/src/server/web/boot.embed.js
@@ -32,61 +32,30 @@
}
//#region Detect language & fetch translations
- if (!localStorage.hasOwnProperty('locale')) {
- const supportedLangs = LANGS;
- let lang = localStorage.getItem('lang');
- if (lang == null || !supportedLangs.includes(lang)) {
- if (supportedLangs.includes(navigator.language)) {
- lang = navigator.language;
- } else {
- lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
-
- // Fallback
- if (lang == null) lang = 'en-US';
- }
- }
-
- const metaRes = await window.fetch('/api/meta', {
- method: 'POST',
- body: JSON.stringify({}),
- credentials: 'omit',
- cache: 'no-cache',
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- if (metaRes.status !== 200) {
- renderError('META_FETCH');
- return;
- }
- const meta = await metaRes.json();
- const v = meta.version;
- if (v == null) {
- renderError('META_FETCH_V');
- return;
- }
+ const supportedLangs = LANGS;
+ /** @type { string } */
+ let lang = localStorage.getItem('lang');
+ if (lang == null || !supportedLangs.includes(lang)) {
+ if (supportedLangs.includes(navigator.language)) {
+ lang = navigator.language;
+ } else {
+ lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
- // for https://github.com/misskey-dev/misskey/issues/10202
- if (lang == null || lang.toString == null || lang.toString() === 'null') {
- console.error('invalid lang value detected!!!', typeof lang, lang);
- lang = 'en-US';
+ // Fallback
+ if (lang == null) lang = 'en-US';
}
+ }
- const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`);
- if (localRes.status === 200) {
- localStorage.setItem('lang', lang);
- localStorage.setItem('locale', await localRes.text());
- localStorage.setItem('localeVersion', v);
- } else {
- renderError('LOCALE_FETCH');
- return;
- }
+ // for https://github.com/misskey-dev/misskey/issues/10202
+ if (lang == null || lang.toString == null || lang.toString() === 'null') {
+ console.error('invalid lang value detected!!!', typeof lang, lang);
+ lang = 'en-US';
}
//#endregion
//#region Script
async function importAppScript() {
- await import(`/embed_vite/${CLIENT_ENTRY}`)
+ await import(CLIENT_ENTRY ? `/embed_vite/${CLIENT_ENTRY.replace('scripts', lang)}` : '/embed_vite/src/_boot_.ts')
.catch(async e => {
console.error(e);
renderError('APP_IMPORT');
@@ -115,10 +84,26 @@
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
}
- const locale = JSON.parse(localStorage.getItem('locale') || '{}');
+ let messages = null;
+ const bootloaderLocales = localStorage.getItem('bootloaderLocales');
+ if (bootloaderLocales) {
+ messages = JSON.parse(bootloaderLocales);
+ }
+ if (!messages) {
+ // older version of misskey does not store bootloaderLocales, stores locale as a whole
+ const legacyLocale = localStorage.getItem('locale');
+ if (legacyLocale) {
+ const parsed = JSON.parse(legacyLocale);
+ messages = {
+ ...(parsed._bootErrors ?? {}),
+ reload: parsed.reload,
+ };
+ }
+ }
+ if (!messages) messages = {};
- const title = locale?._bootErrors?.title || 'Failed to initialize Misskey';
- const reload = locale?.reload || 'Reload';
+ const title = messages?.title || 'Failed to initialize Misskey';
+ const reload = messages?.reload || 'Reload';
document.body.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M12 9v4" /><path d="M12 16v.01" /></svg>
<div class="message">${title}</div>
diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js
index 24794cbf2a..0c0b46f82b 100644
--- a/packages/backend/src/server/web/boot.js
+++ b/packages/backend/src/server/web/boot.js
@@ -22,62 +22,31 @@
return;
}
- //#region Detect language & fetch translations
- if (!localStorage.hasOwnProperty('locale')) {
- const supportedLangs = LANGS;
- let lang = localStorage.getItem('lang');
- if (lang == null || !supportedLangs.includes(lang)) {
- if (supportedLangs.includes(navigator.language)) {
- lang = navigator.language;
- } else {
- lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
-
- // Fallback
- if (lang == null) lang = 'en-US';
- }
- }
-
- const metaRes = await window.fetch('/api/meta', {
- method: 'POST',
- body: JSON.stringify({}),
- credentials: 'omit',
- cache: 'no-cache',
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- if (metaRes.status !== 200) {
- renderError('META_FETCH');
- return;
- }
- const meta = await metaRes.json();
- const v = meta.version;
- if (v == null) {
- renderError('META_FETCH_V');
- return;
- }
+ //#region Detect language
+ const supportedLangs = LANGS;
+ /** @type { string } */
+ let lang = localStorage.getItem('lang');
+ if (lang == null || !supportedLangs.includes(lang)) {
+ if (supportedLangs.includes(navigator.language)) {
+ lang = navigator.language;
+ } else {
+ lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
- // for https://github.com/misskey-dev/misskey/issues/10202
- if (lang == null || lang.toString == null || lang.toString() === 'null') {
- console.error('invalid lang value detected!!!', typeof lang, lang);
- lang = 'en-US';
+ // Fallback
+ if (lang == null) lang = 'en-US';
}
+ }
- const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`);
- if (localRes.status === 200) {
- localStorage.setItem('lang', lang);
- localStorage.setItem('locale', await localRes.text());
- localStorage.setItem('localeVersion', v);
- } else {
- renderError('LOCALE_FETCH');
- return;
- }
+ // for https://github.com/misskey-dev/misskey/issues/10202
+ if (lang == null || lang.toString == null || lang.toString() === 'null') {
+ console.error('invalid lang value detected!!!', typeof lang, lang);
+ lang = 'en-US';
}
//#endregion
//#region Script
async function importAppScript() {
- await import(`/vite/${CLIENT_ENTRY}`)
+ await import(CLIENT_ENTRY ? `/vite/${CLIENT_ENTRY.replace('scripts', lang)}` : '/vite/src/_boot_.ts')
.catch(async e => {
console.error(e);
renderError('APP_IMPORT', e);
@@ -94,23 +63,37 @@
}
//#endregion
+ let isSafeMode = (localStorage.getItem('isSafeMode') === 'true');
+
+ if (!isSafeMode) {
+ const urlParams = new URLSearchParams(window.location.search);
+
+ if (urlParams.has('safemode') && urlParams.get('safemode') === 'true') {
+ localStorage.setItem('isSafeMode', 'true');
+ isSafeMode = true;
+ }
+ }
+
//#region Theme
- const theme = localStorage.getItem('theme');
- if (theme) {
- for (const [k, v] of Object.entries(JSON.parse(theme))) {
- document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
+ if (!isSafeMode) {
+ const theme = localStorage.getItem('theme');
+ if (theme) {
+ for (const [k, v] of Object.entries(JSON.parse(theme))) {
+ document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
- // HTMLã® theme-color é©ç”¨
- if (k === 'htmlThemeColor') {
- for (const tag of document.head.children) {
- if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
- tag.setAttribute('content', v);
- break;
+ // HTMLã® theme-color é©ç”¨
+ if (k === 'htmlThemeColor') {
+ for (const tag of document.head.children) {
+ if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
+ tag.setAttribute('content', v);
+ break;
+ }
}
}
}
}
}
+
const colorScheme = localStorage.getItem('colorScheme');
if (colorScheme) {
document.documentElement.style.setProperty('color-scheme', colorScheme);
@@ -127,11 +110,13 @@
document.documentElement.classList.add('useSystemFont');
}
- const customCss = localStorage.getItem('customCss');
- if (customCss && customCss.length > 0) {
- const style = document.createElement('style');
- style.innerHTML = customCss;
- document.head.appendChild(style);
+ if (!isSafeMode) {
+ const customCss = localStorage.getItem('customCss');
+ if (customCss && customCss.length > 0) {
+ const style = document.createElement('style');
+ style.innerHTML = customCss;
+ document.head.appendChild(style);
+ }
}
async function addStyle(styleText) {
@@ -146,9 +131,25 @@
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
}
- const locale = JSON.parse(localStorage.getItem('locale') || '{}');
+ let messages = null;
+ const bootloaderLocales = localStorage.getItem('bootloaderLocales');
+ if (bootloaderLocales) {
+ messages = JSON.parse(bootloaderLocales);
+ }
+ if (!messages) {
+ // older version of misskey does not store bootloaderLocales, stores locale as a whole
+ const legacyLocale = localStorage.getItem('locale');
+ if (legacyLocale) {
+ const parsed = JSON.parse(legacyLocale);
+ messages = {
+ ...(parsed._bootErrors ?? {}),
+ reload: parsed.reload,
+ };
+ }
+ }
+ if (!messages) messages = {};
- const messages = Object.assign({
+ messages = Object.assign({
title: 'Failed to initialize Misskey',
solution: 'The following actions may solve the problem.',
solution1: 'Update your os and browser',
@@ -159,8 +160,12 @@
otherOption1: 'Clear preferences and cache',
otherOption2: 'Start the simple client',
otherOption3: 'Start the repair tool',
- }, locale?._bootErrors || {});
- const reload = locale?.reload || 'Reload';
+ otherOption4: 'Start Misskey in safe mode',
+ reload: 'Reload',
+ }, messages);
+
+ const safeModeUrl = new URL(window.location.href);
+ safeModeUrl.searchParams.set('safemode', 'true');
let errorsElement = document.getElementById('errors');
@@ -173,7 +178,7 @@
</svg>
<h1>${messages.title}</h1>
<button class="button-big" onclick="location.reload(true);">
- <span class="button-label-big">${reload}</span>
+ <span class="button-label-big">${messages?.reload}</span>
</button>
<p><b>${messages.solution}</b></p>
<p>${messages.solution1}</p>
@@ -182,6 +187,12 @@
<p>${messages.solution4}</p>
<details style="color: #86b300;">
<summary>${messages.otherOption}</summary>
+ <a href="${safeModeUrl}">
+ <button class="button-small">
+ <span class="button-label-small">${messages.otherOption4}</span>
+ </button>
+ </a>
+ <br>
<a href="/flush">
<button class="button-small">
<span class="button-label-small">${messages.otherOption1}</span>
diff --git a/packages/backend/src/server/web/manifest.json b/packages/backend/src/server/web/manifest.json
index 41171d62a1..90d4530857 100644
--- a/packages/backend/src/server/web/manifest.json
+++ b/packages/backend/src/server/web/manifest.json
@@ -34,5 +34,11 @@
"text": "text",
"url": "url"
}
- }
+ },
+ "shortcuts": [
+ {
+ "name": "Safemode",
+ "url": "/?safemode=true"
+ }
+ ]
}
diff --git a/packages/backend/src/server/web/views/base-embed.pug b/packages/backend/src/server/web/views/base-embed.pug
index baa0909676..29de86b8b6 100644
--- a/packages/backend/src/server/web/views/base-embed.pug
+++ b/packages/backend/src/server/web/views/base-embed.pug
@@ -19,7 +19,6 @@ html(class='embed')
meta(name='format-detection' content='telephone=no,date=no,address=no,email=no,url=no')
link(rel='icon' href= icon || '/favicon.ico')
link(rel='apple-touch-icon' href= appleTouchIcon || '/apple-touch-icon.png')
- link(rel='modulepreload' href=`/embed_vite/${entry.file}`)
if !config.frontendEmbedManifestExists
script(type="module" src="/embed_vite/@vite/client")
@@ -40,7 +39,7 @@ html(class='embed')
script.
var VERSION = "#{version}";
- var CLIENT_ENTRY = "#{entry.file}";
+ var CLIENT_ENTRY = !{JSON.stringify(entry.file)};
script(type='application/json' id='misskey_meta' data-generated-at=now)
!= metaJson
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index 3883b5e5ab..a76c75fe5c 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -37,7 +37,6 @@ html
link(rel='prefetch' href=serverErrorImageUrl)
link(rel='prefetch' href=infoImageUrl)
link(rel='prefetch' href=notFoundImageUrl)
- link(rel='modulepreload' href=`/vite/${entry.file}`)
if !config.frontendManifestExists
script(type="module" src="/vite/@vite/client")
@@ -69,7 +68,7 @@ html
script.
var VERSION = "#{version}";
- var CLIENT_ENTRY = "#{entry.file}";
+ var CLIENT_ENTRY = !{JSON.stringify(entry.file)};
script(type='application/json' id='misskey_meta' data-generated-at=now)
!= metaJson
diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts
index 1584f9587e..a339cd86d2 100644
--- a/packages/backend/test-federation/test/note.test.ts
+++ b/packages/backend/test-federation/test/note.test.ts
@@ -63,7 +63,6 @@ describe('Note', () => {
deepStrictEqualWithExcludedFields(note, resolvedNote, [
'id',
'emojis',
- 'reactionAcceptance',
'replyId',
'reply',
'userId',
@@ -105,7 +104,6 @@ describe('Note', () => {
deepStrictEqualWithExcludedFields(note, resolvedNote, [
'id',
'emojis',
- 'reactionAcceptance',
'renoteId',
'renote',
'userId',
diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts
index 2779eb7e81..7e24bb7904 100644
--- a/packages/backend/test-federation/test/utils.ts
+++ b/packages/backend/test-federation/test/utils.ts
@@ -79,6 +79,9 @@ async function createAdmin(host: Host): Promise<Misskey.entities.SignupResponse
rateLimitFactor: 0 as never,
},
}, res.token);
+ await client.request('admin/update-meta', {
+ federation: 'all',
+ }, res.token);
return res;
}).catch(err => {
if (err.info.e.message === 'access denied') return undefined;
@@ -187,7 +190,8 @@ export async function uploadFile(
path = '../../test/resources/192.jpg',
): Promise<Misskey.entities.DriveFile> {
const filename = path.split('/').pop() ?? 'untitled';
- const blob = new Blob([await readFile(join(__dirname, path))]);
+ const buffer = await readFile(join(__dirname, path));
+ const blob = new Blob([new Uint8Array(buffer)]);
const body = new FormData();
body.append('i', user.i);
diff --git a/packages/backend/test/e2e/antennas.ts b/packages/backend/test/e2e/antennas.ts
index 4dbeacf925..1bbacd065b 100644
--- a/packages/backend/test/e2e/antennas.ts
+++ b/packages/backend/test/e2e/antennas.ts
@@ -673,7 +673,6 @@ describe('アンテナ', () => {
assert.deepStrictEqual(response, expected);
});
-
test.skip('ãŒå–å¾—ã§ãã€æ—¥ä»˜æŒ‡å®šã®Paginationã«ä¸€è²«æ€§ãŒã‚ã‚‹ã“ã¨', async () => { });
test.each([
{ label: 'ID指定', offsetBy: 'id' },
diff --git a/packages/backend/test/e2e/clips.ts b/packages/backend/test/e2e/clips.ts
index 570cc61c4b..fe9a217ee8 100644
--- a/packages/backend/test/e2e/clips.ts
+++ b/packages/backend/test/e2e/clips.ts
@@ -363,14 +363,11 @@ describe('クリップ', () => {
const clipLimit = DEFAULT_POLICIES.clipLimit;
const clips = await createMany({}, clipLimit);
const res = await list({
- parameters: { limit: 1 }, // FIXME: 無視ã•れã¦11全部返ã£ã¦ãã‚‹
+ parameters: { limit: clips.length },
});
- // è¿”ã£ã¦ãã‚‹é…列ã«ã¯é †åºä¿éšœãŒãªã„ã®ã§idã§ã‚½ãƒ¼ãƒˆã—ã¦åŽ³å¯†æ¯”è¼ƒ
- assert.deepStrictEqual(
- res.sort(compareBy(s => s.id)),
- clips.sort(compareBy(s => s.id)),
- );
+ // 作æˆresponseã®é…列ã«ã¯é †åºä¿éšœãŒãªã„ã®ã§idã§ã‚½ãƒ¼ãƒˆã—ã¦åŽ³å¯†æ¯”è¼ƒ
+ assert.deepStrictEqual(res.toReversed(), clips.sort(compareBy(s => s.id)));
});
test('ã®ä¸€è¦§ãŒå–å¾—ã§ãã‚‹(空)', async () => {
diff --git a/packages/backend/test/e2e/endpoints.ts b/packages/backend/test/e2e/endpoints.ts
index b52162a687..469f19e2b9 100644
--- a/packages/backend/test/e2e/endpoints.ts
+++ b/packages/backend/test/e2e/endpoints.ts
@@ -24,6 +24,7 @@ describe('Endpoints', () => {
bob = await signup({ username: 'bob' });
carol = await signup({ username: 'carol' });
dave = await signup({ username: 'dave' });
+ await api('admin/update-meta', { federation: 'all' }, alice as misskey.entities.SignupResponse);
}, 1000 * 60 * 2);
describe('signup', () => {
diff --git a/packages/backend/test/e2e/fetch-resource.ts b/packages/backend/test/e2e/fetch-resource.ts
index 740295bda8..bef98893c6 100644
--- a/packages/backend/test/e2e/fetch-resource.ts
+++ b/packages/backend/test/e2e/fetch-resource.ts
@@ -6,7 +6,7 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
-import { channel, clip, galleryPost, page, play, post, signup, simpleGet, uploadFile } from '../utils.js';
+import { api, channel, clip, galleryPost, page, play, post, signup, simpleGet, uploadFile } from '../utils.js';
import type { SimpleGetResponse } from '../utils.js';
import type * as misskey from 'misskey-js';
@@ -78,6 +78,7 @@ describe('Webリソース', () => {
beforeAll(async () => {
alice = await signup({ username: 'alice' });
+ await api('admin/update-meta', { federation: 'all' }, alice as misskey.entities.SignupResponse);
aliceUploadedFile = (await uploadFile(alice)).body;
alicesPost = await post(alice, {
text: 'test',
diff --git a/packages/backend/test/e2e/ff-visibility.ts b/packages/backend/test/e2e/ff-visibility.ts
index 5d0c70a3c2..02582ae815 100644
--- a/packages/backend/test/e2e/ff-visibility.ts
+++ b/packages/backend/test/e2e/ff-visibility.ts
@@ -16,6 +16,7 @@ describe('FF visibility', () => {
beforeAll(async () => {
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
+ await api('admin/update-meta', { federation: 'all' }, alice as misskey.entities.SignupResponse);
}, 1000 * 60 * 2);
test('followingVisibility, followersVisibility ãŒã¨ã‚‚ã« public ãªãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ•ォロー/フォロワーを誰ã§ã‚‚見れる', async () => {
diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts
index e53c3d8f34..4f7d1a4d69 100644
--- a/packages/backend/test/e2e/timelines.ts
+++ b/packages/backend/test/e2e/timelines.ts
@@ -9,6 +9,7 @@
import * as assert from 'assert';
import { setTimeout } from 'node:timers/promises';
import { Redis } from 'ioredis';
+import { SignupResponse, Note, UserList } from 'misskey-js/entities.js';
import { api, post, randomString, sendEnvUpdateRequest, signup, uploadUrl } from '../utils.js';
import { loadConfig } from '@/config.js';
@@ -16,1554 +17,1929 @@ function genHost() {
return randomString() + '.example.com';
}
-function waitForPushToTl() {
- return setTimeout(500);
-}
-
let redisForTimelines: Redis;
+let root: SignupResponse;
describe('Timelines', () => {
- beforeAll(() => {
+ beforeAll(async () => {
redisForTimelines = new Redis(loadConfig().redisForTimelines);
- });
+ root = await signup({ username: 'root' });
+ }, 1000 * 60 * 2);
- describe('Home TL', () => {
- test.concurrent('自分㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice] = await Promise.all([signup()]);
+ describe.each([
+ { enableFanoutTimeline: true },
+ { enableFanoutTimeline: false },
+ ])('Timelines (enableFanoutTimeline: $enableFanoutTimeline)', ({ enableFanoutTimeline }) => {
+ function waitForPushToTl() {
+ return setTimeout(250);
+ }
- const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
+ beforeAll(async () => {
+ await api('admin/update-meta', { enableFanoutTimeline }, root);
+ }, 1000 * 60 * 2);
- await waitForPushToTl();
+ describe('Home TL', () => {
+ test('自分㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice] = await Promise.all([signup()]);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
- assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
- });
+ await waitForPushToTl();
- test.concurrent('フォローã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote = await post(bob, { text: 'hi' });
- const carolNote = await post(carol, { text: 'hi' });
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
+ });
- await waitForPushToTl();
+ test('フォローã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi' });
+ const carolNote = await post(carol, { text: 'hi' });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('フォローã—ã¦ã„るユーザー㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
- const carolNote = await post(carol, { text: 'hi' });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- await waitForPushToTl();
+ test('フォローã—ã¦ã„るユーザー㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
+ const carolNote = await post(carol, { text: 'hi' });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('withReplies: false ã§ãƒ•ォローã—ã¦ã„るユーザーã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- await waitForPushToTl();
+ test('withReplies: false ã§ãƒ•ォローã—ã¦ã„るユーザーã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await api('following/create', { userId: bob.id }, alice);
- await api('following/update', { userId: bob.id, withReplies: true }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- await waitForPushToTl();
+ test('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- });
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/update', { userId: bob.id, withReplies: true }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
- test.concurrent('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®ä»–人ã¸ã®DM返信ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ await waitForPushToTl();
- await api('following/create', { userId: bob.id }, alice);
- await api('following/update', { userId: bob.id, withReplies: true }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified', visibleUserIds: [carolNote.id] });
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await waitForPushToTl();
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ test('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®ä»–人ã¸ã®DM返信ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- });
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/update', { userId: bob.id, withReplies: true }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified', visibleUserIds: [carolNote.id] });
- test.concurrent('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®ä»–人㮠visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ await waitForPushToTl();
- await api('following/create', { userId: carol.id }, bob);
- await api('following/create', { userId: bob.id }, alice);
- await api('following/update', { userId: bob.id, withReplies: true }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await waitForPushToTl();
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ test('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®ä»–人㮠visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- });
+ await api('following/create', { userId: carol.id }, bob);
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/update', { userId: bob.id, withReplies: true }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
- test.concurrent('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®è¡Œã£ãŸåˆ¥ã®ãƒ•ォローã—ã¦ã„るユーザー㮠visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ await waitForPushToTl();
- await api('following/create', { userId: bob.id }, alice);
- await api('following/create', { userId: carol.id }, alice);
- await api('following/create', { userId: carol.id }, bob);
- await api('following/update', { userId: bob.id, withReplies: true }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await waitForPushToTl();
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ test('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®è¡Œã£ãŸåˆ¥ã®ãƒ•ォローã—ã¦ã„るユーザー㮠visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
- assert.strictEqual(res.body.find(note => note.id === carolNote.id)?.text, 'hi');
- });
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- test.concurrent('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®è‡ªåˆ†ã® visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/create', { userId: carol.id }, alice);
+ await api('following/create', { userId: carol.id }, bob);
+ await api('following/update', { userId: bob.id, withReplies: true }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
- await api('following/create', { userId: bob.id }, alice);
- await api('following/create', { userId: alice.id }, bob);
- await api('following/update', { userId: bob.id, withReplies: true }, alice);
- await setTimeout(1000);
- const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
- const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
+ await waitForPushToTl();
- await waitForPushToTl();
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === carolNote.id)?.text, 'hi');
+ });
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
- });
+ test('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®è‡ªåˆ†ã® visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
- test.concurrent('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®è¡Œã£ãŸåˆ¥ã®ãƒ•ォローã—ã¦ã„ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŠ•ç¨¿ã¸ã® visibility: specified ãªè¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- await api('following/create', { userId: bob.id }, alice);
- await api('following/create', { userId: carol.id }, alice);
- await api('following/update', { userId: bob.id, withReplies: true }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified', visibleUserIds: [carolNote.id] });
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/create', { userId: alice.id }, bob);
+ await api('following/update', { userId: bob.id, withReplies: true }, alice);
+ await setTimeout(250);
+ const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
+ const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
- });
+ assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
+ });
- test.concurrent('withReplies: false ã§ãƒ•ォローã—ã¦ã„るユーザーã®ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼è‡ªèº«ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®è¡Œã£ãŸåˆ¥ã®ãƒ•ォローã—ã¦ã„ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŠ•ç¨¿ã¸ã® visibility: specified ãªè¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote1 = await post(bob, { text: 'hi' });
- const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/create', { userId: carol.id }, alice);
+ await api('following/update', { userId: bob.id, withReplies: true }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified', visibleUserIds: [carolNote.id] });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
- assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
+ });
- test.concurrent('withReplies: false ã§ãƒ•ォローã—ã¦ã„るユーザーã‹ã‚‰ã®è‡ªåˆ†ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('withReplies: false ã§ãƒ•ォローã—ã¦ã„るユーザーã®ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼è‡ªèº«ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const aliceNote = await post(alice, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote1 = await post(bob, { text: 'hi' });
+ const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
+ });
- test.concurrent('自分ã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('withReplies: false ã§ãƒ•ォローã—ã¦ã„るユーザーã‹ã‚‰ã®è‡ªåˆ†ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
- const bobNote = await post(bob, { text: 'hi' });
- const aliceNote = await post(alice, { text: 'hi', replyId: bobNote.id });
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- await waitForPushToTl();
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const aliceNote = await post(alice, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ await waitForPushToTl();
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
- });
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- test.concurrent('フォローã—ã¦ã„るユーザーã®ä»–äººã®æŠ•ç¨¿ã®ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { renoteId: carolNote.id });
+ test('自分ã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
- await waitForPushToTl();
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ const bobNote = await post(bob, { text: 'hi' });
+ const aliceNote = await post(alice, { text: 'hi', replyId: bobNote.id });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('[withRenotes: false] フォローã—ã¦ã„るユーザーã®ä»–äººã®æŠ•ç¨¿ã®ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { renoteId: carolNote.id });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ });
- await waitForPushToTl();
+ test('フォローã—ã¦ã„るユーザーã®ä»–äººã®æŠ•ç¨¿ã®ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const res = await api('notes/timeline', {
- withRenotes: false,
- }, alice);
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { renoteId: carolNote.id });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('[withRenotes: false] フォローã—ã¦ã„るユーザーã®ä»–äººã®æŠ•ç¨¿ã®å¼•用ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- await waitForPushToTl();
+ test('[withRenotes: false] フォローã—ã¦ã„ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŠ•ç¨¿ãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const res = await api('notes/timeline', {
- withRenotes: false,
- }, alice);
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi' });
+ const carolNote = await post(carol, { text: 'hi' });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('フォローã—ã¦ã„るユーザーã®ä»–人ã¸ã® visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const res = await api('notes/timeline', {
+ limit: 100,
+ withRenotes: false,
+ }, alice);
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- await waitForPushToTl();
+ test('[withRenotes: false] フォローã—ã¦ã„るユーザーã®ãƒ•ァイルã®ã¿ã®æŠ•稿ãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const [bobFile, carolFile] = await Promise.all([
+ uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'),
+ uploadUrl(carol, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'),
+ ]);
+ const bobNote = await post(bob, { fileIds: [bobFile.id] });
+ const carolNote = await post(carol, { fileIds: [carolFile.id] });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('フォローã—ã¦ã„るユーザーãŒè¡Œã£ãŸãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã®ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const res = await api('notes/timeline', {
+ limit: 100,
+ withRenotes: false,
+ }, alice);
- await api('following/create', { userId: bob.id }, alice);
- await api('mute/create', { userId: carol.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- await waitForPushToTl();
+ test('[withRenotes: false] フォローã—ã¦ã„るユーザーã®ä»–äººã®æŠ•ç¨¿ã®ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { renoteId: carolNote.id });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーãŒè¡Œã£ãŸãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const res = await api('notes/timeline', {
+ withRenotes: false,
+ }, alice);
- await api('following/create', { userId: bob.id }, alice);
- await api('following/update', { userId: bob.id, withReplies: true }, alice);
- await api('mute/create', { userId: carol.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- await waitForPushToTl();
+ test('[withRenotes: false] フォローã—ã¦ã„るユーザーã®ä»–äººã®æŠ•ç¨¿ã®å¼•用ãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('ミュートã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆã®ã€é–¢ä¿‚ã®ãªã„ユーザã«ã‚ˆã‚‹å¼•用ノートã®ã€ãƒ•ォローã—ã¦ã„るユーザーã«ã‚ˆã‚‹ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
+ const res = await api('notes/timeline', {
+ withRenotes: false,
+ }, alice);
- await api('following/create', { userId: bob.id }, alice);
- await api('mute/create', { userId: carol.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const daveNote = await post(dave, { text: 'quote hi', renoteId: carolNote.id });
- const bobNote = await post(bob, { renoteId: daveNote.id });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- await waitForPushToTl();
+ test('フォローã—ã¦ã„るユーザーã®ä»–人ã¸ã® visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] });
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === daveNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('ミュートã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆã®ã€é–¢ä¿‚ã®ãªã„ユーザã«ã‚ˆã‚‹ãƒªãƒ—ライã®ã€ãƒ•ォローã—ã¦ã„るユーザーã«ã‚ˆã‚‹ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await api('following/create', { userId: bob.id }, alice);
- await api('mute/create', { userId: carol.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const daveNote = await post(dave, { text: 'quote hi', replyId: carolNote.id });
- const bobNote = await post(bob, { renoteId: daveNote.id });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- await waitForPushToTl();
+ test('フォローã—ã¦ã„るユーザーãŒè¡Œã£ãŸãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã®ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ await api('following/create', { userId: bob.id }, alice);
+ await api('mute/create', { userId: carol.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === daveNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('フォローã—ã¦ã„るリモートユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
- await api('following/create', { userId: bob.id }, alice);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- const bobNote = await post(bob, { text: 'hi' });
+ test('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーãŒè¡Œã£ãŸãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- await waitForPushToTl();
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/update', { userId: bob.id, withReplies: true }, alice);
+ await api('mute/create', { userId: carol.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ await waitForPushToTl();
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- test.concurrent('フォローã—ã¦ã„るリモートユーザー㮠visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
- await api('following/create', { userId: bob.id }, alice);
+ test('ミュートã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆã®ã€é–¢ä¿‚ã®ãªã„ユーザã«ã‚ˆã‚‹å¼•用ノートã®ã€ãƒ•ォローã—ã¦ã„るユーザーã«ã‚ˆã‚‹ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
- const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
+ await api('following/create', { userId: bob.id }, alice);
+ await api('mute/create', { userId: carol.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const daveNote = await post(dave, { text: 'quote hi', renoteId: carolNote.id });
+ const bobNote = await post(bob, { renoteId: daveNote.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === daveNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- test.concurrent('[withFiles: true] フォローã—ã¦ã„るユーザーã®ãƒ•ァイル付ãノートã®ã¿å«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ test('ミュートã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆã®ã€é–¢ä¿‚ã®ãªã„ユーザã«ã‚ˆã‚‹ãƒªãƒ—ライã®ã€ãƒ•ォローã—ã¦ã„るユーザーã«ã‚ˆã‚‹ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const [bobFile, carolFile] = await Promise.all([
- uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'),
- uploadUrl(carol, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'),
- ]);
- const bobNote1 = await post(bob, { text: 'hi' });
- const bobNote2 = await post(bob, { fileIds: [bobFile.id] });
- const carolNote1 = await post(carol, { text: 'hi' });
- const carolNote2 = await post(carol, { fileIds: [carolFile.id] });
+ await api('following/create', { userId: bob.id }, alice);
+ await api('mute/create', { userId: carol.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const daveNote = await post(dave, { text: 'quote hi', replyId: carolNote.id });
+ const bobNote = await post(bob, { renoteId: daveNote.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/timeline', { limit: 100, withFiles: true }, alice);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
- assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
- assert.strictEqual(res.body.some(note => note.id === carolNote1.id), false);
- assert.strictEqual(res.body.some(note => note.id === carolNote2.id), false);
- }, 1000 * 30);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === daveNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- test.concurrent('フォローã—ã¦ã„るユーザーã®ãƒãƒ£ãƒ³ãƒãƒ«æŠ•稿ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('フォローã—ã¦ã„るリモートユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
- const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
+ await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
+ await api('following/create', { userId: bob.id }, alice);
- await waitForPushToTl();
+ const bobNote = await post(bob, { text: 'hi' });
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ await waitForPushToTl();
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- test.concurrent('自分㮠visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice] = await Promise.all([signup()]);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- const aliceNote = await post(alice, { text: 'hi', visibility: 'specified' });
+ test('フォローã—ã¦ã„るリモートユーザー㮠visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
- await waitForPushToTl();
+ await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
+ await api('following/create', { userId: bob.id }, alice);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
- assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
- });
+ await waitForPushToTl();
- test.concurrent('フォローã—ã¦ã„るユーザーã®è‡ªèº«ã‚’ visibleUserIds ã«æŒ‡å®šã—㟠visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- await waitForPushToTl();
+ test('[withFiles: true] フォローã—ã¦ã„るユーザーã®ãƒ•ァイル付ãノートã®ã¿å«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const [bobFile, carolFile] = await Promise.all([
+ uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'),
+ uploadUrl(carol, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'),
+ ]);
+ const bobNote1 = await post(bob, { text: 'hi' });
+ const bobNote2 = await post(bob, { fileIds: [bobFile.id] });
+ const carolNote1 = await post(carol, { text: 'hi' });
+ const carolNote2 = await post(carol, { fileIds: [carolFile.id] });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
- });
+ await waitForPushToTl();
- test.concurrent('フォローã—ã¦ã„ãªã„ユーザーã®è‡ªèº«ã‚’ visibleUserIds ã«æŒ‡å®šã—㟠visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ const res = await api('notes/timeline', { limit: 100, withFiles: true }, alice);
- const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] });
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote1.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote2.id), false);
+ }, 1000 * 30);
- await waitForPushToTl();
+ test('フォローã—ã¦ã„るユーザーã®ãƒãƒ£ãƒ³ãƒãƒ«æŠ•稿ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('フォローã—ã¦ã„るユーザーã®è‡ªèº«ã‚’ visibleUserIds ã«æŒ‡å®šã—ã¦ã„ãªã„ visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- await waitForPushToTl();
+ test('自分㮠visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice] = await Promise.all([signup()]);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ const aliceNote = await post(alice, { text: 'hi', visibility: 'specified' });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('フォローã—ã¦ã„ãªã„ユーザーã‹ã‚‰ã® visibility: specified ãªãƒŽãƒ¼ãƒˆã«è¿”ä¿¡ã—ãŸã¨ãã®è‡ªèº«ã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] });
- const aliceNote = await post(alice, { text: 'ok', visibility: 'specified', visibleUserIds: [bob.id], replyId: bobNote.id });
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
+ });
- await waitForPushToTl();
+ test('フォローã—ã¦ã„るユーザーã®è‡ªèº«ã‚’ visibleUserIds ã«æŒ‡å®šã—㟠visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] });
- assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'ok');
- });
+ await waitForPushToTl();
- /* TODO
- test.concurrent('自身㮠visibility: specified ãªãƒŽãƒ¼ãƒˆã¸ã®ãƒ•ォローã—ã¦ã„ãªã„ユーザーã‹ã‚‰ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] });
- const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
+ });
- await waitForPushToTl();
+ test('フォローã—ã¦ã„ãªã„ユーザーã®è‡ªèº«ã‚’ visibleUserIds ã«æŒ‡å®šã—㟠visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- assert.strictEqual(res.body.find(note => note.id === bobNote.id).text, 'ok');
- });
- */
+ await waitForPushToTl();
- // â†‘ã®æŒ™å‹•ãŒç†æƒ³ã ã‘ã©å®Ÿè£…ãŒé¢å€’ã‹ã‚‚
- test.concurrent('自身㮠visibility: specified ãªãƒŽãƒ¼ãƒˆã¸ã®ãƒ•ォローã—ã¦ã„ãªã„ユーザーã‹ã‚‰ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] });
- const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- await waitForPushToTl();
+ test('フォローã—ã¦ã„るユーザーã®è‡ªèº«ã‚’ visibleUserIds ã«æŒ‡å®šã—ã¦ã„ãªã„ visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const res = await api('notes/timeline', { limit: 100 }, alice);
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('FTT: ローカルユーザー㮠HTL ã«ã¯ãƒ—ッシュã•れる', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await api('following/create', {
- userId: alice.id,
- }, bob);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- const aliceNote = await post(alice, { text: 'I\'m Alice.' });
- const bobNote = await post(bob, { text: 'I\'m Bob.' });
- const carolNote = await post(carol, { text: 'I\'m Carol.' });
+ test('フォローã—ã¦ã„ãªã„ユーザーã‹ã‚‰ã® visibility: specified ãªãƒŽãƒ¼ãƒˆã«è¿”ä¿¡ã—ãŸã¨ãã®è‡ªèº«ã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
- await waitForPushToTl();
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- // NOTE: notes/timeline ã ã¨ DB ã¸ã®ãƒ•ォールãƒãƒƒã‚¯ãŒåйãã®ã§ Redis を直接見ã¦ç¢ºã‹ã‚ã‚‹
- assert.strictEqual(await redisForTimelines.exists(`list:homeTimeline:${bob.id}`), 1);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] });
+ const aliceNote = await post(alice, { text: 'ok', visibility: 'specified', visibleUserIds: [bob.id], replyId: bobNote.id });
- const bobHTL = await redisForTimelines.lrange(`list:homeTimeline:${bob.id}`, 0, -1);
- assert.strictEqual(bobHTL.includes(aliceNote.id), true);
- assert.strictEqual(bobHTL.includes(bobNote.id), true);
- assert.strictEqual(bobHTL.includes(carolNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('FTT: リモートユーザー㮠HTL ã«ã¯ãƒ—ッシュã•れãªã„', async () => {
- const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await api('following/create', {
- userId: alice.id,
- }, bob);
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'ok');
+ });
- await post(alice, { text: 'I\'m Alice.' });
- await post(bob, { text: 'I\'m Bob.' });
+ /* TODO
+ test('自身㮠visibility: specified ãªãƒŽãƒ¼ãƒˆã¸ã®ãƒ•ォローã—ã¦ã„ãªã„ユーザーã‹ã‚‰ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
+ const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] });
+ const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id });
+ await waitForPushToTl();
+ const res = await api('notes/timeline', { limit: 100 }, alice);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === bobNote.id).text, 'ok');
+ });
+ */
- await waitForPushToTl();
+ // â†‘ã®æŒ™å‹•ãŒç†æƒ³ã ã‘ã©å®Ÿè£…ãŒé¢å€’ã‹ã‚‚
+ test('自身㮠visibility: specified ãªãƒŽãƒ¼ãƒˆã¸ã®ãƒ•ォローã—ã¦ã„ãªã„ユーザーã‹ã‚‰ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- // NOTE: notes/timeline ã ã¨ DB ã¸ã®ãƒ•ォールãƒãƒƒã‚¯ãŒåйãã®ã§ Redis を直接見ã¦ç¢ºã‹ã‚ã‚‹
- assert.strictEqual(await redisForTimelines.exists(`list:homeTimeline:${bob.id}`), 0);
- });
- });
+ const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] });
+ const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id });
- describe('Local TL', () => {
- test.concurrent('visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ await waitForPushToTl();
- const carolNote = await post(carol, { text: 'hi', visibility: 'home' });
- const bobNote = await post(bob, { text: 'hi' });
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await waitForPushToTl();
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- const res = await api('notes/local-timeline', { limit: 100 }, alice);
+ test('FTT: ローカルユーザー㮠HTL ã«ã¯ãƒ—ッシュã•れる', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- });
+ await api('following/create', {
+ userId: alice.id,
+ }, bob);
- test.concurrent('他人ã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const aliceNote = await post(alice, { text: 'I\'m Alice.' });
+ const bobNote = await post(bob, { text: 'I\'m Bob.' });
+ const carolNote = await post(carol, { text: 'I\'m Carol.' });
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+ await waitForPushToTl();
- await waitForPushToTl();
+ if (enableFanoutTimeline) {
+ // NOTE: notes/timeline ã ã¨ DB ã¸ã®ãƒ•ォールãƒãƒƒã‚¯ãŒåйãã®ã§ Redis を直接見ã¦ç¢ºã‹ã‚ã‚‹
+ assert.strictEqual(await redisForTimelines.exists(`list:homeTimeline:${bob.id}`), 1);
- const res = await api('notes/local-timeline', { limit: 100 }, alice);
+ const bobHTL = await redisForTimelines.lrange(`list:homeTimeline:${bob.id}`, 0, -1);
+ assert.strictEqual(bobHTL.includes(aliceNote.id), true);
+ assert.strictEqual(bobHTL.includes(bobNote.id), true);
+ assert.strictEqual(bobHTL.includes(carolNote.id), false);
+ } else {
+ assert.strictEqual(await redisForTimelines.exists(`list:homeTimeline:${bob.id}`), 0);
+ }
+ });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
- });
+ test('FTT: リモートユーザー㮠HTL ã«ã¯ãƒ—ッシュã•れãªã„', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
- test.concurrent('他人ã®ãã®äººè‡ªèº«ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ await api('following/create', {
+ userId: alice.id,
+ }, bob);
- const bobNote1 = await post(bob, { text: 'hi' });
- const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
+ await post(alice, { text: 'I\'m Alice.' });
+ await post(bob, { text: 'I\'m Bob.' });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/local-timeline', { limit: 100 }, alice);
+ // NOTE: notes/timeline ã ã¨ DB ã¸ã®ãƒ•ォールãƒãƒƒã‚¯ãŒåйãã®ã§ Redis を直接見ã¦ç¢ºã‹ã‚ã‚‹
+ assert.strictEqual(await redisForTimelines.exists(`list:homeTimeline:${bob.id}`), 0);
+ });
- assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
- assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
- });
+ describe('å‡çµ', () => {
+ let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse;
+ let aliceNote: Note, bobNote: Note, carolNote: Note;
- test.concurrent('ãƒãƒ£ãƒ³ãƒãƒ«æŠ•稿ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ beforeAll(async () => {
+ [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
- const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/create', { userId: carol.id }, alice);
+ aliceNote = await post(alice, { text: 'hi' });
+ bobNote = await post(bob, { text: 'yo' });
+ carolNote = await post(carol, { text: 'kon\'nichiwa' });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/local-timeline', { limit: 100 }, alice);
+ await api('admin/suspend-user', { userId: carol.id }, root);
+ await setTimeout(100);
+ });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ test('å‡çµå¾Œã«å‡çµã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆã¯è¦‹ãˆãªããªã‚‹', async () => {
+ const res = await api('notes/timeline', { limit: 100 }, alice);
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- test.concurrent('リモートユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
+ test('å‡çµè§£é™¤å¾Œã«å‡çµã•れã¦ã„ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆã¯è¦‹ãˆã‚‹ã‚ˆã†ã«ãªã‚‹', async () => {
+ await api('admin/unsuspend-user', { userId: carol.id }, root);
+ await setTimeout(100);
- const bobNote = await post(bob, { text: 'hi' });
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await waitForPushToTl();
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === carolNote.id)?.text, 'kon\'nichiwa');
+ });
+ });
- const res = await api('notes/local-timeline', { limit: 100 }, alice);
+ describe('å‡çµ (Renote)', () => {
+ let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse;
+ let aliceNote: Note, bobNote: Note, carolNote: Note, bobRenote: Note, carolRenote: Note;
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ beforeAll(async () => {
+ [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- // å«ã¾ã‚Œã¦ã‚‚良ã„ã¨æ€ã†ã‘ã©å®Ÿè£…ãŒé¢å€’ãªã®ã§å«ã¾ã‚Œãªã„
- test.concurrent('フォローã—ã¦ã„るユーザー㮠visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/create', { userId: carol.id }, alice);
+ aliceNote = await post(alice, { text: 'hi' });
+ bobNote = await post(bob, { text: 'yo' });
+ carolNote = await post(carol, { text: 'kon\'nichiwa' });
+ bobRenote = await post(bob, { renoteId: carolNote.id });
+ carolRenote = await post(carol, { renoteId: bobNote.id });
- await api('following/create', { userId: carol.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi', visibility: 'home' });
- const bobNote = await post(bob, { text: 'hi' });
+ await waitForPushToTl();
- await waitForPushToTl();
+ await api('admin/suspend-user', { userId: carol.id }, root);
+ await setTimeout(100);
+ });
- const res = await api('notes/local-timeline', { limit: 100 }, alice);
+ test('å‡çµå¾Œã«å‡çµã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã«å¯¾ã™ã‚‹Renoteã‚„å‡çµã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®RenoteãŒè¦‹ãˆãªããªã‚‹', async () => {
+ const res = await api('notes/timeline', { limit: 100 }, alice);
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobRenote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolRenote.id), false);
+ });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- });
+ test('å‡çµè§£é™¤å¾Œã«å‡çµã•れã¦ã„ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã«å¯¾ã™ã‚‹Renoteã‚„å‡çµã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®RenoteãŒè¦‹ãˆã‚‹ã‚ˆã†ã«ãªã‚‹', async () => {
+ await api('admin/unsuspend-user', { userId: carol.id }, root);
+ await setTimeout(100);
- test.concurrent('ミュートã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await api('mute/create', { userId: carol.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi' });
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobRenote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolRenote.id), true);
+ });
+ });
- await waitForPushToTl();
+ describe('å‡çµ(リモート)', () => {
+ let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse;
+ let aliceNote: Note, bobNote: Note, carolNote: Note;
- const res = await api('notes/local-timeline', { limit: 100 }, alice);
+ beforeAll(async () => {
+ [alice, bob, carol] = await Promise.all([signup(), signup({ host: genHost() }), signup({ host: genHost() })]);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- });
+ await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/create', { userId: carol.id }, alice);
+ aliceNote = await post(alice, { text: 'hi' });
+ bobNote = await post(bob, { text: 'yo' });
+ carolNote = await post(carol, { text: 'kon\'nichiwa' });
+
+ await waitForPushToTl();
- test.concurrent('フォローã—ã¦ã„るユーザーãŒè¡Œã£ãŸãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã®ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ await api('admin/suspend-user', { userId: carol.id }, root);
+ await setTimeout(100);
+ });
- await api('following/create', { userId: bob.id }, alice);
- await api('mute/create', { userId: carol.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
+ test('å‡çµå¾Œã«å‡çµã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆã¯è¦‹ãˆãªããªã‚‹', async () => {
+ const res = await api('notes/timeline', { limit: 100 }, alice);
- await waitForPushToTl();
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- const res = await api('notes/local-timeline', { limit: 100 }, alice);
+ test('å‡çµè§£é™¤å¾Œã«å‡çµã•れã¦ã„ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆã¯è¦‹ãˆã‚‹ã‚ˆã†ã«ãªã‚‹', async () => {
+ await api('admin/unsuspend-user', { userId: carol.id }, root);
+ await setTimeout(100);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ const res = await api('notes/timeline', { limit: 100 }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
+ });
+ });
});
- test.concurrent('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーãŒè¡Œã£ãŸãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ describe('Local TL', () => {
+ test('visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- await api('following/create', { userId: bob.id }, alice);
- await api('following/update', { userId: bob.id, withReplies: true }, alice);
- await api('mute/create', { userId: carol.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+ const carolNote = await post(carol, { text: 'hi', visibility: 'home' });
+ const bobNote = await post(bob, { text: 'hi' });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/local-timeline', { limit: 100 }, alice);
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- test.concurrent('ミュートã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆã®ã€é–¢ä¿‚ã®ãªã„ユーザã«ã‚ˆã‚‹å¼•用ノートã®ã€ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
+ test('他人ã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- await api('mute/create', { userId: carol.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const daveNote = await post(dave, { text: 'quote hi', renoteId: carolNote.id });
- const bobNote = await post(bob, { renoteId: daveNote.id });
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/local-timeline', { limit: 100 }, alice);
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === daveNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
+ });
- test.concurrent('ミュートã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆã®ã€é–¢ä¿‚ã®ãªã„ユーザã«ã‚ˆã‚‹ãƒªãƒ—ライã®ã€ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
+ test('他人ã®ãã®äººè‡ªèº«ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- await api('mute/create', { userId: carol.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const daveNote = await post(dave, { text: 'quote hi', replyId: carolNote.id });
- const bobNote = await post(bob, { renoteId: daveNote.id });
+ const bobNote1 = await post(bob, { text: 'hi' });
+ const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/local-timeline', { limit: 100 }, alice);
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === daveNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
+ });
- test.concurrent('withReplies: false ã§ãƒ•ォローã—ã¦ã„るユーザーã‹ã‚‰ã®è‡ªåˆ†ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('ãƒãƒ£ãƒ³ãƒãƒ«æŠ•稿ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const aliceNote = await post(alice, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
+ const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
+ const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/local-timeline', { limit: 100 }, alice);
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- test.concurrent('withReplies: false ã§ãƒ•ォローã—ã¦ã„ãªã„ユーザーã‹ã‚‰ã®è‡ªåˆ†ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('リモートユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
- await setTimeout(1000);
- const aliceNote = await post(alice, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
+ const bobNote = await post(bob, { text: 'hi' });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/local-timeline', { limit: 100 }, alice);
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- test.concurrent('[withReplies: true] 他人ã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ // å«ã¾ã‚Œã¦ã‚‚良ã„ã¨æ€ã†ã‘ã©å®Ÿè£…ãŒé¢å€’ãªã®ã§å«ã¾ã‚Œãªã„
+ test('フォローã—ã¦ã„るユーザー㮠visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+ await api('following/create', { userId: carol.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi', visibility: 'home' });
+ const bobNote = await post(bob, { text: 'hi' });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/local-timeline', { limit: 100, withReplies: true }, alice);
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- test.concurrent('[withFiles: true] ファイル付ãノートã®ã¿å«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('ミュートã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png');
- const bobNote1 = await post(bob, { text: 'hi' });
- const bobNote2 = await post(bob, { fileIds: [file.id] });
+ await api('mute/create', { userId: carol.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi' });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/local-timeline', { limit: 100, withFiles: true }, alice);
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
- assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
- }, 1000 * 10);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- describe('Social TL', () => {
- test.concurrent('ローカルユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('フォローã—ã¦ã„るユーザーãŒè¡Œã£ãŸãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã®ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const bobNote = await post(bob, { text: 'hi' });
+ await api('following/create', { userId: bob.id }, alice);
+ await api('mute/create', { userId: carol.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- test.concurrent('ローカルユーザー㮠visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーãŒè¡Œã£ãŸãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/update', { userId: bob.id, withReplies: true }, alice);
+ await api('mute/create', { userId: carol.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- test.concurrent('フォローã—ã¦ã„るローカルユーザー㮠visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('ミュートã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆã®ã€é–¢ä¿‚ã®ãªã„ユーザã«ã‚ˆã‚‹å¼•用ノートã®ã€ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
+ await api('mute/create', { userId: carol.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const daveNote = await post(dave, { text: 'quote hi', renoteId: carolNote.id });
+ const bobNote = await post(bob, { renoteId: daveNote.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === daveNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- test.concurrent('withReplies: false ã§ãƒ•ォローã—ã¦ã„るユーザーã‹ã‚‰ã®è‡ªåˆ†ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('ミュートã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆã®ã€é–¢ä¿‚ã®ãªã„ユーザã«ã‚ˆã‚‹ãƒªãƒ—ライã®ã€ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const aliceNote = await post(alice, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
+ await api('mute/create', { userId: carol.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const daveNote = await post(dave, { text: 'quote hi', replyId: carolNote.id });
+ const bobNote = await post(bob, { renoteId: daveNote.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === daveNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- test.concurrent('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®ä»–人㮠visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ test('withReplies: false ã§ãƒ•ォローã—ã¦ã„るユーザーã‹ã‚‰ã®è‡ªåˆ†ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
- await api('following/create', { userId: carol.id }, bob);
- await api('following/create', { userId: bob.id }, alice);
- await api('following/update', { userId: bob.id, withReplies: true }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- await waitForPushToTl();
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const aliceNote = await post(alice, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
- const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
+ await waitForPushToTl();
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
- });
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
- test.concurrent('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®è¡Œã£ãŸåˆ¥ã®ãƒ•ォローã—ã¦ã„るユーザー㮠visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- await api('following/create', { userId: bob.id }, alice);
- await api('following/create', { userId: carol.id }, alice);
- await api('following/create', { userId: carol.id }, bob);
- await api('following/update', { userId: bob.id, withReplies: true }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+ test('withReplies: false ã§ãƒ•ォローã—ã¦ã„ãªã„ユーザーã‹ã‚‰ã®è‡ªåˆ†ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
- await waitForPushToTl();
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
+ await setTimeout(250);
+ const aliceNote = await post(alice, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
- assert.strictEqual(res.body.find((note: any) => note.id === carolNote.id)?.text, 'hi');
- });
+ await waitForPushToTl();
- test.concurrent('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®è‡ªåˆ†ã® visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
- await api('following/create', { userId: bob.id }, alice);
- await api('following/create', { userId: alice.id }, bob);
- await api('following/update', { userId: bob.id, withReplies: true }, alice);
- await setTimeout(1000);
- const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
- const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- await waitForPushToTl();
+ test('[withReplies: true] 他人ã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
- });
+ await waitForPushToTl();
- test.concurrent('他人ã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const res = await api('notes/local-timeline', { limit: 100, withReplies: true }, alice);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- await waitForPushToTl();
+ test('[withFiles: true] ファイル付ãノートã®ã¿å«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
+ const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png');
+ const bobNote1 = await post(bob, { text: 'hi' });
+ const bobNote2 = await post(bob, { fileIds: [file.id] });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
- });
+ await waitForPushToTl();
- test.concurrent('リモートユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
+ const res = await api('notes/local-timeline', { limit: 100, withFiles: true }, alice);
- const bobNote = await post(bob, { text: 'hi' });
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
+ }, 1000 * 10);
- await waitForPushToTl();
+ describe('å‡çµ', () => {
+ let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse;
+ let aliceNote: Note, bobNote: Note, carolNote: Note;
- const res = await api('notes/local-timeline', { limit: 100 }, alice);
+ beforeAll(async () => {
+ [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ aliceNote = await post(alice, { text: 'hi' });
+ bobNote = await post(bob, { text: 'yo' });
+ carolNote = await post(carol, { text: 'kon\'nichiwa' });
- test.concurrent('フォローã—ã¦ã„るリモートユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
+ await waitForPushToTl();
+ });
- await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
- await api('following/create', { userId: bob.id }, alice);
+ test('å‡çµå¾Œã«å‡çµã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆã¯è¦‹ãˆãªããªã‚‹', async () => {
+ await api('admin/suspend-user', { userId: carol.id }, root);
+ await setTimeout(100);
- const bobNote = await post(bob, { text: 'hi' });
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
- await waitForPushToTl();
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
+ test('å‡çµè§£é™¤å¾Œã«å‡çµã•れã¦ã„ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆã¯è¦‹ãˆã‚‹ã‚ˆã†ã«ãªã‚‹', async () => {
+ await api('admin/unsuspend-user', { userId: carol.id }, root);
+ await setTimeout(100);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === carolNote.id)?.text, 'kon\'nichiwa');
+ });
+ });
});
- test.concurrent('フォローã—ã¦ã„るリモートユーザー㮠visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
+ describe('Social TL', () => {
+ test('ローカルユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
- await api('following/create', { userId: bob.id }, alice);
+ const bobNote = await post(bob, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
+ await waitForPushToTl();
- await waitForPushToTl();
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ test('ローカルユーザー㮠visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- test.concurrent('withReplies: false ã§ãƒ•ォローã—ã¦ã„ãªã„ユーザーã‹ã‚‰ã®è‡ªåˆ†ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
- await setTimeout(1000);
- const aliceNote = await post(alice, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
+ await waitForPushToTl();
- await waitForPushToTl();
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- const res = await api('notes/local-timeline', { limit: 100 }, alice);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ test('フォローã—ã¦ã„るローカルユーザー㮠visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- test.concurrent('[withReplies: true] 他人ã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+ await waitForPushToTl();
- await waitForPushToTl();
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- const res = await api('notes/hybrid-timeline', { limit: 100, withReplies: true }, alice);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ test('withReplies: false ã§ãƒ•ォローã—ã¦ã„るユーザーã‹ã‚‰ã®è‡ªåˆ†ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
- test.concurrent('[withFiles: true] ファイル付ãノートã®ã¿å«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png');
- const bobNote1 = await post(bob, { text: 'hi' });
- const bobNote2 = await post(bob, { fileIds: [file.id] });
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const aliceNote = await post(alice, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/hybrid-timeline', { limit: 100, withFiles: true }, alice);
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
- assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
- }, 1000 * 10);
- });
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- describe('User List TL', () => {
- test.concurrent('リスインã—ã¦ã„るフォローã—ã¦ã„ãªã„ユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®ä»–人㮠visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
- await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote = await post(bob, { text: 'hi' });
+ await api('following/create', { userId: carol.id }, bob);
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/update', { userId: bob.id, withReplies: true }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ });
- test.concurrent('リスインã—ã¦ã„るフォローã—ã¦ã„ãªã„ユーザー㮠visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®è¡Œã£ãŸåˆ¥ã®ãƒ•ォローã—ã¦ã„るユーザー㮠visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
- const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
- await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- await waitForPushToTl();
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/create', { userId: carol.id }, alice);
+ await api('following/create', { userId: carol.id }, bob);
+ await api('following/update', { userId: bob.id, withReplies: true }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
- const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
+ await waitForPushToTl();
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- test.concurrent('リスインã—ã¦ã„るフォローã—ã¦ã„ãªã„ユーザー㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
+ assert.strictEqual(res.body.find((note: any) => note.id === carolNote.id)?.text, 'hi');
+ });
- const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
- await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
+ test('withReplies: true ã§ãƒ•ォローã—ã¦ã„るユーザーã®è‡ªåˆ†ã® visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
- await waitForPushToTl();
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/create', { userId: alice.id }, bob);
+ await api('following/update', { userId: bob.id, withReplies: true }, alice);
+ await setTimeout(250);
+ const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
+ const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('リスインã—ã¦ã„るフォローã—ã¦ã„ãªã„ユーザーã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
- await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+ assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
+ });
- await waitForPushToTl();
+ test('他人ã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ await waitForPushToTl();
- test.concurrent('リスインã—ã¦ã„るフォローã—ã¦ã„ãªã„ユーザーã®ãƒ¦ãƒ¼ã‚¶ãƒ¼è‡ªèº«ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
- await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote1 = await post(bob, { text: 'hi' });
- const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
+ });
- await waitForPushToTl();
+ test('リモートユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
- const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
+ const bobNote = await post(bob, { text: 'hi' });
- assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
- assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
- });
+ await waitForPushToTl();
- test.concurrent('withReplies: false ã§ãƒªã‚¹ã‚¤ãƒ³ã—ã¦ã„るフォローã—ã¦ã„ãªã„ユーザーã‹ã‚‰ã®è‡ªåˆ†ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
- const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
- await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: false }, alice);
- await setTimeout(1000);
- const aliceNote = await post(alice, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- await waitForPushToTl();
+ test('フォローã—ã¦ã„るリモートユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
- const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
+ await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
+ await api('following/create', { userId: bob.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ const bobNote = await post(bob, { text: 'hi' });
- test.concurrent('withReplies: false ã§ãƒªã‚¹ã‚¤ãƒ³ã—ã¦ã„るフォローã—ã¦ã„ãªã„ユーザーã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ await waitForPushToTl();
- const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
- await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: false }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- await waitForPushToTl();
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
+ test('フォローã—ã¦ã„るリモートユーザー㮠visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
+ await api('following/create', { userId: bob.id }, alice);
- test.concurrent('withReplies: true ã§ãƒªã‚¹ã‚¤ãƒ³ã—ã¦ã„るフォローã—ã¦ã„ãªã„ユーザーã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
- const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
- await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: true }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+ await waitForPushToTl();
- await waitForPushToTl();
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ test('withReplies: false ã§ãƒ•ォローã—ã¦ã„ãªã„ユーザーã‹ã‚‰ã®è‡ªåˆ†ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
- test.concurrent('リスインã—ã¦ã„るフォローã—ã¦ã„るユーザー㮠visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- await api('following/create', { userId: bob.id }, alice);
- const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
- await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
+ await setTimeout(250);
+ const aliceNote = await post(alice, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- test.concurrent('リスインã—ã¦ã„るフォローã—ã¦ã„るユーザー㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('[withReplies: true] 他人ã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- await api('following/create', { userId: bob.id }, alice);
- const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
- await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
+ const res = await api('notes/hybrid-timeline', { limit: 100, withReplies: true }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- test.concurrent('リスインã—ã¦ã„る自分㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice] = await Promise.all([signup(), signup()]);
+ test('[withFiles: true] ファイル付ãノートã®ã¿å«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
- await api('users/lists/push', { listId: list.id, userId: alice.id }, alice);
- await setTimeout(1000);
- const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
+ const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png');
+ const bobNote1 = await post(bob, { text: 'hi' });
+ const bobNote2 = await post(bob, { fileIds: [file.id] });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
+ const res = await api('notes/hybrid-timeline', { limit: 100, withFiles: true }, alice);
- assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
+ }, 1000 * 10);
- test.concurrent('リスインã—ã¦ã„るユーザーã®ãƒãƒ£ãƒ³ãƒãƒ«ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ describe('å‡çµ', () => {
+ /*
+ * bob = 未フォローã®ãƒ­ãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ (å‡çµå¯¾è±¡ã§ãªã„)
+ * carol = 未フォローã®ãƒ­ãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ (å‡çµå¯¾è±¡)
+ * dave = フォローã—ã¦ã„るローカルユーザー (å‡çµå¯¾è±¡)
+ */
+ let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse, dave: SignupResponse;
+ let aliceNote: Note, bobNote: Note, carolNote: Note, daveNote: Note;
- const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
- const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
- await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
+ beforeAll(async () => {
+ [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
- await waitForPushToTl();
+ await api('following/create', { userId: dave.id }, alice);
+ aliceNote = await post(alice, { text: 'hi' });
+ bobNote = await post(bob, { text: 'yo' });
+ carolNote = await post(carol, { text: 'kon\'nichiwa' });
+ daveNote = await post(dave, { text: 'hello' });
- const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
+ await waitForPushToTl();
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ await api('admin/suspend-user', { userId: carol.id }, root);
+ await api('admin/suspend-user', { userId: dave.id }, root);
+ await setTimeout(250);
+ });
- test.concurrent('[withFiles: true] リスインã—ã¦ã„るユーザーã®ãƒ•ァイル付ãノートã®ã¿å«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('å‡çµå¾Œã«å‡çµã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆã¯è¦‹ãˆãªããªã‚‹', async () => {
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
- await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png');
- const bobNote1 = await post(bob, { text: 'hi' });
- const bobNote2 = await post(bob, { fileIds: [file.id] });
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === daveNote.id), false);
+ });
- await waitForPushToTl();
+ test('å‡çµè§£é™¤å¾Œã«å‡çµã•れã¦ã„ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆã¯è¦‹ãˆã‚‹ã‚ˆã†ã«ãªã‚‹', async () => {
+ await api('admin/unsuspend-user', { userId: carol.id }, root);
+ await api('admin/unsuspend-user', { userId: dave.id }, root);
+ await setTimeout(250);
- const res = await api('notes/user-list-timeline', { listId: list.id, withFiles: true }, alice);
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
- assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
- }, 1000 * 10);
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === daveNote.id), true);
+ });
+ });
- test.concurrent('リスインã—ã¦ã„るユーザーã®è‡ªèº«å®›ã¦ã® visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ describe('å‡çµ (リモート)', () => {
+ /*
+ * carol = 未フォローã®ãƒªãƒ¢ãƒ¼ãƒˆãƒ¦ãƒ¼ã‚¶ãƒ¼ (å‡çµå¯¾è±¡)
+ * elle = フォローã—ã¦ã„るリモートユーザー (å‡çµå¯¾è±¡)
+ */
+ let alice: SignupResponse, carol: SignupResponse, elle: SignupResponse;
+ let aliceNote: Note, carolNote: Note, elleNote: Note;
- const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
- await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] });
+ beforeAll(async () => {
+ [alice, carol, elle] = await Promise.all([signup(), signup({ host: genHost() }), signup({ host: genHost() })]);
- await waitForPushToTl();
+ await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
+ await api('following/create', { userId: elle.id }, alice);
+ aliceNote = await post(alice, { text: 'hi' });
+ carolNote = await post(carol, { text: 'kon\'nichiwa' });
+ elleNote = await post(elle, { text: 'hi there' });
- const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
+ await waitForPushToTl();
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
- });
+ await api('admin/suspend-user', { userId: carol.id }, root);
+ await api('admin/suspend-user', { userId: elle.id }, root);
+ await setTimeout(250);
+ });
- test.concurrent('リスインã—ã¦ã„るユーザーã®è‡ªèº«å®›ã¦ã§ã¯ãªã„ visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ test('å‡çµå¾Œã«å‡çµã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆã¯è¦‹ãˆãªããªã‚‹', async () => {
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
- await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await api('users/lists/push', { listId: list.id, userId: carol.id }, alice);
- await setTimeout(1000);
- const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] });
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === elleNote.id), false);
+ });
- await waitForPushToTl();
+ test('å‡çµè§£é™¤å¾Œã«å‡çµã•れã¦ã„ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆã¯è¦‹ãˆã‚‹ã‚ˆã†ã«ãªã‚‹', async () => {
+ await api('admin/unsuspend-user', { userId: carol.id }, root);
+ await api('admin/unsuspend-user', { userId: elle.id }, root);
+ await setTimeout(250);
- const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === elleNote.id), true);
+ });
+ });
});
- });
- describe('User TL', () => {
- test.concurrent('ノートãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ describe('User List TL', () => {
+ test('リスインã—ã¦ã„るフォローã—ã¦ã„ãªã„ユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const bobNote = await post(bob, { text: 'hi' });
+ const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+ await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi' });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('users/notes', { userId: bob.id }, alice);
+ const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- test.concurrent('フォローã—ã¦ã„ãªã„ユーザー㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('リスインã—ã¦ã„るフォローã—ã¦ã„ãªã„ユーザー㮠visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
+ const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+ await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('users/notes', { userId: bob.id }, alice);
+ const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- test.concurrent('フォローã—ã¦ã„るユーザー㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('リスインã—ã¦ã„るフォローã—ã¦ã„ãªã„ユーザー㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- await api('following/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
+ const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+ await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('users/notes', { userId: bob.id }, alice);
+ const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- test.concurrent('自身㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice] = await Promise.all([signup()]);
+ test('リスインã—ã¦ã„るフォローã—ã¦ã„ãªã„ユーザーã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
+ const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+ await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('users/notes', { userId: alice.id }, alice);
+ const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- test.concurrent('ãƒãƒ£ãƒ³ãƒãƒ«æŠ•稿ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('リスインã—ã¦ã„るフォローã—ã¦ã„ãªã„ユーザーã®ãƒ¦ãƒ¼ã‚¶ãƒ¼è‡ªèº«ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
- const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
+ const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+ await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote1 = await post(bob, { text: 'hi' });
+ const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('users/notes', { userId: bob.id }, alice);
+ const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
+ });
- test.concurrent('[withReplies: false] 他人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ test('withReplies: false ã§ãƒªã‚¹ã‚¤ãƒ³ã—ã¦ã„るフォローã—ã¦ã„ãªã„ユーザーã‹ã‚‰ã®è‡ªåˆ†ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote1 = await post(bob, { text: 'hi' });
- const bobNote2 = await post(bob, { text: 'hi', replyId: carolNote.id });
+ const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+ await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+ await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: false }, alice);
+ await setTimeout(250);
+ const aliceNote = await post(alice, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('users/notes', { userId: bob.id }, alice);
+ const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
- assert.strictEqual(res.body.some(note => note.id === bobNote2.id), false);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- test.concurrent('[withReplies: true] 他人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ test('withReplies: false ã§ãƒªã‚¹ã‚¤ãƒ³ã—ã¦ã„るフォローã—ã¦ã„ãªã„ユーザーã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote1 = await post(bob, { text: 'hi' });
- const bobNote2 = await post(bob, { text: 'hi', replyId: carolNote.id });
+ const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+ await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+ await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: false }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
+ const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
- assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- test.concurrent('[withReplies: true] 他人ã¸ã® visibility: specified ãªè¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ test('withReplies: true ã§ãƒªã‚¹ã‚¤ãƒ³ã—ã¦ã„るフォローã—ã¦ã„ãªã„ユーザーã®ä»–人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote1 = await post(bob, { text: 'hi' });
- const bobNote2 = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified' });
+ const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+ await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+ await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: true }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
+ const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
- assert.strictEqual(res.body.some(note => note.id === bobNote2.id), false);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- test.concurrent('[withFiles: true] ファイル付ãノートã®ã¿å«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('リスインã—ã¦ã„るフォローã—ã¦ã„るユーザー㮠visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png');
- const bobNote1 = await post(bob, { text: 'hi' });
- const bobNote2 = await post(bob, { fileIds: [file.id] });
+ await api('following/create', { userId: bob.id }, alice);
+ const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+ await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('users/notes', { userId: bob.id, withFiles: true }, alice);
+ const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
- assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
- }, 1000 * 10);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- test.concurrent('[withChannelNotes: true] ãƒãƒ£ãƒ³ãƒãƒ«æŠ•稿ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('リスインã—ã¦ã„るフォローã—ã¦ã„るユーザー㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
- const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
+ await api('following/create', { userId: bob.id }, alice);
+ const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+ await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice);
+ const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
+ });
- test.concurrent('[withChannelNotes: true] 他人ãŒå–å¾—ã—ãŸå ´åˆã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ãƒãƒ£ãƒ³ãƒãƒ«æŠ•稿ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ test('リスインã—ã¦ã„る自分㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice] = await Promise.all([signup(), signup()]);
- const channel = await api('channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body);
- const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
+ const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+ await api('users/lists/push', { listId: list.id, userId: alice.id }, alice);
+ await setTimeout(250);
+ const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice);
+ const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
+ });
- test.concurrent('[withChannelNotes: true] 自分ãŒå–å¾—ã—ãŸå ´åˆã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ãƒãƒ£ãƒ³ãƒãƒ«æŠ•稿ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [bob] = await Promise.all([signup()]);
+ test('リスインã—ã¦ã„るユーザーã®ãƒãƒ£ãƒ³ãƒãƒ«ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const channel = await api('channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body);
- const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
+ const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
+ const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+ await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, bob);
+ const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- test.concurrent('ミュートã—ã¦ã„るユーザーã«é–¢é€£ã™ã‚‹æŠ•稿ãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ test('[withFiles: true] リスインã—ã¦ã„るユーザーã®ãƒ•ァイル付ãノートã®ã¿å«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- await api('mute/create', { userId: carol.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
+ const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+ await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+ const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png');
+ const bobNote1 = await post(bob, { text: 'hi' });
+ const bobNote2 = await post(bob, { fileIds: [file.id] });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('users/notes', { userId: bob.id }, alice);
+ const res = await api('notes/user-list-timeline', { listId: list.id, withFiles: true }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
+ }, 1000 * 10);
- test.concurrent('ミュートã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆã®ã€é–¢ä¿‚ã®ãªã„ユーザã«ã‚ˆã‚‹å¼•用ノートã®ã€ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
+ test('リスインã—ã¦ã„るユーザーã®è‡ªèº«å®›ã¦ã® visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- await api('mute/create', { userId: carol.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const daveNote = await post(dave, { text: 'quote hi', renoteId: carolNote.id });
- const bobNote = await post(bob, { renoteId: daveNote.id });
+ const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+ await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('users/notes', { userId: bob.id, limit: 100 }, alice);
+ const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
+ });
- test.concurrent('ミュートã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆã®ã€é–¢ä¿‚ã®ãªã„ユーザã«ã‚ˆã‚‹ãƒªãƒ—ライã®ã€ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
+ test('リスインã—ã¦ã„るユーザーã®è‡ªèº«å®›ã¦ã§ã¯ãªã„ visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- await api('following/create', { userId: bob.id }, alice);
- await api('mute/create', { userId: carol.id }, alice);
- await setTimeout(1000);
- const carolNote = await post(carol, { text: 'hi' });
- const daveNote = await post(dave, { text: 'quote hi', replyId: carolNote.id });
- const bobNote = await post(bob, { renoteId: daveNote.id });
+ const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+ await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+ await api('users/lists/push', { listId: list.id, userId: carol.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('users/notes', { userId: bob.id, limit: 100 }, alice);
+ const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
- test.concurrent('ミュートã—ã¦ã„ã¦ã‚‚ userId ã«æŒ‡å®šã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŠ•稿ãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ describe('å‡çµ', () => {
+ let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse;
+ let aliceNote: Note, bobNote: Note, carolNote: Note;
+ let list: UserList;
- await api('mute/create', { userId: bob.id }, alice);
- await setTimeout(1000);
- const bobNote1 = await post(bob, { text: 'hi' });
- const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
- const bobNote3 = await post(bob, { text: 'hi', renoteId: bobNote1.id });
- const bobNote4 = await post(bob, { renoteId: bobNote2.id });
- const bobNote5 = await post(bob, { renoteId: bobNote3.id });
+ beforeAll(async () => {
+ [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
- await waitForPushToTl();
+ list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
- const res = await api('users/notes', { userId: bob.id }, alice);
+ await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+ await api('users/lists/push', { listId: list.id, userId: carol.id }, alice);
+ aliceNote = await post(alice, { text: 'hi' });
+ bobNote = await post(bob, { text: 'yo' });
+ carolNote = await post(carol, { text: 'kon\'nichiwa' });
- assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
- assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
- assert.strictEqual(res.body.some(note => note.id === bobNote3.id), true);
- assert.strictEqual(res.body.some(note => note.id === bobNote4.id), true);
- assert.strictEqual(res.body.some(note => note.id === bobNote5.id), true);
- });
+ await waitForPushToTl();
+
+ await api('admin/suspend-user', { userId: carol.id }, root);
+ await setTimeout(100);
+ });
- test.concurrent('自身㮠visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
- const [alice] = await Promise.all([signup()]);
+ test('å‡çµå¾Œã«å‡çµã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆã¯è¦‹ãˆãªããªã‚‹', async () => {
+ const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- const aliceNote = await post(alice, { text: 'hi', visibility: 'specified' });
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
+ });
- await waitForPushToTl();
+ test('å‡çµè§£é™¤å¾Œã«å‡çµã•れã¦ã„ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆã¯è¦‹ãˆã‚‹ã‚ˆã†ã«ãªã‚‹', async () => {
+ await api('admin/unsuspend-user', { userId: carol.id }, root);
+ await setTimeout(100);
- const res = await api('users/notes', { userId: alice.id, withReplies: true }, alice);
+ const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
+ });
+ });
});
- test.concurrent('visibleUserIds ã«æŒ‡å®šã•れã¦ãªã„ visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
- const [alice, bob] = await Promise.all([signup(), signup()]);
+ describe('User TL', () => {
+ test('ノートãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const bobNote = await post(bob, { text: 'hi', visibility: 'specified' });
+ const bobNote = await post(bob, { text: 'hi' });
- await waitForPushToTl();
+ await waitForPushToTl();
- const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
+ const res = await api('users/notes', { userId: bob.id }, alice);
- assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
- });
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
- /** @see https://github.com/misskey-dev/misskey/issues/14000 */
- test.concurrent('FTT: sinceId ã«ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã‚ˆã‚Šå¤ã„ノートを指定ã—ã¦ã‚‚ã€sinceId ã«ã‚ˆã‚‹çµžã‚Šè¾¼ã¿ãŒæ­£ã—ã動作ã™ã‚‹', async () => {
- const alice = await signup();
- const noteSince = await post(alice, { text: 'Note where id will be `sinceId`.' });
- const note1 = await post(alice, { text: '1' });
- const note2 = await post(alice, { text: '2' });
- await redisForTimelines.del('list:userTimeline:' + alice.id);
- const note3 = await post(alice, { text: '3' });
+ test('フォローã—ã¦ã„ãªã„ユーザー㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
- const res = await api('users/notes', { userId: alice.id, sinceId: noteSince.id });
- assert.deepStrictEqual(res.body, [note1, note2, note3]);
- });
+ const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
+
+ await waitForPushToTl();
+
+ const res = await api('users/notes', { userId: bob.id }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
+
+ test('フォローã—ã¦ã„るユーザー㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
+
+ await api('following/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
+
+ await waitForPushToTl();
+
+ const res = await api('users/notes', { userId: bob.id }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
+ });
+
+ test('自身㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice] = await Promise.all([signup()]);
+
+ const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
+
+ await waitForPushToTl();
+
+ const res = await api('users/notes', { userId: alice.id }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
+ });
+
+ test('ãƒãƒ£ãƒ³ãƒãƒ«æŠ•稿ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
+
+ const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
+ const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
+
+ await waitForPushToTl();
+
+ const res = await api('users/notes', { userId: bob.id }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
+
+ test('[withReplies: false] 他人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
+ /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
+
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote1 = await post(bob, { text: 'hi' });
+ const bobNote2 = await post(bob, { text: 'hi', replyId: carolNote.id });
+
+ await waitForPushToTl();
+
+ const res = await api('users/notes', { userId: bob.id }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), false);
+ });
+
+ test('[withReplies: true] 他人ã¸ã®è¿”ä¿¡ãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote1 = await post(bob, { text: 'hi' });
+ const bobNote2 = await post(bob, { text: 'hi', replyId: carolNote.id });
+
+ await waitForPushToTl();
+
+ const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
+ });
+
+ test('[withReplies: true] 他人ã¸ã® visibility: specified ãªè¿”ä¿¡ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote1 = await post(bob, { text: 'hi' });
+ const bobNote2 = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified' });
+
+ await waitForPushToTl();
+
+ const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), false);
+ });
+
+ test('[withFiles: true] ファイル付ãノートã®ã¿å«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
+
+ const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png');
+ const bobNote1 = await post(bob, { text: 'hi' });
+ const bobNote2 = await post(bob, { fileIds: [file.id] });
+
+ await waitForPushToTl();
+
+ const res = await api('users/notes', { userId: bob.id, withFiles: true }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
+ }, 1000 * 10);
+
+ test('[withChannelNotes: true] ãƒãƒ£ãƒ³ãƒãƒ«æŠ•稿ãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
+
+ const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
+ const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
+
+ await waitForPushToTl();
- test.concurrent('FTT: sinceId ã«ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã‚ˆã‚Šå¤ã„ノートを指定ã—ã¦ã‚‚ã€sinceId 㨠untilId ã«ã‚ˆã‚‹çµžã‚Šè¾¼ã¿ãŒæ­£ã—ã動作ã™ã‚‹', async () => {
- const alice = await signup();
- const noteSince = await post(alice, { text: 'Note where id will be `sinceId`.' });
- const note1 = await post(alice, { text: '1' });
- const note2 = await post(alice, { text: '2' });
- await redisForTimelines.del('list:userTimeline:' + alice.id);
- const note3 = await post(alice, { text: '3' });
- const noteUntil = await post(alice, { text: 'Note where id will be `untilId`.' });
- await post(alice, { text: '4' });
+ const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice);
- const res = await api('users/notes', { userId: alice.id, sinceId: noteSince.id, untilId: noteUntil.id });
- assert.deepStrictEqual(res.body, [note3, note2, note1]);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
+
+ test('[withChannelNotes: true] 他人ãŒå–å¾—ã—ãŸå ´åˆã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ãƒãƒ£ãƒ³ãƒãƒ«æŠ•稿ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
+
+ const channel = await api('channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body);
+ const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
+
+ await waitForPushToTl();
+
+ const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
+
+ test('[withChannelNotes: true] 自分ãŒå–å¾—ã—ãŸå ´åˆã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ãƒãƒ£ãƒ³ãƒãƒ«æŠ•稿ãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [bob] = await Promise.all([signup()]);
+
+ const channel = await api('channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body);
+ const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
+
+ await waitForPushToTl();
+
+ const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, bob);
+
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
+
+ test('ミュートã—ã¦ã„るユーザーã«é–¢é€£ã™ã‚‹æŠ•稿ãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+
+ await api('mute/create', { userId: carol.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
+
+ await waitForPushToTl();
+
+ const res = await api('users/notes', { userId: bob.id }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
+
+ test('ミュートã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆã®ã€é–¢ä¿‚ã®ãªã„ユーザã«ã‚ˆã‚‹å¼•用ノートã®ã€ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
+
+ await api('mute/create', { userId: carol.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const daveNote = await post(dave, { text: 'quote hi', renoteId: carolNote.id });
+ const bobNote = await post(bob, { renoteId: daveNote.id });
+
+ await waitForPushToTl();
+
+ const res = await api('users/notes', { userId: bob.id, limit: 100 }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
+
+ test('ミュートã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆã®ã€é–¢ä¿‚ã®ãªã„ユーザã«ã‚ˆã‚‹ãƒªãƒ—ライã®ã€ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
+
+ await api('following/create', { userId: bob.id }, alice);
+ await api('mute/create', { userId: carol.id }, alice);
+ await setTimeout(250);
+ const carolNote = await post(carol, { text: 'hi' });
+ const daveNote = await post(dave, { text: 'quote hi', replyId: carolNote.id });
+ const bobNote = await post(bob, { renoteId: daveNote.id });
+
+ await waitForPushToTl();
+
+ const res = await api('users/notes', { userId: bob.id, limit: 100 }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
+
+ test('ミュートã—ã¦ã„ã¦ã‚‚ userId ã«æŒ‡å®šã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŠ•稿ãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
+
+ await api('mute/create', { userId: bob.id }, alice);
+ await setTimeout(250);
+ const bobNote1 = await post(bob, { text: 'hi' });
+ const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
+ const bobNote3 = await post(bob, { text: 'hi', renoteId: bobNote1.id });
+ const bobNote4 = await post(bob, { renoteId: bobNote2.id });
+ const bobNote5 = await post(bob, { renoteId: bobNote3.id });
+
+ await waitForPushToTl();
+
+ const res = await api('users/notes', { userId: bob.id }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote3.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote4.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote5.id), true);
+ });
+
+ test('自身㮠visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => {
+ const [alice] = await Promise.all([signup()]);
+
+ const aliceNote = await post(alice, { text: 'hi', visibility: 'specified' });
+
+ await waitForPushToTl();
+
+ const res = await api('users/notes', { userId: alice.id, withReplies: true }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ });
+
+ test('visibleUserIds ã«æŒ‡å®šã•れã¦ãªã„ visibility: specified ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
+
+ const bobNote = await post(bob, { text: 'hi', visibility: 'specified' });
+
+ await waitForPushToTl();
+
+ const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
+
+ /** @see https://github.com/misskey-dev/misskey/issues/14000 */
+ test('FTT: sinceId ã«ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã‚ˆã‚Šå¤ã„ノートを指定ã—ã¦ã‚‚ã€sinceId ã«ã‚ˆã‚‹çµžã‚Šè¾¼ã¿ãŒæ­£ã—ã動作ã™ã‚‹', async () => {
+ const alice = await signup();
+ const noteSince = await post(alice, { text: 'Note where id will be `sinceId`.' });
+ const note1 = await post(alice, { text: '1' });
+ const note2 = await post(alice, { text: '2' });
+ await redisForTimelines.del('list:userTimeline:' + alice.id);
+ const note3 = await post(alice, { text: '3' });
+
+ const res = await api('users/notes', { userId: alice.id, sinceId: noteSince.id });
+ assert.deepStrictEqual(res.body, [note1, note2, note3]);
+ });
+
+ test('FTT: sinceId ã«ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã‚ˆã‚Šå¤ã„ノートを指定ã—ã¦ã‚‚ã€sinceId 㨠untilId ã«ã‚ˆã‚‹çµžã‚Šè¾¼ã¿ãŒæ­£ã—ã動作ã™ã‚‹', async () => {
+ const alice = await signup();
+ const noteSince = await post(alice, { text: 'Note where id will be `sinceId`.' });
+ const note1 = await post(alice, { text: '1' });
+ const note2 = await post(alice, { text: '2' });
+ await redisForTimelines.del('list:userTimeline:' + alice.id);
+ const note3 = await post(alice, { text: '3' });
+ const noteUntil = await post(alice, { text: 'Note where id will be `untilId`.' });
+ await post(alice, { text: '4' });
+
+ const res = await api('users/notes', { userId: alice.id, sinceId: noteSince.id, untilId: noteUntil.id });
+ assert.deepStrictEqual(res.body, [note3, note2, note1]);
+ });
});
- });
- // TODO: リノートミュート済ã¿ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ†ã‚¹ãƒˆ
- // TODO: ページãƒãƒ¼ã‚·ãƒ§ãƒ³ã®ãƒ†ã‚¹ãƒˆ
+ // TODO: リノートミュート済ã¿ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ†ã‚¹ãƒˆ
+ // TODO: ページãƒãƒ¼ã‚·ãƒ§ãƒ³ã®ãƒ†ã‚¹ãƒˆ
+ });
});
diff --git a/packages/backend/test/e2e/well-known.ts b/packages/backend/test/e2e/well-known.ts
index bdb298dfe4..538a990a4e 100644
--- a/packages/backend/test/e2e/well-known.ts
+++ b/packages/backend/test/e2e/well-known.ts
@@ -6,7 +6,7 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
-import { host, origin, relativeFetch, signup } from '../utils.js';
+import { api, host, origin, relativeFetch, signup } from '../utils.js';
import type * as misskey from 'misskey-js';
describe('.well-known', () => {
@@ -14,6 +14,7 @@ describe('.well-known', () => {
beforeAll(async () => {
alice = await signup({ username: 'alice' });
+ await api('admin/update-meta', { federation: 'all' }, alice as misskey.entities.SignupResponse);
}, 1000 * 60 * 2);
test('nodeinfo', async () => {
diff --git a/packages/backend/test/unit/NoteCreateService.ts b/packages/backend/test/unit/NoteCreateService.ts
index f2d4c8ffbb..23f409420e 100644
--- a/packages/backend/test/unit/NoteCreateService.ts
+++ b/packages/backend/test/unit/NoteCreateService.ts
@@ -40,6 +40,7 @@ describe('NoteCreateService', () => {
renoteCount: 0,
repliesCount: 0,
clippedCount: 0,
+ pageCount: 0,
reactions: {},
visibility: 'public',
uri: null,
diff --git a/packages/backend/test/unit/misc/is-renote.ts b/packages/backend/test/unit/misc/is-renote.ts
index 0b713e8bf6..74d17abcb6 100644
--- a/packages/backend/test/unit/misc/is-renote.ts
+++ b/packages/backend/test/unit/misc/is-renote.ts
@@ -23,6 +23,7 @@ const base: MiNote = {
renoteCount: 0,
repliesCount: 0,
clippedCount: 0,
+ pageCount: 0,
reactions: {},
visibility: 'public',
uri: null,
diff --git a/packages/backend/test/unit/queue/processors/CleanRemoteNotesProcessorService.ts b/packages/backend/test/unit/queue/processors/CleanRemoteNotesProcessorService.ts
new file mode 100644
index 0000000000..631e160afc
--- /dev/null
+++ b/packages/backend/test/unit/queue/processors/CleanRemoteNotesProcessorService.ts
@@ -0,0 +1,652 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { jest } from '@jest/globals';
+import { Test, TestingModule } from '@nestjs/testing';
+import ms from 'ms';
+import {
+ type MiNote,
+ type MiUser,
+ type NotesRepository,
+ type NoteFavoritesRepository,
+ type UserNotePiningsRepository,
+ type UsersRepository,
+ type UserProfilesRepository,
+ MiMeta,
+} from '@/models/_.js';
+import { CleanRemoteNotesProcessorService } from '@/queue/processors/CleanRemoteNotesProcessorService.js';
+import { DI } from '@/di-symbols.js';
+import { IdService } from '@/core/IdService.js';
+import { QueueLoggerService } from '@/queue/QueueLoggerService.js';
+import { GlobalModule } from '@/GlobalModule.js';
+import { secureRndstr } from '@/misc/secure-rndstr.js';
+
+describe('CleanRemoteNotesProcessorService', () => {
+ let app: TestingModule;
+ let service: CleanRemoteNotesProcessorService;
+ let idService: IdService;
+ let notesRepository: NotesRepository;
+ let noteFavoritesRepository: NoteFavoritesRepository;
+ let userNotePiningsRepository: UserNotePiningsRepository;
+ let usersRepository: UsersRepository;
+ let userProfilesRepository: UserProfilesRepository;
+
+ // Local user
+ let alice: MiUser;
+ // Remote user 1
+ let bob: MiUser;
+ // Remote user 2
+ let carol: MiUser;
+
+ const meta = new MiMeta();
+
+ // Mock job object
+ const createMockJob = () => ({
+ log: jest.fn(),
+ updateProgress: jest.fn(),
+ });
+
+ async function createUser(data: Partial<MiUser> = {}) {
+ const id = idService.gen();
+ const un = data.username || secureRndstr(16);
+ const user = await usersRepository
+ .insert({
+ id,
+ username: un,
+ usernameLower: un.toLowerCase(),
+ ...data,
+ })
+ .then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
+
+ await userProfilesRepository.save({
+ userId: id,
+ });
+
+ return user;
+ }
+
+ async function createNote(data: Partial<MiNote>, user: MiUser, time?: number): Promise<MiNote> {
+ const id = idService.gen(time);
+ const note = await notesRepository
+ .insert({
+ id: id,
+ text: `note_${id}`,
+ userId: user.id,
+ userHost: user.host,
+ visibility: 'public',
+ ...data,
+ })
+ .then(x => notesRepository.findOneByOrFail(x.identifiers[0]));
+ return note;
+ }
+
+ beforeAll(async () => {
+ app = await Test
+ .createTestingModule({
+ imports: [
+ GlobalModule,
+ ],
+ providers: [
+ CleanRemoteNotesProcessorService,
+ IdService,
+ {
+ provide: QueueLoggerService,
+ useFactory: () => ({
+ logger: {
+ createSubLogger: () => ({
+ info: jest.fn(),
+ warn: jest.fn(),
+ succ: jest.fn(),
+ }),
+ },
+ }),
+ },
+ ],
+ })
+ .overrideProvider(DI.meta).useFactory({ factory: () => meta })
+ .compile();
+
+ service = app.get(CleanRemoteNotesProcessorService);
+ idService = app.get(IdService);
+ notesRepository = app.get(DI.notesRepository);
+ noteFavoritesRepository = app.get(DI.noteFavoritesRepository);
+ userNotePiningsRepository = app.get(DI.userNotePiningsRepository);
+ usersRepository = app.get(DI.usersRepository);
+ userProfilesRepository = app.get(DI.userProfilesRepository);
+
+ alice = await createUser({ username: 'alice', host: null });
+ bob = await createUser({ username: 'bob', host: 'remote1.example.com' });
+ carol = await createUser({ username: 'carol', host: 'remote2.example.com' });
+
+ app.enableShutdownHooks();
+ });
+
+ beforeEach(() => {
+ // Reset mocks
+ jest.clearAllMocks();
+
+ // Set default meta values
+ meta.enableRemoteNotesCleaning = true;
+ meta.remoteNotesCleaningMaxProcessingDurationInMinutes = 0.3;
+ meta.remoteNotesCleaningExpiryDaysForEachNotes = 30;
+ }, 60 * 1000);
+
+ afterEach(async () => {
+ // Clean up test data
+ await Promise.all([
+ notesRepository.createQueryBuilder().delete().execute(),
+ userNotePiningsRepository.createQueryBuilder().delete().execute(),
+ noteFavoritesRepository.createQueryBuilder().delete().execute(),
+ ]);
+ }, 60 * 1000);
+
+ afterAll(async () => {
+ await app.close();
+ });
+
+ describe('basic', () => {
+ test('should skip cleaning when enableRemoteNotesCleaning is false', async () => {
+ meta.enableRemoteNotesCleaning = false;
+ const job = createMockJob();
+
+ const result = await service.process(job as any);
+
+ expect(result).toEqual({
+ deletedCount: 0,
+ oldest: null,
+ newest: null,
+ skipped: true,
+ transientErrors: 0,
+ });
+ });
+
+ test('should return success result when enableRemoteNotesCleaning is true and no notes to clean', async () => {
+ const job = createMockJob();
+
+ await createNote({}, alice);
+ const result = await service.process(job as any);
+
+ expect(result).toEqual({
+ deletedCount: 0,
+ oldest: null,
+ newest: null,
+ skipped: false,
+ transientErrors: 0,
+ });
+ }, 3000);
+
+ test('should clean remote notes and return stats', async () => {
+ // Remote notes
+ const remoteNotes = await Promise.all([
+ createNote({}, bob),
+ createNote({}, carol),
+ createNote({}, bob, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000),
+ createNote({}, carol, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 2000), // Note older than expiry
+ ]);
+
+ // Local notes
+ const localNotes = await Promise.all([
+ createNote({}, alice),
+ createNote({}, alice, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000),
+ ]);
+
+ const job = createMockJob();
+
+ const result = await service.process(job as any);
+
+ expect(result).toEqual({
+ deletedCount: 2,
+ oldest: expect.any(Number),
+ newest: expect.any(Number),
+ skipped: false,
+ transientErrors: 0,
+ });
+
+ // Check side-by-side from all notes
+ const remainingNotes = await notesRepository.find();
+ expect(remainingNotes.length).toBe(4);
+ expect(remainingNotes.some(n => n.id === remoteNotes[0].id)).toBe(true);
+ expect(remainingNotes.some(n => n.id === remoteNotes[1].id)).toBe(true);
+ expect(remainingNotes.some(n => n.id === remoteNotes[2].id)).toBe(false);
+ expect(remainingNotes.some(n => n.id === remoteNotes[3].id)).toBe(false);
+ expect(remainingNotes.some(n => n.id === localNotes[0].id)).toBe(true);
+ expect(remainingNotes.some(n => n.id === localNotes[1].id)).toBe(true);
+ });
+ });
+
+ describe('advanced', () => {
+ // ãŠæ°—ã«å…¥ã‚Š
+ test('should not delete note that is favorited by any user', async () => {
+ const job = createMockJob();
+
+ // Create old remote note that should be deleted
+ const olderRemoteNote = await createNote({}, bob, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000);
+
+ // Favorite the note
+ await noteFavoritesRepository.save({
+ id: idService.gen(),
+ userId: alice.id,
+ noteId: olderRemoteNote.id,
+ });
+
+ const result = await service.process(job as any);
+
+ expect(result.deletedCount).toBe(0);
+ expect(result.skipped).toBe(false);
+
+ const remainingNote = await notesRepository.findOneBy({ id: olderRemoteNote.id });
+ expect(remainingNote).not.toBeNull();
+ });
+
+ // ピン留ã‚
+ test('should not delete note that is pinned by the user', async () => {
+ const job = createMockJob();
+
+ // Create old remote note that should be deleted
+ const olderRemoteNote = await createNote({}, bob, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000);
+
+ // Pin the note by the user who created it
+ await userNotePiningsRepository.save({
+ id: idService.gen(),
+ userId: bob.id, // Same user as the note creator
+ noteId: olderRemoteNote.id,
+ });
+
+ const result = await service.process(job as any);
+
+ expect(result.deletedCount).toBe(0);
+ expect(result.skipped).toBe(false);
+
+ const remainingNote = await notesRepository.findOneBy({ id: olderRemoteNote.id });
+ expect(remainingNote).not.toBeNull();
+ });
+
+ // クリップ
+ test('should not delete note that is clipped', async () => {
+ const job = createMockJob();
+
+ // Create old remote note that is clipped
+ const clippedNote = await createNote({
+ clippedCount: 1, // Clipped
+ }, bob, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000);
+
+ const result = await service.process(job as any);
+
+ expect(result.deletedCount).toBe(0);
+ expect(result.skipped).toBe(false);
+
+ const remainingNote = await notesRepository.findOneBy({ id: clippedNote.id });
+ expect(remainingNote).not.toBeNull();
+ });
+
+ // ページ
+ test('should not delete note that is embedded in a page', async () => {
+ const job = createMockJob();
+
+ // Create old remote note that is embedded in a page
+ const clippedNote = await createNote({
+ pageCount: 1, // Embedded in a page
+ }, bob, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000);
+
+ const result = await service.process(job as any);
+
+ expect(result.deletedCount).toBe(0);
+ expect(result.skipped).toBe(false);
+
+ const remainingNote = await notesRepository.findOneBy({ id: clippedNote.id });
+ expect(remainingNote).not.toBeNull();
+ });
+
+ // å¤ã„reply, renoteãŒå«ã¾ã‚Œã¦ã„ã‚‹æ™‚ã®æŒ™å‹•
+ test('should handle reply/renote relationships correctly', async () => {
+ const job = createMockJob();
+
+ // Create old remote notes with reply/renote relationships
+ const originalNote = await createNote({}, bob, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000);
+ const replyNote = await createNote({
+ replyId: originalNote.id,
+ }, carol, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 2000);
+ const renoteNote = await createNote({
+ renoteId: originalNote.id,
+ }, bob, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 3000);
+
+ const result = await service.process(job as any);
+
+ // Should delete all three notes as they are all old and remote
+ expect(result.deletedCount).toBe(3);
+ expect(result.skipped).toBe(false);
+
+ const remainingNotes = await notesRepository.find();
+ expect(remainingNotes.some(n => n.id === originalNote.id)).toBe(false);
+ expect(remainingNotes.some(n => n.id === replyNote.id)).toBe(false);
+ expect(remainingNotes.some(n => n.id === renoteNote.id)).toBe(false);
+ });
+
+ // å¤ã„ãƒªãƒ¢ãƒ¼ãƒˆãƒŽãƒ¼ãƒˆã«æ–°ã—ã„リプライãŒã‚る時ã€ã©ã¡ã‚‰ã‚‚削除ã•れãªã„
+ test('should not delete both old remote note with new reply', async () => {
+ const job = createMockJob();
+
+ // Create old remote note that should be deleted
+ const oldNote = await createNote({}, bob, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000);
+
+ // Create a reply note that is newer than the expiry period
+ const recentReplyNote = await createNote({
+ replyId: oldNote.id,
+ }, carol, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) + 1000);
+
+ const result = await service.process(job as any);
+
+ expect(result.deletedCount).toBe(0); // Only the old note should be deleted
+ expect(result.skipped).toBe(false);
+
+ const remainingNotes = await notesRepository.find();
+ expect(remainingNotes.some(n => n.id === oldNote.id)).toBe(true);
+ expect(remainingNotes.some(n => n.id === recentReplyNote.id)).toBe(true); // Recent reply note should remain
+ });
+
+ // å¤ã„ãƒªãƒ¢ãƒ¼ãƒˆãƒŽãƒ¼ãƒˆã«æ–°ã—ã„リプライã¨å¤ã„リプライãŒã‚る時ã€å…¨ã¦æ®‹ã‚‹
+ test('should not delete old remote note with new reply and old reply', async () => {
+ const job = createMockJob();
+
+ // Create old remote note that should be deleted
+ const oldNote = await createNote({}, bob, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000);
+
+ // Create a reply note that is newer than the expiry period
+ const recentReplyNote = await createNote({
+ replyId: oldNote.id,
+ }, carol, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) + 1000);
+
+ // Create an old reply note that should be deleted
+ const oldReplyNote = await createNote({
+ replyId: oldNote.id,
+ }, carol, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 2000);
+
+ const result = await service.process(job as any);
+
+ expect(result.deletedCount).toBe(0);
+ expect(result.skipped).toBe(false);
+
+ const remainingNotes = await notesRepository.find();
+ expect(remainingNotes.some(n => n.id === oldNote.id)).toBe(true);
+ expect(remainingNotes.some(n => n.id === recentReplyNote.id)).toBe(true); // Recent reply note should remain
+ expect(remainingNotes.some(n => n.id === oldReplyNote.id)).toBe(true); // Old reply note should be deleted
+ });
+
+ // リプライãŒãŠæ°—ã«å…¥ã‚Šã•れã¦ã„ã‚‹ã¨ãã€ã©ã¡ã‚‰ã‚‚削除ã•れãªã„
+ test('should not delete reply note that is favorited', async () => {
+ const job = createMockJob();
+
+ // Create old remote note that should be deleted
+ const olderRemoteNote = await createNote({}, bob, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000);
+
+ // Create a reply note that is newer than the expiry period
+ const replyNote = await createNote({
+ replyId: olderRemoteNote.id,
+ }, carol, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 2000);
+
+ // Favorite the reply note
+ await noteFavoritesRepository.save({
+ id: idService.gen(),
+ userId: alice.id,
+ noteId: replyNote.id,
+ });
+
+ const result = await service.process(job as any);
+
+ expect(result.deletedCount).toBe(0); // Only the old note should be deleted
+ expect(result.skipped).toBe(false);
+
+ const remainingNotes = await notesRepository.find();
+ expect(remainingNotes.some(n => n.id === olderRemoteNote.id)).toBe(true);
+ expect(remainingNotes.some(n => n.id === replyNote.id)).toBe(true); // Recent reply note should remain
+ });
+
+ // リプライãŒãƒ”ン留ã‚ã•れã¦ã„ã‚‹ã¨ãã€ã©ã¡ã‚‰ã‚‚削除ã•れãªã„
+ test('should not delete reply note that is pinned', async () => {
+ const job = createMockJob();
+
+ // Create old remote note that should be deleted
+ const olderRemoteNote = await createNote({}, bob, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000);
+
+ // Create a reply note that is newer than the expiry period
+ const replyNote = await createNote({
+ replyId: olderRemoteNote.id,
+ }, carol, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 2000);
+
+ // Pin the reply note
+ await userNotePiningsRepository.save({
+ id: idService.gen(),
+ userId: carol.id,
+ noteId: replyNote.id,
+ });
+
+ const result = await service.process(job as any);
+
+ expect(result.deletedCount).toBe(0); // Only the old note should be deleted
+ expect(result.skipped).toBe(false);
+
+ const remainingNotes = await notesRepository.find();
+ expect(remainingNotes.some(n => n.id === olderRemoteNote.id)).toBe(true);
+ expect(remainingNotes.some(n => n.id === replyNote.id)).toBe(true); // Reply note should remain
+ });
+
+ // リプライãŒã‚¯ãƒªãƒƒãƒ—ã•れã¦ã„ã‚‹ã¨ãã€ã©ã¡ã‚‰ã‚‚削除ã•れãªã„
+ test('should not delete reply note that is clipped', async () => {
+ const job = createMockJob();
+
+ // Create old remote note that should be deleted
+ const olderRemoteNote = await createNote({}, bob, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000);
+
+ // Create a reply note that is old but clipped
+ const replyNote = await createNote({
+ replyId: olderRemoteNote.id,
+ clippedCount: 1, // Clipped
+ }, carol, Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 2000);
+
+ const result = await service.process(job as any);
+
+ expect(result.deletedCount).toBe(0); // Both notes should be kept because reply is clipped
+ expect(result.skipped).toBe(false);
+
+ const remainingNotes = await notesRepository.find();
+ expect(remainingNotes.some(n => n.id === olderRemoteNote.id)).toBe(true);
+ expect(remainingNotes.some(n => n.id === replyNote.id)).toBe(true);
+ });
+
+ test('should handle mixed scenarios with multiple conditions', async () => {
+ const job = createMockJob();
+
+ // Create various types of notes
+ const oldTime = Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000;
+
+ // Should be deleted: old remote note with no special conditions
+ const deletableNote = await createNote({}, bob, oldTime);
+
+ // Should NOT be deleted: old remote note but favorited
+ const favoritedNote = await createNote({}, carol, oldTime);
+ await noteFavoritesRepository.save({
+ id: idService.gen(),
+ userId: alice.id,
+ noteId: favoritedNote.id,
+ });
+
+ // Should NOT be deleted: old remote note but pinned
+ const pinnedNote = await createNote({}, bob, oldTime);
+ await userNotePiningsRepository.save({
+ id: idService.gen(),
+ userId: bob.id,
+ noteId: pinnedNote.id,
+ });
+
+ // Should NOT be deleted: old remote note but clipped
+ const clippedNote = await createNote({
+ clippedCount: 2,
+ }, carol, oldTime);
+
+ // Should NOT be deleted: old local note
+ const localNote = await createNote({}, alice, oldTime);
+
+ // Should NOT be deleted: new remote note
+ const newerRemoteNote = await createNote({}, bob);
+
+ const result = await service.process(job as any);
+
+ expect(result.deletedCount).toBe(1); // Only deletableNote should be deleted
+ expect(result.skipped).toBe(false);
+
+ const remainingNotes = await notesRepository.find();
+ expect(remainingNotes.length).toBe(5);
+ expect(remainingNotes.some(n => n.id === deletableNote.id)).toBe(false); // Deleted
+ expect(remainingNotes.some(n => n.id === favoritedNote.id)).toBe(true); // Kept
+ expect(remainingNotes.some(n => n.id === pinnedNote.id)).toBe(true); // Kept
+ expect(remainingNotes.some(n => n.id === clippedNote.id)).toBe(true); // Kept
+ expect(remainingNotes.some(n => n.id === localNote.id)).toBe(true); // Kept
+ expect(remainingNotes.some(n => n.id === newerRemoteNote.id)).toBe(true); // Kept
+ });
+
+ // 大é‡ã®ãƒŽãƒ¼ãƒˆ
+ test('should handle large number of notes correctly', async () => {
+ const AMOUNT = 130;
+ const job = createMockJob();
+
+ const oldTime = Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000;
+ const noteIds = [];
+ for (let i = 0; i < AMOUNT; i++) {
+ const note = await createNote({}, bob, oldTime - i);
+ noteIds.push(note.id);
+ }
+
+ const result = await service.process(job as any);
+
+ // Should delete all notes, but may require multiple batches
+ expect(result.deletedCount).toBe(AMOUNT);
+ expect(result.skipped).toBe(false);
+
+ const remainingNotes = await notesRepository.find();
+ expect(remainingNotes.length).toBe(0);
+ });
+
+ // 大é‡ã®ãƒŽãƒ¼ãƒˆ + リプライ or リノート
+ test('should handle large number of notes with replies correctly', async () => {
+ const AMOUNT = 130;
+ const job = createMockJob();
+
+ const oldTime = Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000;
+ const noteIds = [];
+ for (let i = 0; i < AMOUNT; i++) {
+ const note = await createNote({}, bob, oldTime - i - AMOUNT);
+ noteIds.push(note.id);
+ if (i % 2 === 0) {
+ // Create a reply for every second note
+ await createNote({ replyId: note.id }, carol, oldTime - i);
+ } else {
+ // Create a renote for every second note
+ await createNote({ renoteId: note.id }, bob, oldTime - i);
+ }
+ }
+
+ const result = await service.process(job as any);
+ // Should delete all notes, but may require multiple batches
+ expect(result.deletedCount).toBe(AMOUNT * 2);
+ expect(result.skipped).toBe(false);
+ });
+
+ // 大é‡ã®å¤ã„ノート + æ–°ã—ã„リプライ or リノート
+ test('should handle large number of old notes with new replies correctly', async () => {
+ const AMOUNT = 130;
+ const job = createMockJob();
+
+ const oldTime = Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000;
+ const newTime = Date.now();
+ const noteIds = [];
+ for (let i = 0; i < AMOUNT; i++) {
+ const note = await createNote({}, bob, oldTime - i);
+ noteIds.push(note.id);
+ if (i % 2 === 0) {
+ // Create a reply for every second note
+ await createNote({ replyId: note.id }, carol, newTime + i);
+ } else {
+ // Create a renote for every second note
+ await createNote({ renoteId: note.id }, bob, newTime + i);
+ }
+ }
+ const result = await service.process(job as any);
+
+ expect(result.deletedCount).toBe(0);
+ expect(result.skipped).toBe(false);
+ });
+
+ // 大é‡ã®æ®‹ã™å¯¾è±¡(clippedCount: 1)ã¨å¤§é‡ã®å‰Šé™¤å¯¾è±¡
+ test('should handle large number of notes, mixed conditions with clippedCount', async () => {
+ const AMOUNT_BASE = 70;
+ const job = createMockJob();
+
+ const oldTime = Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000;
+ const noteIds = [];
+ for (let i = 0; i < AMOUNT_BASE; i++) {
+ const note = await createNote({ clippedCount: 1 }, bob, oldTime - i - AMOUNT_BASE);
+ noteIds.push(note.id);
+ }
+ for (let i = 0; i < AMOUNT_BASE; i++) {
+ const note = await createNote({}, carol, oldTime - i);
+ noteIds.push(note.id);
+ }
+
+ const result = await service.process(job as any);
+
+ expect(result.deletedCount).toBe(AMOUNT_BASE); // Assuming half are deletable
+ expect(result.skipped).toBe(false);
+ });
+
+ // 大é‡ã®æ®‹ã™å¯¾è±¡(リプライ)ã¨å¤§é‡ã®å‰Šé™¤å¯¾è±¡
+ test('should handle large number of notes, mixed conditions with replies', async () => {
+ const AMOUNT_BASE = 70;
+ const job = createMockJob();
+ const oldTime = Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 1000;
+ const newTime = Date.now();
+ for (let i = 0; i < AMOUNT_BASE; i++) {
+ // should remain
+ const note = await createNote({}, carol, oldTime - AMOUNT_BASE - i);
+ // should remain
+ await createNote({ replyId: note.id }, bob, newTime + i);
+ }
+
+ const noteIdsExpectedToBeDeleted = [];
+ for (let i = 0; i < AMOUNT_BASE; i++) {
+ // should be deleted
+ const note = await createNote({}, bob, oldTime - i);
+ noteIdsExpectedToBeDeleted.push(note.id);
+ }
+
+ const result = await service.process(job as any);
+ expect(result.deletedCount).toBe(AMOUNT_BASE); // Assuming all replies are deletable
+ expect(result.skipped).toBe(false);
+
+ const remainingNotes = await notesRepository.find();
+ expect(remainingNotes.length).toBe(AMOUNT_BASE * 2); // Only replies should remain
+ noteIdsExpectedToBeDeleted.forEach(id => {
+ expect(remainingNotes.some(n => n.id === id)).toBe(false); // All original notes should be deleted
+ });
+ });
+
+ test('should update cursor correctly during batch processing', async () => {
+ const job = createMockJob();
+
+ // Create notes with specific timing to test cursor behavior
+ const baseTime = Date.now() - ms(`${meta.remoteNotesCleaningExpiryDaysForEachNotes} days`) - 10000;
+
+ const note1 = await createNote({}, bob, baseTime);
+ const note2 = await createNote({}, carol, baseTime - 1000);
+ const note3 = await createNote({}, bob, baseTime - 2000);
+
+ const result = await service.process(job as any);
+
+ expect(result.deletedCount).toBe(3);
+ expect(result.newest).toBe(idService.parse(note1.id).date.getTime());
+ expect(result.oldest).toBe(idService.parse(note3.id).date.getTime());
+ expect(result.skipped).toBe(false);
+ });
+ });
+});
diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts
index 7eecf8bb0d..ace614115c 100644
--- a/packages/backend/test/utils.ts
+++ b/packages/backend/test/utils.ts
@@ -317,7 +317,7 @@ export const uploadFile = async (user?: UserToken, { path, name, blob }: UploadO
const formData = new FormData();
formData.append('file', blob ??
- new File([await readFile(absPath)], basename(absPath.toString())));
+ new File([new Uint8Array(await readFile(absPath))], basename(absPath.toString())));
formData.append('force', 'true');
if (name) {
formData.append('name', name);
@@ -608,8 +608,8 @@ export async function initTestDb(justBorrow = false, initEntities?: any[]) {
username: config.db.user,
password: config.db.pass,
database: config.db.db,
- synchronize: true && !justBorrow,
- dropSchema: true && !justBorrow,
+ synchronize: !justBorrow,
+ dropSchema: !justBorrow,
entities: initEntities ?? entities,
});
@@ -661,7 +661,9 @@ export async function captureWebhook<T = SystemWebhookPayload>(postAction: () =>
let timeoutHandle: NodeJS.Timeout | null = null;
const result = await new Promise<string>(async (resolve, reject) => {
fastify.all('/', async (req, res) => {
- timeoutHandle && clearTimeout(timeoutHandle);
+ if (timeoutHandle) {
+ clearTimeout(timeoutHandle);
+ }
const body = JSON.stringify(req.body);
res.status(200).send('ok');
diff --git a/packages/frontend-builder/README.txt b/packages/frontend-builder/README.txt
new file mode 100644
index 0000000000..428166e792
--- /dev/null
+++ b/packages/frontend-builder/README.txt
@@ -0,0 +1 @@
+This package contains the common scripts that are used to build the frontend and frontend-embed packages.
diff --git a/packages/frontend-builder/eslint.config.js b/packages/frontend-builder/eslint.config.js
new file mode 100644
index 0000000000..a13490c97f
--- /dev/null
+++ b/packages/frontend-builder/eslint.config.js
@@ -0,0 +1,52 @@
+import globals from 'globals';
+import tsParser from '@typescript-eslint/parser';
+import sharedConfig from '../shared/eslint.config.js';
+
+// eslint-disable-next-line import/no-default-export
+export default [
+ ...sharedConfig,
+ {
+ files: [
+ '**/*.ts',
+ ],
+ languageOptions: {
+ globals: {
+ ...Object.fromEntries(Object.entries(globals.node).map(([key]) => [key, 'off'])),
+ ...globals.browser,
+
+ // Node.js
+ module: false,
+ require: false,
+ __dirname: false,
+
+ // Misskey
+ _DEV_: false,
+ _LANGS_: false,
+ _VERSION_: false,
+ _ENV_: false,
+ _PERF_PREFIX_: false,
+ },
+ parserOptions: {
+ parser: tsParser,
+ project: ['./tsconfig.json'],
+ sourceType: 'module',
+ tsconfigRootDir: import.meta.dirname,
+ },
+ },
+ rules: {
+ '@typescript-eslint/no-unused-vars': 'off',
+ '@typescript-eslint/no-empty-interface': ['error', {
+ allowSingleExtends: true,
+ }],
+ 'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
+ // window ã®ç¦æ­¢ç†ç”±: グローãƒãƒ«ã‚¹ã‚³ãƒ¼ãƒ—ã¨è¡çªã—ã€äºˆæœŸã›ã¬çµæžœã‚’æ‹›ããŸã‚
+ // e ã®ç¦æ­¢ç†ç”±: error ã‚„ event ãªã©ã€è¤‡æ•°ã®ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã®é ­æ–‡å­—ã§ã‚り分ã‹ã‚Šã«ãã„ãŸã‚
+ 'id-denylist': ['error', 'window', 'e'],
+ 'no-shadow': ['warn'],
+ },
+ },
+ {
+ ignores: [
+ ],
+ },
+];
diff --git a/packages/frontend-builder/locale-inliner.ts b/packages/frontend-builder/locale-inliner.ts
new file mode 100644
index 0000000000..9bef465eeb
--- /dev/null
+++ b/packages/frontend-builder/locale-inliner.ts
@@ -0,0 +1,153 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as fs from 'fs/promises';
+import * as path from 'node:path';
+import MagicString from 'magic-string';
+import { collectModifications } from './locale-inliner/collect-modifications.js';
+import { applyWithLocale } from './locale-inliner/apply-with-locale.js';
+import { blankLogger } from './logger.js';
+import type { Logger } from './logger.js';
+import type { Locale } from '../../locales/index.js';
+import type { Manifest as ViteManifest } from 'vite';
+
+export class LocaleInliner {
+ outputDir: string;
+ scriptsDir: string;
+ i18nFile: string;
+ i18nFileName: string;
+ logger: Logger;
+ chunks: ScriptChunk[];
+
+ static async create(options: {
+ outputDir: string,
+ scriptsDir: string,
+ i18nFile: string,
+ logger: Logger,
+ }): Promise<LocaleInliner> {
+ const manifest: ViteManifest = JSON.parse(await fs.readFile(`${options.outputDir}/manifest.json`, 'utf-8'));
+ return new LocaleInliner({ ...options, manifest });
+ }
+
+ constructor(options: {
+ outputDir: string,
+ scriptsDir: string,
+ i18nFile: string,
+ manifest: ViteManifest,
+ logger: Logger,
+ }) {
+ this.outputDir = options.outputDir;
+ this.scriptsDir = options.scriptsDir;
+ this.i18nFile = options.i18nFile;
+ this.i18nFileName = this.stripScriptDir(options.manifest[this.i18nFile].file);
+ this.logger = options.logger;
+ this.chunks = Object.values(options.manifest).filter(chunk => this.isScriptFile(chunk.file)).map(chunk => ({
+ fileName: this.stripScriptDir(chunk.file),
+ chunkName: chunk.name,
+ }));
+ }
+
+ async loadFiles() {
+ await Promise.all(this.chunks.map(async chunk => {
+ const filePath = path.join(this.outputDir, this.scriptsDir, chunk.fileName);
+ chunk.sourceCode = await fs.readFile(filePath, 'utf-8');
+ }));
+ }
+
+ collectsModifications() {
+ for (const chunk of this.chunks) {
+ if (!chunk.sourceCode) {
+ throw new Error(`Source code for ${chunk.fileName} is not loaded.`);
+ }
+ const fileLogger = this.logger.prefixed(`${chunk.fileName} (${chunk.chunkName}): `);
+ chunk.modifications = collectModifications(chunk.sourceCode, chunk.fileName, fileLogger, this);
+ }
+ }
+
+ async saveAllLocales(locales: Record<string, Locale>) {
+ const localeNames = Object.keys(locales);
+ for (const localeName of localeNames) {
+ this.logger.info(`Creating bundle for ${localeName}`);
+ await this.saveLocale(localeName, locales[localeName]);
+ }
+ this.logger.info('Done');
+ }
+
+ async saveLocale(localeName: string, localeJson: Locale) {
+ // create directory
+ await fs.mkdir(path.join(this.outputDir, localeName), { recursive: true });
+ const localeLogger = localeName === 'ja-JP' ? this.logger : blankLogger; // we want to log for single locale only
+ for (const chunk of this.chunks) {
+ if (!chunk.sourceCode || !chunk.modifications) {
+ throw new Error(`Source code or modifications for ${chunk.fileName} is not available.`);
+ }
+ const fileLogger = localeLogger.prefixed(`${chunk.fileName} (${chunk.chunkName}): `);
+ const magicString = new MagicString(chunk.sourceCode);
+ applyWithLocale(magicString, chunk.modifications, localeName, localeJson, fileLogger);
+
+ await fs.writeFile(path.join(this.outputDir, localeName, chunk.fileName), magicString.toString());
+ }
+ }
+
+ isScriptFile(fileName: string) {
+ return fileName.startsWith(this.scriptsDir + '/') && fileName.endsWith('.js');
+ }
+
+ stripScriptDir(fileName: string) {
+ if (!fileName.startsWith(this.scriptsDir + '/')) {
+ throw new Error(`${fileName} does not start with ${this.scriptsDir}/`);
+ }
+ return fileName.slice(this.scriptsDir.length + 1);
+ }
+}
+
+interface ScriptChunk {
+ fileName: string;
+ chunkName?: string;
+ sourceCode?: string;
+ modifications?: TextModification[];
+}
+
+export type TextModification = {
+ type: 'delete';
+ begin: number;
+ end: number;
+ localizedOnly: boolean;
+} | {
+ // can be used later to insert '../scripts' for common files
+ type: 'insert';
+ begin: number;
+ text: string;
+ localizedOnly: boolean;
+} | {
+ type: 'replace';
+ begin: number;
+ end: number;
+ text: string;
+ localizedOnly: boolean;
+} | {
+ type: 'localized';
+ begin: number;
+ end: number;
+ localizationKey: string[];
+ localizedOnly: true;
+} | {
+ type: 'parameterized-function';
+ begin: number;
+ end: number;
+ localizationKey: string[];
+ localizedOnly: true;
+} | {
+ type: 'locale-name';
+ begin: number;
+ end: number;
+ literal: boolean;
+ localizedOnly: true;
+} | {
+ type: 'locale-json';
+ begin: number;
+ end: number;
+ localizedOnly: true;
+};
diff --git a/packages/frontend-builder/locale-inliner/apply-with-locale.ts b/packages/frontend-builder/locale-inliner/apply-with-locale.ts
new file mode 100644
index 0000000000..5e601cdf12
--- /dev/null
+++ b/packages/frontend-builder/locale-inliner/apply-with-locale.ts
@@ -0,0 +1,102 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MagicString from 'magic-string';
+import { assertNever } from '../utils.js';
+import type { Locale, ILocale } from '../../../locales/index.js';
+import type { TextModification } from '../locale-inliner.js';
+import type { Logger } from '../logger.js';
+
+export function applyWithLocale(
+ sourceCode: MagicString,
+ modifications: TextModification[],
+ localeName: string,
+ localeJson: Locale,
+ fileLogger: Logger,
+) {
+ for (const modification of modifications) {
+ switch (modification.type) {
+ case 'delete':
+ sourceCode.remove(modification.begin, modification.end);
+ break;
+ case 'insert':
+ sourceCode.appendRight(modification.begin, modification.text);
+ break;
+ case 'replace':
+ sourceCode.update(modification.begin, modification.end, modification.text);
+ break;
+ case 'localized': {
+ const accessed = getPropertyByPath(localeJson, modification.localizationKey);
+ if (accessed == null) {
+ fileLogger.warn(`Cannot find localization key ${modification.localizationKey.join('.')}`);
+ }
+ sourceCode.update(modification.begin, modification.end, JSON.stringify(accessed));
+ break;
+ }
+ case 'parameterized-function': {
+ const accessed = getPropertyByPath(localeJson, modification.localizationKey);
+ let replacement: string;
+ if (typeof accessed === 'string') {
+ replacement = formatFunction(accessed);
+ } else if (typeof accessed === 'object' && accessed !== null) {
+ replacement = `({${Object.entries(accessed).map(([key, value]) => `${JSON.stringify(key)}:${formatFunction(value)}`).join(',')}})`;
+ } else {
+ fileLogger.warn(`Cannot find localization key ${modification.localizationKey.join('.')}`);
+ replacement = '(() => "")'; // placeholder for missing locale
+ }
+ sourceCode.update(modification.begin, modification.end, replacement);
+ break;
+
+ function formatFunction(format: string): string {
+ const params = new Set<string>();
+ const components: string[] = [];
+ let lastIndex = 0;
+ for (const match of format.matchAll(/\{(.+?)}/g)) {
+ const [fullMatch, paramName] = match;
+ if (lastIndex < match.index) {
+ components.push(JSON.stringify(format.slice(lastIndex, match.index)));
+ }
+ params.add(paramName);
+ components.push(paramName);
+ lastIndex = match.index + fullMatch.length;
+ }
+ components.push(JSON.stringify(format.slice(lastIndex)));
+
+ // we replace with `(({name,count})=>(name+count+"some"))`
+ const paramList = Array.from(params).join(',');
+ let body = components.filter(x => x !== '""').join('+');
+ if (body === '') body = '""'; // if the body is empty, we return empty string
+ return `(({${paramList}})=>(${body}))`;
+ }
+ }
+ case 'locale-name': {
+ sourceCode.update(modification.begin, modification.end, modification.literal ? JSON.stringify(localeName) : localeName);
+ break;
+ }
+ case 'locale-json': {
+ // locale-json is inlined to place where initialize module-level variable which is executed only once.
+ // In such case we can use JSON.parse to speed up the parsing script.
+ // https://v8.dev/blog/cost-of-javascript-2019#json
+ sourceCode.update(modification.begin, modification.end, `JSON.parse(${JSON.stringify(JSON.stringify(localeJson))})`);
+ break;
+ }
+ default: {
+ assertNever(modification);
+ }
+ }
+ }
+}
+
+function getPropertyByPath(localeJson: ILocale, localizationKey: string[]): string | object | null {
+ if (localizationKey.length === 0) return localeJson;
+ let current: ILocale | string = localeJson;
+ for (const key of localizationKey) {
+ if (typeof current !== 'object' || !(key in current)) {
+ return null; // Key not found
+ }
+ current = current[key];
+ }
+ return current;
+}
diff --git a/packages/frontend-builder/locale-inliner/collect-modifications.ts b/packages/frontend-builder/locale-inliner/collect-modifications.ts
new file mode 100644
index 0000000000..59e5d96517
--- /dev/null
+++ b/packages/frontend-builder/locale-inliner/collect-modifications.ts
@@ -0,0 +1,425 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { parseAst } from 'vite';
+import * as estreeWalker from 'estree-walker';
+import { assertNever, assertType } from '../utils.js';
+import type { AstNode, ProgramNode } from 'rollup';
+import type * as estree from 'estree';
+import type { LocaleInliner, TextModification } from '../locale-inliner.js';
+import type { Logger } from '../logger.js';
+
+// WalkerContext is not exported from estree-walker, so we define it here
+interface WalkerContext {
+ skip: () => void;
+}
+
+export function collectModifications(sourceCode: string, fileName: string, fileLogger: Logger, inliner: LocaleInliner): TextModification[] {
+ let programNode: ProgramNode;
+ try {
+ programNode = parseAst(sourceCode);
+ } catch (err) {
+ fileLogger.error(`Failed to parse source code: ${err}`);
+ return [];
+ }
+ if (programNode.sourceType !== 'module') {
+ fileLogger.error('Source code is not a module.');
+ return [];
+ }
+
+ const modifications: TextModification[] = [];
+
+ // first
+ // 1) replace all `scripts/` path literals with locale code
+ // 2) replace all `localStorage.getItem("lang")` with `localeName` variable
+ // 3) replace all `await window.fetch(`/assets/locales/${d}.${x}.json`).then(u=>u.json())` with `localeJson` variable
+ estreeWalker.walk(programNode, {
+ enter(this: WalkerContext, node: Node) {
+ assertType<AstNode>(node);
+
+ if (node.type === 'Literal' && typeof node.value === 'string' && node.raw) {
+ if (node.raw.substring(1).startsWith(inliner.scriptsDir)) {
+ // we find `scripts/\w+\.js` literal and replace 'scripts' part with locale code
+ fileLogger.debug(`${lineCol(sourceCode, node)}: found ${inliner.scriptsDir}/ path literal ${node.raw}`);
+ modifications.push({
+ type: 'locale-name',
+ begin: node.start + 1,
+ end: node.start + 1 + inliner.scriptsDir.length,
+ literal: false,
+ localizedOnly: true,
+ });
+ }
+ if (node.raw.substring(1, node.raw.length - 1) === `${inliner.scriptsDir}/${inliner.i18nFileName}`) {
+ // we find `scripts/i18n.ts` literal.
+ // This is tipically in depmap and replace with this file name to avoid unnecessary loading i18n script
+ fileLogger.debug(`${lineCol(sourceCode, node)}: found ${inliner.i18nFileName} path literal ${node.raw}`);
+ modifications.push({
+ type: 'replace',
+ begin: node.end - 1 - inliner.i18nFileName.length,
+ end: node.end - 1,
+ text: fileName,
+ localizedOnly: true,
+ });
+ }
+ }
+
+ if (isLocalStorageGetItemLang(node)) {
+ fileLogger.debug(`${lineCol(sourceCode, node)}: found localStorage.getItem("lang") call`);
+ modifications.push({
+ type: 'locale-name',
+ begin: node.start,
+ end: node.end,
+ literal: true,
+ localizedOnly: true,
+ });
+ }
+
+ if (isAwaitFetchLocaleThenJson(node)) {
+ // await window.fetch(`/assets/locales/${d}.${x}.json`).then(u=>u.json(), () => null)
+ fileLogger.debug(`${lineCol(sourceCode, node)}: found await window.fetch(\`/assets/locales/\${d}.\${x}.json\`).then(u=>u.json()) call`);
+ modifications.push({
+ type: 'locale-json',
+ begin: node.start,
+ end: node.end,
+ localizedOnly: true,
+ });
+ }
+ },
+ });
+
+ const importSpecifierResult = findImportSpecifier(programNode, inliner.i18nFileName, 'i18n');
+
+ switch (importSpecifierResult.type) {
+ case 'no-import':
+ fileLogger.debug('No import of i18n found, skipping inlining.');
+ return modifications;
+ case 'no-specifiers':
+ fileLogger.debug('Importing i18n without specifiers, removing the import.');
+ modifications.push({
+ type: 'delete',
+ begin: importSpecifierResult.importNode.start,
+ end: importSpecifierResult.importNode.end,
+ localizedOnly: false,
+ });
+ return modifications;
+ case 'unexpected-specifiers':
+ fileLogger.info(`Importing ${inliner.i18nFileName} found but with unexpected specifiers. Skipping inlining.`);
+ return modifications;
+ case 'specifier':
+ fileLogger.debug(`Found import i18n as ${importSpecifierResult.localI18nIdentifier}`);
+ break;
+ }
+
+ const i18nImport = importSpecifierResult.importNode;
+ const localI18nIdentifier = importSpecifierResult.localI18nIdentifier;
+
+ // Check if the identifier is already declared in the file.
+ // If it is, we may overwrite it and cause issues so we skip inlining
+ let isSupported = true;
+ estreeWalker.walk(programNode, {
+ enter(node) {
+ if (node.type === 'VariableDeclaration') {
+ assertType<estree.VariableDeclaration>(node);
+ for (const id of node.declarations.flatMap(x => declsOfPattern(x.id))) {
+ if (id === localI18nIdentifier) {
+ isSupported = false;
+ }
+ }
+ }
+ },
+ });
+
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+ if (!isSupported) {
+ fileLogger.error(`Duplicated identifier "${localI18nIdentifier}" in variable declaration. Skipping inlining.`);
+ return modifications;
+ }
+
+ fileLogger.debug(`imports i18n as ${localI18nIdentifier}`);
+
+ // In case of substitution failure, we will preserve the import statement
+ // otherwise we will remove it.
+ let preserveI18nImport = false;
+
+ const toSkip = new Set();
+ toSkip.add(i18nImport);
+ estreeWalker.walk(programNode, {
+ enter(this: WalkerContext, node, parent, property) {
+ assertType<AstNode>(node);
+ assertType<AstNode>(parent);
+ if (toSkip.has(node)) {
+ // This is the import specifier, skip processing it
+ this.skip();
+ return;
+ }
+
+ // We don't care original name part of the import declaration
+ if (node.type === 'ImportDeclaration') this.skip();
+
+ if (node.type === 'Identifier') {
+ assertType<estree.Identifier>(node);
+ assertType<estree.Property | estree.MemberExpression | estree.ExportSpecifier>(parent);
+ if (parent.type === 'Property' && !parent.computed && property === 'key') return; // we don't care 'id' part of { id: expr }
+ if (parent.type === 'MemberExpression' && !parent.computed && property === 'property') return; // we don't care 'id' part of { id: expr }
+ if (parent.type === 'ExportSpecifier' && property === 'exported') return; // we don't care 'id' part of { id: expr }
+ if (node.name === localI18nIdentifier) {
+ fileLogger.error(`${lineCol(sourceCode, node)}: Using i18n identifier "${localI18nIdentifier}" directly. Skipping inlining.`);
+ preserveI18nImport = true;
+ }
+ } else if (node.type === 'MemberExpression') {
+ assertType<estree.MemberExpression>(node);
+ const i18nPath = parseI18nPropertyAccess(node);
+ if (i18nPath != null && i18nPath.length >= 2 && i18nPath[0] === 'ts') {
+ if (parent.type === 'CallExpression' && property === 'callee') return; // we don't want to process `i18n.ts.property.stringBuiltinMethod()`
+ if (i18nPath.at(-1)?.startsWith('_')) fileLogger.debug(`found i18n grouped property access ${i18nPath.join('.')}`);
+ else fileLogger.debug(`${lineCol(sourceCode, node)}: found i18n property access ${i18nPath.join('.')}`);
+ // it's i18n.ts.propertyAccess
+ // i18n.ts.* will always be resolved to string or object containing strings
+ modifications.push({
+ type: 'localized',
+ begin: node.start,
+ end: node.end,
+ localizationKey: i18nPath.slice(1), // remove 'ts' prefix
+ localizedOnly: true,
+ });
+ this.skip();
+ } else if (i18nPath != null && i18nPath.length >= 2 && i18nPath[0] === 'tsx') {
+ // it's parameterized locale substitution (`i18n.tsx.property(parameters)`)
+ // we expect the parameter to be an object literal
+ fileLogger.debug(`${lineCol(sourceCode, node)}: found i18n function access (object) ${i18nPath.join('.')}`);
+ modifications.push({
+ type: 'parameterized-function',
+ begin: node.start,
+ end: node.end,
+ localizationKey: i18nPath.slice(1), // remove 'tsx' prefix
+ localizedOnly: true,
+ });
+ this.skip();
+ }
+ } else if (node.type === 'ArrowFunctionExpression') {
+ assertType<estree.ArrowFunctionExpression>(node);
+ // If there is 'i18n' in the parameters, we care interior of the function
+ if (node.params.flatMap(param => declsOfPattern(param)).includes(localI18nIdentifier)) this.skip();
+ }
+ },
+ });
+
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+ if (!preserveI18nImport) {
+ fileLogger.debug('removing i18n import statement');
+ modifications.push({
+ type: 'delete',
+ begin: i18nImport.start,
+ end: i18nImport.end,
+ localizedOnly: true,
+ });
+ }
+
+ function parseI18nPropertyAccess(node: estree.Expression | estree.Super): string[] | null {
+ if (node.type === 'Identifier' && node.name === localI18nIdentifier) return []; // i18n itself
+ if (node.type !== 'MemberExpression') return null;
+ // super.*
+ if (node.object.type === 'Super') return null;
+
+ // i18n?.property is not supported
+ if (node.optional) return null;
+
+ let id: string | null = null;
+ if (node.computed) {
+ if (node.property.type === 'Literal' && typeof node.property.value === 'string') {
+ id = node.property.value;
+ }
+ } else {
+ if (node.property.type === 'Identifier') {
+ id = node.property.name;
+ }
+ }
+ // non-constant property access
+ if (id == null) return null;
+
+ const parentAccess = parseI18nPropertyAccess(node.object);
+ if (parentAccess == null) return null;
+ return [...parentAccess, id];
+ }
+
+ return modifications;
+}
+
+function declsOfPattern(pattern: estree.Pattern | null): string[] {
+ if (pattern == null) return [];
+ switch (pattern.type) {
+ case 'Identifier':
+ return [pattern.name];
+ case 'ObjectPattern':
+ return pattern.properties.flatMap(prop => {
+ switch (prop.type) {
+ case 'Property':
+ return declsOfPattern(prop.value);
+ case 'RestElement':
+ return declsOfPattern(prop.argument);
+ default:
+ assertNever(prop);
+ }
+ });
+ case 'ArrayPattern':
+ return pattern.elements.flatMap(p => declsOfPattern(p));
+ case 'RestElement':
+ return declsOfPattern(pattern.argument);
+ case 'AssignmentPattern':
+ return declsOfPattern(pattern.left);
+ case 'MemberExpression':
+ // assignment pattern so no new variable is declared
+ return [];
+ default:
+ assertNever(pattern);
+ }
+}
+
+function lineCol(sourceCode: string, node: estree.Node): string {
+ assertType<AstNode>(node);
+ const leading = sourceCode.slice(0, node.start);
+ const lines = leading.split('\n');
+ const line = lines.length;
+ const col = lines[lines.length - 1].length + 1; // +1 for 1-based index
+ return `(${line}:${col})`;
+}
+
+//region checker functions
+
+type Node =
+ | estree.AssignmentProperty
+ | estree.CatchClause
+ | estree.Class
+ | estree.ClassBody
+ | estree.Expression
+ | estree.Function
+ | estree.Identifier
+ | estree.Literal
+ | estree.MethodDefinition
+ | estree.ModuleDeclaration
+ | estree.ModuleSpecifier
+ | estree.Pattern
+ | estree.PrivateIdentifier
+ | estree.Program
+ | estree.Property
+ | estree.PropertyDefinition
+ | estree.SpreadElement
+ | estree.Statement
+ | estree.Super
+ | estree.SwitchCase
+ | estree.TemplateElement
+ | estree.VariableDeclarator
+ ;
+
+// localStorage.getItem("lang")
+function isLocalStorageGetItemLang(getItemCall: Node): boolean {
+ if (getItemCall.type !== 'CallExpression') return false;
+ if (getItemCall.arguments.length !== 1) return false;
+
+ const langLiteral = getItemCall.arguments[0];
+ if (!isStringLiteral(langLiteral, 'lang')) return false;
+
+ const getItemFunction = getItemCall.callee;
+ if (!isMemberExpression(getItemFunction, 'getItem')) return false;
+
+ const localStorageObject = getItemFunction.object;
+ if (!isIdentifier(localStorageObject, 'localStorage')) return false;
+
+ return true;
+}
+
+// await window.fetch(`/assets/locales/${d}.${x}.json`).then(u => u.json(), ....)
+function isAwaitFetchLocaleThenJson(awaitNode: Node): boolean {
+ if (awaitNode.type !== 'AwaitExpression') return false;
+
+ const thenCall = awaitNode.argument;
+ if (thenCall.type !== 'CallExpression') return false;
+ if (thenCall.arguments.length < 1) return false;
+
+ const arrowFunction = thenCall.arguments[0];
+ if (arrowFunction.type !== 'ArrowFunctionExpression') return false;
+ if (arrowFunction.params.length !== 1) return false;
+
+ const arrowBodyCall = arrowFunction.body;
+ if (arrowBodyCall.type !== 'CallExpression') return false;
+
+ const jsonFunction = arrowBodyCall.callee;
+ if (!isMemberExpression(jsonFunction, 'json')) return false;
+
+ const thenFunction = thenCall.callee;
+ if (!isMemberExpression(thenFunction, 'then')) return false;
+
+ const fetchCall = thenFunction.object;
+ if (fetchCall.type !== 'CallExpression') return false;
+ if (fetchCall.arguments.length !== 1) return false;
+
+ // `/assets/locales/${d}.${x}.json`
+ const assetLocaleTemplate = fetchCall.arguments[0];
+ if (assetLocaleTemplate.type !== 'TemplateLiteral') return false;
+ if (assetLocaleTemplate.quasis.length !== 3) return false;
+ if (assetLocaleTemplate.expressions.length !== 2) return false;
+ if (assetLocaleTemplate.quasis[0].value.cooked !== '/assets/locales/') return false;
+ if (assetLocaleTemplate.quasis[1].value.cooked !== '.') return false;
+ if (assetLocaleTemplate.quasis[2].value.cooked !== '.json') return false;
+
+ const fetchFunction = fetchCall.callee;
+ if (!isMemberExpression(fetchFunction, 'fetch')) return false;
+ const windowObject = fetchFunction.object;
+ if (!isIdentifier(windowObject, 'window')) return false;
+
+ return true;
+}
+
+type SpecifierResult =
+ | { type: 'no-import' }
+ | { type: 'no-specifiers', importNode: estree.ImportDeclaration & AstNode }
+ | { type: 'unexpected-specifiers', importNode: estree.ImportDeclaration & AstNode }
+ | { type: 'specifier', localI18nIdentifier: string, importNode: estree.ImportDeclaration & AstNode }
+ ;
+
+function findImportSpecifier(programNode: ProgramNode, i18nFileName: string, i18nSymbol: string): SpecifierResult {
+ const imports = programNode.body.filter(x => x.type === 'ImportDeclaration');
+ const importNode = imports.find(x => x.source.value === `./${i18nFileName}`) as estree.ImportDeclaration | undefined;
+ if (!importNode) return { type: 'no-import' };
+ assertType<AstNode>(importNode);
+
+ if (importNode.specifiers.length === 0) {
+ return { type: 'no-specifiers', importNode };
+ }
+
+ if (importNode.specifiers.length !== 1) {
+ return { type: 'unexpected-specifiers', importNode };
+ }
+ const i18nImportSpecifier = importNode.specifiers[0];
+ if (i18nImportSpecifier.type !== 'ImportSpecifier') {
+ return { type: 'unexpected-specifiers', importNode };
+ }
+
+ if (i18nImportSpecifier.imported.type !== 'Identifier') {
+ return { type: 'unexpected-specifiers', importNode };
+ }
+
+ const importingIdentifier = i18nImportSpecifier.imported.name;
+ if (importingIdentifier !== i18nSymbol) {
+ return { type: 'unexpected-specifiers', importNode };
+ }
+ const localI18nIdentifier = i18nImportSpecifier.local.name;
+ return { type: 'specifier', localI18nIdentifier, importNode };
+}
+
+// checker helpers
+function isMemberExpression(node: Node, property: string): node is estree.MemberExpression {
+ return node.type === 'MemberExpression' && !node.computed && node.property.type === 'Identifier' && node.property.name === property;
+}
+
+function isStringLiteral(node: Node, value: string): node is estree.Literal {
+ return node.type === 'Literal' && typeof node.value === 'string' && node.value === value;
+}
+
+function isIdentifier(node: Node, name: string): node is estree.Identifier {
+ return node.type === 'Identifier' && node.name === name;
+}
+
+//endregion
diff --git a/packages/frontend-builder/logger.ts b/packages/frontend-builder/logger.ts
new file mode 100644
index 0000000000..c619882380
--- /dev/null
+++ b/packages/frontend-builder/logger.ts
@@ -0,0 +1,73 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as process from 'node:process';
+
+const debug = process.env.BUILDER_DEBUG !== undefined && process.env.BUILDER_DEBUG !== '0';
+
+export interface Logger {
+ debug(message: string): void;
+
+ warn(message: string): void;
+
+ error(message: string): void;
+
+ info(message: string): void;
+
+ prefixed(newPrefix: string): Logger;
+}
+
+interface RootLogger extends Logger {
+ warningCount: number;
+ errorCount: number;
+}
+
+export function createLogger(): RootLogger {
+ return loggerFactory('', {
+ warningCount: 0,
+ errorCount: 0,
+ });
+}
+
+type LogContext = {
+ warningCount: number;
+ errorCount: number;
+};
+
+function loggerFactory(prefix: string, context: LogContext): RootLogger {
+ return {
+ debug: (message: string) => {
+ if (debug) console.log(`[DBG] ${prefix}${message}`);
+ },
+ warn: (message: string) => {
+ context.warningCount++;
+ console.log(`${debug ? '[WRN]' : 'w:'} ${prefix}${message}`);
+ },
+ error: (message: string) => {
+ context.errorCount++;
+ console.error(`${debug ? '[ERR]' : 'e:'} ${prefix}${message}`);
+ },
+ info: (message: string) => {
+ console.error(`${debug ? '[INF]' : 'i:'} ${prefix}${message}`);
+ },
+ prefixed: (newPrefix: string) => {
+ return loggerFactory(`${prefix}${newPrefix}`, context);
+ },
+ get warningCount() {
+ return context.warningCount;
+ },
+ get errorCount() {
+ return context.errorCount;
+ },
+ };
+}
+
+export const blankLogger: Logger = {
+ debug: () => void 0,
+ warn: () => void 0,
+ error: () => void 0,
+ info: () => void 0,
+ prefixed: () => blankLogger,
+};
diff --git a/packages/frontend-builder/package.json b/packages/frontend-builder/package.json
new file mode 100644
index 0000000000..5fdd25b32d
--- /dev/null
+++ b/packages/frontend-builder/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "frontend-builder",
+ "type": "module",
+ "scripts": {
+ "eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
+ "typecheck": "tsc --noEmit",
+ "lint": "pnpm typecheck && pnpm eslint"
+ },
+ "exports": {
+ "./*": "./js/*"
+ },
+ "devDependencies": {
+ "@types/estree": "1.0.8",
+ "@types/node": "22.17.0",
+ "@typescript-eslint/eslint-plugin": "8.38.0",
+ "@typescript-eslint/parser": "8.38.0",
+ "rollup": "4.46.2",
+ "typescript": "5.9.2"
+ },
+ "dependencies": {
+ "estree-walker": "3.0.3",
+ "magic-string": "0.30.17",
+ "vite": "7.0.6"
+ }
+}
diff --git a/packages/frontend-builder/rollup-plugin-remove-unref-i18n.ts b/packages/frontend-builder/rollup-plugin-remove-unref-i18n.ts
new file mode 100644
index 0000000000..4a2bfa67d9
--- /dev/null
+++ b/packages/frontend-builder/rollup-plugin-remove-unref-i18n.ts
@@ -0,0 +1,53 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as estreeWalker from 'estree-walker';
+import MagicString from 'magic-string';
+import { assertType } from './utils.js';
+import type { Plugin } from 'vite';
+import type { CallExpression, Expression, Program } from 'estree';
+import type { AstNode } from 'rollup';
+
+// This plugin transforms `unref(i18n)` to `i18n` in the code, which is useful for removing unnecessary unref calls
+// and helps locale inliner runs after vite build to inline the locale data into the final build.
+//
+// locale inliner cannot know minifiedSymbol(i18n) is 'unref(i18n)' or 'otherFunctionsWithEffect(i18n)' so
+// it is necessary to remove unref calls before minification.
+export function pluginRemoveUnrefI18n(
+ {
+ i18nSymbolName = 'i18n',
+ }: {
+ i18nSymbolName?: string
+ } = {}): Plugin {
+ return {
+ name: 'UnwindCssModuleClassName',
+ renderChunk(code) {
+ if (!code.includes('unref(i18n)')) return null;
+ const ast = this.parse(code) as Program;
+ const magicString = new MagicString(code);
+ estreeWalker.walk(ast, {
+ enter(node) {
+ if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === 'unref'
+ && node.arguments.length === 1) {
+ // calls to unref with single argument
+ const arg = node.arguments[0];
+ if (arg.type === 'Identifier' && arg.name === i18nSymbolName) {
+ // this is unref(i18n) so replace it with i18n
+ // to replace, remove the 'unref(' and the trailing ')'
+ assertType<CallExpression & AstNode>(node);
+ assertType<Expression & AstNode>(arg);
+ magicString.remove(node.start, arg.start);
+ magicString.remove(arg.end, node.end);
+ }
+ }
+ },
+ });
+ return {
+ code: magicString.toString(),
+ map: magicString.generateMap({ hires: true }),
+ };
+ },
+ };
+}
diff --git a/packages/frontend-builder/tsconfig.json b/packages/frontend-builder/tsconfig.json
new file mode 100644
index 0000000000..9250b2f3da
--- /dev/null
+++ b/packages/frontend-builder/tsconfig.json
@@ -0,0 +1,29 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "nodenext",
+ "moduleResolution": "nodenext",
+ "declaration": true,
+ "declarationMap": true,
+ "sourceMap": false,
+ "noEmit": true,
+ "removeComments": true,
+ "resolveJsonModule": true,
+ "strict": true,
+ "strictFunctionTypes": true,
+ "strictNullChecks": true,
+ "experimentalDecorators": true,
+ "noImplicitReturns": true,
+ "esModuleInterop": true,
+ "verbatimModuleSyntax": true,
+ "baseUrl": ".",
+ "typeRoots": [
+ "./@types",
+ "./node_modules/@types"
+ ],
+ "lib": [
+ "esnext"
+ ]
+ }
+}
diff --git a/packages/frontend-builder/utils.ts b/packages/frontend-builder/utils.ts
new file mode 100644
index 0000000000..71ffebe03e
--- /dev/null
+++ b/packages/frontend-builder/utils.ts
@@ -0,0 +1,12 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export function assertNever(x: never): never {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ throw new Error(`Unexpected type: ${(x as any)?.type ?? x}`);
+}
+
+export function assertType<T>(node: unknown): asserts node is T {
+}
diff --git a/packages/frontend-embed/build.ts b/packages/frontend-embed/build.ts
new file mode 100644
index 0000000000..737233a4d0
--- /dev/null
+++ b/packages/frontend-embed/build.ts
@@ -0,0 +1,51 @@
+import * as fs from 'fs/promises';
+import url from 'node:url';
+import path from 'node:path';
+import { execa } from 'execa';
+import locales from '../../locales/index.js';
+import { LocaleInliner } from '../frontend-builder/locale-inliner.js'
+import { createLogger } from '../frontend-builder/logger';
+
+// requires node 21 or later
+const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
+const outputDir = __dirname + '/../../built/_frontend_embed_vite_';
+
+/**
+ * @return {Promise<void>}
+ */
+async function viteBuild() {
+ await execa('vite', ['build'], {
+ cwd: __dirname,
+ stdout: process.stdout,
+ stderr: process.stderr,
+ });
+}
+
+
+async function buildAllLocale() {
+ const logger = createLogger()
+ const inliner = await LocaleInliner.create({
+ outputDir,
+ logger,
+ scriptsDir: 'scripts',
+ i18nFile: 'src/i18n.ts',
+ })
+
+ await inliner.loadFiles();
+
+ inliner.collectsModifications();
+
+ await inliner.saveAllLocales(locales);
+
+ if (logger.errorCount > 0) {
+ throw new Error(`Build failed with ${logger.errorCount} errors and ${logger.warningCount} warnings.`);
+ }
+}
+
+async function build() {
+ await fs.rm(outputDir, { recursive: true, force: true });
+ await viteBuild();
+ await buildAllLocale();
+}
+
+await build();
diff --git a/packages/frontend-embed/package.json b/packages/frontend-embed/package.json
index bae5b0ebd3..73bcd798f0 100644
--- a/packages/frontend-embed/package.json
+++ b/packages/frontend-embed/package.json
@@ -4,68 +4,69 @@
"type": "module",
"scripts": {
"watch": "vite",
- "build": "vite build",
+ "build": "tsx build.ts",
"typecheck": "vue-tsc --noEmit",
"eslint": "eslint --quiet \"src/**/*.{ts,vue}\"",
"lint": "pnpm typecheck && pnpm eslint"
},
"dependencies": {
- "@discordapp/twemoji": "15.1.0",
+ "@discordapp/twemoji": "16.0.1",
"@rollup/plugin-json": "6.1.0",
"@rollup/plugin-replace": "6.0.2",
"@rollup/pluginutils": "5.2.0",
- "@twemoji/parser": "15.1.1",
- "@vitejs/plugin-vue": "5.2.4",
- "@vue/compiler-sfc": "3.5.17",
+ "@twemoji/parser": "16.0.0",
+ "@vitejs/plugin-vue": "6.0.1",
+ "@vue/compiler-sfc": "3.5.19",
"astring": "1.9.0",
"buraha": "0.0.1",
"estree-walker": "3.0.3",
- "icons-subsetter": "workspace:*",
"frontend-shared": "workspace:*",
+ "icons-subsetter": "workspace:*",
"json5": "2.2.3",
- "mfm-js": "0.24.0",
+ "mfm-js": "0.25.0",
"misskey-js": "workspace:*",
"punycode.js": "2.3.1",
- "rollup": "4.45.1",
- "sass": "1.89.2",
- "shiki": "3.8.0",
+ "rollup": "4.48.0",
+ "sass": "1.90.0",
+ "shiki": "3.11.0",
"tinycolor2": "1.6.0",
"tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0",
- "typescript": "5.8.3",
+ "typescript": "5.9.2",
"uuid": "11.1.0",
- "vite": "6.3.5",
- "vue": "3.5.17"
+ "vite": "7.1.3",
+ "vue": "3.5.19"
},
"devDependencies": {
- "@misskey-dev/summaly": "5.2.2",
- "@tabler/icons-webfont": "3.34.0",
+ "@misskey-dev/summaly": "5.2.3",
+ "@tabler/icons-webfont": "3.34.1",
"@testing-library/vue": "8.1.0",
"@types/estree": "1.0.8",
"@types/micromatch": "4.0.9",
- "@types/node": "22.16.4",
+ "@types/node": "22.17.2",
"@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1",
- "@typescript-eslint/eslint-plugin": "8.37.0",
- "@typescript-eslint/parser": "8.37.0",
+ "@typescript-eslint/eslint-plugin": "8.40.0",
+ "@typescript-eslint/parser": "8.40.0",
"@vitest/coverage-v8": "3.2.4",
- "@vue/runtime-core": "3.5.17",
+ "@vue/runtime-core": "3.5.19",
"acorn": "8.15.0",
- "cross-env": "7.0.3",
+ "cross-env": "10.0.0",
"eslint-plugin-import": "2.32.0",
- "eslint-plugin-vue": "10.3.0",
+ "eslint-plugin-vue": "10.4.0",
"fast-glob": "3.3.3",
- "happy-dom": "17.6.3",
+ "happy-dom": "18.0.1",
"intersection-observer": "0.12.2",
"micromatch": "4.0.8",
- "msw": "2.10.4",
+ "msw": "2.10.5",
"nodemon": "3.1.10",
"prettier": "3.6.2",
- "start-server-and-test": "2.0.12",
+ "start-server-and-test": "2.0.13",
+ "tsx": "4.20.4",
"vite-plugin-turbosnap": "1.0.3",
- "vue-component-type-helpers": "2.2.12",
+ "vue-component-type-helpers": "3.0.6",
"vue-eslint-parser": "10.2.0",
- "vue-tsc": "2.2.12"
+ "vue-tsc": "3.0.6"
}
}
diff --git a/packages/frontend-embed/src/boot.ts b/packages/frontend-embed/src/boot.ts
index 459b283e23..9d69437c30 100644
--- a/packages/frontend-embed/src/boot.ts
+++ b/packages/frontend-embed/src/boot.ts
@@ -17,15 +17,16 @@ import { createApp, defineAsyncComponent } from 'vue';
import defaultLightTheme from '@@/themes/l-light.json5';
import defaultDarkTheme from '@@/themes/d-dark.json5';
import { MediaProxy } from '@@/js/media-proxy.js';
+import { storeBootloaderErrors } from '@@/js/store-boot-errors';
import { applyTheme, assertIsTheme } from '@/theme.js';
import { fetchCustomEmojis } from '@/custom-emojis.js';
import { DI } from '@/di.js';
import { serverMetadata } from '@/server-metadata.js';
-import { url, version, locale, lang, updateLocale } from '@@/js/config.js';
+import { url, version, lang } from '@@/js/config.js';
import { parseEmbedParams } from '@@/js/embed-page.js';
import { postMessageToParentWindow, setIframeId } from '@/post-message.js';
import { serverContext } from '@/server-context.js';
-import { i18n, updateI18n } from '@/i18n.js';
+import { i18n } from '@/i18n.js';
import type { Theme } from '@/theme.js';
@@ -76,19 +77,7 @@ if (embedParams.colorMode === 'dark') {
//#endregion
//#region Detect language & fetch translations
-const localeVersion = localStorage.getItem('localeVersion');
-const localeOutdated = (localeVersion == null || localeVersion !== version || locale == null);
-if (localeOutdated) {
- const res = await window.fetch(`/assets/locales/${lang}.${version}.json`);
- if (res.status === 200) {
- const newLocale = await res.text();
- const parsedNewLocale = JSON.parse(newLocale);
- localStorage.setItem('locale', newLocale);
- localStorage.setItem('localeVersion', version);
- updateLocale(parsedNewLocale);
- updateI18n(parsedNewLocale);
- }
-}
+storeBootloaderErrors({ ...i18n.ts._bootErrors, reload: i18n.ts.reload });
//#endregion
// サイズã®åˆ¶é™
diff --git a/packages/frontend-embed/src/i18n.ts b/packages/frontend-embed/src/i18n.ts
index 6ad503b089..0b2b206b7e 100644
--- a/packages/frontend-embed/src/i18n.ts
+++ b/packages/frontend-embed/src/i18n.ts
@@ -5,11 +5,12 @@
import { markRaw } from 'vue';
import { I18n } from '@@/js/i18n.js';
+import { locale } from '@@/js/locale.js';
import type { Locale } from '../../../locales/index.js';
-import { locale } from '@@/js/config.js';
export const i18n = markRaw(new I18n<Locale>(locale, _DEV_));
+// test 以外ã§ã¯ä½¿ã‚ãªã„ã“ã¨ã€‚インライン化ã•れã¦ã‚‹ã®ã§ã ã„ãŸã„æ„味ãŒãªã„
export function updateI18n(newLocale: Locale) {
i18n.locale = newLocale;
}
diff --git a/packages/frontend-embed/src/theme.ts b/packages/frontend-embed/src/theme.ts
index 680ab80167..c9b1c0d0c6 100644
--- a/packages/frontend-embed/src/theme.ts
+++ b/packages/frontend-embed/src/theme.ts
@@ -3,6 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
+// TODO: (å¯èƒ½ãªéƒ¨åˆ†ã‚’)sharedã«æŠ½å‡ºã—㦠frontend ã¨å…±é€šåŒ–
+
import tinycolor from 'tinycolor2';
import lightTheme from '@@/themes/_light.json5';
import darkTheme from '@@/themes/_dark.json5';
diff --git a/packages/frontend-embed/src/workers/draw-blurhash.ts b/packages/frontend-embed/src/workers/draw-blurhash.ts
index 22de6cd3a8..6e49f6bf66 100644
--- a/packages/frontend-embed/src/workers/draw-blurhash.ts
+++ b/packages/frontend-embed/src/workers/draw-blurhash.ts
@@ -18,5 +18,5 @@ onmessage = (event) => {
render(event.data.hash, canvas);
const bitmap = canvas.transferToImageBitmap();
- postMessage({ id: event.data.id, bitmap });
+ postMessage({ id: event.data.id, bitmap }, [bitmap]);
};
diff --git a/packages/frontend-embed/vite.config.ts b/packages/frontend-embed/vite.config.ts
index a057581b3a..3ddee9b8a9 100644
--- a/packages/frontend-embed/vite.config.ts
+++ b/packages/frontend-embed/vite.config.ts
@@ -8,6 +8,7 @@ import locales from '../../locales/index.js';
import meta from '../../package.json';
import packageInfo from './package.json' with { type: 'json' };
import pluginJson5 from './vite.json5.js';
+import { pluginRemoveUnrefI18n } from '../frontend-builder/rollup-plugin-remove-unref-i18n';
const url = process.env.NODE_ENV === 'development' ? yaml.load(await fsp.readFile('../../.config/default.yml', 'utf-8')).url : null;
const host = url ? (new URL(url)).hostname : undefined;
@@ -85,6 +86,7 @@ export function getConfig(): UserConfig {
plugins: [
pluginVue(),
+ pluginRemoveUnrefI18n(),
pluginJson5(),
],
@@ -135,15 +137,20 @@ export function getConfig(): UserConfig {
manifest: 'manifest.json',
rollupOptions: {
input: {
- app: './src/boot.ts',
+ i18n: './src/i18n.ts',
+ entry: './src/boot.ts',
},
external: externalPackages.map(p => p.match),
+ preserveEntrySignatures: 'allow-extension',
output: {
manualChunks: {
vue: ['vue'],
+ // dependencies of i18n.ts
+ 'config': ['@@/js/config.js'],
},
- chunkFileNames: process.env.NODE_ENV === 'production' ? '[hash:8].js' : '[name]-[hash:8].js',
- assetFileNames: process.env.NODE_ENV === 'production' ? '[hash:8][extname]' : '[name]-[hash:8][extname]',
+ entryFileNames: 'scripts/[hash:8].js',
+ chunkFileNames: 'scripts/[hash:8].js',
+ assetFileNames: 'assets/[hash:8][extname]',
paths(id) {
for (const p of externalPackages) {
if (p.match.test(id)) {
diff --git a/packages/frontend-shared/js/config.ts b/packages/frontend-shared/js/config.ts
index 26dd36d6c3..ac5c5629f3 100644
--- a/packages/frontend-shared/js/config.ts
+++ b/packages/frontend-shared/js/config.ts
@@ -3,8 +3,6 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import type { Locale } from '../../../locales/index.js';
-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const address = new URL(document.querySelector<HTMLMetaElement>('meta[property="instance_url"]')?.content || location.href);
const siteName = document.querySelector<HTMLMetaElement>('meta[property="og:site_name"]')?.content;
@@ -17,13 +15,8 @@ export const apiUrl = location.origin + '/api';
export const wsOrigin = location.origin;
export const lang = localStorage.getItem('lang') ?? 'en-US';
export const langs = _LANGS_;
-const preParseLocale = localStorage.getItem('locale');
-export let locale: Locale = preParseLocale ? JSON.parse(preParseLocale) : null;
export const version = _VERSION_;
export const instanceName = (siteName === 'Misskey' || siteName == null) ? host : siteName;
export const ui = localStorage.getItem('ui');
export const debug = localStorage.getItem('debug') === 'true';
-
-export function updateLocale(newLocale: Locale): void {
- locale = newLocale;
-}
+export const isSafeMode = localStorage.getItem('isSafeMode') === 'true';
diff --git a/packages/frontend-shared/js/const.ts b/packages/frontend-shared/js/const.ts
index 5c33c38f44..b2d83fff8b 100644
--- a/packages/frontend-shared/js/const.ts
+++ b/packages/frontend-shared/js/const.ts
@@ -88,6 +88,7 @@ export const ROLE_POLICIES = [
'canManageCustomEmojis',
'canManageAvatarDecorations',
'canSearchNotes',
+ 'canSearchUsers',
'canUseTranslator',
'canHideAds',
'driveCapacityMb',
diff --git a/packages/frontend-shared/js/emojilist.json b/packages/frontend-shared/js/emojilist.json
index 75d5c34d71..19004f1bcc 100644
--- a/packages/frontend-shared/js/emojilist.json
+++ b/packages/frontend-shared/js/emojilist.json
@@ -44,6 +44,8 @@
["😑", "expressionless", 0],
["😒", "unamused", 0],
["🙄", "roll_eyes", 0],
+ ["🙂â€â†”ï¸", "head_shaking_horizontally", 0],
+ ["🙂â€â†•ï¸", "head_shaking_vertically", 0],
["🤔", "thinking", 0],
["🤥", "lying_face", 0],
["🤭", "hand_over_mouth", 0],
@@ -76,6 +78,7 @@
["😥", "disappointed_relieved", 0],
["🤤", "drooling_face", 0],
["😪", "sleepy", 0],
+ ["🫩", "face_with_bags_under_eyes", 0],
["😓", "sweat", 0],
["🥵", "hot", 0],
["🥶", "cold", 0],
@@ -92,6 +95,7 @@
["🥴", "woozy", 0],
["🥱", "yawning", 0],
["😴", "sleeping", 0],
+ ["ðŸ‘ï¸â€ðŸ—¨ï¸", "eye_in_speech_bubble", 0],
["💤", "zzz", 0],
["😶â€ðŸŒ«ï¸", "face_in_clouds", 0],
["😮â€ðŸ’¨", "face_exhaling", 0],
@@ -185,6 +189,7 @@
["👤", "bust_in_silhouette", 1],
["👥", "busts_in_silhouette", 1],
["🗣", "speaking_head", 1],
+ ["🫆", "fingerprint", 1],
["👶", "baby", 1],
["🧒", "child", 1],
["👦", "boy", 1],
@@ -198,8 +203,9 @@
["🧑â€ðŸ¦°", "red_hair", 1],
["👩â€ðŸ¦°", "red_hair_woman", 1],
["👨â€ðŸ¦°", "red_hair_man", 1],
+ ["👱", "blonde_person", 1],
["👱â€â™€ï¸", "blonde_woman", 1],
- ["👱", "blonde_man", 1],
+ ["👱â€â™‚ï¸", "blonde_man", 1],
["🧑â€ðŸ¦³", "white_hair", 1],
["👩â€ðŸ¦³", "white_hair_woman", 1],
["👨â€ðŸ¦³", "white_hair_man", 1],
@@ -207,21 +213,28 @@
["👩â€ðŸ¦²", "bald_woman", 1],
["👨â€ðŸ¦²", "bald_man", 1],
["🧔", "bearded_person", 1],
+ ["🧔â€â™€ï¸", "beard_woman", 1],
+ ["🧔â€â™‚ï¸", "beard_man", 1],
["🧓", "older_adult", 1],
["👴", "older_man", 1],
["👵", "older_woman", 1],
["👲", "man_with_gua_pi_mao", 1],
["🧕", "woman_with_headscarf", 1],
+ ["👳", "person_with_turban", 1],
["👳â€â™€ï¸", "woman_with_turban", 1],
- ["👳", "man_with_turban", 1],
+ ["👳â€â™‚ï¸", "man_with_turban", 1],
+ ["👮", "police_officer", 1],
["👮â€â™€ï¸", "policewoman", 1],
- ["👮", "policeman", 1],
+ ["👮â€â™‚ï¸", "policeman", 1],
+ ["👷", "construction_worker", 1],
["👷â€â™€ï¸", "construction_worker_woman", 1],
- ["👷", "construction_worker_man", 1],
+ ["👷â€â™‚ï¸", "construction_worker_man", 1],
+ ["💂", "guard", 1],
["💂â€â™€ï¸", "guardswoman", 1],
- ["💂", "guardsman", 1],
+ ["💂â€â™‚ï¸", "guardsman", 1],
+ ["🕵", "detective", 1],
["🕵ï¸â€â™€ï¸", "female_detective", 1],
- ["🕵", "male_detective", 1],
+ ["🕵ï¸â€â™‚ï¸", "male_detective", 1],
["🧑â€âš•ï¸", "health_worker", 1],
["👩â€âš•ï¸", "woman_health_worker", 1],
["👨â€âš•ï¸", "man_health_worker", 1],
@@ -270,26 +283,35 @@
["🧑â€âš–ï¸", "judge", 1],
["👩â€âš–ï¸", "woman_judge", 1],
["👨â€âš–ï¸", "man_judge", 1],
+ ["🦸", "superhero", 1],
["🦸â€â™€ï¸", "woman_superhero", 1],
["🦸â€â™‚ï¸", "man_superhero", 1],
+ ["🦹", "supervillain", 1],
["🦹â€â™€ï¸", "woman_supervillain", 1],
["🦹â€â™‚ï¸", "man_supervillain", 1],
["🤶", "mrs_claus", 1],
["🧑â€ðŸŽ„", "mx_claus", 1],
["🎅", "santa", 1],
["🥷", "ninja", 1],
+ ["🧙", "mage", 1],
["🧙â€â™€ï¸", "sorceress", 1],
["🧙â€â™‚ï¸", "wizard", 1],
+ ["ðŸ§", "elf", 1],
["ðŸ§â€â™€ï¸", "woman_elf", 1],
["ðŸ§â€â™‚ï¸", "man_elf", 1],
+ ["🧛", "vampire", 1],
["🧛â€â™€ï¸", "woman_vampire", 1],
["🧛â€â™‚ï¸", "man_vampire", 1],
+ ["🧟", "zombie", 1],
["🧟â€â™€ï¸", "woman_zombie", 1],
["🧟â€â™‚ï¸", "man_zombie", 1],
+ ["🧞", "genie", 1],
["🧞â€â™€ï¸", "woman_genie", 1],
["🧞â€â™‚ï¸", "man_genie", 1],
+ ["🧜", "merperson", 1],
["🧜â€â™€ï¸", "mermaid", 1],
["🧜â€â™‚ï¸", "merman", 1],
+ ["🧚", "fairy", 1],
["🧚â€â™€ï¸", "woman_fairy", 1],
["🧚â€â™‚ï¸", "man_fairy", 1],
["👼", "angel", 1],
@@ -305,68 +327,108 @@
["👸", "princess", 1],
["🤴", "prince", 1],
["👰", "person_with_veil", 1],
- ["👰", "bride_with_veil", 1],
+ ["👰â€â™€ï¸", "bride_with_veil", 1],
+ ["👰â€â™‚ï¸", "man_with_veil", 1],
["🤵", "person_in_tuxedo", 1],
- ["🤵", "man_in_tuxedo", 1],
+ ["🤵â€â™€ï¸", "woman_in_tuxedo", 1],
+ ["🤵â€â™‚ï¸", "man_in_tuxedo", 1],
+ ["ðŸƒ", "running_person", 1],
+ ["ðŸƒâ€âž¡ï¸", "running_person_facing_right", 1],
["ðŸƒâ€â™€ï¸", "running_woman", 1],
- ["ðŸƒ", "running_man", 1],
+ ["ðŸƒâ€â™€ï¸â€âž¡ï¸", "running_woman_facing_right", 1],
+ ["ðŸƒâ€â™‚ï¸", "running_man", 1],
+ ["ðŸƒâ€â™‚ï¸â€âž¡ï¸", "running_man_facing_right", 1],
+ ["🚶", "walking_person", 1],
+ ["🚶â€âž¡ï¸", "walking_person_facing_right", 1],
["🚶â€â™€ï¸", "walking_woman", 1],
- ["🚶", "walking_man", 1],
+ ["🚶â€â™€ï¸â€âž¡ï¸", "walking_woman_facing_right", 1],
+ ["🚶â€â™‚ï¸", "walking_man", 1],
+ ["🚶â€â™‚ï¸â€âž¡ï¸", "walking_man_facing_right", 1],
["💃", "dancer", 1],
["🕺", "man_dancing", 1],
- ["👯", "dancing_women", 1],
+ ["👯", "dancing_people", 1],
+ ["👯â€â™€ï¸", "dancing_women", 1],
["👯â€â™‚ï¸", "dancing_men", 1],
["👫", "couple", 1],
["🧑â€ðŸ¤â€ðŸ§‘", "people_holding_hands", 1],
["👬", "two_men_holding_hands", 1],
["👭", "two_women_holding_hands", 1],
["🫂", "people_hugging", 1],
+ ["🙇", "bowing_person", 1],
["🙇â€â™€ï¸", "bowing_woman", 1],
- ["🙇", "bowing_man", 1],
+ ["🙇â€â™‚ï¸", "bowing_man", 1],
+ ["🤦", "person_facepalming", 1],
["🤦â€â™‚ï¸", "man_facepalming", 1],
["🤦â€â™€ï¸", "woman_facepalming", 1],
- ["🤷", "woman_shrugging", 1],
+ ["🤷", "person_shrugging", 1],
+ ["🤷â€â™€ï¸", "woman_shrugging", 1],
["🤷â€â™‚ï¸", "man_shrugging", 1],
- ["ðŸ’", "tipping_hand_woman", 1],
+ ["ðŸ’", "tipping_hand_person", 1],
+ ["ðŸ’â€â™€ï¸", "tipping_hand_woman", 1],
["ðŸ’â€â™‚ï¸", "tipping_hand_man", 1],
- ["🙅", "no_good_woman", 1],
+ ["🙅", "no_good_person", 1],
+ ["🙅â€â™€ï¸", "no_good_woman", 1],
["🙅â€â™‚ï¸", "no_good_man", 1],
- ["🙆", "ok_woman", 1],
+ ["🙆", "ok_person", 1],
+ ["🙆â€â™€ï¸", "ok_woman", 1],
["🙆â€â™‚ï¸", "ok_man", 1],
- ["🙋", "raising_hand_woman", 1],
+ ["🙋", "raising_hand_person", 1],
+ ["🙋â€â™€ï¸", "raising_hand_woman", 1],
["🙋â€â™‚ï¸", "raising_hand_man", 1],
- ["🙎", "pouting_woman", 1],
+ ["🙎", "pouting_person", 1],
+ ["🙎â€â™€ï¸", "pouting_woman", 1],
["🙎â€â™‚ï¸", "pouting_man", 1],
- ["ðŸ™", "frowning_woman", 1],
+ ["ðŸ™", "frowning_person", 1],
+ ["ðŸ™â€â™€ï¸", "frowning_woman", 1],
["ðŸ™â€â™‚ï¸", "frowning_man", 1],
- ["💇", "haircut_woman", 1],
+ ["💇", "haircut_person", 1],
+ ["💇â€â™€ï¸", "haircut_woman", 1],
["💇â€â™‚ï¸", "haircut_man", 1],
- ["💆", "massage_woman", 1],
+ ["💆", "massage_person", 1],
+ ["💆â€â™€ï¸", "massage_woman", 1],
["💆â€â™‚ï¸", "massage_man", 1],
+ ["🧖", "person_in_steamy_room", 1],
["🧖â€â™€ï¸", "woman_in_steamy_room", 1],
["🧖â€â™‚ï¸", "man_in_steamy_room", 1],
+ ["ðŸ§", "person_deaf", 1],
["ðŸ§â€â™€ï¸", "woman_deaf", 1],
["ðŸ§â€â™‚ï¸", "man_deaf", 1],
+ ["ðŸ§", "person_standing", 1],
["ðŸ§â€â™€ï¸", "woman_standing", 1],
["ðŸ§â€â™‚ï¸", "man_standing", 1],
+ ["🧎", "person_kneeling", 1],
+ ["🧎â€âž¡ï¸", "person_kneeling_facing_right", 1],
["🧎â€â™€ï¸", "woman_kneeling", 1],
+ ["🧎â€â™€ï¸â€âž¡ï¸", "woman_kneeling_facing_right", 1],
["🧎â€â™‚ï¸", "man_kneeling", 1],
+ ["🧎â€â™‚ï¸â€âž¡ï¸", "man_kneeling_facing_right", 1],
["🧑â€ðŸ¦¯", "person_with_probing_cane", 1],
+ ["🧑â€ðŸ¦¯â€âž¡ï¸", "person_with_probing_cane_facing_right", 1],
["👩â€ðŸ¦¯", "woman_with_probing_cane", 1],
+ ["👩â€ðŸ¦¯â€âž¡ï¸", "woman_with_probing_cane_facing_right", 1],
["👨â€ðŸ¦¯", "man_with_probing_cane", 1],
+ ["👨â€ðŸ¦¯â€âž¡ï¸", "man_with_probing_cane_facing_right", 1],
["🧑â€ðŸ¦¼", "person_in_motorized_wheelchair", 1],
+ ["🧑â€ðŸ¦¼â€âž¡ï¸", "person_in_motorized_wheelchair_facing_right", 1],
["👩â€ðŸ¦¼", "woman_in_motorized_wheelchair", 1],
+ ["👩â€ðŸ¦¼â€âž¡ï¸", "woman_in_motorized_wheelchair_facing_right", 1],
["👨â€ðŸ¦¼", "man_in_motorized_wheelchair", 1],
+ ["👨â€ðŸ¦¼â€âž¡ï¸", "man_in_motorized_wheelchair_facing_right", 1],
["🧑â€ðŸ¦½", "person_in_manual_wheelchair", 1],
+ ["🧑â€ðŸ¦½â€âž¡ï¸", "person_in_manual_wheelchair_facing_right", 1],
["👩â€ðŸ¦½", "woman_in_manual_wheelchair", 1],
+ ["👩â€ðŸ¦½â€âž¡ï¸", "woman_in_manual_wheelchair_facing_right", 1],
["👨â€ðŸ¦½", "man_in_manual_wheelchair", 1],
- ["💑", "couple_with_heart_woman_man", 1],
+ ["👨â€ðŸ¦½â€âž¡ï¸", "man_in_manual_wheelchair_facing_right", 1],
+ ["💑", "couple_with_heart", 1],
+ ["👩â€â¤ï¸â€ðŸ‘¨", "couple_with_heart_woman_man", 1],
["👩â€â¤ï¸â€ðŸ‘©", "couple_with_heart_woman_woman", 1],
["👨â€â¤ï¸â€ðŸ‘¨", "couple_with_heart_man_man", 1],
- ["ðŸ’", "couplekiss_man_woman", 1],
+ ["ðŸ’", "couplekiss", 1],
+ ["👩â€â¤ï¸â€ðŸ’‹â€ðŸ‘¨", "couplekiss_woman_man", 1],
["👩â€â¤ï¸â€ðŸ’‹â€ðŸ‘©", "couplekiss_woman_woman", 1],
["👨â€â¤ï¸â€ðŸ’‹â€ðŸ‘¨", "couplekiss_man_man", 1],
- ["👪", "family_man_woman_boy", 1],
+ ["👨â€ðŸ‘©â€ðŸ‘¦", "family_man_woman_boy", 1],
["👨â€ðŸ‘©â€ðŸ‘§", "family_man_woman_girl", 1],
["👨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦", "family_man_woman_girl_boy", 1],
["👨â€ðŸ‘©â€ðŸ‘¦â€ðŸ‘¦", "family_man_woman_boy_boy", 1],
@@ -391,6 +453,11 @@
["👨â€ðŸ‘§â€ðŸ‘¦", "family_man_girl_boy", 1],
["👨â€ðŸ‘¦â€ðŸ‘¦", "family_man_boy_boy", 1],
["👨â€ðŸ‘§â€ðŸ‘§", "family_man_girl_girl", 1],
+ ["👪", "family", 1],
+ ["🧑â€ðŸ§‘â€ðŸ§’", "family_adult_adult_child", 1],
+ ["🧑â€ðŸ§‘â€ðŸ§’â€ðŸ§’", "family_adult_adult_child_child", 1],
+ ["🧑â€ðŸ§’", "family_adult_child", 1],
+ ["🧑â€ðŸ§’â€ðŸ§’", "family_adult_child_child", 1],
["🧶", "yarn", 1],
["🧵", "thread", 1],
["🧥", "coat", 1],
@@ -475,6 +542,7 @@
["ðŸ¦â€â¬›", "black_bird", 2],
["🦅", "eagle", 2],
["🦉", "owl", 2],
+ ["ðŸ¦â€ðŸ”¥", "phoenix", 2],
["🦇", "bat", 2],
["ðŸº", "wolf", 2],
["ðŸ—", "boar", 2],
@@ -575,6 +643,7 @@
["🌿", "herb", 2],
["☘", "shamrock", 2],
["ðŸ€", "four_leaf_clover", 2],
+ ["🪾", "leafless_tree", 2],
["ðŸŽ", "bamboo", 2],
["🎋", "tanabata_tree", 2],
["ðŸƒ", "leaves", 2],
@@ -648,6 +717,7 @@
["🪸", "coral", 2],
["🪹", "empty_nest", 2],
["🪺", "nest_with_eggs", 2],
+ ["ðŸ‹â€ðŸŸ©", "lime", 3],
["ðŸ", "green_apple", 3],
["ðŸŽ", "apple", 3],
["ðŸ", "pear", 3],
@@ -667,6 +737,8 @@
["🥑", "avocado", 3],
["🫛", "pea_pod", 3],
["🥦", "broccoli", 3],
+ ["ðŸ„â€ðŸŸ«", "brown_mushroom", 3],
+ ["🫜", "root_vegetable", 3],
["ðŸ…", "tomato", 3],
["ðŸ†", "eggplant", 3],
["🥒", "cucumber", 3],
@@ -786,8 +858,9 @@
["ðŸ¥", "flying_disc", 4],
["🎱", "8ball", 4],
["⛳", "golf", 4],
+ ["ðŸŒ", "golfing_person", 4],
["ðŸŒï¸â€â™€ï¸", "golfing_woman", 4],
- ["ðŸŒ", "golfing_man", 4],
+ ["ðŸŒï¸â€â™‚ï¸", "golfing_man", 4],
["ðŸ“", "ping_pong", 4],
["ðŸ¸", "badminton", 4],
["🥅", "goal_net", 4],
@@ -799,10 +872,13 @@
["â›·", "skier", 4],
["ðŸ‚", "snowboarder", 4],
["🤺", "person_fencing", 4],
+ ["🤼", "people_wrestling", 4],
["🤼â€â™€ï¸", "women_wrestling", 4],
["🤼â€â™‚ï¸", "men_wrestling", 4],
+ ["🤸", "person_cartwheeling", 4],
["🤸â€â™€ï¸", "woman_cartwheeling", 4],
["🤸â€â™‚ï¸", "man_cartwheeling", 4],
+ ["🤾", "person_playing_handball", 4],
["🤾â€â™€ï¸", "woman_playing_handball", 4],
["🤾â€â™‚ï¸", "man_playing_handball", 4],
["⛸", "ice_skate", 4],
@@ -813,27 +889,37 @@
["🎣", "fishing_pole_and_fish", 4],
["🥊", "boxing_glove", 4],
["🥋", "martial_arts_uniform", 4],
+ ["🚣", "rowing_person", 4],
["🚣â€â™€ï¸", "rowing_woman", 4],
- ["🚣", "rowing_man", 4],
+ ["🚣â€â™‚ï¸", "rowing_man", 4],
+ ["🧗", "climbing_person", 4],
["🧗â€â™€ï¸", "climbing_woman", 4],
["🧗â€â™‚ï¸", "climbing_man", 4],
+ ["ðŸŠ", "swimming_person", 4],
["ðŸŠâ€â™€ï¸", "swimming_woman", 4],
- ["ðŸŠ", "swimming_man", 4],
+ ["ðŸŠâ€â™‚ï¸", "swimming_man", 4],
+ ["🤽", "person_playing_water_polo", 4],
["🤽â€â™€ï¸", "woman_playing_water_polo", 4],
["🤽â€â™‚ï¸", "man_playing_water_polo", 4],
+ ["🧘", "person_in_lotus_position", 4],
["🧘â€â™€ï¸", "woman_in_lotus_position", 4],
["🧘â€â™‚ï¸", "man_in_lotus_position", 4],
+ ["ðŸ„", "surfing_person", 4],
["ðŸ„â€â™€ï¸", "surfing_woman", 4],
- ["ðŸ„", "surfing_man", 4],
+ ["ðŸ„â€â™‚ï¸", "surfing_man", 4],
["🛀", "bath", 4],
+ ["⛹", "basketball_person", 4],
["⛹ï¸â€â™€ï¸", "basketball_woman", 4],
- ["⛹", "basketball_man", 4],
+ ["⛹ï¸â€â™‚ï¸", "basketball_man", 4],
+ ["ðŸ‹", "weight_lifting_person", 4],
["ðŸ‹ï¸â€â™€ï¸", "weight_lifting_woman", 4],
- ["ðŸ‹", "weight_lifting_man", 4],
+ ["ðŸ‹ï¸â€â™‚ï¸", "weight_lifting_man", 4],
+ ["🚴", "biking_person", 4],
["🚴â€â™€ï¸", "biking_woman", 4],
- ["🚴", "biking_man", 4],
+ ["🚴â€â™‚ï¸", "biking_man", 4],
+ ["🚵", "mountain_biking_person", 4],
["🚵â€â™€ï¸", "mountain_biking_woman", 4],
- ["🚵", "mountain_biking_man", 4],
+ ["🚵â€â™‚ï¸", "mountain_biking_man", 4],
["ðŸ‡", "horse_racing", 4],
["🤿", "diving_mask", 4],
["🪀", "yo_yo", 4],
@@ -856,6 +942,7 @@
["🎭", "performing_arts", 4],
["🎨", "art", 4],
["🎪", "circus_tent", 4],
+ ["🤹", "person_juggling", 4],
["🤹â€â™€ï¸", "woman_juggling", 4],
["🤹â€â™‚ï¸", "man_juggling", 4],
["🎤", "microphone", 4],
@@ -872,6 +959,7 @@
["🪕", "banjo", 4],
["🪗", "accordion", 4],
["🪘", "long_drum", 4],
+ ["🪉", "harp", 4],
["🎬", "clapper", 4],
["🎮", "video_game", 4],
["👾", "space_invader", 4],
@@ -1076,8 +1164,10 @@
["🪙", "coin", 6],
["💳", "credit_card", 6],
["🪪", "identification_card", 6],
+ ["🥾", "hiking_boot", 6],
["💎", "gem", 6],
["âš–", "balance_scale", 6],
+ ["⛓ï¸â€ðŸ’¥", "broken_chain", 6],
["🧰", "toolbox", 6],
["🔧", "wrench", 6],
["🔨", "hammer", 6],
@@ -1093,6 +1183,7 @@
["🪛", "screwdriver", 6],
["ðŸª", "hook", 6],
["🪜", "ladder", 6],
+ ["ðŸª", "shovel", 6],
["🧱", "brick", 6],
["⛓", "chains", 6],
["🧲", "magnet", 6],
@@ -1304,6 +1395,8 @@
["♓", "pisces", 7],
["🆔", "id", 7],
["âš›", "atom_symbol", 7],
+ ["♀ï¸", "female_sign", 7],
+ ["♂ï¸", "male_sign", 7],
["âš§ï¸", "transgender_symbol", 7],
["🈳", "u7a7a", 7],
["🈹", "u5272", 7],
@@ -1463,9 +1556,11 @@
["♾", "infinity", 7],
["💲", "heavy_dollar_sign", 7],
["💱", "currency_exchange", 7],
+ ["âš•ï¸", "medical_symbol", 7],
["©ï¸", "copyright", 7],
["®ï¸", "registered", 7],
["â„¢ï¸", "tm", 7],
+ ["🫟", "splatter", 7],
["🔚", "end", 7],
["🔙", "back", 7],
["🔛", "on", 7],
@@ -1576,6 +1671,7 @@
["🇧🇲", "bermuda", 8],
["🇧🇹", "bhutan", 8],
["🇧🇴", "bolivia", 8],
+ ["🇧🇻", "bouvet_island", 8],
["🇧🇶", "caribbean_netherlands", 8],
["🇧🇦", "bosnia_herzegovina", 8],
["🇧🇼", "botswana", 8],
@@ -1593,10 +1689,12 @@
["🇮🇨", "canary_islands", 8],
["🇰🇾", "cayman_islands", 8],
["🇨🇫", "central_african_republic", 8],
+ ["🇪🇦", "ceuta_melilla", 8],
["🇹🇩", "chad", 8],
["🇨🇱", "chile", 8],
["🇨🇳", "cn", 8],
["🇨🇽", "christmas_island", 8],
+ ["🇨🇵", "clipperton_island", 8],
["🇨🇨", "cocos_islands", 8],
["🇨🇴", "colombia", 8],
["🇰🇲", "comoros", 8],
@@ -1610,6 +1708,7 @@
["🇨🇾", "cyprus", 8],
["🇨🇿", "czech_republic", 8],
["🇩🇰", "denmark", 8],
+ ["🇩🇬", "diego_garcia", 8],
["🇩🇯", "djibouti", 8],
["🇩🇲", "dominica", 8],
["🇩🇴", "dominican_republic", 8],
@@ -1646,6 +1745,7 @@
["🇬🇼", "guinea_bissau", 8],
["🇬🇾", "guyana", 8],
["🇭🇹", "haiti", 8],
+ ["🇭🇲", "heard_mcdonald_islands", 8],
["🇭🇳", "honduras", 8],
["🇭🇰", "hong_kong", 8],
["🇭🇺", "hungary", 8],
@@ -1733,10 +1833,12 @@
["🇷🇴", "romania", 8],
["🇷🇺", "ru", 8],
["🇷🇼", "rwanda", 8],
+ ["🇨🇶", "sark", 8],
["🇧🇱", "st_barthelemy", 8],
["🇸🇭", "st_helena", 8],
["🇰🇳", "st_kitts_nevis", 8],
["🇱🇨", "st_lucia", 8],
+ ["🇲🇫", "st_martin", 8],
["🇵🇲", "st_pierre_miquelon", 8],
["🇻🇨", "st_vincent_grenadines", 8],
["🇼🇸", "samoa", 8],
@@ -1762,6 +1864,7 @@
["🇸🇩", "sudan", 8],
["🇸🇷", "suriname", 8],
["🇸🇿", "swaziland", 8],
+ ["🇸🇯", "svalbard_jan_mayen", 8],
["🇸🇪", "sweden", 8],
["🇨🇭", "switzerland", 8],
["🇸🇾", "syria", 8],
@@ -1788,6 +1891,7 @@
["ðŸ´ó §ó ¢ó ³ó £ó ´ó ¿", "scotland", 8],
["ðŸ´ó §ó ¢ó ·ó ¬ó ³ó ¿", "wales", 8],
["🇺🇸", "us", 8],
+ ["🇺🇲", "us_outlying_islands", 8],
["🇻🇮", "us_virgin_islands", 8],
["🇺🇾", "uruguay", 8],
["🇺🇿", "uzbekistan", 8],
diff --git a/packages/frontend-shared/js/i18n.ts b/packages/frontend-shared/js/i18n.ts
index 25581b969a..3b103c4714 100644
--- a/packages/frontend-shared/js/i18n.ts
+++ b/packages/frontend-shared/js/i18n.ts
@@ -28,7 +28,8 @@ type ParametersOf<T extends ILocale, TKey extends FlattenKeys<T, ParameterizedSt
: never;
type Tsx<T extends ILocale> = {
- readonly [K in keyof T as T[K] extends string ? never : K]: T[K] extends ParameterizedString<infer P>
+ // `string extends T[K] ? never : K` part removes non-parameterized string keys from Tsx type.
+ readonly [K in keyof T as string extends T[K] ? never : K]: T[K] extends ParameterizedString<infer P>
? (arg: { readonly [_ in P]: string | number }) => string
// @ts-expect-error -- 証明çœç•¥
: Tsx<T[K]>;
@@ -39,11 +40,7 @@ export class I18n<T extends ILocale> {
private devMode: boolean;
constructor(public locale: T, devMode = false) {
- // å ´åˆã«ã‚ˆã£ã¦ã¯ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚¢ãƒƒãƒ—å‰ã®ç¿»è¨³ãƒ‡ãƒ¼ã‚¿ã‚’å‚ç…§ã—ãŸçµæžœå­˜åœ¨ã—ãªã„プロパティã«ã‚¢ã‚¯ã‚»ã‚¹ã—ã¦ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆãŒèµ·å‹•ã§ããªããªã‚‹ã“ã¨ãŒã‚ã‚‹å•題ã®å¿œæ€¥å‡¦ç½®ã¨ã—ã¦éždevモードã§ã‚‚プロキシã™ã‚‹
- // TODO: https://github.com/misskey-dev/misskey/issues/14453 ãŒå®Ÿè£…ã•れãŸã‚‰ãã®ã‚ˆã†ãªã“ã¨ã¯ç™ºç”Ÿã—å¾—ãªããªã‚‹ãŸã‚消ã™
- const oukyuusyoti = true;
-
- this.devMode = devMode || oukyuusyoti;
+ this.devMode = devMode;
//#region BIND
this.t = this.t.bind(this);
diff --git a/packages/frontend-shared/js/locale.ts b/packages/frontend-shared/js/locale.ts
new file mode 100644
index 0000000000..87e3922fd9
--- /dev/null
+++ b/packages/frontend-shared/js/locale.ts
@@ -0,0 +1,14 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { lang, version } from '@@/js/config.js';
+import type { Locale } from '../../../locales/index.js';
+
+// ã“ã“ã¯ãƒ“ルド時㫠const locale = JSON.parse("...") ã¿ãŸã„ãªæ„Ÿã˜ã§ç½®ãæ›ãˆã‚‰ã‚Œã‚‹ã®ã§ top-level await ã¯æ¶ˆãˆã‚‹
+export let locale: Locale = await window.fetch(`/assets/locales/${lang}.${version}.json`).then(r => r.json(), () => null);
+
+export function updateLocale(newLocale: Locale): void {
+ locale = newLocale;
+}
diff --git a/packages/frontend-shared/js/store-boot-errors.ts b/packages/frontend-shared/js/store-boot-errors.ts
new file mode 100644
index 0000000000..31e6248445
--- /dev/null
+++ b/packages/frontend-shared/js/store-boot-errors.ts
@@ -0,0 +1,12 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import type { Locale } from '../../../locales/index.js';
+
+type BootLoaderLocaleBody = Locale['_bootErrors'] & { reload: Locale['reload'] };
+
+export function storeBootloaderErrors(locale: BootLoaderLocaleBody) {
+ localStorage.setItem('bootloaderLocales', JSON.stringify(locale));
+}
diff --git a/packages/frontend-shared/package.json b/packages/frontend-shared/package.json
index 7caa7fd283..ea2d66a7c7 100644
--- a/packages/frontend-shared/package.json
+++ b/packages/frontend-shared/package.json
@@ -21,13 +21,13 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
- "@types/node": "22.16.4",
- "@typescript-eslint/eslint-plugin": "8.37.0",
- "@typescript-eslint/parser": "8.37.0",
- "esbuild": "0.25.6",
- "eslint-plugin-vue": "10.3.0",
+ "@types/node": "22.17.2",
+ "@typescript-eslint/eslint-plugin": "8.40.0",
+ "@typescript-eslint/parser": "8.40.0",
+ "esbuild": "0.25.9",
+ "eslint-plugin-vue": "10.4.0",
"nodemon": "3.1.10",
- "typescript": "5.8.3",
+ "typescript": "5.9.2",
"vue-eslint-parser": "10.2.0"
},
"files": [
@@ -35,6 +35,6 @@
],
"dependencies": {
"misskey-js": "workspace:*",
- "vue": "3.5.17"
+ "vue": "3.5.19"
}
}
diff --git a/packages/frontend/.gitignore b/packages/frontend/.gitignore
index 1aa0ac14e8..4f8684c18e 100644
--- a/packages/frontend/.gitignore
+++ b/packages/frontend/.gitignore
@@ -1 +1,2 @@
/storybook-static
+/build/
diff --git a/packages/frontend/.storybook/charts.ts b/packages/frontend/.storybook/charts.ts
index 31bb9e51c5..93e1287d69 100644
--- a/packages/frontend/.storybook/charts.ts
+++ b/packages/frontend/.storybook/charts.ts
@@ -6,7 +6,7 @@
import { HttpResponse, http } from 'msw';
import type { DefaultBodyType, HttpResponseResolver, JsonBodyType, PathParams } from 'msw';
import seedrandom from 'seedrandom';
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
function getChartArray(seed: string, limit: number, option?: { accumulate?: boolean, mul?: number }): number[] {
const rng = seedrandom(seed);
diff --git a/packages/frontend/.storybook/mocks.ts b/packages/frontend/.storybook/mocks.ts
index 29cb112ccb..84d5ee6c33 100644
--- a/packages/frontend/.storybook/mocks.ts
+++ b/packages/frontend/.storybook/mocks.ts
@@ -34,7 +34,7 @@ export const commonHandlers = [
}),
http.get('/twemoji/:codepoints.svg', async ({ params }) => {
const { codepoints } = params;
- const value = await fetch(`https://unpkg.com/@discordapp/twemoji@15.0.2/dist/svg/${codepoints}.svg`).then((response) => response.blob());
+ const value = await fetch(`https://unpkg.com/@discordapp/twemoji@16.0.1/dist/svg/${codepoints}.svg`).then((response) => response.blob());
return new HttpResponse(value, {
headers: {
'Content-Type': 'image/svg+xml',
diff --git a/packages/frontend/.storybook/preview.ts b/packages/frontend/.storybook/preview.ts
index fb855c1410..2aac8af400 100644
--- a/packages/frontend/.storybook/preview.ts
+++ b/packages/frontend/.storybook/preview.ts
@@ -9,7 +9,6 @@ import { type Preview, setup } from '@storybook/vue3';
import isChromatic from 'chromatic/isChromatic';
import { initialize, mswLoader } from 'msw-storybook-addon';
import { userDetailed } from './fakes.js';
-import locale from './locale.js';
import { commonHandlers, onUnhandledRequest } from './mocks.js';
import themes from './themes.js';
import '../src/style.scss';
@@ -55,7 +54,6 @@ function initLocalStorage() {
...userDetailed(),
policies: {},
}));
- localStorage.setItem('locale', JSON.stringify(locale));
}
initialize({
diff --git a/packages/frontend/.vscode/storybook.code-snippets b/packages/frontend/.vscode/storybook.code-snippets
index 785d0a1608..d7063f7200 100644
--- a/packages/frontend/.vscode/storybook.code-snippets
+++ b/packages/frontend/.vscode/storybook.code-snippets
@@ -42,7 +42,7 @@
"prefix": "storyimplevent",
"body": [
"/* eslint-disable @typescript-eslint/explicit-function-return-type */",
- "import { action } from '@storybook/addon-actions';",
+ "import { action } from 'storybook/actions';",
"import { StoryObj } from '@storybook/vue3';",
"import $1 from './$1.vue';",
"export const Default = {",
diff --git a/packages/frontend/build.ts b/packages/frontend/build.ts
new file mode 100644
index 0000000000..0401c2b9ba
--- /dev/null
+++ b/packages/frontend/build.ts
@@ -0,0 +1,51 @@
+import * as fs from 'fs/promises';
+import url from 'node:url';
+import path from 'node:path';
+import { execa } from 'execa';
+import locales from '../../locales/index.js';
+import { LocaleInliner } from '../frontend-builder/locale-inliner.js'
+import { createLogger } from '../frontend-builder/logger';
+
+// requires node 21 or later
+const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
+const outputDir = __dirname + '/../../built/_frontend_vite_';
+
+/**
+ * @return {Promise<void>}
+ */
+async function viteBuild() {
+ await execa('vite', ['build'], {
+ cwd: __dirname,
+ stdout: process.stdout,
+ stderr: process.stderr,
+ });
+}
+
+
+async function buildAllLocale() {
+ const logger = createLogger()
+ const inliner = await LocaleInliner.create({
+ outputDir,
+ logger,
+ scriptsDir: 'scripts',
+ i18nFile: 'src/i18n.ts',
+ })
+
+ await inliner.loadFiles();
+
+ inliner.collectsModifications();
+
+ await inliner.saveAllLocales(locales);
+
+ if (logger.errorCount > 0) {
+ throw new Error(`Build failed with ${logger.errorCount} errors and ${logger.warningCount} warnings.`);
+ }
+}
+
+async function build() {
+ await fs.rm(outputDir, { recursive: true, force: true });
+ await viteBuild();
+ await buildAllLocale();
+}
+
+await build();
diff --git a/packages/frontend/lib/vite-plugin-create-search-index.ts b/packages/frontend/lib/vite-plugin-create-search-index.ts
index 97f4e589a3..f17b43b0e3 100644
--- a/packages/frontend/lib/vite-plugin-create-search-index.ts
+++ b/packages/frontend/lib/vite-plugin-create-search-index.ts
@@ -8,7 +8,7 @@
import { parse as vueSfcParse } from 'vue/compiler-sfc';
import {
createLogger,
- EnvironmentModuleGraph,
+ type EnvironmentModuleGraph,
type LogErrorOptions,
type LogOptions,
normalizePath,
@@ -39,6 +39,7 @@ export interface SearchIndexItem {
path?: string;
label: string;
keywords: string[];
+ texts: string[];
icon?: string;
inlining?: string[];
}
@@ -227,14 +228,14 @@ function extractElementText2Inner(node: TemplateChildNode, processingNodeName: s
// region extractUsageInfoFromTemplateAst
/**
- * SearchLabel/SearchKeyword/SearchIconを探ã—ã¦æŠ½å‡ºã™ã‚‹é–¢æ•°
+ * SearchLabel/SearchText/SearchIconを探ã—ã¦æŠ½å‡ºã™ã‚‹é–¢æ•°
*/
-function extractSugarTags(nodes: TemplateChildNode[], id: string): { label: string | null, keywords: string[], icon: string | null } {
+function extractSugarTags(nodes: TemplateChildNode[], id: string): { label: string | null; texts: string[]; icon: string | null; } {
let label: string | null | undefined = undefined;
let icon: string | null | undefined = undefined;
- const keywords: string[] = [];
+ const texts: string[] = [];
- logger.info(`Extracting labels and keywords from ${nodes.length} nodes`);
+ logger.info(`Extracting labels and texts from ${nodes.length} nodes`);
walkVueElements(nodes, null, (node) => {
switch (node.tag) {
@@ -248,10 +249,10 @@ function extractSugarTags(nodes: TemplateChildNode[], id: string): { label: stri
label = extractElementText(node, id);
return;
- case 'SearchKeyword':
+ case 'SearchText':
const content = extractElementText(node, id);
if (content) {
- keywords.push(content);
+ texts.push(content);
}
return;
case 'SearchIcon':
@@ -278,8 +279,8 @@ function extractSugarTags(nodes: TemplateChildNode[], id: string): { label: stri
});
// デãƒãƒƒã‚°æƒ…å ±
- logger.info(`Extraction completed: label=${label}, keywords=[${keywords.join(', ')}, icon=${icon}]`);
- return { label: label ?? null, keywords, icon: icon ?? null };
+ logger.info(`Extraction completed: label=${label}, text=[${texts.join(', ')}, icon=${icon}]`);
+ return { label: label ?? null, texts, icon: icon ?? null };
}
function getStringProp(attr: AttributeNode | DirectiveNode | null, id: string): string | null {
@@ -351,33 +352,36 @@ function extractUsageInfoFromTemplateAst(
parentId: parentId ?? undefined,
label: '', // デフォルト値
keywords: [],
+ texts: [],
};
// ãƒã‚¤ãƒ³ãƒ‰ãƒ—ロパティをå–å¾—
- const path = getStringProp(findAttribute(node.props, 'path'), id)
- const icon = getStringProp(findAttribute(node.props, 'icon'), id)
- const label = getStringProp(findAttribute(node.props, 'label'), id)
- const inlining = getStringArrayProp(findAttribute(node.props, 'inlining'), id)
- const keywords = getStringArrayProp(findAttribute(node.props, 'keywords'), id)
+ const path = getStringProp(findAttribute(node.props, 'path'), id);
+ const icon = getStringProp(findAttribute(node.props, 'icon'), id);
+ const label = getStringProp(findAttribute(node.props, 'label'), id);
+ const inlining = getStringArrayProp(findAttribute(node.props, 'inlining'), id);
+ const keywords = getStringArrayProp(findAttribute(node.props, 'keywords'), id);
+ const texts = getStringArrayProp(findAttribute(node.props, 'texts'), id);
if (path) markerInfo.path = path;
if (icon) markerInfo.icon = icon;
if (label) markerInfo.label = label;
if (inlining) markerInfo.inlining = inlining;
if (keywords) markerInfo.keywords = keywords;
+ if (texts) markerInfo.texts = texts;
- //pathãŒãªã„å ´åˆã¯ãƒ•ァイルパスを設定
+ // pathãŒãªã„å ´åˆã¯ãƒ•ァイルパスを設定
if (markerInfo.path == null && parentId == null) {
markerInfo.path = id.match(/.*(\/(admin|settings)\/[^\/]+)\.vue$/)?.[1];
}
- // SearchLabelã¨SearchKeywordを抽出 (AST全体を探索)
+ // SearchLabelã¨SearchTextを抽出 (AST全体を探索)
{
const extracted = extractSugarTags(node.children, id);
if (extracted.label && markerInfo.label) logger.warn(`Duplicate label found for ${markerId} at ${id}:${node.loc.start.line}`);
if (extracted.icon && markerInfo.icon) logger.warn(`Duplicate icon found for ${markerId} at ${id}:${node.loc.start.line}`);
markerInfo.label = extracted.label ?? markerInfo.label ?? '';
- markerInfo.keywords = [...extracted.keywords, ...markerInfo.keywords];
+ markerInfo.texts = [...extracted.texts, ...markerInfo.texts];
markerInfo.icon = extracted.icon ?? markerInfo.icon ?? undefined;
}
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index b8964549f4..108fae7305 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -4,7 +4,7 @@
"type": "module",
"scripts": {
"watch": "vite",
- "build": "vite build",
+ "build": "tsx build.ts",
"storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"",
"build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
"build-storybook": "pnpm build-storybook-pre && storybook build --webpack-stats-json storybook-static",
@@ -17,73 +17,75 @@
},
"dependencies": {
"@analytics/google-analytics": "1.1.0",
- "@discordapp/twemoji": "15.1.0",
+ "@discordapp/twemoji": "16.0.1",
"@github/webauthn-json": "2.1.1",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@misskey-dev/browser-image-resizer": "2024.1.0",
"@rollup/plugin-json": "6.1.0",
"@rollup/plugin-replace": "6.0.2",
"@rollup/pluginutils": "5.2.0",
- "@sentry/vue": "9.39.0",
- "@syuilo/aiscript": "0.19.0",
- "@twemoji/parser": "15.1.1",
- "@vitejs/plugin-vue": "5.2.4",
- "@vue/compiler-sfc": "3.5.17",
+ "@sentry/vue": "10.5.0",
+ "@syuilo/aiscript": "1.1.0",
+ "@syuilo/aiscript-0-19-0": "npm:@syuilo/aiscript@^0.19.0",
+ "@twemoji/parser": "16.0.0",
+ "@vitejs/plugin-vue": "6.0.1",
+ "@vue/compiler-sfc": "3.5.19",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15",
- "analytics": "0.8.16",
+ "analytics": "0.8.19",
"astring": "1.9.0",
"broadcast-channel": "7.1.0",
"buraha": "0.0.1",
"canvas-confetti": "1.9.3",
"chart.js": "4.5.0",
"chartjs-adapter-date-fns": "3.0.0",
- "chartjs-chart-matrix": "2.1.1",
+ "chartjs-chart-matrix": "3.0.0",
"chartjs-plugin-gradient": "0.6.1",
"chartjs-plugin-zoom": "2.2.0",
- "chromatic": "11.29.0",
+ "chromatic": "13.1.3",
"compare-versions": "6.1.1",
- "cropperjs": "2.0.0",
+ "cropperjs": "2.0.1",
"date-fns": "4.1.0",
"estree-walker": "3.0.3",
"eventemitter3": "5.0.1",
+ "execa": "9.6.0",
"frontend-shared": "workspace:*",
"icons-subsetter": "workspace:*",
"idb-keyval": "6.2.2",
"insert-text-at-cursor": "0.3.0",
+ "ios-haptics": "0.1.0",
"is-file-animated": "1.0.2",
"json5": "2.2.3",
- "magic-string": "0.30.17",
+ "magic-string": "0.30.18",
"matter-js": "0.20.0",
- "mfm-js": "0.24.0",
+ "mfm-js": "0.25.0",
"misskey-bubble-game": "workspace:*",
"misskey-js": "workspace:*",
"misskey-reversi": "workspace:*",
"photoswipe": "5.4.4",
"punycode.js": "2.3.1",
- "rollup": "4.45.1",
+ "rollup": "4.48.0",
"sanitize-html": "2.17.0",
- "sass": "1.89.2",
- "shiki": "3.8.0",
+ "sass": "1.90.0",
+ "shiki": "3.11.0",
"strict-event-emitter-types": "2.0.0",
"textarea-caret": "3.1.0",
- "three": "0.178.0",
+ "three": "0.179.1",
"throttle-debounce": "5.0.2",
"tinycolor2": "1.6.0",
"tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0",
- "typescript": "5.8.3",
+ "typescript": "5.9.2",
"v-code-diff": "1.13.1",
- "vite": "6.3.5",
- "vue": "3.5.17",
+ "vite": "7.1.3",
+ "vue": "3.5.19",
"vuedraggable": "next",
"wanakana": "5.3.1"
},
"devDependencies": {
- "@misskey-dev/summaly": "5.2.2",
- "@storybook/addon-actions": "8.6.14",
+ "@misskey-dev/summaly": "5.2.3",
"@storybook/addon-essentials": "8.6.14",
"@storybook/addon-interactions": "8.6.14",
- "@storybook/addon-links": "8.6.14",
+ "@storybook/addon-links": "9.1.3",
"@storybook/addon-mdx-gfm": "8.6.14",
"@storybook/addon-storysource": "8.6.14",
"@storybook/blocks": "8.6.14",
@@ -91,56 +93,57 @@
"@storybook/core-events": "8.6.14",
"@storybook/manager-api": "8.6.14",
"@storybook/preview-api": "8.6.14",
- "@storybook/react": "8.6.14",
- "@storybook/react-vite": "8.6.14",
+ "@storybook/react": "9.1.3",
+ "@storybook/react-vite": "9.1.3",
"@storybook/test": "8.6.14",
"@storybook/theming": "8.6.14",
"@storybook/types": "8.6.14",
- "@storybook/vue3": "8.6.14",
- "@storybook/vue3-vite": "8.6.14",
- "@tabler/icons-webfont": "3.34.0",
+ "@storybook/vue3": "9.1.3",
+ "@storybook/vue3-vite": "9.1.3",
+ "@tabler/icons-webfont": "3.34.1",
"@testing-library/vue": "8.1.0",
"@types/canvas-confetti": "1.9.0",
"@types/estree": "1.0.8",
- "@types/matter-js": "0.19.8",
+ "@types/matter-js": "0.20.0",
"@types/micromatch": "4.0.9",
- "@types/node": "22.16.4",
+ "@types/node": "22.17.2",
"@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/sanitize-html": "2.16.0",
"@types/seedrandom": "3.0.8",
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1",
- "@typescript-eslint/eslint-plugin": "8.37.0",
- "@typescript-eslint/parser": "8.37.0",
+ "@typescript-eslint/eslint-plugin": "8.40.0",
+ "@typescript-eslint/parser": "8.40.0",
"@vitest/coverage-v8": "3.2.4",
- "@vue/compiler-core": "3.5.17",
- "@vue/runtime-core": "3.5.17",
+ "@vue/compiler-core": "3.5.19",
+ "@vue/runtime-core": "3.5.19",
"acorn": "8.15.0",
- "cross-env": "7.0.3",
- "cypress": "14.5.2",
+ "cross-env": "10.0.0",
+ "cypress": "14.5.4",
"eslint-plugin-import": "2.32.0",
- "eslint-plugin-vue": "10.3.0",
+ "eslint-plugin-vue": "10.4.0",
"fast-glob": "3.3.3",
- "happy-dom": "17.6.3",
+ "happy-dom": "18.0.1",
"intersection-observer": "0.12.2",
"micromatch": "4.0.8",
"minimatch": "10.0.3",
- "msw": "2.10.4",
+ "msw": "2.10.5",
"msw-storybook-addon": "2.0.5",
"nodemon": "3.1.10",
"prettier": "3.6.2",
- "react": "19.1.0",
- "react-dom": "19.1.0",
+ "react": "19.1.1",
+ "react-dom": "19.1.1",
"seedrandom": "3.0.5",
- "start-server-and-test": "2.0.12",
- "storybook": "8.6.14",
+ "start-server-and-test": "2.0.13",
+ "storybook": "9.1.3",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
+ "tsx": "4.20.4",
"vite-plugin-turbosnap": "1.0.3",
"vitest": "3.2.4",
"vitest-fetch-mock": "0.4.5",
- "vue-component-type-helpers": "2.2.12",
+ "vue-component-type-helpers": "3.0.6",
"vue-eslint-parser": "10.2.0",
- "vue-tsc": "2.2.12"
+ "vue-tsc": "3.0.6"
}
}
diff --git a/packages/frontend/src/_boot_.ts b/packages/frontend/src/_boot_.ts
index 354fb95544..111a4abbfd 100644
--- a/packages/frontend/src/_boot_.ts
+++ b/packages/frontend/src/_boot_.ts
@@ -16,7 +16,7 @@ import '@/style.scss';
import { mainBoot } from '@/boot/main-boot.js';
import { subBoot } from '@/boot/sub-boot.js';
-const subBootPaths = ['/share', '/auth', '/miauth', '/oauth', '/signup-complete', '/install-extensions'];
+const subBootPaths = ['/share', '/auth', '/miauth', '/oauth', '/signup-complete', '/verify-email', '/install-extensions'];
if (subBootPaths.some(i => window.location.pathname === i || window.location.pathname.startsWith(i + '/'))) {
subBoot();
diff --git a/packages/frontend/src/accounts.ts b/packages/frontend/src/accounts.ts
index 3693ac3308..afa2ecb911 100644
--- a/packages/frontend/src/accounts.ts
+++ b/packages/frontend/src/accounts.ts
@@ -23,7 +23,7 @@ export async function getAccounts(): Promise<{
host: string;
id: Misskey.entities.User['id'];
username: Misskey.entities.User['username'];
- user?: Misskey.entities.User | null;
+ user?: Misskey.entities.MeDetailed | null;
token: string | null;
}[]> {
const tokens = store.s.accountTokens;
@@ -38,7 +38,7 @@ export async function getAccounts(): Promise<{
}));
}
-async function addAccount(host: string, user: Misskey.entities.User, token: AccountWithToken['token']) {
+async function addAccount(host: string, user: Misskey.entities.MeDetailed, token: AccountWithToken['token']) {
if (!prefer.s.accounts.some(x => x[0] === host && x[1].id === user.id)) {
store.set('accountTokens', { ...store.s.accountTokens, [host + '/' + user.id]: token });
store.set('accountInfos', { ...store.s.accountInfos, [host + '/' + user.id]: user });
@@ -149,9 +149,10 @@ export function updateCurrentAccountPartial(accountData: Partial<Misskey.entitie
export async function refreshCurrentAccount() {
if (!$i) return;
+ const me = $i;
return fetchAccount($i.token, $i.id).then(updateCurrentAccount).catch(reason => {
if (reason === isAccountDeleted) {
- removeAccount(host, $i.id);
+ removeAccount(host, me.id);
if (Object.keys(store.s.accountTokens).length > 0) {
login(Object.values(store.s.accountTokens)[0]);
} else {
@@ -214,46 +215,58 @@ export async function openAccountMenu(opts: {
includeCurrentAccount?: boolean;
withExtraOperation: boolean;
active?: Misskey.entities.User['id'];
- onChoose?: (account: Misskey.entities.User) => void;
+ onChoose?: (account: Misskey.entities.MeDetailed) => void;
}, ev: MouseEvent) {
if (!$i) return;
+ const me = $i;
- function createItem(host: string, id: Misskey.entities.User['id'], username: Misskey.entities.User['username'], account: Misskey.entities.User | null | undefined, token: string): MenuItem {
+ const callback = opts.onChoose;
+
+ function createItem(host: string, id: Misskey.entities.User['id'], username: Misskey.entities.User['username'], account: Misskey.entities.MeDetailed | null | undefined, token: string | null): MenuItem {
if (account) {
return {
type: 'user' as const,
user: account,
active: opts.active != null ? opts.active === id : false,
action: async () => {
- if (opts.onChoose) {
- opts.onChoose(account);
+ if (callback) {
+ callback(account);
} else {
switchAccount(host, id);
}
},
};
- } else {
+ } else if (token != null) {
return {
type: 'button' as const,
text: username,
active: opts.active != null ? opts.active === id : false,
action: async () => {
- if (opts.onChoose) {
+ if (callback) {
fetchAccount(token, id).then(account => {
- opts.onChoose(account);
+ callback(account);
});
} else {
switchAccount(host, id);
}
},
};
+ } else {
+ return {
+ type: 'button' as const,
+ text: username,
+ active: opts.active != null ? opts.active === id : false,
+ action: async () => {
+ // TODO
+ },
+ };
}
}
const menuItems: MenuItem[] = [];
// TODO: $iã®ãƒ›ã‚¹ãƒˆã‚‚比較ã—ãŸã„ã‘ã©é€šå¸¸null
- const accountItems = (await getAccounts().then(accounts => accounts.filter(x => x.id !== $i.id))).map(a => createItem(a.host, a.id, a.username, a.user, a.token));
+ const accountItems = (await getAccounts().then(accounts => accounts.filter(x => x.id !== me.id))).map(a => createItem(a.host, a.id, a.username, a.user, a.token));
if (opts.withExtraOperation) {
menuItems.push({
diff --git a/packages/frontend/src/aiscript/ui.ts b/packages/frontend/src/aiscript/ui.ts
index a27ece512e..9c330da3c5 100644
--- a/packages/frontend/src/aiscript/ui.ts
+++ b/packages/frontend/src/aiscript/ui.ts
@@ -4,11 +4,11 @@
*/
import { utils, values } from '@syuilo/aiscript';
-import { genId } from '@/utility/id.js';
import { ref } from 'vue';
-import type { Ref } from 'vue';
import * as Misskey from 'misskey-js';
import { assertStringAndIsIn } from './common.js';
+import type { Ref } from 'vue';
+import { genId } from '@/utility/id.js';
const ALIGNS = ['left', 'center', 'right'] as const;
const FONTS = ['serif', 'sans-serif', 'monospace'] as const;
@@ -21,16 +21,15 @@ type BorderStyle = (typeof BORDER_STYLES)[number];
export type AsUiComponentBase = {
id: string;
hidden?: boolean;
+ children?: AsUiComponent['id'][];
};
export type AsUiRoot = AsUiComponentBase & {
type: 'root';
- children: AsUiComponent['id'][];
};
export type AsUiContainer = AsUiComponentBase & {
type: 'container';
- children?: AsUiComponent['id'][];
align?: Align;
bgColor?: string;
fgColor?: string;
@@ -123,7 +122,6 @@ export type AsUiSelect = AsUiComponentBase & {
export type AsUiFolder = AsUiComponentBase & {
type: 'folder';
- children?: AsUiComponent['id'][];
title?: string;
opened?: boolean;
};
diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts
index 992bde9bd1..574012ff78 100644
--- a/packages/frontend/src/boot/common.ts
+++ b/packages/frontend/src/boot/common.ts
@@ -5,9 +5,10 @@
import { computed, watch, version as vueVersion } from 'vue';
import { compareVersions } from 'compare-versions';
-import { version, lang, updateLocale, locale, apiUrl } from '@@/js/config.js';
+import { version, lang, apiUrl, isSafeMode } from '@@/js/config.js';
import defaultLightTheme from '@@/themes/l-light.json5';
import defaultDarkTheme from '@@/themes/d-green-lime.json5';
+import { storeBootloaderErrors } from '@@/js/store-boot-errors';
import type { App } from 'vue';
import widgets from '@/widgets/index.js';
import directives from '@/directives/index.js';
@@ -28,6 +29,7 @@ import { miLocalStorage } from '@/local-storage.js';
import { fetchCustomEmojis } from '@/custom-emojis.js';
import { prefer } from '@/preferences.js';
import { $i } from '@/i.js';
+import { launchPlugins } from '@/plugin.js';
export async function common(createVue: () => Promise<App<Element>>) {
console.info(`Misskey v${version}`);
@@ -79,25 +81,7 @@ export async function common(createVue: () => Promise<App<Element>>) {
//#endregion
//#region Detect language & fetch translations
- const localeVersion = miLocalStorage.getItem('localeVersion');
- const localeOutdated = (localeVersion == null || localeVersion !== version || locale == null);
-
- async function fetchAndUpdateLocale({ useCache } = { useCache: true }) {
- const fetchOptions: RequestInit | undefined = useCache ? undefined : { cache: 'no-store' };
- const res = await window.fetch(`/assets/locales/${lang}.${version}.json`, fetchOptions);
- if (res.status === 200) {
- const newLocale = await res.text();
- const parsedNewLocale = JSON.parse(newLocale);
- miLocalStorage.setItem('locale', newLocale);
- miLocalStorage.setItem('localeVersion', version);
- updateLocale(parsedNewLocale);
- updateI18n(parsedNewLocale);
- }
- }
-
- if (localeOutdated) {
- fetchAndUpdateLocale();
- }
+ storeBootloaderErrors({ ...i18n.ts._bootErrors, reload: i18n.ts.reload });
if (import.meta.hot) {
import.meta.hot.on('locale-update', async (updatedLang: string) => {
@@ -106,7 +90,8 @@ export async function common(createVue: () => Promise<App<Element>>) {
await new Promise(resolve => {
window.setTimeout(resolve, 500);
});
- await fetchAndUpdateLocale({ useCache: false });
+ // fetch with cache: 'no-store' to ensure the latest locale is fetched
+ await window.fetch(`/assets/locales/${lang}.${version}.json`, { cache: 'no-store' }).then(async res => res.status === 200 && await res.text());
window.location.reload();
}
});
@@ -168,28 +153,35 @@ export async function common(createVue: () => Promise<App<Element>>) {
// NOTE: ã“ã®å‡¦ç†ã¯å¿…ãšã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆæ›´æ–°ãƒã‚§ãƒƒã‚¯å‡¦ç†ã‚ˆã‚Šå¾Œã«æ¥ã‚‹ã“ã¨(ãƒ†ãƒ¼ãƒžå†æ§‹ç¯‰ã®ãŸã‚)
watch(store.r.darkMode, (darkMode) => {
- applyTheme(darkMode
- ? (prefer.s.darkTheme ?? defaultDarkTheme)
- : (prefer.s.lightTheme ?? defaultLightTheme),
- );
- }, { immediate: miLocalStorage.getItem('theme') == null });
+ const theme = (() => {
+ if (darkMode) {
+ return isSafeMode ? defaultDarkTheme : (prefer.s.darkTheme ?? defaultDarkTheme);
+ } else {
+ return isSafeMode ? defaultLightTheme : (prefer.s.lightTheme ?? defaultLightTheme);
+ }
+ })();
+
+ applyTheme(theme);
+ }, { immediate: isSafeMode || miLocalStorage.getItem('theme') == null });
window.document.documentElement.dataset.colorScheme = store.s.darkMode ? 'dark' : 'light';
- const darkTheme = prefer.model('darkTheme');
- const lightTheme = prefer.model('lightTheme');
+ if (!isSafeMode) {
+ const darkTheme = prefer.model('darkTheme');
+ const lightTheme = prefer.model('lightTheme');
- watch(darkTheme, (theme) => {
- if (store.s.darkMode) {
- applyTheme(theme ?? defaultDarkTheme);
- }
- });
+ watch(darkTheme, (theme) => {
+ if (store.s.darkMode) {
+ applyTheme(theme ?? defaultDarkTheme);
+ }
+ });
- watch(lightTheme, (theme) => {
- if (!store.s.darkMode) {
- applyTheme(theme ?? defaultLightTheme);
- }
- });
+ watch(lightTheme, (theme) => {
+ if (!store.s.darkMode) {
+ applyTheme(theme ?? defaultLightTheme);
+ }
+ });
+ }
//#region Sync dark mode
if (prefer.s.syncDeviceDarkMode) {
@@ -203,17 +195,19 @@ export async function common(createVue: () => Promise<App<Element>>) {
});
//#endregion
- if (prefer.s.darkTheme && store.s.darkMode) {
- if (miLocalStorage.getItem('themeId') !== prefer.s.darkTheme.id) applyTheme(prefer.s.darkTheme);
- } else if (prefer.s.lightTheme && !store.s.darkMode) {
- if (miLocalStorage.getItem('themeId') !== prefer.s.lightTheme.id) applyTheme(prefer.s.lightTheme);
- }
+ if (!isSafeMode) {
+ if (prefer.s.darkTheme && store.s.darkMode) {
+ if (miLocalStorage.getItem('themeId') !== prefer.s.darkTheme.id) applyTheme(prefer.s.darkTheme);
+ } else if (prefer.s.lightTheme && !store.s.darkMode) {
+ if (miLocalStorage.getItem('themeId') !== prefer.s.lightTheme.id) applyTheme(prefer.s.lightTheme);
+ }
- fetchInstanceMetaPromise.then(() => {
- // TODO: instance.defaultLightTheme/instance.defaultDarkThemeãŒä¸æ­£ãªå½¢å¼ã ã£ãŸå ´åˆã®ã‚±ã‚¢
- if (prefer.s.lightTheme == null && instance.defaultLightTheme != null) prefer.commit('lightTheme', JSON.parse(instance.defaultLightTheme));
- if (prefer.s.darkTheme == null && instance.defaultDarkTheme != null) prefer.commit('darkTheme', JSON.parse(instance.defaultDarkTheme));
- });
+ fetchInstanceMetaPromise.then(() => {
+ // TODO: instance.defaultLightTheme/instance.defaultDarkThemeãŒä¸æ­£ãªå½¢å¼ã ã£ãŸå ´åˆã®ã‚±ã‚¢
+ if (prefer.s.lightTheme == null && instance.defaultLightTheme != null) prefer.commit('lightTheme', JSON.parse(instance.defaultLightTheme));
+ if (prefer.s.darkTheme == null && instance.defaultDarkTheme != null) prefer.commit('darkTheme', JSON.parse(instance.defaultDarkTheme));
+ });
+ }
watch(prefer.r.overridedDeviceKind, (kind) => {
updateDeviceKind(kind);
@@ -345,6 +339,12 @@ export async function common(createVue: () => Promise<App<Element>>) {
});
}
+ try {
+ await launchPlugins();
+ } catch (error) {
+ console.error('Failed to launch plugins:', error);
+ }
+
app.mount(rootEl);
// boot.jsã®ã‚„ã¤ã‚’解除
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index ae4e0445db..18817d3f79 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -26,10 +26,9 @@ import { mainRouter } from '@/router.js';
import { makeHotkey } from '@/utility/hotkey.js';
import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom-emojis.js';
import { prefer } from '@/preferences.js';
-import { launchPlugins } from '@/plugin.js';
import { updateCurrentAccountPartial } from '@/accounts.js';
-import { signout } from '@/signout.js';
import { migrateOldSettings } from '@/pref-migrate.js';
+import { unisonReload } from '@/utility/unison-reload.js';
export async function mainBoot() {
const { isClientUpdated, lastVersion } = await common(async () => {
@@ -79,8 +78,6 @@ export async function mainBoot() {
}
}
- launchPlugins();
-
try {
if (prefer.s.enableSeasonalScreenEffect) {
const month = new Date().getMonth() + 1;
@@ -371,11 +368,6 @@ export async function mainBoot() {
});
});
- main.on('unreadAntenna', () => {
- updateCurrentAccountPartial({ hasUnreadAntenna: true });
- sound.playMisskeySfx('antenna');
- });
-
main.on('newChatMessage', () => {
updateCurrentAccountPartial({ hasUnreadChatMessages: true });
sound.playMisskeySfx('chatMessage');
@@ -391,6 +383,8 @@ export async function mainBoot() {
}
// shortcut
+ let safemodeRequestCount = 0;
+ let safemodeRequestTimer: number | null = null;
const keymap = {
'p|n': () => {
if ($i == null) return;
@@ -402,6 +396,24 @@ export async function mainBoot() {
's': () => {
mainRouter.push('/search');
},
+ 'g': {
+ callback: () => {
+ // mã‚’5回押ã™ã¨ã‚»ãƒ¼ãƒ•モードã«å…¥ã‚‹
+ safemodeRequestCount++;
+ if (safemodeRequestCount >= 5) {
+ miLocalStorage.setItem('isSafeMode', 'true');
+ unisonReload();
+ } else {
+ if (safemodeRequestTimer != null) {
+ window.clearTimeout(safemodeRequestTimer);
+ }
+ safemodeRequestTimer = window.setTimeout(() => {
+ safemodeRequestCount = 0;
+ }, 300);
+ }
+ },
+ allowRepeat: true,
+ },
} as const satisfies Keymap;
window.document.addEventListener('keydown', makeHotkey(keymap), { passive: false });
diff --git a/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts b/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts
index b62096bbe9..2eb17b1b9e 100644
--- a/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts
+++ b/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts
@@ -2,14 +2,13 @@
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
-
-/* eslint-disable @typescript-eslint/explicit-function-return-type */
-import { action } from '@storybook/addon-actions';
-import type { StoryObj } from '@storybook/vue3';
+
+import { action } from 'storybook/actions';
import { HttpResponse, http } from 'msw';
import { userDetailed } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkAbuseReportWindow from './MkAbuseReportWindow.vue';
+import type { StoryObj } from '@storybook/vue3';
export const Default = {
render(args) {
return {
diff --git a/packages/frontend/src/components/MkAccountMoved.stories.impl.ts b/packages/frontend/src/components/MkAccountMoved.stories.impl.ts
index b907b5b25a..fff29262f1 100644
--- a/packages/frontend/src/components/MkAccountMoved.stories.impl.ts
+++ b/packages/frontend/src/components/MkAccountMoved.stories.impl.ts
@@ -4,7 +4,7 @@
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw';
import { commonHandlers } from '../../.storybook/mocks.js';
diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue
index 70766634ce..c786e9fe9f 100644
--- a/packages/frontend/src/components/MkAchievements.vue
+++ b/packages/frontend/src/components/MkAchievements.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
[$style.iconFrame_platinum]: ACHIEVEMENT_BADGES[achievement.name].frame === 'platinum',
}]"
>
- <div :class="[$style.iconInner]" :style="{ background: ACHIEVEMENT_BADGES[achievement.name].bg }">
+ <div :class="[$style.iconInner]" :style="{ background: ACHIEVEMENT_BADGES[achievement.name].bg ?? '' }">
<img :class="$style.iconImg" :src="ACHIEVEMENT_BADGES[achievement.name].img">
</div>
</div>
@@ -61,8 +61,8 @@ import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/utili
const props = withDefaults(defineProps<{
user: Misskey.entities.User;
- withLocked: boolean;
- withDescription: boolean;
+ withLocked?: boolean;
+ withDescription?: boolean;
}>(), {
withLocked: true,
withDescription: true,
@@ -71,7 +71,7 @@ const props = withDefaults(defineProps<{
const achievements = ref<Misskey.entities.UsersAchievementsResponse | null>(null);
const lockedAchievements = computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements.value ?? []).some(a => a.name === x)));
-function fetch() {
+function _fetch_() {
misskeyApi('users/achievements', { userId: props.user.id }).then(res => {
achievements.value = [];
for (const t of ACHIEVEMENT_TYPES) {
@@ -84,11 +84,11 @@ function fetch() {
function clickHere() {
claimAchievement('clickedClickHere');
- fetch();
+ _fetch_();
}
onMounted(() => {
- fetch();
+ _fetch_();
});
</script>
diff --git a/packages/frontend/src/components/MkAnimBg.vue b/packages/frontend/src/components/MkAnimBg.vue
index e57fbcdee3..19a21f6e24 100644
--- a/packages/frontend/src/components/MkAnimBg.vue
+++ b/packages/frontend/src/components/MkAnimBg.vue
@@ -44,7 +44,7 @@ function initShaderProgram(gl: WebGLRenderingContext, vsSource: string, fsSource
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
const shaderProgram = gl.createProgram();
- if (shaderProgram == null || vertexShader == null || fragmentShader == null) return null;
+ if (vertexShader == null || fragmentShader == null) return null;
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
@@ -71,8 +71,10 @@ onMounted(() => {
canvas.width = width;
canvas.height = height;
- const gl = canvas.getContext('webgl', { premultipliedAlpha: true });
- if (gl == null) return;
+ const maybeGl = canvas.getContext('webgl', { premultipliedAlpha: true });
+ if (maybeGl == null) return;
+
+ const gl = maybeGl;
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT);
@@ -229,8 +231,8 @@ onMounted(() => {
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.DYNAMIC_DRAW);
if (isChromatic()) {
- gl!.uniform1f(u_time, 0);
- gl!.drawArrays(gl!.TRIANGLE_STRIP, 0, 4);
+ gl.uniform1f(u_time, 0);
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
} else {
function render(timeStamp: number) {
let sizeChanged = false;
@@ -249,8 +251,8 @@ onMounted(() => {
gl.viewport(0, 0, width, height);
}
- gl!.uniform1f(u_time, timeStamp);
- gl!.drawArrays(gl!.TRIANGLE_STRIP, 0, 4);
+ gl.uniform1f(u_time, timeStamp);
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
handle = window.requestAnimationFrame(render);
}
@@ -263,6 +265,8 @@ onUnmounted(() => {
if (handle) {
window.cancelAnimationFrame(handle);
}
+
+ // TODO: WebGLリソースã®è§£æ”¾
});
</script>
diff --git a/packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts b/packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts
index 627cb0c4ff..743bdda032 100644
--- a/packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts
+++ b/packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts
@@ -4,7 +4,7 @@
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw';
import { commonHandlers } from '../../.storybook/mocks.js';
diff --git a/packages/frontend/src/components/MkAntennaEditor.stories.impl.ts b/packages/frontend/src/components/MkAntennaEditor.stories.impl.ts
index 4d921a4c48..5c4b05481a 100644
--- a/packages/frontend/src/components/MkAntennaEditor.stories.impl.ts
+++ b/packages/frontend/src/components/MkAntennaEditor.stories.impl.ts
@@ -4,7 +4,7 @@
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw';
import { commonHandlers } from '../../.storybook/mocks.js';
diff --git a/packages/frontend/src/components/MkAntennaEditorDialog.stories.impl.ts b/packages/frontend/src/components/MkAntennaEditorDialog.stories.impl.ts
index 5878b52fb9..1a70cb745c 100644
--- a/packages/frontend/src/components/MkAntennaEditorDialog.stories.impl.ts
+++ b/packages/frontend/src/components/MkAntennaEditorDialog.stories.impl.ts
@@ -4,7 +4,7 @@
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw';
import { commonHandlers } from '../../.storybook/mocks.js';
diff --git a/packages/frontend/src/components/MkAutocomplete.stories.impl.ts b/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
index 64ccb708aa..15aab8daed 100644
--- a/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
+++ b/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
@@ -4,7 +4,7 @@
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import { expect, userEvent, waitFor, within } from '@storybook/test';
import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw';
diff --git a/packages/frontend/src/components/MkButton.stories.impl.ts b/packages/frontend/src/components/MkButton.stories.impl.ts
index 0a569b3beb..4420cc4f05 100644
--- a/packages/frontend/src/components/MkButton.stories.impl.ts
+++ b/packages/frontend/src/components/MkButton.stories.impl.ts
@@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable import/no-default-export */
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3';
import MkButton from './MkButton.vue';
export const Default = {
diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue
index a77ebd6ac5..b729128a21 100644
--- a/packages/frontend/src/components/MkButton.vue
+++ b/packages/frontend/src/components/MkButton.vue
@@ -36,6 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { nextTick, onMounted, useTemplateRef } from 'vue';
+import type { MkABehavior } from '@/components/global/MkA.vue';
const props = defineProps<{
type?: 'button' | 'submit' | 'reset';
@@ -45,7 +46,7 @@ const props = defineProps<{
inline?: boolean;
link?: boolean;
to?: string;
- linkBehavior?: null | 'window' | 'browser';
+ linkBehavior?: MkABehavior;
autofocus?: boolean;
wait?: boolean;
danger?: boolean;
diff --git a/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts b/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts
index 4304c2e2b7..095805ba95 100644
--- a/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts
+++ b/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts
@@ -2,9 +2,9 @@
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
-
+
import { HttpResponse, http } from 'msw';
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import { expect, userEvent, within } from '@storybook/test';
import { channel } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
diff --git a/packages/frontend/src/components/MkChannelList.stories.impl.ts b/packages/frontend/src/components/MkChannelList.stories.impl.ts
index 47ca864dc0..33a61c8f7a 100644
--- a/packages/frontend/src/components/MkChannelList.stories.impl.ts
+++ b/packages/frontend/src/components/MkChannelList.stories.impl.ts
@@ -3,14 +3,13 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-/* eslint-disable @typescript-eslint/explicit-function-return-type */
-/* eslint-disable import/no-default-export */
-import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw';
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import { channel } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkChannelList from './MkChannelList.vue';
+import type { StoryObj } from '@storybook/vue3';
+import { Paginator } from '@/utility/paginator.js';
export const Default = {
render(args) {
return {
@@ -33,10 +32,7 @@ export const Default = {
};
},
args: {
- pagination: {
- endpoint: 'channels/search',
- limit: 10,
- },
+ paginator: new Paginator('channels/search', {}),
},
parameters: {
chromatic: {
diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue
index 4d67bba70d..c54081ad42 100644
--- a/packages/frontend/src/components/MkChart.vue
+++ b/packages/frontend/src/components/MkChart.vue
@@ -589,7 +589,10 @@ const fetchDriveFilesChart = async (): Promise<typeof chartData> => {
};
const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+ const host = props.args?.host;
+ if (host == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return {
series: [{
name: 'In',
@@ -611,7 +614,10 @@ const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
};
const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+ const host = props.args?.host;
+ if (host == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return {
series: [{
name: 'Users',
@@ -626,7 +632,10 @@ const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData
};
const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+ const host = props.args?.host;
+ if (host == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return {
series: [{
name: 'Notes',
@@ -641,7 +650,10 @@ const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData
};
const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+ const host = props.args?.host;
+ if (host == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return {
series: [{
name: 'Following',
@@ -664,7 +676,10 @@ const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> =
};
const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+ const host = props.args?.host;
+ if (host == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return {
bytes: true,
series: [{
@@ -680,7 +695,10 @@ const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof char
};
const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+ const host = props.args?.host;
+ if (host == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return {
series: [{
name: 'Drive files',
@@ -695,7 +713,10 @@ const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof char
};
const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/user/notes', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
+ const userId = props.args?.user?.id;
+ if (userId == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/user/notes', { userId: userId, limit: props.limit, span: props.span });
return {
series: [...(props.args?.withoutAll ? [] : [{
name: 'All',
@@ -727,7 +748,10 @@ const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
};
const fetchPerUserPvChart = async (): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/user/pv', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
+ const userId = props.args?.user?.id;
+ if (userId == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/user/pv', { userId: userId, limit: props.limit, span: props.span });
return {
series: [{
name: 'Unique PV (user)',
@@ -754,7 +778,10 @@ const fetchPerUserPvChart = async (): Promise<typeof chartData> => {
};
const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
+ const userId = props.args?.user?.id;
+ if (userId == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/user/following', { userId: userId, limit: props.limit, span: props.span });
return {
series: [{
name: 'Local',
@@ -769,7 +796,10 @@ const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
};
const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
+ const userId = props.args?.user?.id;
+ if (userId == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/user/following', { userId: userId, limit: props.limit, span: props.span });
return {
series: [{
name: 'Local',
@@ -784,7 +814,10 @@ const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
};
const fetchPerUserDriveChart = async (): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/user/drive', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
+ const userId = props.args?.user?.id;
+ if (userId == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/user/drive', { userId: userId, limit: props.limit, span: props.span });
return {
bytes: true,
series: [{
diff --git a/packages/frontend/src/components/MkChatHistories.stories.impl.ts b/packages/frontend/src/components/MkChatHistories.stories.impl.ts
index 8268adc36f..74fdff6fdd 100644
--- a/packages/frontend/src/components/MkChatHistories.stories.impl.ts
+++ b/packages/frontend/src/components/MkChatHistories.stories.impl.ts
@@ -4,7 +4,7 @@
*/
import { http, HttpResponse } from 'msw';
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import { chatMessage } from '../../.storybook/fakes';
import MkChatHistories from './MkChatHistories.vue';
import type { StoryObj } from '@storybook/vue3';
diff --git a/packages/frontend/src/components/MkClickerGame.stories.impl.ts b/packages/frontend/src/components/MkClickerGame.stories.impl.ts
index 6e1eb13d61..f9012742cb 100644
--- a/packages/frontend/src/components/MkClickerGame.stories.impl.ts
+++ b/packages/frontend/src/components/MkClickerGame.stories.impl.ts
@@ -2,9 +2,9 @@
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
-
+
import { HttpResponse, http } from 'msw';
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import { expect, userEvent, within } from '@storybook/test';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkClickerGame from './MkClickerGame.vue';
diff --git a/packages/frontend/src/components/MkCodeEditor.stories.impl.ts b/packages/frontend/src/components/MkCodeEditor.stories.impl.ts
index c76b6fd08e..24b8e9119b 100644
--- a/packages/frontend/src/components/MkCodeEditor.stories.impl.ts
+++ b/packages/frontend/src/components/MkCodeEditor.stories.impl.ts
@@ -6,7 +6,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable import/no-default-export */
import type { StoryObj } from '@storybook/vue3';
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import MkCodeEditor from './MkCodeEditor.vue';
const code = `for (let i, 100) {
<: if (i % 15 == 0) "FizzBuzz"
diff --git a/packages/frontend/src/components/MkColorInput.stories.impl.ts b/packages/frontend/src/components/MkColorInput.stories.impl.ts
index 3df92ca858..f8ec58bbcc 100644
--- a/packages/frontend/src/components/MkColorInput.stories.impl.ts
+++ b/packages/frontend/src/components/MkColorInput.stories.impl.ts
@@ -6,7 +6,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable import/no-default-export */
import type { StoryObj } from '@storybook/vue3';
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import MkColorInput from './MkColorInput.vue';
export const Default = {
render(args) {
diff --git a/packages/frontend/src/components/MkCropperDialog.stories.impl.ts b/packages/frontend/src/components/MkCropperDialog.stories.impl.ts
index 78cb4120de..bd6733f9a8 100644
--- a/packages/frontend/src/components/MkCropperDialog.stories.impl.ts
+++ b/packages/frontend/src/components/MkCropperDialog.stories.impl.ts
@@ -4,7 +4,7 @@
*/
import { HttpResponse, http } from 'msw';
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import { file } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkCropperDialog from './MkCropperDialog.vue';
diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue
index 7f592fba79..6c07eac47a 100644
--- a/packages/frontend/src/components/MkCropperDialog.vue
+++ b/packages/frontend/src/components/MkCropperDialog.vue
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { onMounted, useTemplateRef, ref } from 'vue';
+import { onMounted, useTemplateRef, ref, onUnmounted } from 'vue';
import * as Misskey from 'misskey-js';
import Cropper from 'cropperjs';
import tinycolor from 'tinycolor2';
@@ -55,17 +55,19 @@ const imgEl = useTemplateRef('imgEl');
let cropper: Cropper | null = null;
const loading = ref(true);
-const ok = async () => {
- const promise = new Promise<Misskey.entities.DriveFile>(async (res) => {
- const croppedImage = await cropper?.getCropperImage();
- const croppedSection = await cropper?.getCropperSelection();
+async function ok() {
+ const promise = new Promise<Blob>(async (res) => {
+ if (cropper == null) throw new Error('Cropper is not initialized');
+
+ const croppedImage = await cropper.getCropperImage()!;
+ const croppedSection = await cropper.getCropperSelection()!;
// 拡大率を計算ã—ã€(ã»ã¼)å…ƒã®å¤§ãã•ã«æˆ»ã™
const zoomedRate = croppedImage.getBoundingClientRect().width / croppedImage.clientWidth;
const widthToRender = croppedSection.getBoundingClientRect().width / zoomedRate;
- const croppedCanvas = await croppedSection?.$toCanvas({ width: widthToRender });
- croppedCanvas?.toBlob(blob => {
+ const croppedCanvas = await croppedSection.$toCanvas({ width: widthToRender });
+ croppedCanvas.toBlob(blob => {
if (!blob) return;
res(blob);
});
@@ -74,25 +76,27 @@ const ok = async () => {
const f = await promise;
emit('ok', f);
- dialogEl.value!.close();
-};
+ if (dialogEl.value != null) dialogEl.value.close();
+}
-const cancel = () => {
+function cancel() {
emit('cancel');
- dialogEl.value!.close();
-};
+ if (dialogEl.value != null) dialogEl.value.close();
+}
-const onImageLoad = () => {
+function onImageLoad() {
loading.value = false;
if (cropper) {
cropper.getCropperImage()!.$center('contain');
cropper.getCropperSelection()!.$center();
}
-};
+}
onMounted(() => {
- cropper = new Cropper(imgEl.value!, {
+ if (imgEl.value == null) return; // TSを黙らã™ãŸã‚
+
+ cropper = new Cropper(imgEl.value, {
});
const computedStyle = getComputedStyle(window.document.documentElement);
@@ -104,16 +108,22 @@ onMounted(() => {
selection.outlined = true;
window.setTimeout(() => {
- cropper!.getCropperImage()!.$center('contain');
+ if (cropper == null) return;
+ cropper.getCropperImage()!.$center('contain');
selection.$center();
}, 100);
// モーダルオープンアニメーションãŒçµ‚ã‚ã£ãŸã‚ã¨ã§å†åº¦èª¿æ•´
window.setTimeout(() => {
- cropper!.getCropperImage()!.$center('contain');
+ if (cropper == null) return;
+ cropper.getCropperImage()!.$center('contain');
selection.$center();
}, 500);
});
+
+onUnmounted(() => {
+ URL.revokeObjectURL(imgUrl);
+});
</script>
<style lang="scss" scoped>
diff --git a/packages/frontend/src/components/MkCwButton.stories.impl.ts b/packages/frontend/src/components/MkCwButton.stories.impl.ts
index bbe5f4eddb..de38b98c4b 100644
--- a/packages/frontend/src/components/MkCwButton.stories.impl.ts
+++ b/packages/frontend/src/components/MkCwButton.stories.impl.ts
@@ -6,7 +6,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable import/no-default-export */
import type { StoryObj } from '@storybook/vue3';
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import { expect, userEvent, within } from '@storybook/test';
import { file } from '../../.storybook/fakes.js';
import MkCwButton from './MkCwButton.vue';
diff --git a/packages/frontend/src/components/MkDialog.stories.impl.ts b/packages/frontend/src/components/MkDialog.stories.impl.ts
index 57c7916049..c168d31cce 100644
--- a/packages/frontend/src/components/MkDialog.stories.impl.ts
+++ b/packages/frontend/src/components/MkDialog.stories.impl.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import { expect, userEvent, waitFor, within } from '@storybook/test';
import type { StoryObj } from '@storybook/vue3';
import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/components/MkDonation.stories.impl.ts b/packages/frontend/src/components/MkDonation.stories.impl.ts
index 71d0c20c63..bd1b74281d 100644
--- a/packages/frontend/src/components/MkDonation.stories.impl.ts
+++ b/packages/frontend/src/components/MkDonation.stories.impl.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3';
import { onBeforeUnmount } from 'vue';
import MkDonation from './MkDonation.vue';
diff --git a/packages/frontend/src/components/MkDrive.file.stories.impl.ts b/packages/frontend/src/components/MkDrive.file.stories.impl.ts
index 933383775c..9981ee77ac 100644
--- a/packages/frontend/src/components/MkDrive.file.stories.impl.ts
+++ b/packages/frontend/src/components/MkDrive.file.stories.impl.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3';
import MkDrive_file from './MkDrive.file.vue';
import { file } from '../../.storybook/fakes.js';
diff --git a/packages/frontend/src/components/MkDrive.folder.stories.impl.ts b/packages/frontend/src/components/MkDrive.folder.stories.impl.ts
index e6c7c2f645..6fa8d2253f 100644
--- a/packages/frontend/src/components/MkDrive.folder.stories.impl.ts
+++ b/packages/frontend/src/components/MkDrive.folder.stories.impl.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3';
import { http, HttpResponse } from 'msw';
import * as Misskey from 'misskey-js';
diff --git a/packages/frontend/src/components/MkDrive.stories.impl.ts b/packages/frontend/src/components/MkDrive.stories.impl.ts
index 4394eebfda..00930af380 100644
--- a/packages/frontend/src/components/MkDrive.stories.impl.ts
+++ b/packages/frontend/src/components/MkDrive.stories.impl.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3';
import { http, HttpResponse } from 'msw';
import * as Misskey from 'misskey-js';
diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue
index 9fe1c7ef21..9f1364aec4 100644
--- a/packages/frontend/src/components/MkDrive.vue
+++ b/packages/frontend/src/components/MkDrive.vue
@@ -145,18 +145,19 @@ import { claimAchievement } from '@/utility/achievements.js';
import { prefer } from '@/preferences.js';
import { chooseFileFromPcAndUpload, selectDriveFolder } from '@/utility/drive.js';
import { store } from '@/store.js';
-import { isSeparatorNeeded, getSeparatorInfo, makeDateGroupedTimelineComputedRef } from '@/utility/timeline-date-separate.js';
+import { makeDateGroupedTimelineComputedRef } from '@/utility/timeline-date-separate.js';
import { globalEvents, useGlobalEvent } from '@/events.js';
import { checkDragDataType, getDragData, setDragData } from '@/drag-and-drop.js';
import { getDriveFileMenu } from '@/utility/get-drive-file-menu.js';
import { Paginator } from '@/utility/paginator.js';
const props = withDefaults(defineProps<{
- initialFolder?: Misskey.entities.DriveFolder['id'] | null;
+ initialFolder?: Misskey.entities.DriveFolder | Misskey.entities.DriveFolder['id'] | null;
type?: string;
multiple?: boolean;
select?: 'file' | 'folder' | null;
}>(), {
+ initialFolder: null,
multiple: false,
select: null,
});
@@ -293,7 +294,7 @@ function onDragleave() {
draghover.value = false;
}
-function onDrop(ev: DragEvent) {
+function onDrop(ev: DragEvent): void | boolean {
draghover.value = false;
if (!ev.dataTransfer) return;
@@ -363,7 +364,7 @@ function onDrop(ev: DragEvent) {
//#endregion
}
-function onUploadRequested(files: File[], folder: Misskey.entities.DriveFolder | null) {
+function onUploadRequested(files: File[], folder?: Misskey.entities.DriveFolder | null) {
os.launchUploader(files, {
folderId: folder?.id ?? null,
});
diff --git a/packages/frontend/src/components/MkDriveFileThumbnail.vue b/packages/frontend/src/components/MkDriveFileThumbnail.vue
index 88afdef114..3933421fc0 100644
--- a/packages/frontend/src/components/MkDriveFileThumbnail.vue
+++ b/packages/frontend/src/components/MkDriveFileThumbnail.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:forceBlurhash="forceBlurhash"
/>
<img
- v-else-if="isThumbnailAvailable"
+ v-else-if="isThumbnailAvailable && file.thumbnailUrl != null"
:src="file.thumbnailUrl"
:alt="file.name"
:title="file.name"
diff --git a/packages/frontend/src/components/MkDriveFolderSelectDialog.vue b/packages/frontend/src/components/MkDriveFolderSelectDialog.vue
index 2ebab1088f..d5b6b0cbec 100644
--- a/packages/frontend/src/components/MkDriveFolderSelectDialog.vue
+++ b/packages/frontend/src/components/MkDriveFolderSelectDialog.vue
@@ -39,13 +39,13 @@ withDefaults(defineProps<{
});
const emit = defineEmits<{
- (ev: 'done', r?: Misskey.entities.DriveFolder[]): void;
+ (ev: 'done', r?: (Misskey.entities.DriveFolder | null)[]): void;
(ev: 'closed'): void;
}>();
const dialog = useTemplateRef('dialog');
-const selected = ref<Misskey.entities.DriveFolder[]>([]);
+const selected = ref<(Misskey.entities.DriveFolder | null)[]>([]);
function ok() {
emit('done', selected.value);
@@ -57,7 +57,7 @@ function cancel() {
dialog.value?.close();
}
-function onChangeSelection(v: Misskey.entities.DriveFolder[]) {
+function onChangeSelection(v: (Misskey.entities.DriveFolder | null)[]) {
selected.value = v;
}
</script>
diff --git a/packages/frontend/src/components/MkEmojiPicker.stories.impl.ts b/packages/frontend/src/components/MkEmojiPicker.stories.impl.ts
index bf4158a2c8..cc934040f5 100644
--- a/packages/frontend/src/components/MkEmojiPicker.stories.impl.ts
+++ b/packages/frontend/src/components/MkEmojiPicker.stories.impl.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import { expect, userEvent, waitFor, within } from '@storybook/test';
import type { StoryObj } from '@storybook/vue3';
import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 68da098439..6904c417ce 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -141,6 +141,7 @@ import { $i } from '@/i.js';
import { checkReactionPermissions } from '@/utility/check-reaction-permissions.js';
import { prefer } from '@/preferences.js';
import { useRouter } from '@/router.js';
+import { haptic } from '@/utility/haptic.js';
const router = useRouter();
@@ -151,7 +152,7 @@ const props = withDefaults(defineProps<{
asDrawer?: boolean;
asWindow?: boolean;
asReactionPicker?: boolean; // 今ã¯ä½¿ã‚れã¦ãªã„ãŒå°†æ¥çš„ã«ä½¿ã„ãã†
- targetNote?: Misskey.entities.Note;
+ targetNote?: Misskey.entities.Note | null;
}>(), {
showPinned: true,
});
@@ -431,6 +432,8 @@ function chosen(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef,
const key = getKey(emoji);
emit('chosen', key);
+ haptic();
+
// 最近使ã£ãŸçµµæ–‡å­—æ›´æ–°
if (!pinned.value?.includes(key)) {
let recents = store.s.recentlyUsedEmojis;
@@ -495,7 +498,7 @@ function done(query?: string): boolean | void {
function settings() {
emit('esc');
- router.push('settings/emoji-palette');
+ router.push('/settings/emoji-palette');
}
onMounted(() => {
@@ -585,6 +588,14 @@ defineExpose({
grid-template-columns: var(--columns);
font-size: 30px;
+ > .config {
+ aspect-ratio: 1 / 1;
+ width: auto;
+ height: auto;
+ min-width: 0;
+ font-size: 14px;
+ }
+
> .item {
aspect-ratio: 1 / 1;
width: auto;
diff --git a/packages/frontend/src/components/MkEmojiPickerDialog.vue b/packages/frontend/src/components/MkEmojiPickerDialog.vue
index 1627dc8760..0ff4e8f38d 100644
--- a/packages/frontend/src/components/MkEmojiPickerDialog.vue
+++ b/packages/frontend/src/components/MkEmojiPickerDialog.vue
@@ -44,11 +44,11 @@ import { prefer } from '@/preferences.js';
const props = withDefaults(defineProps<{
manualShowing?: boolean | null;
- anchorElement?: HTMLElement;
+ anchorElement?: HTMLElement | null;
showPinned?: boolean;
pinnedEmojis?: string[],
asReactionPicker?: boolean;
- targetNote?: Misskey.entities.Note;
+ targetNote?: Misskey.entities.Note | null;
choseAndClose?: boolean;
}>(), {
manualShowing: null,
diff --git a/packages/frontend/src/components/MkExtensionInstaller.vue b/packages/frontend/src/components/MkExtensionInstaller.vue
index a2247d844b..c9d18ee731 100644
--- a/packages/frontend/src/components/MkExtensionInstaller.vue
+++ b/packages/frontend/src/components/MkExtensionInstaller.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInfo :warn="true">{{ i18n.ts._externalResourceInstaller.checkVendorBeforeInstall }}</MkInfo>
- <div v-if="isPlugin" class="_gaps_s">
+ <div v-if="extension.type === 'plugin'" class="_gaps_s">
<MkFolder :defaultOpen="true">
<template #icon><i class="ti ti-info-circle"></i></template>
<template #label>{{ i18n.ts.metadata }}</template>
@@ -60,7 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkCode :code="extension.raw"/>
</MkFolder>
</div>
- <div v-else-if="isTheme" class="_gaps_s">
+ <div v-else-if="extension.type === 'theme'" class="_gaps_s">
<MkFolder :defaultOpen="true">
<template #icon><i class="ti ti-info-circle"></i></template>
<template #label>{{ i18n.ts.metadata }}</template>
@@ -78,7 +78,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</FormSplit>
<MkKeyValue>
<template #key>{{ i18n.ts._externalResourceInstaller._meta.base }}</template>
- <template #value>{{ i18n.ts[extension.meta.base ?? 'none'] }}</template>
+ <template #value>{{ { light: i18n.ts.light, dark: i18n.ts.dark, none: i18n.ts.none }[extension.meta.base ?? 'none'] }}</template>
</MkKeyValue>
</div>
</MkFolder>
diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue
index b65f610986..c7361a19c6 100644
--- a/packages/frontend/src/components/MkFollowButton.vue
+++ b/packages/frontend/src/components/MkFollowButton.vue
@@ -46,6 +46,7 @@ import { claimAchievement } from '@/utility/achievements.js';
import { pleaseLogin } from '@/utility/please-login.js';
import { $i } from '@/i.js';
import { prefer } from '@/preferences.js';
+import { haptic } from '@/utility/haptic.js';
const props = withDefaults(defineProps<{
user: Misskey.entities.UserDetailed,
@@ -84,6 +85,8 @@ async function onClick() {
wait.value = true;
+ haptic();
+
try {
if (isFollowing.value) {
const { canceled } = await os.confirm({
diff --git a/packages/frontend/src/components/MkFormFooter.vue b/packages/frontend/src/components/MkFormFooter.vue
index 96214a9542..eb559e611c 100644
--- a/packages/frontend/src/components/MkFormFooter.vue
+++ b/packages/frontend/src/components/MkFormFooter.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div :class="$style.root">
+<div v-if="form.modified.value" :class="$style.root">
<div :class="$style.text">{{ i18n.tsx.thereAreNChanges({ n: form.modifiedCount.value }) }}</div>
<div style="margin-left: auto;" class="_buttons">
<MkButton danger rounded @click="form.discard"><i class="ti ti-x"></i> {{ i18n.ts.discard }}</MkButton>
@@ -16,16 +16,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { } from 'vue';
import MkButton from './MkButton.vue';
+import type { useForm } from '@/composables/use-form.js';
import { i18n } from '@/i18n.js';
const props = withDefaults(defineProps<{
- form: {
- modifiedCount: {
- value: number;
- };
- discard: () => void;
- save: () => void;
- };
+ form: ReturnType<typeof useForm>;
canSaving?: boolean;
}>(), {
canSaving: true,
diff --git a/packages/frontend/src/components/MkImageEffectorDialog.Layer.vue b/packages/frontend/src/components/MkImageEffectorDialog.Layer.vue
index d8466fa7ca..f734325039 100644
--- a/packages/frontend/src/components/MkImageEffectorDialog.Layer.vue
+++ b/packages/frontend/src/components/MkImageEffectorDialog.Layer.vue
@@ -14,73 +14,15 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</template>
- <div :class="$style.root" class="_gaps">
- <div v-for="[k, v] in Object.entries(fx.params)" :key="k">
- <MkSwitch
- v-if="v.type === 'boolean'"
- v-model="layer.params[k]"
- >
- <template #label>{{ fx.params[k].label ?? k }}</template>
- </MkSwitch>
- <MkRange
- v-else-if="v.type === 'number'"
- v-model="layer.params[k]"
- continuousUpdate
- :min="v.min"
- :max="v.max"
- :step="v.step"
- :textConverter="fx.params[k].toViewValue"
- @thumbDoubleClicked="() => {
- if (fx.params[k].default != null) {
- layer.params[k] = fx.params[k].default;
- } else {
- layer.params[k] = v.min;
- }
- }"
- >
- <template #label>{{ fx.params[k].label ?? k }}</template>
- </MkRange>
- <MkRadios
- v-else-if="v.type === 'number:enum'"
- v-model="layer.params[k]"
- >
- <template #label>{{ fx.params[k].label ?? k }}</template>
- <option v-for="item in v.enum" :value="item.value">{{ item.label }}</option>
- </MkRadios>
- <div v-else-if="v.type === 'seed'">
- <MkRange
- v-model="layer.params[k]"
- continuousUpdate
- type="number"
- :min="0"
- :max="10000"
- :step="1"
- >
- <template #label>{{ fx.params[k].label ?? k }}</template>
- </MkRange>
- </div>
- <MkInput
- v-else-if="v.type === 'color'"
- :modelValue="getHex(layer.params[k])"
- type="color"
- @update:modelValue="v => { const c = getRgb(v); if (c != null) layer.params[k] = c; }"
- >
- <template #label>{{ fx.params[k].label ?? k }}</template>
- </MkInput>
- </div>
- </div>
+ <MkImageEffectorFxForm v-model="layer.params" :paramDefs="fx.params" />
</MkFolder>
</template>
<script setup lang="ts">
import type { ImageEffectorLayer } from '@/utility/image-effector/ImageEffector.js';
-import { i18n } from '@/i18n.js';
import MkFolder from '@/components/MkFolder.vue';
import MkButton from '@/components/MkButton.vue';
-import MkInput from '@/components/MkInput.vue';
-import MkRadios from '@/components/MkRadios.vue';
-import MkSwitch from '@/components/MkSwitch.vue';
-import MkRange from '@/components/MkRange.vue';
+import MkImageEffectorFxForm from '@/components/MkImageEffectorFxForm.vue';
import { FXS } from '@/utility/image-effector/fxs.js';
const layer = defineModel<ImageEffectorLayer>('layer', { required: true });
@@ -94,28 +36,4 @@ const emit = defineEmits<{
(e: 'swapUp'): void;
(e: 'swapDown'): void;
}>();
-
-function getHex(c: [number, number, number]) {
- return `#${c.map(x => (x * 255).toString(16).padStart(2, '0')).join('')}`;
-}
-
-function getRgb(hex: string | number): [number, number, number] | null {
- if (
- typeof hex === 'number' ||
- typeof hex !== 'string' ||
- !/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(hex)
- ) {
- return null;
- }
-
- const m = hex.slice(1).match(/[0-9a-fA-F]{2}/g);
- if (m == null) return [0, 0, 0];
- return m.map(x => parseInt(x, 16) / 255) as [number, number, number];
-}
</script>
-
-<style module>
-.root {
-
-}
-</style>
diff --git a/packages/frontend/src/components/MkImageEffectorFxForm.vue b/packages/frontend/src/components/MkImageEffectorFxForm.vue
new file mode 100644
index 0000000000..d7ab620132
--- /dev/null
+++ b/packages/frontend/src/components/MkImageEffectorFxForm.vue
@@ -0,0 +1,95 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div class="_gaps">
+ <div v-for="v, k in paramDefs" :key="k">
+ <MkSwitch
+ v-if="v.type === 'boolean'"
+ v-model="params[k]">
+ <template #label>{{ v.label ?? k }}</template>
+ <template v-if="v.caption != null" #caption>{{ v.caption }}</template>
+ </MkSwitch>
+ <MkRange
+ v-else-if="v.type === 'number'"
+ v-model="params[k]"
+ continuousUpdate
+ :min="v.min"
+ :max="v.max"
+ :step="v.step"
+ :textConverter="v.toViewValue"
+ @thumbDoubleClicked="() => {
+ params[k] = v.default;
+ }"
+ >
+ <template #label>{{ v.label ?? k }}</template>
+ <template v-if="v.caption != null" #caption>{{ v.caption }}</template>
+ </MkRange>
+ <MkRadios v-else-if="v.type === 'number:enum'" v-model="params[k]">
+ <template #label>{{ v.label ?? k }}</template>
+ <template v-if="v.caption != null" #caption>{{ v.caption }}</template>
+ <option v-for="item in v.enum" :value="item.value">
+ <i v-if="item.icon" :class="item.icon"></i>
+ <template v-else>{{ item.label }}</template>
+ </option>
+ </MkRadios>
+ <div v-else-if="v.type === 'seed'">
+ <MkRange v-model="params[k]" continuousUpdate type="number" :min="0" :max="10000" :step="1">
+ <template #label>{{ v.label ?? k }}</template>
+ <template v-if="v.caption != null" #caption>{{ v.caption }}</template>
+ </MkRange>
+ </div>
+ <MkInput v-else-if="v.type === 'color'" :modelValue="getHex(params[k])" type="color" @update:modelValue="v => { const c = getRgb(v); if (c != null) params[k] = c; }">
+ <template #label>{{ v.label ?? k }}</template>
+ <template v-if="v.caption != null" #caption>{{ v.caption }}</template>
+ </MkInput>
+ </div>
+ <div v-if="Object.keys(paramDefs).length === 0" :class="$style.nothingToConfigure">
+ {{ i18n.ts._imageEffector.nothingToConfigure }}
+ </div>
+</div>
+</template>
+
+<script setup lang="ts">
+import MkInput from '@/components/MkInput.vue';
+import MkRadios from '@/components/MkRadios.vue';
+import MkSwitch from '@/components/MkSwitch.vue';
+import MkRange from '@/components/MkRange.vue';
+import { i18n } from '@/i18n.js';
+import type { ImageEffectorRGB, ImageEffectorFxParamDefs } from '@/utility/image-effector/ImageEffector.js';
+
+defineProps<{
+ paramDefs: ImageEffectorFxParamDefs;
+}>();
+
+const params = defineModel<Record<string, any>>({ required: true });
+
+function getHex(c: ImageEffectorRGB) {
+ return `#${c.map(x => (x * 255).toString(16).padStart(2, '0')).join('')}`;
+}
+
+function getRgb(hex: string | number): ImageEffectorRGB | null {
+ if (
+ typeof hex === 'number' ||
+ typeof hex !== 'string' ||
+ !/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(hex)
+ ) {
+ return null;
+ }
+
+ const m = hex.slice(1).match(/[0-9a-fA-F]{2}/g);
+ if (m == null) return [0, 0, 0];
+ return m.map(x => parseInt(x, 16) / 255) as ImageEffectorRGB;
+}
+</script>
+
+<style module>
+.nothingToConfigure {
+ opacity: 0.7;
+ text-align: center;
+ font-size: 14px;
+ padding: 0 10px;
+}
+</style>
diff --git a/packages/frontend/src/components/MkImgPreviewDialog.stories.impl.ts b/packages/frontend/src/components/MkImgPreviewDialog.stories.impl.ts
index 339e6d10f3..7da705a23f 100644
--- a/packages/frontend/src/components/MkImgPreviewDialog.stories.impl.ts
+++ b/packages/frontend/src/components/MkImgPreviewDialog.stories.impl.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { StoryObj } from '@storybook/vue3';
+import type { StoryObj } from '@storybook/vue3';
import { file } from '../../.storybook/fakes.js';
import MkImgPreviewDialog from './MkImgPreviewDialog.vue';
export const Default = {
diff --git a/packages/frontend/src/components/MkImgWithBlurhash.vue b/packages/frontend/src/components/MkImgWithBlurhash.vue
index 361aeff4d0..983a0932c3 100644
--- a/packages/frontend/src/components/MkImgWithBlurhash.vue
+++ b/packages/frontend/src/components/MkImgWithBlurhash.vue
@@ -52,15 +52,20 @@ import TestWebGL2 from '@/workers/test-webgl2?worker';
import { WorkerMultiDispatch } from '@@/js/worker-multi-dispatch.js';
import { extractAvgColorFromBlurhash } from '@@/js/extract-avg-color-from-blurhash.js';
+// テスト環境㧠Web Worker インスタンスã¯ä½œæˆã§ããªã„
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-expect-error
+const isTest = (import.meta.env.MODE === 'test' || window.Cypress != null);
+
const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resolve => {
- // テスト環境㧠Web Worker インスタンスã¯ä½œæˆã§ããªã„
- if (import.meta.env.MODE === 'test') {
+ if (isTest) {
const canvas = window.document.createElement('canvas');
canvas.width = 64;
canvas.height = 64;
resolve(canvas);
return;
}
+
const testWorker = new TestWebGL2();
testWorker.addEventListener('message', event => {
if (event.data.result) {
@@ -189,7 +194,7 @@ function drawAvg() {
}
async function draw() {
- if (import.meta.env.MODE === 'test' && props.hash == null) return;
+ if (isTest && props.hash == null) return;
drawAvg();
diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue
index 584afff55c..d8725ade0b 100644
--- a/packages/frontend/src/components/MkLaunchPad.vue
+++ b/packages/frontend/src/components/MkLaunchPad.vue
@@ -34,9 +34,10 @@ import { deviceKind } from '@/utility/device-kind.js';
import { prefer } from '@/preferences.js';
const props = withDefaults(defineProps<{
- anchorElement?: HTMLElement;
+ anchorElement?: HTMLElement | null;
anchor?: { x: string; y: string; };
}>(), {
+ anchorElement: null,
anchor: () => ({ x: 'right', y: 'center' }),
});
diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue
index 309ef727da..163f172f57 100644
--- a/packages/frontend/src/components/MkLink.vue
+++ b/packages/frontend/src/components/MkLink.vue
@@ -17,11 +17,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { defineAsyncComponent, ref } from 'vue';
import { url as local } from '@@/js/config.js';
+import { maybeMakeRelative } from '@@/js/url.js';
+import type { MkABehavior } from '@/components/global/MkA.vue';
import { useTooltip } from '@/composables/use-tooltip.js';
import * as os from '@/os.js';
import { isEnabledUrlPreview } from '@/utility/url-preview.js';
-import type { MkABehavior } from '@/components/global/MkA.vue';
-import { maybeMakeRelative } from '@@/js/url.js';
const props = withDefaults(defineProps<{
url: string;
@@ -39,10 +39,12 @@ const el = ref<HTMLElement | { $el: HTMLElement }>();
if (isEnabledUrlPreview.value) {
useTooltip(el, (showing) => {
+ const anchorElement = el.value instanceof HTMLElement ? el.value : el.value?.$el;
+ if (anchorElement == null) return;
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
showing,
url: props.url,
- source: el.value instanceof HTMLElement ? el.value : el.value?.$el,
+ anchorElement: anchorElement,
}, {
closed: () => dispose(),
});
diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue
index b7052ad918..e3bb39549f 100644
--- a/packages/frontend/src/components/MkMediaAudio.vue
+++ b/packages/frontend/src/components/MkMediaAudio.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@contextmenu.stop
@keydown.stop
>
- <button v-if="hide" :class="$style.hidden" @click="show">
+ <button v-if="hide" :class="$style.hidden" @click="reveal">
<div :class="$style.hiddenTextWrapper">
<b v-if="audio.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ prefer.s.dataSaver.media ? ` (${i18n.ts.audio}${audio.size ? ' ' + bytes(audio.size) : ''})` : '' }}</b>
<b v-else style="display: block;"><i class="ti ti-music"></i> {{ prefer.s.dataSaver.media && audio.size ? bytes(audio.size) : i18n.ts.audio }}</b>
@@ -157,7 +157,7 @@ const audioEl = useTemplateRef('audioEl');
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
const hide = ref((prefer.s.nsfw === 'force' || prefer.s.dataSaver.media) ? true : (props.audio.isSensitive && prefer.s.nsfw !== 'ignore'));
-async function show() {
+async function reveal() {
if (props.audio.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) {
const { canceled } = await os.confirm({
type: 'question',
diff --git a/packages/frontend/src/components/MkMediaBanner.vue b/packages/frontend/src/components/MkMediaBanner.vue
index f23cf507fb..7730e01a9f 100644
--- a/packages/frontend/src/components/MkMediaBanner.vue
+++ b/packages/frontend/src/components/MkMediaBanner.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="$style.root">
<MkMediaAudio v-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" :audio="media"/>
- <div v-else-if="media.isSensitive && hide" :class="$style.sensitive" @click="show">
+ <div v-else-if="media.isSensitive && hide" :class="$style.sensitive" @click="reveal">
<span style="font-size: 1.6em;"><i class="ti ti-alert-triangle"></i></span>
<b>{{ i18n.ts.sensitive }}</b>
<span>{{ i18n.ts.clickToShow }}</span>
@@ -37,7 +37,7 @@ const props = defineProps<{
const hide = ref(true);
-async function show() {
+async function reveal() {
if (props.media.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) {
const { canceled } = await os.confirm({
type: 'question',
diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue
index 1e5eb06a31..99ea606a11 100644
--- a/packages/frontend/src/components/MkMediaImage.vue
+++ b/packages/frontend/src/components/MkMediaImage.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div :class="[hide ? $style.hidden : $style.visible, (image.isSensitive && prefer.s.highlightSensitiveMedia) && $style.sensitive]" @click="onclick">
+<div :class="[hide ? $style.hidden : $style.visible, (image.isSensitive && prefer.s.highlightSensitiveMedia) && $style.sensitive]" @click="reveal">
<component
:is="disableImageLink ? 'div' : 'a'"
v-bind="disableImageLink ? {
@@ -96,10 +96,10 @@ const url = computed(() => (props.raw || prefer.s.loadRawImages)
? props.image.url
: prefer.s.disableShowingAnimatedImages
? getStaticImageUrl(props.image.url)
- : props.image.thumbnailUrl,
+ : props.image.thumbnailUrl!,
);
-async function onclick(ev: MouseEvent) {
+async function reveal(ev: MouseEvent) {
if (!props.controls) {
return;
}
diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue
index 4a1100c324..bfc8179e13 100644
--- a/packages/frontend/src/components/MkMediaList.vue
+++ b/packages/frontend/src/components/MkMediaList.vue
@@ -94,6 +94,8 @@ async function calcAspectRatio() {
onMounted(() => {
calcAspectRatio();
+ if (gallery.value == null) return; // TSを黙らã™ãŸã‚
+
lightbox = new PhotoSwipeLightbox({
dataSource: props.mediaList
.filter(media => {
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index 81a5ab27c7..b0f7a909d3 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@contextmenu.stop
@keydown.stop
>
- <button v-if="hide" :class="$style.hidden" @click="show">
+ <button v-if="hide" :class="$style.hidden" @click="reveal">
<div :class="$style.hiddenTextWrapper">
<b v-if="video.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ prefer.s.dataSaver.media ? ` (${i18n.ts.video}${video.size ? ' ' + bytes(video.size) : ''})` : '' }}</b>
<b v-else style="display: block;"><i class="ti ti-movie"></i> {{ prefer.s.dataSaver.media && video.size ? bytes(video.size) : i18n.ts.video }}</b>
@@ -178,7 +178,7 @@ function hasFocus() {
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
const hide = ref((prefer.s.nsfw === 'force' || prefer.s.dataSaver.media) ? true : (props.video.isSensitive && prefer.s.nsfw !== 'ignore'));
-async function show() {
+async function reveal() {
if (props.video.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) {
const { canceled } = await os.confirm({
type: 'question',
diff --git a/packages/frontend/src/components/MkMenu.child.vue b/packages/frontend/src/components/MkMenu.child.vue
index f7cd72b6c6..37c3a3f5e3 100644
--- a/packages/frontend/src/components/MkMenu.child.vue
+++ b/packages/frontend/src/components/MkMenu.child.vue
@@ -16,7 +16,7 @@ import type { MenuItem } from '@/types/menu.js';
const props = defineProps<{
items: MenuItem[];
- targetElement: HTMLElement;
+ anchorElement: HTMLElement;
rootElement: HTMLElement;
width?: number;
}>();
@@ -36,10 +36,10 @@ const SCROLLBAR_THICKNESS = 16;
function setPosition() {
if (el.value == null) return;
const rootRect = props.rootElement.getBoundingClientRect();
- const parentRect = props.targetElement.getBoundingClientRect();
+ const parentRect = props.anchorElement.getBoundingClientRect();
const myRect = el.value.getBoundingClientRect();
- let left = props.targetElement.offsetWidth;
+ let left = props.anchorElement.offsetWidth;
let top = (parentRect.top - rootRect.top) - 8;
if (rootRect.left + left + myRect.width >= (window.innerWidth - SCROLLBAR_THICKNESS)) {
left = -myRect.width;
@@ -59,7 +59,7 @@ function onChildClosed(actioned?: boolean) {
}
}
-watch(() => props.targetElement, () => {
+watch(() => props.anchorElement, () => {
setPosition();
});
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index fbae4f0d8a..6c8fac934c 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -208,7 +208,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</span>
</div>
<div v-if="childMenu">
- <XChild ref="child" :items="childMenu" :targetElement="childTarget!" :rootElement="itemsEl!" @actioned="childActioned" @closed="closeChild"/>
+ <XChild ref="child" :items="childMenu" :anchorElement="childTarget!" :rootElement="itemsEl!" @actioned="childActioned" @closed="closeChild"/>
</div>
</div>
</template>
diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue
index 06686ddfc0..660d5a26be 100644
--- a/packages/frontend/src/components/MkModal.vue
+++ b/packages/frontend/src/components/MkModal.vue
@@ -91,7 +91,7 @@ const emit = defineEmits<{
(ev: 'opened'): void;
(ev: 'click'): void;
(ev: 'esc'): void;
- (ev: 'close'): void;
+ (ev: 'close'): void; // TODO: (refactor) closing ã«æ”¹åã™ã‚‹
(ev: 'closed'): void;
}>();
@@ -148,7 +148,6 @@ function close(opts: { useSendAnimation?: boolean } = {}) {
useSendAnime.value = true;
}
- // eslint-disable-next-line vue/no-mutating-props
if (props.anchorElement) props.anchorElement.style.pointerEvents = 'auto';
showing.value = false;
emit('close');
@@ -319,7 +318,6 @@ const alignObserver = new ResizeObserver((entries, observer) => {
onMounted(() => {
watch(() => props.anchorElement, async () => {
if (props.anchorElement) {
- // eslint-disable-next-line vue/no-mutating-props
props.anchorElement.style.pointerEvents = 'none';
}
fixed.value = (type.value === 'drawer') || (getFixedContainer(props.anchorElement) != null);
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 0605030d5b..729bded03c 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:class="[$style.root, { [$style.showActionsOnlyHover]: prefer.s.showNoteActionsOnlyHover, [$style.skipRender]: prefer.s.skipNoteRender }]"
tabindex="0"
>
- <MkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
+ <MkNoteSub v-if="appearNote.replyId && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
<div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div>
<div v-if="isRenote" :class="$style.renote">
<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
@@ -99,7 +99,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="isEnabledUrlPreview">
<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :class="$style.urlPreview"/>
</div>
- <div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div>
+ <div v-if="appearNote.renoteId" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div>
<button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click="collapsed = false">
<span :class="$style.collapsedLabel">{{ i18n.ts.showMore }}</span>
</button>
@@ -265,24 +265,22 @@ const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', nul
let note = deepClone(props.note);
-// コンãƒãƒ¼ãƒãƒ³ãƒˆåˆæœŸåŒ–ã«éžåŒæœŸçš„ãªå‡¦ç†ã‚’行ã†ã¨Transitionã®ãƒ¬ãƒ³ãƒ€ãƒªãƒ³ã‚°ãŒãƒã‚°ã‚‹ãŸã‚åŒæœŸçš„ã«å®Ÿè¡Œã§ãるメソッドãŒå®Ÿè£…ã•れるã®ã‚’å¾…ã¤å¿…è¦ãŒã‚ã‚‹
-// https://github.com/aiscript-dev/aiscript/issues/937
-//// plugin
-//const noteViewInterruptors = getPluginHandlers('note_view_interruptor');
-//if (noteViewInterruptors.length > 0) {
-// let result: Misskey.entities.Note | null = deepClone(note);
-// for (const interruptor of noteViewInterruptors) {
-// try {
-// result = await interruptor.handler(result!) as Misskey.entities.Note | null;
-// } catch (err) {
-// console.error(err);
-// }
-// }
-// note = result as Misskey.entities.Note;
-//}
+// plugin
+const noteViewInterruptors = getPluginHandlers('note_view_interruptor');
+if (noteViewInterruptors.length > 0) {
+ let result: Misskey.entities.Note | null = deepClone(note);
+ for (const interruptor of noteViewInterruptors) {
+ try {
+ result = interruptor.handler(result!) as Misskey.entities.Note | null;
+ } catch (err) {
+ console.error(err);
+ }
+ }
+ note = result as Misskey.entities.Note;
+}
const isRenote = Misskey.note.isPureRenote(note);
-const appearNote = getAppearNote(note);
+const appearNote = getAppearNote(note) ?? note;
const { $note: $appearNote, subscribe: subscribeManuallyToNoteCapture } = useNoteCapture({
note: appearNote,
parentNote: note,
@@ -431,7 +429,7 @@ if (!props.mock) {
showing,
users,
count: appearNote.renoteCount,
- targetElement: renoteButton.value,
+ anchorElement: renoteButton.value,
}, {
closed: () => dispose(),
});
@@ -454,7 +452,7 @@ if (!props.mock) {
reaction: 'â¤ï¸',
users,
count: $appearNote.reactionCount,
- targetElement: reactButton.value!,
+ anchorElement: reactButton.value!,
}, {
closed: () => dispose(),
});
@@ -462,14 +460,12 @@ if (!props.mock) {
}
}
-function renote(viaKeyboard = false) {
+function renote() {
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
showMovedDialog();
const { menu } = getRenoteMenu({ note: note, renoteButton, mock: props.mock });
- os.popupMenu(menu, renoteButton.value, {
- viaKeyboard,
- });
+ os.popupMenu(menu, renoteButton.value);
subscribeManuallyToNoteCapture();
}
@@ -658,7 +654,7 @@ function showRenoteMenu(): void {
getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote),
{ type: 'divider' },
getAbuseNoteMenu(note, i18n.ts.reportAbuseRenote),
- ($i?.isModerator || $i?.isAdmin) ? getUnrenote() : undefined,
+ ...(($i?.isModerator || $i?.isAdmin) ? [getUnrenote()] : []),
], renoteTime.value);
}
}
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index fb37bb1ae6..48fd9908bd 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<MkNoteSub v-for="note in conversation" :key="note.id" :class="$style.replyToMore" :note="note"/>
</div>
- <MkNoteSub v-if="appearNote.reply" :note="appearNote.reply" :class="$style.replyTo"/>
+ <MkNoteSub v-if="appearNote.replyId" :note="appearNote.reply" :class="$style.replyTo"/>
<div v-if="isRenote" :class="$style.renote">
<MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/>
<i class="ti ti-repeat" style="margin-right: 4px;"></i>
@@ -287,23 +287,22 @@ const inChannel = inject('inChannel', null);
let note = deepClone(props.note);
-// コンãƒãƒ¼ãƒãƒ³ãƒˆåˆæœŸåŒ–ã«éžåŒæœŸçš„ãªå‡¦ç†ã‚’行ã†ã¨Transitionã®ãƒ¬ãƒ³ãƒ€ãƒªãƒ³ã‚°ãŒãƒã‚°ã‚‹ãŸã‚åŒæœŸçš„ã«å®Ÿè¡Œã§ãるメソッドãŒå®Ÿè£…ã•れるã®ã‚’å¾…ã¤å¿…è¦ãŒã‚ã‚‹
-//// plugin
-//const noteViewInterruptors = getPluginHandlers('note_view_interruptor');
-//if (noteViewInterruptors.length > 0) {
-// let result: Misskey.entities.Note | null = deepClone(note);
-// for (const interruptor of noteViewInterruptors) {
-// try {
-// result = await interruptor.handler(result!) as Misskey.entities.Note | null;
-// } catch (err) {
-// console.error(err);
-// }
-// }
-// note = result as Misskey.entities.Note;
-//}
+// plugin
+const noteViewInterruptors = getPluginHandlers('note_view_interruptor');
+if (noteViewInterruptors.length > 0) {
+ let result: Misskey.entities.Note | null = deepClone(note);
+ for (const interruptor of noteViewInterruptors) {
+ try {
+ result = interruptor.handler(result!) as Misskey.entities.Note | null;
+ } catch (err) {
+ console.error(err);
+ }
+ }
+ note = result as Misskey.entities.Note;
+}
const isRenote = Misskey.note.isPureRenote(note);
-const appearNote = getAppearNote(note);
+const appearNote = getAppearNote(note) ?? note;
const { $note: $appearNote, subscribe: subscribeManuallyToNoteCapture } = useNoteCapture({
note: appearNote,
parentNote: note,
@@ -393,6 +392,9 @@ const reactionsPaginator = markRaw(new Paginator('notes/reactions', {
}));
useTooltip(renoteButton, async (showing) => {
+ const anchorElement = renoteButton.value;
+ if (anchorElement == null) return;
+
const renotes = await misskeyApi('notes/renotes', {
noteId: appearNote.id,
limit: 11,
@@ -406,7 +408,7 @@ useTooltip(renoteButton, async (showing) => {
showing,
users,
count: appearNote.renoteCount,
- targetElement: renoteButton.value,
+ anchorElement: anchorElement,
}, {
closed: () => dispose(),
});
@@ -429,7 +431,7 @@ if (appearNote.reactionAcceptance === 'likeOnly') {
reaction: 'â¤ï¸',
users,
count: $appearNote.reactionCount,
- targetElement: reactButton.value!,
+ anchorElement: reactButton.value!,
}, {
closed: () => dispose(),
});
diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue
index e684cf2a30..ed0b3ad555 100644
--- a/packages/frontend/src/components/MkNoteSimple.vue
+++ b/packages/frontend/src/components/MkNoteSimple.vue
@@ -4,8 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div :class="$style.root">
- <MkAvatar :class="$style.avatar" :user="note.user" link preview/>
+<div v-if="note" :class="$style.root">
+ <MkAvatar :class="[$style.avatar, prefer.s.useStickyIcons ? $style.useSticky : null]" :user="note.user" link preview/>
<div :class="$style.main">
<MkNoteHeader :class="$style.header" :note="note" :mini="true"/>
<div>
@@ -19,6 +19,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</div>
+<div v-else :class="$style.deleted">
+ {{ i18n.ts.deletedNote }}
+</div>
</template>
<script lang="ts" setup>
@@ -27,9 +30,11 @@ import * as Misskey from 'misskey-js';
import MkNoteHeader from '@/components/MkNoteHeader.vue';
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
import MkCwButton from '@/components/MkCwButton.vue';
+import { i18n } from '@/i18n.js';
+import { prefer } from '@/preferences.js';
const props = defineProps<{
- note: Misskey.entities.Note;
+ note: Misskey.entities.Note | null;
}>();
const showContent = ref(false);
@@ -50,9 +55,12 @@ const showContent = ref(false);
width: 34px;
height: 34px;
border-radius: 8px;
- position: sticky !important;
- top: calc(16px + var(--MI-stickyTop, 0px));
- left: 0;
+
+ &.useSticky {
+ position: sticky !important;
+ top: calc(16px + var(--MI-stickyTop, 0px));
+ left: 0;
+ }
}
.main {
@@ -101,4 +109,14 @@ const showContent = ref(false);
height: 48px;
}
}
+
+.deleted {
+ text-align: center;
+ padding: 8px !important;
+ margin: 8px 8px 0 8px;
+ --color: light-dark(rgba(0, 0, 0, 0.05), rgba(0, 0, 0, 0.15));
+ background-size: auto auto;
+ background-image: repeating-linear-gradient(135deg, transparent, transparent 10px, var(--color) 4px, var(--color) 14px);
+ border-radius: 8px;
+}
</style>
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 4fd1c210cb..3f5cc51938 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -4,7 +4,10 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div v-if="!muted" :class="[$style.root, { [$style.children]: depth > 1 }]">
+<div v-if="note == null" :class="$style.deleted">
+ {{ i18n.ts.deletedNote }}
+</div>
+<div v-else-if="!muted" :class="[$style.root, { [$style.children]: depth > 1 }]">
<div :class="$style.main">
<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
@@ -53,7 +56,7 @@ import { userPage } from '@/filters/user.js';
import { checkWordMute } from '@/utility/check-word-mute.js';
const props = withDefaults(defineProps<{
- note: Misskey.entities.Note;
+ note: Misskey.entities.Note | null;
detail?: boolean;
// how many notes are in between this one and the note being viewed in detail
@@ -62,12 +65,12 @@ const props = withDefaults(defineProps<{
depth: 1,
});
-const muted = ref($i ? checkWordMute(props.note, $i, $i.mutedWords) : false);
+const muted = ref(props.note && $i ? checkWordMute(props.note, $i, $i.mutedWords) : false);
const showContent = ref(false);
const replies = ref<Misskey.entities.Note[]>([]);
-if (props.detail) {
+if (props.detail && props.note) {
misskeyApi('notes/children', {
noteId: props.note.id,
limit: 5,
@@ -160,4 +163,14 @@ if (props.detail) {
margin: 8px 8px 0 8px;
border-radius: 8px;
}
+
+.deleted {
+ text-align: center;
+ padding: 8px !important;
+ margin: 8px 8px 0 8px;
+ --color: light-dark(rgba(0, 0, 0, 0.05), rgba(0, 0, 0, 0.15));
+ background-size: auto auto;
+ background-image: repeating-linear-gradient(135deg, transparent, transparent 10px, var(--color) 4px, var(--color) 14px);
+ border-radius: 8px;
+}
</style>
diff --git a/packages/frontend/src/components/MkNotesTimeline.vue b/packages/frontend/src/components/MkNotesTimeline.vue
index 83af7db26f..d94cf3924c 100644
--- a/packages/frontend/src/components/MkNotesTimeline.vue
+++ b/packages/frontend/src/components/MkNotesTimeline.vue
@@ -4,21 +4,28 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkPagination :paginator="paginator" :autoLoad="autoLoad" :pullToRefresh="pullToRefresh" :withControl="withControl">
+<MkPagination :paginator="paginator" :direction="direction" :autoLoad="autoLoad" :pullToRefresh="pullToRefresh" :withControl="withControl">
<template #empty><MkResult type="empty" :text="i18n.ts.noNotes"/></template>
<template #default="{ items: notes }">
<div :class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap }]">
<template v-for="(note, i) in notes" :key="note.id">
- <div v-if="i > 0 && isSeparatorNeeded(paginator.items.value[i -1].createdAt, note.createdAt)" :data-scroll-anchor="note.id">
- <div :class="$style.date">
- <span><i class="ti ti-chevron-up"></i> {{ getSeparatorInfo(paginator.items.value[i -1].createdAt, note.createdAt).prevText }}</span>
+ <div
+ v-if="i > 0 && isSeparatorNeeded(paginator.items.value[i - 1].createdAt, note.createdAt)"
+ :data-scroll-anchor="note.id"
+ :class="{ '_gaps': !noGap }"
+ >
+ <div :class="[$style.date, { [$style.noGap]: noGap }]">
+ <span><i class="ti ti-chevron-up"></i> {{ getSeparatorInfo(paginator.items.value[i - 1].createdAt, note.createdAt)?.prevText }}</span>
<span style="height: 1em; width: 1px; background: var(--MI_THEME-divider);"></span>
- <span>{{ getSeparatorInfo(paginator.items.value[i -1].createdAt, note.createdAt).nextText }} <i class="ti ti-chevron-down"></i></span>
+ <span>{{ getSeparatorInfo(paginator.items.value[i - 1].createdAt, note.createdAt)?.nextText }} <i class="ti ti-chevron-down"></i></span>
</div>
<MkNote :class="$style.note" :note="note" :withHardMute="true"/>
+ <div v-if="note._shouldInsertAd_" :class="$style.ad">
+ <MkAd :preferForms="['horizontal', 'horizontal-big']"/>
+ </div>
</div>
- <div v-else-if="note._shouldInsertAd_" :class="[$style.noteWithAd, { '_gaps': !noGap }]" :data-scroll-anchor="note.id">
+ <div v-else-if="note._shouldInsertAd_" :class="{ '_gaps': !noGap }" :data-scroll-anchor="note.id">
<MkNote :class="$style.note" :note="note" :withHardMute="true"/>
<div :class="$style.ad">
<MkAd :preferForms="['horizontal', 'horizontal-big']"/>
@@ -43,11 +50,14 @@ import { isSeparatorNeeded, getSeparatorInfo } from '@/utility/timeline-date-sep
const props = withDefaults(defineProps<{
paginator: T;
noGap?: boolean;
+
+ direction?: 'up' | 'down' | 'both';
autoLoad?: boolean;
pullToRefresh?: boolean;
withControl?: boolean;
}>(), {
autoLoad: true,
+ direction: 'down',
pullToRefresh: true,
withControl: true,
});
@@ -103,7 +113,10 @@ defineExpose({
opacity: 0.75;
padding: 8px 8px;
margin: 0 auto;
- border-bottom: solid 0.5px var(--MI_THEME-divider);
+
+ &.noGap {
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
+ }
}
.ad:empty {
diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue
index 1310ea6a77..d21e09a984 100644
--- a/packages/frontend/src/components/MkPageWindow.vue
+++ b/packages/frontend/src/components/MkPageWindow.vue
@@ -23,8 +23,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<div :class="$style.root" class="_forceShrinkSpacer">
- <StackingRouterView v-if="prefer.s['experimental.stackingRouterView']" :key="reloadCount" :router="windowRouter"/>
- <RouterView v-else :key="reloadCount" :router="windowRouter"/>
+ <StackingRouterView v-if="prefer.s['experimental.stackingRouterView']" :key="reloadCount.toString() + ':stacking'" :router="windowRouter"/>
+ <RouterView v-else :key="reloadCount.toString() + ':non-stacking'" :router="windowRouter"/>
</div>
</MkWindow>
</template>
@@ -58,20 +58,15 @@ const windowRouter = createRouter(props.initialPath);
const pageMetadata = ref<null | PageMetadata>(null);
const windowEl = useTemplateRef('windowEl');
-const history = ref<{ path: string; }[]>([{
+const _history_ = ref<{ path: string; }[]>([{
path: windowRouter.getCurrentFullPath(),
}]);
const buttonsLeft = computed(() => {
- const buttons: Record<string, unknown>[] = [];
-
- if (history.value.length > 1) {
- buttons.push({
- icon: 'ti ti-arrow-left',
- onClick: back,
- });
- }
-
- return buttons;
+ return _history_.value.length > 1 ? [{
+ icon: 'ti ti-arrow-left',
+ title: i18n.ts.goBack,
+ onClick: back,
+ }] : [];
});
const buttonsRight = computed(() => {
const buttons = [{
@@ -97,12 +92,12 @@ function getSearchMarker(path: string) {
const searchMarkerId = ref<string | null>(getSearchMarker(props.initialPath));
windowRouter.addListener('push', ctx => {
- history.value.push({ path: ctx.fullPath });
+ _history_.value.push({ path: ctx.fullPath });
});
windowRouter.addListener('replace', ctx => {
- history.value.pop();
- history.value.push({ path: ctx.fullPath });
+ _history_.value.pop();
+ _history_.value.push({ path: ctx.fullPath });
});
windowRouter.addListener('change', ctx => {
@@ -150,8 +145,8 @@ const contextmenu = computed(() => ([{
}]));
function back() {
- history.value.pop();
- windowRouter.replace(history.value.at(-1)!.path);
+ _history_.value.pop();
+ windowRouter.replaceByPath(_history_.value.at(-1)!.path);
}
function reload() {
@@ -163,7 +158,7 @@ function close() {
}
function expand() {
- mainRouter.push(windowRouter.getCurrentFullPath(), 'forcePage');
+ mainRouter.pushByPath(windowRouter.getCurrentFullPath(), 'forcePage');
windowEl.value?.close();
}
diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue
index 8ca1c80e84..4ea62f2812 100644
--- a/packages/frontend/src/components/MkPagination.vue
+++ b/packages/frontend/src/components/MkPagination.vue
@@ -25,15 +25,15 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div v-else key="_root_" class="_gaps">
- <slot :items="unref(paginator.items)" :fetching="paginator.fetching.value || paginator.fetchingOlder.value"></slot>
- <div v-if="paginator.order.value === 'oldest'">
- <MkButton v-if="!paginator.fetchingNewer.value" :class="$style.more" :wait="paginator.fetchingNewer.value" primary rounded @click="paginator.fetchNewer()">
+ <div v-if="direction === 'up' || direction === 'both'" v-show="upButtonVisible">
+ <MkButton v-if="!upButtonLoading" :class="$style.more" primary rounded @click="upButtonClick">
{{ i18n.ts.loadMore }}
</MkButton>
<MkLoading v-else/>
</div>
- <div v-else v-show="paginator.canFetchOlder.value">
- <MkButton v-if="!paginator.fetchingOlder.value" :class="$style.more" :wait="paginator.fetchingOlder.value" primary rounded @click="paginator.fetchOlder()">
+ <slot :items="unref(paginator.items)" :fetching="paginator.fetching.value || paginator.fetchingOlder.value"></slot>
+ <div v-if="direction === 'down' || direction === 'both'" v-show="downButtonVisible">
+ <MkButton v-if="!downButtonLoading" :class="$style.more" primary rounded @click="downButtonClick">
{{ i18n.ts.loadMore }}
</MkButton>
<MkLoading v-else/>
@@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup generic="T extends IPaginator">
import { isLink } from '@@/js/is-link.js';
-import { onMounted, watch, unref } from 'vue';
+import { onMounted, computed, watch, unref } from 'vue';
import type { UnwrapRef } from 'vue';
import type { IPaginator } from '@/utility/paginator.js';
import MkButton from '@/components/MkButton.vue';
@@ -58,11 +58,20 @@ import * as os from '@/os.js';
const props = withDefaults(defineProps<{
paginator: T;
+
+ // ページãƒãƒ¼ã‚·ãƒ§ãƒ³ã‚’進ã‚ã‚‹æ–¹å‘
+ // up: 上方å‘
+ // down: ä¸‹æ–¹å‘ (default)
+ // both: åŒæ–¹å‘
+ // NOTE: ã“ã®æ–¹å‘ã¯ãƒšãƒ¼ã‚¸ãƒãƒ¼ã‚·ãƒ§ãƒ³ã®æ–¹å‘ã§ã‚ã£ã¦ã€ã‚¢ã‚¤ãƒ†ãƒ ã®ä¸¦ã³é †ã§ã¯ãªã„
+ direction?: 'up' | 'down' | 'both';
+
autoLoad?: boolean;
pullToRefresh?: boolean;
withControl?: boolean;
}>(), {
autoLoad: true,
+ direction: 'down',
pullToRefresh: true,
withControl: false,
});
@@ -93,6 +102,36 @@ if (props.paginator.computedParams) {
}, { immediate: false, deep: true });
}
+const upButtonVisible = computed(() => {
+ return props.paginator.order.value === 'oldest' ? props.paginator.canFetchOlder.value : props.paginator.canFetchNewer.value;
+});
+const upButtonLoading = computed(() => {
+ return props.paginator.order.value === 'oldest' ? props.paginator.fetchingOlder.value : props.paginator.fetchingNewer.value;
+});
+
+function upButtonClick() {
+ if (props.paginator.order.value === 'oldest') {
+ props.paginator.fetchOlder();
+ } else {
+ props.paginator.fetchNewer();
+ }
+}
+
+const downButtonVisible = computed(() => {
+ return props.paginator.order.value === 'oldest' ? props.paginator.canFetchNewer.value : props.paginator.canFetchOlder.value;
+});
+const downButtonLoading = computed(() => {
+ return props.paginator.order.value === 'oldest' ? props.paginator.fetchingNewer.value : props.paginator.fetchingOlder.value;
+});
+
+function downButtonClick() {
+ if (props.paginator.order.value === 'oldest') {
+ props.paginator.fetchNewer();
+ } else {
+ props.paginator.fetchOlder();
+ }
+}
+
defineSlots<{
empty: () => void;
default: (props: { items: UnwrapRef<T['items']> }) => void;
diff --git a/packages/frontend/src/components/MkPollEditor.vue b/packages/frontend/src/components/MkPollEditor.vue
index 22fe189a63..174c923bcf 100644
--- a/packages/frontend/src/components/MkPollEditor.vue
+++ b/packages/frontend/src/components/MkPollEditor.vue
@@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkInput>
</section>
<section v-else-if="expiration === 'after'">
- <MkInput v-model="after" small type="number" min="1" class="input">
+ <MkInput v-model="after" small type="number" :min="1" class="input">
<template #label>{{ i18n.ts._poll.duration }}</template>
</MkInput>
<MkSelect v-model="unit" small>
diff --git a/packages/frontend/src/components/MkPositionSelector.vue b/packages/frontend/src/components/MkPositionSelector.vue
index 002950cdf1..739f55125b 100644
--- a/packages/frontend/src/components/MkPositionSelector.vue
+++ b/packages/frontend/src/components/MkPositionSelector.vue
@@ -44,6 +44,11 @@ const y = defineModel<string>('y', { default: 'center' });
height: 32px;
background: var(--MI_THEME-panel);
border-radius: 4px;
+ transition: background 0.1s ease;
+
+ &:not(.active):hover {
+ background: var(--MI_THEME-buttonHoverBg);
+ }
&.active {
background: var(--MI_THEME-accentedBg);
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 174a73e0fd..56683b8f8c 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -907,6 +907,11 @@ async function post(ev?: MouseEvent) {
if (uploader.items.value.some(x => x.uploaded == null)) {
await uploadFiles();
+
+ // アップロード失敗ã—ãŸã‚‚ã®ãŒã‚ã£ãŸã‚‰ä¸­æ­¢
+ if (uploader.items.value.some(x => x.uploaded == null)) {
+ return;
+ }
}
let postData = {
@@ -954,7 +959,16 @@ async function post(ev?: MouseEvent) {
if (postAccount.value) {
const storedAccounts = await getAccounts();
- token = storedAccounts.find(x => x.id === postAccount.value?.id)?.token;
+ const storedAccount = storedAccounts.find(x => x.id === postAccount.value?.id);
+ if (storedAccount && storedAccount.token != null) {
+ token = storedAccount.token;
+ } else {
+ await os.alert({
+ type: 'error',
+ text: 'cannot find the token of the selected account.',
+ });
+ return;
+ }
}
posting.value = true;
diff --git a/packages/frontend/src/components/MkPostFormDialog.vue b/packages/frontend/src/components/MkPostFormDialog.vue
index 1f7796bd83..bf332e706e 100644
--- a/packages/frontend/src/components/MkPostFormDialog.vue
+++ b/packages/frontend/src/components/MkPostFormDialog.vue
@@ -57,7 +57,7 @@ async function _close() {
modal.value?.close();
}
-function onEsc(ev: KeyboardEvent) {
+function onEsc() {
_close();
}
diff --git a/packages/frontend/src/components/MkPullToRefresh.vue b/packages/frontend/src/components/MkPullToRefresh.vue
index c792ff3488..89aca5d29b 100644
--- a/packages/frontend/src/components/MkPullToRefresh.vue
+++ b/packages/frontend/src/components/MkPullToRefresh.vue
@@ -27,6 +27,7 @@ import { onMounted, onUnmounted, ref, useTemplateRef } from 'vue';
import { getScrollContainer } from '@@/js/scroll.js';
import { i18n } from '@/i18n.js';
import { isHorizontalSwipeSwiping } from '@/utility/touch.js';
+import { haptic } from '@/utility/haptic.js';
const SCROLL_STOP = 10;
const MAX_PULL_DISTANCE = Infinity;
@@ -203,6 +204,8 @@ function moving(event: MouseEvent | TouchEvent) {
pullDistance.value = Math.min(Math.max(moveHeight, 0), MAX_PULL_DISTANCE);
isPulledEnough.value = pullDistance.value >= FIRE_THRESHOLD;
+
+ if (isPulledEnough.value) haptic();
}
/**
diff --git a/packages/frontend/src/components/MkPushNotificationAllowButton.vue b/packages/frontend/src/components/MkPushNotificationAllowButton.vue
index 9c37eb5e72..c651d3a3f5 100644
--- a/packages/frontend/src/components/MkPushNotificationAllowButton.vue
+++ b/packages/frontend/src/components/MkPushNotificationAllowButton.vue
@@ -78,7 +78,7 @@ function subscribe() {
// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
return promiseDialog(registration.value.pushManager.subscribe({
userVisibleOnly: true,
- applicationServerKey: urlBase64ToUint8Array(instance.swPublickey),
+ applicationServerKey: urlBase64ToBase64(instance.swPublickey),
})
.then(async subscription => {
pushSubscription.value = subscription;
@@ -131,22 +131,16 @@ function encode(buffer: ArrayBuffer | null) {
}
/**
- * Convert the URL safe base64 string to a Uint8Array
+ * Convert the URL safe base64 string to a base64 string
* @param base64String base64 string
*/
-function urlBase64ToUint8Array(base64String: string): Uint8Array {
+function urlBase64ToBase64(base64String: string): string {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/-/g, '+')
.replace(/_/g, '/');
- const rawData = window.atob(base64);
- const outputArray = new Uint8Array(rawData.length);
-
- for (let i = 0; i < rawData.length; ++i) {
- outputArray[i] = rawData.charCodeAt(i);
- }
- return outputArray;
+ return base64;
}
if (navigator.serviceWorker == null) {
diff --git a/packages/frontend/src/components/MkRange.vue b/packages/frontend/src/components/MkRange.vue
index 67a9094cad..c0acfa8c60 100644
--- a/packages/frontend/src/components/MkRange.vue
+++ b/packages/frontend/src/components/MkRange.vue
@@ -167,7 +167,7 @@ function onMouseenter() {
text: computed(() => {
return props.textConverter(finalValue.value);
}),
- targetElement: thumbEl.value ?? undefined,
+ anchorElement: thumbEl.value ?? undefined,
}, {
closed: () => dispose(),
});
@@ -191,7 +191,7 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
text: computed(() => {
return props.textConverter(finalValue.value);
}),
- targetElement: thumbEl.value ?? undefined,
+ anchorElement: thumbEl.value ?? undefined,
}, {
closed: () => dispose(),
});
diff --git a/packages/frontend/src/components/MkReactionIcon.vue b/packages/frontend/src/components/MkReactionIcon.vue
index 7d62456e03..2bfdfa7599 100644
--- a/packages/frontend/src/components/MkReactionIcon.vue
+++ b/packages/frontend/src/components/MkReactionIcon.vue
@@ -28,7 +28,7 @@ if (props.withTooltip) {
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkReactionTooltip.vue')), {
showing,
reaction: props.reaction.replace(/^:(\w+):$/, ':$1@.:'),
- targetElement: elRef.value.$el,
+ anchorElement: elRef.value.$el,
}, {
closed: () => dispose(),
});
diff --git a/packages/frontend/src/components/MkReactionTooltip.vue b/packages/frontend/src/components/MkReactionTooltip.vue
index 77ca841ad0..971ebc060b 100644
--- a/packages/frontend/src/components/MkReactionTooltip.vue
+++ b/packages/frontend/src/components/MkReactionTooltip.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkTooltip ref="tooltip" :showing="showing" :targetElement="targetElement" :maxWidth="340" @closed="emit('closed')">
+<MkTooltip ref="tooltip" :showing="showing" :anchorElement="anchorElement" :maxWidth="340" @closed="emit('closed')">
<div :class="$style.root">
<MkReactionIcon :reaction="reaction" :class="$style.icon" :noStyle="true"/>
<div :class="$style.name">{{ reaction.replace('@.', '') }}</div>
@@ -20,7 +20,7 @@ import MkReactionIcon from '@/components/MkReactionIcon.vue';
defineProps<{
showing: boolean;
reaction: string;
- targetElement: HTMLElement;
+ anchorElement: HTMLElement;
}>();
const emit = defineEmits<{
diff --git a/packages/frontend/src/components/MkReactionsViewer.details.vue b/packages/frontend/src/components/MkReactionsViewer.details.vue
index d24e0b15bf..1c785f0fd1 100644
--- a/packages/frontend/src/components/MkReactionsViewer.details.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.details.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkTooltip ref="tooltip" :showing="showing" :targetElement="targetElement" :maxWidth="340" @closed="emit('closed')">
+<MkTooltip ref="tooltip" :showing="showing" :anchorElement="anchorElement" :maxWidth="340" @closed="emit('closed')">
<div :class="$style.root">
<div :class="$style.reaction">
<MkReactionIcon :reaction="reaction" :class="$style.reactionIcon" :noStyle="true"/>
@@ -33,7 +33,7 @@ defineProps<{
reaction: string;
users: Misskey.entities.UserLite[];
count: number;
- targetElement: HTMLElement;
+ anchorElement: HTMLElement;
}>();
const emit = defineEmits<{
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index 7d76dffa5a..d96f0e2420 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -38,6 +38,7 @@ import { prefer } from '@/preferences.js';
import { DI } from '@/di.js';
import { noteEvents } from '@/composables/use-note-capture.js';
import { mute as muteEmoji, unmute as unmuteEmoji, checkMuted as isEmojiMuted } from '@/utility/emoji-mute.js';
+import { haptic } from '@/utility/haptic.js';
const props = defineProps<{
noteId: Misskey.entities.Note['id'];
@@ -57,18 +58,22 @@ const emit = defineEmits<{
const buttonEl = useTemplateRef('buttonEl');
const emojiName = computed(() => props.reaction.replace(/:/g, '').replace(/@\./, ''));
-const emoji = computed(() => customEmojisMap.get(emojiName.value) ?? getUnicodeEmoji(props.reaction));
const canToggle = computed(() => {
+ const emoji = customEmojisMap.get(emojiName.value) ?? getUnicodeEmoji(props.reaction);
+
// TODO
- //return !props.reaction.match(/@\w/) && $i && emoji.value && checkReactionPermissions($i, props.note, emoji.value);
- return !props.reaction.match(/@\w/) && $i && emoji.value;
+ //return !props.reaction.match(/@\w/) && $i && emoji && checkReactionPermissions($i, props.note, emoji);
+ return !props.reaction.match(/@\w/) && $i && emoji;
});
const canGetInfo = computed(() => !props.reaction.match(/@\w/) && props.reaction.includes(':'));
const isLocalCustomEmoji = props.reaction[0] === ':' && props.reaction.includes('@.');
async function toggleReaction() {
if (!canToggle.value) return;
+ if ($i == null) return;
+
+ const me = $i;
const oldReaction = props.myReaction;
if (oldReaction) {
@@ -80,6 +85,7 @@ async function toggleReaction() {
if (oldReaction !== props.reaction) {
sound.playMisskeySfx('reaction');
+ haptic();
}
if (mock) {
@@ -91,7 +97,7 @@ async function toggleReaction() {
noteId: props.noteId,
}).then(() => {
noteEvents.emit(`unreacted:${props.noteId}`, {
- userId: $i!.id,
+ userId: me.id,
reaction: oldReaction,
});
if (oldReaction !== props.reaction) {
@@ -99,10 +105,12 @@ async function toggleReaction() {
noteId: props.noteId,
reaction: props.reaction,
}).then(() => {
+ const emoji = customEmojisMap.get(emojiName.value);
+ if (emoji == null) return;
noteEvents.emit(`reacted:${props.noteId}`, {
- userId: $i!.id,
+ userId: me.id,
reaction: props.reaction,
- emoji: emoji.value,
+ emoji: emoji,
});
});
}
@@ -118,6 +126,7 @@ async function toggleReaction() {
}
sound.playMisskeySfx('reaction');
+ haptic();
if (mock) {
emit('reactionToggled', props.reaction, (props.count + 1));
@@ -128,10 +137,13 @@ async function toggleReaction() {
noteId: props.noteId,
reaction: props.reaction,
}).then(() => {
+ const emoji = customEmojisMap.get(emojiName.value);
+ if (emoji == null) return;
+
noteEvents.emit(`reacted:${props.noteId}`, {
- userId: $i!.id,
+ userId: me.id,
reaction: props.reaction,
- emoji: emoji.value,
+ emoji: emoji,
});
});
// TODO: 上ä½ã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆã§ã‚„ã‚‹
@@ -214,6 +226,8 @@ onMounted(() => {
if (!mock) {
useTooltip(buttonEl, async (showing) => {
+ if (buttonEl.value == null) return;
+
const reactions = await misskeyApiGet('notes/reactions', {
noteId: props.noteId,
type: props.reaction,
@@ -228,7 +242,7 @@ if (!mock) {
reaction: props.reaction,
users,
count: props.count,
- targetElement: buttonEl.value,
+ anchorElement: buttonEl.value,
}, {
closed: () => dispose(),
});
diff --git a/packages/frontend/src/components/MkRoleSelectDialog.vue b/packages/frontend/src/components/MkRoleSelectDialog.vue
index fc7ba50fb3..f1cc98def4 100644
--- a/packages/frontend/src/components/MkRoleSelectDialog.vue
+++ b/packages/frontend/src/components/MkRoleSelectDialog.vue
@@ -105,9 +105,7 @@ async function addRole() {
.map(r => ({ text: r.name, value: r }));
const { canceled, result: role } = await os.select({ items });
- if (canceled) {
- return;
- }
+ if (canceled || role == null) return;
selectedRoleIds.value.push(role.id);
}
diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue
index 485d163ac4..9cbaf676c7 100644
--- a/packages/frontend/src/components/MkSelect.vue
+++ b/packages/frontend/src/components/MkSelect.vue
@@ -39,13 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</template>
-<script lang="ts" setup>
-import { onMounted, nextTick, ref, watch, computed, toRefs, useSlots } from 'vue';
-import { useInterval } from '@@/js/use-interval.js';
-import type { VNode, VNodeChild } from 'vue';
-import type { MenuItem } from '@/types/menu.js';
-import * as os from '@/os.js';
-
+<script lang="ts">
type ItemOption = {
type?: 'option';
value: string | number | null;
@@ -60,11 +54,32 @@ type ItemGroup = {
export type MkSelectItem = ItemOption | ItemGroup;
+type ValuesOfItems<T> = T extends (infer U)[]
+ ? U extends { type: 'group'; items: infer V }
+ ? V extends (infer W)[]
+ ? W extends { value: infer X }
+ ? X
+ : never
+ : never
+ : U extends { value: infer Y }
+ ? Y
+ : never
+ : never;
+</script>
+
+<script lang="ts" setup generic="T extends MkSelectItem[]">
+import { onMounted, nextTick, ref, watch, computed, toRefs, useSlots } from 'vue';
+import { useInterval } from '@@/js/use-interval.js';
+import type { VNode, VNodeChild } from 'vue';
+import type { MenuItem } from '@/types/menu.js';
+import * as os from '@/os.js';
+
// TODO: itemsã‚’slot内ã®optionã§æŒ‡å®šã™ã‚‹ç”¨æ³•ã¯å»ƒæ­¢ã™ã‚‹(props.itemsを必須化ã™ã‚‹)
// see: https://github.com/misskey-dev/misskey/issues/15558
+// ã‚ã¨åž‹æŽ¨è«–ã¨ç›¸æ€§ãŒè‰¯ããªã„
const props = defineProps<{
- modelValue: string | number | null;
+ modelValue: ValuesOfItems<T>;
required?: boolean;
readonly?: boolean;
disabled?: boolean;
@@ -73,11 +88,11 @@ const props = defineProps<{
inline?: boolean;
small?: boolean;
large?: boolean;
- items?: MkSelectItem[];
+ items?: T;
}>();
const emit = defineEmits<{
- (ev: 'update:modelValue', value: string | number | null): void;
+ (ev: 'update:modelValue', value: ValuesOfItems<T>): void;
}>();
const slots = useSlots();
diff --git a/packages/frontend/src/components/MkServerSetupWizard.vue b/packages/frontend/src/components/MkServerSetupWizard.vue
index 65e0d6d9de..1d2dfed297 100644
--- a/packages/frontend/src/components/MkServerSetupWizard.vue
+++ b/packages/frontend/src/components/MkServerSetupWizard.vue
@@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #icon><i class="ti ti-planet"></i></template>
<div class="_gaps_s">
- <div>{{ i18n.ts._serverSetupWizard.doYouConnectToFediverse_description1 }}<br>{{ i18n.ts._serverSetupWizard.doYouConnectToFediverse_description2 }}</div>
+ <div>{{ i18n.ts._serverSetupWizard.doYouConnectToFediverse_description1 }}<br>{{ i18n.ts._serverSetupWizard.doYouConnectToFediverse_description2 }}<br><MkLink target="_blank" url="https://wikipedia.org/wiki/Fediverse">{{ i18n.ts.learnMore }}</MkLink></div>
<MkRadios v-model="q_federation" :vertical="true">
<option value="yes">{{ i18n.ts.yes }}</option>
@@ -63,6 +63,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkRadios>
<MkInfo v-if="q_federation === 'yes'">{{ i18n.ts._serverSetupWizard.youCanConfigureMoreFederationSettingsLater }}</MkInfo>
+
+ <MkSwitch v-if="q_federation === 'yes'" v-model="q_remoteContentsCleaning">
+ <template #label>{{ i18n.ts._serverSetupWizard.remoteContentsCleaning }}</template>
+ <template #caption>{{ i18n.ts._serverSetupWizard.remoteContentsCleaning_description }}</template>
+ </MkSwitch>
</div>
</MkFolder>
@@ -111,6 +116,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<div>{{ serverSettings.federation === 'none' ? i18n.ts.no : i18n.ts.all }}</div>
</div>
<div>
+ <div><b>{{ i18n.ts._serverSettings.remoteNotesCleaning }}:</b></div>
+ <div>{{ serverSettings.enableRemoteNotesCleaning ? i18n.ts.yes : i18n.ts.no }}</div>
+ </div>
+ <div>
<div><b>FTT:</b></div>
<div>{{ serverSettings.enableFanoutTimeline ? i18n.ts.yes : i18n.ts.no }}</div>
</div>
@@ -124,6 +133,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div>
+ <div><b>{{ i18n.ts._serverSettings.entrancePageStyle }}:</b></div>
+ <div>{{ serverSettings.clientOptions.entrancePageStyle }}</div>
+ </div>
+
+ <div>
<div><b>{{ i18n.ts._role.baseRole }}/{{ i18n.ts._role._options.rateLimitFactor }}:</b></div>
<div>{{ defaultPolicies.rateLimitFactor }}</div>
</div>
@@ -185,7 +199,9 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import MkFolder from '@/components/MkFolder.vue';
import MkRadios from '@/components/MkRadios.vue';
+import MkSwitch from '@/components/MkSwitch.vue';
import MkInfo from '@/components/MkInfo.vue';
+import MkLink from '@/components/MkLink.vue';
const emit = defineEmits<{
(ev: 'finished'): void;
@@ -200,6 +216,7 @@ const q_name = ref('');
const q_use = ref('single');
const q_scale = ref('small');
const q_federation = ref('yes');
+const q_remoteContentsCleaning = ref(true);
const q_adminName = ref('');
const q_adminEmail = ref('');
@@ -217,9 +234,13 @@ const serverSettings = computed<Misskey.entities.AdminUpdateMetaRequest>(() => {
emailRequiredForSignup: q_use.value === 'open',
enableIpLogging: q_use.value === 'open',
federation: q_federation.value === 'yes' ? 'all' : 'none',
+ enableRemoteNotesCleaning: q_remoteContentsCleaning.value,
enableFanoutTimeline: true,
enableFanoutTimelineDbFallback: q_use.value === 'single',
enableReactionsBuffering,
+ clientOptions: {
+ entrancePageStyle: q_use.value === 'open' ? 'classic' : 'simple',
+ },
};
});
diff --git a/packages/frontend/src/components/MkServerSetupWizardDialog.vue b/packages/frontend/src/components/MkServerSetupWizardDialog.vue
new file mode 100644
index 0000000000..ea2c5dd47f
--- /dev/null
+++ b/packages/frontend/src/components/MkServerSetupWizardDialog.vue
@@ -0,0 +1,57 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkModalWindow
+ ref="windowEl"
+ :withOkButton="false"
+ :okButtonDisabled="false"
+ :width="500"
+ :height="600"
+ @close="onCloseModalWindow"
+ @closed="emit('closed')"
+>
+ <template #header>Server setup wizard</template>
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
+ <Suspense>
+ <template #default>
+ <MkServerSetupWizard @finished="onWizardFinished"/>
+ </template>
+ <template #fallback>
+ <MkLoading/>
+ </template>
+ </Suspense>
+ </div>
+</MkModalWindow>
+</template>
+
+<script setup lang="ts">
+import { useTemplateRef } from 'vue';
+import MkModalWindow from '@/components/MkModalWindow.vue';
+import MkServerSetupWizard from '@/components/MkServerSetupWizard.vue';
+
+const emit = defineEmits<{
+ (ev: 'closed'),
+}>();
+
+const windowEl = useTemplateRef('windowEl');
+
+function onWizardFinished() {
+ windowEl.value?.close();
+}
+
+function onCloseModalWindow() {
+ windowEl.value?.close();
+}
+</script>
+
+<style module lang="scss">
+.root {
+ max-height: 410px;
+ height: 410px;
+ display: flex;
+ flex-direction: column;
+}
+</style>
diff --git a/packages/frontend/src/components/MkSignin.password.vue b/packages/frontend/src/components/MkSignin.password.vue
index cd003a39df..3b3b6a0b73 100644
--- a/packages/frontend/src/components/MkSignin.password.vue
+++ b/packages/frontend/src/components/MkSignin.password.vue
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkCaptcha v-if="instance.enableMcaptcha" ref="mcaptcha" v-model="mCaptchaResponse" provider="mcaptcha" :sitekey="instance.mcaptchaSiteKey" :instanceUrl="instance.mcaptchaInstanceUrl"/>
<MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/>
<MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" provider="turnstile" :sitekey="instance.turnstileSiteKey"/>
- <MkCaptcha v-if="instance.enableTestcaptcha" ref="testcaptcha" v-model="testcaptchaResponse" provider="testcaptcha"/>
+ <MkCaptcha v-if="instance.enableTestcaptcha" ref="testcaptcha" v-model="testcaptchaResponse" provider="testcaptcha" :sitekey="null"/>
</div>
<MkButton type="submit" :disabled="needCaptcha && captchaFailed" large primary rounded style="margin: 0 auto;" data-cy-signin-page-password-continue>{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
diff --git a/packages/frontend/src/components/MkStreamingNotesTimeline.vue b/packages/frontend/src/components/MkStreamingNotesTimeline.vue
index 3e50bdefd2..bc6ebf0918 100644
--- a/packages/frontend/src/components/MkStreamingNotesTimeline.vue
+++ b/packages/frontend/src/components/MkStreamingNotesTimeline.vue
@@ -32,9 +32,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-for="(note, i) in paginator.items.value" :key="note.id">
<div v-if="i > 0 && isSeparatorNeeded(paginator.items.value[i -1].createdAt, note.createdAt)" :data-scroll-anchor="note.id">
<div :class="$style.date">
- <span><i class="ti ti-chevron-up"></i> {{ getSeparatorInfo(paginator.items.value[i -1].createdAt, note.createdAt).prevText }}</span>
+ <span><i class="ti ti-chevron-up"></i> {{ getSeparatorInfo(paginator.items.value[i -1].createdAt, note.createdAt)?.prevText }}</span>
<span style="height: 1em; width: 1px; background: var(--MI_THEME-divider);"></span>
- <span>{{ getSeparatorInfo(paginator.items.value[i -1].createdAt, note.createdAt).nextText }} <i class="ti ti-chevron-down"></i></span>
+ <span>{{ getSeparatorInfo(paginator.items.value[i -1].createdAt, note.createdAt)?.nextText }} <i class="ti ti-chevron-down"></i></span>
</div>
<MkNote :class="$style.note" :note="note" :withHardMute="true"/>
</div>
@@ -47,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkNote v-else :class="$style.note" :note="note" :withHardMute="true" :data-scroll-anchor="note.id"/>
</template>
</component>
- <button v-show="paginator.canFetchOlder.value" key="_more_" :disabled="paginator.fetchingOlder.value" class="_button" :class="$style.more" @click="paginator.fetchOlder">
+ <button v-show="paginator.canFetchOlder.value" key="_more_" v-appear="prefer.s.enableInfiniteScroll ? paginator.fetchOlder : null" :disabled="paginator.fetchingOlder.value" class="_button" :class="$style.more" @click="paginator.fetchOlder">
<div v-if="!paginator.fetchingOlder.value">{{ i18n.ts.loadMore }}</div>
<MkLoading v-else :inline="true"/>
</button>
@@ -297,76 +297,97 @@ function prepend(note: Misskey.entities.Note & MisskeyEntity) {
}
}
-let connection: Misskey.IChannelConnection | null = null;
-let connection2: Misskey.IChannelConnection | null = null;
-
const stream = store.s.realtimeMode ? useStream() : null;
+const connections = {
+ antenna: null as Misskey.IChannelConnection<Misskey.Channels['antenna']> | null,
+ homeTimeline: null as Misskey.IChannelConnection<Misskey.Channels['homeTimeline']> | null,
+ localTimeline: null as Misskey.IChannelConnection<Misskey.Channels['localTimeline']> | null,
+ hybridTimeline: null as Misskey.IChannelConnection<Misskey.Channels['hybridTimeline']> | null,
+ globalTimeline: null as Misskey.IChannelConnection<Misskey.Channels['globalTimeline']> | null,
+ main: null as Misskey.IChannelConnection<Misskey.Channels['main']> | null,
+ userList: null as Misskey.IChannelConnection<Misskey.Channels['userList']> | null,
+ channel: null as Misskey.IChannelConnection<Misskey.Channels['channel']> | null,
+ roleTimeline: null as Misskey.IChannelConnection<Misskey.Channels['roleTimeline']> | null,
+};
+
function connectChannel() {
if (stream == null) return;
if (props.src === 'antenna') {
if (props.antenna == null) return;
- connection = stream.useChannel('antenna', {
+ connections.antenna = stream.useChannel('antenna', {
antennaId: props.antenna,
});
+ connections.antenna.on('note', prepend);
} else if (props.src === 'home') {
- connection = stream.useChannel('homeTimeline', {
+ connections.homeTimeline = stream.useChannel('homeTimeline', {
withRenotes: props.withRenotes,
withFiles: props.onlyFiles ? true : undefined,
});
- connection2 = stream.useChannel('main');
+ connections.main = stream.useChannel('main');
+ connections.homeTimeline.on('note', prepend);
} else if (props.src === 'local') {
- connection = stream.useChannel('localTimeline', {
+ connections.localTimeline = stream.useChannel('localTimeline', {
withRenotes: props.withRenotes,
withReplies: props.withReplies,
withFiles: props.onlyFiles ? true : undefined,
});
+ connections.localTimeline.on('note', prepend);
} else if (props.src === 'social') {
- connection = stream.useChannel('hybridTimeline', {
+ connections.hybridTimeline = stream.useChannel('hybridTimeline', {
withRenotes: props.withRenotes,
withReplies: props.withReplies,
withFiles: props.onlyFiles ? true : undefined,
});
+ connections.hybridTimeline.on('note', prepend);
} else if (props.src === 'global') {
- connection = stream.useChannel('globalTimeline', {
+ connections.globalTimeline = stream.useChannel('globalTimeline', {
withRenotes: props.withRenotes,
withFiles: props.onlyFiles ? true : undefined,
});
+ connections.globalTimeline.on('note', prepend);
} else if (props.src === 'mentions') {
- connection = stream.useChannel('main');
- connection.on('mention', prepend);
+ connections.main = stream.useChannel('main');
+ connections.main.on('mention', prepend);
} else if (props.src === 'directs') {
const onNote = note => {
if (note.visibility === 'specified') {
prepend(note);
}
};
- connection = stream.useChannel('main');
- connection.on('mention', onNote);
+ connections.main = stream.useChannel('main');
+ connections.main.on('mention', onNote);
} else if (props.src === 'list') {
if (props.list == null) return;
- connection = stream.useChannel('userList', {
+ connections.userList = stream.useChannel('userList', {
withRenotes: props.withRenotes,
withFiles: props.onlyFiles ? true : undefined,
listId: props.list,
});
+ connections.userList.on('note', prepend);
} else if (props.src === 'channel') {
if (props.channel == null) return;
- connection = stream.useChannel('channel', {
+ connections.channel = stream.useChannel('channel', {
channelId: props.channel,
});
+ connections.channel.on('note', prepend);
} else if (props.src === 'role') {
if (props.role == null) return;
- connection = stream.useChannel('roleTimeline', {
+ connections.roleTimeline = stream.useChannel('roleTimeline', {
roleId: props.role,
});
+ connections.roleTimeline.on('note', prepend);
}
- if (props.src !== 'directs' && props.src !== 'mentions') connection?.on('note', prepend);
}
function disconnectChannel() {
- if (connection) connection.dispose();
- if (connection2) connection2.dispose();
+ for (const key in connections) {
+ const conn = connections[key as keyof typeof connections];
+ if (conn != null) {
+ conn.dispose();
+ connections[key as keyof typeof connections] = null;
+ }
+ }
}
if (store.s.realtimeMode) {
diff --git a/packages/frontend/src/components/MkStreamingNotificationsTimeline.vue b/packages/frontend/src/components/MkStreamingNotificationsTimeline.vue
index 4617e659c8..15e8e2105f 100644
--- a/packages/frontend/src/components/MkStreamingNotificationsTimeline.vue
+++ b/packages/frontend/src/components/MkStreamingNotificationsTimeline.vue
@@ -25,15 +25,15 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<div v-for="(notification, i) in paginator.items.value" :key="notification.id" :data-scroll-anchor="notification.id" :class="$style.item">
<div v-if="i > 0 && isSeparatorNeeded(paginator.items.value[i -1].createdAt, notification.createdAt)" :class="$style.date">
- <span><i class="ti ti-chevron-up"></i> {{ getSeparatorInfo(paginator.items.value[i -1].createdAt, notification.createdAt).prevText }}</span>
+ <span><i class="ti ti-chevron-up"></i> {{ getSeparatorInfo(paginator.items.value[i -1].createdAt, notification.createdAt)?.prevText }}</span>
<span style="height: 1em; width: 1px; background: var(--MI_THEME-divider);"></span>
- <span>{{ getSeparatorInfo(paginator.items.value[i -1].createdAt, notification.createdAt).nextText }} <i class="ti ti-chevron-down"></i></span>
+ <span>{{ getSeparatorInfo(paginator.items.value[i -1].createdAt, notification.createdAt)?.nextText }} <i class="ti ti-chevron-down"></i></span>
</div>
- <MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :class="$style.content" :note="notification.note" :withHardMute="true"/>
+ <MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type) && 'note' in notification" :class="$style.content" :note="notification.note" :withHardMute="true"/>
<XNotification v-else :class="$style.content" :notification="notification" :withTime="true" :full="true"/>
</div>
</component>
- <button v-show="paginator.canFetchOlder.value" key="_more_" :disabled="paginator.fetchingOlder.value" class="_button" :class="$style.more" @click="paginator.fetchOlder">
+ <button v-show="paginator.canFetchOlder.value" key="_more_" v-appear="prefer.s.enableInfiniteScroll ? paginator.fetchOlder : null" :disabled="paginator.fetchingOlder.value" class="_button" :class="$style.more" @click="paginator.fetchOlder">
<div v-if="!paginator.fetchingOlder.value">{{ i18n.ts.loadMore }}</div>
<MkLoading v-else/>
</button>
@@ -59,7 +59,7 @@ import { isSeparatorNeeded, getSeparatorInfo } from '@/utility/timeline-date-sep
import { Paginator } from '@/utility/paginator.js';
const props = defineProps<{
- excludeTypes?: typeof notificationTypes[number][];
+ excludeTypes?: typeof notificationTypes[number][] | null;
}>();
const rootEl = useTemplateRef('rootEl');
diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue
index 3f8d92a61d..dbc673333c 100644
--- a/packages/frontend/src/components/MkSuperMenu.vue
+++ b/packages/frontend/src/components/MkSuperMenu.vue
@@ -52,9 +52,9 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ item.label }}
</template>
<template v-else>
- <span style="opacity: 0.7; font-size: 90%;">{{ item.parentLabels.join(' > ') }}</span>
+ <span style="opacity: 0.7; font-size: 90%; word-break: break-word;">{{ item.parentLabels.join(' > ') }}</span>
<br>
- <span>{{ item.label }}</span>
+ <span style="word-break: break-word;">{{ item.label }}</span>
</template>
</span>
</MkA>
@@ -95,7 +95,7 @@ export type SuperMenuDef = {
<script lang="ts" setup>
import { useTemplateRef, ref, watch, nextTick, computed } from 'vue';
import { getScrollContainer } from '@@/js/scroll.js';
-import type { SearchIndexItem } from '@/utility/settings-search-index.js';
+import type { SearchIndexItem } from '@/utility/inapp-search.js';
import MkInput from '@/components/MkInput.vue';
import { i18n } from '@/i18n.js';
import { useRouter } from '@/router.js';
@@ -165,12 +165,28 @@ watch(rawSearchQuery, (value) => {
});
};
- for (const item of searchIndexItemById.values()) {
- if (
- compareStringIncludes(item.label, value) ||
- item.keywords.some((x) => compareStringIncludes(x, value))
- ) {
+ // label, keywords, texts ã®é †ã«å„ªå…ˆã—ã¦è¡¨ç¤º
+
+ let items = Array.from(searchIndexItemById.values());
+
+ for (const item of items) {
+ if (compareStringIncludes(item.label, value)) {
+ addSearchResult(item);
+ items = items.filter(i => i.id !== item.id);
+ }
+ }
+
+ for (const item of items) {
+ if (item.keywords.some((x) => compareStringIncludes(x, value))) {
+ addSearchResult(item);
+ items = items.filter(i => i.id !== item.id);
+ }
+ }
+
+ for (const item of items) {
+ if (item.texts.some((x) => compareStringIncludes(x, value))) {
addSearchResult(item);
+ items = items.filter(i => i.id !== item.id);
}
}
}
@@ -186,7 +202,7 @@ function searchOnKeyDown(ev: KeyboardEvent) {
if (ev.key === 'Enter' && searchSelectedIndex.value != null) {
ev.preventDefault();
- router.push(searchResult.value[searchSelectedIndex.value].path + '#' + searchResult.value[searchSelectedIndex.value].id);
+ router.pushByPath(searchResult.value[searchSelectedIndex.value].path + '#' + searchResult.value[searchSelectedIndex.value].id);
} else if (ev.key === 'ArrowDown') {
ev.preventDefault();
const current = searchSelectedIndex.value ?? -1;
diff --git a/packages/frontend/src/components/MkSwitch.vue b/packages/frontend/src/components/MkSwitch.vue
index 92359b773a..9a2bea3616 100644
--- a/packages/frontend/src/components/MkSwitch.vue
+++ b/packages/frontend/src/components/MkSwitch.vue
@@ -30,6 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { toRefs } from 'vue';
import type { Ref } from 'vue';
import XButton from '@/components/MkSwitch.button.vue';
+import { haptic } from '@/utility/haptic.js';
const props = defineProps<{
modelValue: boolean | Ref<boolean>;
@@ -48,6 +49,8 @@ const toggle = () => {
if (props.disabled) return;
emit('update:modelValue', !checked.value);
emit('change', !checked.value);
+
+ haptic();
};
</script>
diff --git a/packages/frontend/src/components/MkTabs.vue b/packages/frontend/src/components/MkTabs.vue
index 5c4a67b026..57fb6548ba 100644
--- a/packages/frontend/src/components/MkTabs.vue
+++ b/packages/frontend/src/components/MkTabs.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div :class="$style.tabs">
+<div :class="[$style.tabs, { [$style.centered]: props.centered }]">
<div :class="$style.tabsInner">
<button
v-for="t in tabs" :ref="(el) => tabRefs[t.key] = (el as HTMLElement)" v-tooltip.noDelay="t.title"
@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div
ref="tabHighlightEl"
- :class="[$style.tabHighlight, { [$style.animate]: prefer.s.animation }]"
+ :class="[$style.tabHighlight, { [$style.animate]: prefer.s.animation, [$style.tabHighlightUpper]: tabHighlightUpper }]"
></div>
</div>
</template>
@@ -39,17 +39,10 @@ SPDX-License-Identifier: AGPL-3.0-only
export type Tab = {
key: string;
onClick?: (ev: MouseEvent) => void;
-} & (
- | {
- iconOnly?: false;
- title: string;
- icon?: string;
- }
- | {
- iconOnly: true;
- icon: string;
- }
-);
+ iconOnly?: boolean;
+ title: string;
+ icon?: string;
+};
</script>
<script lang="ts" setup>
@@ -59,6 +52,8 @@ import { prefer } from '@/preferences.js';
const props = withDefaults(defineProps<{
tabs?: Tab[];
tab?: string;
+ centered?: boolean;
+ tabHighlightUpper?: boolean;
}>(), {
tabs: () => ([] as Tab[]),
});
@@ -169,6 +164,16 @@ onUnmounted(() => {
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none;
+
+ &.centered {
+ text-align: center;
+ }
+}
+
+@container (max-width: 450px) {
+ .tabs {
+ font-size: 80%;
+ }
}
.tabsInner {
@@ -227,5 +232,10 @@ onUnmounted(() => {
&.animate {
transition: width 0.15s ease, left 0.15s ease;
}
+
+ &.tabHighlightUpper {
+ top: 0;
+ bottom: auto;
+ }
}
</style>
diff --git a/packages/frontend/src/components/MkTagItem.stories.impl.ts b/packages/frontend/src/components/MkTagItem.stories.impl.ts
index ac932c8342..9e8c2d8917 100644
--- a/packages/frontend/src/components/MkTagItem.stories.impl.ts
+++ b/packages/frontend/src/components/MkTagItem.stories.impl.ts
@@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable import/no-default-export */
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3';
import MkTagItem from './MkTagItem.vue';
diff --git a/packages/frontend/src/components/MkTooltip.vue b/packages/frontend/src/components/MkTooltip.vue
index 3fe80f4ab4..aa041c88e5 100644
--- a/packages/frontend/src/components/MkTooltip.vue
+++ b/packages/frontend/src/components/MkTooltip.vue
@@ -31,7 +31,7 @@ import { prefer } from '@/preferences.js';
const props = withDefaults(defineProps<{
showing: boolean;
- targetElement?: HTMLElement;
+ anchorElement?: HTMLElement;
x?: number;
y?: number;
text?: string;
@@ -58,7 +58,7 @@ const zIndex = os.claimZIndex('high');
function setPosition() {
if (el.value == null) return;
const data = calcPopupPosition(el.value, {
- anchorElement: props.targetElement,
+ anchorElement: props.anchorElement,
direction: props.direction,
align: 'center',
innerMargin: props.innerMargin,
diff --git a/packages/frontend/src/components/MkUpdated.vue b/packages/frontend/src/components/MkUpdated.vue
index 79ab464cb0..eba8e5472c 100644
--- a/packages/frontend/src/components/MkUpdated.vue
+++ b/packages/frontend/src/components/MkUpdated.vue
@@ -4,10 +4,11 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkModal ref="modal" :zPriority="'middle'" @click="modal?.close()" @closed="$emit('closed')">
+<MkModal ref="modal" preferType="dialog" :zPriority="'middle'" @click="modal?.close()" @closed="$emit('closed')">
<div :class="$style.root">
<div :class="$style.title"><MkSparkle>{{ i18n.ts.misskeyUpdated }}</MkSparkle></div>
<div :class="$style.version">✨{{ version }}🚀</div>
+ <div v-if="isBeta" :class="$style.beta">{{ i18n.ts.thankYouForTestingBeta }}</div>
<MkButton full @click="whatIsNew">{{ i18n.ts.whatIsNew }}</MkButton>
<MkButton :class="$style.gotIt" primary full @click="modal?.close()">{{ i18n.ts.gotIt }}</MkButton>
</div>
@@ -25,6 +26,8 @@ import { confetti } from '@/utility/confetti.js';
const modal = useTemplateRef('modal');
+const isBeta = version.includes('-beta') || version.includes('-alpha') || version.includes('-rc');
+
function whatIsNew() {
modal.value?.close();
window.open(`https://misskey-hub.net/docs/releases/#_${version.replace(/\./g, '')}`, '_blank');
@@ -58,6 +61,10 @@ onMounted(() => {
margin: 1em 0;
}
+.beta {
+ margin: 1em 0;
+}
+
.gotIt {
margin: 8px 0 0 0;
}
diff --git a/packages/frontend/src/components/MkUrlPreviewPopup.vue b/packages/frontend/src/components/MkUrlPreviewPopup.vue
index fd36d6a82b..09558b0319 100644
--- a/packages/frontend/src/components/MkUrlPreviewPopup.vue
+++ b/packages/frontend/src/components/MkUrlPreviewPopup.vue
@@ -20,7 +20,7 @@ import { prefer } from '@/preferences.js';
const props = defineProps<{
showing: boolean;
url: string;
- source: HTMLElement;
+ anchorElement: HTMLElement;
}>();
const emit = defineEmits<{
@@ -32,9 +32,9 @@ const top = ref(0);
const left = ref(0);
onMounted(() => {
- const rect = props.source.getBoundingClientRect();
- const x = Math.max((rect.left + (props.source.offsetWidth / 2)) - (300 / 2), 6) + window.scrollX;
- const y = rect.top + props.source.offsetHeight + window.scrollY;
+ const rect = props.anchorElement.getBoundingClientRect();
+ const x = Math.max((rect.left + (props.anchorElement.offsetWidth / 2)) - (300 / 2), 6) + window.scrollX;
+ const y = rect.top + props.anchorElement.offsetHeight + window.scrollY;
top.value = y;
left.value = x;
diff --git a/packages/frontend/src/components/MkUsersTooltip.vue b/packages/frontend/src/components/MkUsersTooltip.vue
index 0cb7f22e93..d0bfebc463 100644
--- a/packages/frontend/src/components/MkUsersTooltip.vue
+++ b/packages/frontend/src/components/MkUsersTooltip.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkTooltip ref="tooltip" :showing="showing" :targetElement="targetElement" :maxWidth="250" @closed="emit('closed')">
+<MkTooltip ref="tooltip" :showing="showing" :anchorElement="anchorElement" :maxWidth="250" @closed="emit('closed')">
<div :class="$style.root">
<div v-for="u in users" :key="u.id" :class="$style.user">
<MkAvatar :class="$style.avatar" :user="u"/>
@@ -23,7 +23,7 @@ defineProps<{
showing: boolean;
users: Misskey.entities.UserLite[];
count: number;
- targetElement: HTMLElement;
+ anchorElement: HTMLElement;
}>();
const emit = defineEmits<{
diff --git a/packages/frontend/src/components/MkVisibilityPicker.vue b/packages/frontend/src/components/MkVisibilityPicker.vue
index 3801195da6..88b934bb58 100644
--- a/packages/frontend/src/components/MkVisibilityPicker.vue
+++ b/packages/frontend/src/components/MkVisibilityPicker.vue
@@ -53,7 +53,7 @@ const props = withDefaults(defineProps<{
currentVisibility: typeof Misskey.noteVisibilities[number];
isSilenced: boolean;
localOnly: boolean;
- anchorElement?: HTMLElement;
+ anchorElement?: HTMLElement | null;
isReplyVisibilitySpecified?: boolean;
}>(), {
});
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index a809e9040d..50520b5d9d 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</div>
- <div v-if="stats" :class="$style.stats">
+ <div v-if="stats && instance.clientOptions.showActivitiesForVisitor !== false" :class="$style.stats">
<div :class="[$style.statsItem, $style.panel]">
<div :class="$style.statsItemLabel">{{ i18n.ts.users }}</div>
<div :class="$style.statsItemCount"><MkNumber :value="stats.originalUsersCount"/></div>
@@ -40,13 +40,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.statsItemCount"><MkNumber :value="stats.originalNotesCount"/></div>
</div>
</div>
- <div v-if="instance.policies.ltlAvailable" :class="[$style.tl, $style.panel]">
+ <div v-if="instance.policies.ltlAvailable && instance.clientOptions.showTimelineForVisitor !== false" :class="[$style.tl, $style.panel]">
<div :class="$style.tlHeader">{{ i18n.ts.letsLookAtTimeline }}</div>
<div :class="$style.tlBody">
<MkStreamingNotesTimeline src="local"/>
</div>
</div>
- <div :class="$style.panel">
+ <div v-if="instance.clientOptions.showActivitiesForVisitor !== false" :class="$style.panel">
<XActiveUsersChart/>
</div>
</div>
@@ -55,12 +55,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
import * as Misskey from 'misskey-js';
+import { instanceName } from '@@/js/config.js';
+import type { MenuItem } from '@/types/menu.js';
import XSigninDialog from '@/components/MkSigninDialog.vue';
import XSignupDialog from '@/components/MkSignupDialog.vue';
import MkButton from '@/components/MkButton.vue';
import MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue';
import MkInfo from '@/components/MkInfo.vue';
-import { instanceName } from '@@/js/config.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
@@ -68,13 +69,14 @@ import { instance } from '@/instance.js';
import MkNumber from '@/components/MkNumber.vue';
import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue';
import { openInstanceMenu } from '@/ui/_common_/common.js';
-import type { MenuItem } from '@/types/menu.js';
const stats = ref<Misskey.entities.StatsResponse | null>(null);
-misskeyApi('stats', {}).then((res) => {
- stats.value = res;
-});
+if (instance.clientOptions.showActivitiesForVisitor !== false) {
+ misskeyApi('stats', {}).then((res) => {
+ stats.value = res;
+ });
+}
function signin() {
const { dispose } = os.popup(XSigninDialog, {
diff --git a/packages/frontend/src/components/MkWidgets.vue b/packages/frontend/src/components/MkWidgets.vue
index f606b0b001..08a018ea9b 100644
--- a/packages/frontend/src/components/MkWidgets.vue
+++ b/packages/frontend/src/components/MkWidgets.vue
@@ -51,6 +51,7 @@ export type DefaultStoredWidget = {
<script lang="ts" setup>
import { defineAsyncComponent, ref, computed } from 'vue';
+import { isLink } from '@@/js/is-link.js';
import { genId } from '@/utility/id.js';
import MkSelect from '@/components/MkSelect.vue';
import MkButton from '@/components/MkButton.vue';
@@ -58,7 +59,6 @@ import { widgets as widgetDefs, federationWidgets } from '@/widgets/index.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
-import { isLink } from '@@/js/is-link.js';
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
@@ -81,7 +81,7 @@ const emit = defineEmits<{
(ev: 'updateWidgets', widgets: Widget[]): void;
(ev: 'addWidget', widget: Widget): void;
(ev: 'removeWidget', widget: Widget): void;
- (ev: 'updateWidget', widget: Partial<Widget>): void;
+ (ev: 'updateWidget', widget: { id: Widget['id']; data: Widget['data']; }): void;
(ev: 'exit'): void;
}>();
@@ -104,7 +104,7 @@ const addWidget = () => {
const removeWidget = (widget) => {
emit('removeWidget', widget);
};
-const updateWidget = (id, data) => {
+const updateWidget = (id: Widget['id'], data: Widget['data']) => {
emit('updateWidget', { id, data });
};
diff --git a/packages/frontend/src/components/form/suspense.vue b/packages/frontend/src/components/form/suspense.vue
index 821f07510b..3b23acf612 100644
--- a/packages/frontend/src/components/form/suspense.vue
+++ b/packages/frontend/src/components/form/suspense.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkLoading/>
</div>
<div v-else-if="resolved">
- <slot :result="result"></slot>
+ <slot :result="result as T"></slot>
</div>
<div v-else>
<div :class="$style.error">
diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue
index 4004db5b12..ae1b4549ec 100644
--- a/packages/frontend/src/components/global/MkA.vue
+++ b/packages/frontend/src/components/global/MkA.vue
@@ -64,7 +64,7 @@ function onContextmenu(ev) {
icon: 'ti ti-player-eject',
text: i18n.ts.showInPage,
action: () => {
- router.push(props.to, 'forcePage');
+ router.pushByPath(props.to, 'forcePage');
},
}, { type: 'divider' }, {
icon: 'ti ti-external-link',
@@ -99,6 +99,6 @@ function nav(ev: MouseEvent) {
return openWindow();
}
- router.push(props.to, ev.ctrlKey ? 'forcePage' : null);
+ router.pushByPath(props.to, ev.ctrlKey ? 'forcePage' : null);
}
</script>
diff --git a/packages/frontend/src/components/global/MkAd.stories.impl.ts b/packages/frontend/src/components/global/MkAd.stories.impl.ts
index c5a928b5cf..07e06a6897 100644
--- a/packages/frontend/src/components/global/MkAd.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkAd.stories.impl.ts
@@ -2,11 +2,10 @@
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
-
-/* eslint-disable @typescript-eslint/explicit-function-return-type */
+
import { expect, userEvent, waitFor, within } from '@storybook/test';
-import type { StoryObj } from '@storybook/vue3';
import MkAd from './MkAd.vue';
+import type { StoryObj } from '@storybook/vue3';
import { i18n } from '@/i18n.js';
const common = {
@@ -68,7 +67,7 @@ const common = {
await expect(imgAgain).toBeInTheDocument();
},
args: {
- prefer: [],
+ preferForms: [],
specify: {
id: 'someadid',
ratio: 1,
diff --git a/packages/frontend/src/components/global/MkAd.vue b/packages/frontend/src/components/global/MkAd.vue
index 2f55700b47..c592079f03 100644
--- a/packages/frontend/src/components/global/MkAd.vue
+++ b/packages/frontend/src/components/global/MkAd.vue
@@ -52,7 +52,7 @@ import { prefer } from '@/preferences.js';
type Ad = (typeof instance)['ads'][number];
const props = defineProps<{
- preferForms: string[];
+ preferForms?: string[];
specify?: Ad;
}>();
@@ -71,7 +71,7 @@ const choseAd = (): Ad | null => {
ratio: 0,
} : ad);
- let ads = allAds.filter(ad => props.preferForms.includes(ad.place));
+ let ads = props.preferForms ? allAds.filter(ad => props.preferForms!.includes(ad.place)) : allAds;
if (ads.length === 0) {
ads = allAds.filter(ad => ad.place === 'square');
diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue
index 8a9cc5286a..c2548cc7be 100644
--- a/packages/frontend/src/components/global/MkAvatar.vue
+++ b/packages/frontend/src/components/global/MkAvatar.vue
@@ -84,7 +84,6 @@ const bound = computed(() => props.link
: {});
const url = computed(() => {
- if (props.user.avatarUrl == null) return null;
if (prefer.s.disableShowingAnimatedImages || prefer.s.dataSaver.avatar) return getStaticImageUrl(props.user.avatarUrl);
return props.user.avatarUrl;
});
diff --git a/packages/frontend/src/components/global/MkError.stories.impl.ts b/packages/frontend/src/components/global/MkError.stories.impl.ts
index e150493a18..497cdfb3d5 100644
--- a/packages/frontend/src/components/global/MkError.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkError.stories.impl.ts
@@ -4,7 +4,7 @@
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import { expect, waitFor } from '@storybook/test';
import type { StoryObj } from '@storybook/vue3';
import MkError from './MkError.vue';
diff --git a/packages/frontend/src/components/global/MkPageHeader.stories.impl.ts b/packages/frontend/src/components/global/MkPageHeader.stories.impl.ts
index 15938d0495..0ac6304054 100644
--- a/packages/frontend/src/components/global/MkPageHeader.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkPageHeader.stories.impl.ts
@@ -2,7 +2,7 @@
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
-
+
import { waitFor } from '@storybook/test';
import MkPageHeader from './MkPageHeader.vue';
import type { StoryObj } from '@storybook/vue3';
@@ -59,6 +59,7 @@ export const Icon = {
{
...OneTab.args.tabs[0],
icon: 'ti ti-home',
+ title: 'Home',
},
],
},
@@ -71,6 +72,7 @@ export const IconOnly = {
{
key: Icon.args.tabs[0].key,
icon: Icon.args.tabs[0].icon,
+ title: Icon.args.tabs[0].title,
iconOnly: true,
},
],
diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
index f2173b2e22..a1b57f30d9 100644
--- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
@@ -39,17 +39,10 @@ SPDX-License-Identifier: AGPL-3.0-only
export type Tab = {
key: string;
onClick?: (ev: MouseEvent) => void;
-} & (
- | {
- iconOnly?: false;
- title: string;
- icon?: string;
- }
- | {
- iconOnly: true;
- icon: string;
- }
-);
+ iconOnly?: boolean;
+ title: string;
+ icon?: string;
+};
</script>
<script lang="ts" setup>
@@ -59,7 +52,7 @@ import { prefer } from '@/preferences.js';
const props = withDefaults(defineProps<{
tabs?: Tab[];
tab?: string;
- rootEl?: HTMLElement;
+ rootEl?: HTMLElement | null;
}>(), {
tabs: () => ([] as Tab[]),
});
diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index 542c3d8d12..2f4de840db 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -52,6 +52,7 @@ export type PageHeaderProps = {
actions?: PageHeaderItem[] | null;
thin?: boolean;
hideTitle?: boolean;
+ canOmitTitle?: boolean;
displayMyAvatar?: boolean;
};
</script>
@@ -77,7 +78,7 @@ const emit = defineEmits<{
const injectedPageMetadata = inject(DI.pageMetadata, ref(null));
const pageMetadata = computed(() => props.overridePageMetadata ?? injectedPageMetadata.value);
-const hideTitle = computed(() => inject('shouldOmitHeaderTitle', false) || props.hideTitle);
+const hideTitle = computed(() => inject('shouldOmitHeaderTitle', false) || props.hideTitle || (props.canOmitTitle && props.tabs.length > 0));
const thin_ = props.thin || inject('shouldHeaderThin', false);
const el = useTemplateRef('el');
diff --git a/packages/frontend/src/components/global/MkUrl.vue b/packages/frontend/src/components/global/MkUrl.vue
index 914c495d7a..159af6f11e 100644
--- a/packages/frontend/src/components/global/MkUrl.vue
+++ b/packages/frontend/src/components/global/MkUrl.vue
@@ -62,7 +62,7 @@ if (props.showUrlPreview && isEnabledUrlPreview.value) {
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
showing,
url: props.url,
- source: el.value instanceof HTMLElement ? el.value : el.value?.$el,
+ anchorElement: el.value instanceof HTMLElement ? el.value : el.value?.$el,
}, {
closed: () => dispose(),
});
diff --git a/packages/frontend/src/components/global/PageWithHeader.vue b/packages/frontend/src/components/global/PageWithHeader.vue
index d90afb652e..d368dee88a 100644
--- a/packages/frontend/src/components/global/PageWithHeader.vue
+++ b/packages/frontend/src/components/global/PageWithHeader.vue
@@ -6,14 +6,22 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div ref="rootEl" :class="[$style.root, reversed ? '_pageScrollableReversed' : '_pageScrollable']">
<MkStickyContainer>
- <template #header><MkPageHeader v-model:tab="tab" v-bind="pageHeaderProps"/></template>
+ <template #header>
+ <MkPageHeader v-if="prefer.s.showPageTabBarBottom && (props.tabs?.length ?? 0) > 0" v-bind="pageHeaderPropsWithoutTabs"/>
+ <MkPageHeader v-else v-model:tab="tab" v-bind="pageHeaderProps"/>
+ </template>
<div :class="$style.body">
<MkSwiper v-if="prefer.s.enableHorizontalSwipe && swipable && (props.tabs?.length ?? 1) > 1" v-model:tab="tab" :class="$style.swiper" :tabs="props.tabs">
<slot></slot>
</MkSwiper>
<slot v-else></slot>
</div>
- <template #footer><slot name="footer"></slot></template>
+ <template #footer>
+ <slot name="footer"></slot>
+ <div v-if="prefer.s.showPageTabBarBottom && (props.tabs?.length ?? 0) > 0" :class="$style.footerTabs">
+ <MkTabs v-model:tab="tab" :tabs="props.tabs" :centered="true" :tabHighlightUpper="true"/>
+ </div>
+ </template>
</MkStickyContainer>
</div>
</template>
@@ -26,6 +34,7 @@ import { useScrollPositionKeeper } from '@/composables/use-scroll-position-keepe
import MkSwiper from '@/components/MkSwiper.vue';
import { useRouter } from '@/router.js';
import { prefer } from '@/preferences.js';
+import MkTabs from '@/components/MkTabs.vue';
const props = withDefaults(defineProps<PageHeaderProps & {
reversed?: boolean;
@@ -40,6 +49,11 @@ const pageHeaderProps = computed(() => {
return rest;
});
+const pageHeaderPropsWithoutTabs = computed(() => {
+ const { reversed, tabs, ...rest } = props;
+ return rest;
+});
+
const tab = defineModel<string>('tab');
const rootEl = useTemplateRef('rootEl');
@@ -68,4 +82,11 @@ defineExpose({
.body, .swiper {
min-height: calc(100cqh - (var(--MI-stickyTop, 0px) + var(--MI-stickyBottom, 0px)));
}
+
+.footerTabs {
+ background: color(from var(--MI_THEME-pageHeaderBg) srgb r g b / 0.75);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
+ border-top: solid 0.5px var(--MI_THEME-divider);
+}
</style>
diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue
index 27f7b18559..d42beb531d 100644
--- a/packages/frontend/src/components/global/RouterView.vue
+++ b/packages/frontend/src/components/global/RouterView.vue
@@ -30,19 +30,21 @@ const props = defineProps<{
router?: Router;
}>();
-const router = props.router ?? inject(DI.router);
+const _router = props.router ?? inject(DI.router);
-if (router == null) {
+if (_router == null) {
throw new Error('no router provided');
}
+const router = _router;
+
const viewId = randomId();
provide(DI.viewId, viewId);
const currentDepth = inject(DI.routerCurrentDepth, 0);
provide(DI.routerCurrentDepth, currentDepth + 1);
-const current = router.current!;
+const current = router.current;
const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage);
const currentPageProps = ref(current.props);
let currentRoutePath = current.route.path;
@@ -52,14 +54,10 @@ router.useListener('change', ({ resolved }) => {
if (resolved == null || 'redirect' in resolved.route) return;
if (resolved.route.path === currentRoutePath && deepEqual(resolved.props, currentPageProps.value)) return;
- function _() {
- currentPageComponent.value = resolved.route.component;
- currentPageProps.value = resolved.props;
- key.value = router.getCurrentFullPath();
- currentRoutePath = resolved.route.path;
- }
-
- _();
+ currentPageComponent.value = resolved.route.component;
+ currentPageProps.value = resolved.props;
+ key.value = router.getCurrentFullPath();
+ currentRoutePath = resolved.route.path;
});
</script>
diff --git a/packages/frontend/src/components/global/SearchKeyword.vue b/packages/frontend/src/components/global/SearchText.vue
index 27a284faf0..27a284faf0 100644
--- a/packages/frontend/src/components/global/SearchKeyword.vue
+++ b/packages/frontend/src/components/global/SearchText.vue
diff --git a/packages/frontend/src/components/global/StackingRouterView.vue b/packages/frontend/src/components/global/StackingRouterView.vue
index c95c74aef3..4c56767608 100644
--- a/packages/frontend/src/components/global/StackingRouterView.vue
+++ b/packages/frontend/src/components/global/StackingRouterView.vue
@@ -76,7 +76,7 @@ function mount() {
function back() {
const prev = tabs.value[tabs.value.length - 2];
tabs.value = [...tabs.value.slice(0, tabs.value.length - 1)];
- router.replace(prev.fullPath);
+ router?.replaceByPath(prev.fullPath);
}
router.useListener('change', ({ resolved }) => {
@@ -87,7 +87,7 @@ router.useListener('change', ({ resolved }) => {
const fullPath = router.getCurrentFullPath();
if (tabs.value.some(tab => tab.routePath === routePath && deepEqual(resolved.props, tab.props))) {
- const newTabs = [];
+ const newTabs = [] as typeof tabs.value;
for (const tab of tabs.value) {
newTabs.push(tab);
diff --git a/packages/frontend/src/components/grid/MkCellTooltip.vue b/packages/frontend/src/components/grid/MkCellTooltip.vue
index fd289c6cd9..6cd4f9ec1c 100644
--- a/packages/frontend/src/components/grid/MkCellTooltip.vue
+++ b/packages/frontend/src/components/grid/MkCellTooltip.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkTooltip ref="tooltip" :showing="showing" :targetElement="targetElement" :maxWidth="250" @closed="emit('closed')">
+<MkTooltip ref="tooltip" :showing="showing" :anchorElement="anchorElement" :maxWidth="250" @closed="emit('closed')">
<div :class="$style.root">
{{ content }}
</div>
@@ -18,7 +18,7 @@ import MkTooltip from '@/components/MkTooltip.vue';
defineProps<{
showing: boolean;
content: string;
- targetElement: HTMLElement;
+ anchorElement: HTMLElement;
}>();
const emit = defineEmits<{
diff --git a/packages/frontend/src/components/grid/MkDataCell.vue b/packages/frontend/src/components/grid/MkDataCell.vue
index 444509e6b3..0f326b14ca 100644
--- a/packages/frontend/src/components/grid/MkDataCell.vue
+++ b/packages/frontend/src/components/grid/MkDataCell.vue
@@ -300,7 +300,7 @@ useTooltip(rootEl, (showing) => {
const result = os.popup(defineAsyncComponent(() => import('@/components/grid/MkCellTooltip.vue')), {
showing,
content,
- targetElement: rootEl.value!,
+ anchorElement: rootEl.value!,
}, {
closed: () => {
result.dispose();
diff --git a/packages/frontend/src/components/grid/MkGrid.stories.impl.ts b/packages/frontend/src/components/grid/MkGrid.stories.impl.ts
index f85bf146e8..5ed8465299 100644
--- a/packages/frontend/src/components/grid/MkGrid.stories.impl.ts
+++ b/packages/frontend/src/components/grid/MkGrid.stories.impl.ts
@@ -4,7 +4,7 @@
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3';
import { ref } from 'vue';
import { commonHandlers } from '../../../.storybook/mocks.js';
diff --git a/packages/frontend/src/components/index.ts b/packages/frontend/src/components/index.ts
index 19766e8575..6b1b80695f 100644
--- a/packages/frontend/src/components/index.ts
+++ b/packages/frontend/src/components/index.ts
@@ -31,7 +31,7 @@ import PageWithHeader from './global/PageWithHeader.vue';
import PageWithAnimBg from './global/PageWithAnimBg.vue';
import SearchMarker from './global/SearchMarker.vue';
import SearchLabel from './global/SearchLabel.vue';
-import SearchKeyword from './global/SearchKeyword.vue';
+import SearchText from './global/SearchText.vue';
import SearchIcon from './global/SearchIcon.vue';
import type { App } from 'vue';
@@ -71,7 +71,7 @@ export const components = {
PageWithAnimBg: PageWithAnimBg,
SearchMarker: SearchMarker,
SearchLabel: SearchLabel,
- SearchKeyword: SearchKeyword,
+ SearchText: SearchText,
SearchIcon: SearchIcon,
};
@@ -105,7 +105,7 @@ declare module '@vue/runtime-core' {
PageWithAnimBg: typeof PageWithAnimBg;
SearchMarker: typeof SearchMarker;
SearchLabel: typeof SearchLabel;
- SearchKeyword: typeof SearchKeyword;
+ SearchText: typeof SearchText;
SearchIcon: typeof SearchIcon;
}
}
diff --git a/packages/frontend/src/deck.ts b/packages/frontend/src/deck.ts
index 73a3cecd3b..208adae8fe 100644
--- a/packages/frontend/src/deck.ts
+++ b/packages/frontend/src/deck.ts
@@ -62,6 +62,8 @@ export type Column = {
withSensitive?: boolean;
onlyFiles?: boolean;
soundSetting?: SoundStore;
+ // The cache for the name of the antenna, channel, list, or role
+ timelineNameCache?: string;
};
const _currentProfile = prefer.s['deck.profiles'].find(p => p.name === prefer.s['deck.profile']);
diff --git a/packages/frontend/src/directives/appear.ts b/packages/frontend/src/directives/appear.ts
index 802477e00b..f5fec108dc 100644
--- a/packages/frontend/src/directives/appear.ts
+++ b/packages/frontend/src/directives/appear.ts
@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
+import { throttle } from 'throttle-debounce';
import type { Directive } from 'vue';
export default {
@@ -10,12 +11,14 @@ export default {
const fn = binding.value;
if (fn == null) return;
- const observer = new IntersectionObserver(entries => {
+ const check = throttle(1000, (entries) => {
if (entries.some(entry => entry.isIntersecting)) {
fn();
}
});
+ const observer = new IntersectionObserver(check);
+
observer.observe(src);
src._observer_ = observer;
diff --git a/packages/frontend/src/directives/tooltip.ts b/packages/frontend/src/directives/tooltip.ts
index 750acd0588..62aecbc87c 100644
--- a/packages/frontend/src/directives/tooltip.ts
+++ b/packages/frontend/src/directives/tooltip.ts
@@ -57,7 +57,7 @@ export default {
text: self.text,
asMfm: binding.modifiers.mfm,
direction: binding.modifiers.left ? 'left' : binding.modifiers.right ? 'right' : binding.modifiers.top ? 'top' : binding.modifiers.bottom ? 'bottom' : 'top',
- targetElement: el,
+ anchorElement: el,
}, {
closed: () => dispose(),
});
diff --git a/packages/frontend/src/directives/user-preview.ts b/packages/frontend/src/directives/user-preview.ts
index 94deea82c7..b11ef8f088 100644
--- a/packages/frontend/src/directives/user-preview.ts
+++ b/packages/frontend/src/directives/user-preview.ts
@@ -6,6 +6,7 @@
import { defineAsyncComponent, ref } from 'vue';
import type { Directive } from 'vue';
import { popup } from '@/os.js';
+import { isTouchUsing } from '@/utility/touch.js';
export class UserPreview {
private el;
@@ -107,6 +108,7 @@ export class UserPreview {
export default {
mounted(el: HTMLElement, binding, vn) {
if (binding.value == null) return;
+ if (isTouchUsing) return;
// TODO: æ–°ãŸã«ãƒ—ロパティを作るã®ã‚’ã‚„ã‚Mapを使ã†
// ãŸã ãƒ¡ãƒ¢ãƒªçš„ã«ã¯â†“ã®æ–¹ãŒçœãƒ¡ãƒ¢ãƒªã‹ã‚‚ã—れãªã„ã®ã§æ¤œè¨Žä¸­
diff --git a/packages/frontend/src/i18n.ts b/packages/frontend/src/i18n.ts
index 6ad503b089..0b2b206b7e 100644
--- a/packages/frontend/src/i18n.ts
+++ b/packages/frontend/src/i18n.ts
@@ -5,11 +5,12 @@
import { markRaw } from 'vue';
import { I18n } from '@@/js/i18n.js';
+import { locale } from '@@/js/locale.js';
import type { Locale } from '../../../locales/index.js';
-import { locale } from '@@/js/config.js';
export const i18n = markRaw(new I18n<Locale>(locale, _DEV_));
+// test 以外ã§ã¯ä½¿ã‚ãªã„ã“ã¨ã€‚インライン化ã•れã¦ã‚‹ã®ã§ã ã„ãŸã„æ„味ãŒãªã„
export function updateI18n(newLocale: Locale) {
i18n.locale = newLocale;
}
diff --git a/packages/frontend/src/lib/nirax.ts b/packages/frontend/src/lib/nirax.ts
index a166df9eb0..74dda9decd 100644
--- a/packages/frontend/src/lib/nirax.ts
+++ b/packages/frontend/src/lib/nirax.ts
@@ -58,7 +58,7 @@ export type RouterEvents = {
beforeFullPath: string;
fullPath: string;
route: RouteDef | null;
- props: Map<string, string> | null;
+ props: Map<string, string | boolean> | null;
}) => void;
same: () => void;
};
@@ -77,6 +77,112 @@ export type PathResolvedResult = {
};
};
+//#region Path Types
+type Prettify<T> = {
+ [K in keyof T]: T[K]
+} & {};
+
+type RemoveNever<T> = {
+ [K in keyof T as T[K] extends never ? never : K]: T[K];
+} & {};
+
+type IsPathParameter<Part extends string> = Part extends `${string}:${infer Parameter}` ? Parameter : never;
+
+type GetPathParamKeys<Path extends string> =
+ Path extends `${infer A}/${infer B}`
+ ? IsPathParameter<A> | GetPathParamKeys<B>
+ : IsPathParameter<Path>;
+
+type GetPathParams<Path extends string> = Prettify<{
+ [Param in GetPathParamKeys<Path> as Param extends `${string}?` ? never : Param]: string;
+} & {
+ [Param in GetPathParamKeys<Path> as Param extends `${infer OptionalParam}?` ? OptionalParam : never]?: string;
+}>;
+
+type UnwrapReadOnly<T> = T extends ReadonlyArray<infer U>
+ ? U
+ : T extends Readonly<infer U>
+ ? U
+ : T;
+
+type GetPaths<Def extends RouteDef> = Def extends { path: infer Path }
+ ? Path extends string
+ ? Def extends { children: infer Children }
+ ? Children extends RouteDef[]
+ ? Path | `${Path}${FlattenAllPaths<Children>}`
+ : Path
+ : Path
+ : never
+ : never;
+
+type FlattenAllPaths<Defs extends RouteDef[]> = GetPaths<Defs[number]>;
+
+type GetSinglePathQuery<Def extends RouteDef, Path extends FlattenAllPaths<RouteDef[]>> = RemoveNever<
+ Def extends { path: infer BasePath, children: infer Children }
+ ? BasePath extends string
+ ? Path extends `${BasePath}${infer ChildPath}`
+ ? Children extends RouteDef[]
+ ? ChildPath extends FlattenAllPaths<Children>
+ ? GetPathQuery<Children, ChildPath>
+ : Record<string, never>
+ : never
+ : never
+ : never
+ : Def['path'] extends Path
+ ? Def extends { query: infer Query }
+ ? Query extends Record<string, string>
+ ? UnwrapReadOnly<{ [Key in keyof Query]?: string; }>
+ : Record<string, never>
+ : Record<string, never>
+ : Record<string, never>
+ >;
+
+type GetPathQuery<Defs extends RouteDef[], Path extends FlattenAllPaths<Defs>> = GetSinglePathQuery<Defs[number], Path>;
+
+type RequiredIfNotEmpty<K extends string, T extends Record<string, unknown>> = T extends Record<string, never>
+ ? { [Key in K]?: T }
+ : { [Key in K]: T };
+
+type NotRequiredIfEmpty<T extends Record<string, unknown>> = T extends Record<string, never> ? T | undefined : T;
+
+type GetRouterOperationProps<Defs extends RouteDef[], Path extends FlattenAllPaths<Defs>> = NotRequiredIfEmpty<RequiredIfNotEmpty<'params', GetPathParams<Path>> & {
+ query?: GetPathQuery<Defs, Path>;
+ hash?: string;
+}>;
+//#endregion
+
+function buildFullPath(args: {
+ path: string;
+ params?: Record<string, string>;
+ query?: Record<string, string>;
+ hash?: string;
+}) {
+ let fullPath = args.path;
+
+ if (args.params) {
+ for (const key in args.params) {
+ const value = args.params[key];
+ const replaceRegex = new RegExp(`:${key}(\\?)?`, 'g');
+ fullPath = fullPath.replace(replaceRegex, value ? encodeURIComponent(value) : '');
+ }
+ // remove any optional parameters that are not provided
+ fullPath = fullPath.replace(/\/:\w+\?(?=\/|$)/g, '');
+ }
+
+ if (args.query) {
+ const queryString = new URLSearchParams(args.query).toString();
+ if (queryString) {
+ fullPath += '?' + queryString;
+ }
+ }
+
+ if (args.hash) {
+ fullPath += '#' + encodeURIComponent(args.hash);
+ }
+
+ return fullPath;
+}
+
function parsePath(path: string): ParsedPath {
const res = [] as ParsedPath;
@@ -282,7 +388,7 @@ export class Nirax<DEF extends RouteDef[]> extends EventEmitter<RouterEvents> {
}
}
- if (res.route.loginRequired && !this.isLoggedIn) {
+ if (res.route.loginRequired && !this.isLoggedIn && 'component' in res.route) {
res.route.component = this.notFoundPageComponent;
res.props.set('showLoginPopup', true);
}
@@ -310,14 +416,35 @@ export class Nirax<DEF extends RouteDef[]> extends EventEmitter<RouterEvents> {
return this.currentFullPath;
}
- public push(fullPath: string, flag?: RouterFlag) {
+ public push<P extends FlattenAllPaths<DEF>>(path: P, props?: GetRouterOperationProps<DEF, P>, flag?: RouterFlag | null) {
+ const fullPath = buildFullPath({
+ path,
+ params: props?.params,
+ query: props?.query,
+ hash: props?.hash,
+ });
+ this.pushByPath(fullPath, flag);
+ }
+
+ public replace<P extends FlattenAllPaths<DEF>>(path: P, props?: GetRouterOperationProps<DEF, P>) {
+ const fullPath = buildFullPath({
+ path,
+ params: props?.params,
+ query: props?.query,
+ hash: props?.hash,
+ });
+ this.replaceByPath(fullPath);
+ }
+
+ /** ã©ã†ã—ã¦ã‚‚å¿…è¦ãªå ´åˆã«ä½¿ç”¨ï¼ˆãƒ‘スãŒç¢ºå®šã—ã¦ã„ã‚‹å ´åˆã¯ `Nirax.push` を使用ã™ã‚‹ã“ã¨ï¼‰ */
+ public pushByPath(fullPath: string, flag?: RouterFlag | null) {
const beforeFullPath = this.currentFullPath;
if (fullPath === beforeFullPath) {
this.emit('same');
return;
}
if (this.navHook) {
- const cancel = this.navHook(fullPath, flag);
+ const cancel = this.navHook(fullPath, flag ?? undefined);
if (cancel) return;
}
const res = this.navigate(fullPath);
@@ -333,14 +460,15 @@ export class Nirax<DEF extends RouteDef[]> extends EventEmitter<RouterEvents> {
}
}
- public replace(fullPath: string) {
+ /** ã©ã†ã—ã¦ã‚‚å¿…è¦ãªå ´åˆã«ä½¿ç”¨ï¼ˆãƒ‘スãŒç¢ºå®šã—ã¦ã„ã‚‹å ´åˆã¯ `Nirax.replace` を使用ã™ã‚‹ã“ã¨ï¼‰ */
+ public replaceByPath(fullPath: string) {
const res = this.navigate(fullPath);
this.emit('replace', {
fullPath: res._parsedRoute.fullPath,
});
}
- public useListener<E extends keyof RouterEvents, L = RouterEvents[E]>(event: E, listener: L) {
+ public useListener<E extends keyof RouterEvents>(event: E, listener: EventEmitter.EventListener<RouterEvents, E>) {
this.addListener(event, listener);
onBeforeUnmount(() => {
diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts
index 78fba9f7b4..687983bcdb 100644
--- a/packages/frontend/src/local-storage.ts
+++ b/packages/frontend/src/local-storage.ts
@@ -22,8 +22,7 @@ export type Keys = (
'fontSize' |
'ui' |
'ui_temp' |
- 'locale' |
- 'localeVersion' |
+ 'bootloaderLocales' |
'theme' |
'themeId' |
'customCss' |
@@ -33,6 +32,7 @@ export type Keys = (
'preferences' |
'latestPreferencesUpdate' |
'hidePreferencesRestoreSuggestion' |
+ 'isSafeMode' |
`miux:${string}` |
`ui:folder:${string}` |
`themes:${string}` | // DEPRECATED
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index bf0e5e1b37..56a2b8d269 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -9,7 +9,7 @@ import { markRaw, ref, defineAsyncComponent, nextTick } from 'vue';
import { EventEmitter } from 'eventemitter3';
import * as Misskey from 'misskey-js';
import type { Component, Ref } from 'vue';
-import type { ComponentProps as CP } from 'vue-component-type-helpers';
+import type { ComponentEmit, ComponentProps as CP } from 'vue-component-type-helpers';
import type { Form, GetFormResultType } from '@/utility/form.js';
import type { MenuItem } from '@/types/menu.js';
import type { PostFormProps } from '@/types/post-form.js';
@@ -157,28 +157,9 @@ export function claimZIndex(priority: keyof typeof zIndexes = 'low'): number {
return zIndexes[priority];
}
-// InstanceType<typeof Component>['$emit'] ã ã¨ã‚¤ãƒ³ã‚¿ãƒ¼ã‚»ã‚¯ã‚·ãƒ§ãƒ³åž‹ãŒè¿”ã£ã¦ãã¦
-// 使ã„物ã«ãªã‚‰ãªã„ã®ã§ã€ä»£ã‚り㫠['$props'] ã‹ã‚‰è‰²ã€…çœãã“ã¨ã§ emit ã®åž‹ã‚’生æˆã™ã‚‹
-// FIXME: 何故㋠*.ts ファイルã‹ã‚‰ã ã¨åž‹ãŒã†ã¾ãå–れãªã„?ã“ã¨ãŒã‚ã‚‹ã®ã‚’ãªã‚“ã¨ã‹ã—ãŸã„
-type ComponentEmit<T> = T extends new () => { $props: infer Props }
- ? [keyof Pick<T, Extract<keyof T, `on${string}`>>] extends [never]
- ? Record<string, unknown> // *.ts ファイルã‹ã‚‰åž‹ãŒã†ã¾ãå–れãªã„ã¨ã用(ã“れãŒãªã„㨠{} ã«ãªã£ã¦åž‹ã‚¨ãƒ©ãƒ¼ãŒã†ã‚‹ã•ã„)
- : EmitsExtractor<Props>
- : T extends (...args: any) => any
- ? ReturnType<T> extends { [x: string]: any; __ctx?: { [x: string]: any; props: infer Props } }
- ? [keyof Pick<T, Extract<keyof T, `on${string}`>>] extends [never]
- ? Record<string, unknown>
- : EmitsExtractor<Props>
- : never
- : never;
-
// props ã« ref を許å¯ã™ã‚‹ã‚ˆã†ã«ã™ã‚‹
type ComponentProps<T extends Component> = { [K in keyof CP<T>]: CP<T>[K] | Ref<CP<T>[K]> };
-type EmitsExtractor<T> = {
- [K in keyof T as K extends `onVnode${string}` ? never : K extends `on${infer E}` ? Uncapitalize<E> : K extends string ? never : K]: T[K];
-};
-
export function popup<T extends Component>(
component: T,
props: ComponentProps<T>,
@@ -703,7 +684,7 @@ export async function cropImageFile(imageFile: File | Blob, options: {
});
}
-export function popupMenu(items: MenuItem[], anchorElement?: HTMLElement | EventTarget | null, options?: {
+export function popupMenu(items: (MenuItem | null)[], anchorElement?: HTMLElement | EventTarget | null, options?: {
align?: string;
width?: number;
onClosing?: () => void;
@@ -715,7 +696,7 @@ export function popupMenu(items: MenuItem[], anchorElement?: HTMLElement | Event
let returnFocusTo = getHTMLElementOrNull(anchorElement) ?? getHTMLElementOrNull(window.document.activeElement);
return new Promise(resolve => nextTick(() => {
const { dispose } = popup(MkPopupMenu, {
- items,
+ items: items.filter(x => x != null),
anchorElement,
width: options?.width,
align: options?.align,
diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index 057deec4cf..a481972174 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -111,6 +111,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<div>
<a style="display: inline-block;" class="purpledotdigital" title="Purple Dot Digital" href="https://purpledotdigital.com/" target="_blank"><img style="width: 100%;" src="https://assets.misskey-hub.net/sponsors/purple-dot-digital.jpg" alt="Purple Dot Digital"></a>
</div>
+ <div>
+ <a style="display: inline-block;" class="sads-llc" title="åˆåŒä¼šç¤¾ã‚µãƒƒã‚º" href="https://sads-llc.co.jp/" target="_blank"><img style="width: 100%;" src="https://assets.misskey-hub.net/sponsors/sads-llc.png" alt="åˆåŒä¼šç¤¾ã‚µãƒƒã‚º"></a>
+ </div>
</div>
</FormSection>
<FormSection>
@@ -286,6 +289,12 @@ const patronsWithIcon = [{
}, {
name: '井上åƒäºŒåå››',
icon: 'https://assets.misskey-hub.net/patrons/193afa1f039b4c339866039c3dcd74bf.jpg',
+}, {
+ name: 'NigN',
+ icon: 'https://assets.misskey-hub.net/patrons/1ccaef8e73ec4a50b59ff7cd688ceb84.jpg',
+}, {
+ name: 'ã—ゃã©ã‹ã®',
+ icon: 'https://assets.misskey-hub.net/patrons/5bec3c6b402942619e03f7a2ae76d69e.jpg',
}];
const patrons = [
@@ -399,6 +408,8 @@ const patrons = [
'ã¿ã‚Šã‚ã„',
'æ±é›² ç¥ç€',
'ã»ã¨ãƒ©ã‚º',
+ 'スズカケン',
+ '蒼井よã¿ã“',
];
const thereIsTreasure = ref($i && !claimedAchievements.includes('foundTreasure'));
diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue
index b166dfd940..7e514c5a73 100644
--- a/packages/frontend/src/pages/about.emojis.vue
+++ b/packages/frontend/src/pages/about.emojis.vue
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkFoldableSection>
- <MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category">
+ <MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category ?? '___root___'">
<template #header>{{ category || i18n.ts.other }}</template>
<div :class="$style.emojis">
<XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" :emoji="emoji"/>
@@ -48,7 +48,7 @@ import { $i } from '@/i.js';
const customEmojiTags = getCustomEmojiTags();
const q = ref('');
-const searchEmojis = ref<Misskey.entities.EmojiSimple[]>(null);
+const searchEmojis = ref<Misskey.entities.EmojiSimple[] | null>(null);
const selectedTags = ref(new Set());
function search() {
diff --git a/packages/frontend/src/pages/about.federation.vue b/packages/frontend/src/pages/about.federation.vue
index 47ec675d57..fd5e061d52 100644
--- a/packages/frontend/src/pages/about.federation.vue
+++ b/packages/frontend/src/pages/about.federation.vue
@@ -22,20 +22,46 @@ SPDX-License-Identifier: AGPL-3.0-only
<option value="blocked">{{ i18n.ts.blocked }}</option>
<option value="notResponding">{{ i18n.ts.notResponding }}</option>
</MkSelect>
- <MkSelect v-model="sort">
+ <MkSelect
+ v-model="sort" :items="[{
+ label: `${i18n.ts.pubSub} (${i18n.ts.descendingOrder})`,
+ value: '+pubSub',
+ }, {
+ label: `${i18n.ts.pubSub} (${i18n.ts.ascendingOrder})`,
+ value: '-pubSub',
+ }, {
+ label: `${i18n.ts.notes} (${i18n.ts.descendingOrder})`,
+ value: '+notes',
+ }, {
+ label: `${i18n.ts.notes} (${i18n.ts.ascendingOrder})`,
+ value: '-notes',
+ }, {
+ label: `${i18n.ts.users} (${i18n.ts.descendingOrder})`,
+ value: '+users',
+ }, {
+ label: `${i18n.ts.users} (${i18n.ts.ascendingOrder})`,
+ value: '-users',
+ }, {
+ label: `${i18n.ts.following} (${i18n.ts.descendingOrder})`,
+ value: '+following',
+ }, {
+ label: `${i18n.ts.following} (${i18n.ts.ascendingOrder})`,
+ value: '-following',
+ }, {
+ label: `${i18n.ts.followers} (${i18n.ts.descendingOrder})`,
+ value: '+followers',
+ }, {
+ label: `${i18n.ts.followers} (${i18n.ts.ascendingOrder})`,
+ value: '-followers',
+ }, {
+ label: `${i18n.ts.registeredAt} (${i18n.ts.descendingOrder})`,
+ value: '+firstRetrievedAt',
+ }, {
+ label: `${i18n.ts.registeredAt} (${i18n.ts.ascendingOrder})`,
+ value: '-firstRetrievedAt',
+ }] as const"
+ >
<template #label>{{ i18n.ts.sort }}</template>
- <option value="+pubSub">{{ i18n.ts.pubSub }} ({{ i18n.ts.descendingOrder }})</option>
- <option value="-pubSub">{{ i18n.ts.pubSub }} ({{ i18n.ts.ascendingOrder }})</option>
- <option value="+notes">{{ i18n.ts.notes }} ({{ i18n.ts.descendingOrder }})</option>
- <option value="-notes">{{ i18n.ts.notes }} ({{ i18n.ts.ascendingOrder }})</option>
- <option value="+users">{{ i18n.ts.users }} ({{ i18n.ts.descendingOrder }})</option>
- <option value="-users">{{ i18n.ts.users }} ({{ i18n.ts.ascendingOrder }})</option>
- <option value="+following">{{ i18n.ts.following }} ({{ i18n.ts.descendingOrder }})</option>
- <option value="-following">{{ i18n.ts.following }} ({{ i18n.ts.ascendingOrder }})</option>
- <option value="+followers">{{ i18n.ts.followers }} ({{ i18n.ts.descendingOrder }})</option>
- <option value="-followers">{{ i18n.ts.followers }} ({{ i18n.ts.ascendingOrder }})</option>
- <option value="+firstRetrievedAt">{{ i18n.ts.registeredAt }} ({{ i18n.ts.descendingOrder }})</option>
- <option value="-firstRetrievedAt">{{ i18n.ts.registeredAt }} ({{ i18n.ts.ascendingOrder }})</option>
</MkSelect>
</FormSplit>
</div>
@@ -52,6 +78,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, markRaw, ref } from 'vue';
+import * as Misskey from 'misskey-js';
import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkPagination from '@/components/MkPagination.vue';
@@ -62,7 +89,7 @@ import { Paginator } from '@/utility/paginator.js';
const host = ref('');
const state = ref('federating');
-const sort = ref('+pubSub');
+const sort = ref<NonNullable<Misskey.entities.FederationInstancesRequest['sort']>>('+pubSub');
const paginator = markRaw(new Paginator('federation/instances', {
limit: 10,
offsetMode: true,
diff --git a/packages/frontend/src/pages/about.overview.vue b/packages/frontend/src/pages/about.overview.vue
index 0b9eee7d49..4786615173 100644
--- a/packages/frontend/src/pages/about.overview.vue
+++ b/packages/frontend/src/pages/about.overview.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_m">
<div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }">
<div style="overflow: clip;">
- <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.bannerIcon"/>
+ <img :src="instance.iconUrl ?? '/favicon.ico'" alt="" :class="$style.bannerIcon"/>
<div :class="$style.bannerName">
<b>{{ instance.name ?? host }}</b>
</div>
diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue
index 0edf2db1eb..ba9acd3ad2 100644
--- a/packages/frontend/src/pages/about.vue
+++ b/packages/frontend/src/pages/about.vue
@@ -48,34 +48,22 @@ watch(tab, () => {
const headerActions = computed(() => []);
-const headerTabs = computed(() => {
- const items = [];
-
- items.push({
- key: 'overview',
- title: i18n.ts.overview,
- }, {
- key: 'emojis',
- title: i18n.ts.customEmojis,
- icon: 'ti ti-icons',
- });
-
- if (instance.federation !== 'none') {
- items.push({
- key: 'federation',
- title: i18n.ts.federation,
- icon: 'ti ti-whirl',
- });
- }
-
- items.push({
- key: 'charts',
- title: i18n.ts.charts,
- icon: 'ti ti-chart-line',
- });
-
- return items;
-});
+const headerTabs = computed(() => [{
+ key: 'overview',
+ title: i18n.ts.overview,
+}, {
+ key: 'emojis',
+ title: i18n.ts.customEmojis,
+ icon: 'ti ti-icons',
+}, ...(instance.federation !== 'none' ? [{
+ key: 'federation',
+ title: i18n.ts.federation,
+ icon: 'ti ti-whirl',
+}] : []), {
+ key: 'charts',
+ title: i18n.ts.charts,
+ icon: 'ti ti-chart-line',
+}]);
definePage(() => ({
title: i18n.ts.instanceInfo,
diff --git a/packages/frontend/src/pages/achievements.vue b/packages/frontend/src/pages/achievements.vue
index 1560403b70..f4de2df6d5 100644
--- a/packages/frontend/src/pages/achievements.vue
+++ b/packages/frontend/src/pages/achievements.vue
@@ -16,9 +16,11 @@ import { onActivated, onDeactivated, onMounted, onUnmounted } from 'vue';
import MkAchievements from '@/components/MkAchievements.vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
-import { $i } from '@/i.js';
+import { ensureSignin } from '@/i.js';
import { claimAchievement } from '@/utility/achievements.js';
+const $i = ensureSignin();
+
let timer: number | null;
function viewAchievements3min() {
diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue
index 7a49ba542f..052829ffe2 100644
--- a/packages/frontend/src/pages/admin-file.vue
+++ b/packages/frontend/src/pages/admin-file.vue
@@ -111,13 +111,13 @@ const props = defineProps<{
fileId: string,
}>();
-async function fetch() {
+async function _fetch_() {
file.value = await misskeyApi('drive/files/show', { fileId: props.fileId });
info.value = await misskeyApi('admin/drive/show-file', { fileId: props.fileId });
isSensitive.value = file.value.isSensitive;
}
-fetch();
+_fetch_();
async function del() {
const { canceled } = await os.confirm({
@@ -172,7 +172,7 @@ const headerTabs = computed(() => [{
key: 'raw',
title: 'Raw data',
icon: 'ti ti-code',
-}]);
+}].filter(x => x != null));
definePage(() => ({
title: file.value ? `${i18n.ts.file}: ${file.value.name}` : i18n.ts.file,
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index a194b9a94f..38e3c7a11b 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -6,58 +6,57 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
<div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
- <FormSuspense :p="init">
- <div v-if="tab === 'overview'" class="_gaps_m">
- <div class="aeakzknw">
- <MkAvatar class="avatar" :user="user" indicator link preview/>
- <div class="body">
- <span class="name"><MkUserName class="name" :user="user"/></span>
- <span class="sub"><span class="acct _monospace">@{{ acct(user) }}</span></span>
- <span class="state">
- <span v-if="suspended" class="suspended">Suspended</span>
- <span v-if="silenced" class="silenced">Silenced</span>
- <span v-if="moderator" class="moderator">Moderator</span>
- </span>
- </div>
+ <div v-if="tab === 'overview'" class="_gaps_m">
+ <div class="aeakzknw">
+ <MkAvatar class="avatar" :user="user" indicator link preview/>
+ <div class="body">
+ <span class="name"><MkUserName class="name" :user="user"/></span>
+ <span class="sub"><span class="acct _monospace">@{{ acct(user) }}</span></span>
+ <span class="state">
+ <span v-if="suspended" class="suspended">Suspended</span>
+ <span v-if="silenced" class="silenced">Silenced</span>
+ <span v-if="moderator" class="moderator">Moderator</span>
+ </span>
</div>
+ </div>
- <MkInfo v-if="isSystem">{{ i18n.ts.isSystemAccount }}</MkInfo>
+ <MkInfo v-if="isSystem">{{ i18n.ts.isSystemAccount }}</MkInfo>
- <FormLink v-if="user.host" :to="`/instance-info/${user.host}`">{{ i18n.ts.instanceInfo }}</FormLink>
+ <FormLink v-if="user.host" :to="`/instance-info/${user.host}`">{{ i18n.ts.instanceInfo }}</FormLink>
- <div style="display: flex; flex-direction: column; gap: 1em;">
- <MkKeyValue :copy="user.id" oneline>
- <template #key>ID</template>
- <template #value><span class="_monospace">{{ user.id }}</span></template>
- </MkKeyValue>
- <!-- è¦ã‚‹ï¼Ÿ
+ <div style="display: flex; flex-direction: column; gap: 1em;">
+ <MkKeyValue :copy="user.id" oneline>
+ <template #key>ID</template>
+ <template #value><span class="_monospace">{{ user.id }}</span></template>
+ </MkKeyValue>
+ <!-- è¦ã‚‹ï¼Ÿ
<MkKeyValue v-if="ips.length > 0" :copy="user.id" oneline>
<template #key>IP (recent)</template>
<template #value><span class="_monospace">{{ ips[0].ip }}</span></template>
</MkKeyValue>
-->
- <template v-if="!isSystem">
- <MkKeyValue oneline>
- <template #key>{{ i18n.ts.createdAt }}</template>
- <template #value><span class="_monospace"><MkTime :time="user.createdAt" :mode="'detail'"/></span></template>
- </MkKeyValue>
- <MkKeyValue v-if="info" oneline>
- <template #key>{{ i18n.ts.lastActiveDate }}</template>
- <template #value><span class="_monospace"><MkTime :time="info.lastActiveDate" :mode="'detail'"/></span></template>
- </MkKeyValue>
- <MkKeyValue v-if="info" oneline>
- <template #key>{{ i18n.ts.email }}</template>
- <template #value><span class="_monospace">{{ info.email }}</span></template>
- </MkKeyValue>
- </template>
- </div>
+ <template v-if="!isSystem">
+ <MkKeyValue oneline>
+ <template #key>{{ i18n.ts.createdAt }}</template>
+ <template #value><span class="_monospace"><MkTime :time="user.createdAt" :mode="'detail'"/></span></template>
+ </MkKeyValue>
+ <MkKeyValue v-if="info" oneline>
+ <template #key>{{ i18n.ts.lastActiveDate }}</template>
+ <template #value><span class="_monospace"><MkTime :time="info.lastActiveDate" :mode="'detail'"/></span></template>
+ </MkKeyValue>
+ <MkKeyValue v-if="info" oneline>
+ <template #key>{{ i18n.ts.email }}</template>
+ <template #value><span class="_monospace">{{ info.email }}</span></template>
+ </MkKeyValue>
+ </template>
+ </div>
- <MkTextarea v-if="!isSystem" v-model="moderationNote" manualSave>
- <template #label>{{ i18n.ts.moderationNote }}</template>
- <template #caption>{{ i18n.ts.moderationNoteDescription }}</template>
- </MkTextarea>
+ <MkTextarea v-if="!isSystem" v-model="moderationNote" manualSave>
+ <template #label>{{ i18n.ts.moderationNote }}</template>
+ <template #caption>{{ i18n.ts.moderationNoteDescription }}</template>
+ </MkTextarea>
- <!--
+ <!--
<FormSection>
<template #label>ActivityPub</template>
@@ -93,119 +92,118 @@ SPDX-License-Identifier: AGPL-3.0-only
</FormSection>
-->
- <FormSection v-if="!isSystem">
- <div class="_gaps">
- <MkSwitch v-model="suspended" @update:modelValue="toggleSuspend">{{ i18n.ts.suspend }}</MkSwitch>
+ <FormSection v-if="!isSystem">
+ <div class="_gaps">
+ <MkSwitch v-model="suspended" @update:modelValue="toggleSuspend">{{ i18n.ts.suspend }}</MkSwitch>
- <div>
- <MkButton v-if="user.host == null" inline style="margin-right: 8px;" @click="resetPassword"><i class="ti ti-key"></i> {{ i18n.ts.resetPassword }}</MkButton>
- </div>
+ <div>
+ <MkButton v-if="user.host == null" inline style="margin-right: 8px;" @click="resetPassword"><i class="ti ti-key"></i> {{ i18n.ts.resetPassword }}</MkButton>
+ </div>
- <MkFolder>
- <template #icon><i class="ti ti-license"></i></template>
- <template #label>{{ i18n.ts._role.policies }}</template>
- <div class="_gaps">
- <div v-for="policy in Object.keys(info.policies)" :key="policy">
- {{ policy }} ... {{ info.policies[policy] }}
- </div>
+ <MkFolder>
+ <template #icon><i class="ti ti-license"></i></template>
+ <template #label>{{ i18n.ts._role.policies }}</template>
+ <div class="_gaps">
+ <div v-for="policy in Object.keys(info.policies)" :key="policy">
+ {{ policy }} ... {{ info.policies[policy] }}
</div>
- </MkFolder>
+ </div>
+ </MkFolder>
- <MkFolder>
- <template #icon><i class="ti ti-password"></i></template>
- <template #label>IP</template>
- <MkInfo v-if="!iAmAdmin" warn>{{ i18n.ts.requireAdminForView }}</MkInfo>
- <MkInfo v-else>The date is the IP address was first acknowledged.</MkInfo>
- <template v-if="iAmAdmin && ips">
- <div v-for="record in ips" :key="record.ip" class="_monospace" :class="$style.ip" style="margin: 1em 0;">
- <span class="date">{{ record.createdAt }}</span>
- <span class="ip">{{ record.ip }}</span>
- </div>
- </template>
- </MkFolder>
+ <MkFolder>
+ <template #icon><i class="ti ti-password"></i></template>
+ <template #label>IP</template>
+ <MkInfo v-if="!iAmAdmin" warn>{{ i18n.ts.requireAdminForView }}</MkInfo>
+ <MkInfo v-else>The date is the IP address was first acknowledged.</MkInfo>
+ <template v-if="iAmAdmin && ips">
+ <div v-for="record in ips" :key="record.ip" class="_monospace" :class="$style.ip" style="margin: 1em 0;">
+ <span class="date">{{ record.createdAt }}</span>
+ <span class="ip">{{ record.ip }}</span>
+ </div>
+ </template>
+ </MkFolder>
- <div>
- <MkButton v-if="iAmModerator" inline danger style="margin-right: 8px;" @click="unsetUserAvatar"><i class="ti ti-user-circle"></i> {{ i18n.ts.unsetUserAvatar }}</MkButton>
- <MkButton v-if="iAmModerator" inline danger @click="unsetUserBanner"><i class="ti ti-photo"></i> {{ i18n.ts.unsetUserBanner }}</MkButton>
- </div>
- <MkButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ i18n.ts.deleteAccount }}</MkButton>
+ <div>
+ <MkButton v-if="iAmModerator" inline danger style="margin-right: 8px;" @click="unsetUserAvatar"><i class="ti ti-user-circle"></i> {{ i18n.ts.unsetUserAvatar }}</MkButton>
+ <MkButton v-if="iAmModerator" inline danger @click="unsetUserBanner"><i class="ti ti-photo"></i> {{ i18n.ts.unsetUserBanner }}</MkButton>
</div>
- </FormSection>
- </div>
+ <MkButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ i18n.ts.deleteAccount }}</MkButton>
+ </div>
+ </FormSection>
+ </div>
- <div v-else-if="tab === 'roles'" class="_gaps">
- <MkButton v-if="user.host == null" primary rounded @click="assignRole"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton>
+ <div v-else-if="tab === 'roles'" class="_gaps">
+ <MkButton v-if="user.host == null" primary rounded @click="assignRole"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton>
- <div v-for="role in info.roles" :key="role.id">
- <div :class="$style.roleItemMain">
- <MkRolePreview :class="$style.role" :role="role" :forModeration="true"/>
- <button class="_button" @click="toggleRoleItem(role)"><i class="ti ti-chevron-down"></i></button>
- <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>
- <div v-if="expandedRoles.includes(role.id)" :class="$style.roleItemSub">
- <div>Assigned: <MkTime :time="info.roleAssigns.find(a => a.roleId === role.id).createdAt" mode="detail"/></div>
- <div v-if="info.roleAssigns.find(a => a.roleId === role.id).expiresAt">Period: {{ new Date(info.roleAssigns.find(a => a.roleId === role.id).expiresAt).toLocaleString() }}</div>
- <div v-else>Period: {{ i18n.ts.indefinitely }}</div>
- </div>
+ <div v-for="role in info.roles" :key="role.id">
+ <div :class="$style.roleItemMain">
+ <MkRolePreview :class="$style.role" :role="role" :forModeration="true"/>
+ <button class="_button" @click="toggleRoleItem(role)"><i class="ti ti-chevron-down"></i></button>
+ <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>
+ <div v-if="expandedRoleIds.includes(role.id)" :class="$style.roleItemSub">
+ <div>Assigned: <MkTime :time="info.roleAssigns.find(a => a.roleId === role.id)!.createdAt" mode="detail"/></div>
+ <div v-if="info.roleAssigns.find(a => a.roleId === role.id)!.expiresAt">Period: {{ new Date(info.roleAssigns.find(a => a.roleId === role.id)!.expiresAt!).toLocaleString() }}</div>
+ <div v-else>Period: {{ i18n.ts.indefinitely }}</div>
</div>
</div>
+ </div>
- <div v-else-if="tab === 'announcements'" class="_gaps">
- <MkButton primary rounded @click="createAnnouncement"><i class="ti ti-plus"></i> {{ i18n.ts.new }}</MkButton>
+ <div v-else-if="tab === 'announcements'" class="_gaps">
+ <MkButton primary rounded @click="createAnnouncement"><i class="ti ti-plus"></i> {{ i18n.ts.new }}</MkButton>
- <MkSelect v-model="announcementsStatus">
- <template #label>{{ i18n.ts.filter }}</template>
- <option value="active">{{ i18n.ts.active }}</option>
- <option value="archived">{{ i18n.ts.archived }}</option>
- </MkSelect>
+ <MkSelect v-model="announcementsStatus">
+ <template #label>{{ i18n.ts.filter }}</template>
+ <option value="active">{{ i18n.ts.active }}</option>
+ <option value="archived">{{ i18n.ts.archived }}</option>
+ </MkSelect>
- <MkPagination :paginator="announcementsPaginator">
- <template #default="{ items }">
- <div class="_gaps_s">
- <div v-for="announcement in items" :key="announcement.id" v-panel :class="$style.announcementItem" @click="editAnnouncement(announcement)">
- <span style="margin-right: 0.5em;">
- <i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i>
- <i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i>
- <i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--MI_THEME-error);"></i>
- <i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--MI_THEME-success);"></i>
- </span>
- <span>{{ announcement.title }}</span>
- <span v-if="announcement.reads > 0" style="margin-left: auto; opacity: 0.7;">{{ i18n.ts.messageRead }}</span>
- </div>
+ <MkPagination :paginator="announcementsPaginator">
+ <template #default="{ items }">
+ <div class="_gaps_s">
+ <div v-for="announcement in items" :key="announcement.id" v-panel :class="$style.announcementItem" @click="editAnnouncement(announcement)">
+ <span style="margin-right: 0.5em;">
+ <i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i>
+ <i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i>
+ <i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--MI_THEME-error);"></i>
+ <i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--MI_THEME-success);"></i>
+ </span>
+ <span>{{ announcement.title }}</span>
+ <span v-if="announcement.reads > 0" style="margin-left: auto; opacity: 0.7;">{{ i18n.ts.messageRead }}</span>
</div>
- </template>
- </MkPagination>
- </div>
+ </div>
+ </template>
+ </MkPagination>
+ </div>
- <div v-else-if="tab === 'drive'" class="_gaps">
- <MkFileListForAdmin :paginator="filesPaginator" viewMode="grid"/>
- </div>
+ <div v-else-if="tab === 'drive'" class="_gaps">
+ <MkFileListForAdmin :paginator="filesPaginator" viewMode="grid"/>
+ </div>
- <div v-else-if="tab === 'chart'" class="_gaps_m">
- <div class="cmhjzshm">
- <div class="selects">
- <MkSelect v-model="chartSrc" style="margin: 0 10px 0 0; flex: 1;">
- <option value="per-user-notes">{{ i18n.ts.notes }}</option>
- </MkSelect>
- </div>
- <div class="charts">
- <div class="label">{{ i18n.tsx.recentNHours({ n: 90 }) }}</div>
- <MkChart class="chart" :src="chartSrc" span="hour" :limit="90" :args="{ user, withoutAll: true }" :detailed="true"></MkChart>
- <div class="label">{{ i18n.tsx.recentNDays({ n: 90 }) }}</div>
- <MkChart class="chart" :src="chartSrc" span="day" :limit="90" :args="{ user, withoutAll: true }" :detailed="true"></MkChart>
- </div>
+ <div v-else-if="tab === 'chart'" class="_gaps_m">
+ <div class="cmhjzshm">
+ <div class="selects">
+ <MkSelect v-model="chartSrc" style="margin: 0 10px 0 0; flex: 1;">
+ <option value="per-user-notes">{{ i18n.ts.notes }}</option>
+ </MkSelect>
+ </div>
+ <div class="charts">
+ <div class="label">{{ i18n.tsx.recentNHours({ n: 90 }) }}</div>
+ <MkChart class="chart" :src="chartSrc" span="hour" :limit="90" :args="{ user, withoutAll: true }" :detailed="true"></MkChart>
+ <div class="label">{{ i18n.tsx.recentNDays({ n: 90 }) }}</div>
+ <MkChart class="chart" :src="chartSrc" span="day" :limit="90" :args="{ user, withoutAll: true }" :detailed="true"></MkChart>
</div>
</div>
+ </div>
- <div v-else-if="tab === 'raw'" class="_gaps_m">
- <MkObjectView v-if="info && $i.isAdmin" tall :value="info">
- </MkObjectView>
+ <div v-else-if="tab === 'raw'" class="_gaps_m">
+ <MkObjectView v-if="info && $i.isAdmin" tall :value="info">
+ </MkObjectView>
- <MkObjectView tall :value="user">
- </MkObjectView>
- </div>
- </FormSuspense>
+ <MkObjectView tall :value="user">
+ </MkObjectView>
+ </div>
</div>
</PageWithHeader>
</template>
@@ -224,7 +222,6 @@ import MkButton from '@/components/MkButton.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
import MkSelect from '@/components/MkSelect.vue';
-import FormSuspense from '@/components/form/suspense.vue';
import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
import MkInfo from '@/components/MkInfo.vue';
import * as os from '@/os.js';
@@ -232,11 +229,13 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import { acct } from '@/filters/user.js';
import { definePage } from '@/page.js';
import { i18n } from '@/i18n.js';
-import { iAmAdmin, $i, iAmModerator } from '@/i.js';
+import { ensureSignin, iAmAdmin, iAmModerator } from '@/i.js';
import MkRolePreview from '@/components/MkRolePreview.vue';
import MkPagination from '@/components/MkPagination.vue';
import { Paginator } from '@/utility/paginator.js';
+const $i = ensureSignin();
+
const props = withDefaults(defineProps<{
userId: string;
initialTab?: string;
@@ -244,18 +243,19 @@ const props = withDefaults(defineProps<{
initialTab: 'overview',
});
+const result = await _fetch_();
+
const tab = ref(props.initialTab);
const chartSrc = ref('per-user-notes');
-const user = ref<null | Misskey.entities.UserDetailed>();
-const init = ref<ReturnType<typeof createFetcher>>();
-const info = ref<any>();
-const ips = ref<Misskey.entities.AdminGetUserIpsResponse | null>(null);
+const user = ref(result.user);
+const info = ref(result.info);
+const ips = ref(result.ips);
const ap = ref<any>(null);
-const moderator = ref(false);
-const silenced = ref(false);
-const suspended = ref(false);
-const isSystem = ref(false);
-const moderationNote = ref('');
+const moderator = ref(info.value.isModerator);
+const silenced = ref(info.value.isSilenced);
+const suspended = ref(info.value.isSuspended);
+const isSystem = ref(user.value.host == null && user.value.username.includes('.'));
+const moderationNote = ref(info.value.moderationNote);
const filesPaginator = markRaw(new Paginator('admin/drive/files', {
limit: 10,
computedParams: computed(() => ({
@@ -272,34 +272,37 @@ const announcementsPaginator = markRaw(new Paginator('admin/announcements/list',
status: announcementsStatus.value,
})),
}));
-const expandedRoles = ref([]);
+const expandedRoleIds = ref<(typeof info.value.roles[number]['id'])[]>([]);
-function createFetcher() {
- return () => Promise.all([misskeyApi('users/show', {
+function _fetch_() {
+ return Promise.all([misskeyApi('users/show', {
userId: props.userId,
}), misskeyApi('admin/show-user', {
userId: props.userId,
}), iAmAdmin ? misskeyApi('admin/get-user-ips', {
userId: props.userId,
- }) : Promise.resolve(null)]).then(([_user, _info, _ips]) => {
- user.value = _user;
- info.value = _info;
- ips.value = _ips;
- moderator.value = info.value.isModerator;
- silenced.value = info.value.isSilenced;
- suspended.value = info.value.isSuspended;
- moderationNote.value = info.value.moderationNote;
- isSystem.value = user.value.host == null && user.value.username.includes('.');
-
- watch(moderationNote, async () => {
- await misskeyApi('admin/update-user-note', { userId: user.value.id, text: moderationNote.value });
- await refreshUser();
- });
- });
+ }) : Promise.resolve(null)]).then(([_user, _info, _ips]) => ({
+ user: _user,
+ info: _info,
+ ips: _ips,
+ }));
}
-function refreshUser() {
- init.value = createFetcher();
+watch(moderationNote, async () => {
+ await misskeyApi('admin/update-user-note', { userId: user.value.id, text: moderationNote.value });
+ await refreshUser();
+});
+
+async function refreshUser() {
+ const result = await _fetch_();
+ user.value = result.user;
+ info.value = result.info;
+ ips.value = result.ips;
+ moderator.value = info.value.isModerator;
+ silenced.value = info.value.isSilenced;
+ suspended.value = info.value.isSuspended;
+ isSystem.value = user.value.host == null && user.value.username.includes('.');
+ moderationNote.value = info.value.moderationNote;
}
async function updateRemoteUser() {
@@ -456,7 +459,7 @@ async function assignRole() {
refreshUser();
}
-async function unassignRole(role, ev) {
+async function unassignRole(role: typeof info.value.roles[number], ev: MouseEvent) {
os.popupMenu([{
text: i18n.ts.unassign,
icon: 'ti ti-x',
@@ -468,11 +471,11 @@ async function unassignRole(role, ev) {
}], ev.currentTarget ?? ev.target);
}
-function toggleRoleItem(role) {
- if (expandedRoles.value.includes(role.id)) {
- expandedRoles.value = expandedRoles.value.filter(x => x !== role.id);
+function toggleRoleItem(role: typeof info.value.roles[number]) {
+ if (expandedRoleIds.value.includes(role.id)) {
+ expandedRoleIds.value = expandedRoleIds.value.filter(x => x !== role.id);
} else {
- expandedRoles.value.push(role.id);
+ expandedRoleIds.value.push(role.id);
}
}
@@ -493,12 +496,6 @@ async function editAnnouncement(announcement) {
});
}
-watch(() => props.userId, () => {
- init.value = createFetcher();
-}, {
- immediate: true,
-});
-
watch(user, () => {
misskeyApi('ap/get', {
uri: user.value.uri ?? `${url}/users/${user.value.id}`,
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue
index c5baeda7b0..06a28db088 100644
--- a/packages/frontend/src/pages/admin/ads.vue
+++ b/packages/frontend/src/pages/admin/ads.vue
@@ -140,15 +140,15 @@ function toggleDayOfWeek(ad, index) {
function add() {
ads.value.unshift({
- id: null,
+ id: '',
memo: '',
place: 'square',
priority: 'middle',
ratio: 1,
url: '',
- imageUrl: null,
- expiresAt: null,
- startsAt: null,
+ imageUrl: '',
+ expiresAt: new Date().toISOString(),
+ startsAt: new Date().toISOString(),
dayOfWeek: 0,
});
}
@@ -160,7 +160,7 @@ function remove(ad) {
}).then(({ canceled }) => {
if (canceled) return;
ads.value = ads.value.filter(x => x !== ad);
- if (ad.id == null) return;
+ if (ad.id === '') return;
os.apiWithDialog('admin/ad/delete', {
id: ad.id,
}).then(() => {
@@ -170,7 +170,7 @@ function remove(ad) {
}
function save(ad) {
- if (ad.id == null) {
+ if (ad.id === '') {
misskeyApi('admin/ad/create', {
...ad,
expiresAt: new Date(ad.expiresAt).getTime(),
@@ -207,7 +207,7 @@ function save(ad) {
}
function more() {
- misskeyApi('admin/ad/list', { untilId: ads.value.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => {
+ misskeyApi('admin/ad/list', { untilId: ads.value.reduce((acc, ad) => ad.id !== '' ? ad : acc).id, publishing: publishing }).then(adsResponse => {
if (adsResponse == null) return;
ads.value = ads.value.concat(adsResponse.map(r => {
const exdate = new Date(r.expiresAt);
diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue
index 6c580f87f1..7ed280358a 100644
--- a/packages/frontend/src/pages/admin/bot-protection.vue
+++ b/packages/frontend/src/pages/admin/bot-protection.vue
@@ -4,158 +4,161 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkFolder>
- <template #icon><i class="ti ti-shield"></i></template>
- <template #label>{{ i18n.ts.botProtection }}</template>
- <template v-if="botProtectionForm.savedState.provider === 'hcaptcha'" #suffix>hCaptcha</template>
- <template v-else-if="botProtectionForm.savedState.provider === 'mcaptcha'" #suffix>mCaptcha</template>
- <template v-else-if="botProtectionForm.savedState.provider === 'recaptcha'" #suffix>reCAPTCHA</template>
- <template v-else-if="botProtectionForm.savedState.provider === 'turnstile'" #suffix>Turnstile</template>
- <template v-else-if="botProtectionForm.savedState.provider === 'testcaptcha'" #suffix>testCaptcha</template>
- <template v-else #suffix>{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</template>
- <template #footer>
- <MkFormFooter :canSaving="canSaving" :form="botProtectionForm"/>
- </template>
+<SearchMarker markerId="botProtection" :keywords="['bot', 'protection', 'captcha', 'hcaptcha', 'mcaptcha', 'recaptcha', 'turnstile']">
+ <MkFolder>
+ <template #icon><SearchIcon><i class="ti ti-shield"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts.botProtection }}</SearchLabel></template>
+ <template v-if="botProtectionForm.savedState.provider === 'hcaptcha'" #suffix>hCaptcha</template>
+ <template v-else-if="botProtectionForm.savedState.provider === 'mcaptcha'" #suffix>mCaptcha</template>
+ <template v-else-if="botProtectionForm.savedState.provider === 'recaptcha'" #suffix>reCAPTCHA</template>
+ <template v-else-if="botProtectionForm.savedState.provider === 'turnstile'" #suffix>Turnstile</template>
+ <template v-else-if="botProtectionForm.savedState.provider === 'testcaptcha'" #suffix>testCaptcha</template>
+ <template v-else #suffix>{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</template>
+ <template v-if="botProtectionForm.modified.value" #footer>
+ <MkFormFooter :canSaving="canSaving" :form="botProtectionForm"/>
+ </template>
- <div class="_gaps_m">
- <MkRadios v-model="botProtectionForm.state.provider">
- <option value="none">{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</option>
- <option value="hcaptcha">hCaptcha</option>
- <option value="mcaptcha">mCaptcha</option>
- <option value="recaptcha">reCAPTCHA</option>
- <option value="turnstile">Turnstile</option>
- <option value="testcaptcha">testCaptcha</option>
- </MkRadios>
+ <div class="_gaps_m">
+ <MkRadios v-model="botProtectionForm.state.provider">
+ <option value="none">{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</option>
+ <option value="hcaptcha">hCaptcha</option>
+ <option value="mcaptcha">mCaptcha</option>
+ <option value="recaptcha">reCAPTCHA</option>
+ <option value="turnstile">Turnstile</option>
+ <option value="testcaptcha">testCaptcha</option>
+ </MkRadios>
- <template v-if="botProtectionForm.state.provider === 'hcaptcha'">
- <MkInput v-model="botProtectionForm.state.hcaptchaSiteKey" debounce>
- <template #prefix><i class="ti ti-key"></i></template>
- <template #label>{{ i18n.ts.hcaptchaSiteKey }}</template>
- </MkInput>
- <MkInput v-model="botProtectionForm.state.hcaptchaSecretKey" debounce>
- <template #prefix><i class="ti ti-key"></i></template>
- <template #label>{{ i18n.ts.hcaptchaSecretKey }}</template>
- </MkInput>
- <FormSlot v-if="botProtectionForm.state.hcaptchaSiteKey">
- <template #label>{{ i18n.ts._captcha.verify }}</template>
- <MkCaptcha
- v-model="captchaResult"
- provider="hcaptcha"
- :sitekey="botProtectionForm.state.hcaptchaSiteKey"
- :secretKey="botProtectionForm.state.hcaptchaSecretKey"
- />
- </FormSlot>
- <MkInfo>
- <div :class="$style.captchaInfoMsg">
- <div>{{ i18n.ts._captcha.testSiteKeyMessage }}</div>
- <div>
- <span>ref: </span><a href="https://docs.hcaptcha.com/#integration-testing-test-keys" target="_blank">hCaptcha Developer Guide</a>
+ <template v-if="botProtectionForm.state.provider === 'hcaptcha'">
+ <MkInput v-model="botProtectionForm.state.hcaptchaSiteKey" debounce>
+ <template #prefix><i class="ti ti-key"></i></template>
+ <template #label>{{ i18n.ts.hcaptchaSiteKey }}</template>
+ </MkInput>
+ <MkInput v-model="botProtectionForm.state.hcaptchaSecretKey" debounce>
+ <template #prefix><i class="ti ti-key"></i></template>
+ <template #label>{{ i18n.ts.hcaptchaSecretKey }}</template>
+ </MkInput>
+ <FormSlot v-if="botProtectionForm.state.hcaptchaSiteKey">
+ <template #label>{{ i18n.ts._captcha.verify }}</template>
+ <MkCaptcha
+ v-model="captchaResult"
+ provider="hcaptcha"
+ :sitekey="botProtectionForm.state.hcaptchaSiteKey"
+ :secretKey="botProtectionForm.state.hcaptchaSecretKey"
+ />
+ </FormSlot>
+ <MkInfo>
+ <div :class="$style.captchaInfoMsg">
+ <div>{{ i18n.ts._captcha.testSiteKeyMessage }}</div>
+ <div>
+ <span>ref: </span><a href="https://docs.hcaptcha.com/#integration-testing-test-keys" target="_blank">hCaptcha Developer Guide</a>
+ </div>
</div>
- </div>
- </MkInfo>
- </template>
+ </MkInfo>
+ </template>
- <template v-else-if="botProtectionForm.state.provider === 'mcaptcha'">
- <MkInput v-model="botProtectionForm.state.mcaptchaSiteKey" debounce>
- <template #prefix><i class="ti ti-key"></i></template>
- <template #label>{{ i18n.ts.mcaptchaSiteKey }}</template>
- </MkInput>
- <MkInput v-model="botProtectionForm.state.mcaptchaSecretKey" debounce>
- <template #prefix><i class="ti ti-key"></i></template>
- <template #label>{{ i18n.ts.mcaptchaSecretKey }}</template>
- </MkInput>
- <MkInput v-model="botProtectionForm.state.mcaptchaInstanceUrl" debounce>
- <template #prefix><i class="ti ti-link"></i></template>
- <template #label>{{ i18n.ts.mcaptchaInstanceUrl }}</template>
- </MkInput>
- <FormSlot v-if="botProtectionForm.state.mcaptchaSiteKey && botProtectionForm.state.mcaptchaInstanceUrl">
- <template #label>{{ i18n.ts._captcha.verify }}</template>
- <MkCaptcha
- v-model="captchaResult"
- provider="mcaptcha"
- :sitekey="botProtectionForm.state.mcaptchaSiteKey"
- :secretKey="botProtectionForm.state.mcaptchaSecretKey"
- :instanceUrl="botProtectionForm.state.mcaptchaInstanceUrl"
- />
- </FormSlot>
- </template>
+ <template v-else-if="botProtectionForm.state.provider === 'mcaptcha'">
+ <MkInput v-model="botProtectionForm.state.mcaptchaSiteKey" debounce>
+ <template #prefix><i class="ti ti-key"></i></template>
+ <template #label>{{ i18n.ts.mcaptchaSiteKey }}</template>
+ </MkInput>
+ <MkInput v-model="botProtectionForm.state.mcaptchaSecretKey" debounce>
+ <template #prefix><i class="ti ti-key"></i></template>
+ <template #label>{{ i18n.ts.mcaptchaSecretKey }}</template>
+ </MkInput>
+ <MkInput v-model="botProtectionForm.state.mcaptchaInstanceUrl" debounce>
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label>{{ i18n.ts.mcaptchaInstanceUrl }}</template>
+ </MkInput>
+ <FormSlot v-if="botProtectionForm.state.mcaptchaSiteKey && botProtectionForm.state.mcaptchaInstanceUrl">
+ <template #label>{{ i18n.ts._captcha.verify }}</template>
+ <MkCaptcha
+ v-model="captchaResult"
+ provider="mcaptcha"
+ :sitekey="botProtectionForm.state.mcaptchaSiteKey"
+ :secretKey="botProtectionForm.state.mcaptchaSecretKey"
+ :instanceUrl="botProtectionForm.state.mcaptchaInstanceUrl"
+ />
+ </FormSlot>
+ </template>
- <template v-else-if="botProtectionForm.state.provider === 'recaptcha'">
- <MkInput v-model="botProtectionForm.state.recaptchaSiteKey" debounce>
- <template #prefix><i class="ti ti-key"></i></template>
- <template #label>{{ i18n.ts.recaptchaSiteKey }}</template>
- </MkInput>
- <MkInput v-model="botProtectionForm.state.recaptchaSecretKey" debounce>
- <template #prefix><i class="ti ti-key"></i></template>
- <template #label>{{ i18n.ts.recaptchaSecretKey }}</template>
- </MkInput>
- <FormSlot v-if="botProtectionForm.state.recaptchaSiteKey">
- <template #label>{{ i18n.ts._captcha.verify }}</template>
- <MkCaptcha
- v-model="captchaResult"
- provider="recaptcha"
- :sitekey="botProtectionForm.state.recaptchaSiteKey"
- :secretKey="botProtectionForm.state.recaptchaSecretKey"
- />
- </FormSlot>
- <MkInfo>
- <div :class="$style.captchaInfoMsg">
- <div>{{ i18n.ts._captcha.testSiteKeyMessage }}</div>
- <div>
- <span>ref: </span>
- <a
- href="https://developers.google.com/recaptcha/docs/faq?hl=ja#id-like-to-run-automated-tests-with-recaptcha.-what-should-i-do"
- target="_blank"
- >reCAPTCHA FAQ</a>
+ <template v-else-if="botProtectionForm.state.provider === 'recaptcha'">
+ <MkInput v-model="botProtectionForm.state.recaptchaSiteKey" debounce>
+ <template #prefix><i class="ti ti-key"></i></template>
+ <template #label>{{ i18n.ts.recaptchaSiteKey }}</template>
+ </MkInput>
+ <MkInput v-model="botProtectionForm.state.recaptchaSecretKey" debounce>
+ <template #prefix><i class="ti ti-key"></i></template>
+ <template #label>{{ i18n.ts.recaptchaSecretKey }}</template>
+ </MkInput>
+ <FormSlot v-if="botProtectionForm.state.recaptchaSiteKey">
+ <template #label>{{ i18n.ts._captcha.verify }}</template>
+ <MkCaptcha
+ v-model="captchaResult"
+ provider="recaptcha"
+ :sitekey="botProtectionForm.state.recaptchaSiteKey"
+ :secretKey="botProtectionForm.state.recaptchaSecretKey"
+ />
+ </FormSlot>
+ <MkInfo>
+ <div :class="$style.captchaInfoMsg">
+ <div>{{ i18n.ts._captcha.testSiteKeyMessage }}</div>
+ <div>
+ <span>ref: </span>
+ <a
+ href="https://developers.google.com/recaptcha/docs/faq?hl=ja#id-like-to-run-automated-tests-with-recaptcha.-what-should-i-do"
+ target="_blank"
+ >reCAPTCHA FAQ</a>
+ </div>
</div>
- </div>
- </MkInfo>
- </template>
+ </MkInfo>
+ </template>
- <template v-else-if="botProtectionForm.state.provider === 'turnstile'">
- <MkInput v-model="botProtectionForm.state.turnstileSiteKey" debounce>
- <template #prefix><i class="ti ti-key"></i></template>
- <template #label>{{ i18n.ts.turnstileSiteKey }}</template>
- </MkInput>
- <MkInput v-model="botProtectionForm.state.turnstileSecretKey" debounce>
- <template #prefix><i class="ti ti-key"></i></template>
- <template #label>{{ i18n.ts.turnstileSecretKey }}</template>
- </MkInput>
- <FormSlot v-if="botProtectionForm.state.turnstileSiteKey">
- <template #label>{{ i18n.ts._captcha.verify }}</template>
- <MkCaptcha
- v-model="captchaResult"
- provider="turnstile"
- :sitekey="botProtectionForm.state.turnstileSiteKey"
- :secretKey="botProtectionForm.state.turnstileSecretKey"
- />
- </FormSlot>
- <MkInfo>
- <div :class="$style.captchaInfoMsg">
- <div>
- {{ i18n.ts._captcha.testSiteKeyMessage }}
- </div>
- <div>
- <span>ref: </span><a href="https://developers.cloudflare.com/turnstile/troubleshooting/testing/" target="_blank">Cloudflare Docs</a>
+ <template v-else-if="botProtectionForm.state.provider === 'turnstile'">
+ <MkInput v-model="botProtectionForm.state.turnstileSiteKey" debounce>
+ <template #prefix><i class="ti ti-key"></i></template>
+ <template #label>{{ i18n.ts.turnstileSiteKey }}</template>
+ </MkInput>
+ <MkInput v-model="botProtectionForm.state.turnstileSecretKey" debounce>
+ <template #prefix><i class="ti ti-key"></i></template>
+ <template #label>{{ i18n.ts.turnstileSecretKey }}</template>
+ </MkInput>
+ <FormSlot v-if="botProtectionForm.state.turnstileSiteKey">
+ <template #label>{{ i18n.ts._captcha.verify }}</template>
+ <MkCaptcha
+ v-model="captchaResult"
+ provider="turnstile"
+ :sitekey="botProtectionForm.state.turnstileSiteKey"
+ :secretKey="botProtectionForm.state.turnstileSecretKey"
+ />
+ </FormSlot>
+ <MkInfo>
+ <div :class="$style.captchaInfoMsg">
+ <div>
+ {{ i18n.ts._captcha.testSiteKeyMessage }}
+ </div>
+ <div>
+ <span>ref: </span><a href="https://developers.cloudflare.com/turnstile/troubleshooting/testing/" target="_blank">Cloudflare Docs</a>
+ </div>
</div>
- </div>
- </MkInfo>
- </template>
+ </MkInfo>
+ </template>
- <template v-else-if="botProtectionForm.state.provider === 'testcaptcha'">
- <MkInfo warn><span v-html="i18n.ts.testCaptchaWarning"></span></MkInfo>
- <FormSlot>
- <template #label>{{ i18n.ts._captcha.verify }}</template>
- <MkCaptcha v-model="captchaResult" provider="testcaptcha" :sitekey="null"/>
- </FormSlot>
- </template>
- </div>
-</MkFolder>
+ <template v-else-if="botProtectionForm.state.provider === 'testcaptcha'">
+ <MkInfo warn><span v-html="i18n.ts.testCaptchaWarning"></span></MkInfo>
+ <FormSlot>
+ <template #label>{{ i18n.ts._captcha.verify }}</template>
+ <MkCaptcha v-model="captchaResult" provider="testcaptcha" :sitekey="null"/>
+ </FormSlot>
+ </template>
+ </div>
+ </MkFolder>
+</SearchMarker>
</template>
<script lang="ts" setup>
import { computed, defineAsyncComponent, ref, watch } from 'vue';
import * as Misskey from 'misskey-js';
+import type { ApiWithDialogCustomErrors } from '@/os.js';
import MkRadios from '@/components/MkRadios.vue';
import MkInput from '@/components/MkInput.vue';
import FormSlot from '@/components/form/slot.vue';
@@ -167,7 +170,6 @@ import { useForm } from '@/composables/use-form.js';
import MkFormFooter from '@/components/MkFormFooter.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkInfo from '@/components/MkInfo.vue';
-import type { ApiWithDialogCustomErrors } from '@/os.js';
const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));
diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue
index 19258216f6..f78a4f27bd 100644
--- a/packages/frontend/src/pages/admin/branding.vue
+++ b/packages/frontend/src/pages/admin/branding.vue
@@ -6,89 +6,137 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :tabs="headerTabs">
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
- <FormSuspense :p="init">
+ <SearchMarker path="/admin/branding" :label="i18n.ts.branding" :keywords="['branding']" icon="ti ti-paint">
<div class="_gaps_m">
- <MkInput v-model="iconUrl" type="url">
- <template #prefix><i class="ti ti-link"></i></template>
- <template #label>{{ i18n.ts._serverSettings.iconUrl }}</template>
- </MkInput>
+ <SearchMarker :keywords="['entrance', 'welcome', 'landing', 'front', 'home', 'page', 'style']">
+ <MkRadios v-model="entrancePageStyle">
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.entrancePageStyle }}</SearchLabel></template>
+ <option value="classic">Classic</option>
+ <option value="simple">Simple</option>
+ </MkRadios>
+ </SearchMarker>
- <MkInput v-model="app192IconUrl" type="url">
- <template #prefix><i class="ti ti-link"></i></template>
- <template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/192px)</template>
- <template #caption>
- <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div>
- <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div>
- <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div>
- <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '192x192px' }) }}</strong></div>
- </template>
- </MkInput>
+ <SearchMarker :keywords="['timeline']">
+ <MkSwitch v-model="showTimelineForVisitor">
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.showTimelineForVisitor }}</SearchLabel></template>
+ </MkSwitch>
+ </SearchMarker>
- <MkInput v-model="app512IconUrl" type="url">
- <template #prefix><i class="ti ti-link"></i></template>
- <template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/512px)</template>
- <template #caption>
- <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div>
- <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div>
- <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div>
- <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '512x512px' }) }}</strong></div>
- </template>
- </MkInput>
+ <SearchMarker :keywords="['activity', 'activities']">
+ <MkSwitch v-model="showActivitiesForVisitor">
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.showActivitiesForVisitor }}</SearchLabel></template>
+ </MkSwitch>
+ </SearchMarker>
- <MkInput v-model="bannerUrl" type="url">
- <template #prefix><i class="ti ti-link"></i></template>
- <template #label>{{ i18n.ts.bannerUrl }}</template>
- </MkInput>
+ <SearchMarker :keywords="['icon', 'image']">
+ <MkInput v-model="iconUrl" type="url">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.iconUrl }}</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="backgroundImageUrl" type="url">
- <template #prefix><i class="ti ti-link"></i></template>
- <template #label>{{ i18n.ts.backgroundImageUrl }}</template>
- </MkInput>
+ <SearchMarker :keywords="['icon', 'image']">
+ <MkInput v-model="app192IconUrl" type="url">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.iconUrl }} (App/192px)</SearchLabel></template>
+ <template #caption>
+ <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div>
+ <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div>
+ <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div>
+ <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '192x192px' }) }}</strong></div>
+ </template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="notFoundImageUrl" type="url">
- <template #prefix><i class="ti ti-link"></i></template>
- <template #label>{{ i18n.ts.notFoundDescription }}</template>
- </MkInput>
+ <SearchMarker :keywords="['icon', 'image']">
+ <MkInput v-model="app512IconUrl" type="url">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.iconUrl }} (App/512px)</SearchLabel></template>
+ <template #caption>
+ <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div>
+ <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div>
+ <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div>
+ <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '512x512px' }) }}</strong></div>
+ </template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="infoImageUrl" type="url">
- <template #prefix><i class="ti ti-link"></i></template>
- <template #label>{{ i18n.ts.nothing }}</template>
- </MkInput>
+ <SearchMarker :keywords="['banner', 'image']">
+ <MkInput v-model="bannerUrl" type="url">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label><SearchLabel>{{ i18n.ts.bannerUrl }}</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="serverErrorImageUrl" type="url">
- <template #prefix><i class="ti ti-link"></i></template>
- <template #label>{{ i18n.ts.somethingHappened }}</template>
- </MkInput>
+ <SearchMarker :keywords="['background', 'image']">
+ <MkInput v-model="backgroundImageUrl" type="url">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label><SearchLabel>{{ i18n.ts.backgroundImageUrl }}</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
- <MkColorInput v-model="themeColor">
- <template #label>{{ i18n.ts.themeColor }}</template>
- </MkColorInput>
+ <SearchMarker :keywords="['image']">
+ <MkInput v-model="notFoundImageUrl" type="url">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label><SearchLabel>{{ i18n.ts.notFoundDescription }}</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
- <MkTextarea v-model="defaultLightTheme">
- <template #label>{{ i18n.ts.instanceDefaultLightTheme }}</template>
- <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
- </MkTextarea>
+ <SearchMarker :keywords="['image']">
+ <MkInput v-model="infoImageUrl" type="url">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label><SearchLabel>{{ i18n.ts.nothing }}</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
- <MkTextarea v-model="defaultDarkTheme">
- <template #label>{{ i18n.ts.instanceDefaultDarkTheme }}</template>
- <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
- </MkTextarea>
+ <SearchMarker :keywords="['image']">
+ <MkInput v-model="serverErrorImageUrl" type="url">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label><SearchLabel>{{ i18n.ts.somethingHappened }}</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="repositoryUrl" type="url">
- <template #prefix><i class="ti ti-link"></i></template>
- <template #label>{{ i18n.ts.repositoryUrl }}</template>
- </MkInput>
+ <SearchMarker :keywords="['theme', 'color']">
+ <MkColorInput v-model="themeColor">
+ <template #label><SearchLabel>{{ i18n.ts.themeColor }}</SearchLabel></template>
+ </MkColorInput>
+ </SearchMarker>
- <MkInput v-model="feedbackUrl" type="url">
- <template #prefix><i class="ti ti-link"></i></template>
- <template #label>{{ i18n.ts.feedbackUrl }}</template>
- </MkInput>
+ <SearchMarker :keywords="['theme', 'default', 'light']">
+ <MkTextarea v-model="defaultLightTheme">
+ <template #label><SearchLabel>{{ i18n.ts.instanceDefaultLightTheme }}</SearchLabel></template>
+ <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
+ </MkTextarea>
+ </SearchMarker>
- <MkTextarea v-model="manifestJsonOverride">
- <template #label>{{ i18n.ts._serverSettings.manifestJsonOverride }}</template>
- </MkTextarea>
+ <SearchMarker :keywords="['theme', 'default', 'dark']">
+ <MkTextarea v-model="defaultDarkTheme">
+ <template #label><SearchLabel>{{ i18n.ts.instanceDefaultDarkTheme }}</SearchLabel></template>
+ <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
+ </MkTextarea>
+ </SearchMarker>
+
+ <SearchMarker>
+ <MkInput v-model="repositoryUrl" type="url">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label><SearchLabel>{{ i18n.ts.repositoryUrl }}</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
+
+ <SearchMarker>
+ <MkInput v-model="feedbackUrl" type="url">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label><SearchLabel>{{ i18n.ts.feedbackUrl }}</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
+
+ <SearchMarker>
+ <MkTextarea v-model="manifestJsonOverride">
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.manifestJsonOverride }}</SearchLabel></template>
+ </MkTextarea>
+ </SearchMarker>
</div>
- </FormSuspense>
+ </SearchMarker>
</div>
<template #footer>
<div :class="$style.footer">
@@ -106,7 +154,6 @@ import JSON5 from 'json5';
import { host } from '@@/js/config.js';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
-import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { instance, fetchInstance } from '@/instance.js';
@@ -114,42 +161,36 @@ import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import MkButton from '@/components/MkButton.vue';
import MkColorInput from '@/components/MkColorInput.vue';
+import MkRadios from '@/components/MkRadios.vue';
+import MkSwitch from '@/components/MkSwitch.vue';
-const iconUrl = ref<string | null>(null);
-const app192IconUrl = ref<string | null>(null);
-const app512IconUrl = ref<string | null>(null);
-const bannerUrl = ref<string | null>(null);
-const backgroundImageUrl = ref<string | null>(null);
-const themeColor = ref<string | null>(null);
-const defaultLightTheme = ref<string | null>(null);
-const defaultDarkTheme = ref<string | null>(null);
-const serverErrorImageUrl = ref<string | null>(null);
-const infoImageUrl = ref<string | null>(null);
-const notFoundImageUrl = ref<string | null>(null);
-const repositoryUrl = ref<string | null>(null);
-const feedbackUrl = ref<string | null>(null);
-const manifestJsonOverride = ref<string>('{}');
+const meta = await misskeyApi('admin/meta');
-async function init() {
- const meta = await misskeyApi('admin/meta');
- iconUrl.value = meta.iconUrl;
- app192IconUrl.value = meta.app192IconUrl;
- app512IconUrl.value = meta.app512IconUrl;
- bannerUrl.value = meta.bannerUrl;
- backgroundImageUrl.value = meta.backgroundImageUrl;
- themeColor.value = meta.themeColor;
- defaultLightTheme.value = meta.defaultLightTheme;
- defaultDarkTheme.value = meta.defaultDarkTheme;
- serverErrorImageUrl.value = meta.serverErrorImageUrl;
- infoImageUrl.value = meta.infoImageUrl;
- notFoundImageUrl.value = meta.notFoundImageUrl;
- repositoryUrl.value = meta.repositoryUrl;
- feedbackUrl.value = meta.feedbackUrl;
- manifestJsonOverride.value = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t');
-}
+const entrancePageStyle = ref(meta.clientOptions.entrancePageStyle ?? 'classic');
+const showTimelineForVisitor = ref(meta.clientOptions.showTimelineForVisitor ?? true);
+const showActivitiesForVisitor = ref(meta.clientOptions.showActivitiesForVisitor ?? true);
+const iconUrl = ref(meta.iconUrl);
+const app192IconUrl = ref(meta.app192IconUrl);
+const app512IconUrl = ref(meta.app512IconUrl);
+const bannerUrl = ref(meta.bannerUrl);
+const backgroundImageUrl = ref(meta.backgroundImageUrl);
+const themeColor = ref(meta.themeColor);
+const defaultLightTheme = ref(meta.defaultLightTheme);
+const defaultDarkTheme = ref(meta.defaultDarkTheme);
+const serverErrorImageUrl = ref(meta.serverErrorImageUrl);
+const infoImageUrl = ref(meta.infoImageUrl);
+const notFoundImageUrl = ref(meta.notFoundImageUrl);
+const repositoryUrl = ref(meta.repositoryUrl);
+const feedbackUrl = ref(meta.feedbackUrl);
+const manifestJsonOverride = ref(meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t'));
function save() {
os.apiWithDialog('admin/update-meta', {
+ clientOptions: {
+ entrancePageStyle: entrancePageStyle.value,
+ showTimelineForVisitor: showTimelineForVisitor.value,
+ showActivitiesForVisitor: showActivitiesForVisitor.value,
+ },
iconUrl: iconUrl.value,
app192IconUrl: app192IconUrl.value,
app512IconUrl: app512IconUrl.value,
diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue
index 17f2f8b593..8eb403f94c 100644
--- a/packages/frontend/src/pages/admin/email-settings.vue
+++ b/packages/frontend/src/pages/admin/email-settings.vue
@@ -6,48 +6,67 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :tabs="headerTabs">
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
- <FormSuspense :p="init">
+ <SearchMarker path="/admin/email-settings" :label="i18n.ts.emailServer" :keywords="['email']" icon="ti ti-mail">
<div class="_gaps_m">
- <MkSwitch v-model="enableEmail">
- <template #label>{{ i18n.ts.enableEmail }} ({{ i18n.ts.recommended }})</template>
- <template #caption>{{ i18n.ts.emailConfigInfo }}</template>
- </MkSwitch>
+ <SearchMarker>
+ <MkSwitch v-model="enableEmail">
+ <template #label><SearchLabel>{{ i18n.ts.enableEmail }}</SearchLabel> ({{ i18n.ts.recommended }})</template>
+ <template #caption><SearchText>{{ i18n.ts.emailConfigInfo }}</SearchText></template>
+ </MkSwitch>
+ </SearchMarker>
<template v-if="enableEmail">
- <MkInput v-model="email" type="email">
- <template #label>{{ i18n.ts.emailAddress }}</template>
- </MkInput>
+ <SearchMarker>
+ <MkInput v-model="email" type="email">
+ <template #label><SearchLabel>{{ i18n.ts.emailAddress }}</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
- <FormSection>
- <template #label>{{ i18n.ts.smtpConfig }}</template>
+ <SearchMarker>
+ <FormSection>
+ <template #label><SearchLabel>{{ i18n.ts.smtpConfig }}</SearchLabel></template>
- <div class="_gaps_m">
- <FormSplit :minWidth="280">
- <MkInput v-model="smtpHost">
- <template #label>{{ i18n.ts.smtpHost }}</template>
- </MkInput>
- <MkInput v-model="smtpPort" type="number">
- <template #label>{{ i18n.ts.smtpPort }}</template>
- </MkInput>
- </FormSplit>
- <FormSplit :minWidth="280">
- <MkInput v-model="smtpUser">
- <template #label>{{ i18n.ts.smtpUser }}</template>
- </MkInput>
- <MkInput v-model="smtpPass" type="password">
- <template #label>{{ i18n.ts.smtpPass }}</template>
- </MkInput>
- </FormSplit>
- <FormInfo>{{ i18n.ts.emptyToDisableSmtpAuth }}</FormInfo>
- <MkSwitch v-model="smtpSecure">
- <template #label>{{ i18n.ts.smtpSecure }}</template>
- <template #caption>{{ i18n.ts.smtpSecureInfo }}</template>
- </MkSwitch>
- </div>
- </FormSection>
+ <div class="_gaps_m">
+ <FormSplit :minWidth="280">
+ <SearchMarker>
+ <MkInput v-model="smtpHost">
+ <template #label><SearchLabel>{{ i18n.ts.smtpHost }}</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
+ <SearchMarker>
+ <MkInput v-model="smtpPort" type="number">
+ <template #label><SearchLabel>{{ i18n.ts.smtpPort }}</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
+ </FormSplit>
+
+ <FormSplit :minWidth="280">
+ <SearchMarker>
+ <MkInput v-model="smtpUser">
+ <template #label><SearchLabel>{{ i18n.ts.smtpUser }}</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
+ <SearchMarker>
+ <MkInput v-model="smtpPass" type="password">
+ <template #label><SearchLabel>{{ i18n.ts.smtpPass }}</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
+ </FormSplit>
+
+ <FormInfo>{{ i18n.ts.emptyToDisableSmtpAuth }}</FormInfo>
+
+ <SearchMarker>
+ <MkSwitch v-model="smtpSecure">
+ <template #label><SearchLabel>{{ i18n.ts.smtpSecure }}</SearchLabel></template>
+ <template #caption><SearchText>{{ i18n.ts.smtpSecureInfo }}</SearchText></template>
+ </MkSwitch>
+ </SearchMarker>
+ </div>
+ </FormSection>
+ </SearchMarker>
</template>
</div>
- </FormSuspense>
+ </SearchMarker>
</div>
<template #footer>
<div :class="$style.footer">
@@ -67,7 +86,6 @@ import { ref, computed } from 'vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkInput from '@/components/MkInput.vue';
import FormInfo from '@/components/MkInfo.vue';
-import FormSuspense from '@/components/form/suspense.vue';
import FormSplit from '@/components/form/split.vue';
import FormSection from '@/components/form/section.vue';
import * as os from '@/os.js';
@@ -77,24 +95,15 @@ import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import MkButton from '@/components/MkButton.vue';
-const enableEmail = ref<boolean>(false);
-const email = ref<string | null>(null);
-const smtpSecure = ref<boolean>(false);
-const smtpHost = ref<string>('');
-const smtpPort = ref<number>(0);
-const smtpUser = ref<string>('');
-const smtpPass = ref<string>('');
+const meta = await misskeyApi('admin/meta');
-async function init() {
- const meta = await misskeyApi('admin/meta');
- enableEmail.value = meta.enableEmail;
- email.value = meta.email;
- smtpSecure.value = meta.smtpSecure;
- smtpHost.value = meta.smtpHost;
- smtpPort.value = meta.smtpPort;
- smtpUser.value = meta.smtpUser;
- smtpPass.value = meta.smtpPass;
-}
+const enableEmail = ref(meta.enableEmail);
+const email = ref(meta.email);
+const smtpSecure = ref(meta.smtpSecure);
+const smtpHost = ref(meta.smtpHost);
+const smtpPort = ref(meta.smtpPort);
+const smtpUser = ref(meta.smtpUser);
+const smtpPass = ref(meta.smtpPass);
async function testEmail() {
const { canceled, result: destination } = await os.inputText({
diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue
index 845fb12c5d..3a4eb8c3c7 100644
--- a/packages/frontend/src/pages/admin/external-services.vue
+++ b/packages/frontend/src/pages/admin/external-services.vue
@@ -6,36 +6,49 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
- <FormSuspense :p="init">
+ <SearchMarker path="/admin/external-services" :label="i18n.ts.externalServices" :keywords="['external', 'services', 'thirdparty']" icon="ti ti-link">
<div class="_gaps_m">
- <MkFolder>
- <template #label>Google Analytics<span class="_beta">{{ i18n.ts.beta }}</span></template>
+ <SearchMarker v-slot="slotProps">
+ <MkFolder :defaultOpen="slotProps.isParentOfTarget">
+ <template #label><SearchLabel>Google Analytics</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template>
- <div class="_gaps_m">
- <MkInput v-model="googleAnalyticsMeasurementId">
- <template #prefix><i class="ti ti-key"></i></template>
- <template #label>Measurement ID</template>
- </MkInput>
- <MkButton primary @click="save_googleAnalytics">Save</MkButton>
- </div>
- </MkFolder>
+ <div class="_gaps_m">
+ <SearchMarker>
+ <MkInput v-model="googleAnalyticsMeasurementId">
+ <template #prefix><i class="ti ti-key"></i></template>
+ <template #label><SearchLabel>Measurement ID</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
- <MkFolder>
- <template #label>DeepL Translation</template>
+ <MkButton primary @click="save_googleAnalytics">Save</MkButton>
+ </div>
+ </MkFolder>
+ </SearchMarker>
- <div class="_gaps_m">
- <MkInput v-model="deeplAuthKey">
- <template #prefix><i class="ti ti-key"></i></template>
- <template #label>DeepL Auth Key</template>
- </MkInput>
- <MkSwitch v-model="deeplIsPro">
- <template #label>Pro account</template>
- </MkSwitch>
- <MkButton primary @click="save_deepl">Save</MkButton>
- </div>
- </MkFolder>
+ <SearchMarker v-slot="slotProps">
+ <MkFolder :defaultOpen="slotProps.isParentOfTarget">
+ <template #label><SearchLabel>DeepL Translation</SearchLabel></template>
+
+ <div class="_gaps_m">
+ <SearchMarker>
+ <MkInput v-model="deeplAuthKey">
+ <template #prefix><i class="ti ti-key"></i></template>
+ <template #label><SearchLabel>Auth Key</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
+
+ <SearchMarker>
+ <MkSwitch v-model="deeplIsPro">
+ <template #label><SearchLabel>Pro account</SearchLabel></template>
+ </MkSwitch>
+ </SearchMarker>
+
+ <MkButton primary @click="save_deepl">Save</MkButton>
+ </div>
+ </MkFolder>
+ </SearchMarker>
</div>
- </FormSuspense>
+ </SearchMarker>
</div>
</PageWithHeader>
</template>
@@ -45,7 +58,6 @@ import { ref, computed } from 'vue';
import MkInput from '@/components/MkInput.vue';
import MkButton from '@/components/MkButton.vue';
import MkSwitch from '@/components/MkSwitch.vue';
-import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { fetchInstance } from '@/instance.js';
@@ -53,17 +65,11 @@ import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import MkFolder from '@/components/MkFolder.vue';
-const deeplAuthKey = ref<string>('');
-const deeplIsPro = ref<boolean>(false);
+const meta = await misskeyApi('admin/meta');
-const googleAnalyticsMeasurementId = ref<string>('');
-
-async function init() {
- const meta = await misskeyApi('admin/meta');
- deeplAuthKey.value = meta.deeplAuthKey ?? '';
- deeplIsPro.value = meta.deeplIsPro;
- googleAnalyticsMeasurementId.value = meta.googleAnalyticsMeasurementId ?? '';
-}
+const deeplAuthKey = ref(meta.deeplAuthKey ?? '');
+const deeplIsPro = ref(meta.deeplIsPro);
+const googleAnalyticsMeasurementId = ref(meta.googleAnalyticsMeasurementId ?? '');
function save_deepl() {
os.apiWithDialog('admin/update-meta', {
diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index a87028f008..94994dc94c 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInfo v-if="noEmailServer" warn>{{ i18n.ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
</div>
- <MkSuperMenu :def="menuDef" :grid="narrow"></MkSuperMenu>
+ <MkSuperMenu :def="menuDef" :searchIndex="searchIndex" :grid="narrow"></MkSuperMenu>
</div>
</div>
</div>
@@ -44,6 +44,9 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import { lookupUser, lookupUserByEmail, lookupFile } from '@/utility/admin-lookup.js';
import { definePage, provideMetadataReceiver, provideReactiveMetadata } from '@/page.js';
import { useRouter } from '@/router.js';
+import { genSearchIndexes } from '@/utility/inapp-search.js';
+
+const searchIndex = await import('search-index:admin').then(({ searchIndexes }) => genSearchIndexes(searchIndexes));
const isEmpty = (x: string | null) => x == null || x === '';
@@ -324,12 +327,6 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePage(() => INFO.value);
-
-defineExpose({
- header: {
- title: i18n.ts.controlPanel,
- },
-});
</script>
<style lang="scss" scoped>
diff --git a/packages/frontend/src/pages/admin/job-queue.job.vue b/packages/frontend/src/pages/admin/job-queue.job.vue
index 659aa02b50..f96830e57a 100644
--- a/packages/frontend/src/pages/admin/job-queue.job.vue
+++ b/packages/frontend/src/pages/admin/job-queue.job.vue
@@ -98,7 +98,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkKeyValue>
<MkKeyValue v-if="job.progress != null && typeof job.progress === 'number' && job.progress > 0">
<template #key>Progress</template>
- <template #value>{{ Math.floor(job.progress * 100) }}%</template>
+ <template #value>{{ Math.floor(job.progress) }}%</template>
</MkKeyValue>
</div>
<MkFolder :withSpacer="false">
@@ -150,11 +150,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton><i class="ti ti-device-floppy"></i> Update</MkButton>
</div>
<div v-else-if="tab === 'result'">
- <MkCode :code="String(job.returnValue)"/>
+ <MkCode :code="JSON5.stringify(job.returnValue, null, '\t')" lang="json5"/>
</div>
<div v-else-if="tab === 'error'" class="_gaps_s">
<MkCode v-for="log in job.stacktrace" :code="log" lang="stacktrace"/>
</div>
+ <div v-else-if="tab === 'logs'">
+ <MkButton primary rounded @click="loadLogs()"><i class="ti ti-refresh"></i> Load logs</MkButton>
+ <div v-for="log in logs">{{ log }}</div>
+ </div>
</MkFolder>
</template>
@@ -198,6 +202,7 @@ const emit = defineEmits<{
const tab = ref('info');
const editData = ref(JSON5.stringify(props.job.data, null, '\t'));
const canEdit = true;
+const logs = ref<string[]>([]);
type TlType = TlEvent<{
type: 'created' | 'processed' | 'finished';
@@ -268,6 +273,10 @@ async function removeJob() {
os.apiWithDialog('admin/queue/remove-job', { queue: props.queueType, jobId: props.job.id });
}
+async function loadLogs() {
+ logs.value = await os.apiWithDialog('admin/queue/show-job-logs', { queue: props.queueType, jobId: props.job.id });
+}
+
// TODO
// function moveJob() {
//
diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue
index 819f229c10..435dd9c462 100644
--- a/packages/frontend/src/pages/admin/moderation.vue
+++ b/packages/frontend/src/pages/admin/moderation.vue
@@ -6,140 +6,171 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :tabs="headerTabs">
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
- <FormSuspense :p="init">
+ <SearchMarker path="/admin/moderation" :label="i18n.ts.moderation" :keywords="['moderation']" icon="ti ti-shield" :inlining="['serverRules']">
<div class="_gaps_m">
- <MkSwitch :modelValue="enableRegistration" @update:modelValue="onChange_enableRegistration">
- <template #label>{{ i18n.ts._serverSettings.openRegistration }}</template>
- <template #caption>
- <div>{{ i18n.ts._serverSettings.thisSettingWillAutomaticallyOffWhenModeratorsInactive }}</div>
- <div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._serverSettings.openRegistrationWarning }}</div>
- </template>
- </MkSwitch>
+ <SearchMarker :keywords="['open', 'registration']">
+ <MkSwitch :modelValue="enableRegistration" @update:modelValue="onChange_enableRegistration">
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.openRegistration }}</SearchLabel></template>
+ <template #caption>
+ <div><SearchText>{{ i18n.ts._serverSettings.thisSettingWillAutomaticallyOffWhenModeratorsInactive }}</SearchText></div>
+ <div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> <SearchText>{{ i18n.ts._serverSettings.openRegistrationWarning }}</SearchText></div>
+ </template>
+ </MkSwitch>
+ </SearchMarker>
- <MkSwitch v-model="emailRequiredForSignup" @change="onChange_emailRequiredForSignup">
- <template #label>{{ i18n.ts.emailRequiredForSignup }} ({{ i18n.ts.recommended }})</template>
- </MkSwitch>
+ <SearchMarker :keywords="['email', 'required', 'signup']">
+ <MkSwitch v-model="emailRequiredForSignup" @change="onChange_emailRequiredForSignup">
+ <template #label><SearchLabel>{{ i18n.ts.emailRequiredForSignup }}</SearchLabel> ({{ i18n.ts.recommended }})</template>
+ </MkSwitch>
+ </SearchMarker>
- <MkSelect v-model="ugcVisibilityForVisitor" @update:modelValue="onChange_ugcVisibilityForVisitor">
- <template #label>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor }}</template>
- <option value="all">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.all }}</option>
- <option value="local">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.localOnly }} ({{ i18n.ts.recommended }})</option>
- <option value="none">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.none }}</option>
- <template #caption>
- <div>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description }}</div>
- <div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description2 }}</div>
- </template>
- </MkSelect>
+ <SearchMarker :keywords="['ugc', 'content', 'visibility', 'visitor', 'guest']">
+ <MkSelect
+ v-model="ugcVisibilityForVisitor" :items="[{
+ value: 'all',
+ label: i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.all,
+ }, {
+ value: 'local',
+ label: i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.localOnly + ' (' + i18n.ts.recommended + ')',
+ }, {
+ value: 'none',
+ label: i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.none,
+ }] as const" @update:modelValue="onChange_ugcVisibilityForVisitor"
+ >
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor }}</SearchLabel></template>
+ <template #caption>
+ <div><SearchText>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description }}</SearchText></div>
+ <div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> <SearchText>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description2 }}</SearchText></div>
+ </template>
+ </MkSelect>
+ </SearchMarker>
- <FormLink to="/admin/server-rules">{{ i18n.ts.serverRules }}</FormLink>
+ <XServerRules/>
- <MkFolder>
- <template #icon><i class="ti ti-lock-star"></i></template>
- <template #label>{{ i18n.ts.preservedUsernames }}</template>
+ <SearchMarker :keywords="['preserved', 'usernames']">
+ <MkFolder>
+ <template #icon><SearchIcon><i class="ti ti-lock-star"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts.preservedUsernames }}</SearchLabel></template>
- <div class="_gaps">
- <MkTextarea v-model="preservedUsernames">
- <template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template>
- </MkTextarea>
- <MkButton primary @click="save_preservedUsernames">{{ i18n.ts.save }}</MkButton>
- </div>
- </MkFolder>
+ <div class="_gaps">
+ <MkTextarea v-model="preservedUsernames">
+ <template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template>
+ </MkTextarea>
+ <MkButton primary @click="save_preservedUsernames">{{ i18n.ts.save }}</MkButton>
+ </div>
+ </MkFolder>
+ </SearchMarker>
- <MkFolder>
- <template #icon><i class="ti ti-message-exclamation"></i></template>
- <template #label>{{ i18n.ts.sensitiveWords }}</template>
+ <SearchMarker :keywords="['sensitive', 'words']">
+ <MkFolder>
+ <template #icon><SearchIcon><i class="ti ti-message-exclamation"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts.sensitiveWords }}</SearchLabel></template>
- <div class="_gaps">
- <MkTextarea v-model="sensitiveWords">
- <template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template>
- </MkTextarea>
- <MkButton primary @click="save_sensitiveWords">{{ i18n.ts.save }}</MkButton>
- </div>
- </MkFolder>
+ <div class="_gaps">
+ <MkTextarea v-model="sensitiveWords">
+ <template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template>
+ </MkTextarea>
+ <MkButton primary @click="save_sensitiveWords">{{ i18n.ts.save }}</MkButton>
+ </div>
+ </MkFolder>
+ </SearchMarker>
- <MkFolder>
- <template #icon><i class="ti ti-message-x"></i></template>
- <template #label>{{ i18n.ts.prohibitedWords }}</template>
+ <SearchMarker :keywords="['prohibited', 'words']">
+ <MkFolder>
+ <template #icon><SearchIcon><i class="ti ti-message-x"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts.prohibitedWords }}</SearchLabel></template>
- <div class="_gaps">
- <MkTextarea v-model="prohibitedWords">
- <template #caption>{{ i18n.ts.prohibitedWordsDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template>
- </MkTextarea>
- <MkButton primary @click="save_prohibitedWords">{{ i18n.ts.save }}</MkButton>
- </div>
- </MkFolder>
+ <div class="_gaps">
+ <MkTextarea v-model="prohibitedWords">
+ <template #caption>{{ i18n.ts.prohibitedWordsDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template>
+ </MkTextarea>
+ <MkButton primary @click="save_prohibitedWords">{{ i18n.ts.save }}</MkButton>
+ </div>
+ </MkFolder>
+ </SearchMarker>
- <MkFolder>
- <template #icon><i class="ti ti-user-x"></i></template>
- <template #label>{{ i18n.ts.prohibitedWordsForNameOfUser }}</template>
+ <SearchMarker :keywords="['prohibited', 'name', 'user']">
+ <MkFolder>
+ <template #icon><SearchIcon><i class="ti ti-user-x"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts.prohibitedWordsForNameOfUser }}</SearchLabel></template>
- <div class="_gaps">
- <MkTextarea v-model="prohibitedWordsForNameOfUser">
- <template #caption>{{ i18n.ts.prohibitedWordsForNameOfUserDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template>
- </MkTextarea>
- <MkButton primary @click="save_prohibitedWordsForNameOfUser">{{ i18n.ts.save }}</MkButton>
- </div>
- </MkFolder>
+ <div class="_gaps">
+ <MkTextarea v-model="prohibitedWordsForNameOfUser">
+ <template #caption>{{ i18n.ts.prohibitedWordsForNameOfUserDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template>
+ </MkTextarea>
+ <MkButton primary @click="save_prohibitedWordsForNameOfUser">{{ i18n.ts.save }}</MkButton>
+ </div>
+ </MkFolder>
+ </SearchMarker>
- <MkFolder>
- <template #icon><i class="ti ti-eye-off"></i></template>
- <template #label>{{ i18n.ts.hiddenTags }}</template>
+ <SearchMarker :keywords="['hidden', 'tags', 'hashtags']">
+ <MkFolder>
+ <template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts.hiddenTags }}</SearchLabel></template>
- <div class="_gaps">
- <MkTextarea v-model="hiddenTags">
- <template #caption>{{ i18n.ts.hiddenTagsDescription }}</template>
- </MkTextarea>
- <MkButton primary @click="save_hiddenTags">{{ i18n.ts.save }}</MkButton>
- </div>
- </MkFolder>
+ <div class="_gaps">
+ <MkTextarea v-model="hiddenTags">
+ <template #caption>{{ i18n.ts.hiddenTagsDescription }}</template>
+ </MkTextarea>
+ <MkButton primary @click="save_hiddenTags">{{ i18n.ts.save }}</MkButton>
+ </div>
+ </MkFolder>
+ </SearchMarker>
- <MkFolder>
- <template #icon><i class="ti ti-eye-off"></i></template>
- <template #label>{{ i18n.ts.silencedInstances }}</template>
+ <SearchMarker :keywords="['silenced', 'servers', 'hosts']">
+ <MkFolder>
+ <template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts.silencedInstances }}</SearchLabel></template>
- <div class="_gaps">
- <MkTextarea v-model="silencedHosts">
- <template #caption>{{ i18n.ts.silencedInstancesDescription }}</template>
- </MkTextarea>
- <MkButton primary @click="save_silencedHosts">{{ i18n.ts.save }}</MkButton>
- </div>
- </MkFolder>
+ <div class="_gaps">
+ <MkTextarea v-model="silencedHosts">
+ <template #caption>{{ i18n.ts.silencedInstancesDescription }}</template>
+ </MkTextarea>
+ <MkButton primary @click="save_silencedHosts">{{ i18n.ts.save }}</MkButton>
+ </div>
+ </MkFolder>
+ </SearchMarker>
- <MkFolder>
- <template #icon><i class="ti ti-eye-off"></i></template>
- <template #label>{{ i18n.ts.mediaSilencedInstances }}</template>
+ <SearchMarker :keywords="['media', 'silenced', 'servers', 'hosts']">
+ <MkFolder>
+ <template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts.mediaSilencedInstances }}</SearchLabel></template>
- <div class="_gaps">
- <MkTextarea v-model="mediaSilencedHosts">
- <template #caption>{{ i18n.ts.mediaSilencedInstancesDescription }}</template>
- </MkTextarea>
- <MkButton primary @click="save_mediaSilencedHosts">{{ i18n.ts.save }}</MkButton>
- </div>
- </MkFolder>
+ <div class="_gaps">
+ <MkTextarea v-model="mediaSilencedHosts">
+ <template #caption>{{ i18n.ts.mediaSilencedInstancesDescription }}</template>
+ </MkTextarea>
+ <MkButton primary @click="save_mediaSilencedHosts">{{ i18n.ts.save }}</MkButton>
+ </div>
+ </MkFolder>
+ </SearchMarker>
- <MkFolder>
- <template #icon><i class="ti ti-ban"></i></template>
- <template #label>{{ i18n.ts.blockedInstances }}</template>
+ <SearchMarker :keywords="['blocked', 'servers', 'hosts']">
+ <MkFolder>
+ <template #icon><SearchIcon><i class="ti ti-ban"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts.blockedInstances }}</SearchLabel></template>
- <div class="_gaps">
- <MkTextarea v-model="blockedHosts">
- <template #caption>{{ i18n.ts.blockedInstancesDescription }}</template>
- </MkTextarea>
- <MkButton primary @click="save_blockedHosts">{{ i18n.ts.save }}</MkButton>
- </div>
- </MkFolder>
+ <div class="_gaps">
+ <MkTextarea v-model="blockedHosts">
+ <template #caption>{{ i18n.ts.blockedInstancesDescription }}</template>
+ </MkTextarea>
+ <MkButton primary @click="save_blockedHosts">{{ i18n.ts.save }}</MkButton>
+ </div>
+ </MkFolder>
+ </SearchMarker>
</div>
- </FormSuspense>
+ </SearchMarker>
</div>
</PageWithHeader>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
+import * as Misskey from 'misskey-js';
+import XServerRules from './server-rules.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
-import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { fetchInstance } from '@/instance.js';
@@ -150,32 +181,19 @@ import FormLink from '@/components/form/link.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkSelect from '@/components/MkSelect.vue';
-const enableRegistration = ref<boolean>(false);
-const emailRequiredForSignup = ref<boolean>(false);
-const ugcVisibilityForVisitor = ref<string>('all');
-const sensitiveWords = ref<string>('');
-const prohibitedWords = ref<string>('');
-const prohibitedWordsForNameOfUser = ref<string>('');
-const hiddenTags = ref<string>('');
-const preservedUsernames = ref<string>('');
-const blockedHosts = ref<string>('');
-const silencedHosts = ref<string>('');
-const mediaSilencedHosts = ref<string>('');
+const meta = await misskeyApi('admin/meta');
-async function init() {
- const meta = await misskeyApi('admin/meta');
- enableRegistration.value = !meta.disableRegistration;
- emailRequiredForSignup.value = meta.emailRequiredForSignup;
- ugcVisibilityForVisitor.value = meta.ugcVisibilityForVisitor;
- sensitiveWords.value = meta.sensitiveWords.join('\n');
- prohibitedWords.value = meta.prohibitedWords.join('\n');
- prohibitedWordsForNameOfUser.value = meta.prohibitedWordsForNameOfUser.join('\n');
- hiddenTags.value = meta.hiddenTags.join('\n');
- preservedUsernames.value = meta.preservedUsernames.join('\n');
- blockedHosts.value = meta.blockedHosts.join('\n');
- silencedHosts.value = meta.silencedHosts?.join('\n') ?? '';
- mediaSilencedHosts.value = meta.mediaSilencedHosts.join('\n');
-}
+const enableRegistration = ref(!meta.disableRegistration);
+const emailRequiredForSignup = ref(meta.emailRequiredForSignup);
+const ugcVisibilityForVisitor = ref(meta.ugcVisibilityForVisitor);
+const sensitiveWords = ref(meta.sensitiveWords.join('\n'));
+const prohibitedWords = ref(meta.prohibitedWords.join('\n'));
+const prohibitedWordsForNameOfUser = ref(meta.prohibitedWordsForNameOfUser.join('\n'));
+const hiddenTags = ref(meta.hiddenTags.join('\n'));
+const preservedUsernames = ref(meta.preservedUsernames.join('\n'));
+const blockedHosts = ref(meta.blockedHosts.join('\n'));
+const silencedHosts = ref(meta.silencedHosts?.join('\n') ?? '');
+const mediaSilencedHosts = ref(meta.mediaSilencedHosts.join('\n'));
async function onChange_enableRegistration(value: boolean) {
if (value) {
@@ -203,7 +221,7 @@ function onChange_emailRequiredForSignup(value: boolean) {
});
}
-function onChange_ugcVisibilityForVisitor(value: string) {
+function onChange_ugcVisibilityForVisitor(value: Misskey.entities.AdminUpdateMetaRequest['ugcVisibilityForVisitor']) {
os.apiWithDialog('admin/update-meta', {
ugcVisibilityForVisitor: value,
}).then(() => {
diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue
index 6a6102749e..08bdc8d254 100644
--- a/packages/frontend/src/pages/admin/modlog.vue
+++ b/packages/frontend/src/pages/admin/modlog.vue
@@ -77,8 +77,8 @@ paginator.init();
const timeline = computed(() => {
return paginator.items.value.map(x => ({
id: x.id,
- timestamp: x.createdAt,
- data: x,
+ timestamp: new Date(x.createdAt).getTime(),
+ data: x as Misskey.entities.ModerationLog,
}));
});
diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue
index 7a46ae41c6..d42c23a51e 100644
--- a/packages/frontend/src/pages/admin/object-storage.vue
+++ b/packages/frontend/src/pages/admin/object-storage.vue
@@ -6,70 +6,94 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :tabs="headerTabs">
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
- <FormSuspense :p="init">
+ <SearchMarker path="/admin/object-storage" :label="i18n.ts.objectStorage" :keywords="['objectStorage']" icon="ti ti-cloud">
<div class="_gaps_m">
- <MkSwitch v-model="useObjectStorage">{{ i18n.ts.useObjectStorage }}</MkSwitch>
+ <SearchMarker>
+ <MkSwitch v-model="useObjectStorage"><SearchLabel>{{ i18n.ts.useObjectStorage }}</SearchLabel></MkSwitch>
+ </SearchMarker>
<template v-if="useObjectStorage">
- <MkInput v-model="objectStorageBaseUrl" :placeholder="'https://example.com'" type="url">
- <template #label>{{ i18n.ts.objectStorageBaseUrl }}</template>
- <template #caption>{{ i18n.ts.objectStorageBaseUrlDesc }}</template>
- </MkInput>
+ <SearchMarker>
+ <MkInput v-model="objectStorageBaseUrl" :placeholder="'https://example.com'" type="url">
+ <template #label><SearchLabel>{{ i18n.ts.objectStorageBaseUrl }}</SearchLabel></template>
+ <template #caption><SearchText>{{ i18n.ts.objectStorageBaseUrlDesc }}</SearchText></template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="objectStorageBucket">
- <template #label>{{ i18n.ts.objectStorageBucket }}</template>
- <template #caption>{{ i18n.ts.objectStorageBucketDesc }}</template>
- </MkInput>
+ <SearchMarker>
+ <MkInput v-model="objectStorageBucket">
+ <template #label><SearchLabel>{{ i18n.ts.objectStorageBucket }}</SearchLabel></template>
+ <template #caption><SearchText>{{ i18n.ts.objectStorageBucketDesc }}</SearchText></template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="objectStoragePrefix">
- <template #label>{{ i18n.ts.objectStoragePrefix }}</template>
- <template #caption>{{ i18n.ts.objectStoragePrefixDesc }}</template>
- </MkInput>
+ <SearchMarker>
+ <MkInput v-model="objectStoragePrefix">
+ <template #label><SearchLabel>{{ i18n.ts.objectStoragePrefix }}</SearchLabel></template>
+ <template #caption><SearchText>{{ i18n.ts.objectStoragePrefixDesc }}</SearchText></template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="objectStorageEndpoint" :placeholder="'example.com'">
- <template #label>{{ i18n.ts.objectStorageEndpoint }}</template>
- <template #prefix>https://</template>
- <template #caption>{{ i18n.ts.objectStorageEndpointDesc }}</template>
- </MkInput>
+ <SearchMarker>
+ <MkInput v-model="objectStorageEndpoint" :placeholder="'example.com'">
+ <template #label><SearchLabel>{{ i18n.ts.objectStorageEndpoint }}</SearchLabel></template>
+ <template #prefix>https://</template>
+ <template #caption><SearchText>{{ i18n.ts.objectStorageEndpointDesc }}</SearchText></template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="objectStorageRegion">
- <template #label>{{ i18n.ts.objectStorageRegion }}</template>
- <template #caption>{{ i18n.ts.objectStorageRegionDesc }}</template>
- </MkInput>
+ <SearchMarker>
+ <MkInput v-model="objectStorageRegion">
+ <template #label><SearchLabel>{{ i18n.ts.objectStorageRegion }}</SearchLabel></template>
+ <template #caption><SearchText>{{ i18n.ts.objectStorageRegionDesc }}</SearchText></template>
+ </MkInput>
+ </SearchMarker>
<FormSplit :minWidth="280">
- <MkInput v-model="objectStorageAccessKey">
- <template #prefix><i class="ti ti-key"></i></template>
- <template #label>Access key</template>
- </MkInput>
+ <SearchMarker>
+ <MkInput v-model="objectStorageAccessKey">
+ <template #prefix><i class="ti ti-key"></i></template>
+ <template #label><SearchLabel>Access key</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="objectStorageSecretKey" type="password">
- <template #prefix><i class="ti ti-key"></i></template>
- <template #label>Secret key</template>
- </MkInput>
+ <SearchMarker>
+ <MkInput v-model="objectStorageSecretKey" type="password">
+ <template #prefix><i class="ti ti-key"></i></template>
+ <template #label><SearchLabel>Secret key</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
</FormSplit>
- <MkSwitch v-model="objectStorageUseSSL">
- <template #label>{{ i18n.ts.objectStorageUseSSL }}</template>
- <template #caption>{{ i18n.ts.objectStorageUseSSLDesc }}</template>
- </MkSwitch>
+ <SearchMarker>
+ <MkSwitch v-model="objectStorageUseSSL">
+ <template #label><SearchLabel>{{ i18n.ts.objectStorageUseSSL }}</SearchLabel></template>
+ <template #caption><SearchText>{{ i18n.ts.objectStorageUseSSLDesc }}</SearchText></template>
+ </MkSwitch>
+ </SearchMarker>
- <MkSwitch v-model="objectStorageUseProxy">
- <template #label>{{ i18n.ts.objectStorageUseProxy }}</template>
- <template #caption>{{ i18n.ts.objectStorageUseProxyDesc }}</template>
- </MkSwitch>
+ <SearchMarker>
+ <MkSwitch v-model="objectStorageUseProxy">
+ <template #label><SearchLabel>{{ i18n.ts.objectStorageUseProxy }}</SearchLabel></template>
+ <template #caption><SearchText>{{ i18n.ts.objectStorageUseProxyDesc }}</SearchText></template>
+ </MkSwitch>
+ </SearchMarker>
- <MkSwitch v-model="objectStorageSetPublicRead">
- <template #label>{{ i18n.ts.objectStorageSetPublicRead }}</template>
- </MkSwitch>
+ <SearchMarker>
+ <MkSwitch v-model="objectStorageSetPublicRead">
+ <template #label><SearchLabel>{{ i18n.ts.objectStorageSetPublicRead }}</SearchLabel></template>
+ </MkSwitch>
+ </SearchMarker>
- <MkSwitch v-model="objectStorageS3ForcePathStyle">
- <template #label>s3ForcePathStyle</template>
- <template #caption>{{ i18n.ts.s3ForcePathStyleDesc }}</template>
- </MkSwitch>
+ <SearchMarker>
+ <MkSwitch v-model="objectStorageS3ForcePathStyle">
+ <template #label><SearchLabel>s3ForcePathStyle</SearchLabel></template>
+ <template #caption><SearchText>{{ i18n.ts.s3ForcePathStyleDesc }}</SearchText></template>
+ </MkSwitch>
+ </SearchMarker>
</template>
</div>
- </FormSuspense>
+ </SearchMarker>
</div>
<template #footer>
<div :class="$style.footer">
@@ -94,36 +118,21 @@ import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import MkButton from '@/components/MkButton.vue';
-const useObjectStorage = ref<boolean>(false);
-const objectStorageBaseUrl = ref<string | null>(null);
-const objectStorageBucket = ref<string | null>(null);
-const objectStoragePrefix = ref<string | null>(null);
-const objectStorageEndpoint = ref<string | null>(null);
-const objectStorageRegion = ref<string | null>(null);
-const objectStoragePort = ref<number | null>(null);
-const objectStorageAccessKey = ref<string | null>(null);
-const objectStorageSecretKey = ref<string | null>(null);
-const objectStorageUseSSL = ref<boolean>(false);
-const objectStorageUseProxy = ref<boolean>(false);
-const objectStorageSetPublicRead = ref<boolean>(false);
-const objectStorageS3ForcePathStyle = ref<boolean>(true);
+const meta = await misskeyApi('admin/meta');
-async function init() {
- const meta = await misskeyApi('admin/meta');
- useObjectStorage.value = meta.useObjectStorage;
- objectStorageBaseUrl.value = meta.objectStorageBaseUrl;
- objectStorageBucket.value = meta.objectStorageBucket;
- objectStoragePrefix.value = meta.objectStoragePrefix;
- objectStorageEndpoint.value = meta.objectStorageEndpoint;
- objectStorageRegion.value = meta.objectStorageRegion;
- objectStoragePort.value = meta.objectStoragePort;
- objectStorageAccessKey.value = meta.objectStorageAccessKey;
- objectStorageSecretKey.value = meta.objectStorageSecretKey;
- objectStorageUseSSL.value = meta.objectStorageUseSSL;
- objectStorageUseProxy.value = meta.objectStorageUseProxy;
- objectStorageSetPublicRead.value = meta.objectStorageSetPublicRead;
- objectStorageS3ForcePathStyle.value = meta.objectStorageS3ForcePathStyle;
-}
+const useObjectStorage = ref(meta.useObjectStorage);
+const objectStorageBaseUrl = ref(meta.objectStorageBaseUrl);
+const objectStorageBucket = ref(meta.objectStorageBucket);
+const objectStoragePrefix = ref(meta.objectStoragePrefix);
+const objectStorageEndpoint = ref(meta.objectStorageEndpoint);
+const objectStorageRegion = ref(meta.objectStorageRegion);
+const objectStoragePort = ref(meta.objectStoragePort);
+const objectStorageAccessKey = ref(meta.objectStorageAccessKey);
+const objectStorageSecretKey = ref(meta.objectStorageSecretKey);
+const objectStorageUseSSL = ref(meta.objectStorageUseSSL);
+const objectStorageUseProxy = ref(meta.objectStorageUseProxy);
+const objectStorageSetPublicRead = ref(meta.objectStorageSetPublicRead);
+const objectStorageS3ForcePathStyle = ref(meta.objectStorageS3ForcePathStyle);
function save() {
os.apiWithDialog('admin/update-meta', {
diff --git a/packages/frontend/src/pages/admin/overview.ap-requests.stories.impl.ts b/packages/frontend/src/pages/admin/overview.ap-requests.stories.impl.ts
index 88747ef4ed..855fc6caf0 100644
--- a/packages/frontend/src/pages/admin/overview.ap-requests.stories.impl.ts
+++ b/packages/frontend/src/pages/admin/overview.ap-requests.stories.impl.ts
@@ -5,7 +5,7 @@
import type { StoryObj } from '@storybook/vue3';
import { http, HttpResponse } from 'msw';
-import { action } from '@storybook/addon-actions';
+import { action } from 'storybook/actions';
import { commonHandlers } from '../../../.storybook/mocks.js';
import overview_ap_requests from './overview.ap-requests.vue';
export const Default = {
diff --git a/packages/frontend/src/pages/admin/performance.vue b/packages/frontend/src/pages/admin/performance.vue
index c28621b11e..e3021778e7 100644
--- a/packages/frontend/src/pages/admin/performance.vue
+++ b/packages/frontend/src/pages/admin/performance.vue
@@ -6,102 +6,163 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
- <div class="_gaps">
- <div class="_panel" style="padding: 16px;">
- <MkSwitch v-model="enableServerMachineStats" @change="onChange_enableServerMachineStats">
- <template #label>{{ i18n.ts.enableServerMachineStats }}</template>
- <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
- </MkSwitch>
- </div>
-
- <div class="_panel" style="padding: 16px;">
- <MkSwitch v-model="enableIdenticonGeneration" @change="onChange_enableIdenticonGeneration">
- <template #label>{{ i18n.ts.enableIdenticonGeneration }}</template>
- <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
- </MkSwitch>
- </div>
+ <SearchMarker path="/admin/performance" :label="i18n.ts.performance" :keywords="['performance']" icon="ti ti-bolt">
+ <div class="_gaps">
+ <SearchMarker>
+ <div class="_panel" style="padding: 16px;">
+ <MkSwitch v-model="enableServerMachineStats" @change="onChange_enableServerMachineStats">
+ <template #label><SearchLabel>{{ i18n.ts.enableServerMachineStats }}</SearchLabel></template>
+ <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
+ </MkSwitch>
+ </div>
+ </SearchMarker>
- <div class="_panel" style="padding: 16px;">
- <MkSwitch v-model="enableChartsForRemoteUser" @change="onChange_enableChartsForRemoteUser">
- <template #label>{{ i18n.ts.enableChartsForRemoteUser }}</template>
- <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
- </MkSwitch>
- </div>
+ <SearchMarker>
+ <div class="_panel" style="padding: 16px;">
+ <MkSwitch v-model="enableIdenticonGeneration" @change="onChange_enableIdenticonGeneration">
+ <template #label><SearchLabel>{{ i18n.ts.enableIdenticonGeneration }}</SearchLabel></template>
+ <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
+ </MkSwitch>
+ </div>
+ </SearchMarker>
- <div class="_panel" style="padding: 16px;">
- <MkSwitch v-model="enableStatsForFederatedInstances" @change="onChange_enableStatsForFederatedInstances">
- <template #label>{{ i18n.ts.enableStatsForFederatedInstances }}</template>
- <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
- </MkSwitch>
- </div>
+ <SearchMarker>
+ <div class="_panel" style="padding: 16px;">
+ <MkSwitch v-model="enableChartsForRemoteUser" @change="onChange_enableChartsForRemoteUser">
+ <template #label><SearchLabel>{{ i18n.ts.enableChartsForRemoteUser }}</SearchLabel></template>
+ <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
+ </MkSwitch>
+ </div>
+ </SearchMarker>
- <div class="_panel" style="padding: 16px;">
- <MkSwitch v-model="enableChartsForFederatedInstances" @change="onChange_enableChartsForFederatedInstances">
- <template #label>{{ i18n.ts.enableChartsForFederatedInstances }}</template>
- <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
- </MkSwitch>
- </div>
+ <SearchMarker>
+ <div class="_panel" style="padding: 16px;">
+ <MkSwitch v-model="enableStatsForFederatedInstances" @change="onChange_enableStatsForFederatedInstances">
+ <template #label><SearchLabel>{{ i18n.ts.enableStatsForFederatedInstances }}</SearchLabel></template>
+ <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
+ </MkSwitch>
+ </div>
+ </SearchMarker>
- <MkFolder :defaultOpen="true">
- <template #icon><i class="ti ti-bolt"></i></template>
- <template #label>Misskey® Fan-out Timeline Technology™ (FTT)</template>
- <template v-if="fttForm.savedState.enableFanoutTimeline" #suffix>Enabled</template>
- <template v-else #suffix>Disabled</template>
- <template v-if="fttForm.modified.value" #footer>
- <MkFormFooter :form="fttForm"/>
- </template>
+ <SearchMarker>
+ <div class="_panel" style="padding: 16px;">
+ <MkSwitch v-model="enableChartsForFederatedInstances" @change="onChange_enableChartsForFederatedInstances">
+ <template #label><SearchLabel>{{ i18n.ts.enableChartsForFederatedInstances }}</SearchLabel></template>
+ <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
+ </MkSwitch>
+ </div>
+ </SearchMarker>
- <div class="_gaps">
- <MkSwitch v-model="fttForm.state.enableFanoutTimeline">
- <template #label>{{ i18n.ts.enable }}<span v-if="fttForm.modifiedStates.enableFanoutTimeline" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>
- <div>{{ i18n.ts._serverSettings.fanoutTimelineDescription }}</div>
- <div><MkLink target="_blank" url="https://misskey-hub.net/docs/for-admin/features/ftt/">{{ i18n.ts.details }}</MkLink></div>
+ <SearchMarker>
+ <MkFolder :defaultOpen="true">
+ <template #icon><SearchIcon><i class="ti ti-bolt"></i></SearchIcon></template>
+ <template #label><SearchLabel>Misskey® Fan-out Timeline Technology™ (FTT)</SearchLabel></template>
+ <template v-if="fttForm.savedState.enableFanoutTimeline" #suffix>Enabled</template>
+ <template v-else #suffix>Disabled</template>
+ <template v-if="fttForm.modified.value" #footer>
+ <MkFormFooter :form="fttForm"/>
</template>
- </MkSwitch>
- <template v-if="fttForm.state.enableFanoutTimeline">
- <MkSwitch v-model="fttForm.state.enableFanoutTimelineDbFallback">
- <template #label>{{ i18n.ts._serverSettings.fanoutTimelineDbFallback }}<span v-if="fttForm.modifiedStates.enableFanoutTimelineDbFallback" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts._serverSettings.fanoutTimelineDbFallbackDescription }}</template>
- </MkSwitch>
+ <div class="_gaps">
+ <SearchMarker>
+ <MkSwitch v-model="fttForm.state.enableFanoutTimeline">
+ <template #label><SearchLabel>{{ i18n.ts.enable }}</SearchLabel><span v-if="fttForm.modifiedStates.enableFanoutTimeline" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption>
+ <div><SearchText>{{ i18n.ts._serverSettings.fanoutTimelineDescription }}</SearchText></div>
+ <div><MkLink target="_blank" url="https://misskey-hub.net/docs/for-admin/features/ftt/">{{ i18n.ts.details }}</MkLink></div>
+ </template>
+ </MkSwitch>
+ </SearchMarker>
+
+ <template v-if="fttForm.state.enableFanoutTimeline">
+ <SearchMarker :keywords="['db', 'database', 'fallback']">
+ <MkSwitch v-model="fttForm.state.enableFanoutTimelineDbFallback">
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.fanoutTimelineDbFallback }}</SearchLabel><span v-if="fttForm.modifiedStates.enableFanoutTimelineDbFallback" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption><SearchText>{{ i18n.ts._serverSettings.fanoutTimelineDbFallbackDescription }}</SearchText></template>
+ </MkSwitch>
+ </SearchMarker>
- <MkInput v-model="fttForm.state.perLocalUserUserTimelineCacheMax" type="number">
- <template #label>perLocalUserUserTimelineCacheMax<span v-if="fttForm.modifiedStates.perLocalUserUserTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
- </MkInput>
+ <SearchMarker>
+ <MkInput v-model="fttForm.state.perLocalUserUserTimelineCacheMax" type="number">
+ <template #label><SearchLabel>perLocalUserUserTimelineCacheMax</SearchLabel><span v-if="fttForm.modifiedStates.perLocalUserUserTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="fttForm.state.perRemoteUserUserTimelineCacheMax" type="number">
- <template #label>perRemoteUserUserTimelineCacheMax<span v-if="fttForm.modifiedStates.perRemoteUserUserTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
- </MkInput>
+ <SearchMarker>
+ <MkInput v-model="fttForm.state.perRemoteUserUserTimelineCacheMax" type="number">
+ <template #label><SearchLabel>perRemoteUserUserTimelineCacheMax</SearchLabel><span v-if="fttForm.modifiedStates.perRemoteUserUserTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="fttForm.state.perUserHomeTimelineCacheMax" type="number">
- <template #label>perUserHomeTimelineCacheMax<span v-if="fttForm.modifiedStates.perUserHomeTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
- </MkInput>
+ <SearchMarker>
+ <MkInput v-model="fttForm.state.perUserHomeTimelineCacheMax" type="number">
+ <template #label><SearchLabel>perUserHomeTimelineCacheMax</SearchLabel><span v-if="fttForm.modifiedStates.perUserHomeTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="fttForm.state.perUserListTimelineCacheMax" type="number">
- <template #label>perUserListTimelineCacheMax<span v-if="fttForm.modifiedStates.perUserListTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
- </MkInput>
- </template>
- </div>
- </MkFolder>
+ <SearchMarker>
+ <MkInput v-model="fttForm.state.perUserListTimelineCacheMax" type="number">
+ <template #label><SearchLabel>perUserListTimelineCacheMax</SearchLabel><span v-if="fttForm.modifiedStates.perUserListTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
+ </MkInput>
+ </SearchMarker>
+ </template>
+ </div>
+ </MkFolder>
+ </SearchMarker>
- <MkFolder :defaultOpen="true">
- <template #icon><i class="ti ti-bolt"></i></template>
- <template #label>Misskey® Reactions Boost Technology™ (RBT)<span class="_beta">{{ i18n.ts.beta }}</span></template>
- <template v-if="rbtForm.savedState.enableReactionsBuffering" #suffix>Enabled</template>
- <template v-else #suffix>Disabled</template>
- <template v-if="rbtForm.modified.value" #footer>
- <MkFormFooter :form="rbtForm"/>
- </template>
+ <SearchMarker>
+ <MkFolder :defaultOpen="true">
+ <template #icon><SearchIcon><i class="ti ti-bolt"></i></SearchIcon></template>
+ <template #label><SearchLabel>Misskey® Reactions Boost Technology™ (RBT)</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template>
+ <template v-if="rbtForm.savedState.enableReactionsBuffering" #suffix>Enabled</template>
+ <template v-else #suffix>Disabled</template>
+ <template v-if="rbtForm.modified.value" #footer>
+ <MkFormFooter :form="rbtForm"/>
+ </template>
+
+ <div class="_gaps_m">
+ <SearchMarker>
+ <MkSwitch v-model="rbtForm.state.enableReactionsBuffering">
+ <template #label><SearchLabel>{{ i18n.ts.enable }}</SearchLabel><span v-if="rbtForm.modifiedStates.enableReactionsBuffering" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption><SearchText>{{ i18n.ts._serverSettings.reactionsBufferingDescription }}</SearchText></template>
+ </MkSwitch>
+ </SearchMarker>
+ </div>
+ </MkFolder>
+ </SearchMarker>
+
+ <SearchMarker>
+ <MkFolder :defaultOpen="true">
+ <template #icon><SearchIcon><i class="ti ti-recycle"></i></SearchIcon></template>
+ <template #label><SearchLabel>Remote Notes Cleaning (ä»®)</SearchLabel></template>
+ <template v-if="remoteNotesCleaningForm.savedState.enableRemoteNotesCleaning" #suffix>Enabled</template>
+ <template v-else #suffix>Disabled</template>
+ <template v-if="remoteNotesCleaningForm.modified.value" #footer>
+ <MkFormFooter :form="remoteNotesCleaningForm"/>
+ </template>
- <div class="_gaps_m">
- <MkSwitch v-model="rbtForm.state.enableReactionsBuffering">
- <template #label>{{ i18n.ts.enable }}<span v-if="rbtForm.modifiedStates.enableReactionsBuffering" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts._serverSettings.reactionsBufferingDescription }}</template>
- </MkSwitch>
- </div>
- </MkFolder>
- </div>
+ <div class="_gaps_m">
+ <MkSwitch v-model="remoteNotesCleaningForm.state.enableRemoteNotesCleaning">
+ <template #label><SearchLabel>{{ i18n.ts.enable }}</SearchLabel><span v-if="remoteNotesCleaningForm.modifiedStates.enableRemoteNotesCleaning" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption><SearchText>{{ i18n.ts._serverSettings.remoteNotesCleaning_description }}</SearchText></template>
+ </MkSwitch>
+
+ <template v-if="remoteNotesCleaningForm.state.enableRemoteNotesCleaning">
+ <MkInput v-model="remoteNotesCleaningForm.state.remoteNotesCleaningExpiryDaysForEachNotes" type="number">
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.remoteNotesCleaningExpiryDaysForEachNotes }}</SearchLabel> ({{ i18n.ts.inDays }})<span v-if="remoteNotesCleaningForm.modifiedStates.remoteNotesCleaningExpiryDaysForEachNotes" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #suffix>{{ i18n.ts._time.day }}</template>
+ </MkInput>
+
+ <MkInput v-model="remoteNotesCleaningForm.state.remoteNotesCleaningMaxProcessingDurationInMinutes" type="number">
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.remoteNotesCleaningMaxProcessingDuration }}</SearchLabel> ({{ i18n.ts.inMinutes }})<span v-if="remoteNotesCleaningForm.modifiedStates.remoteNotesCleaningMaxProcessingDurationInMinutes" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #suffix>{{ i18n.ts._time.minute }}</template>
+ </MkInput>
+ </template>
+ </div>
+ </MkFolder>
+ </SearchMarker>
+ </div>
+ </SearchMarker>
</div>
</PageWithHeader>
</template>
@@ -196,12 +257,25 @@ const rbtForm = useForm({
fetchInstance(true);
});
+const remoteNotesCleaningForm = useForm({
+ enableRemoteNotesCleaning: meta.enableRemoteNotesCleaning,
+ remoteNotesCleaningExpiryDaysForEachNotes: meta.remoteNotesCleaningExpiryDaysForEachNotes,
+ remoteNotesCleaningMaxProcessingDurationInMinutes: meta.remoteNotesCleaningMaxProcessingDurationInMinutes,
+}, async (state) => {
+ await os.apiWithDialog('admin/update-meta', {
+ enableRemoteNotesCleaning: state.enableRemoteNotesCleaning,
+ remoteNotesCleaningExpiryDaysForEachNotes: state.remoteNotesCleaningExpiryDaysForEachNotes,
+ remoteNotesCleaningMaxProcessingDurationInMinutes: state.remoteNotesCleaningMaxProcessingDurationInMinutes,
+ });
+ fetchInstance(true);
+});
+
const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePage(() => ({
- title: i18n.ts.other,
- icon: 'ti ti-adjustments',
+ title: i18n.ts.performance,
+ icon: 'ti ti-bolt',
}));
</script>
diff --git a/packages/frontend/src/pages/admin/relays.vue b/packages/frontend/src/pages/admin/relays.vue
index aabf64342e..9eba68022a 100644
--- a/packages/frontend/src/pages/admin/relays.vue
+++ b/packages/frontend/src/pages/admin/relays.vue
@@ -6,18 +6,20 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
<div class="_spacer" style="--MI_SPACER-w: 800px;">
- <div class="_gaps">
- <div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel" style="padding: 16px;">
- <div>{{ relay.inbox }}</div>
- <div style="margin: 8px 0;">
- <i v-if="relay.status === 'accepted'" class="ti ti-check" :class="$style.icon" style="color: var(--MI_THEME-success);"></i>
- <i v-else-if="relay.status === 'rejected'" class="ti ti-ban" :class="$style.icon" style="color: var(--MI_THEME-error);"></i>
- <i v-else class="ti ti-clock" :class="$style.icon"></i>
- <span>{{ i18n.ts._relayStatus[relay.status] }}</span>
+ <SearchMarker path="/admin/relays" :label="i18n.ts.relays" :keywords="['relays']" icon="ti ti-planet">
+ <div class="_gaps">
+ <div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel" style="padding: 16px;">
+ <div>{{ relay.inbox }}</div>
+ <div style="margin: 8px 0;">
+ <i v-if="relay.status === 'accepted'" class="ti ti-check" :class="$style.icon" style="color: var(--MI_THEME-success);"></i>
+ <i v-else-if="relay.status === 'rejected'" class="ti ti-ban" :class="$style.icon" style="color: var(--MI_THEME-error);"></i>
+ <i v-else class="ti ti-clock" :class="$style.icon"></i>
+ <span>{{ i18n.ts._relayStatus[relay.status] }}</span>
+ </div>
+ <MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton>
</div>
- <MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton>
</div>
- </div>
+ </SearchMarker>
</div>
</PageWithHeader>
</template>
@@ -39,7 +41,7 @@ async function addRelay() {
type: 'url',
placeholder: i18n.ts.inboxUrl,
});
- if (canceled) return;
+ if (canceled || inbox == null) return;
misskeyApi('admin/relays/add', {
inbox,
}).then((relay: any) => {
diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue
index 1a903eedb9..b24b640527 100644
--- a/packages/frontend/src/pages/admin/roles.edit.vue
+++ b/packages/frontend/src/pages/admin/roles.edit.vue
@@ -72,12 +72,20 @@ async function save() {
roleId: role.value.id,
...data.value,
});
- router.push('/admin/roles/' + role.value.id);
+ router.push('/admin/roles/:id', {
+ params: {
+ id: role.value.id,
+ }
+ });
} else {
const created = await os.apiWithDialog('admin/roles/create', {
...data.value,
});
- router.push('/admin/roles/' + created.id);
+ router.push('/admin/roles/:id', {
+ params: {
+ id: created.id,
+ }
+ });
}
}
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index c172e22688..bb96a1cde1 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -346,6 +346,26 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkFolder>
+ <MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchUsers, 'canSearchUsers'])">
+ <template #label>{{ i18n.ts._role._options.canSearchUsers }}</template>
+ <template #suffix>
+ <span v-if="role.policies.canSearchUsers.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
+ <span v-else>{{ role.policies.canSearchUsers.value ? i18n.ts.yes : i18n.ts.no }}</span>
+ <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canSearchUsers)"></i></span>
+ </template>
+ <div class="_gaps">
+ <MkSwitch v-model="role.policies.canSearchUsers.useDefault" :readonly="readonly">
+ <template #label>{{ i18n.ts._role.useBaseValue }}</template>
+ </MkSwitch>
+ <MkSwitch v-model="role.policies.canSearchUsers.value" :disabled="role.policies.canSearchUsers.useDefault" :readonly="readonly">
+ <template #label>{{ i18n.ts.enable }}</template>
+ </MkSwitch>
+ <MkRange v-model="role.policies.canSearchUsers.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+ <template #label>{{ i18n.ts._role.priority }}</template>
+ </MkRange>
+ </div>
+ </MkFolder>
+
<MkFolder v-if="matchQuery([i18n.ts._role._options.canUseTranslator, 'canUseTranslator'])">
<template #label>{{ i18n.ts._role._options.canUseTranslator }}</template>
<template #suffix>
diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue
index 1816aec21e..c6c3165828 100644
--- a/packages/frontend/src/pages/admin/roles.role.vue
+++ b/packages/frontend/src/pages/admin/roles.role.vue
@@ -88,7 +88,11 @@ const role = reactive(await misskeyApi('admin/roles/show', {
}));
function edit() {
- router.push('/admin/roles/' + role.id + '/edit');
+ router.push('/admin/roles/:id/edit', {
+ params: {
+ id: role.id,
+ }
+ });
}
async function del() {
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index e78a4bbc11..efdf8620ef 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -122,6 +122,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch>
</MkFolder>
+ <MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchUsers, 'canSearchUsers'])">
+ <template #label>{{ i18n.ts._role._options.canSearchUsers }}</template>
+ <template #suffix>{{ policies.canSearchUsers ? i18n.ts.yes : i18n.ts.no }}</template>
+ <MkSwitch v-model="policies.canSearchUsers">
+ <template #label>{{ i18n.ts.enable }}</template>
+ </MkSwitch>
+ </MkFolder>
+
<MkFolder v-if="matchQuery([i18n.ts._role._options.canUseTranslator, 'canUseTranslator'])">
<template #label>{{ i18n.ts._role._options.canUseTranslator }}</template>
<template #suffix>{{ policies.canUseTranslator ? i18n.ts.yes : i18n.ts.no }}</template>
diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index 9e907a4469..27e35c7e69 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -6,115 +6,153 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
- <div class="_gaps_m">
- <XBotProtection/>
+ <SearchMarker path="/admin/security" :label="i18n.ts.security" :keywords="['security']" icon="ti ti-lock" :inlining="['botProtection']">
+ <div class="_gaps_m">
+ <XBotProtection/>
- <MkFolder>
- <template #icon><i class="ti ti-eye-off"></i></template>
- <template #label>{{ i18n.ts.sensitiveMediaDetection }}</template>
- <template v-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'all'" #suffix>{{ i18n.ts.all }}</template>
- <template v-else-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'local'" #suffix>{{ i18n.ts.localOnly }}</template>
- <template v-else-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'remote'" #suffix>{{ i18n.ts.remoteOnly }}</template>
- <template v-else #suffix>{{ i18n.ts.none }}</template>
- <template v-if="sensitiveMediaDetectionForm.modified.value" #footer>
- <MkFormFooter :form="sensitiveMediaDetectionForm"/>
- </template>
+ <SearchMarker v-slot="slotProps" :keywords="['sensitive', 'media', 'detection']">
+ <MkFolder :defaultOpen="slotProps.isParentOfTarget">
+ <template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts.sensitiveMediaDetection }}</SearchLabel></template>
+ <template v-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'all'" #suffix>{{ i18n.ts.all }}</template>
+ <template v-else-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'local'" #suffix>{{ i18n.ts.localOnly }}</template>
+ <template v-else-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'remote'" #suffix>{{ i18n.ts.remoteOnly }}</template>
+ <template v-else #suffix>{{ i18n.ts.none }}</template>
+ <template v-if="sensitiveMediaDetectionForm.modified.value" #footer>
+ <MkFormFooter :form="sensitiveMediaDetectionForm"/>
+ </template>
- <div class="_gaps_m">
- <span>{{ i18n.ts._sensitiveMediaDetection.description }}</span>
+ <div class="_gaps_m">
+ <div><SearchText>{{ i18n.ts._sensitiveMediaDetection.description }}</SearchText></div>
- <MkRadios v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetection">
- <option value="none">{{ i18n.ts.none }}</option>
- <option value="all">{{ i18n.ts.all }}</option>
- <option value="local">{{ i18n.ts.localOnly }}</option>
- <option value="remote">{{ i18n.ts.remoteOnly }}</option>
- </MkRadios>
+ <MkRadios v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetection">
+ <option value="none">{{ i18n.ts.none }}</option>
+ <option value="all">{{ i18n.ts.all }}</option>
+ <option value="local">{{ i18n.ts.localOnly }}</option>
+ <option value="remote">{{ i18n.ts.remoteOnly }}</option>
+ </MkRadios>
- <MkRange v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetectionSensitivity" :min="0" :max="4" :step="1" :textConverter="(v) => `${v + 1}`">
- <template #label>{{ i18n.ts._sensitiveMediaDetection.sensitivity }}</template>
- <template #caption>{{ i18n.ts._sensitiveMediaDetection.sensitivityDescription }}</template>
- </MkRange>
+ <SearchMarker :keywords="['sensitivity']">
+ <MkRange v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetectionSensitivity" :min="0" :max="4" :step="1" :textConverter="(v) => `${v + 1}`">
+ <template #label><SearchLabel>{{ i18n.ts._sensitiveMediaDetection.sensitivity }}</SearchLabel></template>
+ <template #caption><SearchText>{{ i18n.ts._sensitiveMediaDetection.sensitivityDescription }}</SearchText></template>
+ </MkRange>
+ </SearchMarker>
- <MkSwitch v-model="sensitiveMediaDetectionForm.state.enableSensitiveMediaDetectionForVideos">
- <template #label>{{ i18n.ts._sensitiveMediaDetection.analyzeVideos }}<span class="_beta">{{ i18n.ts.beta }}</span></template>
- <template #caption>{{ i18n.ts._sensitiveMediaDetection.analyzeVideosDescription }}</template>
- </MkSwitch>
+ <SearchMarker :keywords="['video', 'analyze']">
+ <MkSwitch v-model="sensitiveMediaDetectionForm.state.enableSensitiveMediaDetectionForVideos">
+ <template #label><SearchLabel>{{ i18n.ts._sensitiveMediaDetection.analyzeVideos }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template>
+ <template #caption><SearchText>{{ i18n.ts._sensitiveMediaDetection.analyzeVideosDescription }}</SearchText></template>
+ </MkSwitch>
+ </SearchMarker>
- <MkSwitch v-model="sensitiveMediaDetectionForm.state.setSensitiveFlagAutomatically">
- <template #label>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomatically }} ({{ i18n.ts.notRecommended }})</template>
- <template #caption>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomaticallyDescription }}</template>
- </MkSwitch>
+ <SearchMarker :keywords="['flag', 'automatically']">
+ <MkSwitch v-model="sensitiveMediaDetectionForm.state.setSensitiveFlagAutomatically">
+ <template #label><SearchLabel>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomatically }}</SearchLabel> ({{ i18n.ts.notRecommended }})</template>
+ <template #caption><SearchText>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomaticallyDescription }}</SearchText></template>
+ </MkSwitch>
+ </SearchMarker>
- <!-- ç¾çж false positive ãŒå¤šã™ãŽã¦å®Ÿç”¨ã«è€ãˆãªã„
+ <!-- ç¾çж false positive ãŒå¤šã™ãŽã¦å®Ÿç”¨ã«è€ãˆãªã„
<MkSwitch v-model="disallowUploadWhenPredictedAsPorn">
<template #label>{{ i18n.ts._sensitiveMediaDetection.disallowUploadWhenPredictedAsPorn }}</template>
</MkSwitch>
-->
- </div>
- </MkFolder>
+ </div>
+ </MkFolder>
+ </SearchMarker>
- <MkFolder>
- <template #label>Active Email Validation</template>
- <template v-if="emailValidationForm.savedState.enableActiveEmailValidation" #suffix>Enabled</template>
- <template v-else #suffix>Disabled</template>
- <template v-if="emailValidationForm.modified.value" #footer>
- <MkFormFooter :form="emailValidationForm"/>
- </template>
+ <SearchMarker v-slot="slotProps" :keywords="['email', 'validation']">
+ <MkFolder :defaultOpen="slotProps.isParentOfTarget">
+ <template #label><SearchLabel>Active Email Validation</SearchLabel></template>
+ <template v-if="emailValidationForm.savedState.enableActiveEmailValidation" #suffix>Enabled</template>
+ <template v-else #suffix>Disabled</template>
+ <template v-if="emailValidationForm.modified.value" #footer>
+ <MkFormFooter :form="emailValidationForm"/>
+ </template>
- <div class="_gaps_m">
- <span>{{ i18n.ts.activeEmailValidationDescription }}</span>
- <MkSwitch v-model="emailValidationForm.state.enableActiveEmailValidation">
- <template #label>Enable</template>
- </MkSwitch>
- <MkSwitch v-model="emailValidationForm.state.enableVerifymailApi">
- <template #label>Use Verifymail.io API</template>
- </MkSwitch>
- <MkInput v-model="emailValidationForm.state.verifymailAuthKey">
- <template #prefix><i class="ti ti-key"></i></template>
- <template #label>Verifymail.io API Auth Key</template>
- </MkInput>
- <MkSwitch v-model="emailValidationForm.state.enableTruemailApi">
- <template #label>Use TrueMail API</template>
- </MkSwitch>
- <MkInput v-model="emailValidationForm.state.truemailInstance">
- <template #prefix><i class="ti ti-key"></i></template>
- <template #label>TrueMail API Instance</template>
- </MkInput>
- <MkInput v-model="emailValidationForm.state.truemailAuthKey">
- <template #prefix><i class="ti ti-key"></i></template>
- <template #label>TrueMail API Auth Key</template>
- </MkInput>
- </div>
- </MkFolder>
+ <div class="_gaps_m">
+ <div><SearchText>{{ i18n.ts.activeEmailValidationDescription }}</SearchText></div>
- <MkFolder>
- <template #label>Banned Email Domains</template>
- <template v-if="bannedEmailDomainsForm.modified.value" #footer>
- <MkFormFooter :form="bannedEmailDomainsForm"/>
- </template>
+ <SearchMarker>
+ <MkSwitch v-model="emailValidationForm.state.enableActiveEmailValidation">
+ <template #label><SearchLabel>Enable</SearchLabel></template>
+ </MkSwitch>
+ </SearchMarker>
- <div class="_gaps_m">
- <MkTextarea v-model="bannedEmailDomainsForm.state.bannedEmailDomains">
- <template #label>Banned Email Domains List</template>
- </MkTextarea>
- </div>
- </MkFolder>
+ <SearchMarker>
+ <MkSwitch v-model="emailValidationForm.state.enableVerifymailApi">
+ <template #label><SearchLabel>Use Verifymail.io API</SearchLabel></template>
+ </MkSwitch>
+ </SearchMarker>
- <MkFolder>
- <template #label>Log IP address</template>
- <template v-if="ipLoggingForm.savedState.enableIpLogging" #suffix>Enabled</template>
- <template v-else #suffix>Disabled</template>
- <template v-if="ipLoggingForm.modified.value" #footer>
- <MkFormFooter :form="ipLoggingForm"/>
- </template>
+ <SearchMarker>
+ <MkInput v-model="emailValidationForm.state.verifymailAuthKey">
+ <template #prefix><i class="ti ti-key"></i></template>
+ <template #label><SearchLabel>Verifymail.io API Auth Key</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
- <div class="_gaps_m">
- <MkSwitch v-model="ipLoggingForm.state.enableIpLogging">
- <template #label>Enable</template>
- </MkSwitch>
- </div>
- </MkFolder>
- </div>
+ <SearchMarker>
+ <MkSwitch v-model="emailValidationForm.state.enableTruemailApi">
+ <template #label><SearchLabel>Use TrueMail API</SearchLabel></template>
+ </MkSwitch>
+ </SearchMarker>
+
+ <SearchMarker>
+ <MkInput v-model="emailValidationForm.state.truemailInstance">
+ <template #prefix><i class="ti ti-key"></i></template>
+ <template #label><SearchLabel>TrueMail API Instance</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
+
+ <SearchMarker>
+ <MkInput v-model="emailValidationForm.state.truemailAuthKey">
+ <template #prefix><i class="ti ti-key"></i></template>
+ <template #label><SearchLabel>TrueMail API Auth Key</SearchLabel></template>
+ </MkInput>
+ </SearchMarker>
+ </div>
+ </MkFolder>
+ </SearchMarker>
+
+ <SearchMarker v-slot="slotProps" :keywords="['banned', 'email', 'domains', 'blacklist']">
+ <MkFolder :defaultOpen="slotProps.isParentOfTarget">
+ <template #label><SearchLabel>Banned Email Domains</SearchLabel></template>
+ <template v-if="bannedEmailDomainsForm.modified.value" #footer>
+ <MkFormFooter :form="bannedEmailDomainsForm"/>
+ </template>
+
+ <div class="_gaps_m">
+ <SearchMarker>
+ <MkTextarea v-model="bannedEmailDomainsForm.state.bannedEmailDomains">
+ <template #label><SearchLabel>Banned Email Domains List</SearchLabel></template>
+ </MkTextarea>
+ </SearchMarker>
+ </div>
+ </MkFolder>
+ </SearchMarker>
+
+ <SearchMarker v-slot="slotProps" :keywords="['log', 'ipAddress']">
+ <MkFolder :defaultOpen="slotProps.isParentOfTarget">
+ <template #label><SearchLabel>Log IP address</SearchLabel></template>
+ <template v-if="ipLoggingForm.savedState.enableIpLogging" #suffix>Enabled</template>
+ <template v-else #suffix>Disabled</template>
+ <template v-if="ipLoggingForm.modified.value" #footer>
+ <MkFormFooter :form="ipLoggingForm"/>
+ </template>
+
+ <div class="_gaps_m">
+ <SearchMarker>
+ <MkSwitch v-model="ipLoggingForm.state.enableIpLogging">
+ <template #label><SearchLabel>Enable</SearchLabel></template>
+ </MkSwitch>
+ </SearchMarker>
+ </div>
+ </MkFolder>
+ </SearchMarker>
+ </div>
+ </SearchMarker>
</div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue
index 276a7590c4..d26f02b41c 100644
--- a/packages/frontend/src/pages/admin/server-rules.vue
+++ b/packages/frontend/src/pages/admin/server-rules.vue
@@ -4,10 +4,14 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader :tabs="headerTabs">
- <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
+<SearchMarker markerId="serverRules" :keywords="['rules']">
+ <MkFolder>
+ <template #icon><SearchIcon><i class="ti ti-checkbox"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts.serverRules }}</SearchLabel></template>
+
<div class="_gaps_m">
- <div>{{ i18n.ts._serverRules.description }}</div>
+ <div><SearchText>{{ i18n.ts._serverRules.description }}</SearchText></div>
+
<Sortable
v-model="serverRules"
class="_gaps_m"
@@ -33,8 +37,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
</div>
</div>
- </div>
-</PageWithHeader>
+ </MkFolder>
+</SearchMarker>
</template>
<script lang="ts" setup>
@@ -42,9 +46,9 @@ import { defineAsyncComponent, ref, computed } from 'vue';
import * as os from '@/os.js';
import { fetchInstance, instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
-import { definePage } from '@/page.js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
+import MkFolder from '@/components/MkFolder.vue';
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
@@ -60,13 +64,6 @@ const save = async () => {
const remove = (index: number): void => {
serverRules.value.splice(index, 1);
};
-
-const headerTabs = computed(() => []);
-
-definePage(() => ({
- title: i18n.ts.serverRules,
- icon: 'ti ti-checkbox',
-}));
</script>
<style lang="scss" module>
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index f6a2eb1c27..541ee7c0cd 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -6,288 +6,369 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :tabs="headerTabs">
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
- <div class="_gaps_m">
- <MkFolder :defaultOpen="true">
- <template #icon><i class="ti ti-info-circle"></i></template>
- <template #label>{{ i18n.ts.info }}</template>
- <template v-if="infoForm.modified.value" #footer>
- <MkFormFooter :form="infoForm"/>
- </template>
+ <SearchMarker path="/admin/settings" :label="i18n.ts.general" :keywords="['general', 'settings']" icon="ti ti-settings">
+ <div class="_gaps_m">
+ <SearchMarker v-slot="slotProps" :keywords="['information', 'meta']">
+ <MkFolder :defaultOpen="true">
+ <template #icon><SearchIcon><i class="ti ti-info-circle"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts.info }}</SearchLabel></template>
+ <template v-if="infoForm.modified.value" #footer>
+ <MkFormFooter :form="infoForm"/>
+ </template>
- <div class="_gaps">
- <MkInput v-model="infoForm.state.name">
- <template #label>{{ i18n.ts.instanceName }}<span v-if="infoForm.modifiedStates.name" class="_modified">{{ i18n.ts.modified }}</span></template>
- </MkInput>
+ <div class="_gaps">
+ <SearchMarker :keywords="['name']">
+ <MkInput v-model="infoForm.state.name">
+ <template #label><SearchLabel>{{ i18n.ts.instanceName }}</SearchLabel><span v-if="infoForm.modifiedStates.name" class="_modified">{{ i18n.ts.modified }}</span></template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="infoForm.state.shortName">
- <template #label>{{ i18n.ts._serverSettings.shortName }} ({{ i18n.ts.optional }})<span v-if="infoForm.modifiedStates.shortName" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts._serverSettings.shortNameDescription }}</template>
- </MkInput>
+ <SearchMarker :keywords="['shortName']">
+ <MkInput v-model="infoForm.state.shortName">
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.shortName }}</SearchLabel> ({{ i18n.ts.optional }})<span v-if="infoForm.modifiedStates.shortName" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption><SearchText>{{ i18n.ts._serverSettings.shortNameDescription }}</SearchText></template>
+ </MkInput>
+ </SearchMarker>
- <MkTextarea v-model="infoForm.state.description">
- <template #label>{{ i18n.ts.instanceDescription }}<span v-if="infoForm.modifiedStates.description" class="_modified">{{ i18n.ts.modified }}</span></template>
- </MkTextarea>
+ <SearchMarker :keywords="['description']">
+ <MkTextarea v-model="infoForm.state.description">
+ <template #label><SearchLabel>{{ i18n.ts.instanceDescription }}</SearchLabel><span v-if="infoForm.modifiedStates.description" class="_modified">{{ i18n.ts.modified }}</span></template>
+ </MkTextarea>
+ </SearchMarker>
- <FormSplit :minWidth="300">
- <MkInput v-model="infoForm.state.maintainerName">
- <template #label>{{ i18n.ts.maintainerName }}<span v-if="infoForm.modifiedStates.maintainerName" class="_modified">{{ i18n.ts.modified }}</span></template>
- </MkInput>
+ <FormSplit :minWidth="300">
+ <SearchMarker :keywords="['maintainer', 'name']">
+ <MkInput v-model="infoForm.state.maintainerName">
+ <template #label><SearchLabel>{{ i18n.ts.maintainerName }}</SearchLabel><span v-if="infoForm.modifiedStates.maintainerName" class="_modified">{{ i18n.ts.modified }}</span></template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="infoForm.state.maintainerEmail" type="email">
- <template #label>{{ i18n.ts.maintainerEmail }}<span v-if="infoForm.modifiedStates.maintainerEmail" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #prefix><i class="ti ti-mail"></i></template>
- </MkInput>
- </FormSplit>
+ <SearchMarker :keywords="['maintainer', 'email', 'contact']">
+ <MkInput v-model="infoForm.state.maintainerEmail" type="email">
+ <template #label><SearchLabel>{{ i18n.ts.maintainerEmail }}</SearchLabel><span v-if="infoForm.modifiedStates.maintainerEmail" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #prefix><i class="ti ti-mail"></i></template>
+ </MkInput>
+ </SearchMarker>
+ </FormSplit>
- <MkInput v-model="infoForm.state.tosUrl" type="url">
- <template #label>{{ i18n.ts.tosUrl }}<span v-if="infoForm.modifiedStates.tosUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #prefix><i class="ti ti-link"></i></template>
- </MkInput>
+ <SearchMarker :keywords="['tos', 'termsOfService']">
+ <MkInput v-model="infoForm.state.tosUrl" type="url">
+ <template #label><SearchLabel>{{ i18n.ts.tosUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.tosUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #prefix><i class="ti ti-link"></i></template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="infoForm.state.privacyPolicyUrl" type="url">
- <template #label>{{ i18n.ts.privacyPolicyUrl }}<span v-if="infoForm.modifiedStates.privacyPolicyUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #prefix><i class="ti ti-link"></i></template>
- </MkInput>
+ <SearchMarker :keywords="['privacyPolicy']">
+ <MkInput v-model="infoForm.state.privacyPolicyUrl" type="url">
+ <template #label><SearchLabel>{{ i18n.ts.privacyPolicyUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.privacyPolicyUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #prefix><i class="ti ti-link"></i></template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="infoForm.state.inquiryUrl" type="url">
- <template #label>{{ i18n.ts._serverSettings.inquiryUrl }}<span v-if="infoForm.modifiedStates.inquiryUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</template>
- <template #prefix><i class="ti ti-link"></i></template>
- </MkInput>
+ <SearchMarker :keywords="['inquiry', 'contact']">
+ <MkInput v-model="infoForm.state.inquiryUrl" type="url">
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.inquiryUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.inquiryUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption><SearchText>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</SearchText></template>
+ <template #prefix><i class="ti ti-link"></i></template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="infoForm.state.repositoryUrl" type="url">
- <template #label>{{ i18n.ts.repositoryUrl }}<span v-if="infoForm.modifiedStates.repositoryUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts.repositoryUrlDescription }}</template>
- <template #prefix><i class="ti ti-link"></i></template>
- </MkInput>
+ <SearchMarker :keywords="['repository', 'url']">
+ <MkInput v-model="infoForm.state.repositoryUrl" type="url">
+ <template #label><SearchLabel>{{ i18n.ts.repositoryUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.repositoryUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption><SearchText>{{ i18n.ts.repositoryUrlDescription }}</SearchText></template>
+ <template #prefix><i class="ti ti-link"></i></template>
+ </MkInput>
+ </SearchMarker>
- <MkInfo v-if="!instance.providesTarball && !infoForm.state.repositoryUrl" warn>
- {{ i18n.ts.repositoryUrlOrTarballRequired }}
- </MkInfo>
+ <MkInfo v-if="!instance.providesTarball && !infoForm.state.repositoryUrl" warn>
+ {{ i18n.ts.repositoryUrlOrTarballRequired }}
+ </MkInfo>
- <MkInput v-model="infoForm.state.impressumUrl" type="url">
- <template #label>{{ i18n.ts.impressumUrl }}<span v-if="infoForm.modifiedStates.impressumUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts.impressumDescription }}</template>
- <template #prefix><i class="ti ti-link"></i></template>
- </MkInput>
- </div>
- </MkFolder>
+ <SearchMarker :keywords="['impressum', 'legalNotice']">
+ <MkInput v-model="infoForm.state.impressumUrl" type="url">
+ <template #label><SearchLabel>{{ i18n.ts.impressumUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.impressumUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption><SearchText>{{ i18n.ts.impressumDescription }}</SearchText></template>
+ <template #prefix><i class="ti ti-link"></i></template>
+ </MkInput>
+ </SearchMarker>
+ </div>
+ </MkFolder>
+ </SearchMarker>
+
+ <SearchMarker v-slot="slotProps" :keywords="['pinned', 'users']">
+ <MkFolder :defaultOpen="slotProps.isParentOfTarget">
+ <template #icon><SearchIcon><i class="ti ti-user-star"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts.pinnedUsers }}</SearchLabel></template>
+ <template v-if="pinnedUsersForm.modified.value" #footer>
+ <MkFormFooter :form="pinnedUsersForm"/>
+ </template>
- <MkFolder>
- <template #icon><i class="ti ti-user-star"></i></template>
- <template #label>{{ i18n.ts.pinnedUsers }}</template>
- <template v-if="pinnedUsersForm.modified.value" #footer>
- <MkFormFooter :form="pinnedUsersForm"/>
- </template>
+ <MkTextarea v-model="pinnedUsersForm.state.pinnedUsers">
+ <template #label>{{ i18n.ts.pinnedUsers }}<span v-if="pinnedUsersForm.modifiedStates.pinnedUsers" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption><SearchText>{{ i18n.ts.pinnedUsersDescription }}</SearchText></template>
+ </MkTextarea>
+ </MkFolder>
+ </SearchMarker>
- <MkTextarea v-model="pinnedUsersForm.state.pinnedUsers">
- <template #label>{{ i18n.ts.pinnedUsers }}<span v-if="pinnedUsersForm.modifiedStates.pinnedUsers" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts.pinnedUsersDescription }}</template>
- </MkTextarea>
- </MkFolder>
+ <SearchMarker v-slot="slotProps" :keywords="['serviceWorker']">
+ <MkFolder :defaultOpen="slotProps.isParentOfTarget">
+ <template #icon><SearchIcon><i class="ti ti-world-cog"></i></SearchIcon></template>
+ <template #label><SearchLabel>ServiceWorker</SearchLabel></template>
+ <template v-if="serviceWorkerForm.modified.value" #footer>
+ <MkFormFooter :form="serviceWorkerForm"/>
+ </template>
- <MkFolder>
- <template #icon><i class="ti ti-world-cog"></i></template>
- <template #label>ServiceWorker</template>
- <template v-if="serviceWorkerForm.modified.value" #footer>
- <MkFormFooter :form="serviceWorkerForm"/>
- </template>
+ <div class="_gaps">
+ <SearchMarker>
+ <MkSwitch v-model="serviceWorkerForm.state.enableServiceWorker">
+ <template #label><SearchLabel>{{ i18n.ts.enableServiceworker }}</SearchLabel><span v-if="serviceWorkerForm.modifiedStates.enableServiceWorker" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption><SearchText>{{ i18n.ts.serviceworkerInfo }}</SearchText></template>
+ </MkSwitch>
+ </SearchMarker>
- <div class="_gaps">
- <MkSwitch v-model="serviceWorkerForm.state.enableServiceWorker">
- <template #label>{{ i18n.ts.enableServiceworker }}<span v-if="serviceWorkerForm.modifiedStates.enableServiceWorker" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts.serviceworkerInfo }}</template>
- </MkSwitch>
+ <template v-if="serviceWorkerForm.state.enableServiceWorker">
+ <SearchMarker>
+ <MkInput v-model="serviceWorkerForm.state.swPublicKey">
+ <template #label><SearchLabel>Public key</SearchLabel><span v-if="serviceWorkerForm.modifiedStates.swPublicKey" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #prefix><i class="ti ti-key"></i></template>
+ </MkInput>
+ </SearchMarker>
- <template v-if="serviceWorkerForm.state.enableServiceWorker">
- <MkInput v-model="serviceWorkerForm.state.swPublicKey">
- <template #label>Public key<span v-if="serviceWorkerForm.modifiedStates.swPublicKey" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #prefix><i class="ti ti-key"></i></template>
- </MkInput>
+ <SearchMarker>
+ <MkInput v-model="serviceWorkerForm.state.swPrivateKey">
+ <template #label><SearchLabel>Private key</SearchLabel><span v-if="serviceWorkerForm.modifiedStates.swPrivateKey" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #prefix><i class="ti ti-key"></i></template>
+ </MkInput>
+ </SearchMarker>
+ </template>
+ </div>
+ </MkFolder>
+ </SearchMarker>
- <MkInput v-model="serviceWorkerForm.state.swPrivateKey">
- <template #label>Private key<span v-if="serviceWorkerForm.modifiedStates.swPrivateKey" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #prefix><i class="ti ti-key"></i></template>
- </MkInput>
- </template>
- </div>
- </MkFolder>
+ <SearchMarker v-slot="slotProps" :keywords="['ads']">
+ <MkFolder :defaultOpen="slotProps.isParentOfTarget">
+ <template #icon><SearchIcon><i class="ti ti-ad"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts._ad.adsSettings }}</SearchLabel></template>
+ <template v-if="adForm.modified.value" #footer>
+ <MkFormFooter :form="adForm"/>
+ </template>
- <MkFolder>
- <template #icon><i class="ti ti-ad"></i></template>
- <template #label>{{ i18n.ts._ad.adsSettings }}</template>
- <template v-if="adForm.modified.value" #footer>
- <MkFormFooter :form="adForm"/>
- </template>
+ <div class="_gaps">
+ <div class="_gaps_s">
+ <SearchMarker>
+ <MkInput v-model="adForm.state.notesPerOneAd" :min="0" type="number">
+ <template #label><SearchLabel>{{ i18n.ts._ad.notesPerOneAd }}</SearchLabel><span v-if="adForm.modifiedStates.notesPerOneAd" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption>{{ i18n.ts._ad.setZeroToDisable }}</template>
+ </MkInput>
+ </SearchMarker>
- <div class="_gaps">
- <div class="_gaps_s">
- <MkInput v-model="adForm.state.notesPerOneAd" :min="0" type="number">
- <template #label>{{ i18n.ts._ad.notesPerOneAd }}<span v-if="adForm.modifiedStates.notesPerOneAd" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts._ad.setZeroToDisable }}</template>
- </MkInput>
- <MkInfo v-if="adForm.state.notesPerOneAd > 0 && adForm.state.notesPerOneAd < 20" :warn="true">
- {{ i18n.ts._ad.adsTooClose }}
- </MkInfo>
- </div>
- </div>
- </MkFolder>
+ <MkInfo v-if="adForm.state.notesPerOneAd > 0 && adForm.state.notesPerOneAd < 20" :warn="true">
+ {{ i18n.ts._ad.adsTooClose }}
+ </MkInfo>
+ </div>
+ </div>
+ </MkFolder>
+ </SearchMarker>
- <MkFolder>
- <template #icon><i class="ti ti-world-search"></i></template>
- <template #label>{{ i18n.ts._urlPreviewSetting.title }}</template>
- <template v-if="urlPreviewForm.modified.value" #footer>
- <MkFormFooter :form="urlPreviewForm"/>
- </template>
+ <SearchMarker v-slot="slotProps" :keywords="['url', 'preview']">
+ <MkFolder :defaultOpen="slotProps.isParentOfTarget">
+ <template #icon><SearchIcon><i class="ti ti-world-search"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.title }}</SearchLabel></template>
+ <template v-if="urlPreviewForm.modified.value" #footer>
+ <MkFormFooter :form="urlPreviewForm"/>
+ </template>
- <div class="_gaps">
- <MkSwitch v-model="urlPreviewForm.state.urlPreviewEnabled">
- <template #label>{{ i18n.ts._urlPreviewSetting.enable }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewEnabled" class="_modified">{{ i18n.ts.modified }}</span></template>
- </MkSwitch>
+ <div class="_gaps">
+ <SearchMarker>
+ <MkSwitch v-model="urlPreviewForm.state.urlPreviewEnabled">
+ <template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.enable }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewEnabled" class="_modified">{{ i18n.ts.modified }}</span></template>
+ </MkSwitch>
+ </SearchMarker>
- <template v-if="urlPreviewForm.state.urlPreviewEnabled">
- <MkSwitch v-model="urlPreviewForm.state.urlPreviewAllowRedirect">
- <template #label>{{ i18n.ts._urlPreviewSetting.allowRedirect }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewAllowRedirect" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts._urlPreviewSetting.allowRedirectDescription }}</template>
- </MkSwitch>
+ <template v-if="urlPreviewForm.state.urlPreviewEnabled">
+ <SearchMarker :keywords="['allow', 'redirect']">
+ <MkSwitch v-model="urlPreviewForm.state.urlPreviewAllowRedirect">
+ <template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.allowRedirect }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewAllowRedirect" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption>{{ i18n.ts._urlPreviewSetting.allowRedirectDescription }}</template>
+ </MkSwitch>
+ </SearchMarker>
- <MkSwitch v-model="urlPreviewForm.state.urlPreviewRequireContentLength">
- <template #label>{{ i18n.ts._urlPreviewSetting.requireContentLength }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewRequireContentLength" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts._urlPreviewSetting.requireContentLengthDescription }}</template>
- </MkSwitch>
+ <SearchMarker :keywords="['contentLength']">
+ <MkSwitch v-model="urlPreviewForm.state.urlPreviewRequireContentLength">
+ <template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.requireContentLength }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewRequireContentLength" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption>{{ i18n.ts._urlPreviewSetting.requireContentLengthDescription }}</template>
+ </MkSwitch>
+ </SearchMarker>
- <MkInput v-model="urlPreviewForm.state.urlPreviewMaximumContentLength" type="number">
- <template #label>{{ i18n.ts._urlPreviewSetting.maximumContentLength }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewMaximumContentLength" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts._urlPreviewSetting.maximumContentLengthDescription }}</template>
- </MkInput>
+ <SearchMarker :keywords="['contentLength']">
+ <MkInput v-model="urlPreviewForm.state.urlPreviewMaximumContentLength" type="number">
+ <template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.maximumContentLength }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewMaximumContentLength" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption>{{ i18n.ts._urlPreviewSetting.maximumContentLengthDescription }}</template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="urlPreviewForm.state.urlPreviewTimeout" type="number">
- <template #label>{{ i18n.ts._urlPreviewSetting.timeout }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewTimeout" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts._urlPreviewSetting.timeoutDescription }}</template>
- </MkInput>
+ <SearchMarker :keywords="['timeout']">
+ <MkInput v-model="urlPreviewForm.state.urlPreviewTimeout" type="number">
+ <template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.timeout }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewTimeout" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption>{{ i18n.ts._urlPreviewSetting.timeoutDescription }}</template>
+ </MkInput>
+ </SearchMarker>
- <MkInput v-model="urlPreviewForm.state.urlPreviewUserAgent" type="text">
- <template #label>{{ i18n.ts._urlPreviewSetting.userAgent }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewUserAgent" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts._urlPreviewSetting.userAgentDescription }}</template>
- </MkInput>
+ <SearchMarker :keywords="['userAgent']">
+ <MkInput v-model="urlPreviewForm.state.urlPreviewUserAgent" type="text">
+ <template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.userAgent }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewUserAgent" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption>{{ i18n.ts._urlPreviewSetting.userAgentDescription }}</template>
+ </MkInput>
+ </SearchMarker>
- <div>
- <MkInput v-model="urlPreviewForm.state.urlPreviewSummaryProxyUrl" type="text">
- <template #label>{{ i18n.ts._urlPreviewSetting.summaryProxy }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewSummaryProxyUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>[{{ i18n.ts.notUsePleaseLeaveBlank }}] {{ i18n.ts._urlPreviewSetting.summaryProxyDescription }}</template>
- </MkInput>
+ <div>
+ <SearchMarker :keywords="['proxy']">
+ <MkInput v-model="urlPreviewForm.state.urlPreviewSummaryProxyUrl" type="text">
+ <template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.summaryProxy }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewSummaryProxyUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption>[{{ i18n.ts.notUsePleaseLeaveBlank }}] {{ i18n.ts._urlPreviewSetting.summaryProxyDescription }}</template>
+ </MkInput>
+ </SearchMarker>
- <div :class="$style.subCaption">
- {{ i18n.ts._urlPreviewSetting.summaryProxyDescription2 }}
- <ul style="padding-left: 20px; margin: 4px 0">
- <li>{{ i18n.ts._urlPreviewSetting.timeout }} / key:timeout</li>
- <li>{{ i18n.ts._urlPreviewSetting.maximumContentLength }} / key:contentLengthLimit</li>
- <li>{{ i18n.ts._urlPreviewSetting.requireContentLength }} / key:contentLengthRequired</li>
- <li>{{ i18n.ts._urlPreviewSetting.userAgent }} / key:userAgent</li>
- </ul>
- </div>
+ <div :class="$style.subCaption">
+ {{ i18n.ts._urlPreviewSetting.summaryProxyDescription2 }}
+ <ul style="padding-left: 20px; margin: 4px 0">
+ <li>{{ i18n.ts._urlPreviewSetting.timeout }} / key:timeout</li>
+ <li>{{ i18n.ts._urlPreviewSetting.maximumContentLength }} / key:contentLengthLimit</li>
+ <li>{{ i18n.ts._urlPreviewSetting.requireContentLength }} / key:contentLengthRequired</li>
+ <li>{{ i18n.ts._urlPreviewSetting.userAgent }} / key:userAgent</li>
+ </ul>
+ </div>
+ </div>
+ </template>
</div>
- </template>
- </div>
- </MkFolder>
+ </MkFolder>
+ </SearchMarker>
- <MkFolder>
- <template #icon><i class="ti ti-planet"></i></template>
- <template #label>{{ i18n.ts.federation }}</template>
- <template v-if="federationForm.savedState.federation === 'all'" #suffix>{{ i18n.ts.all }}</template>
- <template v-else-if="federationForm.savedState.federation === 'specified'" #suffix>{{ i18n.ts.specifyHost }}</template>
- <template v-else-if="federationForm.savedState.federation === 'none'" #suffix>{{ i18n.ts.none }}</template>
- <template v-if="federationForm.modified.value" #footer>
- <MkFormFooter :form="federationForm"/>
- </template>
+ <SearchMarker v-slot="slotProps" :keywords="['federation']">
+ <MkFolder :defaultOpen="slotProps.isParentOfTarget">
+ <template #icon><SearchIcon><i class="ti ti-planet"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts.federation }}</SearchLabel></template>
+ <template v-if="federationForm.savedState.federation === 'all'" #suffix>{{ i18n.ts.all }}</template>
+ <template v-else-if="federationForm.savedState.federation === 'specified'" #suffix>{{ i18n.ts.specifyHost }}</template>
+ <template v-else-if="federationForm.savedState.federation === 'none'" #suffix>{{ i18n.ts.none }}</template>
+ <template v-if="federationForm.modified.value" #footer>
+ <MkFormFooter :form="federationForm"/>
+ </template>
- <div class="_gaps">
- <MkRadios v-model="federationForm.state.federation">
- <template #label>{{ i18n.ts.behavior }}<span v-if="federationForm.modifiedStates.federation" class="_modified">{{ i18n.ts.modified }}</span></template>
- <option value="all">{{ i18n.ts.all }}</option>
- <option value="specified">{{ i18n.ts.specifyHost }}</option>
- <option value="none">{{ i18n.ts.none }}</option>
- </MkRadios>
+ <div class="_gaps">
+ <SearchMarker>
+ <MkRadios v-model="federationForm.state.federation">
+ <template #label><SearchLabel>{{ i18n.ts.behavior }}</SearchLabel><span v-if="federationForm.modifiedStates.federation" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <option value="all">{{ i18n.ts.all }}</option>
+ <option value="specified">{{ i18n.ts.specifyHost }}</option>
+ <option value="none">{{ i18n.ts.none }}</option>
+ </MkRadios>
+ </SearchMarker>
- <MkTextarea v-if="federationForm.state.federation === 'specified'" v-model="federationForm.state.federationHosts">
- <template #label>{{ i18n.ts.federationAllowedHosts }}<span v-if="federationForm.modifiedStates.federationHosts" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts.federationAllowedHostsDescription }}</template>
- </MkTextarea>
+ <SearchMarker :keywords="['hosts']">
+ <MkTextarea v-if="federationForm.state.federation === 'specified'" v-model="federationForm.state.federationHosts">
+ <template #label><SearchLabel>{{ i18n.ts.federationAllowedHosts }}</SearchLabel><span v-if="federationForm.modifiedStates.federationHosts" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption>{{ i18n.ts.federationAllowedHostsDescription }}</template>
+ </MkTextarea>
+ </SearchMarker>
- <MkFolder>
- <template #icon><i class="ti ti-list"></i></template>
- <template #label><SearchLabel>{{ i18n.ts._serverSettings.deliverSuspendedSoftware }}</SearchLabel></template>
- <template #footer>
- <div class="_buttons">
- <MkButton @click="federationForm.state.deliverSuspendedSoftware.push({software: '', versionRange: ''})"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
- </div>
- </template>
+ <SearchMarker :keywords="['suspended', 'software']">
+ <MkFolder>
+ <template #icon><i class="ti ti-list"></i></template>
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.deliverSuspendedSoftware }}</SearchLabel></template>
+ <template #footer>
+ <div class="_buttons">
+ <MkButton @click="federationForm.state.deliverSuspendedSoftware.push({software: '', versionRange: ''})"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
+ </div>
+ </template>
- <div :class="$style.metadataRoot" class="_gaps_s">
- <MkInfo>{{ i18n.ts._serverSettings.deliverSuspendedSoftwareDescription }}</MkInfo>
- <div v-for="(element, index) in federationForm.state.deliverSuspendedSoftware" :key="index" v-panel :class="$style.fieldDragItem">
- <button class="_button" :class="$style.dragItemRemove" @click="federationForm.state.deliverSuspendedSoftware.splice(index, 1)"><i class="ti ti-x"></i></button>
- <div :class="$style.dragItemForm">
- <FormSplit :minWidth="200">
- <MkInput v-model="element.software" small :placeholder="i18n.ts.softwareName">
- </MkInput>
- <MkInput v-model="element.versionRange" small :placeholder="i18n.ts.version">
- </MkInput>
- </FormSplit>
- </div>
- </div>
- </div>
- </MkFolder>
+ <div :class="$style.metadataRoot" class="_gaps_s">
+ <MkInfo>{{ i18n.ts._serverSettings.deliverSuspendedSoftwareDescription }}</MkInfo>
+ <div v-for="(element, index) in federationForm.state.deliverSuspendedSoftware" :key="index" v-panel :class="$style.fieldDragItem">
+ <button class="_button" :class="$style.dragItemRemove" @click="federationForm.state.deliverSuspendedSoftware.splice(index, 1)"><i class="ti ti-x"></i></button>
+ <div :class="$style.dragItemForm">
+ <FormSplit :minWidth="200">
+ <MkInput v-model="element.software" small :placeholder="i18n.ts.softwareName">
+ </MkInput>
+ <MkInput v-model="element.versionRange" small :placeholder="i18n.ts.version">
+ </MkInput>
+ </FormSplit>
+ </div>
+ </div>
+ </div>
+ </MkFolder>
+ </SearchMarker>
- <MkSwitch v-model="federationForm.state.signToActivityPubGet">
- <template #label>{{ i18n.ts._serverSettings.signToActivityPubGet }}<span v-if="federationForm.modifiedStates.signToActivityPubGet" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts._serverSettings.signToActivityPubGet_description }}</template>
- </MkSwitch>
+ <SearchMarker :keywords="['sign', 'get']">
+ <MkSwitch v-model="federationForm.state.signToActivityPubGet">
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.signToActivityPubGet }}</SearchLabel><span v-if="federationForm.modifiedStates.signToActivityPubGet" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption><SearchText>{{ i18n.ts._serverSettings.signToActivityPubGet_description }}</SearchText></template>
+ </MkSwitch>
+ </SearchMarker>
- <MkSwitch v-model="federationForm.state.proxyRemoteFiles">
- <template #label>{{ i18n.ts._serverSettings.proxyRemoteFiles }}<span v-if="federationForm.modifiedStates.proxyRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts._serverSettings.proxyRemoteFiles_description }}</template>
- </MkSwitch>
+ <SearchMarker :keywords="['proxy', 'remote', 'files']">
+ <MkSwitch v-model="federationForm.state.proxyRemoteFiles">
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.proxyRemoteFiles }}</SearchLabel><span v-if="federationForm.modifiedStates.proxyRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption><SearchText>{{ i18n.ts._serverSettings.proxyRemoteFiles_description }}</SearchText></template>
+ </MkSwitch>
+ </SearchMarker>
- <MkSwitch v-model="federationForm.state.allowExternalApRedirect">
- <template #label>{{ i18n.ts._serverSettings.allowExternalApRedirect }}<span v-if="federationForm.modifiedStates.allowExternalApRedirect" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>
- <div>{{ i18n.ts._serverSettings.allowExternalApRedirect_description }}</div>
- <div>{{ i18n.ts.needToRestartServerToApply }}</div>
- </template>
- </MkSwitch>
+ <SearchMarker :keywords="['allow', 'external', 'redirect']">
+ <MkSwitch v-model="federationForm.state.allowExternalApRedirect">
+ <template #label><SearchLabel>{{ i18n.ts._serverSettings.allowExternalApRedirect }}</SearchLabel><span v-if="federationForm.modifiedStates.allowExternalApRedirect" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption>
+ <div><SearchText>{{ i18n.ts._serverSettings.allowExternalApRedirect_description }}</SearchText></div>
+ <div>{{ i18n.ts.needToRestartServerToApply }}</div>
+ </template>
+ </MkSwitch>
+ </SearchMarker>
- <MkSwitch v-model="federationForm.state.cacheRemoteFiles">
- <template #label>{{ i18n.ts.cacheRemoteFiles }}<span v-if="federationForm.modifiedStates.cacheRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}{{ i18n.ts.youCanCleanRemoteFilesCache }}</template>
- </MkSwitch>
+ <SearchMarker :keywords="['cache', 'remote', 'files']">
+ <MkSwitch v-model="federationForm.state.cacheRemoteFiles">
+ <template #label><SearchLabel>{{ i18n.ts.cacheRemoteFiles }}</SearchLabel><span v-if="federationForm.modifiedStates.cacheRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption><SearchText>{{ i18n.ts.cacheRemoteFilesDescription }}</SearchText>{{ i18n.ts.youCanCleanRemoteFilesCache }}</template>
+ </MkSwitch>
+ </SearchMarker>
- <template v-if="federationForm.state.cacheRemoteFiles">
- <MkSwitch v-model="federationForm.state.cacheRemoteSensitiveFiles">
- <template #label>{{ i18n.ts.cacheRemoteSensitiveFiles }}<span v-if="federationForm.modifiedStates.cacheRemoteSensitiveFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
- <template #caption>{{ i18n.ts.cacheRemoteSensitiveFilesDescription }}</template>
- </MkSwitch>
- </template>
- </div>
- </MkFolder>
+ <template v-if="federationForm.state.cacheRemoteFiles">
+ <SearchMarker :keywords="['cache', 'remote', 'sensitive', 'files']">
+ <MkSwitch v-model="federationForm.state.cacheRemoteSensitiveFiles">
+ <template #label><SearchLabel>{{ i18n.ts.cacheRemoteSensitiveFiles }}</SearchLabel><span v-if="federationForm.modifiedStates.cacheRemoteSensitiveFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
+ <template #caption><SearchText>{{ i18n.ts.cacheRemoteSensitiveFilesDescription }}</SearchText></template>
+ </MkSwitch>
+ </SearchMarker>
+ </template>
+ </div>
+ </MkFolder>
+ </SearchMarker>
- <MkFolder>
- <template #icon><i class="ti ti-ghost"></i></template>
- <template #label>{{ i18n.ts.proxyAccount }}</template>
- <template v-if="proxyAccountForm.modified.value" #footer>
- <MkFormFooter :form="proxyAccountForm"/>
- </template>
+ <SearchMarker v-slot="slotProps" :keywords="['proxy', 'account']">
+ <MkFolder :defaultOpen="slotProps.isParentOfTarget">
+ <template #icon><SearchIcon><i class="ti ti-ghost"></i></SearchIcon></template>
+ <template #label><SearchLabel>{{ i18n.ts.proxyAccount }}</SearchLabel></template>
+ <template v-if="proxyAccountForm.modified.value" #footer>
+ <MkFormFooter :form="proxyAccountForm"/>
+ </template>
- <div class="_gaps">
- <MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo>
+ <div class="_gaps">
+ <MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo>
- <MkTextarea v-model="proxyAccountForm.state.description" :max="500" tall mfmAutocomplete :mfmPreview="true">
- <template #label>{{ i18n.ts._profile.description }}</template>
- <template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template>
- </MkTextarea>
- </div>
- </MkFolder>
- </div>
+ <SearchMarker :keywords="['description']">
+ <MkTextarea v-model="proxyAccountForm.state.description" :max="500" tall mfmAutocomplete :mfmPreview="true">
+ <template #label><SearchLabel>{{ i18n.ts._profile.description }}</SearchLabel></template>
+ <template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template>
+ </MkTextarea>
+ </SearchMarker>
+ </div>
+ </MkFolder>
+ </SearchMarker>
+
+ <MkButton primary @click="openSetupWizard">
+ Open setup wizard
+ </MkButton>
+ </div>
+ </SearchMarker>
</div>
</PageWithHeader>
</template>
@@ -425,6 +506,20 @@ const proxyAccountForm = useForm({
fetchInstance(true);
});
+async function openSetupWizard() {
+ const { canceled } = await os.confirm({
+ type: 'warning',
+ title: i18n.ts._serverSettings.restartServerSetupWizardConfirm_title,
+ text: i18n.ts._serverSettings.restartServerSetupWizardConfirm_text,
+ });
+ if (canceled) return;
+
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkServerSetupWizardDialog.vue').then(x => x.default), {
+ }, {
+ closed: () => dispose(),
+ });
+}
+
const headerTabs = computed(() => []);
definePage(() => ({
diff --git a/packages/frontend/src/pages/admin/system-webhook.vue b/packages/frontend/src/pages/admin/system-webhook.vue
index d5402f608c..0fd255d5f6 100644
--- a/packages/frontend/src/pages/admin/system-webhook.vue
+++ b/packages/frontend/src/pages/admin/system-webhook.vue
@@ -6,17 +6,21 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
<div class="_spacer" style="--MI_SPACER-w: 900px;">
- <div class="_gaps_m">
- <MkButton primary @click="onCreateWebhookClicked">
- <i class="ti ti-plus"></i> {{ i18n.ts._webhookSettings.createWebhook }}
- </MkButton>
+ <SearchMarker path="/admin/system-webhook" label="SystemWebhook" :keywords="['webhook']" icon="ti ti-webhook">
+ <div class="_gaps_m">
+ <SearchMarker>
+ <MkButton primary @click="onCreateWebhookClicked">
+ <i class="ti ti-plus"></i> <SearchLabel>{{ i18n.ts._webhookSettings.createWebhook }}</SearchLabel>
+ </MkButton>
+ </SearchMarker>
- <FormSection>
- <div class="_gaps">
- <XItem v-for="item in webhooks" :key="item.id" :entity="item" @edit="onEditButtonClicked" @delete="onDeleteButtonClicked"/>
- </div>
- </FormSection>
- </div>
+ <FormSection>
+ <div class="_gaps">
+ <XItem v-for="item in webhooks" :key="item.id" :entity="item" @edit="onEditButtonClicked" @delete="onDeleteButtonClicked"/>
+ </div>
+ </FormSection>
+ </div>
+ </SearchMarker>
</div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/announcement.vue b/packages/frontend/src/pages/announcement.vue
index f9b870eda1..0bcfd28f67 100644
--- a/packages/frontend/src/pages/announcement.vue
+++ b/packages/frontend/src/pages/announcement.vue
@@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton primary @click="read(announcement)"><i class="ti ti-check"></i> {{ i18n.ts.gotIt }}</MkButton>
</div>
</div>
- <MkError v-else-if="error" @retry="fetch()"/>
+ <MkError v-else-if="error" @retry="_fetch_()"/>
<MkLoading v-else/>
</Transition>
</div>
@@ -66,7 +66,7 @@ const announcement = ref<Misskey.entities.Announcement | null>(null);
const error = ref<any>(null);
const path = computed(() => props.announcementId);
-function fetch() {
+function _fetch_() {
announcement.value = null;
misskeyApi('announcements/show', {
announcementId: props.announcementId,
@@ -96,7 +96,7 @@ async function read(target: Misskey.entities.Announcement): Promise<void> {
}
}
-watch(() => path.value, fetch, { immediate: true });
+watch(() => path.value, _fetch_, { immediate: true });
const headerActions = computed(() => []);
diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue
index 7d2393dba5..9030fa0e29 100644
--- a/packages/frontend/src/pages/antenna-timeline.vue
+++ b/packages/frontend/src/pages/antenna-timeline.vue
@@ -37,21 +37,12 @@ const props = defineProps<{
const antenna = ref<Misskey.entities.Antenna | null>(null);
const tlEl = useTemplateRef('tlEl');
-async function timetravel() {
- const { canceled, result: date } = await os.inputDate({
- title: i18n.ts.date,
- });
- if (canceled) return;
-
- tlEl.value.timetravel(date);
-}
-
function settings() {
- router.push(`/my/antennas/${props.antennaId}`);
-}
-
-function focus() {
- tlEl.value.focus();
+ router.push('/my/antennas/:antennaId', {
+ params: {
+ antennaId: props.antennaId,
+ },
+ });
}
watch(() => props.antennaId, async () => {
@@ -61,10 +52,6 @@ watch(() => props.antennaId, async () => {
}, { immediate: true });
const headerActions = computed(() => antenna.value ? [{
- icon: 'ti ti-calendar-time',
- text: i18n.ts.jumpToSpecifiedDate,
- handler: timetravel,
-}, {
icon: 'ti ti-settings',
text: i18n.ts.settings,
handler: settings,
diff --git a/packages/frontend/src/pages/api-console.vue b/packages/frontend/src/pages/api-console.vue
index 7571696b84..f436fc72fa 100644
--- a/packages/frontend/src/pages/api-console.vue
+++ b/packages/frontend/src/pages/api-console.vue
@@ -68,6 +68,11 @@ function send() {
function onEndpointChange() {
misskeyApi('endpoint', { endpoint: endpoint.value }, withCredential.value ? undefined : null).then(resp => {
+ if (resp == null) {
+ body.value = '{}';
+ return;
+ }
+
const endpointBody = {};
for (const p of resp.params) {
endpointBody[p.name] =
diff --git a/packages/frontend/src/pages/auth.form.vue b/packages/frontend/src/pages/auth.form.vue
index 5b1fd1a386..1a0c9b36c4 100644
--- a/packages/frontend/src/pages/auth.form.vue
+++ b/packages/frontend/src/pages/auth.form.vue
@@ -44,11 +44,13 @@ const name = computed(() => {
});
function cancel() {
- misskeyApi('auth/deny', {
- token: props.session.token,
- }).then(() => {
- emit('denied');
- });
+ //misskeyApi('auth/deny', {
+ // token: props.session.token,
+ //}).then(() => {
+ // emit('denied');
+ //});
+
+ emit('denied');
}
function accept() {
diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue
index ede0f268ee..7e13d0ab36 100644
--- a/packages/frontend/src/pages/auth.vue
+++ b/packages/frontend/src/pages/auth.vue
@@ -75,14 +75,15 @@ onMounted(async () => {
if (!$i) return;
try {
- session.value = await misskeyApi('auth/session/show', {
+ const result = await misskeyApi('auth/session/show', {
token: props.token,
});
+ session.value = result;
// æ—¢ã«é€£æºã—ã¦ã„ãŸå ´åˆ
- if (session.value.app.isAuthorized) {
+ if (result.app.isAuthorized) {
await misskeyApi('auth/accept', {
- token: session.value.token,
+ token: result.token,
});
accepted();
} else {
diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue
index 72281ea882..ce26a26109 100644
--- a/packages/frontend/src/pages/channel-editor.vue
+++ b/packages/frontend/src/pages/channel-editor.vue
@@ -92,7 +92,7 @@ const props = defineProps<{
}>();
const channel = ref<Misskey.entities.Channel | null>(null);
-const name = ref<string | null>(null);
+const name = ref<string>('');
const description = ref<string | null>(null);
const bannerUrl = ref<string | null>(null);
const bannerId = ref<string | null>(null);
@@ -114,20 +114,22 @@ watch(() => bannerId.value, async () => {
async function fetchChannel() {
if (props.channelId == null) return;
- channel.value = await misskeyApi('channels/show', {
+ const result = await misskeyApi('channels/show', {
channelId: props.channelId,
});
- name.value = channel.value.name;
- description.value = channel.value.description;
- bannerId.value = channel.value.bannerId;
- bannerUrl.value = channel.value.bannerUrl;
- isSensitive.value = channel.value.isSensitive;
- pinnedNotes.value = channel.value.pinnedNoteIds.map(id => ({
+ name.value = result.name;
+ description.value = result.description;
+ bannerId.value = result.bannerId;
+ bannerUrl.value = result.bannerUrl;
+ isSensitive.value = result.isSensitive;
+ pinnedNotes.value = result.pinnedNoteIds.map(id => ({
id,
}));
- color.value = channel.value.color;
- allowRenoteToExternal.value = channel.value.allowRenoteToExternal;
+ color.value = result.color;
+ allowRenoteToExternal.value = result.allowRenoteToExternal;
+
+ channel.value = result;
}
fetchChannel();
@@ -154,29 +156,36 @@ function save() {
name: name.value,
description: description.value,
bannerId: bannerId.value,
- pinnedNoteIds: pinnedNotes.value.map(x => x.id),
color: color.value,
isSensitive: isSensitive.value,
allowRenoteToExternal: allowRenoteToExternal.value,
- };
+ } satisfies Misskey.entities.ChannelsCreateRequest;
- if (props.channelId) {
- params.channelId = props.channelId;
- os.apiWithDialog('channels/update', params);
+ if (props.channelId != null) {
+ os.apiWithDialog('channels/update', {
+ ...params,
+ channelId: props.channelId,
+ pinnedNoteIds: pinnedNotes.value.map(x => x.id),
+ });
} else {
os.apiWithDialog('channels/create', params).then(created => {
- router.push(`/channels/${created.id}`);
+ router.push('/channels/:channelId', {
+ params: {
+ channelId: created.id,
+ },
+ });
});
}
}
async function archive() {
+ if (props.channelId == null) return;
+
const { canceled } = await os.confirm({
type: 'warning',
title: i18n.tsx.channelArchiveConfirmTitle({ name: name.value }),
text: i18n.ts.channelArchiveConfirmDescription,
});
-
if (canceled) return;
misskeyApi('channels/update', {
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue
index 116aabaee2..9e1608f24d 100644
--- a/packages/frontend/src/pages/channel.vue
+++ b/packages/frontend/src/pages/channel.vue
@@ -112,7 +112,7 @@ const favorited = ref(false);
const searchQuery = ref('');
const searchPaginator = shallowRef();
const searchKey = ref('');
-const featuredPaginator = markRaw(new Paginator('channels/featured', {
+const featuredPaginator = markRaw(new Paginator('notes/featured', {
limit: 10,
computedParams: computed(() => ({
channelId: props.channelId,
@@ -131,6 +131,8 @@ watch(() => props.channelId, async () => {
channel.value = await misskeyApi('channels/show', {
channelId: props.channelId,
});
+ if (channel.value == null) return; // TSを黙らã™ãŸã‚
+
favorited.value = channel.value.isFavorited ?? false;
if (favorited.value || channel.value.isFollowing) {
tab.value = 'timeline';
@@ -147,7 +149,11 @@ watch(() => props.channelId, async () => {
}, { immediate: true });
function edit() {
- router.push(`/channels/${channel.value?.id}/edit`);
+ router.push('/channels/:channelId/edit', {
+ params: {
+ channelId: props.channelId,
+ },
+ });
}
function openPostForm() {
diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue
index 324e0c573a..1e7301d06d 100644
--- a/packages/frontend/src/pages/channels.vue
+++ b/packages/frontend/src/pages/channels.vue
@@ -110,6 +110,11 @@ async function search() {
const type = searchType.value.toString().trim();
+ if (type !== 'nameAndDescription' && type !== 'nameOnly') {
+ console.error(`Unrecognized search type: ${type}`);
+ return;
+ }
+
channelPaginator.value = markRaw(new Paginator('channels/search', {
limit: 10,
params: {
diff --git a/packages/frontend/src/pages/chat/XMessage.vue b/packages/frontend/src/pages/chat/XMessage.vue
index c5e8d0fdb6..613c4e4dcc 100644
--- a/packages/frontend/src/pages/chat/XMessage.vue
+++ b/packages/frontend/src/pages/chat/XMessage.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="[$style.root, { [$style.isMe]: isMe }]">
- <MkAvatar :class="$style.avatar" :user="message.fromUser!" :link="!isMe" :preview="false"/>
+ <MkAvatar :class="[$style.avatar, prefer.s.useStickyIcons ? $style.useSticky : null]" :user="message.fromUser!" :link="!isMe" :preview="false"/>
<div :class="[$style.body, message.file != null ? $style.fullWidth : null]" @contextmenu.stop="onContextmenu">
<div :class="$style.header"><MkUserName v-if="!isMe && prefer.s['chat.showSenderName'] && message.fromUser != null" :user="message.fromUser"/></div>
<MkFukidashi :class="$style.fukidashi" :tail="isMe ? 'right' : 'left'" :fullWidth="message.file != null" :accented="isMe">
@@ -231,11 +231,14 @@ function showMenu(ev: MouseEvent, contextmenu = false) {
}
.avatar {
- position: sticky;
- top: calc(16px + var(--MI-stickyTop, 0px));
display: block;
width: 50px;
height: 50px;
+
+ &.useSticky {
+ position: sticky;
+ top: calc(16px + var(--MI-stickyTop, 0px));
+ }
}
@container (max-width: 450px) {
diff --git a/packages/frontend/src/pages/chat/home.home.vue b/packages/frontend/src/pages/chat/home.home.vue
index a0853fb0c9..756bf8a342 100644
--- a/packages/frontend/src/pages/chat/home.home.vue
+++ b/packages/frontend/src/pages/chat/home.home.vue
@@ -86,7 +86,11 @@ function start(ev: MouseEvent) {
async function startUser() {
// TODO: localOnly ã¯é€£åˆã«å¯¾å¿œã—ãŸã‚‰æ¶ˆã™
os.selectUser({ localOnly: true }).then(user => {
- router.push(`/chat/user/${user.id}`);
+ router.push('/chat/user/:userId', {
+ params: {
+ userId: user.id,
+ }
+ });
});
}
@@ -101,7 +105,11 @@ async function createRoom() {
name: result,
});
- router.push(`/chat/room/${room.id}`);
+ router.push('/chat/room/:roomId', {
+ params: {
+ roomId: room.id,
+ }
+ });
}
async function search() {
diff --git a/packages/frontend/src/pages/chat/home.invitations.vue b/packages/frontend/src/pages/chat/home.invitations.vue
index 3cbe186e9d..19d57ea205 100644
--- a/packages/frontend/src/pages/chat/home.invitations.vue
+++ b/packages/frontend/src/pages/chat/home.invitations.vue
@@ -61,7 +61,11 @@ async function join(invitation: Misskey.entities.ChatRoomInvitation) {
roomId: invitation.room.id,
});
- router.push(`/chat/room/${invitation.room.id}`);
+ router.push('/chat/room/:roomId', {
+ params: {
+ roomId: invitation.room.id,
+ },
+ });
}
async function ignore(invitation: Misskey.entities.ChatRoomInvitation) {
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index 1cb07017e9..0f306896c9 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -312,6 +312,7 @@ const headerActions = computed(() => [{
handler: add,
}, {
icon: 'ti ti-dots',
+ text: i18n.ts.more,
handler: menu,
}]);
diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue
index 1def215afc..e3cc1d988e 100644
--- a/packages/frontend/src/pages/drive.file.info.vue
+++ b/packages/frontend/src/pages/drive.file.info.vue
@@ -105,7 +105,7 @@ const folderHierarchy = computed(() => {
});
const isImage = computed(() => file.value?.type.startsWith('image/'));
-async function fetch() {
+async function _fetch_() {
fetching.value = true;
file.value = await misskeyApi('drive/files/show', {
@@ -119,7 +119,7 @@ async function fetch() {
}
function postThis() {
- if (!file.value) return;
+ if (file.value == null) return;
os.post({
initialFiles: [file.value],
@@ -127,26 +127,28 @@ function postThis() {
}
function move() {
- if (!file.value) return;
+ if (file.value == null) return;
+
+ const f = file.value;
selectDriveFolder(null).then(folder => {
misskeyApi('drive/files/update', {
- fileId: file.value.id,
+ fileId: f.id,
folderId: folder[0] ? folder[0].id : null,
}).then(async () => {
- await fetch();
+ await _fetch_();
});
});
}
function toggleSensitive() {
- if (!file.value) return;
+ if (file.value == null) return;
os.apiWithDialog('drive/files/update', {
fileId: file.value.id,
isSensitive: !file.value.isSensitive,
}).then(async () => {
- await fetch();
+ await _fetch_();
}).catch(err => {
os.alert({
type: 'error',
@@ -157,7 +159,9 @@ function toggleSensitive() {
}
function rename() {
- if (!file.value) return;
+ if (file.value == null) return;
+
+ const f = file.value;
os.inputText({
title: i18n.ts.renameFile,
@@ -166,16 +170,18 @@ function rename() {
}).then(({ canceled, result: name }) => {
if (canceled) return;
os.apiWithDialog('drive/files/update', {
- fileId: file.value.id,
+ fileId: f.id,
name: name,
}).then(async () => {
- await fetch();
+ await _fetch_();
});
});
}
async function describe() {
- if (!file.value) return;
+ if (file.value == null) return;
+
+ const f = file.value;
const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkFileCaptionEditWindow.vue').then(x => x.default), {
default: file.value.comment ?? '',
@@ -183,10 +189,10 @@ async function describe() {
}, {
done: caption => {
os.apiWithDialog('drive/files/update', {
- fileId: file.value.id,
+ fileId: f.id,
comment: caption.length === 0 ? null : caption,
}).then(async () => {
- await fetch();
+ await _fetch_();
});
},
closed: () => dispose(),
@@ -194,7 +200,7 @@ async function describe() {
}
async function deleteFile() {
- if (!file.value) return;
+ if (file.value == null) return;
const { canceled } = await os.confirm({
type: 'warning',
@@ -212,7 +218,7 @@ async function deleteFile() {
}
onMounted(async () => {
- await fetch();
+ await _fetch_();
});
</script>
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index b4fc4a46d9..201ce003f0 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -156,12 +156,9 @@ async function done() {
isSensitive: isSensitive.value,
localOnly: localOnly.value,
roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.value.map(x => x.id),
+ fileId: file.value ? file.value.id : undefined,
};
- if (file.value) {
- params.fileId = file.value.id;
- }
-
if (props.emoji) {
await os.apiWithDialog('admin/emoji/update', {
id: props.emoji.id,
@@ -177,7 +174,12 @@ async function done() {
windowEl.value?.close();
} else {
- const created = await os.apiWithDialog('admin/emoji/add', params);
+ if (params.fileId == null) return;
+
+ const created = await os.apiWithDialog('admin/emoji/add', {
+ ...params,
+ fileId: params.fileId, // TSを黙らã™ãŸã‚
+ });
emit('done', {
created: created,
diff --git a/packages/frontend/src/pages/explore.users.vue b/packages/frontend/src/pages/explore.users.vue
index 72f2a6813c..08f9f5e582 100644
--- a/packages/frontend/src/pages/explore.users.vue
+++ b/packages/frontend/src/pages/explore.users.vue
@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</div>
<div v-else>
- <MkFoldableSection ref="tagsEl" :foldable="true" :expanded="false" class="_margin">
+ <MkFoldableSection :foldable="true" :expanded="false" class="_margin">
<template #header><i class="ti ti-hash ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularTags }}</template>
<div>
@@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkFoldableSection>
- <MkFoldableSection v-if="tag != null" :key="`${tag}`" class="_margin">
+ <MkFoldableSection v-if="tagUsersPaginator != null" :key="`${tag}`" class="_margin">
<template #header><i class="ti ti-hash ti-fw" style="margin-right: 0.5em;"></i>{{ tag }}</template>
<MkUserList :paginator="tagUsersPaginator"/>
</MkFoldableSection>
@@ -78,22 +78,17 @@ const props = defineProps<{
}>();
const origin = ref('local');
-const tagsEl = useTemplateRef('tagsEl');
const tagsLocal = ref<Misskey.entities.Hashtag[]>([]);
const tagsRemote = ref<Misskey.entities.Hashtag[]>([]);
-watch(() => props.tag, () => {
- if (tagsEl.value) tagsEl.value.toggleContent(props.tag == null);
-});
-
-const tagUsersPaginator = markRaw(new Paginator('hashtags/users', {
+const tagUsersPaginator = props.tag != null ? markRaw(new Paginator('hashtags/users', {
limit: 30,
params: {
tag: props.tag,
origin: 'combined',
sort: '+follower',
},
-}));
+})) : null;
const pinnedUsersPaginator = markRaw(new Paginator('pinned-users', {
noPaging: true,
diff --git a/packages/frontend/src/pages/explore.vue b/packages/frontend/src/pages/explore.vue
index c4f6ddc33e..6416816e6c 100644
--- a/packages/frontend/src/pages/explore.vue
+++ b/packages/frontend/src/pages/explore.vue
@@ -26,18 +26,12 @@ import { definePage } from '@/page.js';
import { i18n } from '@/i18n.js';
const props = withDefaults(defineProps<{
- tag?: string;
initialTab?: string;
}>(), {
initialTab: 'featured',
});
const tab = ref(props.initialTab);
-const tagsEl = useTemplateRef('tagsEl');
-
-watch(() => props.tag, () => {
- if (tagsEl.value) tagsEl.value.toggleContent(props.tag == null);
-});
const headerActions = computed(() => []);
diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index 4386209f7c..81b9d1cead 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -127,7 +127,7 @@ var results = []
// ã©ã‚Œã ã‘å·»ãæˆ»ã—ã¦ã„ã‚‹ã‹
var cursor = 0
-@do() {
+@main() {
if (cursor != 0) {
results = results.slice(0, (cursor + 1))
cursor = 0
@@ -175,7 +175,7 @@ var cursor = 0
onClick: forward
}, {
text: "引ãç›´ã™"
- onClick: do
+ onClick: main
}]
})
Ui:C:postFormButton({
@@ -191,7 +191,7 @@ var cursor = 0
])
}
-do()
+main()
`;
const PRESET_QUIZ = `/// @ ${AISCRIPT_VERSION}
@@ -383,7 +383,7 @@ if (props.id) {
const title = ref(flash.value?.title ?? 'New Play');
const summary = ref(flash.value?.summary ?? '');
-const permissions = ref(flash.value?.permissions ?? []);
+const permissions = ref([]); // not implemented yet
const visibility = ref<'private' | 'public'>(flash.value?.visibility ?? 'public');
const script = ref(flash.value?.script ?? PRESET_DEFAULT);
@@ -412,9 +412,9 @@ function selectPreset(ev: MouseEvent) {
}
async function save() {
- if (flash.value) {
+ if (flash.value != null) {
os.apiWithDialog('flash/update', {
- flashId: props.id,
+ flashId: flash.value.id,
title: title.value,
summary: summary.value,
permissions: permissions.value,
@@ -429,7 +429,11 @@ async function save() {
script: script.value,
visibility: visibility.value,
});
- router.push('/play/' + created.id + '/edit');
+ router.push('/play/:id/edit', {
+ params: {
+ id: created.id,
+ },
+ });
}
}
@@ -444,6 +448,8 @@ function show() {
}
async function del() {
+ if (flash.value == null) return;
+
const { canceled } = await os.confirm({
type: 'warning',
text: i18n.tsx.deleteAreYouSure({ x: flash.value.title }),
@@ -451,7 +457,7 @@ async function del() {
if (canceled) return;
await os.apiWithDialog('flash/delete', {
- flashId: props.id,
+ flashId: flash.value.id,
});
router.push('/play');
}
@@ -464,6 +470,7 @@ definePage(() => ({
title: flash.value ? `${i18n.ts._play.edit}: ${flash.value.title}` : i18n.ts._play.new,
}));
</script>
+
<style lang="scss" module>
.footer {
backdrop-filter: var(--MI-blur, blur(15px));
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index 1c9cb92bc2..f318a9f817 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -63,11 +63,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, onDeactivated, onUnmounted, ref, watch, shallowRef, defineAsyncComponent } from 'vue';
import * as Misskey from 'misskey-js';
-import { Interpreter, Parser, values } from '@syuilo/aiscript';
import { url } from '@@/js/config.js';
import type { Ref } from 'vue';
import type { AsUiComponent, AsUiRoot } from '@/aiscript/ui.js';
import type { MenuItem } from '@/types/menu.js';
+import type { Interpreter } from '@syuilo/aiscript';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
@@ -180,8 +180,6 @@ async function unlike() {
watch(() => props.id, fetchFlash, { immediate: true });
-const parser = new Parser();
-
const started = ref(false);
const aiscript = shallowRef<Interpreter | null>(null);
const root = ref<AsUiRoot>();
@@ -196,9 +194,15 @@ async function run() {
if (aiscript.value) aiscript.value.abort();
if (!flash.value) return;
+ const isLegacy = !flash.value.script.replaceAll(' ', '').startsWith('///@1.0.0');
+
+ const { Interpreter, Parser, values } = isLegacy ? (await import('@syuilo/aiscript-0-19-0') as any) : await import('@syuilo/aiscript');
+
+ const parser = new Parser();
+
components.value = [];
- aiscript.value = new Interpreter({
+ const interpreter = new Interpreter({
...createAiScriptEnv({
storageKey: 'flash:' + flash.value.id,
}),
@@ -217,6 +221,8 @@ async function run() {
},
});
+ aiscript.value = interpreter;
+
let ast;
try {
ast = parser.parse(flash.value.script);
@@ -228,8 +234,8 @@ async function run() {
return;
}
try {
- await aiscript.value.exec(ast);
- } catch (err) {
+ await interpreter.exec(ast);
+ } catch (err: any) {
os.alert({
type: 'error',
title: 'AiScript Error',
@@ -366,6 +372,7 @@ definePage(() => ({
> .items {
display: flex;
+ flex-wrap: wrap;
justify-content: center;
gap: 12px;
padding: 16px;
diff --git a/packages/frontend/src/pages/gallery/edit.vue b/packages/frontend/src/pages/gallery/edit.vue
index 9c0078e15a..3fd462e0b9 100644
--- a/packages/frontend/src/pages/gallery/edit.vue
+++ b/packages/frontend/src/pages/gallery/edit.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkTextarea>
<div class="_gaps_s">
- <div v-for="file in files" :key="file.id" class="wqugxsfx" :style="{ backgroundImage: file ? `url(${ file.thumbnailUrl })` : null }">
+ <div v-for="file in files" :key="file.id" class="wqugxsfx" :style="{ backgroundImage: file ? `url(${ file.thumbnailUrl })` : '' }">
<div class="name">{{ file.name }}</div>
<button v-tooltip="i18n.ts.remove" class="remove _button" @click="remove(file)"><i class="ti ti-x"></i></button>
</div>
@@ -85,7 +85,11 @@ async function save() {
fileIds: files.value.map(file => file.id),
isSensitive: isSensitive.value,
});
- router.push(`/gallery/${props.postId}`);
+ router.push('/gallery/:postId', {
+ params: {
+ postId: props.postId,
+ },
+ });
} else {
const created = await os.apiWithDialog('gallery/posts/create', {
title: title.value,
@@ -93,7 +97,11 @@ async function save() {
fileIds: files.value.map(file => file.id),
isSensitive: isSensitive.value,
});
- router.push(`/gallery/${created.id}`);
+ router.push('/gallery/:postId', {
+ params: {
+ postId: created.id,
+ },
+ });
}
}
diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue
index d02b72dd99..eab435c002 100644
--- a/packages/frontend/src/pages/gallery/post.vue
+++ b/packages/frontend/src/pages/gallery/post.vue
@@ -150,7 +150,11 @@ async function unlike() {
}
function edit() {
- router.push(`/gallery/${post.value.id}/edit`);
+ router.push('/gallery/:postId/edit', {
+ params: {
+ postId: props.postId,
+ },
+ });
}
async function reportAbuse() {
diff --git a/packages/frontend/src/pages/install-extensions.vue b/packages/frontend/src/pages/install-extensions.vue
index 1b3c6616cc..cad3b2a00a 100644
--- a/packages/frontend/src/pages/install-extensions.vue
+++ b/packages/frontend/src/pages/install-extensions.vue
@@ -80,7 +80,7 @@ function close_(): void {
}
}
-async function fetch() {
+async function _fetch_() {
if (!url.value || !hash.value) {
errorKV.value = {
title: i18n.ts._externalResourceInstaller._errors._invalidParams.title,
@@ -161,7 +161,7 @@ async function fetch() {
},
raw: res.data,
};
- } catch (err) {
+ } catch (err: any) {
switch (err.message.toLowerCase()) {
case 'this theme is already installed':
errorKV.value = {
@@ -229,7 +229,7 @@ async function install() {
const urlParams = new URLSearchParams(window.location.search);
url.value = urlParams.get('url');
hash.value = urlParams.get('hash');
-fetch();
+_fetch_();
definePage(() => ({
title: i18n.ts._externalResourceInstaller.title,
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index 4be5fa447d..473207fe6e 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -198,7 +198,7 @@ if (iAmModerator) {
});
}
-async function fetch(): Promise<void> {
+async function _fetch_(): Promise<void> {
if (iAmAdmin) {
meta.value = await misskeyApi('admin/meta');
}
@@ -276,7 +276,7 @@ function refreshMetadata(): void {
});
}
-fetch();
+_fetch_();
const headerActions = computed(() => [{
text: `https://${props.host}`,
diff --git a/packages/frontend/src/pages/list.vue b/packages/frontend/src/pages/list.vue
index 1261428c1c..a52b562c7f 100644
--- a/packages/frontend/src/pages/list.vue
+++ b/packages/frontend/src/pages/list.vue
@@ -103,6 +103,7 @@ definePage(() => ({
icon: 'ti ti-list',
}));
</script>
+
<style lang="scss" module>
.userItem {
display: flex;
diff --git a/packages/frontend/src/pages/lookup.vue b/packages/frontend/src/pages/lookup.vue
index c969473b19..182b2f703d 100644
--- a/packages/frontend/src/pages/lookup.vue
+++ b/packages/frontend/src/pages/lookup.vue
@@ -29,7 +29,7 @@ import MkButton from '@/components/MkButton.vue';
const state = ref<'fetching' | 'done'>('fetching');
-function fetch() {
+function _fetch_() {
const params = new URL(window.location.href).searchParams;
// acctã®ã»ã†ã¯deprecated
@@ -44,12 +44,19 @@ function fetch() {
if (uri.startsWith('https://')) {
promise = misskeyApi('ap/show', {
uri,
- });
- promise.then(res => {
+ }).then(res => {
if (res.type === 'User') {
- mainRouter.replace(res.object.host ? `/@${res.object.username}@${res.object.host}` : `/@${res.object.username}`);
+ mainRouter.replace('/@:acct/:page?', {
+ params: {
+ acct: res.object.host != null ? `${res.object.username}@${res.object.host}` : res.object.username,
+ },
+ });
} else if (res.type === 'Note') {
- mainRouter.replace(`/notes/${res.object.id}`);
+ mainRouter.replace('/notes/:noteId/:initialTab?', {
+ params: {
+ noteId: res.object.id,
+ },
+ });
} else {
os.alert({
type: 'error',
@@ -61,9 +68,12 @@ function fetch() {
if (uri.startsWith('acct:')) {
uri = uri.slice(5);
}
- promise = misskeyApi('users/show', Misskey.acct.parse(uri));
- promise.then(user => {
- mainRouter.replace(user.host ? `/@${user.username}@${user.host}` : `/@${user.username}`);
+ promise = misskeyApi('users/show', Misskey.acct.parse(uri)).then(user => {
+ mainRouter.replace('/@:acct/:page?', {
+ params: {
+ acct: user.host != null ? `${user.username}@${user.host}` : user.username,
+ },
+ });
});
}
@@ -83,7 +93,7 @@ function goToMisskey(): void {
window.location.href = '/';
}
-fetch();
+_fetch_();
const headerActions = computed(() => []);
diff --git a/packages/frontend/src/pages/my-antennas/index.vue b/packages/frontend/src/pages/my-antennas/index.vue
index 95a3108e3a..d7625a8a1c 100644
--- a/packages/frontend/src/pages/my-antennas/index.vue
+++ b/packages/frontend/src/pages/my-antennas/index.vue
@@ -30,11 +30,11 @@ import { antennasCache } from '@/cache.js';
const antennas = computed(() => antennasCache.value.value ?? []);
-function fetch() {
+function _fetch_() {
antennasCache.fetch();
}
-fetch();
+_fetch_();
const headerActions = computed(() => [{
asFullButton: true,
@@ -42,7 +42,7 @@ const headerActions = computed(() => [{
text: i18n.ts.reload,
handler: () => {
antennasCache.delete();
- fetch();
+ _fetch_();
},
}]);
diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue
index fb31cd542c..43d5432f66 100644
--- a/packages/frontend/src/pages/my-lists/index.vue
+++ b/packages/frontend/src/pages/my-lists/index.vue
@@ -17,8 +17,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="items.length > 0" class="_gaps">
<MkA v-for="list in items" :key="list.id" class="_panel" :class="$style.list" :to="`/my/lists/${ list.id }`">
- <div style="margin-bottom: 4px;">{{ list.name }} <span :class="$style.nUsers">({{ i18n.tsx.nUsers({ n: `${list.userIds.length}/${$i.policies['userEachUserListsLimit']}` }) }})</span></div>
- <MkAvatars :userIds="list.userIds" :limit="10"/>
+ <div style="margin-bottom: 4px;">{{ list.name }} <span :class="$style.nUsers">({{ i18n.tsx.nUsers({ n: `${list.userIds!.length}/${$i.policies['userEachUserListsLimit']}` }) }})</span></div>
+ <MkAvatars :userIds="list.userIds!" :limit="10"/>
</MkA>
</div>
</div>
@@ -40,20 +40,20 @@ const $i = ensureSignin();
const items = computed(() => userListsCache.value.value ?? []);
-function fetch() {
+function _fetch_() {
userListsCache.fetch();
}
-fetch();
+_fetch_();
async function create() {
const { canceled, result: name } = await os.inputText({
title: i18n.ts.enterListName,
});
- if (canceled) return;
+ if (canceled || name == null) return;
await os.apiWithDialog('users/lists/create', { name: name });
userListsCache.delete();
- fetch();
+ _fetch_();
}
const headerActions = computed(() => [{
@@ -62,7 +62,7 @@ const headerActions = computed(() => [{
text: i18n.ts.reload,
handler: () => {
userListsCache.delete();
- fetch();
+ _fetch_();
},
}]);
@@ -74,7 +74,7 @@ definePage(() => ({
}));
onActivated(() => {
- fetch();
+ _fetch_();
});
</script>
diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue
index 6b5a797023..eb8e26be3b 100644
--- a/packages/frontend/src/pages/my-lists/list.vue
+++ b/packages/frontend/src/pages/my-lists/list.vue
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkFolder defaultOpen>
<template #label>{{ i18n.ts.members }}</template>
- <template #caption>{{ i18n.tsx.nUsers({ n: `${list.userIds.length}/${$i.policies['userEachUserListsLimit']}` }) }}</template>
+ <template #caption>{{ i18n.tsx.nUsers({ n: `${list.userIds!.length}/${$i.policies['userEachUserListsLimit']}` }) }}</template>
<div class="_gaps_s">
<MkButton rounded primary style="margin: 0 auto;" @click="addUser()">{{ i18n.ts.addUser }}</MkButton>
diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue
index 9de1ef099b..abd2a5d8a1 100644
--- a/packages/frontend/src/pages/note.vue
+++ b/packages/frontend/src/pages/note.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<Transition :name="prefer.s.animation ? 'fade' : ''" mode="out-in">
<div v-if="note">
<div v-if="showNext" class="_margin">
- <MkNotesTimeline :withControl="false" :pullToRefresh="false" class="" :paginator="showNext === 'channel' ? nextChannelPaginator : nextUserPaginator" :noGap="true"/>
+ <MkNotesTimeline direction="up" :withControl="false" :pullToRefresh="false" class="" :paginator="showNext === 'channel' ? nextChannelPaginator : nextUserPaginator" :noGap="true"/>
</div>
<div class="_margin">
@@ -81,7 +81,6 @@ const error = ref();
const prevUserPaginator = markRaw(new Paginator('users/notes', {
limit: 10,
initialId: props.noteId,
- initialDirection: 'older',
computedParams: computed(() => note.value ? ({
userId: note.value.userId,
}) : undefined),
@@ -99,7 +98,6 @@ const nextUserPaginator = markRaw(new Paginator('users/notes', {
const prevChannelPaginator = markRaw(new Paginator('channels/timeline', {
limit: 10,
initialId: props.noteId,
- initialDirection: 'older',
computedParams: computed(() => note.value && note.value.channelId != null ? ({
channelId: note.value.channelId,
}) : undefined),
@@ -128,7 +126,7 @@ function fetchNote() {
noteId: props.noteId,
}).then(res => {
note.value = res;
- const appearNote = getAppearNote(res);
+ const appearNote = getAppearNote(res) ?? res;
// å¤ã„ノートã¯è¢«ã‚¯ãƒªãƒƒãƒ—数をカウントã—ã¦ã„ãªã„ã®ã§ã€2023-10-01以å‰ã®ã‚‚ã®ã¯å¼·åˆ¶çš„ã«notes/clipsã‚’å©ã
if ((appearNote.clippedCount ?? 0) > 0 || new Date(appearNote.createdAt).getTime() < new Date('2023-10-01').getTime()) {
misskeyApi('notes/clips', {
diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue
index a8c1fb654c..71c957460c 100644
--- a/packages/frontend/src/pages/notifications.vue
+++ b/packages/frontend/src/pages/notifications.vue
@@ -31,7 +31,7 @@ import { Paginator } from '@/utility/paginator.js';
const tab = ref('all');
const includeTypes = ref<string[] | null>(null);
-const excludeTypes = computed(() => includeTypes.value ? notificationTypes.filter(t => !includeTypes.value.includes(t)) : null);
+const excludeTypes = computed(() => includeTypes.value ? notificationTypes.filter(t => !includeTypes.value!.includes(t)) : null);
const mentionsPaginator = markRaw(new Paginator('notes/mentions', {
limit: 10,
@@ -71,7 +71,7 @@ const headerActions = computed(() => [tab.value === 'all' ? {
text: i18n.ts.markAllAsRead,
icon: 'ti ti-check',
handler: () => {
- os.apiWithDialog('notifications/mark-all-as-read');
+ os.apiWithDialog('notifications/mark-all-as-read', {});
},
} : undefined].filter(x => x !== undefined));
diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue
index 8a9b9a9b08..9fe03ae981 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.vue
@@ -154,7 +154,11 @@ async function save() {
pageId.value = created.id;
currentName.value = name.value.trim();
- mainRouter.replace(`/pages/edit/${pageId.value}`);
+ mainRouter.replace('/pages/edit/:initPageId', {
+ params: {
+ initPageId: pageId.value,
+ },
+ });
}
}
@@ -189,7 +193,11 @@ async function duplicate() {
pageId.value = created.id;
currentName.value = name.value.trim();
- mainRouter.push(`/pages/edit/${pageId.value}`);
+ mainRouter.push('/pages/edit/:initPageId', {
+ params: {
+ initPageId: pageId.value,
+ },
+ });
}
async function add() {
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index cd63e51fd5..5cb13a9c3f 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -267,7 +267,11 @@ function showMenu(ev: MouseEvent) {
menuItems.push({
icon: 'ti ti-pencil',
text: i18n.ts.edit,
- action: () => router.push(`/pages/edit/${page.value.id}`),
+ action: () => router.push('/pages/edit/:initPageId', {
+ params: {
+ initPageId: page.value!.id,
+ },
+ }),
});
if ($i.pinnedPageId === page.value.id) {
diff --git a/packages/frontend/src/pages/registry.value.vue b/packages/frontend/src/pages/registry.value.vue
index 5c5bbfba39..827866ae2a 100644
--- a/packages/frontend/src/pages/registry.value.vue
+++ b/packages/frontend/src/pages/registry.value.vue
@@ -62,10 +62,10 @@ const props = defineProps<{
}>();
const scope = computed(() => props.path.split('/').slice(0, -1));
-const key = computed(() => props.path.split('/').at(-1));
+const key = computed(() => props.path.split('/').at(-1)!);
const value = ref<any>(null);
-const valueForEditor = ref<string | null>(null);
+const valueForEditor = ref<string>('');
function fetchValue() {
misskeyApi('i/registry/get-detail', {
diff --git a/packages/frontend/src/pages/registry.vue b/packages/frontend/src/pages/registry.vue
index 3762dadd12..389438242e 100644
--- a/packages/frontend/src/pages/registry.vue
+++ b/packages/frontend/src/pages/registry.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton primary @click="createKey">{{ i18n.ts._registry.createKey }}</MkButton>
<div v-if="scopesWithDomain" class="_gaps_m">
- <FormSection v-for="domain in scopesWithDomain" :key="domain.domain">
+ <FormSection v-for="domain in scopesWithDomain" :key="domain.domain ?? 'system'">
<template #label>{{ domain.domain ? domain.domain.toUpperCase() : i18n.ts.system }}</template>
<div class="_gaps_s">
<FormLink v-for="scope in domain.scopes" :to="`/registry/keys/${domain.domain ?? '@'}/${scope.join('/')}`" class="_monospace">{{ scope.length === 0 ? '(root)' : scope.join('/') }}</FormLink>
diff --git a/packages/frontend/src/pages/reset-password.vue b/packages/frontend/src/pages/reset-password.vue
index 9e2069f0e5..53077c34b0 100644
--- a/packages/frontend/src/pages/reset-password.vue
+++ b/packages/frontend/src/pages/reset-password.vue
@@ -34,6 +34,7 @@ const props = defineProps<{
const password = ref('');
async function save() {
+ if (props.token == null) return;
await os.apiWithDialog('reset-password', {
token: props.token,
password: password.value,
diff --git a/packages/frontend/src/pages/reversi/index.vue b/packages/frontend/src/pages/reversi/index.vue
index e4d921b8d2..0ae374649d 100644
--- a/packages/frontend/src/pages/reversi/index.vue
+++ b/packages/frontend/src/pages/reversi/index.vue
@@ -168,7 +168,11 @@ function startGame(game: Misskey.entities.ReversiGameDetailed) {
playbackRate: 1,
});
- router.push(`/reversi/g/${game.id}`);
+ router.push('/reversi/g/:gameId', {
+ params: {
+ gameId: game.id,
+ },
+ });
}
async function matchHeatbeat() {
diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue
index 751a67190a..d73363d058 100644
--- a/packages/frontend/src/pages/scratchpad.vue
+++ b/packages/frontend/src/pages/scratchpad.vue
@@ -82,10 +82,10 @@ const logs = ref<{
text: string;
print: boolean;
}[]>([]);
-const root = ref<AsUiRoot>();
+const root = ref<AsUiRoot | undefined>();
const components = ref<Ref<AsUiComponent>[]>([]);
const uiKey = ref(0);
-const uiInspectorOpenedComponents = ref(new Map<string, boolean>);
+const uiInspectorOpenedComponents = ref(new WeakMap<AsUiComponent | Ref<AsUiComponent>, boolean>);
const saved = miLocalStorage.getItem('scratchpad');
if (saved) {
@@ -186,11 +186,13 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
const showns = computed(() => {
+ if (root.value == null) return new Set<string>();
const result = new Set<string>();
(function addChildrenToResult(c: AsUiComponent) {
result.add(c.id);
- if (c.children) {
- const childComponents = components.value.filter(v => c.children.includes(v.value.id));
+ const children = c.children;
+ if (children) {
+ const childComponents = components.value.filter(v => children.includes(v.value.id));
for (const child of childComponents) {
addChildrenToResult(child.value);
}
diff --git a/packages/frontend/src/pages/search.note.vue b/packages/frontend/src/pages/search.note.vue
index f19c1e7efb..fb34d592a6 100644
--- a/packages/frontend/src/pages/search.note.vue
+++ b/packages/frontend/src/pages/search.note.vue
@@ -264,10 +264,18 @@ async function search() {
const res = await apLookup(searchParams.value.query);
if (res.type === 'User') {
- router.push(`/@${res.object.username}@${res.object.host}`);
+ router.push('/@:acct/:page?', {
+ params: {
+ acct: `${res.object.username}@${res.object.host}`,
+ },
+ });
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
} else if (res.type === 'Note') {
- router.push(`/notes/${res.object.id}`);
+ router.push('/notes/:noteId/:initialTab?', {
+ params: {
+ noteId: res.object.id,
+ },
+ });
}
return;
@@ -282,7 +290,7 @@ async function search() {
text: i18n.ts.lookupConfirm,
});
if (!confirm.canceled) {
- router.push(`/${searchParams.value.query}`);
+ router.pushByPath(`/${searchParams.value.query}`);
return;
}
}
@@ -293,7 +301,11 @@ async function search() {
text: i18n.ts.openTagPageConfirm,
});
if (!confirm.canceled) {
- router.push(`/tags/${encodeURIComponent(searchParams.value.query.substring(1))}`);
+ router.push('/tags/:tag', {
+ params: {
+ tag: searchParams.value.query.substring(1),
+ },
+ });
return;
}
}
diff --git a/packages/frontend/src/pages/search.user.vue b/packages/frontend/src/pages/search.user.vue
index bd67d41a80..5110fca10c 100644
--- a/packages/frontend/src/pages/search.user.vue
+++ b/packages/frontend/src/pages/search.user.vue
@@ -77,10 +77,18 @@ async function search() {
const res = await promise;
if (res.type === 'User') {
- router.push(`/@${res.object.username}@${res.object.host}`);
+ router.push('/@:acct/:page?', {
+ params: {
+ acct: `${res.object.username}@${res.object.host}`,
+ },
+ });
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
} else if (res.type === 'Note') {
- router.push(`/notes/${res.object.id}`);
+ router.push('/notes/:noteId/:initialTab?', {
+ params: {
+ noteId: res.object.id,
+ },
+ });
}
return;
@@ -95,7 +103,7 @@ async function search() {
text: i18n.ts.lookupConfirm,
});
if (!confirm.canceled) {
- router.push(`/${query}`);
+ router.pushByPath(`/${query}`);
return;
}
}
@@ -106,7 +114,11 @@ async function search() {
text: i18n.ts.openTagPageConfirm,
});
if (!confirm.canceled) {
- router.push(`/user-tags/${encodeURIComponent(query.substring(1))}`);
+ router.push('/user-tags/:tag', {
+ params: {
+ tag: query.substring(1),
+ },
+ });
return;
}
}
diff --git a/packages/frontend/src/pages/search.vue b/packages/frontend/src/pages/search.vue
index b6d21a4616..8d2bf9eb42 100644
--- a/packages/frontend/src/pages/search.vue
+++ b/packages/frontend/src/pages/search.vue
@@ -15,16 +15,22 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div v-else-if="tab === 'user'" class="_spacer" style="--MI_SPACER-w: 800px;">
- <XUser v-bind="props"/>
+ <div v-if="usersSearchAvailable">
+ <XUser v-bind="props"/>
+ </div>
+ <div v-else>
+ <MkInfo warn>{{ i18n.ts.usersSearchNotAvailable }}</MkInfo>
+ </div>
</div>
</PageWithHeader>
</template>
<script lang="ts" setup>
import { computed, defineAsyncComponent, ref, toRef } from 'vue';
+import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
-import { notesSearchAvailable } from '@/utility/check-permissions.js';
+import { notesSearchAvailable, usersSearchAvailable } from '@/utility/check-permissions.js';
import MkInfo from '@/components/MkInfo.vue';
const props = withDefaults(defineProps<{
diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue
index 2f639cd090..ca404b43c4 100644
--- a/packages/frontend/src/pages/settings/2fa.vue
+++ b/packages/frontend/src/pages/settings/2fa.vue
@@ -20,12 +20,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkFolder :defaultOpen="true">
<template #icon><i class="ti ti-shield-lock"></i></template>
<template #label><SearchLabel>{{ i18n.ts.totp }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts.totpDescription }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts.totpDescription }}</SearchText></template>
<template #suffix><i v-if="$i.twoFactorEnabled" class="ti ti-check" style="color: var(--MI_THEME-success)"></i></template>
<div v-if="$i.twoFactorEnabled" class="_gaps_s">
<div v-text="i18n.ts._2fa.alreadyRegistered"/>
- <template v-if="$i.securityKeysList.length > 0">
+ <template v-if="$i.securityKeysList!.length > 0">
<MkButton @click="renewTOTP">{{ i18n.ts._2fa.renewTOTP }}</MkButton>
<MkInfo>{{ i18n.ts._2fa.whyTOTPOnlyRenew }}</MkInfo>
</template>
@@ -58,7 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-else>
<MkButton primary @click="addSecurityKey">{{ i18n.ts._2fa.registerSecurityKey }}</MkButton>
- <MkFolder v-for="key in $i.securityKeysList" :key="key.id">
+ <MkFolder v-for="key in $i.securityKeysList!" :key="key.id">
<template #label>{{ key.name }}</template>
<template #suffix><I18n :src="i18n.ts.lastUsedAt"><template #t><MkTime :time="key.lastUsed"/></template></I18n></template>
<div class="_buttons">
@@ -72,9 +72,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</SearchMarker>
<SearchMarker :keywords="['password', 'less', 'key', 'passkey', 'login', 'signin']">
- <MkSwitch :disabled="!$i.twoFactorEnabled || $i.securityKeysList.length === 0" :modelValue="usePasswordLessLogin" @update:modelValue="v => updatePasswordLessLogin(v)">
+ <MkSwitch :disabled="!$i.twoFactorEnabled || $i.securityKeysList!.length === 0" :modelValue="usePasswordLessLogin" @update:modelValue="v => updatePasswordLessLogin(v)">
<template #label><SearchLabel>{{ i18n.ts.passwordLessLogin }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts.passwordLessLoginDescription }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts.passwordLessLoginDescription }}</SearchText></template>
</MkSwitch>
</SearchMarker>
</div>
diff --git a/packages/frontend/src/pages/settings/account-data.vue b/packages/frontend/src/pages/settings/account-data.vue
index 5a00d7a9d7..c75667b06b 100644
--- a/packages/frontend/src/pages/settings/account-data.vue
+++ b/packages/frontend/src/pages/settings/account-data.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker path="/settings/account-data" :label="i18n.ts._settings.accountData" :keywords="['import', 'export', 'data', 'archive']" icon="ti ti-package">
<div class="_gaps_m">
<MkFeatureBanner icon="/client-assets/package_3d.png" color="#ff9100">
- <SearchKeyword>{{ i18n.ts._settings.accountDataBanner }}</SearchKeyword>
+ <SearchText>{{ i18n.ts._settings.accountDataBanner }}</SearchText>
</MkFeatureBanner>
<div class="_gaps_s">
diff --git a/packages/frontend/src/pages/settings/accounts.vue b/packages/frontend/src/pages/settings/accounts.vue
index 2fd0a021da..764ec72652 100644
--- a/packages/frontend/src/pages/settings/accounts.vue
+++ b/packages/frontend/src/pages/settings/accounts.vue
@@ -11,7 +11,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<!--<MkButton @click="refreshAllAccounts"><i class="ti ti-refresh"></i></MkButton>-->
</div>
- <MkUserCardMini v-for="x in accounts" :key="x[0] + x[1].id" :user="x[1]" :class="$style.user" @click.prevent="menu(x[0], x[1], $event)"/>
+ <template v-for="x in accounts" :key="x.host + x.id">
+ <MkUserCardMini v-if="x.user" :user="x.user" :class="$style.user" @click.prevent="showMenu(x.host, x.id, $event)"/>
+ </template>
</div>
</SearchMarker>
</template>
@@ -24,29 +26,29 @@ import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { $i } from '@/i.js';
-import { switchAccount, removeAccount, login, getAccountWithSigninDialog, getAccountWithSignupDialog } from '@/accounts.js';
+import { switchAccount, removeAccount, login, getAccountWithSigninDialog, getAccountWithSignupDialog, getAccounts } from '@/accounts.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import { prefer } from '@/preferences.js';
-const accounts = prefer.r.accounts;
+const accounts = await getAccounts();
function refreshAllAccounts() {
// TODO
}
-function menu(host: string, account: Misskey.entities.UserDetailed, ev: MouseEvent) {
+function showMenu(host: string, id: string, ev: MouseEvent) {
let menu: MenuItem[];
menu = [{
text: i18n.ts.switch,
icon: 'ti ti-switch-horizontal',
- action: () => switchAccount(host, account.id),
+ action: () => switchAccount(host, id),
}, {
text: i18n.ts.remove,
icon: 'ti ti-trash',
- action: () => removeAccount(host, account.id),
+ action: () => removeAccount(host, id),
}];
os.popupMenu(menu, ev.currentTarget ?? ev.target);
diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue
index 5f51a5e079..54e214241b 100644
--- a/packages/frontend/src/pages/settings/apps.vue
+++ b/packages/frontend/src/pages/settings/apps.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<template #label>{{ token.name }}</template>
<template #caption>{{ token.description }}</template>
- <template #suffix><MkTime :time="token.lastUsedAt"/></template>
+ <template v-if="token.lastUsedAt != null" #suffix><MkTime :time="token.lastUsedAt"/></template>
<template #footer>
<MkButton danger @click="revoke(token)"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
</template>
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #key>{{ i18n.ts.installedDate }}</template>
<template #value><MkTime :time="token.createdAt" :mode="'detail'"/></template>
</MkKeyValue>
- <MkKeyValue oneline>
+ <MkKeyValue v-if="token.lastUsedAt != null" oneline>
<template #key>{{ i18n.ts.lastUsedDate }}</template>
<template #value><MkTime :time="token.lastUsedAt" :mode="'detail'"/></template>
</MkKeyValue>
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue
index c58cd57c65..d3d642f156 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.vue
@@ -17,13 +17,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.decorations">
<XDecoration
v-for="(avatarDecoration, i) in $i.avatarDecorations"
- :decoration="avatarDecorations.find(d => d.id === avatarDecoration.id)"
+ :decoration="avatarDecorations.find(d => d.id === avatarDecoration.id) ?? { id: '', url: '', name: '?', roleIdsThatCanBeUsedThisDecoration: [] }"
:angle="avatarDecoration.angle"
:flipH="avatarDecoration.flipH"
:offsetX="avatarDecoration.offsetX"
:offsetY="avatarDecoration.offsetY"
:active="true"
- @click="openDecoration(avatarDecoration, i)"
+ @click="openAttachedDecoration(i)"
/>
</div>
@@ -50,6 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { ref, defineAsyncComponent, computed } from 'vue';
import * as Misskey from 'misskey-js';
import XDecoration from './avatar-decoration.decoration.vue';
+import XDialog from './avatar-decoration.dialog.vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
@@ -68,14 +69,24 @@ misskeyApi('get-avatar-decorations').then(_avatarDecorations => {
loading.value = false;
});
-async function openDecoration(avatarDecoration, index?: number) {
- const { dispose } = await os.popupAsyncWithDialog(import('./avatar-decoration.dialog.vue').then(x => x.default), {
+function openAttachedDecoration(index: number) {
+ openDecoration(avatarDecorations.value.find(d => d.id === $i.avatarDecorations[index].id) ?? { id: '', url: '', name: '?', roleIdsThatCanBeUsedThisDecoration: [] }, index);
+}
+
+async function openDecoration(avatarDecoration: {
+ id: string;
+ url: string;
+ name: string;
+ roleIdsThatCanBeUsedThisDecoration: string[];
+}, index?: number) {
+ const { dispose } = os.popup(XDialog, {
decoration: avatarDecoration,
- usingIndex: index,
+ usingIndex: index ?? null,
}, {
'attach': async (payload) => {
const decoration = {
id: avatarDecoration.id,
+ url: avatarDecoration.url,
angle: payload.angle,
flipH: payload.flipH,
offsetX: payload.offsetX,
@@ -90,13 +101,14 @@ async function openDecoration(avatarDecoration, index?: number) {
'update': async (payload) => {
const decoration = {
id: avatarDecoration.id,
+ url: avatarDecoration.url,
angle: payload.angle,
flipH: payload.flipH,
offsetX: payload.offsetX,
offsetY: payload.offsetY,
};
const update = [...$i.avatarDecorations];
- update[index] = decoration;
+ update[index!] = decoration;
await os.apiWithDialog('i/update', {
avatarDecorations: update,
});
@@ -104,7 +116,7 @@ async function openDecoration(avatarDecoration, index?: number) {
},
'detach': async () => {
const update = [...$i.avatarDecorations];
- update.splice(index, 1);
+ update.splice(index!, 1);
await os.apiWithDialog('i/update', {
avatarDecorations: update,
});
diff --git a/packages/frontend/src/pages/settings/connect.vue b/packages/frontend/src/pages/settings/connect.vue
index 1e701096c5..28579b915f 100644
--- a/packages/frontend/src/pages/settings/connect.vue
+++ b/packages/frontend/src/pages/settings/connect.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker path="/settings/connect" :label="i18n.ts._settings.serviceConnection" :keywords="['app', 'service', 'connect', 'webhook', 'api', 'token']" icon="ti ti-link">
<div class="_gaps_m">
<MkFeatureBanner icon="/client-assets/link_3d.png" color="#ff0088">
- <SearchKeyword>{{ i18n.ts._settings.serviceConnectionBanner }}</SearchKeyword>
+ <SearchText>{{ i18n.ts._settings.serviceConnectionBanner }}</SearchText>
</MkFeatureBanner>
<SearchMarker :keywords="['api', 'app', 'token', 'accessToken']">
diff --git a/packages/frontend/src/pages/settings/custom-css.vue b/packages/frontend/src/pages/settings/custom-css.vue
index 9b0e04860e..83a188b2cb 100644
--- a/packages/frontend/src/pages/settings/custom-css.vue
+++ b/packages/frontend/src/pages/settings/custom-css.vue
@@ -7,6 +7,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_m">
<FormInfo warn>{{ i18n.ts.customCssWarn }}</FormInfo>
+ <FormInfo v-if="isSafeMode" warn>{{ i18n.ts.customCssIsDisabledBecauseSafeMode }}</FormInfo>
+
<MkCodeEditor v-model="localCustomCss" manualSave lang="css">
<template #label>CSS</template>
</MkCodeEditor>
@@ -17,6 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { ref, watch, computed } from 'vue';
import MkCodeEditor from '@/components/MkCodeEditor.vue';
import FormInfo from '@/components/MkInfo.vue';
+import { isSafeMode } from '@@/js/config.js';
import * as os from '@/os.js';
import { unisonReload } from '@/utility/unison-reload.js';
import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/pages/settings/deck.vue b/packages/frontend/src/pages/settings/deck.vue
index ae882d1ee2..7a19b0495b 100644
--- a/packages/frontend/src/pages/settings/deck.vue
+++ b/packages/frontend/src/pages/settings/deck.vue
@@ -97,8 +97,8 @@ import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { prefer } from '@/preferences.js';
import MkPreferenceContainer from '@/components/MkPreferenceContainer.vue';
-import { reloadAsk } from '@/utility/reload-ask.js';
import { selectFile } from '@/utility/drive.js';
+import { suggestReload } from '@/utility/reload-suggest.js';
const navWindow = prefer.model('deck.navWindow');
const useSimpleUiForNonRootPages = prefer.model('deck.useSimpleUiForNonRootPages');
@@ -109,8 +109,8 @@ const menuPosition = prefer.model('deck.menuPosition');
const navbarPosition = prefer.model('deck.navbarPosition');
const wallpaper = prefer.model('deck.wallpaper');
-watch(wallpaper, async () => {
- await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
+watch(wallpaper, () => {
+ suggestReload();
});
function setWallpaper(ev: MouseEvent) {
diff --git a/packages/frontend/src/pages/settings/drive.WatermarkItem.vue b/packages/frontend/src/pages/settings/drive.WatermarkItem.vue
index b466f35fc5..bb91d5e212 100644
--- a/packages/frontend/src/pages/settings/drive.WatermarkItem.vue
+++ b/packages/frontend/src/pages/settings/drive.WatermarkItem.vue
@@ -43,7 +43,7 @@ async function edit() {
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkWatermarkEditorDialog.vue')), {
preset: deepClone(props.preset),
}, {
- ok: (preset: WatermarkPreset) => {
+ ok: (preset) => {
emit('updatePreset', preset);
},
closed: () => dispose(),
diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue
index 1b99f6dea5..cfa4df18fc 100644
--- a/packages/frontend/src/pages/settings/drive.vue
+++ b/packages/frontend/src/pages/settings/drive.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker path="/settings/drive" :label="i18n.ts.drive" :keywords="['drive']" icon="ti ti-cloud">
<div class="_gaps_m">
<MkFeatureBanner icon="/client-assets/cloud_3d.png" color="#0059ff">
- <SearchKeyword>{{ i18n.ts._settings.driveBanner }}</SearchKeyword>
+ <SearchText>{{ i18n.ts._settings.driveBanner }}</SearchText>
</MkFeatureBanner>
<SearchMarker :keywords="['capacity', 'usage']">
@@ -60,7 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkPreferenceContainer k="keepOriginalFilename">
<MkSwitch v-model="keepOriginalFilename">
<template #label><SearchLabel>{{ i18n.ts.keepOriginalFilename }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts.keepOriginalFilenameDescription }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts.keepOriginalFilenameDescription }}</SearchText></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
@@ -74,7 +74,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker :keywords="['auto', 'nsfw', 'sensitive', 'media', 'file']">
<MkSwitch v-model="autoSensitive" @update:modelValue="saveProfile()">
<template #label><SearchLabel>{{ i18n.ts.enableAutoSensitive }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template>
- <template #caption><SearchKeyword>{{ i18n.ts.enableAutoSensitiveDescription }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts.enableAutoSensitiveDescription }}</SearchText></template>
</MkSwitch>
</SearchMarker>
</div>
diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue
index fb8f51041e..469a3c2f1c 100644
--- a/packages/frontend/src/pages/settings/email.vue
+++ b/packages/frontend/src/pages/settings/email.vue
@@ -74,7 +74,7 @@ import { instance } from '@/instance.js';
const $i = ensureSignin();
-const emailAddress = ref($i.email);
+const emailAddress = ref($i.email ?? '');
const onChangeReceiveAnnouncementEmail = (v) => {
misskeyApi('i/update', {
diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue
index 61e3ca8b6c..250c1735be 100644
--- a/packages/frontend/src/pages/settings/index.vue
+++ b/packages/frontend/src/pages/settings/index.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div>{{ i18n.ts._preferencesBackup.autoPreferencesBackupIsNotEnabledForThisDevice }}</div>
<div><button class="_textButton" @click="enableAutoBackup">{{ i18n.ts.enable }}</button> | <button class="_textButton" @click="skipAutoBackup">{{ i18n.ts.skip }}</button></div>
</MkInfo>
- <MkSuperMenu :def="menuDef" :grid="narrow" :searchIndex="SETTING_INDEX"></MkSuperMenu>
+ <MkSuperMenu :def="menuDef" :grid="narrow" :searchIndex="searchIndex"></MkSuperMenu>
</div>
</div>
<div v-if="!(narrow && currentPage?.route.name == null)" class="main">
@@ -42,12 +42,12 @@ import { instance } from '@/instance.js';
import { definePage, provideMetadataReceiver, provideReactiveMetadata } from '@/page.js';
import * as os from '@/os.js';
import { useRouter } from '@/router.js';
-import { searchIndexes } from '@/utility/settings-search-index.js';
import { enableAutoBackup, getPreferencesProfileMenu } from '@/preferences/utility.js';
import { store } from '@/store.js';
import { signout } from '@/signout.js';
+import { genSearchIndexes } from '@/utility/inapp-search.js';
-const SETTING_INDEX = searchIndexes; // TODO: lazy load
+const searchIndex = await import('search-index:settings').then(({ searchIndexes }) => genSearchIndexes(searchIndexes));
const indexInfo = {
title: i18n.ts.settings,
@@ -188,6 +188,8 @@ const menuDef = computed<SuperMenuDef[]>(() => [{
}]);
onMounted(() => {
+ if (el.value == null) return; // TSを黙らã™ãŸã‚
+
ro.observe(el.value);
narrow.value = el.value.offsetWidth < NARROW_THRESHOLD;
@@ -198,6 +200,8 @@ onMounted(() => {
});
onActivated(() => {
+ if (el.value == null) return; // TSを黙らã™ãŸã‚
+
narrow.value = el.value.offsetWidth < NARROW_THRESHOLD;
if (!narrow.value && currentPage.value?.route.name == null) {
@@ -215,7 +219,7 @@ watch(router.currentRef, (to) => {
}
});
-const emailNotConfigured = computed(() => instance.enableEmail && ($i.email == null || !$i.emailVerified));
+const emailNotConfigured = computed(() => $i && instance.enableEmail && ($i.email == null || !$i.emailVerified));
provideMetadataReceiver((metadataGetter) => {
const info = metadataGetter();
diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue
index 6ca313da81..6fd9f07a47 100644
--- a/packages/frontend/src/pages/settings/mute-block.vue
+++ b/packages/frontend/src/pages/settings/mute-block.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker path="/settings/mute-block" :label="i18n.ts.muteAndBlock" icon="ti ti-ban" :keywords="['mute', 'block']">
<div class="_gaps_m">
<MkFeatureBanner icon="/client-assets/prohibited_3d.png" color="#ff2600">
- <SearchKeyword>{{ i18n.ts._settings.muteAndBlockBanner }}</SearchKeyword>
+ <SearchText>{{ i18n.ts._settings.muteAndBlockBanner }}</SearchText>
</MkFeatureBanner>
<div class="_gaps_s">
@@ -159,8 +159,6 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div v-if="expandedBlockItems.includes(item.id)" :class="$style.userItemSub">
<div>Blocked at: <MkTime :time="item.createdAt" mode="detail"/></div>
- <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div>
- <div v-else>Period: {{ i18n.ts.indefinitely }}</div>
</div>
</div>
</div>
@@ -189,10 +187,10 @@ import { ensureSignin } from '@/i.js';
import MkInfo from '@/components/MkInfo.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkSwitch from '@/components/MkSwitch.vue';
-import { reloadAsk } from '@/utility/reload-ask.js';
import { prefer } from '@/preferences.js';
import MkFeatureBanner from '@/components/MkFeatureBanner.vue';
import { Paginator } from '@/utility/paginator.js';
+import { suggestReload } from '@/utility/reload-suggest.js';
const $i = ensureSignin();
@@ -216,8 +214,8 @@ const showSoftWordMutedWord = prefer.model('showSoftWordMutedWord');
watch([
showSoftWordMutedWord,
-], async () => {
- await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
+], () => {
+ suggestReload();
});
async function unrenoteMute(user, ev) {
diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue
index e57195c8a2..f7c634b42e 100644
--- a/packages/frontend/src/pages/settings/navbar.vue
+++ b/packages/frontend/src/pages/settings/navbar.vue
@@ -64,7 +64,6 @@ import MkPreferenceContainer from '@/components/MkPreferenceContainer.vue';
import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
import { store } from '@/store.js';
-import { reloadAsk } from '@/utility/reload-ask.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { prefer } from '@/preferences.js';
@@ -92,7 +91,7 @@ async function addItem() {
value: '-', text: i18n.ts.divider,
}],
});
- if (canceled) return;
+ if (canceled || item == null) return;
items.value = [...items.value, {
id: genId(),
type: item,
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index 4e8d88ab74..84ecc23e84 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker path="/settings/notifications" :label="i18n.ts.notifications" :keywords="['notifications']" icon="ti ti-bell">
<div class="_gaps_m">
<MkFeatureBanner icon="/client-assets/bell_3d.png" color="#ffff00">
- <SearchKeyword>{{ i18n.ts._settings.notificationsBanner }}</SearchKeyword>
+ <SearchText>{{ i18n.ts._settings.notificationsBanner }}</SearchText>
</MkFeatureBanner>
<FormSection first>
@@ -43,9 +43,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</FormSection>
<FormSection>
<div class="_gaps_s">
- <FormLink @click="readAllNotifications">{{ i18n.ts.markAsReadAllNotifications }}</FormLink>
- <FormLink @click="testNotification">{{ i18n.ts._notification.sendTestNotification }}</FormLink>
- <FormLink @click="flushNotification">{{ i18n.ts._notification.flushNotification }}</FormLink>
+ <MkButton @click="readAllNotifications">{{ i18n.ts.markAsReadAllNotifications }}</MkButton>
+ <MkButton @click="testNotification">{{ i18n.ts._notification.sendTestNotification }}</MkButton>
+ <MkButton @click="flushNotification">{{ i18n.ts._notification.flushNotification }}</MkButton>
</div>
</FormSection>
<FormSection>
@@ -55,11 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkPushNotificationAllowButton ref="allowButton"/>
<MkSwitch :disabled="!pushRegistrationInServer" :modelValue="sendReadMessage" @update:modelValue="onChangeSendReadMessage">
<template #label>{{ i18n.ts.sendPushNotificationReadMessage }}</template>
- <template #caption>
- <I18n :src="i18n.ts.sendPushNotificationReadMessageCaption">
- <template #emptyPushNotificationMessage>{{ i18n.ts._notification.emptyPushNotificationMessage }}</template>
- </I18n>
- </template>
+ <template #caption>{{ i18n.ts.sendPushNotificationReadMessageCaption }}</template>
</MkSwitch>
</div>
</FormSection>
@@ -76,6 +72,7 @@ import FormLink from '@/components/form/link.vue';
import FormSection from '@/components/form/section.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkSwitch from '@/components/MkSwitch.vue';
+import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { ensureSignin } from '@/i.js';
import { misskeyApi } from '@/utility/misskey-api.js';
@@ -96,7 +93,7 @@ const sendReadMessage = computed(() => pushRegistrationInServer.value?.sendReadM
const userLists = await misskeyApi('users/lists/list');
async function readAllNotifications() {
- await os.apiWithDialog('notifications/mark-all-as-read');
+ await os.apiWithDialog('notifications/mark-all-as-read', {});
}
async function updateReceiveConfig(type: typeof notificationTypes[number], value: NotificationConfig) {
@@ -134,7 +131,7 @@ async function flushNotification() {
if (canceled) return;
- os.apiWithDialog('notifications/flush');
+ os.apiWithDialog('notifications/flush', {});
}
const headerActions = computed(() => []);
diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue
index ac432e9f32..730cce183a 100644
--- a/packages/frontend/src/pages/settings/other.vue
+++ b/packages/frontend/src/pages/settings/other.vue
@@ -75,7 +75,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_m">
<FormInfo warn>{{ i18n.ts._accountDelete.mayTakeTime }}</FormInfo>
<FormInfo>{{ i18n.ts._accountDelete.sendEmail }}</FormInfo>
- <MkButton v-if="!$i.isDeleted" danger @click="deleteAccount"><SearchKeyword>{{ i18n.ts._accountDelete.requestAccountDelete }}</SearchKeyword></MkButton>
+ <MkButton v-if="!$i.isDeleted" danger @click="deleteAccount"><SearchText>{{ i18n.ts._accountDelete.requestAccountDelete }}</SearchText></MkButton>
<MkButton v-else disabled>{{ i18n.ts._accountDelete.inProgress }}</MkButton>
</div>
</MkFolder>
@@ -99,6 +99,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="enableFolderPageView">
<template #label>Enable folder page view</template>
</MkSwitch>
+ <MkSwitch v-model="enableHapticFeedback">
+ <template #label>Enable haptic feedback</template>
+ </MkSwitch>
</div>
</MkFolder>
</SearchMarker>
@@ -128,9 +131,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<hr>
- <MkButton @click="readAllChatMessages">Read all chat messages</MkButton>
+ <template v-if="$i.policies.chatAvailability !== 'unavailable'">
+ <MkButton @click="readAllChatMessages">Read all chat messages</MkButton>
- <hr>
+ <hr>
+ </template>
<FormSlot>
<MkButton danger @click="migrate"><i class="ti ti-refresh"></i> {{ i18n.ts.migrateOldSettings }}</MkButton>
@@ -155,13 +160,13 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import { ensureSignin } from '@/i.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
-import { reloadAsk } from '@/utility/reload-ask.js';
import FormSection from '@/components/form/section.vue';
import { prefer } from '@/preferences.js';
import MkRolePreview from '@/components/MkRolePreview.vue';
import { signout } from '@/signout.js';
import { migrateOldSettings } from '@/pref-migrate.js';
import { hideAllTips as _hideAllTips, resetAllTips as _resetAllTips } from '@/tips.js';
+import { suggestReload } from '@/utility/reload-suggest.js';
const $i = ensureSignin();
@@ -171,9 +176,10 @@ const skipNoteRender = prefer.model('skipNoteRender');
const devMode = prefer.model('devMode');
const stackingRouterView = prefer.model('experimental.stackingRouterView');
const enableFolderPageView = prefer.model('experimental.enableFolderPageView');
+const enableHapticFeedback = prefer.model('experimental.enableHapticFeedback');
-watch(skipNoteRender, async () => {
- await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
+watch(skipNoteRender, () => {
+ suggestReload();
});
async function deleteAccount() {
diff --git a/packages/frontend/src/pages/settings/plugin.install.vue b/packages/frontend/src/pages/settings/plugin.install.vue
index 22b53b4b96..8ed7f2a7b6 100644
--- a/packages/frontend/src/pages/settings/plugin.install.vue
+++ b/packages/frontend/src/pages/settings/plugin.install.vue
@@ -40,7 +40,7 @@ async function install() {
code.value = null;
router.push('/settings/plugin');
- } catch (err) {
+ } catch (err: any) {
os.alert({
type: 'error',
title: 'Install failed',
diff --git a/packages/frontend/src/pages/settings/plugin.vue b/packages/frontend/src/pages/settings/plugin.vue
index 16d5947ad2..7c6ce90e7e 100644
--- a/packages/frontend/src/pages/settings/plugin.vue
+++ b/packages/frontend/src/pages/settings/plugin.vue
@@ -7,10 +7,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker path="/settings/plugin" :label="i18n.ts.plugins" :keywords="['plugin', 'addon', 'extension']" icon="ti ti-plug">
<div class="_gaps_m">
<MkFeatureBanner icon="/client-assets/electric_plug_3d.png" color="#ffbb00">
- <SearchKeyword>{{ i18n.ts._settings.pluginBanner }}</SearchKeyword>
+ <SearchText>{{ i18n.ts._settings.pluginBanner }}</SearchText>
</MkFeatureBanner>
- <FormLink to="/settings/plugin/install"><template #icon><i class="ti ti-download"></i></template>{{ i18n.ts._plugin.install }}</FormLink>
+ <MkInfo v-if="isSafeMode" warn>{{ i18n.ts.pluginsAreDisabledBecauseSafeMode }}</MkInfo>
+
+ <FormLink v-else to="/settings/plugin/install"><template #icon><i class="ti ti-download"></i></template>{{ i18n.ts._plugin.install }}</FormLink>
<FormSection>
<template #label>{{ i18n.ts.manage }}</template>
@@ -103,10 +105,12 @@ import MkCode from '@/components/MkCode.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
import MkFeatureBanner from '@/components/MkFeatureBanner.vue';
+import MkInfo from '@/components/MkInfo.vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { changePluginActive, configPlugin, pluginLogs, uninstallPlugin, reloadPlugin } from '@/plugin.js';
import { prefer } from '@/preferences.js';
+import { isSafeMode } from '@@/js/config.js';
import * as os from '@/os.js';
const plugins = prefer.r.plugins;
diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue
index 0e400778aa..fdf2373bfc 100644
--- a/packages/frontend/src/pages/settings/preferences.vue
+++ b/packages/frontend/src/pages/settings/preferences.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker path="/settings/preferences" :label="i18n.ts.preferences" :keywords="['general', 'preferences']" icon="ti ti-adjustments">
<div class="_gaps_m">
<MkFeatureBanner icon="/client-assets/gear_3d.png" color="#00ff9d">
- <SearchKeyword>{{ i18n.ts._settings.preferencesBanner }}</SearchKeyword>
+ <SearchText>{{ i18n.ts._settings.preferencesBanner }}</SearchText>
</MkFeatureBanner>
<div class="_gaps_s">
@@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker :keywords="['realtimemode']">
<MkSwitch v-model="realtimeMode">
<template #label><i class="ti ti-bolt"></i> <SearchLabel>{{ i18n.ts.realtimeMode }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts._settings.realtimeMode_description }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts._settings.realtimeMode_description }}</SearchText></template>
</MkSwitch>
</SearchMarker>
@@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkPreferenceContainer k="pollingInterval">
<MkRange v-model="pollingInterval" :min="1" :max="3" :step="1" easing :showTicks="true" :textConverter="(v) => v === 1 ? i18n.ts.low : v === 2 ? i18n.ts.middle : v === 3 ? i18n.ts.high : ''">
<template #label><SearchLabel>{{ i18n.ts._settings.contentsUpdateFrequency }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts._settings.contentsUpdateFrequency_description }}</SearchKeyword><br><SearchKeyword>{{ i18n.ts._settings.contentsUpdateFrequency_description2 }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts._settings.contentsUpdateFrequency_description }}</SearchText><br><SearchText>{{ i18n.ts._settings.contentsUpdateFrequency_description2 }}</SearchText></template>
<template #prefix><i class="ti ti-player-play"></i></template>
<template #suffix><i class="ti ti-player-track-next"></i></template>
</MkRange>
@@ -165,7 +165,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkPreferenceContainer k="collapseRenotes">
<MkSwitch v-model="collapseRenotes">
<template #label><SearchLabel>{{ i18n.ts.collapseRenotes }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts.collapseRenotesDescription }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts.collapseRenotesDescription }}</SearchText></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
@@ -449,7 +449,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_m">
<MkFeatureBanner icon="/client-assets/mens_room_3d.png" color="#0011ff">
- <SearchKeyword>{{ i18n.ts._settings.accessibilityBanner }}</SearchKeyword>
+ <SearchText>{{ i18n.ts._settings.accessibilityBanner }}</SearchText>
</MkFeatureBanner>
<div class="_gaps_s">
@@ -477,6 +477,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkPreferenceContainer>
</SearchMarker>
+ <SearchMarker :keywords="['tabs', 'tabbar', 'bottom', 'under']">
+ <MkPreferenceContainer k="showPageTabBarBottom">
+ <MkSwitch v-model="showPageTabBarBottom">
+ <template #label><SearchLabel>{{ i18n.ts._settings.showPageTabBarBottom }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
<SearchMarker :keywords="['swipe', 'horizontal', 'tab']">
<MkPreferenceContainer k="enableHorizontalSwipe">
<MkSwitch v-model="enableHorizontalSwipe">
@@ -489,7 +497,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkPreferenceContainer k="enablePullToRefresh">
<MkSwitch v-model="enablePullToRefresh">
<template #label><SearchLabel>{{ i18n.ts._settings.enablePullToRefresh }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts._settings.enablePullToRefresh_description }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts._settings.enablePullToRefresh_description }}</SearchText></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
@@ -571,7 +579,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkPreferenceContainer k="animation">
<MkSwitch :modelValue="!reduceAnimation" @update:modelValue="v => reduceAnimation = !v">
<template #label><SearchLabel>{{ i18n.ts._settings.uiAnimations }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts.turnOffToImprovePerformance }}</SearchText></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
@@ -580,7 +588,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkPreferenceContainer k="useBlurEffect">
<MkSwitch v-model="useBlurEffect">
<template #label><SearchLabel>{{ i18n.ts.useBlurEffect }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts.turnOffToImprovePerformance }}</SearchText></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
@@ -589,7 +597,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkPreferenceContainer k="useBlurEffectForModal">
<MkSwitch v-model="useBlurEffectForModal">
<template #label><SearchLabel>{{ i18n.ts.useBlurEffectForModal }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts.turnOffToImprovePerformance }}</SearchText></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
@@ -598,7 +606,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkPreferenceContainer k="enableHighQualityImagePlaceholders">
<MkSwitch v-model="enableHighQualityImagePlaceholders">
<template #label><SearchLabel>{{ i18n.ts._settings.enableHighQualityImagePlaceholders }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts.turnOffToImprovePerformance }}</SearchText></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
@@ -607,7 +615,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkPreferenceContainer k="useStickyIcons">
<MkSwitch v-model="useStickyIcons">
<template #label><SearchLabel>{{ i18n.ts._settings.useStickyIcons }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts.turnOffToImprovePerformance }}</SearchText></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
@@ -795,7 +803,6 @@ import MkInfo from '@/components/MkInfo.vue';
import { store } from '@/store.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
-import { reloadAsk } from '@/utility/reload-ask.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { miLocalStorage } from '@/local-storage.js';
@@ -807,6 +814,7 @@ import { claimAchievement } from '@/utility/achievements.js';
import { instance } from '@/instance.js';
import { ensureSignin } from '@/i.js';
import { genId } from '@/utility/id.js';
+import { suggestReload } from '@/utility/reload-suggest.js';
const $i = ensureSignin();
@@ -866,6 +874,7 @@ const animatedMfm = prefer.model('animatedMfm');
const disableShowingAnimatedImages = prefer.model('disableShowingAnimatedImages');
const keepScreenOn = prefer.model('keepScreenOn');
const enableHorizontalSwipe = prefer.model('enableHorizontalSwipe');
+const showPageTabBarBottom = prefer.model('showPageTabBarBottom');
const enablePullToRefresh = prefer.model('enablePullToRefresh');
const useNativeUiForVideoAudioPlayer = prefer.model('useNativeUiForVideoAudioPlayer');
const contextMenu = prefer.model('contextMenu');
@@ -877,8 +886,6 @@ const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null);
watch(lang, () => {
miLocalStorage.setItem('lang', lang.value as string);
- miLocalStorage.removeItem('locale');
- miLocalStorage.removeItem('localeVersion');
});
watch(fontSize, () => {
@@ -925,11 +932,12 @@ watch([
useSystemFont,
makeEveryTextElementsSelectable,
enableHorizontalSwipe,
+ showPageTabBarBottom,
enablePullToRefresh,
reduceAnimation,
showAvailableReactionsFirstInNote,
-], async () => {
- await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
+], () => {
+ suggestReload();
});
const emojiIndexLangs = ['en-US', 'ja-JP', 'ja-JP_hira'] as const;
@@ -1023,7 +1031,6 @@ function testNotification(): void {
const notification: Misskey.entities.Notification = {
id: genId(),
createdAt: new Date().toUTCString(),
- isRead: false,
type: 'test',
};
diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue
index 4e6425667e..54a6c0af82 100644
--- a/packages/frontend/src/pages/settings/privacy.vue
+++ b/packages/frontend/src/pages/settings/privacy.vue
@@ -7,13 +7,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker path="/settings/privacy" :label="i18n.ts.privacy" :keywords="['privacy']" icon="ti ti-lock-open">
<div class="_gaps_m">
<MkFeatureBanner icon="/client-assets/unlocked_3d.png" color="#aeff00">
- <SearchKeyword>{{ i18n.ts._settings.privacyBanner }}</SearchKeyword>
+ <SearchText>{{ i18n.ts._settings.privacyBanner }}</SearchText>
</MkFeatureBanner>
<SearchMarker :keywords="['follow', 'lock']">
<MkSwitch v-model="isLocked" @update:modelValue="save()">
<template #label><SearchLabel>{{ i18n.ts.makeFollowManuallyApprove }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts.lockedAccountInfo }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts.lockedAccountInfo }}</SearchText></template>
</MkSwitch>
</SearchMarker>
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker :keywords="['reaction', 'public']">
<MkSwitch v-model="publicReactions" @update:modelValue="save()">
<template #label><SearchLabel>{{ i18n.ts.makeReactionsPublic }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts.makeReactionsPublicDescription }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts.makeReactionsPublicDescription }}</SearchText></template>
</MkSwitch>
</SearchMarker>
@@ -53,28 +53,28 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker :keywords="['online', 'status']">
<MkSwitch v-model="hideOnlineStatus" @update:modelValue="save()">
<template #label><SearchLabel>{{ i18n.ts.hideOnlineStatus }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts.hideOnlineStatusDescription }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts.hideOnlineStatusDescription }}</SearchText></template>
</MkSwitch>
</SearchMarker>
<SearchMarker :keywords="['crawle', 'index', 'search']">
<MkSwitch v-model="noCrawle" @update:modelValue="save()">
<template #label><SearchLabel>{{ i18n.ts.noCrawle }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts.noCrawleDescription }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts.noCrawleDescription }}</SearchText></template>
</MkSwitch>
</SearchMarker>
<SearchMarker :keywords="['crawle', 'ai']">
<MkSwitch v-model="preventAiLearning" @update:modelValue="save()">
<template #label><SearchLabel>{{ i18n.ts.preventAiLearning }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts.preventAiLearningDescription }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts.preventAiLearningDescription }}</SearchText></template>
</MkSwitch>
</SearchMarker>
<SearchMarker :keywords="['explore']">
<MkSwitch v-model="isExplorable" @update:modelValue="save()">
<template #label><SearchLabel>{{ i18n.ts.makeExplorable }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts.makeExplorableDescription }}</SearchKeyword></template>
+ <template #caption><SearchText>{{ i18n.ts.makeExplorableDescription }}</SearchText></template>
</MkSwitch>
</SearchMarker>
@@ -125,17 +125,21 @@ SPDX-License-Identifier: AGPL-3.0-only
<option value="absolute">{{ i18n.ts._accountSettings.notesOlderThanSpecifiedDateAndTime }}</option>
</MkSelect>
- <MkSelect v-if="makeNotesFollowersOnlyBefore_type === 'relative'" v-model="makeNotesFollowersOnlyBefore">
- <option :value="-3600">{{ i18n.ts.oneHour }}</option>
- <option :value="-86400">{{ i18n.ts.oneDay }}</option>
- <option :value="-259200">{{ i18n.ts.threeDays }}</option>
- <option :value="-604800">{{ i18n.ts.oneWeek }}</option>
- <option :value="-2592000">{{ i18n.ts.oneMonth }}</option>
- <option :value="-7776000">{{ i18n.ts.threeMonths }}</option>
- <option :value="-31104000">{{ i18n.ts.oneYear }}</option>
+ <MkSelect v-if="makeNotesFollowersOnlyBefore_type === 'relative'" v-model="makeNotesFollowersOnlyBefore_selection">
+ <option v-for="preset in makeNotesFollowersOnlyBefore_presets" :value="preset.value">{{ preset.label }}</option>
+ <option value="custom">{{ i18n.ts.custom }}</option>
</MkSelect>
<MkInput
+ v-if="makeNotesFollowersOnlyBefore_type === 'relative' && makeNotesFollowersOnlyBefore_isCustomMode"
+ v-model="makeNotesFollowersOnlyBefore_customMonths"
+ type="number"
+ :min="1"
+ >
+ <template #suffix>{{ i18n.ts._time.month }}</template>
+ </MkInput>
+
+ <MkInput
v-if="makeNotesFollowersOnlyBefore_type === 'absolute'"
:modelValue="formatDateTimeString(new Date(makeNotesFollowersOnlyBefore * 1000), 'yyyy-MM-dd')"
type="date"
@@ -146,7 +150,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<template #caption>
- <div><SearchKeyword>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBeforeDescription }}</SearchKeyword></div>
+ <div><SearchText>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBeforeDescription }}</SearchText></div>
</template>
</FormSlot>
</SearchMarker>
@@ -156,23 +160,35 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label><SearchLabel>{{ i18n.ts._accountSettings.makeNotesHiddenBefore }}</SearchLabel></template>
<div class="_gaps_s">
- <MkSelect :modelValue="makeNotesHiddenBefore_type" @update:modelValue="makeNotesHiddenBefore = $event === 'relative' ? -604800 : $event === 'absolute' ? Math.floor(Date.now() / 1000) : null">
- <option :value="null">{{ i18n.ts.none }}</option>
- <option value="relative">{{ i18n.ts._accountSettings.notesHavePassedSpecifiedPeriod }}</option>
- <option value="absolute">{{ i18n.ts._accountSettings.notesOlderThanSpecifiedDateAndTime }}</option>
+ <MkSelect
+ :items="[{
+ value: null,
+ label: i18n.ts.none
+ }, {
+ value: 'relative',
+ label: i18n.ts._accountSettings.notesHavePassedSpecifiedPeriod
+ }, {
+ value: 'absolute',
+ label: i18n.ts._accountSettings.notesOlderThanSpecifiedDateAndTime
+ }] as const" :modelValue="makeNotesHiddenBefore_type" @update:modelValue="makeNotesHiddenBefore = $event === 'relative' ? -604800 : $event === 'absolute' ? Math.floor(Date.now() / 1000) : null"
+ >
</MkSelect>
- <MkSelect v-if="makeNotesHiddenBefore_type === 'relative'" v-model="makeNotesHiddenBefore">
- <option :value="-3600">{{ i18n.ts.oneHour }}</option>
- <option :value="-86400">{{ i18n.ts.oneDay }}</option>
- <option :value="-259200">{{ i18n.ts.threeDays }}</option>
- <option :value="-604800">{{ i18n.ts.oneWeek }}</option>
- <option :value="-2592000">{{ i18n.ts.oneMonth }}</option>
- <option :value="-7776000">{{ i18n.ts.threeMonths }}</option>
- <option :value="-31104000">{{ i18n.ts.oneYear }}</option>
+ <MkSelect v-if="makeNotesHiddenBefore_type === 'relative'" v-model="makeNotesHiddenBefore_selection">
+ <option v-for="preset in makeNotesHiddenBefore_presets" :value="preset.value">{{ preset.label }}</option>
+ <option value="custom">{{ i18n.ts.custom }}</option>
</MkSelect>
<MkInput
+ v-if="makeNotesHiddenBefore_type === 'relative' && makeNotesHiddenBefore_isCustomMode"
+ v-model="makeNotesHiddenBefore_customMonths"
+ type="number"
+ :min="1"
+ >
+ <template #suffix>{{ i18n.ts._time.month }}</template>
+ </MkInput>
+
+ <MkInput
v-if="makeNotesHiddenBefore_type === 'absolute'"
:modelValue="formatDateTimeString(new Date(makeNotesHiddenBefore * 1000), 'yyyy-MM-dd')"
type="date"
@@ -183,7 +199,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<template #caption>
- <div><SearchKeyword>{{ i18n.ts._accountSettings.makeNotesHiddenBeforeDescription }}</SearchKeyword></div>
+ <div><SearchText>{{ i18n.ts._accountSettings.makeNotesHiddenBeforeDescription }}</SearchText></div>
</template>
</FormSlot>
</SearchMarker>
@@ -241,6 +257,37 @@ const makeNotesFollowersOnlyBefore_type = computed(() => {
}
});
+const makeNotesFollowersOnlyBefore_presets = [
+ { label: i18n.ts.oneHour, value: -3600 },
+ { label: i18n.ts.oneDay, value: -86400 },
+ { label: i18n.ts.threeDays, value: -259200 },
+ { label: i18n.ts.oneWeek, value: -604800 },
+ { label: i18n.ts.oneMonth, value: -2592000 },
+ { label: i18n.ts.threeMonths, value: -7776000 },
+ { label: i18n.ts.oneYear, value: -31104000 },
+];
+
+const makeNotesFollowersOnlyBefore_isCustomMode = ref(
+ makeNotesFollowersOnlyBefore.value != null &&
+ makeNotesFollowersOnlyBefore.value < 0 &&
+ !makeNotesFollowersOnlyBefore_presets.some((preset) => preset.value === makeNotesFollowersOnlyBefore.value),
+);
+
+const makeNotesFollowersOnlyBefore_selection = computed({
+ get: () => makeNotesFollowersOnlyBefore_isCustomMode.value ? 'custom' : makeNotesFollowersOnlyBefore.value,
+ set(value) {
+ makeNotesFollowersOnlyBefore_isCustomMode.value = value === 'custom';
+ if (value !== 'custom') makeNotesFollowersOnlyBefore.value = value;
+ },
+});
+
+const makeNotesFollowersOnlyBefore_customMonths = computed({
+ get: () => makeNotesFollowersOnlyBefore.value ? Math.abs(makeNotesFollowersOnlyBefore.value) / (30 * 24 * 60 * 60) : null,
+ set(value) {
+ if (value != null && value > 0) makeNotesFollowersOnlyBefore.value = -Math.abs(Math.floor(Number(value))) * 30 * 24 * 60 * 60;
+ },
+});
+
const makeNotesHiddenBefore_type = computed(() => {
if (makeNotesHiddenBefore.value == null) {
return null;
@@ -251,6 +298,37 @@ const makeNotesHiddenBefore_type = computed(() => {
}
});
+const makeNotesHiddenBefore_presets = [
+ { label: i18n.ts.oneHour, value: -3600 },
+ { label: i18n.ts.oneDay, value: -86400 },
+ { label: i18n.ts.threeDays, value: -259200 },
+ { label: i18n.ts.oneWeek, value: -604800 },
+ { label: i18n.ts.oneMonth, value: -2592000 },
+ { label: i18n.ts.threeMonths, value: -7776000 },
+ { label: i18n.ts.oneYear, value: -31104000 },
+];
+
+const makeNotesHiddenBefore_isCustomMode = ref(
+ makeNotesHiddenBefore.value != null &&
+ makeNotesHiddenBefore.value < 0 &&
+ !makeNotesHiddenBefore_presets.some((preset) => preset.value === makeNotesHiddenBefore.value),
+);
+
+const makeNotesHiddenBefore_selection = computed({
+ get: () => makeNotesHiddenBefore_isCustomMode.value ? 'custom' : makeNotesHiddenBefore.value,
+ set(value) {
+ makeNotesHiddenBefore_isCustomMode.value = value === 'custom';
+ if (value !== 'custom') makeNotesHiddenBefore.value = value;
+ },
+});
+
+const makeNotesHiddenBefore_customMonths = computed({
+ get: () => makeNotesHiddenBefore.value ? Math.abs(makeNotesHiddenBefore.value) / (30 * 24 * 60 * 60) : null,
+ set(value) {
+ if (value != null && value > 0) makeNotesHiddenBefore.value = -Math.abs(Math.floor(Number(value))) * 30 * 24 * 60 * 60;
+ },
+});
+
watch([makeNotesFollowersOnlyBefore, makeNotesHiddenBefore], () => {
save();
});
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index ce7f31cd23..4816a6e33b 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker path="/settings/profile" :label="i18n.ts.profile" :keywords="['profile']" icon="ti ti-user">
<div class="_gaps_m">
<div class="_panel">
- <div :class="$style.banner" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }">
+ <div :class="$style.banner" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : '' }">
<div :class="$style.bannerEdit">
<SearchMarker :keywords="['banner', 'change']">
<MkButton primary rounded @click="changeBanner"><SearchLabel>{{ i18n.ts._profile.changeBanner }}</SearchLabel></MkButton>
@@ -110,7 +110,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInput v-model="profile.followedMessage" :max="200" manualSave :mfmPreview="false">
<template #label><SearchLabel>{{ i18n.ts._profile.followedMessage }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template>
<template #caption>
- <div><SearchKeyword>{{ i18n.ts._profile.followedMessageDescription }}</SearchKeyword></div>
+ <div><SearchText>{{ i18n.ts._profile.followedMessageDescription }}</SearchText></div>
<div>{{ i18n.ts._profile.followedMessageDescriptionForLockedAccount }}</div>
</template>
</MkInput>
diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue
index 2562993be3..bc77c1f0af 100644
--- a/packages/frontend/src/pages/settings/security.vue
+++ b/packages/frontend/src/pages/settings/security.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker path="/settings/security" :label="i18n.ts.security" :keywords="['security']" icon="ti ti-lock" :inlining="['2fa']">
<div class="_gaps_m">
<MkFeatureBanner icon="/client-assets/locked_with_key_3d.png" color="#ffbf00">
- <SearchKeyword>{{ i18n.ts._settings.securityBanner }}</SearchKeyword>
+ <SearchText>{{ i18n.ts._settings.securityBanner }}</SearchText>
</MkFeatureBanner>
<SearchMarker :keywords="['password']">
@@ -24,30 +24,34 @@ SPDX-License-Identifier: AGPL-3.0-only
<X2fa/>
- <FormSection>
- <template #label>{{ i18n.ts.signinHistory }}</template>
- <MkPagination :paginator="paginator" withControl>
- <template #default="{items}">
- <div>
- <div v-for="item in items" :key="item.id" v-panel class="timnmucd">
- <header>
- <i v-if="item.success" class="ti ti-check icon succ"></i>
- <i v-else class="ti ti-circle-x icon fail"></i>
- <code class="ip _monospace">{{ item.ip }}</code>
- <MkTime :time="item.createdAt" class="time"/>
- </header>
+ <SearchMarker :keywords="['signin', 'login', 'history', 'log']">
+ <FormSection>
+ <template #label><SearchLabel>{{ i18n.ts.signinHistory }}</SearchLabel></template>
+ <MkPagination :paginator="paginator" withControl>
+ <template #default="{items}">
+ <div>
+ <div v-for="item in items" :key="item.id" v-panel class="timnmucd">
+ <header>
+ <i v-if="item.success" class="ti ti-check icon succ"></i>
+ <i v-else class="ti ti-circle-x icon fail"></i>
+ <code class="ip _monospace">{{ item.ip }}</code>
+ <MkTime :time="item.createdAt" class="time"/>
+ </header>
+ </div>
</div>
- </div>
- </template>
- </MkPagination>
- </FormSection>
+ </template>
+ </MkPagination>
+ </FormSection>
+ </SearchMarker>
- <FormSection>
- <FormSlot>
- <MkButton danger @click="regenerateToken"><i class="ti ti-refresh"></i> {{ i18n.ts.regenerateLoginToken }}</MkButton>
- <template #caption>{{ i18n.ts.regenerateLoginTokenDescription }}</template>
- </FormSlot>
- </FormSection>
+ <SearchMarker :keywords="['regenerate', 'refresh', 'reset', 'token']">
+ <FormSection>
+ <FormSlot>
+ <MkButton danger @click="regenerateToken"><i class="ti ti-refresh"></i> <SearchLabel>{{ i18n.ts.regenerateLoginToken }}</SearchLabel></MkButton>
+ <template #caption>{{ i18n.ts.regenerateLoginTokenDescription }}</template>
+ </FormSlot>
+ </FormSection>
+ </SearchMarker>
</div>
</SearchMarker>
</template>
@@ -76,14 +80,14 @@ async function change() {
type: 'password',
autocomplete: 'new-password',
});
- if (canceled2) return;
+ if (canceled2 || newPassword == null) return;
const { canceled: canceled3, result: newPassword2 } = await os.inputText({
title: i18n.ts.newPasswordRetype,
type: 'password',
autocomplete: 'new-password',
});
- if (canceled3) return;
+ if (canceled3 || newPassword2 == null) return;
if (newPassword !== newPassword2) {
os.alert({
diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue
index 590db19bca..ea5b347525 100644
--- a/packages/frontend/src/pages/settings/sounds.vue
+++ b/packages/frontend/src/pages/settings/sounds.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker path="/settings/sounds" :label="i18n.ts.sounds" :keywords="['sounds']" icon="ti ti-music">
<div class="_gaps_m">
<MkFeatureBanner icon="/client-assets/speaker_high_volume_3d.png" color="#ff006f">
- <SearchKeyword>{{ i18n.ts._settings.soundsBanner }}</SearchKeyword>
+ <SearchText>{{ i18n.ts._settings.soundsBanner }}</SearchText>
</MkFeatureBanner>
<SearchMarker :keywords="['mute']">
diff --git a/packages/frontend/src/pages/settings/statusbar.statusbar.vue b/packages/frontend/src/pages/settings/statusbar.statusbar.vue
index dbb640123a..561d31148f 100644
--- a/packages/frontend/src/pages/settings/statusbar.statusbar.vue
+++ b/packages/frontend/src/pages/settings/statusbar.statusbar.vue
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="statusbar.props.shuffle">
<template #label>{{ i18n.ts.shuffle }}</template>
</MkSwitch>
- <MkInput v-model="statusbar.props.refreshIntervalSec" manualSave type="number" min="1">
+ <MkInput v-model="statusbar.props.refreshIntervalSec" manualSave type="number" :min="1">
<template #label>{{ i18n.ts.refreshInterval }}</template>
</MkInput>
<MkRange v-model="statusbar.props.marqueeDuration" :min="5" :max="150" :step="1">
@@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch>
</template>
<template v-else-if="statusbar.type === 'federation'">
- <MkInput v-model="statusbar.props.refreshIntervalSec" manualSave type="number" min="1">
+ <MkInput v-model="statusbar.props.refreshIntervalSec" manualSave type="number" :min="1">
<template #label>{{ i18n.ts.refreshInterval }}</template>
</MkInput>
<MkRange v-model="statusbar.props.marqueeDuration" :min="5" :max="150" :step="1">
@@ -104,7 +104,7 @@ const props = defineProps<{
userLists: Misskey.entities.UserList[] | null;
}>();
-const statusbar = reactive(deepClone(prefer.s.statusbars.find(x => x.id === props._id)));
+const statusbar = reactive(deepClone(prefer.s.statusbars.find(x => x.id === props._id))!);
watch(() => statusbar.type, () => {
if (statusbar.type === 'rss') {
diff --git a/packages/frontend/src/pages/settings/theme.install.vue b/packages/frontend/src/pages/settings/theme.install.vue
index ac95279402..f79357c361 100644
--- a/packages/frontend/src/pages/settings/theme.install.vue
+++ b/packages/frontend/src/pages/settings/theme.install.vue
@@ -10,8 +10,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkCodeEditor>
<div class="_buttons">
- <MkButton :disabled="installThemeCode == null || installThemeCode.trim() === ''" inline @click="() => previewTheme(installThemeCode)"><i class="ti ti-eye"></i> {{ i18n.ts.preview }}</MkButton>
- <MkButton :disabled="installThemeCode == null || installThemeCode.trim() === ''" primary inline @click="() => install(installThemeCode)"><i class="ti ti-check"></i> {{ i18n.ts.install }}</MkButton>
+ <MkButton :disabled="installThemeCode == null || installThemeCode.trim() === ''" inline @click="() => previewTheme(installThemeCode!)"><i class="ti ti-eye"></i> {{ i18n.ts.preview }}</MkButton>
+ <MkButton :disabled="installThemeCode == null || installThemeCode.trim() === ''" primary inline @click="() => install(installThemeCode!)"><i class="ti ti-check"></i> {{ i18n.ts.install }}</MkButton>
</div>
</div>
</template>
@@ -39,7 +39,7 @@ async function install(code: string): Promise<void> {
});
installThemeCode.value = null;
router.push('/settings/theme');
- } catch (err) {
+ } catch (err: any) {
switch (err.message.toLowerCase()) {
case 'this theme is already installed':
os.alert({
diff --git a/packages/frontend/src/pages/settings/theme.manage.vue b/packages/frontend/src/pages/settings/theme.manage.vue
index fcd0b293e0..e972184278 100644
--- a/packages/frontend/src/pages/settings/theme.manage.vue
+++ b/packages/frontend/src/pages/settings/theme.manage.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts._theme.code }}</template>
<template #caption><button class="_textButton" @click="copyThemeCode()">{{ i18n.ts.copy }}</button></template>
</MkTextarea>
- <MkButton v-if="!builtinThemes.some(t => t.id == selectedTheme.id)" danger @click="uninstall()"><i class="ti ti-trash"></i> {{ i18n.ts.uninstall }}</MkButton>
+ <MkButton v-if="!builtinThemes.some(t => t.id == selectedTheme!.id)" danger @click="uninstall()"><i class="ti ti-trash"></i> {{ i18n.ts.uninstall }}</MkButton>
</template>
</div>
</template>
diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue
index accb1ccc55..beae1224e4 100644
--- a/packages/frontend/src/pages/settings/theme.vue
+++ b/packages/frontend/src/pages/settings/theme.vue
@@ -35,7 +35,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
- <div class="_gaps">
+ <MkInfo v-if="isSafeMode" warn>{{ i18n.ts.themeIsDefaultBecauseSafeMode }}</MkInfo>
+
+ <div v-else class="_gaps">
<template v-if="!store.r.darkMode.value">
<SearchMarker :keywords="['light', 'theme']">
<MkFolder :defaultOpen="true" :max-height="500">
@@ -203,6 +205,7 @@ import { computed, ref, watch } from 'vue';
import JSON5 from 'json5';
import defaultLightTheme from '@@/themes/l-light.json5';
import defaultDarkTheme from '@@/themes/d-green-lime.json5';
+import { isSafeMode } from '@@/js/config.js';
import type { Theme } from '@/theme.js';
import * as os from '@/os.js';
import MkSwitch from '@/components/MkSwitch.vue';
@@ -210,6 +213,7 @@ import FormSection from '@/components/form/section.vue';
import FormLink from '@/components/form/link.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkThemePreview from '@/components/MkThemePreview.vue';
+import MkInfo from '@/components/MkInfo.vue';
import { getBuiltinThemesRef, getThemesRef, removeTheme } from '@/theme.js';
import { isDeviceDarkmode } from '@/utility/is-device-darkmode.js';
import { store } from '@/store.js';
@@ -271,6 +275,7 @@ async function toggleDarkMode() {
const value = !store.r.darkMode.value;
if (syncDeviceDarkMode.value) {
const { canceled } = await os.confirm({
+ type: 'question',
text: i18n.tsx.switchDarkModeManuallyWhenSyncEnabledConfirm({ x: i18n.ts.syncDeviceDarkMode }),
});
if (canceled) return;
diff --git a/packages/frontend/src/pages/settings/webhook.edit.vue b/packages/frontend/src/pages/settings/webhook.edit.vue
index 877d2deb90..a92d3dc457 100644
--- a/packages/frontend/src/pages/settings/webhook.edit.vue
+++ b/packages/frontend/src/pages/settings/webhook.edit.vue
@@ -135,7 +135,7 @@ async function del(): Promise<void> {
webhookId: props.webhookId,
});
- router.push('/settings/webhook');
+ router.push('/settings/connect');
}
async function test(type: Misskey.entities.UserWebhook['on'][number]): Promise<void> {
@@ -149,10 +149,8 @@ async function test(type: Misskey.entities.UserWebhook['on'][number]): Promise<v
});
}
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
const headerActions = computed(() => []);
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
const headerTabs = computed(() => []);
definePage(() => ({
diff --git a/packages/frontend/src/pages/settings/webhook.new.vue b/packages/frontend/src/pages/settings/webhook.new.vue
index e853f967cb..5f36e6bcfc 100644
--- a/packages/frontend/src/pages/settings/webhook.new.vue
+++ b/packages/frontend/src/pages/settings/webhook.new.vue
@@ -40,6 +40,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, computed } from 'vue';
+import * as Misskey from 'misskey-js';
import MkInput from '@/components/MkInput.vue';
import FormSection from '@/components/form/section.vue';
import MkSwitch from '@/components/MkSwitch.vue';
@@ -61,7 +62,7 @@ const event_reaction = ref(true);
const event_mention = ref(true);
async function create(): Promise<void> {
- const events = [];
+ const events = [] as Misskey.entities.UserWebhook['on'];
if (event_follow.value) events.push('follow');
if (event_followed.value) events.push('followed');
if (event_note.value) events.push('note');
diff --git a/packages/frontend/src/pages/signup-complete.vue b/packages/frontend/src/pages/signup-complete.vue
index 15954ccc82..8a907c9066 100644
--- a/packages/frontend/src/pages/signup-complete.vue
+++ b/packages/frontend/src/pages/signup-complete.vue
@@ -51,7 +51,7 @@ function submit() {
os.alert({
type: 'error',
title: i18n.ts.somethingHappened,
- text: i18n.ts.signupPendingError,
+ text: i18n.ts.emailVerificationFailedError,
});
});
}
diff --git a/packages/frontend/src/pages/tag.vue b/packages/frontend/src/pages/tag.vue
index b5a4503b68..047e68f583 100644
--- a/packages/frontend/src/pages/tag.vue
+++ b/packages/frontend/src/pages/tag.vue
@@ -52,7 +52,7 @@ async function post() {
const headerActions = computed(() => [{
icon: 'ti ti-dots',
- label: i18n.ts.more,
+ text: i18n.ts.more,
handler: (ev: MouseEvent) => {
os.popupMenu([{
text: i18n.ts.embed,
diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue
index d1be9e38b7..af3891ac8e 100644
--- a/packages/frontend/src/pages/theme-editor.vue
+++ b/packages/frontend/src/pages/theme-editor.vue
@@ -11,12 +11,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.backgroundColor }}</template>
<div class="cwepdizn-colors">
<div class="row">
- <button v-for="color in bgColors.filter(x => x.kind === 'light')" :key="color.color" class="color _button" :class="{ active: theme.props.bg === color.color }" @click="setBgColor(color)">
+ <button v-for="color in bgColors.filter(x => x.kind === 'light')" class="color _button" :class="{ active: theme.props.bg === color.color }" @click="setBgColor(color)">
<div class="preview" :style="{ background: color.forPreview }"></div>
</button>
</div>
<div class="row">
- <button v-for="color in bgColors.filter(x => x.kind === 'dark')" :key="color.color" class="color _button" :class="{ active: theme.props.bg === color.color }" @click="setBgColor(color)">
+ <button v-for="color in bgColors.filter(x => x.kind === 'dark')" class="color _button" :class="{ active: theme.props.bg === color.color }" @click="setBgColor(color)">
<div class="preview" :style="{ background: color.forPreview }"></div>
</button>
</div>
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.accentColor }}</template>
<div class="cwepdizn-colors">
<div class="row">
- <button v-for="color in accentColors" :key="color" class="color rounded _button" :class="{ active: theme.props.accent === color }" @click="setAccentColor(color)">
+ <button v-for="color in accentColors" class="color rounded _button" :class="{ active: theme.props.accent === color }" @click="setAccentColor(color)">
<div class="preview" :style="{ background: color }"></div>
</button>
</div>
@@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.textColor }}</template>
<div class="cwepdizn-colors">
<div class="row">
- <button v-for="color in fgColors" :key="color" class="color char _button" :class="{ active: (theme.props.fg === color.forLight) || (theme.props.fg === color.forDark) }" @click="setFgColor(color)">
+ <button v-for="color in fgColors" class="color char _button" :class="{ active: (theme.props.fg === color.forLight) || (theme.props.fg === color.forDark) }" @click="setFgColor(color)">
<div class="preview" :style="{ color: color.forPreview ? color.forPreview : theme.base === 'light' ? '#5f5f5f' : '#dadada' }">A</div>
</button>
</div>
@@ -75,17 +75,17 @@ SPDX-License-Identifier: AGPL-3.0-only
import { watch, ref, computed } from 'vue';
import { toUnicode } from 'punycode.js';
import tinycolor from 'tinycolor2';
-import { genId } from '@/utility/id.js';
import JSON5 from 'json5';
import lightTheme from '@@/themes/_light.json5';
import darkTheme from '@@/themes/_dark.json5';
import { host } from '@@/js/config.js';
import type { Theme } from '@/theme.js';
+import { genId } from '@/utility/id.js';
import MkButton from '@/components/MkButton.vue';
import MkCodeEditor from '@/components/MkCodeEditor.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkFolder from '@/components/MkFolder.vue';
-import { $i } from '@/i.js';
+import { ensureSignin } from '@/i.js';
import { addTheme, applyTheme } from '@/theme.js';
import * as os from '@/os.js';
import { store } from '@/store.js';
@@ -94,6 +94,8 @@ import { useLeaveGuard } from '@/composables/use-leave-guard.js';
import { definePage } from '@/page.js';
import { prefer } from '@/preferences.js';
+const $i = ensureSignin();
+
const bgColors = [
{ color: '#f5f5f5', kind: 'light', forPreview: '#f5f5f5' },
{ color: '#f0eee9', kind: 'light', forPreview: '#f3e2b9' },
@@ -123,12 +125,15 @@ const fgColors = [
{ color: 'pink', forLight: '#84667d', forDark: '#e4d1e0', forPreview: '#b12390' },
];
-const theme = ref<Partial<Theme>>({
+const theme = ref<Theme>({
+ id: genId(),
+ name: 'untitled',
+ author: `@${$i.username}@${toUnicode(host)}`,
base: 'light',
props: lightTheme.props,
});
const description = ref<string | null>(null);
-const themeCode = ref<string | null>(null);
+const themeCode = ref<string>('');
const changed = ref(false);
useLeaveGuard(changed);
@@ -194,7 +199,6 @@ async function saveAs() {
theme.value.id = genId();
theme.value.name = name;
- theme.value.author = `@${$i.username}@${toUnicode(host)}`;
if (description.value) theme.value.desc = description.value;
await addTheme(theme.value);
applyTheme(theme.value);
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index b783f7ee0b..f72549df07 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :swipable="true" :displayMyAvatar="true">
+<PageWithHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :swipable="true" :displayMyAvatar="true" :canOmitTitle="true">
<div class="_spacer" style="--MI_SPACER-w: 800px;">
<MkTip v-if="isBasicTimeline(src)" :k="`tl.${src}`" style="margin-bottom: var(--MI-margin);">
{{ i18n.ts._timelineDescription[src] }}
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
ref="tlComponent"
:key="src + withRenotes + withReplies + onlyFiles + withSensitive"
:class="$style.tl"
- :src="src.split(':')[0]"
+ :src="(src.split(':')[0] as (BasicTimelineType | 'list'))"
:list="src.split(':')[1]"
:withRenotes="withRenotes"
:withReplies="withReplies"
@@ -45,8 +45,6 @@ import { miLocalStorage } from '@/local-storage.js';
import { availableBasicTimelines, hasWithReplies, isAvailableBasicTimeline, isBasicTimeline, basicTimelineIconClass } from '@/timelines.js';
import { prefer } from '@/preferences.js';
-provide('shouldOmitHeaderTitle', true);
-
const tlComponent = useTemplateRef('tlComponent');
type TimelinePageSrc = BasicTimelineType | `list:${string}`;
@@ -105,9 +103,11 @@ const withSensitive = computed<boolean>({
set: (x) => saveTlFilter('withSensitive', x),
});
+const showFixedPostForm = prefer.model('showFixedPostForm');
+
async function chooseList(ev: MouseEvent): Promise<void> {
const lists = await userListsCache.fetch();
- const items: MenuItem[] = [
+ const items: (MenuItem | undefined)[] = [
...lists.map(list => ({
type: 'link' as const,
text: list.name,
@@ -121,12 +121,12 @@ async function chooseList(ev: MouseEvent): Promise<void> {
to: '/my/lists',
},
];
- os.popupMenu(items, ev.currentTarget ?? ev.target);
+ os.popupMenu(items.filter(i => i != null), ev.currentTarget ?? ev.target);
}
async function chooseAntenna(ev: MouseEvent): Promise<void> {
const antennas = await antennasCache.fetch();
- const items: MenuItem[] = [
+ const items: (MenuItem | undefined)[] = [
...antennas.map(antenna => ({
type: 'link' as const,
text: antenna.name,
@@ -141,12 +141,12 @@ async function chooseAntenna(ev: MouseEvent): Promise<void> {
to: '/my/antennas',
},
];
- os.popupMenu(items, ev.currentTarget ?? ev.target);
+ os.popupMenu(items.filter(i => i != null), ev.currentTarget ?? ev.target);
}
async function chooseChannel(ev: MouseEvent): Promise<void> {
const channels = await favoritedChannelsCache.fetch();
- const items: MenuItem[] = [
+ const items: (MenuItem | undefined)[] = [
...channels.map(channel => {
const lastReadedAt = miLocalStorage.getItemAsJson(`channelLastReadedAt:${channel.id}`) ?? null;
const hasUnreadNote = (lastReadedAt && channel.lastNotedAt) ? Date.parse(channel.lastNotedAt) > lastReadedAt : !!(!lastReadedAt && channel.lastNotedAt);
@@ -166,7 +166,7 @@ async function chooseChannel(ev: MouseEvent): Promise<void> {
to: '/channels',
},
];
- os.popupMenu(items, ev.currentTarget ?? ev.target);
+ os.popupMenu(items.filter(i => i != null), ev.currentTarget ?? ev.target);
}
function saveSrc(newSrc: TimelinePageSrc): void {
@@ -190,19 +190,6 @@ function saveTlFilter(key: keyof typeof store.s.tl.filter, newValue: boolean) {
}
}
-async function timetravel(): Promise<void> {
- const { canceled, result: date } = await os.inputDate({
- title: i18n.ts.date,
- });
- if (canceled) return;
-
- tlComponent.value.timetravel(date);
-}
-
-function focus(): void {
- tlComponent.value.focus();
-}
-
function switchTlIfNeeded() {
if (isBasicTimeline(src.value) && !isAvailableBasicTimeline(src.value)) {
src.value = availableBasicTimelines()[0];
@@ -217,49 +204,54 @@ onActivated(() => {
});
const headerActions = computed(() => {
- const tmp = [
- {
- icon: 'ti ti-dots',
- text: i18n.ts.options,
- handler: (ev) => {
- const menuItems: MenuItem[] = [];
-
- menuItems.push({
- type: 'switch',
- icon: 'ti ti-repeat',
- text: i18n.ts.showRenotes,
- ref: withRenotes,
- });
+ const items = [{
+ icon: 'ti ti-dots',
+ text: i18n.ts.options,
+ handler: (ev) => {
+ const menuItems: MenuItem[] = [];
- if (isBasicTimeline(src.value) && hasWithReplies(src.value)) {
- menuItems.push({
- type: 'switch',
- icon: 'ti ti-messages',
- text: i18n.ts.showRepliesToOthersInTimeline,
- ref: withReplies,
- disabled: onlyFiles,
- });
- }
+ menuItems.push({
+ type: 'switch',
+ icon: 'ti ti-repeat',
+ text: i18n.ts.showRenotes,
+ ref: withRenotes,
+ });
+ if (isBasicTimeline(src.value) && hasWithReplies(src.value)) {
menuItems.push({
type: 'switch',
- icon: 'ti ti-eye-exclamation',
- text: i18n.ts.withSensitive,
- ref: withSensitive,
- }, {
- type: 'switch',
- icon: 'ti ti-photo',
- text: i18n.ts.fileAttachedOnly,
- ref: onlyFiles,
- disabled: isBasicTimeline(src.value) && hasWithReplies(src.value) ? withReplies : false,
+ icon: 'ti ti-messages',
+ text: i18n.ts.showRepliesToOthersInTimeline,
+ ref: withReplies,
+ disabled: onlyFiles,
});
+ }
- os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
- },
+ menuItems.push({
+ type: 'switch',
+ icon: 'ti ti-eye-exclamation',
+ text: i18n.ts.withSensitive,
+ ref: withSensitive,
+ }, {
+ type: 'switch',
+ icon: 'ti ti-photo',
+ text: i18n.ts.fileAttachedOnly,
+ ref: onlyFiles,
+ disabled: isBasicTimeline(src.value) && hasWithReplies(src.value) ? withReplies : false,
+ }, {
+ type: 'divider',
+ }, {
+ type: 'switch',
+ text: i18n.ts.showFixedPostForm,
+ ref: showFixedPostForm,
+ });
+
+ os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
},
- ];
+ }];
+
if (deviceKind === 'desktop') {
- tmp.unshift({
+ items.unshift({
icon: 'ti ti-refresh',
text: i18n.ts.reload,
handler: (ev: Event) => {
@@ -267,7 +259,8 @@ const headerActions = computed(() => {
},
});
}
- return tmp;
+
+ return items;
});
const headerTabs = computed(() => [...(prefer.r.pinnedUserLists.value.map(l => ({
diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue
index f166495258..57a85a0be7 100644
--- a/packages/frontend/src/pages/user-list-timeline.vue
+++ b/packages/frontend/src/pages/user-list-timeline.vue
@@ -42,7 +42,11 @@ watch(() => props.listId, async () => {
}, { immediate: true });
function settings() {
- router.push(`/my/lists/${props.listId}`);
+ router.push('/my/lists/:listId', {
+ params: {
+ listId: props.listId,
+ }
+ });
}
const headerActions = computed(() => list.value ? [{
diff --git a/packages/frontend/src/pages/user/activity.following.vue b/packages/frontend/src/pages/user/activity.following.vue
index f2a5ad8e75..882b45080e 100644
--- a/packages/frontend/src/pages/user/activity.following.vue
+++ b/packages/frontend/src/pages/user/activity.following.vue
@@ -36,13 +36,15 @@ const props = defineProps<{
const chartEl = useTemplateRef('chartEl');
const legendEl = useTemplateRef('legendEl');
const now = new Date();
-let chartInstance: Chart = null;
+let chartInstance: Chart | null = null;
const chartLimit = 30;
const fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip();
async function renderChart() {
+ if (chartEl.value == null) return;
+
if (chartInstance) {
chartInstance.destroy();
}
diff --git a/packages/frontend/src/pages/user/activity.notes.vue b/packages/frontend/src/pages/user/activity.notes.vue
index ddde84ef25..39c9fd7950 100644
--- a/packages/frontend/src/pages/user/activity.notes.vue
+++ b/packages/frontend/src/pages/user/activity.notes.vue
@@ -36,13 +36,15 @@ const props = defineProps<{
const chartEl = useTemplateRef('chartEl');
const legendEl = useTemplateRef('legendEl');
const now = new Date();
-let chartInstance: Chart = null;
+let chartInstance: Chart | null = null;
const chartLimit = 50;
const fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip();
async function renderChart() {
+ if (chartEl.value == null) return;
+
if (chartInstance) {
chartInstance.destroy();
}
diff --git a/packages/frontend/src/pages/user/activity.pv.vue b/packages/frontend/src/pages/user/activity.pv.vue
index 34e1fe3abf..9e1b92058b 100644
--- a/packages/frontend/src/pages/user/activity.pv.vue
+++ b/packages/frontend/src/pages/user/activity.pv.vue
@@ -36,13 +36,15 @@ const props = defineProps<{
const chartEl = useTemplateRef('chartEl');
const legendEl = useTemplateRef('legendEl');
const now = new Date();
-let chartInstance: Chart = null;
+let chartInstance: Chart | null = null;
const chartLimit = 30;
const fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip();
async function renderChart() {
+ if (chartEl.value == null) return;
+
if (chartInstance) {
chartInstance.destroy();
}
diff --git a/packages/frontend/src/pages/user/follow-list.vue b/packages/frontend/src/pages/user/follow-list.vue
index 6bb1360d42..c383b9b7bd 100644
--- a/packages/frontend/src/pages/user/follow-list.vue
+++ b/packages/frontend/src/pages/user/follow-list.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div>
<MkPagination v-slot="{items}" :paginator="type === 'following' ? followingPaginator : followersPaginator" withControl>
<div :class="$style.users">
- <MkUserInfo v-for="user in items.map(x => type === 'following' ? x.followee : x.follower)" :key="user.id" :user="user"/>
+ <MkUserInfo v-for="user in items.map(x => type === 'following' ? x.followee! : x.follower!)" :key="user.id" :user="user"/>
</div>
</MkPagination>
</div>
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index ed3ae6a2aa..6933d64214 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -25,8 +25,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkUserName class="name" :user="user" :nowrap="true"/>
<div class="bottom">
<span class="username"><MkAcct :user="user" :detail="true"/></span>
- <span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="ti ti-lock"></i></span>
- <span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ti ti-robot"></i></span>
+ <span v-if="user.isLocked"><i class="ti ti-lock"></i></span>
+ <span v-if="user.isBot"><i class="ti ti-robot"></i></span>
<button v-if="$i && !isEditingMemo && !memoDraft" class="_button add-note-button" @click="showMemoTextarea">
<i class="ti ti-edit"/> {{ i18n.ts.addMemo }}
</button>
@@ -43,8 +43,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkUserName :user="user" :nowrap="false" class="name"/>
<div class="bottom">
<span class="username"><MkAcct :user="user" :detail="true"/></span>
- <span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="ti ti-lock"></i></span>
- <span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ti ti-robot"></i></span>
+ <span v-if="user.isLocked"><i class="ti ti-lock"></i></span>
+ <span v-if="user.isBot"><i class="ti ti-robot"></i></span>
</div>
</div>
<div v-if="user.followedMessage != null" class="followedMessage">
@@ -54,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkFukidashi>
</div>
<div v-if="user.roles.length > 0" class="roles">
- <span v-for="role in user.roles" :key="role.id" v-tooltip="role.description" class="role" :style="{ '--color': role.color }">
+ <span v-for="role in user.roles" :key="role.id" v-tooltip="role.description" class="role" :style="{ '--color': role.color ?? '' }">
<MkA v-adaptive-bg :to="`/roles/${role.id}`">
<img v-if="role.iconUrl" style="height: 1.3em; vertical-align: -22%;" :src="role.iconUrl"/>
{{ role.name }}
@@ -228,7 +228,7 @@ const bannerEl = ref<null | HTMLElement>(null);
const memoTextareaEl = ref<null | HTMLElement>(null);
const memoDraft = ref(props.user.memo);
const isEditingMemo = ref(false);
-const moderationNote = ref(props.user.moderationNote);
+const moderationNote = ref(props.user.moderationNote ?? '');
const editModerationNote = ref(false);
watch(moderationNote, async () => {
@@ -249,7 +249,7 @@ const style = computed(() => {
});
const age = computed(() => {
- return calcAge(props.user.birthday);
+ return props.user.birthday ? calcAge(props.user.birthday) : NaN;
});
function menu(ev: MouseEvent) {
diff --git a/packages/frontend/src/pages/user/index.activity.vue b/packages/frontend/src/pages/user/index.activity.vue
index 45bc35067b..210021618e 100644
--- a/packages/frontend/src/pages/user/index.activity.vue
+++ b/packages/frontend/src/pages/user/index.activity.vue
@@ -34,7 +34,7 @@ const props = withDefaults(defineProps<{
limit: 50,
});
-const chartSrc = ref('per-user-notes');
+const chartSrc = ref<'per-user-notes' | 'per-user-pv'>('per-user-notes');
function showMenu(ev: MouseEvent) {
os.popupMenu([{
diff --git a/packages/frontend/src/pages/user/raw.vue b/packages/frontend/src/pages/user/raw.vue
index f0e675b913..145ef5dd92 100644
--- a/packages/frontend/src/pages/user/raw.vue
+++ b/packages/frontend/src/pages/user/raw.vue
@@ -48,7 +48,7 @@ import FormSection from '@/components/form/section.vue';
import MkObjectView from '@/components/MkObjectView.vue';
const props = defineProps<{
- user: Misskey.entities.User;
+ user: Misskey.entities.UserDetailed & { isModerator?: boolean; };
}>();
const moderator = computed(() => props.user.isModerator ?? false);
diff --git a/packages/frontend/src/pages/verify-email.vue b/packages/frontend/src/pages/verify-email.vue
new file mode 100644
index 0000000000..daf3a0c4c6
--- /dev/null
+++ b/packages/frontend/src/pages/verify-email.vue
@@ -0,0 +1,122 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<PageWithAnimBg>
+ <div :class="$style.formContainer">
+ <form :class="$style.form" class="_panel" @submit.prevent="submit()">
+ <div :class="$style.banner">
+ <i class="ti ti-mail-check"></i>
+ </div>
+ <Transition
+ mode="out-in"
+ :enterActiveClass="$style.transition_enterActive"
+ :leaveActiveClass="$style.transition_leaveActive"
+ :enterFromClass="$style.transition_enterFrom"
+ :leaveToClass="$style.transition_leaveTo"
+ >
+ <div v-if="!succeeded" key="input" class="_gaps_m" style="padding: 32px;">
+ <div :class="$style.mainText">{{ i18n.tsx.clickToFinishEmailVerification({ ok: i18n.ts.gotIt }) }}</div>
+ <div>
+ <MkButton gradate large rounded type="submit" :disabled="submitting" style="margin: 0 auto;">
+ {{ submitting ? i18n.ts.processing : i18n.ts.gotIt }}<MkEllipsis v-if="submitting"/>
+ </MkButton>
+ </div>
+ </div>
+ <div v-else key="success" class="_gaps_m" style="padding: 32px;">
+ <div :class="$style.mainText">{{ i18n.ts.emailVerified }}</div>
+ <div>
+ <MkButton large rounded link to="/" linkBehavior="browser" style="margin: 0 auto;">
+ {{ i18n.ts.goToMisskey }}
+ </MkButton>
+ </div>
+ </div>
+ </Transition>
+ </form>
+ </div>
+</PageWithAnimBg>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+import MkButton from '@/components/MkButton.vue';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
+import { misskeyApi } from '@/utility/misskey-api.js';
+
+const submitting = ref(false);
+const succeeded = ref(false);
+
+const props = defineProps<{
+ code: string;
+}>();
+
+function submit() {
+ if (submitting.value) return;
+ submitting.value = true;
+
+ misskeyApi('verify-email', {
+ code: props.code,
+ }).then(() => {
+ succeeded.value = true;
+ submitting.value = false;
+ }).catch(() => {
+ submitting.value = false;
+
+ os.alert({
+ type: 'error',
+ title: i18n.ts.somethingHappened,
+ text: i18n.ts.emailVerificationFailedError,
+ });
+ });
+}
+</script>
+
+<style lang="scss" module>
+.transition_enterActive,
+.transition_leaveActive {
+ transition: opacity 0.3s cubic-bezier(0,0,.35,1), transform 0.3s cubic-bezier(0,0,.35,1);
+}
+.transition_enterFrom {
+ opacity: 0;
+ transform: translateX(50px);
+}
+.transition_leaveTo {
+ opacity: 0;
+ transform: translateX(-50px);
+}
+
+.formContainer {
+ min-height: 100svh;
+ padding: 32px 32px 64px 32px;
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+}
+
+.form {
+ position: relative;
+ display: block;
+ margin: 0 auto;
+ z-index: 10;
+ border-radius: var(--MI-radius);
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
+ overflow: clip;
+ width: 100%;
+ max-width: 500px;
+}
+
+.banner {
+ padding: 16px;
+ text-align: center;
+ font-size: 26px;
+ background-color: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
+}
+
+.mainText {
+ text-align: center;
+}
+</style>
diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.classic.vue
index c2cf937c71..cddec3332e 100644
--- a/packages/frontend/src/pages/welcome.entrance.a.vue
+++ b/packages/frontend/src/pages/welcome.entrance.classic.vue
@@ -53,7 +53,7 @@ function getInstanceIcon(instance: Misskey.entities.FederationInstance): string
misskeyApiGet('federation/instances', {
sort: '+pubSub',
limit: 20,
- blocked: 'false',
+ blocked: false,
}).then(_instances => {
instances.value = _instances;
});
diff --git a/packages/frontend/src/pages/welcome.entrance.simple.vue b/packages/frontend/src/pages/welcome.entrance.simple.vue
new file mode 100644
index 0000000000..c2a2420e50
--- /dev/null
+++ b/packages/frontend/src/pages/welcome.entrance.simple.vue
@@ -0,0 +1,69 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div v-if="meta" :class="$style.root">
+ <MkFeaturedPhotos :class="$style.bg"/>
+ <div :class="$style.logoWrapper">
+ <div :class="$style.poweredBy">Powered by</div>
+ <img :src="misskeysvg" :class="$style.misskey"/>
+ </div>
+ <div :class="$style.contents">
+ <MkVisitorDashboard/>
+ </div>
+</div>
+</template>
+
+<script lang="ts" setup>
+import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue';
+import misskeysvg from '/client-assets/misskey.svg';
+import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue';
+import { instance as meta } from '@/instance.js';
+</script>
+
+<style lang="scss" module>
+.root {
+ height: 100cqh;
+ overflow: auto;
+ overscroll-behavior: contain;
+}
+
+.bg {
+ position: fixed;
+ top: 0;
+ right: 0;
+ width: 80vw; // 100%ã‹ã‚‰shapeã®å¹…を引ã„ã¦ã„ã‚‹
+ height: 100vh;
+}
+
+.logoWrapper {
+ position: fixed;
+ top: 36px;
+ left: 36px;
+ flex: auto;
+ color: #fff;
+ user-select: none;
+ pointer-events: none;
+}
+
+.poweredBy {
+ margin-bottom: 2px;
+}
+
+.misskey {
+ width: 120px;
+
+ @media (max-width: 450px) {
+ width: 100px;
+ }
+}
+
+.contents {
+ position: relative;
+ width: min(430px, calc(100% - 32px));
+ margin: auto;
+ padding: 100px 0 100px 0;
+}
+</style>
diff --git a/packages/frontend/src/pages/welcome.setup.vue b/packages/frontend/src/pages/welcome.setup.vue
index 3e2d086858..393ba98d30 100644
--- a/packages/frontend/src/pages/welcome.setup.vue
+++ b/packages/frontend/src/pages/welcome.setup.vue
@@ -87,7 +87,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<div>{{ i18n.ts._serverSetupWizard.settingsYouMakeHereCanBeChangedLater }}</div>
</div>
- <MkServerSetupWizard :token="token" @finished="onWizardFinished"/>
+ <Suspense>
+ <template #default>
+ <MkServerSetupWizard :token="token" @finished="onWizardFinished"/>
+ </template>
+ <template #fallback>
+ <MkLoading/>
+ </template>
+ </Suspense>
<MkButton rounded style="margin: 0 auto;" @click="skipSettings">
{{ i18n.ts._serverSetupWizard.skipSettings }}
diff --git a/packages/frontend/src/pages/welcome.vue b/packages/frontend/src/pages/welcome.vue
index d3e571c053..2b70996252 100644
--- a/packages/frontend/src/pages/welcome.vue
+++ b/packages/frontend/src/pages/welcome.vue
@@ -6,16 +6,18 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div v-if="instance">
<XSetup v-if="instance.requireSetup"/>
- <XEntrance v-else/>
+ <XEntranceClassic v-else-if="(instance.clientOptions.entrancePageStyle ?? 'classic') === 'classic'"/>
+ <XEntranceSimple v-else/>
</div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import * as Misskey from 'misskey-js';
-import XSetup from './welcome.setup.vue';
-import XEntrance from './welcome.entrance.a.vue';
import { instanceName } from '@@/js/config.js';
+import XSetup from './welcome.setup.vue';
+import XEntranceClassic from './welcome.entrance.classic.vue';
+import XEntranceSimple from './welcome.entrance.simple.vue';
import { definePage } from '@/page.js';
import { fetchInstance } from '@/instance.js';
diff --git a/packages/frontend/src/plugin.ts b/packages/frontend/src/plugin.ts
index d6007a27ed..346e275575 100644
--- a/packages/frontend/src/plugin.ts
+++ b/packages/frontend/src/plugin.ts
@@ -3,18 +3,18 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { ref, defineAsyncComponent } from 'vue';
-import { Interpreter, Parser, utils, values } from '@syuilo/aiscript';
+import { ref } from 'vue';
import { compareVersions } from 'compare-versions';
-import { genId } from '@/utility/id.js';
+import { isSafeMode } from '@@/js/config.js';
import * as Misskey from 'misskey-js';
-import { aiScriptReadline, createAiScriptEnv } from '@/aiscript/api.js';
+import type { Parser, Interpreter, values } from '@syuilo/aiscript';
+import type { FormWithDefault } from '@/utility/form.js';
+import { genId } from '@/utility/id.js';
import { store } from '@/store.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
-import type { FormWithDefault } from '@/utility/form.js';
export type Plugin = {
installId: string;
@@ -38,7 +38,13 @@ export type AiScriptPluginMeta = {
config?: Record<string, any>;
};
-const parser = new Parser();
+let _parser: Parser | null = null;
+
+async function getParser(): Promise<Parser> {
+ const { Parser } = await import('@syuilo/aiscript');
+ _parser ??= new Parser();
+ return _parser;
+}
export function isSupportedAiScriptVersion(version: string): boolean {
try {
@@ -53,6 +59,8 @@ export async function parsePluginMeta(code: string): Promise<AiScriptPluginMeta>
throw new Error('code is required');
}
+ const { Interpreter, utils } = await import('@syuilo/aiscript');
+
const lv = utils.getLangVersion(code);
if (lv == null) {
throw new Error('No language version annotation found');
@@ -62,6 +70,7 @@ export async function parsePluginMeta(code: string): Promise<AiScriptPluginMeta>
let ast;
try {
+ const parser = await getParser();
ast = parser.parse(code);
} catch (err) {
throw new Error('Aiscript syntax error');
@@ -224,14 +233,17 @@ function addPluginHandler<K extends keyof HandlerDef>(installId: Plugin['install
}
export function launchPlugins() {
- for (const plugin of prefer.s.plugins) {
+ return Promise.all(prefer.s.plugins.map(plugin => {
if (plugin.active) {
- launchPlugin(plugin.installId);
+ return launchPlugin(plugin.installId);
+ } else {
+ return Promise.resolve();
}
- }
+ }));
}
async function launchPlugin(id: Plugin['installId']): Promise<void> {
+ if (isSafeMode) return;
const plugin = prefer.s.plugins.find(x => x.installId === id);
if (!plugin) return;
@@ -253,7 +265,10 @@ async function launchPlugin(id: Plugin['installId']): Promise<void> {
await authorizePlugin(plugin);
- const aiscript = new Interpreter(createPluginEnv({
+ const { Interpreter, utils } = await import('@syuilo/aiscript');
+ const { aiScriptReadline } = await import('@/aiscript/api.js');
+
+ const aiscript = new Interpreter(await createPluginEnv({
plugin: plugin,
storageKey: 'plugins:' + plugin.installId,
}), {
@@ -278,7 +293,8 @@ async function launchPlugin(id: Plugin['installId']): Promise<void> {
pluginContexts.set(plugin.installId, aiscript);
- aiscript.exec(parser.parse(plugin.src)).then(
+ const parser = await getParser();
+ await aiscript.exec(parser.parse(plugin.src)).then(
() => {
console.info('Plugin installed:', plugin.name, 'v' + plugin.version);
systemLog('Plugin started');
@@ -334,9 +350,12 @@ export function changePluginActive(plugin: Plugin, active: boolean) {
}
}
-function createPluginEnv(opts: { plugin: Plugin; storageKey: string }): Record<string, values.Value> {
+async function createPluginEnv(opts: { plugin: Plugin; storageKey: string }): Promise<Record<string, values.Value>> {
const id = opts.plugin.installId;
+ const { utils, values } = await import('@syuilo/aiscript');
+ const { createAiScriptEnv } = await import('@/aiscript/api.js');
+
const config = new Map<string, values.Value>();
for (const [k, v] of Object.entries(opts.plugin.config ?? {})) {
config.set(k, utils.jsToVal(typeof opts.plugin.configData[k] !== 'undefined' ? opts.plugin.configData[k] : v.default));
@@ -392,8 +411,8 @@ function createPluginEnv(opts: { plugin: Plugin; storageKey: string }): Record<s
'Plugin:register:note_view_interruptor': values.FN_NATIVE(([handler]) => {
utils.assertFunction(handler);
addPluginHandler(id, 'note_view_interruptor', {
- handler: withContext(ctx => async (note) => {
- return utils.valToJs(await ctx.execFn(handler, [utils.jsToVal(note)]));
+ handler: withContext(ctx => (note) => {
+ return utils.valToJs(ctx.execFnSync(handler, [utils.jsToVal(note)]));
}),
});
}),
diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts
index a83a3153d0..702d9a4acf 100644
--- a/packages/frontend/src/preferences/def.ts
+++ b/packages/frontend/src/preferences/def.ts
@@ -32,6 +32,8 @@ export type SoundStore = {
volume: number;
};
+type OmitStrict<T, K extends keyof T> = T extends any ? Pick<T, Exclude<keyof T, K>> : never;
+
// NOTE: デフォルト値ã¯ä»–ã®è¨­å®šã®çŠ¶æ…‹ã«ä¾å­˜ã—ã¦ã¯ãªã‚‰ãªã„(ä¾å­˜ã—ã¦ã„ãŸå ´åˆã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒãã®è¨­å®šé …ç›®å˜ä½“ã§ã€ŒåˆæœŸå€¤ã«ãƒªã‚»ãƒƒãƒˆã€ã—ãŸå ´åˆä¸å…·åˆã®åŽŸå› ã«ãªã‚‹)
export const PREF_DEF = definePreferences({
@@ -381,8 +383,11 @@ export const PREF_DEF = definePreferences({
showAvailableReactionsFirstInNote: {
default: false,
},
+ showPageTabBarBottom: {
+ default: false,
+ },
plugins: {
- default: [] as Plugin[],
+ default: [] as (OmitStrict<Plugin, 'config'> & { config: Record<string, any> })[],
mergeStrategy: (a, b) => {
const sameIdExists = a.some(x => b.some(y => x.installId === y.installId));
if (sameIdExists) throw new Error();
@@ -495,4 +500,7 @@ export const PREF_DEF = definePreferences({
'experimental.enableFolderPageView': {
default: false,
},
+ 'experimental.enableHapticFeedback': {
+ default: false,
+ },
});
diff --git a/packages/frontend/src/preferences/manager.ts b/packages/frontend/src/preferences/manager.ts
index 0389cf612a..d26d590851 100644
--- a/packages/frontend/src/preferences/manager.ts
+++ b/packages/frontend/src/preferences/manager.ts
@@ -109,10 +109,11 @@ export function definePreferences<T extends Record<string, unknown>>(x: {
}
export function getInitialPrefValue<K extends keyof PREF>(k: K): ValueOf<K> {
- if (typeof PREF_DEF[k].default === 'function') { // factory
- return PREF_DEF[k].default();
+ const _default = PREF_DEF[k as string].default;
+ if (typeof _default === 'function') { // factory
+ return _default();
} else {
- return PREF_DEF[k].default;
+ return _default;
}
}
@@ -468,6 +469,8 @@ export class PreferencesManager {
return local;
} else if (choice === 'merge') {
return mergedValue!;
+ } else { // TSを黙らã™ãŸã‚
+ return undefined;
}
}
diff --git a/packages/frontend/src/router.definition.ts b/packages/frontend/src/router.definition.ts
index 5e0e6f7286..e25e0fe161 100644
--- a/packages/frontend/src/router.definition.ts
+++ b/packages/frontend/src/router.definition.ts
@@ -203,6 +203,9 @@ export const ROUTE_DEF = [{
path: '/signup-complete/:code',
component: page(() => import('@/pages/signup-complete.vue')),
}, {
+ path: '/verify-email/:code',
+ component: page(() => import('@/pages/verify-email.vue')),
+}, {
path: '/announcements',
component: page(() => import('@/pages/announcements.vue')),
}, {
@@ -492,10 +495,6 @@ export const ROUTE_DEF = [{
name: 'performance',
component: page(() => import('@/pages/admin/performance.vue')),
}, {
- path: '/server-rules',
- name: 'server-rules',
- component: page(() => import('@/pages/admin/server-rules.vue')),
- }, {
path: '/invites',
name: 'invites',
component: page(() => import('@/pages/admin/invites.vue')),
@@ -603,4 +602,4 @@ export const ROUTE_DEF = [{
}, {
path: '/:(*)',
component: page(() => import('@/pages/not-found.vue')),
-}] satisfies RouteDef[];
+}] as const satisfies RouteDef[];
diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts
index 97ca63f50d..b1c1708915 100644
--- a/packages/frontend/src/router.ts
+++ b/packages/frontend/src/router.ts
@@ -20,7 +20,7 @@ export function createRouter(fullPath: string): Router {
export const mainRouter = createRouter(window.location.pathname + window.location.search + window.location.hash);
window.addEventListener('popstate', (event) => {
- mainRouter.replace(window.location.pathname + window.location.search + window.location.hash);
+ mainRouter.replaceByPath(window.location.pathname + window.location.search + window.location.hash);
});
mainRouter.addListener('push', ctx => {
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index e9402cfb70..750ca69133 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -106,7 +106,7 @@ export const store = markRaw(new Pizzax('base', {
},
accountInfos: {
where: 'device',
- default: {} as Record<string, Misskey.entities.User>, // host/userId, user
+ default: {} as Record<string, Misskey.entities.MeDetailed>, // host/userId, user
},
enablePreferencesAutoCloudBackup: {
diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss
index ebd2b7e48c..c98b0a4953 100644
--- a/packages/frontend/src/style.scss
+++ b/packages/frontend/src/style.scss
@@ -37,11 +37,6 @@ html {
color: var(--MI_THEME-fg);
accent-color: var(--MI_THEME-accent);
- &, * {
- scrollbar-color: var(--MI_THEME-scrollbarHandle) transparent;
- scrollbar-width: thin;
- }
-
&.f-1 {
font-size: 15px;
}
@@ -64,12 +59,6 @@ html {
}
}
-html._themeChangingFallback_ {
- &, * {
- transition: background 0.5s ease, border 0.5s ease !important;
- }
-}
-
html._themeChanging_ {
view-transition-name: theme-changing;
}
@@ -97,7 +86,11 @@ html::selection {
100% {
opacity: 0;
}
+}
+html, body, main, div {
+ scrollbar-color: var(--MI_THEME-scrollbarHandle) transparent;
+ scrollbar-width: thin;
}
html,
diff --git a/packages/frontend/src/theme.ts b/packages/frontend/src/theme.ts
index e48eb04103..4d03b1d0e9 100644
--- a/packages/frontend/src/theme.ts
+++ b/packages/frontend/src/theme.ts
@@ -3,6 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
+// TODO: (å¯èƒ½ãªéƒ¨åˆ†ã‚’)sharedã«æŠ½å‡ºã—㦠frontend-embed ã¨å…±é€šåŒ–
+
import { ref, nextTick } from 'vue';
import tinycolor from 'tinycolor2';
import lightTheme from '@@/themes/_light.json5';
@@ -23,6 +25,7 @@ export type Theme = {
author: string;
desc?: string;
base?: 'dark' | 'light';
+ kind?: 'dark' | 'light'; // legacy
props: Record<string, string>;
codeHighlighter?: {
base: BundledTheme;
@@ -137,9 +140,10 @@ export function applyTheme(theme: Theme, persist = true) {
}
if (deepEqual(currentTheme, theme)) return;
- currentTheme = theme;
+ // リアクティビティ解除
+ currentTheme = deepClone(theme);
- if (window.document.startViewTransition != null && prefer.s.animation) {
+ if (window.document.startViewTransition != null) {
window.document.documentElement.classList.add('_themeChanging_');
window.document.startViewTransition(async () => {
applyThemeInternal(theme, persist);
@@ -150,15 +154,9 @@ export function applyTheme(theme: Theme, persist = true) {
globalEvents.emit('themeChanged');
});
} else {
- // TODO: ViewTransition API ãŒä¸»è¦ãƒ–ラウザã§å¯¾å¿œã—ãŸã‚‰æ¶ˆã™
- window.document.documentElement.classList.add('_themeChangingFallback_');
- timeout = window.setTimeout(() => {
- window.document.documentElement.classList.remove('_themeChangingFallback_');
- // 色計算ãªã©å†åº¦è¡Œãˆã‚‹ã‚ˆã†ã«ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆå…¨ä½“ã«é€šçŸ¥
- globalEvents.emit('themeChanged');
- }, 500);
-
applyThemeInternal(theme, persist);
+ // 色計算ãªã©å†åº¦è¡Œãˆã‚‹ã‚ˆã†ã«ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆå…¨ä½“ã«é€šçŸ¥
+ globalEvents.emit('themeChanged');
}
}
@@ -170,16 +168,21 @@ export function compile(theme: Theme): Record<string, string> {
return getColor(theme.props[val]);
} else if (val[0] === ':') { // func
const parts = val.split('<');
- const func = parts.shift().substring(1);
- const arg = parseFloat(parts.shift());
- const color = getColor(parts.join('<'));
+ const funcTxt = parts.shift();
+ const argTxt = parts.shift();
+
+ if (funcTxt && argTxt) {
+ const func = funcTxt.substring(1);
+ const arg = parseFloat(argTxt);
+ const color = getColor(parts.join('<'));
- switch (func) {
- case 'darken': return color.darken(arg);
- case 'lighten': return color.lighten(arg);
- case 'alpha': return color.setAlpha(arg);
- case 'hue': return color.spin(arg);
- case 'saturate': return color.saturate(arg);
+ switch (func) {
+ case 'darken': return color.darken(arg);
+ case 'lighten': return color.lighten(arg);
+ case 'alpha': return color.setAlpha(arg);
+ case 'hue': return color.spin(arg);
+ case 'saturate': return color.saturate(arg);
+ }
}
}
diff --git a/packages/frontend/src/types/menu.ts b/packages/frontend/src/types/menu.ts
index 6f2f08cb6d..e0bf135ef8 100644
--- a/packages/frontend/src/types/menu.ts
+++ b/packages/frontend/src/types/menu.ts
@@ -18,7 +18,7 @@ export type MenuAction = (ev: MouseEvent) => void;
export interface MenuButton {
type?: 'button';
text: Text;
- caption?: Text;
+ caption?: Text | null | undefined | ComputedRef<null | undefined>;
icon?: string;
indicate?: boolean;
danger?: boolean;
@@ -38,14 +38,14 @@ export interface MenuDivider extends MenuBase {
export interface MenuLabel extends MenuBase {
type: 'label';
text: Text;
- caption?: Text;
+ caption?: Text | null | undefined | ComputedRef<null | undefined>;
}
export interface MenuLink extends MenuBase {
type: 'link';
to: string;
text: Text;
- caption?: Text;
+ caption?: Text | null | undefined | ComputedRef<null | undefined>;
icon?: string;
indicate?: boolean;
avatar?: Misskey.entities.User;
@@ -57,7 +57,7 @@ export interface MenuA extends MenuBase {
target?: string;
download?: string;
text: Text;
- caption?: Text;
+ caption?: Text | null | undefined | ComputedRef<null | undefined>;
icon?: string;
indicate?: boolean;
}
@@ -74,7 +74,7 @@ export interface MenuSwitch extends MenuBase {
type: 'switch';
ref: Ref<boolean>;
text: Text;
- caption?: Text;
+ caption?: Text | null | undefined | ComputedRef<null | undefined>;
icon?: string;
disabled?: boolean | Ref<boolean>;
}
@@ -82,7 +82,7 @@ export interface MenuSwitch extends MenuBase {
export interface MenuRadio extends MenuBase {
type: 'radio';
text: Text;
- caption?: Text;
+ caption?: Text | null | undefined | ComputedRef<null | undefined>;
icon?: string;
ref: Ref<MenuRadioOptionsDef[keyof MenuRadioOptionsDef]>;
options: MenuRadioOptionsDef;
@@ -92,7 +92,7 @@ export interface MenuRadio extends MenuBase {
export interface MenuRadioOption extends MenuBase {
type: 'radioOption';
text: Text;
- caption?: Text;
+ caption?: Text | null | undefined | ComputedRef<null | undefined>;
action: MenuAction;
active?: boolean | ComputedRef<boolean>;
}
@@ -106,7 +106,7 @@ export interface MenuComponent<T extends Component = any> extends MenuBase {
export interface MenuParent extends MenuBase {
type: 'parent';
text: Text;
- caption?: Text;
+ caption?: Text | null | undefined | ComputedRef<null | undefined>;
icon?: string;
children: MenuItem[] | (() => Promise<MenuItem[]> | MenuItem[]);
}
diff --git a/packages/frontend/src/types/post-form.ts b/packages/frontend/src/types/post-form.ts
index 10e68d2d4a..90c8605f31 100644
--- a/packages/frontend/src/types/post-form.ts
+++ b/packages/frontend/src/types/post-form.ts
@@ -8,7 +8,14 @@ import * as Misskey from 'misskey-js';
export interface PostFormProps {
reply?: Misskey.entities.Note | null;
renote?: Misskey.entities.Note | null;
- channel?: Misskey.entities.Channel | null; // TODO
+ channel?: {
+ id: string;
+ name: string;
+ color: string;
+ isSensitive: boolean;
+ allowRenoteToExternal: boolean;
+ userId: string | null;
+ } | null;
mention?: Misskey.entities.User;
specified?: Misskey.entities.UserDetailed;
initialText?: string;
diff --git a/packages/frontend/src/ui/_common_/PreferenceRestore.vue b/packages/frontend/src/ui/_common_/PreferenceRestore.vue
index 5fd9f5e44b..b9d54cddc6 100644
--- a/packages/frontend/src/ui/_common_/PreferenceRestore.vue
+++ b/packages/frontend/src/ui/_common_/PreferenceRestore.vue
@@ -48,10 +48,6 @@ function skip() {
.title {
padding: 0 10px;
font-weight: bold;
-
- &:empty {
- display: none;
- }
}
.body {
diff --git a/packages/frontend/src/ui/_common_/ReloadSuggestion.vue b/packages/frontend/src/ui/_common_/ReloadSuggestion.vue
new file mode 100644
index 0000000000..8fcfe0b12f
--- /dev/null
+++ b/packages/frontend/src/ui/_common_/ReloadSuggestion.vue
@@ -0,0 +1,71 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :class="$style.root">
+ <span :class="$style.icon">
+ <i class="ti ti-info-circle"></i>
+ </span>
+ <span :class="$style.title">{{ i18n.ts.reloadRequiredToApplySettings }}</span>
+ <span :class="$style.body"><button class="_textButton" style="color: var(--MI_THEME-fgOnAccent);" @click="reload">{{ i18n.ts.reload }}</button> | <button class="_textButton" style="color: var(--MI_THEME-fgOnAccent);" @click="skip">{{ i18n.ts.skip }}</button></span>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { i18n } from '@/i18n.js';
+import { shouldSuggestReload } from '@/utility/reload-suggest.js';
+import { unisonReload } from '@/utility/unison-reload.js';
+
+function reload() {
+ unisonReload();
+}
+
+function skip() {
+ shouldSuggestReload.value = false;
+}
+</script>
+
+<style lang="scss" module>
+.root {
+ --height: 24px;
+ font-size: 0.85em;
+ display: flex;
+ vertical-align: bottom;
+ width: 100%;
+ line-height: var(--height);
+ height: var(--height);
+ overflow: clip;
+ contain: strict;
+ background: var(--MI_THEME-accent);
+ color: var(--MI_THEME-fgOnAccent);
+}
+
+.icon {
+ margin-left: 10px;
+ animation: blink 2s infinite;
+}
+
+.title {
+ padding: 0 10px;
+ font-weight: bold;
+ animation: blink 2s infinite;
+}
+
+.body {
+ min-width: 0;
+ flex: 1;
+ overflow: clip;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+@keyframes blink {
+ 0% { opacity: 1; }
+ 10% { opacity: 1; }
+ 50% { opacity: 0; }
+ 90% { opacity: 1; }
+ 100% { opacity: 1; }
+}
+</style>
diff --git a/packages/frontend/src/ui/_common_/announcements.vue b/packages/frontend/src/ui/_common_/announcements.vue
index f9af8e1ee7..c299e1b1c1 100644
--- a/packages/frontend/src/ui/_common_/announcements.vue
+++ b/packages/frontend/src/ui/_common_/announcements.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div :class="$style.root">
+<div v-if="$i" :class="$style.root">
<MkA
v-for="announcement in $i.unreadAnnouncements.filter(x => x.display === 'banner')"
:key="announcement.id"
diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts
index 9872937d21..a9ad36c97a 100644
--- a/packages/frontend/src/ui/_common_/common.ts
+++ b/packages/frontend/src/ui/_common_/common.ts
@@ -12,7 +12,7 @@ import { i18n } from '@/i18n.js';
import { $i } from '@/i.js';
function toolsMenuItems(): MenuItem[] {
- return [{
+ const items: MenuItem[] = [{
type: 'link',
to: '/scratchpad',
text: i18n.ts.scratchpad,
@@ -27,17 +27,27 @@ function toolsMenuItems(): MenuItem[] {
to: '/clicker',
text: 'ðŸªðŸ‘ˆ',
icon: 'ti ti-cookie',
- }, ($i && ($i.isAdmin || $i.policies.canManageCustomEmojis)) ? {
- type: 'link',
- to: '/custom-emojis-manager',
- text: i18n.ts.manageCustomEmojis,
- icon: 'ti ti-icons',
- } : undefined, ($i && ($i.isAdmin || $i.policies.canManageAvatarDecorations)) ? {
- type: 'link',
- to: '/avatar-decorations',
- text: i18n.ts.manageAvatarDecorations,
- icon: 'ti ti-sparkles',
- } : undefined];
+ }];
+
+ if ($i && ($i.isAdmin || $i.policies.canManageCustomEmojis)) {
+ items.push({
+ type: 'link',
+ to: '/custom-emojis-manager',
+ text: i18n.ts.manageCustomEmojis,
+ icon: 'ti ti-icons',
+ });
+ }
+
+ if ($i && ($i.isAdmin || $i.policies.canManageAvatarDecorations)) {
+ items.push({
+ type: 'link' as const,
+ to: '/avatar-decorations',
+ text: i18n.ts.manageAvatarDecorations,
+ icon: 'ti ti-sparkles',
+ });
+ }
+
+ return items;
}
export function openInstanceMenu(ev: MouseEvent) {
diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue
index da20d23cfd..37c95f2db2 100644
--- a/packages/frontend/src/ui/_common_/common.vue
+++ b/packages/frontend/src/ui/_common_/common.vue
@@ -94,6 +94,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="dev" id="devTicker"><span style="animation: dev-ticker-blink 2s infinite;">DEV BUILD</span></div>
<div v-if="$i && $i.isBot" id="botWarn"><span style="animation: dev-ticker-blink 2s infinite;">{{ i18n.ts.loggedInAsBot }}</span></div>
+
+<div v-if="isSafeMode" id="safemodeWarn">
+ <span style="animation: dev-ticker-blink 2s infinite;">{{ i18n.ts.safeModeEnabled }}</span>&nbsp;
+ <button class="_textButton" style="pointer-events: all;" @click="exitSafeMode">{{ i18n.ts.turnItOff }}</button>
+</div>
</template>
<script lang="ts" setup>
@@ -101,7 +106,10 @@ import { defineAsyncComponent, ref, TransitionGroup } from 'vue';
import * as Misskey from 'misskey-js';
import { swInject } from './sw-inject.js';
import XNotification from './notification.vue';
+import { isSafeMode } from '@@/js/config.js';
import { popups } from '@/os.js';
+import { unisonReload } from '@/utility/unison-reload.js';
+import { miLocalStorage } from '@/local-storage.js';
import { pendingApiRequestsCount } from '@/utility/misskey-api.js';
import * as sound from '@/utility/sound.js';
import { $i } from '@/i.js';
@@ -144,6 +152,13 @@ function onNotification(notification: Misskey.entities.Notification, isClient =
sound.playMisskeySfx('notification');
}
+function exitSafeMode() {
+ miLocalStorage.removeItem('isSafeMode');
+ const url = new URL(window.location.href);
+ url.searchParams.delete('safemode');
+ unisonReload(url.toString());
+}
+
if ($i) {
if (store.s.realtimeMode) {
const connection = useStream().useChannel('main');
@@ -396,7 +411,7 @@ if ($i) {
width: 100%;
height: max-content;
text-align: center;
- z-index: 2147483647;
+ z-index: 2147483646;
color: #ff0;
background: rgba(0, 0, 0, 0.5);
padding: 4px 7px;
@@ -405,6 +420,11 @@ if ($i) {
user-select: none;
}
+#safemodeWarn {
+ @extend #botWarn;
+ z-index: 2147483647;
+}
+
#devTicker {
position: fixed;
bottom: 0;
diff --git a/packages/frontend/src/ui/_common_/mobile-footer-menu.vue b/packages/frontend/src/ui/_common_/mobile-footer-menu.vue
index e2993230be..7ab8a45f51 100644
--- a/packages/frontend/src/ui/_common_/mobile-footer-menu.vue
+++ b/packages/frontend/src/ui/_common_/mobile-footer-menu.vue
@@ -86,7 +86,7 @@ watch(rootEl, () => {
box-sizing: border-box;
background: var(--MI_THEME-navBg);
color: var(--MI_THEME-navFg);
- box-shadow: 0px 0px 6px 6px #0000000f;
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
.item {
diff --git a/packages/frontend/src/ui/_common_/navbar-h.vue b/packages/frontend/src/ui/_common_/navbar-h.vue
index 688e195ce6..a78bdd52d1 100644
--- a/packages/frontend/src/ui/_common_/navbar-h.vue
+++ b/packages/frontend/src/ui/_common_/navbar-h.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="body">
<div class="left">
<button v-click-anime class="item _button instance" @click="openInstanceMenu">
- <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" draggable="false"/>
+ <img :src="instance.iconUrl ?? '/favicon.ico'" draggable="false"/>
</button>
<MkA v-click-anime v-tooltip="i18n.ts.timeline" class="item index" activeClass="active" to="/" exact>
<i class="ti ti-home ti-fw"></i>
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</component>
</template>
<div class="divider"></div>
- <MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime v-tooltip="i18n.ts.controlPanel" class="item" activeClass="active" to="/admin" :behavior="settingsWindowed ? 'window' : null">
+ <MkA v-if="$i && ($i.isAdmin || $i.isModerator)" v-click-anime v-tooltip="i18n.ts.controlPanel" class="item" activeClass="active" to="/admin" :behavior="settingsWindowed ? 'window' : null">
<i class="ti ti-dashboard ti-fw"></i>
</MkA>
<button v-click-anime class="item _button" @click="more">
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkA v-click-anime v-tooltip="i18n.ts.settings" class="item" activeClass="active" to="/settings" :behavior="settingsWindowed ? 'window' : null">
<i class="ti ti-settings ti-fw"></i>
</MkA>
- <button v-click-anime class="item _button account" @click="openAccountMenu">
+ <button v-if="$i" v-click-anime class="item _button account" @click="openAccountMenu">
<MkAvatar :user="$i" class="avatar"/><MkAcct class="acct" :user="$i"/>
</button>
<div class="post" @click="os.post()">
@@ -57,6 +57,7 @@ import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
import { $i } from '@/i.js';
+import { getHTMLElementOrNull } from '@/utility/get-dom-node-or-null.js';
const WINDOW_THRESHOLD = 1400;
@@ -72,8 +73,11 @@ const otherNavItemIndicated = computed<boolean>(() => {
});
async function more(ev: MouseEvent) {
+ const target = getHTMLElementOrNull(ev.currentTarget ?? ev.target);
+ if (!target) return;
+
const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkLaunchPad.vue').then(x => x.default), {
- anchorElement: ev.currentTarget ?? ev.target,
+ anchorElement: target,
anchor: { x: 'center', y: 'bottom' },
}, {
closed: () => dispose(),
diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue
index 28913a54ab..4c43bf2b3b 100644
--- a/packages/frontend/src/ui/_common_/navbar.vue
+++ b/packages/frontend/src/ui/_common_/navbar.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.body">
<div :class="$style.top">
<button v-tooltip.noDelay.right="instance.name ?? i18n.ts.instance" class="_button" :class="$style.instance" @click="openInstanceMenu">
- <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon" style="viewTransitionName: navbar-serverIcon;"/>
+ <img :src="instance.iconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon" style="viewTransitionName: navbar-serverIcon;"/>
</button>
<button v-if="!iconOnly" v-tooltip.noDelay.right="i18n.ts.realtimeMode" class="_button" :class="[$style.realtimeMode, store.r.realtimeMode.value ? $style.on : null]" @click="toggleRealtimeMode">
<i class="ti ti-bolt ti-fw"></i>
diff --git a/packages/frontend/src/ui/_common_/statusbar-federation.vue b/packages/frontend/src/ui/_common_/statusbar-federation.vue
index 7248e8826b..079f1f92bb 100644
--- a/packages/frontend/src/ui/_common_/statusbar-federation.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-federation.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
mode="default"
>
<MkMarqueeText :key="key" :duration="marqueeDuration" :reverse="marqueeReverse">
- <span v-for="instance in instances" :key="instance.id" :class="[$style.item, { [$style.colored]: colored }]" :style="{ background: colored ? instance.themeColor : null }">
+ <span v-for="instance in instances" :key="instance.id" :class="[$style.item, { [$style.colored]: colored }]" :style="{ background: colored ? instance.themeColor ?? '' : '' }">
<img :class="$style.icon" :src="getInstanceIcon(instance)" alt=""/>
<MkA :to="`/instance-info/${instance.host}`" :class="$style.host" class="_monospace">
{{ instance.host }}
@@ -33,9 +33,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
import * as Misskey from 'misskey-js';
+import { useInterval } from '@@/js/use-interval.js';
import MkMarqueeText from '@/components/MkMarqueeText.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
-import { useInterval } from '@@/js/use-interval.js';
import { getProxiedImageUrlNullable } from '@/utility/media-proxy.js';
const props = defineProps<{
@@ -44,7 +44,7 @@ const props = defineProps<{
marqueeDuration?: number;
marqueeReverse?: boolean;
oneByOneInterval?: number;
- refreshIntervalSec?: number;
+ refreshIntervalSec: number;
}>();
const instances = ref<Misskey.entities.FederationInstance[]>([]);
diff --git a/packages/frontend/src/ui/_common_/statusbar-rss.vue b/packages/frontend/src/ui/_common_/statusbar-rss.vue
index 7db0d5267d..202972ed46 100644
--- a/packages/frontend/src/ui/_common_/statusbar-rss.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-rss.vue
@@ -29,18 +29,18 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
import * as Misskey from 'misskey-js';
-import MkMarqueeText from '@/components/MkMarqueeText.vue';
import { useInterval } from '@@/js/use-interval.js';
+import MkMarqueeText from '@/components/MkMarqueeText.vue';
import { shuffle } from '@/utility/shuffle.js';
const props = defineProps<{
- url?: string;
+ url: string;
shuffle?: boolean;
display?: 'marquee' | 'oneByOne';
marqueeDuration?: number;
marqueeReverse?: boolean;
oneByOneInterval?: number;
- refreshIntervalSec?: number;
+ refreshIntervalSec: number;
}>();
const items = ref<Misskey.entities.FetchRssResponse['items']>([]);
diff --git a/packages/frontend/src/ui/_common_/statusbar-user-list.vue b/packages/frontend/src/ui/_common_/statusbar-user-list.vue
index 13139a1064..8a754e6e64 100644
--- a/packages/frontend/src/ui/_common_/statusbar-user-list.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-user-list.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<MkMarqueeText :key="key" :duration="marqueeDuration" :reverse="marqueeReverse">
<span v-for="note in notes" :key="note.id" :class="$style.item">
- <img :class="$style.avatar" :src="note.user.avatarUrl" decoding="async"/>
+ <img v-if="note.user.avatarUrl" :class="$style.avatar" :src="note.user.avatarUrl" decoding="async"/>
<MkA :class="$style.text" :to="notePage(note)">
<Mfm :text="getNoteSummary(note)" :plain="true" :nowrap="true"/>
</MkA>
@@ -33,9 +33,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, watch } from 'vue';
import * as Misskey from 'misskey-js';
+import { useInterval } from '@@/js/use-interval.js';
import MkMarqueeText from '@/components/MkMarqueeText.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
-import { useInterval } from '@@/js/use-interval.js';
import { getNoteSummary } from '@/utility/get-note-summary.js';
import { notePage } from '@/filters/note.js';
@@ -45,7 +45,7 @@ const props = defineProps<{
marqueeDuration?: number;
marqueeReverse?: boolean;
oneByOneInterval?: number;
- refreshIntervalSec?: number;
+ refreshIntervalSec: number;
}>();
const notes = ref<Misskey.entities.Note[]>([]);
diff --git a/packages/frontend/src/ui/_common_/sw-inject.ts b/packages/frontend/src/ui/_common_/sw-inject.ts
index 1459881ba1..63918fbe2f 100644
--- a/packages/frontend/src/ui/_common_/sw-inject.ts
+++ b/packages/frontend/src/ui/_common_/sw-inject.ts
@@ -43,7 +43,7 @@ export function swInject() {
if (mainRouter.currentRoute.value.path === ev.data.url) {
return window.scroll({ top: 0, behavior: 'smooth' });
}
- return mainRouter.push(ev.data.url);
+ return mainRouter.pushByPath(ev.data.url);
default:
return;
}
diff --git a/packages/frontend/src/ui/_common_/titlebar.vue b/packages/frontend/src/ui/_common_/titlebar.vue
index c62b13b73a..1b9d47ec40 100644
--- a/packages/frontend/src/ui/_common_/titlebar.vue
+++ b/packages/frontend/src/ui/_common_/titlebar.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="$style.root">
<div :class="$style.title">
- <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/>
+ <img :src="instance.iconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/>
<span :class="$style.instanceTitle">{{ instance.name ?? host }}</span>
</div>
<div :class="$style.controls">
diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index d2b163a38f..9f6d8267f7 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -10,9 +10,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.nonTitlebarArea">
<XSidebar v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'left'"/>
- <div :class="[$style.main, { [$style.withWallpaper]: withWallpaper, [$style.withSidebarAndTitlebar]: !isMobile && prefer.r['deck.navbarPosition'].value === 'left' && prefer.r.showTitlebar.value }]" :style="{ backgroundImage: prefer.s['deck.wallpaper'] != null ? `url(${ prefer.s['deck.wallpaper'] })` : null }">
+ <div :class="[$style.main, { [$style.withWallpaper]: withWallpaper, [$style.withSidebarAndTitlebar]: !isMobile && prefer.r['deck.navbarPosition'].value === 'left' && prefer.r.showTitlebar.value }]" :style="{ backgroundImage: prefer.s['deck.wallpaper'] != null ? `url(${ prefer.s['deck.wallpaper'] })` : '' }">
<XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'top'"/>
+ <XReloadSuggestion v-if="shouldSuggestReload"/>
+ <XPreferenceRestore v-if="shouldSuggestRestoreBackup"/>
<XAnnouncements v-if="$i"/>
<XStatusBars/>
<div :class="$style.columnsWrapper">
@@ -81,12 +83,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { defineAsyncComponent, ref, useTemplateRef } from 'vue';
-import { genId } from '@/utility/id.js';
import XCommon from './_common_/common.vue';
+import { genId } from '@/utility/id.js';
import XSidebar from '@/ui/_common_/navbar.vue';
import XNavbarH from '@/ui/_common_/navbar-h.vue';
import XMobileFooterMenu from '@/ui/_common_/mobile-footer-menu.vue';
import XTitlebar from '@/ui/_common_/titlebar.vue';
+import XPreferenceRestore from '@/ui/_common_/PreferenceRestore.vue';
+import XReloadSuggestion from '@/ui/_common_/ReloadSuggestion.vue';
import * as os from '@/os.js';
import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
@@ -105,6 +109,8 @@ import XRoleTimelineColumn from '@/ui/deck/role-timeline-column.vue';
import XChatColumn from '@/ui/deck/chat-column.vue';
import { mainRouter } from '@/router.js';
import { columns, layout, columnTypes, switchProfileMenu, addColumn as addColumnToStore, deleteProfile as deleteProfile_ } from '@/deck.js';
+import { shouldSuggestRestoreBackup } from '@/preferences/utility.js';
+import { shouldSuggestReload } from '@/utility/reload-suggest.js';
const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue'));
const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announcements.vue'));
diff --git a/packages/frontend/src/ui/deck/antenna-column.vue b/packages/frontend/src/ui/deck/antenna-column.vue
index 8de894ee88..0042882728 100644
--- a/packages/frontend/src/ui/deck/antenna-column.vue
+++ b/packages/frontend/src/ui/deck/antenna-column.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="async () => { await timeline?.reloadTimeline() }">
<template #header>
- <i class="ti ti-antenna"></i><span style="margin-left: 8px;">{{ column.name || antennaName || i18n.ts._deck._columns.antenna }}</span>
+ <i class="ti ti-antenna"></i><span style="margin-left: 8px;">{{ column.name || column.timelineNameCache || i18n.ts._deck._columns.antenna }}</span>
</template>
<MkStreamingNotesTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId"/>
@@ -35,18 +35,13 @@ const props = defineProps<{
const timeline = useTemplateRef('timeline');
const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
-const antennaName = ref<string | null>(null);
onMounted(() => {
if (props.column.antennaId == null) {
setAntenna();
- }
-});
-
-watch([() => props.column.name, () => props.column.antennaId], () => {
- if (!props.column.name && props.column.antennaId) {
+ } else if (props.column.timelineNameCache == null) {
misskeyApi('antennas/show', { antennaId: props.column.antennaId })
- .then(value => antennaName.value = value.name);
+ .then(value => updateColumn(props.column.id, { timelineNameCache: value.name }));
}
});
@@ -77,6 +72,7 @@ async function setAntenna() {
antennasCache.delete();
updateColumn(props.column.id, {
antennaId: newAntenna.id,
+ timelineNameCache: newAntenna.name,
});
},
closed: () => {
@@ -88,6 +84,7 @@ async function setAntenna() {
updateColumn(props.column.id, {
antennaId: antenna.id,
+ timelineNameCache: antenna.name,
});
}
diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue
index 3439a2a56e..c02499e2d7 100644
--- a/packages/frontend/src/ui/deck/channel-column.vue
+++ b/packages/frontend/src/ui/deck/channel-column.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="async () => { await timeline?.reloadTimeline() }">
<template #header>
- <i class="ti ti-device-tv"></i><span style="margin-left: 8px;">{{ column.name || channel?.name || i18n.ts._deck._columns.channel }}</span>
+ <i class="ti ti-device-tv"></i><span style="margin-left: 8px;">{{ column.name || column.timelineNameCache || i18n.ts._deck._columns.channel }}</span>
</template>
<template v-if="column.channelId">
@@ -46,13 +46,9 @@ const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null,
onMounted(() => {
if (props.column.channelId == null) {
setChannel();
- }
-});
-
-watch([() => props.column.name, () => props.column.channelId], () => {
- if (!props.column.name && props.column.channelId) {
+ } else if (!props.column.name && props.column.channelId) {
misskeyApi('channels/show', { channelId: props.column.channelId })
- .then(value => channel.value = value);
+ .then(value => updateColumn(props.column.id, { timelineNameCache: value.name }));
}
});
@@ -72,7 +68,7 @@ async function setChannel() {
if (canceled || chosenChannel == null) return;
updateColumn(props.column.id, {
channelId: chosenChannel.id,
- name: chosenChannel.name,
+ timelineNameCache: chosenChannel.name,
});
}
diff --git a/packages/frontend/src/ui/deck/list-column.vue b/packages/frontend/src/ui/deck/list-column.vue
index 5b7390b1b2..5c5891ece8 100644
--- a/packages/frontend/src/ui/deck/list-column.vue
+++ b/packages/frontend/src/ui/deck/list-column.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="async () => { await timeline?.reloadTimeline() }">
<template #header>
- <i class="ti ti-list"></i><span style="margin-left: 8px;">{{ (column.name || listName) ?? i18n.ts._deck._columns.list }}</span>
+ <i class="ti ti-list"></i><span style="margin-left: 8px;">{{ column.name || column.timelineNameCache || i18n.ts._deck._columns.list }}</span>
</template>
<MkStreamingNotesTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" :withRenotes="withRenotes"/>
@@ -36,18 +36,13 @@ const props = defineProps<{
const timeline = useTemplateRef('timeline');
const withRenotes = ref(props.column.withRenotes ?? true);
const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
-const listName = ref<string | null>(null);
onMounted(() => {
if (props.column.listId == null) {
setList();
- }
-});
-
-watch([() => props.column.name, () => props.column.listId], () => {
- if (!props.column.name && props.column.listId) {
+ } else if (props.column.timelineNameCache == null) {
misskeyApi('users/lists/show', { listId: props.column.listId })
- .then(value => listName.value = value.name);
+ .then(value => updateColumn(props.column.id, { timelineNameCache: value.name }));
}
});
@@ -89,10 +84,12 @@ async function setList() {
updateColumn(props.column.id, {
listId: res.id,
+ timelineNameCache: res.name,
});
} else {
updateColumn(props.column.id, {
listId: list.id,
+ timelineNameCache: list.name,
});
}
}
diff --git a/packages/frontend/src/ui/deck/role-timeline-column.vue b/packages/frontend/src/ui/deck/role-timeline-column.vue
index ff00dfa6e0..0aafeb56d7 100644
--- a/packages/frontend/src/ui/deck/role-timeline-column.vue
+++ b/packages/frontend/src/ui/deck/role-timeline-column.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="async () => { await timeline?.reloadTimeline() }">
<template #header>
- <i class="ti ti-badge"></i><span style="margin-left: 8px;">{{ column.name || roleName || i18n.ts._deck._columns.roleTimeline }}</span>
+ <i class="ti ti-badge"></i><span style="margin-left: 8px;">{{ column.name || column.timelineNameCache || i18n.ts._deck._columns.roleTimeline }}</span>
</template>
<MkStreamingNotesTimeline v-if="column.roleId" ref="timeline" src="role" :role="column.roleId"/>
@@ -33,18 +33,13 @@ const props = defineProps<{
const timeline = useTemplateRef('timeline');
const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
-const roleName = ref<string | null>(null);
onMounted(() => {
if (props.column.roleId == null) {
setRole();
- }
-});
-
-watch([() => props.column.name, () => props.column.roleId], () => {
- if (!props.column.name && props.column.roleId) {
+ } else if (props.column.timelineNameCache == null) {
misskeyApi('roles/show', { roleId: props.column.roleId })
- .then(value => roleName.value = value.name);
+ .then(value => updateColumn(props.column.id, { timelineNameCache: value.name }));
}
});
@@ -64,6 +59,7 @@ async function setRole() {
if (canceled || role == null) return;
updateColumn(props.column.id, {
roleId: role.id,
+ timelineNameCache: role.name,
});
}
diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue
index f8793d7c75..727fe08989 100644
--- a/packages/frontend/src/ui/universal.vue
+++ b/packages/frontend/src/ui/universal.vue
@@ -12,6 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="[$style.contents, !isMobile && prefer.r.showTitlebar.value ? $style.withSidebarAndTitlebar : null]" @contextmenu.stop="onContextmenu">
<div>
+ <XReloadSuggestion v-if="shouldSuggestReload"/>
<XPreferenceRestore v-if="shouldSuggestRestoreBackup"/>
<XAnnouncements v-if="$i"/>
<XStatusBars :class="$style.statusbars"/>
@@ -38,6 +39,7 @@ import XCommon from './_common_/common.vue';
import type { PageMetadata } from '@/page.js';
import XMobileFooterMenu from '@/ui/_common_/mobile-footer-menu.vue';
import XPreferenceRestore from '@/ui/_common_/PreferenceRestore.vue';
+import XReloadSuggestion from '@/ui/_common_/ReloadSuggestion.vue';
import XTitlebar from '@/ui/_common_/titlebar.vue';
import XSidebar from '@/ui/_common_/navbar.vue';
import * as os from '@/os.js';
@@ -50,6 +52,7 @@ import { mainRouter } from '@/router.js';
import { prefer } from '@/preferences.js';
import { shouldSuggestRestoreBackup } from '@/preferences/utility.js';
import { DI } from '@/di.js';
+import { shouldSuggestReload } from '@/utility/reload-suggest.js';
const XWidgets = defineAsyncComponent(() => import('./_common_/widgets.vue'));
const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue'));
diff --git a/packages/frontend/src/unicode-emoji-indexes/en-US.json b/packages/frontend/src/unicode-emoji-indexes/en-US.json
index 4d8b040ad2..bd8271b911 100644
--- a/packages/frontend/src/unicode-emoji-indexes/en-US.json
+++ b/packages/frontend/src/unicode-emoji-indexes/en-US.json
@@ -44,6 +44,8 @@
"😑": ["face", "indifferent", "-_-", "meh", "deadpan"],
"😒": ["indifference", "bored", "straight face", "serious", "sarcasm", "unimpressed", "skeptical", "dubious", "side_eye"],
"🙄": ["face", "eyeroll", "frustrated"],
+ "🙂â€â†”ï¸": ["face", "head", "horizontally", "no", "shake", "shaking"],
+ "🙂â€â†•ï¸": ["face", "head", "nod", "shaking", "vertically", "yes"],
"🤔": ["face", "hmmm", "think", "consider"],
"🤥": ["face", "lie", "pinocchio"],
"🤭": ["face", "whoops", "shock", "surprise"],
@@ -76,6 +78,7 @@
"😥": ["face", "phew", "sweat", "nervous"],
"🤤": ["face"],
"😪": ["face", "tired", "rest", "nap"],
+ "🫩": ["face", "bags", "bored", "exhausted", "eyes", "fatigued", "late", "sleepy", "tired", "weary"],
"😓": ["face", "hot", "sad", "tired", "exercise"],
"🥵": ["face", "feverish", "heat", "red", "sweating"],
"🥶": ["face", "blue", "freezing", "frozen", "frostbite", "icicles"],
@@ -92,10 +95,11 @@
"🥴": ["face", "dizzy", "intoxicated", "tipsy", "wavy"],
"🥱": ["face", "tired", "yawning"],
"😴": ["face", "tired", "sleepy", "night", "zzz"],
+ "ðŸ‘ï¸â€ðŸ—¨ï¸": ["balloon", "bubble", "eye", "speech", "witness"],
"💤": ["sleepy", "tired", "dream"],
- "😶â€ðŸŒ«ï¸": [],
- "😮â€ðŸ’¨": [],
- "😵â€ðŸ’«": [],
+ "😶â€ðŸŒ«ï¸": ["face", "absentminded", "clouds", "fog", "head"],
+ "😮â€ðŸ’¨": ["face", "blow", "blowing", "exhale", "exhaling", "exhausted", "gasp", "groan", "relief", "sigh", "smiley", "smoke", "whisper", "whistle"],
+ "😵â€ðŸ’«": ["face", "confused", "dizzy", "eyes", "hypnotized", "omg", "smiley", "spiral", "trouble", "whoa", "woah", "woozy"],
"🫠": ["disappear", "dissolve", "liquid", "melt", "toketa"],
"🫢": ["amazement", "awe", "disbelief", "embarrass", "scared", "surprise", "ohoho"],
"🫣": ["captivated", "peep", "stare", "chunibyo"],
@@ -159,19 +163,19 @@
"🤞": ["good", "lucky"],
"🖖": ["hand", "fingers", "spock", "star trek"],
"âœ": ["lower_left_ballpoint_pen", "stationery", "write", "compose"],
- "🫰": [],
- "🫱": [],
- "🫲": [],
- "🫳": [],
- "🫴": [],
- "🫵": [],
+ "🫰": ["<3", "crossed", "expensive", "finger", "hand", "heart", "index", "love", "money", "snap", "thumb"],
+ "🫱": ["hand", "handshake", "hold", "reach", "right", "rightward", "rightwards", "shake"],
+ "🫲": ["hand", "handshake", "hold", "left", "leftward", "leftwards", "reach", "shake"],
+ "🫳": ["dismiss", "down", "drop", "dropped", "hand", "palm", "pick", "shoo", "up"],
+ "🫴": ["beckon", "catch", "come", "hand", "hold", "know", "lift", "me", "offer", "palm", "tell"],
+ "🫵": ["at", "finger", "hand", "index", "pointing", "poke", "viewer", "you"],
"🫶": ["moemoekyun"],
"ðŸ¤": ["hand", "fingers"],
"🤌": ["hand", "fingers"],
"🤳": ["camera", "phone"],
"💅": ["beauty", "manicure", "finger", "fashion", "nail"],
"👄": ["mouth", "kiss"],
- "🫦": [],
+ "🫦": ["anxious", "bite", "biting", "fear", "flirt", "flirting", "kiss", "lip", "lipstick", "nervous", "sexy", "uncomfortable", "worried", "worry"],
"🦷": ["teeth", "dentist"],
"👅": ["mouth", "playful"],
"👂": ["face", "hear", "sound", "listen"],
@@ -180,11 +184,12 @@
"ðŸ‘": ["face", "look", "see", "watch", "stare"],
"👀": ["look", "watch", "stalk", "peek", "see"],
"🧠": ["smart", "intelligent"],
- "🫀": [],
- "ðŸ«": [],
+ "🫀": ["anatomical", "beat", "cardiology", "heart", "heartbeat", "organ", "pulse", "real", "red"],
+ "ðŸ«": ["breath", "breathe", "exhalation", "inhalation", "lung", "lungs", "organ", "respiration"],
"👤": ["user", "person", "human"],
"👥": ["user", "person", "human", "group", "team"],
"🗣": ["user", "person", "human", "sing", "say", "talk"],
+ "🫆": ["clue", "crime", "detective", "fingerprint", "forensics", "identity", "mystery", "print", "safety", "trace"],
"👶": ["child", "boy", "girl", "toddler"],
"🧒": ["gender-neutral", "young"],
"👦": ["man", "male", "guy", "teenager"],
@@ -199,6 +204,7 @@
"👩â€ðŸ¦°": ["woman", "female", "girl", "ginger", "redhead"],
"👨â€ðŸ¦°": ["man", "male", "boy", "guy", "ginger", "redhead"],
"👱â€â™€ï¸": ["woman", "female", "girl", "blonde", "person"],
+ "👱â€â™‚ï¸": ["blond", "blond-haired", "hair", "man"],
"👱": ["man", "male", "boy", "blonde", "guy", "person"],
"🧑â€ðŸ¦³": ["gray", "old", "white"],
"👩â€ðŸ¦³": ["woman", "female", "girl", "gray", "old", "white"],
@@ -207,20 +213,27 @@
"👩â€ðŸ¦²": ["woman", "female", "girl", "bald", "chemotherapy", "hairless", "shaven"],
"👨â€ðŸ¦²": ["man", "male", "boy", "guy", "bald", "chemotherapy", "hairless", "shaven"],
"🧔": ["person", "bewhiskered"],
+ "🧔â€â™€ï¸": ["beard", "bearded", "whiskers", "woman"],
+ "🧔â€â™‚ï¸": ["beard", "bearded", "man", "whiskers"],
"🧓": ["human", "elder", "senior", "gender-neutral"],
"👴": ["human", "male", "men", "old", "elder", "senior"],
"👵": ["human", "female", "women", "lady", "old", "elder", "senior"],
"👲": ["male", "boy", "chinese"],
"🧕": ["female", "hijab", "mantilla", "tichel"],
"👳â€â™€ï¸": ["female", "indian", "hinduism", "arabs", "woman"],
+ "👳â€â™‚ï¸": ["man", "turban", "wearing"],
"👳": ["male", "indian", "hinduism", "arabs"],
"👮â€â™€ï¸": ["woman", "police", "law", "legal", "enforcement", "arrest", "911", "female"],
+ "👮â€â™‚ï¸": ["apprehend", "arrest", "citation", "cop", "law", "man", "officer", "over", "police", "pulled", "undercover"],
"👮": ["man", "police", "law", "legal", "enforcement", "arrest", "911"],
"👷â€â™€ï¸": ["female", "human", "wip", "build", "construction", "worker", "labor", "woman"],
+ "👷â€â™‚ï¸": ["build", "construction", "fix", "hardhat", "hat", "man", "rebuild", "remodel", "repair", "work", "worker"],
"👷": ["male", "human", "wip", "guy", "build", "construction", "worker", "labor"],
"💂â€â™€ï¸": ["uk", "gb", "british", "female", "royal", "woman"],
+ "💂â€â™‚ï¸": ["buckingham", "guard", "helmet", "london", "man", "palace"],
"💂": ["uk", "gb", "british", "male", "guy", "royal"],
"🕵ï¸â€â™€ï¸": ["human", "spy", "detective", "female", "woman"],
+ "🕵ï¸â€â™‚ï¸": ["detective", "man", "sleuth", "spy"],
"🕵": ["human", "spy", "detective"],
"🧑â€âš•ï¸": ["doctor", "nurse", "therapist", "healthcare", "human"],
"👩â€âš•ï¸": ["doctor", "nurse", "therapist", "healthcare", "woman", "human"],
@@ -270,102 +283,152 @@
"🧑â€âš–ï¸": ["justice", "court", "human"],
"👩â€âš–ï¸": ["justice", "court", "woman", "human"],
"👨â€âš–ï¸": ["justice", "court", "man", "human"],
+ "🦸": ["good", "hero", "superhero", "superpower"],
"🦸â€â™€ï¸": ["woman", "female", "good", "heroine", "superpowers"],
"🦸â€â™‚ï¸": ["man", "male", "good", "hero", "superpowers"],
+ "🦹": ["bad", "criminal", "evil", "superpower", "supervillain", "villain"],
"🦹â€â™€ï¸": ["woman", "female", "evil", "bad", "criminal", "heroine", "superpowers"],
"🦹â€â™‚ï¸": ["man", "male", "evil", "bad", "criminal", "hero", "superpowers"],
"🤶": ["woman", "female", "xmas", "mother christmas"],
"🧑â€ðŸŽ„": ["xmas", "christmas"],
"🎅": ["festival", "man", "male", "xmas", "father christmas"],
- "🥷": [],
+ "🥷": ["assassin", "fight", "fighter", "hidden", "ninja", "person", "secret", "skills", "sly", "soldier", "stealth", "war"],
+ "🧙": ["fantasy", "mage", "magic", "play", "sorcerer", "sorceress", "sorcery", "spell", "summon", "witch", "wizard"],
"🧙â€â™€ï¸": ["woman", "female", "mage", "witch"],
"🧙â€â™‚ï¸": ["man", "male", "mage", "sorcerer"],
+ "ðŸ§": ["elf", "elves", "enchantment", "fantasy", "folklore", "magic", "magical", "myth"],
"ðŸ§â€â™€ï¸": ["woman", "female"],
"ðŸ§â€â™‚ï¸": ["man", "male"],
+ "🧛": ["blood", "dracula", "fangs", "halloween", "scary", "supernatural", "teeth", "undead", "vampire"],
"🧛â€â™€ï¸": ["woman", "female"],
"🧛â€â™‚ï¸": ["man", "male", "dracula"],
+ "🧟": ["apocalypse", "dead", "halloween", "horror", "scary", "undead", "walking", "zombie"],
"🧟â€â™€ï¸": ["woman", "female", "undead", "walking dead"],
"🧟â€â™‚ï¸": ["man", "male", "dracula", "undead", "walking dead"],
+ "🧞": ["djinn", "fantasy", "genie", "jinn", "lamp", "myth", "rub", "wishes"],
"🧞â€â™€ï¸": ["woman", "female"],
"🧞â€â™‚ï¸": ["man", "male"],
+ "🧜": ["creature", "fairytale", "folklore", "merperson", "ocean", "sea", "siren", "trident"],
"🧜â€â™€ï¸": ["woman", "female", "merwoman", "ariel"],
"🧜â€â™‚ï¸": ["man", "male", "triton"],
+ "🧚": ["fairy", "fairytale", "fantasy", "myth", "person", "pixie", "tale", "wings"],
"🧚â€â™€ï¸": ["woman", "female"],
"🧚â€â™‚ï¸": ["man", "male"],
"👼": ["heaven", "wings", "halo"],
- "🧌": [],
+ "🧌": ["fairy", "fantasy", "monster", "tale", "troll", "trolling"],
"🤰": ["baby"],
- "🫃": [],
- "🫄": [],
- "🫅": [],
+ "🫃": ["belly", "bloated", "full", "man", "overeat", "pregnant"],
+ "🫄": ["belly", "bloated", "full", "overeat", "person", "pregnant", "stuffed"],
+ "🫅": ["crown", "monarch", "noble", "person", "regal", "royal", "royalty"],
"🤱": ["nursing", "baby"],
- "👩â€ðŸ¼": [],
- "👨â€ðŸ¼": [],
- "🧑â€ðŸ¼": [],
+ "👩â€ðŸ¼": ["baby", "feed", "feeding", "mom", "mother", "nanny", "newborn", "nursing", "woman"],
+ "👨â€ðŸ¼": ["baby", "dad", "father", "feed", "feeding", "man", "nanny", "newborn", "nursing"],
+ "🧑â€ðŸ¼": ["baby", "feed", "feeding", "nanny", "newborn", "nursing", "parent"],
"👸": ["girl", "woman", "female", "blond", "crown", "royal", "queen"],
"🤴": ["boy", "man", "male", "crown", "royal", "king"],
"👰": ["couple", "marriage", "wedding", "woman", "bride"],
- "👰": ["couple", "marriage", "wedding", "woman", "bride"],
- "🤵": ["couple", "marriage", "wedding", "groom"],
+ "👰â€â™€ï¸": ["bride", "veil", "wedding", "woman"],
+ "👰â€â™‚ï¸": ["man", "veil", "wedding"],
"🤵": ["couple", "marriage", "wedding", "groom"],
+ "🤵â€â™€ï¸": ["formal", "tuxedo", "wedding", "woman"],
+ "🤵â€â™‚ï¸": ["formal", "groom", "man", "tuxedo", "wedding"],
+ "ðŸƒâ€âž¡ï¸": ["facing", "fast", "hurry", "marathon", "move", "person", "quick", "race", "racing", "right", "run", "rush", "speed"],
"ðŸƒâ€â™€ï¸": ["woman", "walking", "exercise", "race", "running", "female"],
"ðŸƒ": ["man", "walking", "exercise", "race", "running"],
+ "ðŸƒâ€â™€ï¸â€âž¡ï¸": ["facing", "fast", "hurry", "marathon", "move", "quick", "race", "racing", "right", "run", "rush", "speed", "woman"],
+ "ðŸƒâ€â™‚ï¸": ["fast", "hurry", "man", "marathon", "move", "quick", "race", "racing", "run", "rush", "speed"],
+ "ðŸƒâ€â™‚ï¸â€âž¡ï¸": ["facing", "fast", "hurry", "man", "marathon", "move", "quick", "race", "racing", "right", "run", "rush", "speed"],
+ "🚶â€âž¡ï¸": ["amble", "facing", "gait", "hike", "man", "pace", "pedestrian", "person", "right", "stride", "stroll", "walk", "walking"],
"🚶â€â™€ï¸": ["human", "feet", "steps", "woman", "female"],
"🚶": ["human", "feet", "steps"],
+ "🚶â€â™€ï¸â€âž¡ï¸": ["amble", "facing", "gait", "hike", "man", "pace", "pedestrian", "right", "stride", "stroll", "walk", "walking", "woman"],
+ "🚶â€â™‚ï¸": ["amble", "gait", "hike", "man", "pace", "pedestrian", "stride", "stroll", "walk", "walking"],
+ "🚶â€â™‚ï¸â€âž¡ï¸": ["amble", "facing", "gait", "hike", "man", "pace", "pedestrian", "right", "stride", "stroll", "walk", "walking"],
"💃": ["female", "girl", "woman", "fun"],
"🕺": ["male", "boy", "fun", "dancer"],
"👯": ["female", "bunny", "women", "girls"],
+ "👯â€â™€ï¸": ["bestie", "bff", "bunny", "counterpart", "dancer", "double", "ear", "identical", "pair", "party", "partying", "people", "soulmate", "twin", "twinsies", "women"],
"👯â€â™‚ï¸": ["male", "bunny", "men", "boys"],
"👫": ["pair", "people", "human", "love", "date", "dating", "like", "affection", "valentines", "marriage"],
"🧑â€ðŸ¤â€ðŸ§‘": ["pair", "couple", "love", "like", "bromance", "friendship", "people", "human"],
"👬": ["pair", "couple", "love", "like", "bromance", "friendship", "people", "man", "human"],
"👭": ["pair", "couple", "love", "like", "bromance", "friendship", "people", "female", "human"],
- "🫂": [],
+ "🫂": ["comfort", "embrace", "farewell", "friendship", "goodbye", "hello", "hug", "hugging", "love", "people", "thanks"],
"🙇â€â™€ï¸": ["woman", "female", "girl"],
"🙇": ["man", "male", "boy"],
+ "🙇â€â™‚ï¸": ["apology", "ask", "beg", "bow", "bowing", "favor", "forgive", "gesture", "man", "meditate", "meditation", "pity", "regret", "sorry"],
+ "🤦": ["again", "bewilder", "disbelief", "exasperation", "facepalm", "no", "not", "oh", "omg", "person", "shock", "smh"],
"🤦â€â™‚ï¸": ["man", "male", "boy", "disbelief"],
"🤦â€â™€ï¸": ["woman", "female", "girl", "disbelief"],
"🤷": ["woman", "female", "girl", "confused", "indifferent", "doubt"],
+ "🤷â€â™€ï¸": ["doubt", "dunno", "guess", "idk", "ignorance", "indifference", "knows", "maybe", "shrug", "shrugging", "whatever", "who", "woman"],
"🤷â€â™‚ï¸": ["man", "male", "boy", "confused", "indifferent", "doubt"],
"ðŸ’": ["female", "girl", "woman", "human", "information"],
+ "ðŸ’â€â™€ï¸": ["fetch", "flick", "flip", "gossip", "hand", "sarcasm", "sarcastic", "sassy", "seriously", "tipping", "whatever", "woman"],
"ðŸ’â€â™‚ï¸": ["male", "boy", "man", "human", "information"],
"🙅": ["female", "girl", "woman", "nope"],
+ "🙅â€â™€ï¸": ["forbidden", "gesture", "hand", "no", "not", "prohibit", "woman"],
"🙅â€â™‚ï¸": ["male", "boy", "man", "nope"],
"🙆": ["women", "girl", "female", "pink", "human", "woman"],
+ "🙆â€â™€ï¸": ["exercise", "gesture", "gesturing", "hand", "ok", "omg", "woman"],
"🙆â€â™‚ï¸": ["men", "boy", "male", "blue", "human", "man"],
"🙋": ["female", "girl", "woman"],
+ "🙋â€â™€ï¸": ["gesture", "hand", "here", "know", "me", "pick", "question", "raise", "raising", "woman"],
"🙋â€â™‚ï¸": ["male", "boy", "man"],
"🙎": ["female", "girl", "woman"],
+ "🙎â€â™€ï¸": ["disappointed", "downtrodden", "frown", "grimace", "pouting", "scowl", "sulk", "upset", "whine", "woman"],
"🙎â€â™‚ï¸": ["male", "boy", "man"],
"ðŸ™": ["female", "girl", "woman", "sad", "depressed", "discouraged", "unhappy"],
+ "ðŸ™â€â™€ï¸": ["annoyed", "disappointed", "disgruntled", "disturbed", "frown", "frowning", "frustrated", "gesture", "irritated", "upset", "woman"],
"ðŸ™â€â™‚ï¸": ["male", "boy", "man", "sad", "depressed", "discouraged", "unhappy"],
"💇": ["female", "girl", "woman"],
+ "💇â€â™€ï¸": ["barber", "beauty", "chop", "cosmetology", "cut", "groom", "hair", "haircut", "parlor", "person", "shears", "style", "woman"],
"💇â€â™‚ï¸": ["male", "boy", "man"],
"💆": ["female", "girl", "woman", "head"],
+ "💆â€â™€ï¸": ["face", "getting", "headache", "massage", "relax", "relaxing", "salon", "soothe", "spa", "tension", "therapy", "treatment", "woman"],
"💆â€â™‚ï¸": ["male", "boy", "man", "head"],
+ "🧖": ["day", "luxurious", "pamper", "person", "relax", "room", "sauna", "spa", "steam", "steambath", "unwind"],
"🧖â€â™€ï¸": ["female", "woman", "spa", "steamroom", "sauna"],
"🧖â€â™‚ï¸": ["male", "man", "spa", "steamroom", "sauna"],
+ "ðŸ§": ["accessibility", "deaf", "ear", "gesture", "hear", "person"],
"ðŸ§â€â™€ï¸": ["woman", "female"],
"ðŸ§â€â™‚ï¸": ["man", "male"],
+ "ðŸ§": ["person", "stand", "standing"],
"ðŸ§â€â™€ï¸": ["woman", "female"],
"ðŸ§â€â™‚ï¸": ["man", "male"],
+ "🧎": ["kneel", "kneeling", "knees", "person"],
+ "🧎â€âž¡ï¸": ["facing", "kneel", "kneeling", "knees", "person", "right"],
"🧎â€â™€ï¸": ["woman", "female"],
+ "🧎â€â™€ï¸â€âž¡ï¸": ["facing", "kneel", "kneeling", "knees", "right", "woman"],
"🧎â€â™‚ï¸": ["man", "male"],
+ "🧎â€â™‚ï¸â€âž¡ï¸": ["facing", "kneel", "kneeling", "knees", "man", "right"],
"🧑â€ðŸ¦¯": ["accessibility", "blind"],
+ "🧑â€ðŸ¦¯â€âž¡ï¸": ["accessibility", "blind", "cane", "facing", "person", "probing", "right", "white"],
"👩â€ðŸ¦¯": ["woman", "female", "accessibility", "blind"],
+ "👩â€ðŸ¦¯â€âž¡ï¸": ["accessibility", "blind", "cane", "facing", "probing", "right", "white", "woman"],
"👨â€ðŸ¦¯": ["man", "male", "accessibility", "blind"],
+ "👨â€ðŸ¦¯â€âž¡ï¸": ["accessibility", "blind", "cane", "facing", "man", "probing", "right", "white"],
"🧑â€ðŸ¦¼": ["accessibility"],
+ "🧑â€ðŸ¦¼â€âž¡ï¸": ["accessibility", "facing", "motorized", "person", "right", "wheelchair"],
"👩â€ðŸ¦¼": ["woman", "female", "accessibility"],
+ "👩â€ðŸ¦¼â€âž¡ï¸": ["accessibility", "facing", "motorized", "right", "wheelchair", "woman"],
"👨â€ðŸ¦¼": ["man", "male", "accessibility"],
+ "👨â€ðŸ¦¼â€âž¡ï¸": ["accessibility", "facing", "man", "motorized", "right", "wheelchair"],
"🧑â€ðŸ¦½": ["accessibility"],
+ "🧑â€ðŸ¦½â€âž¡ï¸": ["accessibility", "facing", "manual", "person", "right", "wheelchair"],
"👩â€ðŸ¦½": ["woman", "female", "accessibility"],
+ "👩â€ðŸ¦½â€âž¡ï¸": ["accessibility", "facing", "manual", "right", "wheelchair", "woman"],
"👨â€ðŸ¦½": ["man", "male", "accessibility"],
+ "👨â€ðŸ¦½â€âž¡ï¸": ["accessibility", "facing", "man", "manual", "right", "wheelchair"],
"💑": ["pair", "love", "like", "affection", "human", "dating", "valentines", "marriage"],
+ "👩â€â¤ï¸â€ðŸ‘¨": ["anniversary", "babe", "bae", "couple", "dating", "heart", "kiss", "love", "man", "person", "relationship", "romance", "together", "woman", "you"],
"👩â€â¤ï¸â€ðŸ‘©": ["pair", "love", "like", "affection", "human", "dating", "valentines", "marriage"],
"👨â€â¤ï¸â€ðŸ‘¨": ["pair", "love", "like", "affection", "human", "dating", "valentines", "marriage"],
"ðŸ’": ["pair", "valentines", "love", "like", "dating", "marriage"],
+ "👩â€â¤ï¸â€ðŸ’‹â€ðŸ‘¨": ["anniversary", "babe", "bae", "couple", "date", "dating", "heart", "kiss", "love", "man", "mwah", "person", "romance", "together", "woman", "xoxo"],
"👩â€â¤ï¸â€ðŸ’‹â€ðŸ‘©": ["pair", "valentines", "love", "like", "dating", "marriage"],
"👨â€â¤ï¸â€ðŸ’‹â€ðŸ‘¨": ["pair", "valentines", "love", "like", "dating", "marriage"],
+ "👨â€ðŸ‘©â€ðŸ‘¦": ["boy", "child", "family", "man", "woman"],
"👪": ["home", "parents", "child", "mom", "dad", "father", "mother", "people", "human"],
"👨â€ðŸ‘©â€ðŸ‘§": ["home", "parents", "people", "human", "child"],
"👨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦": ["home", "parents", "people", "human", "children"],
@@ -391,6 +454,10 @@
"👨â€ðŸ‘§â€ðŸ‘¦": ["home", "parent", "people", "human", "children"],
"👨â€ðŸ‘¦â€ðŸ‘¦": ["home", "parent", "people", "human", "children"],
"👨â€ðŸ‘§â€ðŸ‘§": ["home", "parent", "people", "human", "children"],
+ "🧑â€ðŸ§‘â€ðŸ§’": ["adult", "child", "family"],
+ "🧑â€ðŸ§‘â€ðŸ§’â€ðŸ§’": ["adult", "child", "family"],
+ "🧑â€ðŸ§’": ["adult", "child", "family"],
+ "🧑â€ðŸ§’â€ðŸ§’": ["adult", "child", "family"],
"🧶": ["ball", "crochet", "knit"],
"🧵": ["needle", "sewing", "spool", "string"],
"🧥": ["jacket"],
@@ -415,7 +482,7 @@
"👢": ["shoes", "fashion"],
"👞": ["fashion", "male"],
"👟": ["shoes", "sports", "sneakers"],
- "🩴": [],
+ "🩴": ["beach", "flip", "flop", "sandal", "sandals", "shoe", "thong", "thongs", "zÅri"],
"🩰": ["shoes", "sports"],
"🧦": ["stockings", "clothes"],
"🧤": ["hands", "winter", "clothes"],
@@ -424,7 +491,7 @@
"🎩": ["magic", "gentleman", "classy", "circus"],
"🧢": ["cap", "baseball"],
"⛑": ["construction", "build"],
- "🪖": [],
+ "🪖": ["army", "helmet", "military", "soldier", "war", "warrior"],
"🎓": ["school", "college", "degree", "university", "graduation", "cap", "hat", "legal", "learn", "education"],
"👑": ["king", "kod", "leader", "royalty", "lord"],
"🎒": ["student", "education", "bag", "backpack"],
@@ -475,6 +542,7 @@
"ðŸ¦â€â¬›": ["animal", "nature", "bird", "black", "crow", "raven", "rook"],
"🦅": ["animal", "nature", "bird"],
"🦉": ["animal", "nature", "bird", "hoot"],
+ "ðŸ¦â€ðŸ”¥": ["animal", "nature", "ascend", "ascension", "emerge", "fantasy", "firebird", "glory", "immortal", "phoenix", "rebirth", "reincarnation", "reinvent", "renewal", "revival", "revive", "rise", "transform"],
"🦇": ["animal", "nature", "blind", "vampire"],
"ðŸº": ["animal", "nature", "wild"],
"ðŸ—": ["animal", "nature"],
@@ -575,6 +643,7 @@
"🌿": ["vegetable", "plant", "medicine", "weed", "grass", "lawn"],
"☘": ["vegetable", "plant", "nature", "irish", "clover"],
"ðŸ€": ["vegetable", "plant", "nature", "lucky", "irish"],
+ "🪾": ["plant", "nature", "bare", "barren", "branches", "dead", "drought", "leafless", "tree", "trunk", "winter", "wood"],
"ðŸŽ": ["plant", "nature", "vegetable", "panda", "pine_decoration"],
"🎋": ["plant", "nature", "branch", "summer"],
"ðŸƒ": ["nature", "plant", "tree", "vegetable", "grass", "lawn", "spring"],
@@ -644,10 +713,11 @@
"💧": ["water", "drip", "faucet", "spring"],
"💦": ["water", "drip", "oops"],
"🌊": ["sea", "water", "wave", "nature", "tsunami", "disaster"],
- "🪷": [],
- "🪸": [],
- "🪹": [],
- "🪺": [],
+ "🪷": ["plant", "nature", "beauty", "buddhism", "calm", "flower", "hinduism", "lotus", "peace", "purity", "serenity"],
+ "🪸": ["animal", "nature", "change", "climate", "coral", "ocean", "reef", "sea"],
+ "🪹": ["plant", "nature", "branch", "empty", "home", "nest", "nesting"],
+ "🪺": ["plant", "nature", "bird", "branch", "egg", "eggs", "nest", "nesting"],
+ "ðŸ‹â€ðŸŸ©": ["acidity", "citrus", "cocktail", "fruit", "garnish", "key", "lime", "margarita", "mojito", "refreshing", "salsa", "sour", "tangy", "tequila", "tropical", "zest"],
"ðŸ": ["fruit", "nature"],
"ðŸŽ": ["fruit", "mac", "school"],
"ðŸ": ["fruit", "nature", "food"],
@@ -667,6 +737,8 @@
"🥑": ["fruit", "food"],
"🫛": ["beans", "edamame", "legume", "pea", "pod", "vegetable", "food"],
"🥦": ["fruit", "food", "vegetable"],
+ "ðŸ„â€ðŸŸ«": ["food", "fungi", "fungus", "mushroom", "nature", "pizza", "portobello", "shiitake", "shroom", "spore", "sprout", "toppings", "truffle", "vegetable", "vegetarian", "veggie"],
+ "🫜": ["beet", "food", "garden", "radish", "root", "salad", "turnip", "vegetable", "vegetarian"],
"ðŸ…": ["fruit", "vegetable", "nature", "food"],
"ðŸ†": ["vegetable", "nature", "food", "aubergine"],
"🥒": ["fruit", "food", "pickle"],
@@ -759,7 +831,7 @@
"ðŸµ": ["drink", "bowl", "breakfast", "green", "british"],
"🥤": ["drink", "soda"],
"☕": ["beverage", "caffeine", "latte", "espresso"],
- "🫖": [],
+ "🫖": ["brew", "drink", "food", "pot", "tea", "teapot"],
"🧋": ["tapioca"],
"ðŸ¼": ["food", "container", "milk"],
"🧃": ["food", "drink"],
@@ -772,9 +844,9 @@
"🥣": ["food", "breakfast", "cereal", "oatmeal", "porridge"],
"🥡": ["food", "leftovers"],
"🥢": ["food"],
- "🫗": [],
- "🫘": [],
- "🫙": [],
+ "🫗": ["accident", "drink", "empty", "glass", "liquid", "oops", "pour", "pouring", "spill", "water"],
+ "🫘": ["beans", "food", "kidney", "legume", "small"],
+ "🫙": ["condiment", "container", "empty", "jar", "nothing", "sauce", "store"],
"âš½": ["sports", "football"],
"ðŸ€": ["sports", "balls", "NBA"],
"ðŸˆ": ["sports", "balls", "NFL"],
@@ -788,6 +860,7 @@
"⛳": ["sports", "business", "flag", "hole", "summer"],
"ðŸŒï¸â€â™€ï¸": ["sports", "business", "woman", "female"],
"ðŸŒ": ["sports", "business"],
+ "ðŸŒï¸â€â™‚ï¸": ["sport", "ball", "birdie", "caddy", "driving", "golf", "golfing", "green", "man", "pga", "putt", "range", "tee"],
"ðŸ“": ["sports", "pingpong"],
"ðŸ¸": ["sports"],
"🥅": ["sports"],
@@ -799,10 +872,13 @@
"â›·": ["sports", "winter", "snow"],
"ðŸ‚": ["sports", "winter"],
"🤺": ["sports", "fencing", "sword"],
+ "🤼": ["sport", "combat", "duel", "grapple", "people", "ring", "tournament", "wrestle", "wrestling"],
"🤼â€â™€ï¸": ["sports", "wrestlers"],
"🤼â€â™‚ï¸": ["sports", "wrestlers"],
+ "🤸": ["sport", "active", "cartwheel", "cartwheeling", "excited", "flip", "gymnastics", "happy", "person", "somersault"],
"🤸â€â™€ï¸": ["gymnastics"],
"🤸â€â™‚ï¸": ["gymnastics"],
+ "🤾": ["sport", "athletics", "ball", "catch", "chuck", "handball", "hurl", "lob", "person", "pitch", "playing", "throw", "toss"],
"🤾â€â™€ï¸": ["sports"],
"🤾â€â™‚ï¸": ["sports"],
"⛸": ["sports"],
@@ -815,32 +891,42 @@
"🥋": ["judo", "karate", "taekwondo"],
"🚣â€â™€ï¸": ["sports", "hobby", "water", "ship", "woman", "female"],
"🚣": ["sports", "hobby", "water", "ship"],
+ "🚣â€â™‚ï¸": ["sport", "boat", "canoe", "cruise", "fishing", "lake", "man", "oar", "paddle", "raft", "river", "row", "rowboat", "rowing"],
+ "🧗": ["climb", "climber", "climbing", "mountain", "person", "rock", "scale", "up"],
"🧗â€â™€ï¸": ["sports", "hobby", "woman", "female", "rock"],
"🧗â€â™‚ï¸": ["sports", "hobby", "man", "male", "rock"],
"ðŸŠâ€â™€ï¸": ["sports", "exercise", "human", "athlete", "water", "summer", "woman", "female"],
"ðŸŠ": ["sports", "exercise", "human", "athlete", "water", "summer"],
+ "ðŸŠâ€â™‚ï¸": ["sport", "freestyle", "man", "swim", "swimmer", "swimming", "triathlon"],
+ "🤽": ["sport", "person", "playing", "polo", "swimming", "water", "waterpolo"],
"🤽â€â™€ï¸": ["sports", "pool"],
"🤽â€â™‚ï¸": ["sports", "pool"],
+ "🧘": ["cross", "legged", "legs", "lotus", "meditation", "peace", "person", "position", "relax", "serenity", "yoga", "yogi", "zen"],
"🧘â€â™€ï¸": ["woman", "female", "meditation", "yoga", "serenity", "zen", "mindfulness"],
"🧘â€â™‚ï¸": ["man", "male", "meditation", "yoga", "serenity", "zen", "mindfulness"],
"ðŸ„â€â™€ï¸": ["sports", "ocean", "sea", "summer", "beach", "woman", "female"],
"ðŸ„": ["sports", "ocean", "sea", "summer", "beach"],
+ "ðŸ„â€â™‚ï¸": ["sport", "beach", "man", "ocean", "surf", "surfer", "surfing", "swell", "waves"],
"🛀": ["clean", "shower", "bathroom"],
"⛹ï¸â€â™€ï¸": ["sports", "human", "woman", "female"],
"⛹": ["sports", "human"],
+ "⛹ï¸â€â™‚ï¸": ["sport", "athletic", "ball", "basketball", "bouncing", "championship", "dribble", "man", "net", "player", "throw"],
"ðŸ‹ï¸â€â™€ï¸": ["sports", "training", "exercise", "woman", "female"],
"ðŸ‹": ["sports", "training", "exercise"],
+ "ðŸ‹ï¸â€â™‚ï¸": ["sport", "barbell", "bodybuilder", "deadlift", "lifter", "lifting", "man", "powerlifting", "weight", "weightlifter", "weights", "workout"],
"🚴â€â™€ï¸": ["sports", "bike", "exercise", "hipster", "woman", "female"],
"🚴": ["sports", "bike", "exercise", "hipster"],
+ "🚴â€â™‚ï¸": ["sport", "bicycle", "bicyclist", "bike", "biking", "cycle", "cyclist", "man", "riding"],
"🚵â€â™€ï¸": ["transportation", "sports", "human", "race", "bike", "woman", "female"],
"🚵": ["transportation", "sports", "human", "race", "bike"],
+ "🚵â€â™‚ï¸": ["sport", "bicycle", "bicyclist", "bike", "biking", "cycle", "cyclist", "man", "mountain", "riding"],
"ðŸ‡": ["animal", "betting", "competition", "gambling", "luck"],
"🤿": ["sports"],
"🪀": ["sports"],
"ðŸª": ["sports"],
"🦺": ["sports"],
- "🪡": [],
- "🪢": [],
+ "🪡": ["embroidery", "needle", "sew", "sewing", "stitches", "sutures", "tailoring", "thread"],
+ "🪢": ["cord", "knot", "rope", "tangled", "tie", "twine", "twist"],
"🕴": ["suit", "business", "levitate", "hover", "jump"],
"ðŸ†": ["win", "award", "contest", "place", "ftw", "ceremony"],
"🎽": ["play", "pageant"],
@@ -856,6 +942,7 @@
"🎭": ["acting", "theater", "drama"],
"🎨": ["design", "paint", "draw", "colors"],
"🎪": ["festival", "carnival", "party"],
+ "🤹": ["sport", "act", "balance", "balancing", "handle", "juggle", "juggling", "manage", "multitask", "person", "skill"],
"🤹â€â™€ï¸": ["juggle", "balance", "skill", "multitask"],
"🤹â€â™‚ï¸": ["juggle", "balance", "skill", "multitask"],
"🎤": ["sound", "music", "PA", "sing", "talkshow"],
@@ -872,6 +959,7 @@
"🪕": ["music", "instrument"],
"🪗": ["music", "instrument"],
"🪘": ["music", "instrument"],
+ "🪉": ["cupid", "harp", "instrument", "love", "music", "orchestra"],
"🎬": ["movie", "film", "record"],
"🎮": ["play", "console", "PS4", "controller"],
"👾": ["game", "arcade", "play"],
@@ -881,11 +969,11 @@
"🎰": ["bet", "gamble", "vegas", "fruit machine", "luck", "casino"],
"🧩": ["interlocking", "puzzle", "piece"],
"🎳": ["sports", "fun", "play"],
- "🪄": [],
- "🪅": [],
- "🪆": [],
- "🪬": [],
- "🪩": [],
+ "🪄": ["magic", "magician", "wand", "witch", "wizard"],
+ "🪅": ["candy", "celebrate", "celebration", "cinco", "de", "festive", "mayo", "party", "pinada", "pinata", "piñata"],
+ "🪆": ["babooshka", "baboushka", "babushka", "doll", "dolls", "matryoshka", "nesting", "russia"],
+ "🪬": ["amulet", "fatima", "fortune", "guide", "hamsa", "hand", "mary", "miriam", "palm", "protect", "protection"],
+ "🪩": ["ball", "dance", "disco", "glitter", "mirror", "party"],
"🚗": ["red", "transportation", "vehicle"],
"🚕": ["uber", "vehicle", "cars", "transportation"],
"🚙": ["transportation", "vehicle"],
@@ -941,7 +1029,7 @@
"🚀": ["launch", "ship", "staffmode", "NASA", "outer space", "outer_space", "fly"],
"🛰": ["communication", "gps", "orbit", "spaceflight", "NASA", "ISS"],
"🛻": ["car"],
- "🛼": [],
+ "🛼": ["blades", "roller", "skate", "skates", "sport"],
"💺": ["sit", "airplane", "transport", "bus", "flight", "fly"],
"🛶": ["boat", "paddle", "water", "ship"],
"âš“": ["ship", "ferry", "sea", "boat"],
@@ -1013,12 +1101,12 @@
"🕋": ["mecca", "mosque", "islam"],
"⛩": ["temple", "japan", "kyoto"],
"🛕": ["temple"],
- "🪨": [],
- "🪵": [],
- "🛖": [],
- "ðŸ›": [],
- "🛞": [],
- "🛟": [],
+ "🪨": ["boulder", "heavy", "rock", "solid", "stone", "tough"],
+ "🪵": ["log", "lumber", "timber", "wood"],
+ "🛖": ["home", "house", "hut", "roundhouse", "shelter", "yurt"],
+ "ðŸ›": ["amusement", "park", "play", "playground", "playing", "slide", "sliding", "theme"],
+ "🛞": ["car", "circle", "tire", "turn", "vehicle", "wheel"],
+ "🛟": ["buoy", "float", "life", "lifesaver", "preserver", "rescue", "ring", "safety", "save", "saver", "swim"],
"⌚": ["time", "accessories"],
"📱": ["technology", "apple", "gadgets", "dial"],
"📲": ["iphone", "incoming"],
@@ -1059,7 +1147,7 @@
"⌛": ["time", "clock", "oldschool", "limit", "exam", "quiz", "test"],
"📡": ["communication", "future", "radio", "space"],
"🔋": ["power", "energy", "sustain"],
- "🪫": [],
+ "🪫": ["battery", "drained", "electronic", "energy", "low", "power"],
"🔌": ["charger", "power"],
"💡": ["light", "electricity", "idea"],
"🔦": ["dark", "camping", "sight", "night"],
@@ -1075,9 +1163,11 @@
"💰": ["dollar", "payment", "coins", "sale"],
"🪙": ["dollar", "payment", "coins", "sale"],
"💳": ["money", "sales", "dollar", "bill", "payment", "shopping"],
- "🪪": [],
+ "🪪": ["card", "credentials", "document", "id", "identification", "license", "security"],
+ "🥾": ["backpacking", "boot", "brown", "camping", "hiking", "outdoors", "shoe"],
"💎": ["blue", "ruby", "diamond", "jewelry"],
"âš–": ["law", "fairness", "weight"],
+ "⛓ï¸â€ðŸ’¥": ["break", "breaking", "broken", "chain", "cuffs", "freedom"],
"🧰": ["tools", "diy", "fix", "maintainer", "mechanic"],
"🔧": ["tools", "diy", "ikea", "fix", "maintainer"],
"🔨": ["tools", "build", "create"],
@@ -1093,6 +1183,7 @@
"🪛": ["tool"],
"ðŸª": ["tool"],
"🪜": ["tool"],
+ "ðŸª": ["bury", "dig", "garden", "hole", "plant", "scoop", "shovel", "snow", "spade"],
"🧱": ["bricks"],
"⛓": ["lock", "arrest"],
"🧲": ["attraction", "magnetic"],
@@ -1123,8 +1214,8 @@
"🩺": ["health", "hospital", "medicine", "needle", "doctor", "nurse"],
"🪒": ["health"],
"🪮": ["afro", "comb", "hair", "pick"],
- "🩻": [],
- "🩼": [],
+ "🩻": ["bones", "doctor", "medical", "skeleton", "skull", "x-ray", "xray"],
+ "🩼": ["aid", "cane", "crutch", "disability", "help", "hurt", "injured", "mobility", "stick"],
"🧬": ["biologist", "genetics", "life"],
"🧫": ["bacteria", "biology", "culture", "lab"],
"🧪": ["chemistry", "experiment", "lab", "science"],
@@ -1159,7 +1250,7 @@
"🪤": ["household"],
"🪣": ["household"],
"🪥": ["household"],
- "🫧": [],
+ "🫧": ["bubble", "bubbles", "burp", "clean", "floating", "pearl", "soap", "underwater"],
"â›±": ["weather", "summer"],
"🗿": ["rock", "easter island", "moai"],
"ðŸ›": ["mall", "buy", "purchase"],
@@ -1249,8 +1340,8 @@
"🖌": ["drawing", "creativity", "art"],
"ðŸ”": ["search", "zoom", "find", "detective"],
"🔎": ["search", "zoom", "find", "detective"],
- "🪦": [],
- "🪧": [],
+ "🪦": ["cemetery", "dead", "grave", "graveyard", "headstone", "memorial", "rip", "tomb", "tombstone"],
+ "🪧": ["card", "demonstration", "notice", "picket", "placard", "plaque", "protest", "sign"],
"💯": ["score", "perfect", "numbers", "century", "exam", "quiz", "test", "pass", "hundred"],
"🔢": ["numbers", "blue-square"],
"🩷": ["love", "like", "affection", "valentines"],
@@ -1275,8 +1366,8 @@
"💘": ["love", "like", "heart", "affection", "valentines"],
"ðŸ’": ["love", "valentines"],
"💟": ["purple-square", "love", "like"],
- "â¤ï¸â€ðŸ”¥": [],
- "â¤ï¸â€ðŸ©¹": [],
+ "â¤ï¸â€ðŸ”¥": ["burn", "fire", "heart", "love", "lust", "sacred"],
+ "â¤ï¸â€ðŸ©¹": ["healthier", "heart", "improving", "mending", "recovering", "recuperating", "well"],
"☮": ["hippie"],
"âœ": ["christianity"],
"☪": ["islam"],
@@ -1304,6 +1395,8 @@
"♓": ["purple-square", "sign", "zodiac", "astrology"],
"🆔": ["purple-square", "words"],
"âš›": ["science", "physics", "chemistry"],
+ "♀ï¸": ["female", "sign", "woman", "pink-square"],
+ "♂ï¸": ["male", "man", "sign", "blue-square"],
"âš§ï¸": ["purple-square", "woman", "female", "toilet", "loo", "restroom", "gender"],
"🈳": ["kanji", "japanese", "chinese", "empty", "sky", "blue-square", "aki"],
"🈹": ["cut", "divide", "chinese", "kanji", "pink-square", "waribiki"],
@@ -1459,13 +1552,15 @@
"âž–": ["math", "calculation", "subtract", "less"],
"âž—": ["divide", "math", "calculation"],
"✖ï¸": ["math", "calculation"],
- "🟰": [],
+ "🟰": ["answer", "equal", "equality", "equals", "heavy", "math", "sign"],
"♾": ["forever"],
"💲": ["money", "sales", "payment", "currency", "buck"],
"💱": ["money", "sales", "dollar", "travel"],
+ "âš•ï¸": ["aesculapius", "medical", "medicine", "staff", "symbol"],
"©ï¸": ["ip", "license", "circle", "law", "legal"],
"®ï¸": ["alphabet", "circle"],
"â„¢ï¸": ["trademark", "brand", "law", "legal"],
+ "🫟": ["drip", "holi", "ink", "liquid", "mess", "paint", "spill", "splatter", "stain"],
"🔚": ["words", "arrow"],
"🔙": ["arrow", "words", "return"],
"🔛": ["arrow", "words"],
@@ -1576,6 +1671,7 @@
"🇧🇲": ["bm", "bermuda", "flag", "nation", "country", "banner"],
"🇧🇹": ["bt", "bhutan", "flag", "nation", "country", "banner"],
"🇧🇴": ["bo", "bolivia", "flag", "nation", "country", "banner"],
+ "🇧🇻": ["bv", "bouvet", "island", "flag", "nation", "country", "banner"],
"🇧🇶": ["bq", "bonaire", "flag", "nation", "country", "banner"],
"🇧🇦": ["ba", "bosnia", "herzegovina", "flag", "nation", "country", "banner"],
"🇧🇼": ["bw", "botswana", "flag", "nation", "country", "banner"],
@@ -1593,10 +1689,12 @@
"🇮🇨": ["ic", "canary", "islands", "flag", "nation", "country", "banner"],
"🇰🇾": ["ky", "cayman", "islands", "flag", "nation", "country", "banner"],
"🇨🇫": ["cf", "central", "african", "republic", "flag", "nation", "country", "banner"],
+ "🇪🇦": ["ea", "ceuta", "melilla", "flag", "nation", "country", "banner"],
"🇹🇩": ["td", "chad", "flag", "nation", "country", "banner"],
"🇨🇱": ["cl", "chile", "flag", "nation", "country", "banner"],
"🇨🇳": ["cn", "china", "chinese", "prc", "flag", "country", "nation", "banner"],
"🇨🇽": ["cx", "christmas", "island", "flag", "nation", "country", "banner"],
+ "🇨🇵": ["cp", "clipperton", "island", "flag", "nation", "country", "banner"],
"🇨🇨": ["cc", "cocos", "keeling", "islands", "flag", "nation", "country", "banner"],
"🇨🇴": ["co", "colombia", "flag", "nation", "country", "banner"],
"🇰🇲": ["km", "comoros", "flag", "nation", "country", "banner"],
@@ -1610,6 +1708,7 @@
"🇨🇾": ["cy", "cyprus", "flag", "nation", "country", "banner"],
"🇨🇿": ["cz", "czech", "republic", "flag", "nation", "country", "banner"],
"🇩🇰": ["dk", "denmark", "flag", "nation", "country", "banner"],
+ "🇩🇬": ["dg", "diego", "garcia", "flag", "nation", "country", "banner"],
"🇩🇯": ["dj", "djibouti", "flag", "nation", "country", "banner"],
"🇩🇲": ["dm", "dominica", "flag", "nation", "country", "banner"],
"🇩🇴": ["do", "dominican", "republic", "flag", "nation", "country", "banner"],
@@ -1646,6 +1745,7 @@
"🇬🇼": ["gw", "guiana", "bissau", "flag", "nation", "country", "banner"],
"🇬🇾": ["gy", "guyana", "flag", "nation", "country", "banner"],
"🇭🇹": ["ht", "haiti", "flag", "nation", "country", "banner"],
+ "🇭🇲": ["hm", "heard", "mcdonald", "islands", "flag", "nation", "country", "banner"],
"🇭🇳": ["hn", "honduras", "flag", "nation", "country", "banner"],
"🇭🇰": ["hk", "hong", "kong", "flag", "nation", "country", "banner"],
"🇭🇺": ["hu", "hungary", "flag", "nation", "country", "banner"],
@@ -1733,10 +1833,12 @@
"🇷🇴": ["ro", "romania", "flag", "nation", "country", "banner"],
"🇷🇺": ["ru", "russian", "federation", "flag", "nation", "country", "banner"],
"🇷🇼": ["rw", "rwanda", "flag", "nation", "country", "banner"],
+ "🇨🇶": ["cq", "sark", "flag", "nation", "country", "banner"],
"🇧🇱": ["bl", "saint", "barthélemy", "flag", "nation", "country", "banner"],
"🇸🇭": ["sh", "saint", "helena", "ascension", "tristan", "cunha", "flag", "nation", "country", "banner"],
"🇰🇳": ["kn", "saint", "kitts", "nevis", "flag", "nation", "country", "banner"],
"🇱🇨": ["lc", "saint", "lucia", "flag", "nation", "country", "banner"],
+ "🇲🇫": ["mf", "st", "martin", "flag", "nation", "country", "banner"],
"🇵🇲": ["pm", "saint", "pierre", "miquelon", "flag", "nation", "country", "banner"],
"🇻🇨": ["vc", "saint", "vincent", "grenadines", "flag", "nation", "country", "banner"],
"🇼🇸": ["ws", "western", "samoa", "flag", "nation", "country", "banner"],
@@ -1762,6 +1864,7 @@
"🇸🇩": ["sd", "sudan", "flag", "nation", "country", "banner"],
"🇸🇷": ["sr", "suriname", "flag", "nation", "country", "banner"],
"🇸🇿": ["sz", "eswatini", "flag", "nation", "country", "banner"],
+ "🇸🇯": ["sj", "svalbard", "jan", "mayen", "flag", "nation", "country", "banner"],
"🇸🇪": ["se", "sweden", "flag", "nation", "country", "banner"],
"🇨🇭": ["ch", "switzerland", "confoederatio", "helvetica", "flag", "nation", "country", "banner"],
"🇸🇾": ["sy", "syrian", "arab", "republic", "flag", "nation", "country", "banner"],
@@ -1788,6 +1891,7 @@
"ðŸ´ó §ó ¢ó ³ó £ó ´ó ¿": ["flag", "scottish"],
"ðŸ´ó §ó ¢ó ·ó ¬ó ³ó ¿": ["flag", "welsh"],
"🇺🇸": ["us", "usa", "united", "states", "america", "flag", "nation", "country", "banner"],
+ "🇺🇲": ["um", "us", "outlying", "islands", "flag", "nation", "country", "banner"],
"🇻🇮": ["vi", "virgin", "islands", "us", "flag", "nation", "country", "banner"],
"🇺🇾": ["uy", "uruguay", "flag", "nation", "country", "banner"],
"🇺🇿": ["uz", "uzbekistan", "flag", "nation", "country", "banner"],
diff --git a/packages/frontend/src/unicode-emoji-indexes/ja-JP.json b/packages/frontend/src/unicode-emoji-indexes/ja-JP.json
index 9c491804f2..136e96759e 100644
--- a/packages/frontend/src/unicode-emoji-indexes/ja-JP.json
+++ b/packages/frontend/src/unicode-emoji-indexes/ja-JP.json
@@ -36,6 +36,9 @@
"🤡":["ピエロã®é¡”","ピエロ","é¡”"],
"ðŸ˜":["ã«ã‚„ã«ã‚„ã—ãŸé¡”","é¡”","ã«ã‚„ã«ã‚„"],
"😶":["å£ã®ãªã„é¡”","é¡”","å£","é™ã‹ã«","沈黙"],
+ "🙂â€â†”ï¸":["ã„ã„ãˆ","ä¸è³›æˆ","é¡”","首を振る","é¦–ã‚’æ¨ªã«æŒ¯ã‚‹"],
+ "🙂â€â†•ï¸":["ã¯ã„","é ·ã","é¡”","首を振る","é¦–ã‚’ç¸¦ã«æŒ¯ã‚‹"],
+ "🫩":["クマ","徹夜","疲れãŸ","眠ã„","é¡”"],
"🫥":["点線ã®é¡”","è½ã¡è¾¼ã‚“ã ","消ãˆã‚‹","隠れる","内å‘çš„","ç›®ã«è¦‹ãˆãªã„"],
"ðŸ˜":["普通ã®é¡”","無表情","é¡”","å¹³é™"],
"🫤":["å£ãŒæ–œã‚ã«ãªã£ãŸé¡”","ãŒã£ã‹ã‚Š","無関心","ç–‘ã„æ·±ã„","ä¸å®‰"],
@@ -111,6 +114,38 @@
"💩":["ã†ã‚“ã¡","マンガ","漫画","フン","é¡”","モンスター"],
"👻":["ãŠåŒ–ã‘","妖怪","é¡”","ãŠã¨ãŽè©±","ファンタジー","幽霊","モンスター","ãƒãƒ­ã‚¦ã‚£ãƒ¼ãƒ³"],
"💀":["ドクロ","体","æ­»","é¡”","ãŠã¨ãŽè©±","モンスター","骸骨","ãƒãƒ­ã‚¦ã‚£ãƒ¼ãƒ³"],
+ "🫆":["指紋","é‘‘è­˜","生体èªè¨¼","セキュリティ"],
+ "ðŸƒâ€âž¡ï¸":["ジョギング","マラソン","ランナー","ランニング","å³å‘ã","急ã","èµ°ã‚‹","走る人","駆ã‘è¶³"],
+ "ðŸƒâ€â™€ï¸â€âž¡ï¸":["ジョギング","マラソン","ランナー","ランニング","å³å‘ã","女性","急ã","èµ°ã‚‹","走る女","駆ã‘è¶³"],
+ "ðŸƒâ€â™‚ï¸â€âž¡ï¸":["ジョギング","マラソン","ランナー","ランニング","å³å‘ã","男性","èµ°ã‚‹ç”·"],
+ "🚶â€âž¡ï¸":["ウォーキング","ãƒã‚¤ã‚­ãƒ³ã‚°","ã¶ã‚‰ã¤ã","人","å³å‘ã","大股","散歩","æ­©ã","歩行","歩行者"],
+ "🚶â€â™€ï¸â€âž¡ï¸":["ウォーキング","ãƒã‚¤ã‚­ãƒ³ã‚°","å³å‘ã","女性","散歩","æ­©ã","æ­©ã女","歩行","歩行者"],
+ "🚶â€â™‚ï¸â€âž¡ï¸":["ウォーキング","å³å‘ã","æ­©ãç”·","歩行","歩行者","男性"],
+ "🧎â€âž¡ï¸":["ã²ã–ã¾ãšã","人","å³å‘ã","座る","正座ã™ã‚‹äºº"],
+ "🧎â€â™€ï¸â€âž¡ï¸":["ã²ã–ã¾ãšã","å³å‘ã","女性","座る","正座ã™ã‚‹å¥³æ€§"],
+ "🧎â€â™‚ï¸â€âž¡ï¸":["ã²ã–ã¾ãšã","å³å‘ã","座る","正座ã™ã‚‹ç”·æ€§","男性"],
+ "🧑â€ðŸ¦¯â€âž¡ï¸":["アクセシビリティ","å³å‘ã","æ–ã‚’ã¤ã„ãŸäºº","ç›®","視覚","éšœãŒã„"],
+ "👩â€ðŸ¦¯â€âž¡ï¸":["アクセシビリティ","å³å‘ã","女性","æ–ã‚’ã¤ã„ãŸå¥³æ€§","白æ–ã‚’ã¤ã„ãŸå¥³æ€§","ç›®","視覚","éšœãŒã„"],
+ "👨â€ðŸ¦¯â€âž¡ï¸":["アクセシビリティ","å³å‘ã","æ–ã‚’ã¤ã„ãŸç”·æ€§","男性","白æ–ã‚’ã¤ã„ãŸç”·æ€§","ç›®","視覚","éšœãŒã„"],
+ "🧑â€ðŸ¦¼â€âž¡ï¸":["アクセシビリティ","å³å‘ã","車ã„ã™","éšœãŒã„","電動車椅å­ã®äºº"],
+ "👩â€ðŸ¦¼â€âž¡ï¸":["アクセシビリティ","å³å‘ã","女性","車ã„ã™","éšœãŒã„","電動車椅å­ã®å¥³æ€§"],
+ "👨â€ðŸ¦¼â€âž¡ï¸":["アクセシビリティ","å³å‘ã","男性","車ã„ã™","éšœãŒã„","電動車椅å­ã®ç”·æ€§"],
+ "🧑â€ðŸ¦½â€âž¡ï¸":["アクセシビリティ","å³å‘ã","手動å¼è»Šæ¤…å­ã®äºº","車ã„ã™","éšœãŒã„"],
+ "👩â€ðŸ¦½â€âž¡ï¸":["アクセシビリティ","å³å‘ã","女性","手動å¼è»Šæ¤…å­ã®å¥³æ€§","車ã„ã™","éšœãŒã„"],
+ "👨â€ðŸ¦½â€âž¡ï¸":["アクセシビリティ","å³å‘ã","手動å¼è»Šæ¤…å­ã®ç”·æ€§","男性","車ã„ã™","éšœãŒã„"],
+ "👨â€ðŸ‘©â€ðŸ‘¦":["女性","å­ä¾›","å®¶æ—","ç”·ã®å­","男性","親å­"],
+ "🧑â€ðŸ§‘â€ðŸ§’":["大人二人","å­ä¾›ä¸€äºº","å®¶æ—","大人二人ã¨å­ä¾›ä¸€äºº","親å­"],
+ "🧑â€ðŸ§‘â€ðŸ§’â€ðŸ§’":["大人二人","å­ä¾›äºŒäºº","å®¶æ—","大人二人ã¨å­ä¾›äºŒäºº","親å­"],
+ "🧑â€ðŸ§’":["大人一人","å­ä¾›ä¸€äºº","å®¶æ—","大人一人ã¨å­ä¾›ä¸€äºº","親å­"],
+ "🧑â€ðŸ§’â€ðŸ§’":["大人一人","å­ä¾›äºŒäºº","å®¶æ—","大人一人ã¨å­ä¾›äºŒäºº","親å­"],
+ "ðŸ¦â€ðŸ”¥":["ファンタジー","フェニックス","䏿­»é³¥","å†ç”Ÿ","復活","ç«ã®é³¥","生ã¾ã‚Œå¤‰ã‚り","神話","転生","輪廻"],
+ "🪾":["䏿¯›","å¹²ã°ã¤","葉ã®ãªã„木","冬"],
+ "ðŸ‹â€ðŸŸ©":["ã•ã‚ã‚„ã‹","トロピカル","フルーツ","マルガリータ","モヒート","ライム","果物","柑橘類","ç·‘","é…¸ã£ã±ã„"],
+ "ðŸ„â€ðŸŸ«":["ãã®ã“","キノコ","ã—ã„ãŸã‘","トリュフ","ブラウンマッシュルーム","ãƒãƒ¼ãƒˆãƒ™ãƒ­ãƒ¼","マッシュルーム","椎茸","茸","èŒé¡ž"],
+ "🫜":["ビーツ","庭","æ ¹","カブ","野èœ"],
+ "🪉":["オーケストラ","キューピッド","ãƒãƒ¼ãƒ—","弦楽器","æ„›","楽器","音楽"],
+ "⛓ï¸â€ðŸ’¥":["ãã•り","ãƒã‚§ãƒ¼ãƒ³","壊れãŸ","壊れãŸéŽ–"],
+ "ðŸª":["シャベル","ショベル","スコップ","掘る","ç©´","鋤"],
"☠":["ドクロマーク","体","交差ã—ãŸéª¨","æ­»","é¡”","モンスター","骸骨","ãƒãƒ­ã‚¦ã‚£ãƒ¼ãƒ³"],
"👽":["宇宙人","怪ç£","異星人","é¡”","ãŠã¨ãŽè©±","ファンタジー","モンスター","宇宙","UFO"],
"🤖":["ロボットã®é¡”","é¡”","モンスター","ロボット"],
@@ -1518,6 +1553,7 @@
"©ï¸":["コピーライトマーク","著作権"],
"®ï¸":["登録商標マーク","登録済ã¿","商標"],
"â„¢ï¸":["商標マーク","マーク","tm","商標"],
+ "🫟":["ã—ã¶ã","ペンキ","飛沫","é£›ã³æ•£ã‚Š","スプラッシュ"],
"🔚":["ENDã¨å·¦çŸ¢å°","矢å°","端"],
"🔙":["BACKã¨å·¦çŸ¢å°","矢å°","戻る"],
"🔛":["ON!ã¨å·¦å³çŸ¢å°","矢å°","マーク","オン"],
@@ -1643,6 +1679,7 @@
"🇧🇷":["ブラジル国旗","ブラジル","国旗"],
"🇧🇸":["ãƒãƒãƒžå›½æ——","ãƒãƒãƒž","国旗"],
"🇧🇹":["ブータン国旗","ブータン","国旗"],
+ "🇧🇻":["ãƒ–ãƒ¼ãƒ™å³¶ã®æ——","ブーベ島","国旗"],
"🇧🇼":["ボツワナ国旗","ボツワナ","国旗"],
"🇧🇾":["ベラルーシ国旗","ベラルーシ","国旗"],
"🇧🇿":["ベリーズ国旗","ベリーズ","国旗"],
@@ -1658,6 +1695,8 @@
"🇨🇲":["カメルーン国旗","カメルーン","国旗"],
"🇨🇳":["中国国旗","中国","国旗"],
"🇨🇴":["コロンビア国旗","コロンビア","国旗"],
+ "🇨🇵":["ã‚¯ãƒªãƒƒãƒ‘ãƒ¼ãƒˆãƒ³å³¶ã®æ——","クリッパートン島","国旗"],
+ "🇨🇶":["ã‚µãƒ¼ã‚¯å³¶ã®æ——", "サーク島", "国旗"],
"🇨🇷":["コスタリカ国旗","コスタリカ","国旗"],
"🇨🇺":["キューãƒå›½æ——","キューãƒ","国旗"],
"🇨🇻":["カーボベルデ国旗","カーボ","ケープ","国旗","ベルデ"],
@@ -1666,11 +1705,13 @@
"🇨🇾":["キプロス国旗","キプロス","国旗"],
"🇨🇿":["ãƒã‚§ã‚³å›½æ——","ãƒã‚§ã‚³å…±å’Œå›½","国旗"],
"🇩🇪":["ドイツ国旗","国旗","ドイツ"],
+ "🇩🇬":["ãƒ‡ã‚£ã‚¨ã‚´ã‚¬ãƒ«ã‚·ã‚¢å³¶ã®æ——","ディエゴガルシア島","国旗"],
"🇩🇯":["ジブãƒå›½æ——","ジブãƒ","国旗"],
"🇩🇰":["デンマーク国旗","デンマーク","国旗"],
"🇩🇲":["ドミニカ国旗","ドミニカ","国旗"],
"🇩🇴":["ドミニカ共和国国旗","ドミニカ共和国","国旗"],
"🇩🇿":["アルジェリア国旗","アルジェリア","国旗"],
+ "🇪🇦":["ã‚»ã‚¦ã‚¿ãƒ»ãƒ¡ãƒªãƒªãƒ£ã®æ——","セウタ・メリリャ","国旗"],
"🇪🇨":["エクアドル国旗","エクアドル","国旗"],
"ðŸ´ó §ó ¢ó ¥ó ®ó §ó ¿":["ã‚¤ãƒ³ã‚°ãƒ©ãƒ³ãƒ‰ã®æ——","イングランド","æ——"],
"🇪🇪":["エストニア国旗","エストニア","国旗"],
@@ -1706,6 +1747,7 @@
"🇬🇼":["ギニアビサウ国旗","ビサウ","国旗","ギニア"],
"🇬🇾":["ガイアナ国旗","国旗","ガイアナ"],
"🇭🇰":["é¦™æ¸¯ã®æ——","中国","国旗","香港"],
+ "🇭🇲":["ãƒãƒ¼ãƒ‰å³¶ãƒ»ãƒžã‚¯ãƒ‰ãƒŠãƒ«ãƒ‰è«¸å³¶ã®æ——","ãƒãƒ¼ãƒ‰å³¶ãƒ»ãƒžã‚¯ãƒ‰ãƒŠãƒ«ãƒ‰è«¸å³¶","国旗"],
"🇭🇳":["ホンジュラス国旗","国旗","ホンジュラス"],
"🇭🇷":["クロアãƒã‚¢å›½æ——","クロアãƒã‚¢","国旗"],
"🇭🇹":["ãƒã‚¤ãƒå›½æ——","国旗","ãƒã‚¤ãƒ"],
@@ -1751,6 +1793,7 @@
"🇲🇨":["モナコ国旗","国旗","モナコ"],
"🇲🇩":["モルドãƒå›½æ——","国旗","モルドãƒ"],
"🇲🇪":["モンテãƒã‚°ãƒ­å›½æ——","国旗","モンテãƒã‚°ãƒ­"],
+ "🇲🇫":["ã‚µãƒ³ãƒ»ãƒžãƒ«ã‚¿ãƒ³ã®æ——","サン・マルタン","国旗"],
"🇲🇬":["マダガスカル国旗","国旗","マダガスカル"],
"🇲🇭":["マーシャル諸島国旗","国旗","諸島","マーシャル"],
"🇲🇰":["マケドニア国旗","国旗","マケドニア"],
@@ -1811,6 +1854,7 @@
"🇸🇬":["シンガãƒãƒ¼ãƒ«å›½æ——","国旗","シンガãƒãƒ¼ãƒ«"],
"🇸🇭":["ã‚»ãƒ³ãƒˆãƒ˜ãƒ¬ãƒŠå³¶ã®æ——","æ——","ヘレナ","セント"],
"🇸🇮":["スロベニア国旗","国旗","スロベニア"],
+ "🇸🇯":["スãƒãƒ¼ãƒ«ãƒãƒ«è«¸å³¶ãƒ»ãƒ¤ãƒ³ãƒžã‚¤ã‚¨ãƒ³å³¶ã®æ——","スãƒãƒ¼ãƒ«ãƒãƒ«è«¸å³¶ãƒ»ãƒ¤ãƒ³ãƒžã‚¤ã‚¨ãƒ³å³¶","国旗"],
"🇸🇰":["スロãƒã‚­ã‚¢å›½æ——","国旗","スロãƒã‚­ã‚¢"],
"🇸🇱":["シエラレオãƒå›½æ——","国旗","シエラレオãƒ"],
"🇸🇲":["サンマリノ国旗","国旗","サンマリノ"],
@@ -1842,6 +1886,7 @@
"🇹🇿":["タンザニア国旗","国旗","タンザニア"],
"🇺🇦":["ウクライナ国旗","国旗","ウクライナ"],
"🇺🇬":["ウガンダ国旗","国旗","ウガンダ"],
+ "🇺🇲":["åˆè¡†å›½é ˜æœ‰å°é›¢å³¶ã®æ——","åˆè¡†å›½é ˜æœ‰å°é›¢å³¶","国旗"],
"🇺🇳":["å›½é€£ã®æ——","æ——","国連","連åˆ","国際"],
"🇺🇸":["アメリカ国旗","アメリカ","æ——","åˆè¡†","åˆè¡†å›½","アメリカåˆè¡†å›½","åˆè¡†å›½é ˜æœ‰å°é›¢å³¶"],
"🇺🇾":["ウルグアイ国旗","国旗","ウルグアイ"],
diff --git a/packages/frontend/src/unicode-emoji-indexes/ja-JP_hira.json b/packages/frontend/src/unicode-emoji-indexes/ja-JP_hira.json
index 2ad282d501..7dfe022d72 100644
--- a/packages/frontend/src/unicode-emoji-indexes/ja-JP_hira.json
+++ b/packages/frontend/src/unicode-emoji-indexes/ja-JP_hira.json
@@ -36,6 +36,9 @@
"🤡": ["ã´ãˆã‚ã®ã‹ãŠ","ã´ãˆã‚","ã‹ãŠ"],
"ðŸ˜": ["ã«ã‚„ã«ã‚„ã—ãŸã‹ãŠ","ã‹ãŠ","ã«ã‚„ã«ã‚„"],
"😶": ["ãã¡ã®ãªã„ã‹ãŠ","ã‹ãŠ","ãã¡","ã—ãšã‹ã«","ã¡ã‚“ã‚‚ã"],
+ "🙂â€â†”ï¸": ["ã„ã„ãˆ","ãµã•ã‚“ã›ã„","ã‹ãŠ","ãã³ã‚’ãµã‚‹","ãã³ã‚’よã“ã«ãµã‚‹"],
+ "🙂â€â†•ï¸": ["ã¯ã„","ã†ãªãšã","ã‹ãŠ","ãã³ã‚’ãµã‚‹","ãã³ã‚’ãŸã¦ã«ãµã‚‹"],
+ "🫩": ["ãã¾","ã¦ã¤ã‚„","ã¤ã‹ã‚ŒãŸ","ã­ã‚€ã„","ã‹ãŠ"],
"🫥": ["ã¦ã‚“ã›ã‚“ã®ã‹ãŠ","ãŠã¡ã“ã‚“ã ","ããˆã‚‹","ã‹ãれる","ãªã„ã“ã†ã¦ã","ã‚ã«ã¿ãˆãªã„"],
"ðŸ˜": ["ãµã¤ã†ã®ã‹ãŠ","ã‚€ã²ã‚‡ã†ã˜ã‚‡ã†","ã‹ãŠ","ã¸ã„ã›ã„"],
"🫤": ["ãã¡ãŒãªãªã‚ã«ãªã£ãŸã‹ãŠ","ãŒã£ã‹ã‚Š","ã‚€ã‹ã‚“ã—ã‚“","ã†ãŸãŒã„ã¶ã‹ã„","ãµã‚ã‚“"],
@@ -111,6 +114,38 @@
"💩": ["ã†ã‚“ã¡","ã¾ã‚“ãŒ","ãµã‚“","ã‹ãŠ","ã‚‚ã‚“ã™ãŸãƒ¼"],
"👻": ["ãŠã°ã‘","よã†ã‹ã„","ã‹ãŠ","ãŠã¨ãŽã°ãªã—","ãµãã‚“ãŸã˜ãƒ¼","ゆã†ã‚Œã„","ã‚‚ã‚“ã™ãŸãƒ¼","ã¯ã‚ã†ãƒãƒ¼ã‚“"],
"💀": ["ã©ãã‚","ã‹ã‚‰ã ","ã—","ã‹ãŠ","ãŠã¨ãŽã°ãªã—","ã‚‚ã‚“ã™ãŸãƒ¼","ãŒã„ã“ã¤","ã¯ã‚ã†ãƒãƒ¼ã‚“"],
+ "🫆": ["ã—ã‚‚ã‚“","ã‹ã‚“ã—ã","ã›ã„ãŸã„ã«ã‚“ã—ょã†","ã›ãゅりã¦ãƒ"],
+ "ðŸƒâ€âž¡ï¸": ["ã˜ã‚‡ãŽã‚“ã","ã¾ã‚‰ãã‚“","らんãªãƒ¼","らんã«ã‚“ã","ã¿ãŽã‚€ã","ã„ãã","ã¯ã—ã‚‹","ã¯ã—ã‚‹ã²ã¨","ã‹ã‘ã‚ã—"],
+ "ðŸƒâ€â™€ï¸â€âž¡ï¸": ["ã˜ã‚‡ãŽã‚“ã","ã¾ã‚‰ãã‚“","らんãªãƒ¼","らんã«ã‚“ã","ã¿ãŽã‚€ã","ã˜ã‚‡ã›ã„","ã„ãã","ã¯ã—ã‚‹","ã¯ã—ã‚‹ãŠã‚“ãª","ã‹ã‘ã‚ã—"],
+ "ðŸƒâ€â™‚ï¸â€âž¡ï¸": ["ã˜ã‚‡ãŽã‚“ã","ã¾ã‚‰ãã‚“","らんãªãƒ¼","らんã«ã‚“ã","ã¿ãŽã‚€ã","ã ã‚“ã›ã„","ã¯ã—ã‚‹ãŠã¨ã“"],
+ "🚶â€âž¡ï¸": ["ã†ã‰ãƒ¼ãã‚“ã","ã¯ã„ãã‚“ã","ã¶ã‚‰ã¤ã","ã²ã¨","ã¿ãŽã‚€ã","ãŠãŠã¾ãŸ","ã•ã‚“ã½","ã‚ã‚‹ã","ã»ã“ã†","ã»ã“ã†ã—ゃ"],
+ "🚶â€â™€ï¸â€âž¡ï¸": ["ã†ã‰ãƒ¼ãã‚“ã","ã¯ã„ãã‚“ã","ã¿ãŽã‚€ã","ã˜ã‚‡ã›ã„","ã•ã‚“ã½","ã‚ã‚‹ã","ã‚ã‚‹ããŠã‚“ãª","ã»ã“ã†","ã»ã“ã†ã—ゃ"],
+ "🚶â€â™‚ï¸â€âž¡ï¸": ["ã†ã‰ãƒ¼ãã‚“ã","ã¿ãŽã‚€ã","ã‚ã‚‹ããŠã¨ã“","ã»ã“ã†","ã»ã“ã†ã—ゃ","ã ã‚“ã›ã„"],
+ "🧎â€âž¡ï¸": ["ã²ã–ã¾ãšã","ã²ã¨","ã¿ãŽã‚€ã","ã™ã‚ã‚‹","ã›ã„ã–ã™ã‚‹ã²ã¨"],
+ "🧎â€â™€ï¸â€âž¡ï¸": ["ã²ã–ã¾ãšã","ã¿ãŽã‚€ã","ã˜ã‚‡ã›ã„","ã™ã‚ã‚‹","ã›ã„ã–ã™ã‚‹ã˜ã‚‡ã›ã„"],
+ "🧎â€â™‚ï¸â€âž¡ï¸": ["ã²ã–ã¾ãšã","ã¿ãŽã‚€ã","ã™ã‚ã‚‹","ã›ã„ã–ã™ã‚‹ã ã‚“ã›ã„","ã ã‚“ã›ã„"],
+ "🧑â€ðŸ¦¯â€âž¡ï¸": ["ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã¿ãŽã‚€ã","ã¤ãˆã‚’ã¤ã„ãŸã²ã¨","ã‚","ã—ã‹ã","ã—ょã†ãŒã„"],
+ "👩â€ðŸ¦¯â€âž¡ï¸": ["ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã¿ãŽã‚€ã","ã˜ã‚‡ã›ã„","ã¤ãˆã‚’ã¤ã„ãŸã˜ã‚‡ã›ã„","ã¯ãã˜ã‚‡ã†ã‚’ã¤ã„ãŸã˜ã‚‡ã›ã„","ã‚","ã—ã‹ã","ã—ょã†ãŒã„"],
+ "👨â€ðŸ¦¯â€âž¡ï¸": ["ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã¿ãŽã‚€ã","ã¤ãˆã‚’ã¤ã„ãŸã ã‚“ã›ã„","ã ã‚“ã›ã„","ã¯ãã˜ã‚‡ã†ã‚’ã¤ã„ãŸã ã‚“ã›ã„","ã‚","ã—ã‹ã","ã—ょã†ãŒã„"],
+ "🧑â€ðŸ¦¼â€âž¡ï¸": ["ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã¿ãŽã‚€ã","ãã‚‹ã¾ã„ã™","ã—ょã†ãŒã„","ã§ã‚“ã©ã†ãã‚‹ã¾ã„ã™ã®ã²ã¨"],
+ "👩â€ðŸ¦¼â€âž¡ï¸": ["ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã¿ãŽã‚€ã","ã˜ã‚‡ã›ã„","ãã‚‹ã¾ã„ã™","ã—ょã†ãŒã„","ã§ã‚“ã©ã†ãã‚‹ã¾ã„ã™ã®ã˜ã‚‡ã›ã„"],
+ "👨â€ðŸ¦¼â€âž¡ï¸": ["ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã¿ãŽã‚€ã","ã ã‚“ã›ã„","ãã‚‹ã¾ã„ã™","ã—ょã†ãŒã„","ã§ã‚“ã©ã†ãã‚‹ã¾ã„ã™ã®ã ã‚“ã›ã„"],
+ "🧑â€ðŸ¦½â€âž¡ï¸": ["ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã¿ãŽã‚€ã","ã—ã‚…ã©ã†ã—ããã‚‹ã¾ã„ã™ã®ã²ã¨","ãã‚‹ã¾ã„ã™","ã—ょã†ãŒã„"],
+ "👩â€ðŸ¦½â€âž¡ï¸": ["ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã¿ãŽã‚€ã","ã˜ã‚‡ã›ã„","ã—ã‚…ã©ã†ã—ããã‚‹ã¾ã„ã™ã®ã˜ã‚‡ã›ã„","ãã‚‹ã¾ã„ã™","ã—ょã†ãŒã„"],
+ "👨â€ðŸ¦½â€âž¡ï¸": ["ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã¿ãŽã‚€ã","ã—ã‚…ã©ã†ã—ããã‚‹ã¾ã„ã™ã®ã ã‚“ã›ã„","ã ã‚“ã›ã„","ãã‚‹ã¾ã„ã™","ã—ょã†ãŒã„"],
+ "👨â€ðŸ‘©â€ðŸ‘¦": ["ã˜ã‚‡ã›ã„","ã“ã©ã‚‚","ã‹ãžã","ãŠã¨ã“ã®ã“","ã ã‚“ã›ã„","ãŠã‚„ã“"],
+ "🧑â€ðŸ§‘â€ðŸ§’": ["ãŠã¨ãªãµãŸã‚Š","ã“ã©ã‚‚ã²ã¨ã‚Š","ã‹ãžã","ãŠã¨ãªãµãŸã‚Šã¨ã“ã©ã‚‚ã²ã¨ã‚Š","ãŠã‚„ã“"],
+ "🧑â€ðŸ§‘â€ðŸ§’â€ðŸ§’": ["ãŠã¨ãªãµãŸã‚Š","ã“ã©ã‚‚ãµãŸã‚Š","ã‹ãžã","ãŠã¨ãªãµãŸã‚Šã¨ã“ã©ã‚‚ãµãŸã‚Š","ãŠã‚„ã“"],
+ "🧑â€ðŸ§’": ["ãŠã¨ãªã²ã¨ã‚Š","ã“ã©ã‚‚ã²ã¨ã‚Š","ã‹ãžã","ãŠã¨ãªã²ã¨ã‚Šã¨ã“ã©ã‚‚ã²ã¨ã‚Š","ãŠã‚„ã“"],
+ "🧑â€ðŸ§’â€ðŸ§’": ["ãŠã¨ãªã²ã¨ã‚Š","ã“ã©ã‚‚ãµãŸã‚Š","ã‹ãžã","ãŠã¨ãªã²ã¨ã‚Šã¨ã“ã©ã‚‚ãµãŸã‚Š","ãŠã‚„ã“"],
+ "ðŸ¦â€ðŸ”¥": ["ãµãã‚“ãŸã˜ãƒ¼","ãµã‡ã«ã£ãã™","ãµã—ã¡ã‚‡ã†","ã•ã„ã›ã„","ãµã£ã‹ã¤","ã²ã®ã¨ã‚Š","ã†ã¾ã‚Œã‹ã‚り","ã—ã‚“ã‚","ã¦ã‚“ã›ã„","りんã­"],
+ "🪾": ["ãµã‚‚ã†","ã‹ã‚“ã°ã¤","ã¯ã®ãªã„ã","ãµã‚†"],
+ "ðŸ‹â€ðŸŸ©": ["ã•ã‚ã‚„ã‹","ã¨ã‚ã´ã‹ã‚‹","ãµã‚‹ãƒ¼ã¤","ã¾ã‚‹ãŒã‚Šãƒ¼ãŸ","ã‚‚ã²ãƒ¼ã¨","らã„ã‚€","ãã ã‚‚ã®","ã‹ã‚“ãã¤ã‚‹ã„","ã¿ã©ã‚Š","ã™ã£ã±ã„"],
+ "ðŸ„â€ðŸŸ«": ["ãã®ã“","ã—ã„ãŸã‘","ã¨ã‚Šã‚…ãµ","ã¶ã‚‰ã†ã‚“ã¾ã£ã—ゅるーむ","ã½ãƒ¼ã¨ã¹ã‚ー","ã¾ã£ã—ゅるーむ","ãŸã‘","ãã‚“ã‚‹ã„"],
+ "🫜": ["ã³ãƒ¼ã¤","ã«ã‚","ã­","ã‹ã¶","ã‚„ã•ã„"],
+ "🪉": ["ãŠãƒ¼ã‘ã™ã¨ã‚‰","ãゅーã´ã£ã©","ã¯ãƒ¼ã·","ã’ã‚“ãŒã£ã","ã‚ã„","ãŒã£ã","ãŠã‚“ãŒã"],
+ "⛓ï¸â€ðŸ’¥": ["ãã•り","ã¡ã‡ãƒ¼ã‚“","ã“ã‚れãŸ","ã“ã‚れãŸãã•り"],
+ "ðŸª": ["ã—ゃã¹ã‚‹","ã—ょã¹ã‚‹","ã™ã“ã£ã·","ã»ã‚‹","ã‚ãª","ã™ã"],
"☠": ["ã©ãã‚ã¾ãƒ¼ã","ã‹ã‚‰ã ","ã“ã†ã•ã—ãŸã»ã­","ã—","ã‹ãŠ","ã‚‚ã‚“ã™ãŸãƒ¼","ãŒã„ã“ã¤","ã¯ã‚ã†ãƒãƒ¼ã‚“"],
"👽": ["ã†ã¡ã‚…ã†ã˜ã‚“","ã‹ã„ã˜ã‚…ã†","ã„ã›ã„ã˜ã‚“","ã‹ãŠ","ãŠã¨ãŽã°ãªã—","ãµãã‚“ãŸã˜ãƒ¼","ã‚‚ã‚“ã™ãŸãƒ¼","ã†ã¡ã‚…ã†","UFO"],
"🤖": ["ã‚ã¼ã£ã¨ã®ã‹ãŠ","ã‹ãŠ","ã‚‚ã‚“ã™ãŸãƒ¼","ã‚ã¼ã£ã¨"],
@@ -382,9 +417,9 @@
"🚶â€â™€ï¸": ["ã‚ã‚‹ãã˜ã‚‡ã›ã„","ã¯ã„ãã‚“ã","ã»ã“ã†ã—ゃ","ã‚ã‚‹ã","ã†ã‰ãƒ¼ãã‚“ã","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"],
"🚶": ["ã‚ã‚‹ãã²ã¨","ã¯ã„ãã‚“ã","ã»ã“ã†ã—ゃ","ã‚ã‚‹ã","ã†ã‰ãƒ¼ãã‚“ã"],
"🚶â€â™‚ï¸": ["ã‚ã‚‹ãã ã‚“ã›ã„","ã¯ã„ãã‚“ã","ã»ã“ã†ã—ゃ","ã‚ã‚‹ã","ã†ã‰ãƒ¼ãã‚“ã","ãŠã¨ã“","ã ã‚“ã›ã„"],
- "👩â€ðŸ¦¯": ["ã—ã‚ã¤ãˆã‚’ã‚‚ã£ãŸã˜ã‚‡ã›ã„","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã‚ãŒãµã˜ã‚†ã†","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"],
- "🧑â€ðŸ¦¯": ["ã—ã‚ã¤ãˆã‚’ã‚‚ã£ãŸã²ã¨","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã‚ãŒãµã˜ã‚†ã†"],
- "👨â€ðŸ¦¯": ["ã—ã‚ã¤ãˆã‚’ã‚‚ã£ãŸã ã‚“ã›ã„","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã‚ãŒãµã˜ã‚†ã†","ã ã‚“ã›ã„","ãŠã¨ã“"],
+ "👩â€ðŸ¦¯": ["ã¯ãã˜ã‚‡ã†ã‚’ã‚‚ã£ãŸã˜ã‚‡ã›ã„","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã‚ãŒãµã˜ã‚†ã†","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"],
+ "🧑â€ðŸ¦¯": ["ã¯ãã˜ã‚‡ã†ã‚’ã‚‚ã£ãŸã²ã¨","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã‚ãŒãµã˜ã‚†ã†"],
+ "👨â€ðŸ¦¯": ["ã¯ãã˜ã‚‡ã†ã‚’ã‚‚ã£ãŸã ã‚“ã›ã„","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã‚ãŒãµã˜ã‚†ã†","ã ã‚“ã›ã„","ãŠã¨ã“"],
"ðŸƒâ€â™€ï¸": ["ã¯ã—ã‚‹ã˜ã‚‡ã›ã„","ã¾ã‚‰ãã‚“","らんãªãƒ¼","らんã«ã‚“ã","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"],
"ðŸƒ": ["ã¯ã—ã‚‹ã²ã¨","ã¾ã‚‰ãã‚“","らんãªãƒ¼","らんã«ã‚“ã"],
"ðŸƒâ€â™‚ï¸": ["ã¯ã—ã‚‹ã ã‚“ã›ã„","ã¾ã‚‰ãã‚“","らんãªãƒ¼","らんã«ã‚“ã","ãŠã¨ã“","ã ã‚“ã›ã„"],
@@ -1129,7 +1164,7 @@
"🧾": ["りょã†ã—ã‚…ã†ã—ょ","ã‹ã„ã‘ã„","ã¼ã","ã—ょã†ã“","ã—ょã†ã‚ã„"],
"💎": ["ã»ã†ã›ã","ã ã„ã‚ã‚‚ã‚“ã©","ã˜ã‚…ãˆã‚‹","ã‚ã¾ã‚“ã™"],
"âš–": ["ã¯ã‹ã‚Š","ã¦ã‚“ã³ã‚“","ã“ã†ã›ã„","ã¦ã‚“ã³ã‚“ã–","ã‚‚ã®ã•ã—","ã©ã†ã","ã˜ã‚…ã†ã‚Šã‚‡ã†","ã›ã„ã–"],
- "🦯": ["ã—ã‚ã¤ãˆ","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã‚ãŒãµã˜ã‚†ã†"],
+ "🦯": ["ã¯ãã˜ã‚‡ã†","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã‚ãŒãµã˜ã‚†ã†"],
"🧰": ["ã©ã†ãã°ã“","ã‚€ã­","ã›ã„ã³ã—","ã“ã†ã"],
"🔧": ["れんã¡","ã©ã†ã"],
"🪛": ["ã©ã‚‰ã„ã°ãƒ¼","ã­ã˜","ã“ã†ã"],
@@ -1518,6 +1553,7 @@
"©ï¸": ["ã“ã´ãƒ¼ã‚‰ã„ã¨ã¾ãƒ¼ã","ã¡ã‚‡ã•ãã‘ã‚“"],
"®ï¸": ["ã¨ã†ã‚ãã—ょã†ã²ã‚‡ã†ã¾ãƒ¼ã","ã¨ã†ã‚ããšã¿","ã—ょã†ã²ã‚‡ã†"],
"â„¢ï¸": ["ã—ょã†ã²ã‚‡ã†ã¾ãƒ¼ã","ã¾ãƒ¼ã","tm","ã—ょã†ã²ã‚‡ã†"],
+ "🫟": ["ã—ã¶ã","ãºã‚“ã","ã²ã¾ã¤","ã¨ã³ã¡ã‚Š","ã™ã·ã‚‰ã£ã—ã‚…"],
"🔚": ["ENDã¨ã²ã ã‚Šã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã¯ã˜"],
"🔙": ["BACKã¨ã²ã ã‚Šã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã‚‚ã©ã‚‹"],
"🔛": ["ON!ã¨ã•ゆã†ã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã¾ãƒ¼ã","ãŠã‚“"],
@@ -1643,6 +1679,7 @@
"🇧🇷": ["ã¶ã‚‰ã˜ã‚‹ã“ã£ã","ã¶ã‚‰ã˜ã‚‹","ã“ã£ã"],
"🇧🇸": ["ã°ã¯ã¾ã“ã£ã","ã°ã¯ã¾","ã“ã£ã"],
"🇧🇹": ["ã¶ãƒ¼ãŸã‚“ã“ã£ã","ã¶ãƒ¼ãŸã‚“","ã“ã£ã"],
+ "🇧🇻": ["ã¶ãƒ¼ã¹ã¨ã†ã®ã¯ãŸ","ã¶ãƒ¼ã¹ã¨ã†","ã“ã£ã"],
"🇧🇼": ["ã¼ã¤ã‚ãªã“ã£ã","ã¼ã¤ã‚ãª","ã“ã£ã"],
"🇧🇾": ["ã¹ã‚‰ã‚‹ãƒ¼ã—ã“ã£ã","ã¹ã‚‰ã‚‹ãƒ¼ã—","ã“ã£ã"],
"🇧🇿": ["ã¹ã‚Šãƒ¼ãšã“ã£ã","ã¹ã‚Šãƒ¼ãš","ã“ã£ã"],
@@ -1658,6 +1695,8 @@
"🇨🇲": ["ã‹ã‚るーんã“ã£ã","ã‹ã‚るーん","ã“ã£ã"],
"🇨🇳": ["ã¡ã‚…ã†ã”ãã“ã£ã","ã¡ã‚…ã†ã”ã","ã“ã£ã"],
"🇨🇴": ["ã“ã‚ã‚“ã³ã‚ã“ã£ã","ã“ã‚ã‚“ã³ã‚","ã“ã£ã"],
+ "🇨🇵": ["ãりã£ã±ãƒ¼ã¨ã‚“ã¨ã†ã®ã¯ãŸ","ãりã£ã±ãƒ¼ã¨ã‚“ã¨ã†","ã“ã£ã"],
+ "🇨🇶": ["ã•ーãã¨ã†ã®ã¯ãŸ", "ã•ーãã¨ã†", "ã“ã£ã"],
"🇨🇷": ["ã“ã™ãŸã‚Šã‹ã“ã£ã","ã“ã™ãŸã‚Šã‹","ã“ã£ã"],
"🇨🇺": ["ãゅーã°ã“ã£ã","ãゅーã°","ã“ã£ã"],
"🇨🇻": ["ã‹ãƒ¼ã¼ã¹ã‚‹ã§ã“ã£ã","ã‹ãƒ¼ã¼","ã‘ーã·","ã“ã£ã","ã¹ã‚‹ã§"],
@@ -1666,11 +1705,13 @@
"🇨🇾": ["ãã·ã‚ã™ã“ã£ã","ãã·ã‚ã™","ã“ã£ã"],
"🇨🇿": ["ã¡ã‡ã“ã“ã£ã","ã¡ã‡ã“ãょã†ã‚ã“ã","ã“ã£ã"],
"🇩🇪": ["ã©ã„ã¤ã“ã£ã","ã“ã£ã","ã©ã„ã¤"],
+ "🇩🇬": ["ã§ãƒãˆã”ãŒã‚‹ã—ã‚ã¨ã†ã®ã¯ãŸ","ã§ãƒãˆã”ãŒã‚‹ã—ã‚ã¨ã†","ã“ã£ã"],
"🇩🇯": ["ã˜ã¶ã¡ã“ã£ã","ã˜ã¶ã¡","ã“ã£ã"],
"🇩🇰": ["ã§ã‚“ã¾ãƒ¼ãã“ã£ã","ã§ã‚“ã¾ãƒ¼ã","ã“ã£ã"],
"🇩🇲": ["ã©ã¿ã«ã‹ã“ã£ã","ã©ã¿ã«ã‹","ã“ã£ã"],
"🇩🇴": ["ã©ã¿ã«ã‹ãょã†ã‚ã“ãã“ã£ã","ã©ã¿ã«ã‹ãょã†ã‚ã“ã","ã“ã£ã"],
"🇩🇿": ["ã‚ã‚‹ã˜ã‡ã‚Šã‚ã“ã£ã","ã‚ã‚‹ã˜ã‡ã‚Šã‚","ã“ã£ã"],
+ "🇪🇦": ["ã›ã†ãŸãƒ»ã‚りりゃã®ã¯ãŸ","ã›ã†ãŸãƒ»ã‚りりゃ","ã“ã£ã"],
"🇪🇨": ["ãˆãã‚ã©ã‚‹ã“ã£ã","ãˆãã‚ã©ã‚‹","ã“ã£ã"],
"ðŸ´ó §ó ¢ó ¥ó ®ó §ó ¿": ["ã„ã‚“ãらんã©ã®ã¯ãŸ","ã„ã‚“ãらんã©","ã“ã£ã"],
"🇪🇪": ["ãˆã™ã¨ã«ã‚ã“ã£ã","ãˆã™ã¨ã«ã‚","ã“ã£ã"],
@@ -1706,6 +1747,7 @@
"🇬🇼": ["ãŽã«ã‚ã³ã•ã†ã“ã£ã","ã³ã•ã†","ã“ã£ã","ãŽã«ã‚"],
"🇬🇾": ["ãŒã„ã‚ãªã“ã£ã","ã“ã£ã","ãŒã„ã‚ãª"],
"🇭🇰": ["ã»ã‚“ã“ã‚“ã®ã¯ãŸ","ã¡ã‚…ã†ã”ã","ã“ã£ã","ã»ã‚“ã“ã‚“"],
+ "🇭🇲": ["ã¯ãƒ¼ã©ã¨ã†ãƒ»ã¾ãã©ãªã‚‹ã©ã—ょã¨ã†ã®ã¯ãŸ","ã¯ãƒ¼ã©ã¨ã†ãƒ»ã¾ãã©ãªã‚‹ã©ã—ょã¨ã†","ã“ã£ã"],
"🇭🇳": ["ã»ã‚“ã˜ã‚…らã™ã“ã£ã","ã“ã£ã","ã»ã‚“ã˜ã‚…らã™"],
"🇭🇷": ["ãã‚ã‚ã¡ã‚ã“ã£ã","ãã‚ã‚ã¡ã‚","ã“ã£ã"],
"🇭🇹": ["ã¯ã„ã¡ã“ã£ã","ã“ã£ã","ã¯ã„ã¡"],
@@ -1751,6 +1793,7 @@
"🇲🇨": ["ã‚‚ãªã“ã“ã£ã","ã“ã£ã","ã‚‚ãªã“"],
"🇲🇩": ["ã‚‚ã‚‹ã©ã°ã“ã£ã","ã“ã£ã","ã‚‚ã‚‹ã©ã°"],
"🇲🇪": ["ã‚‚ã‚“ã¦ã­ãã‚ã“ã£ã","ã“ã£ã","ã‚‚ã‚“ã¦ã­ãã‚"],
+ "🇲🇫": ["ã•ん・ã¾ã‚‹ãŸã‚“ã®ã¯ãŸ","ã•ん・ã¾ã‚‹ãŸã‚“","ã“ã£ã"],
"🇲🇬": ["ã¾ã ãŒã™ã‹ã‚‹ã“ã£ã","ã“ã£ã","ã¾ã ãŒã™ã‹ã‚‹"],
"🇲🇭": ["ã¾ãƒ¼ã—ゃるã—ょã¨ã†ã“ã£ã","ã“ã£ã","ã—ょã¨ã†","ã¾ãƒ¼ã—ゃる"],
"🇲🇰": ["ã¾ã‘ã©ã«ã‚ã“ã£ã","ã“ã£ã","ã¾ã‘ã©ã«ã‚"],
@@ -1811,6 +1854,7 @@
"🇸🇬": ["ã—ã‚“ãŒã½ãƒ¼ã‚‹ã“ã£ã","ã“ã£ã","ã—ã‚“ãŒã½ãƒ¼ã‚‹"],
"🇸🇭": ["ã›ã‚“ã¨ã¸ã‚Œãªã¨ã†ã®ã¯ãŸ","ã¯ãŸ","ã¸ã‚Œãª","ã›ã‚“ã¨"],
"🇸🇮": ["ã™ã‚ã¹ã«ã‚ã“ã£ã","ã“ã£ã","ã™ã‚ã¹ã«ã‚"],
+ "🇸🇯": ["ã™ã°ãƒ¼ã‚‹ã°ã‚‹ã—ょã¨ã†ãƒ»ã‚„ã‚“ã¾ã„ãˆã‚“ã¨ã†ã®ã¯ãŸ","ã™ã°ãƒ¼ã‚‹ã°ã‚‹ã—ょã¨ã†ãƒ»ã‚„ã‚“ã¾ã„ãˆã‚“ã¨ã†","ã“ã£ã"],
"🇸🇰": ["ã™ã‚ã°ãã‚ã“ã£ã","ã“ã£ã","ã™ã‚ã°ãã‚"],
"🇸🇱": ["ã—ãˆã‚‰ã‚ŒãŠã­ã“ã£ã","ã“ã£ã","ã—ãˆã‚‰ã‚ŒãŠã­"],
"🇸🇲": ["ã•ã‚“ã¾ã‚Šã®ã“ã£ã","ã“ã£ã","ã•ã‚“ã¾ã‚Šã®"],
@@ -1842,6 +1886,7 @@
"🇹🇿": ["ãŸã‚“ã–ã«ã‚ã“ã£ã","ã“ã£ã","ãŸã‚“ã–ã«ã‚"],
"🇺🇦": ["ã†ãらã„ãªã“ã£ã","ã“ã£ã","ã†ãらã„ãª"],
"🇺🇬": ["ã†ãŒã‚“ã ã“ã£ã","ã“ã£ã","ã†ãŒã‚“ã "],
+ "🇺🇲": ["ãŒã£ã—ã‚…ã†ã“ãりょã†ã‚†ã†ã—ょã†ã‚Šã¨ã†ã®ã¯ãŸ","ãŒã£ã—ã‚…ã†ã“ãりょã†ã‚†ã†ã—ょã†ã‚Šã¨ã†","ã“ã£ã"],
"🇺🇳": ["ã“ãれんã®ã¯ãŸ","ã¯ãŸ","ã“ãれん","れんã”ã†","ã“ãã•ã„"],
"🇺🇸": ["ã‚ã‚りã‹ã“ã£ã","ã‚ã‚りã‹","ã¯ãŸ","ã”ã†ã—ã‚…ã†","ãŒã£ã—ã‚…ã†ã“ã","ã‚ã‚りã‹ãŒã£ã—ã‚…ã†ã“ã","ãŒã£ã—ã‚…ã†ã“ãりょã†ã‚†ã†ã—ょã†ã‚Šã¨ã†"],
"🇺🇾": ["ã†ã‚‹ãã‚ã„ã“ã£ã","ã“ã£ã","ã†ã‚‹ãã‚ã„"],
diff --git a/packages/frontend/src/utility/admin-lookup.ts b/packages/frontend/src/utility/admin-lookup.ts
index 7405e229fe..18eebaa8f8 100644
--- a/packages/frontend/src/utility/admin-lookup.ts
+++ b/packages/frontend/src/utility/admin-lookup.ts
@@ -12,7 +12,7 @@ export async function lookupUser() {
const { canceled, result } = await os.inputText({
title: i18n.ts.usernameOrUserId,
});
- if (canceled) return;
+ if (canceled || result == null) return;
const show = (user) => {
os.pageWindow(`/admin/user/${user.id}`);
@@ -46,13 +46,13 @@ export async function lookupUserByEmail() {
title: i18n.ts.emailAddress,
type: 'email',
});
- if (canceled) return;
+ if (canceled || result == null) return;
try {
const user = await os.apiWithDialog('admin/accounts/find-by-email', { email: result });
os.pageWindow(`/admin/user/${user.id}`);
- } catch (err) {
+ } catch (err: any) {
if (err.code === 'USER_NOT_FOUND') {
os.alert({
type: 'error',
diff --git a/packages/frontend/src/utility/autocomplete.ts b/packages/frontend/src/utility/autocomplete.ts
index 1246c32554..82109af1a0 100644
--- a/packages/frontend/src/utility/autocomplete.ts
+++ b/packages/frontend/src/utility/autocomplete.ts
@@ -78,7 +78,10 @@ export class Autocomplete {
const caretPos = Number(this.textarea.selectionStart);
const text = this.text.substring(0, caretPos).split('\n').pop()!;
- const mentionIndex = text.lastIndexOf('@');
+ // メンションã«å«ã‚られる文字ã®ã¿ã§æ§‹æˆã•れãŸã€æœ€ã‚‚末尾ã«ã‚る文字列を抽出
+ const mentionCandidate = text.split(/[^a-zA-Z0-9_@.\-]+/).pop()!;
+
+ const mentionIndex = mentionCandidate.lastIndexOf('@');
const hashtagIndex = text.lastIndexOf('#');
const emojiIndex = text.lastIndexOf(':');
const mfmTagIndex = text.lastIndexOf('$');
@@ -97,7 +100,7 @@ export class Autocomplete {
const afterLastMfmParam = text.split(/\$\[[a-zA-Z]+/).pop();
- const isMention = mentionIndex !== -1;
+ const maybeMention = mentionIndex !== -1;
const isHashtag = hashtagIndex !== -1;
const isMfmParam = mfmParamIndex !== -1 && afterLastMfmParam?.includes('.') && !afterLastMfmParam.includes(' ');
const isMfmTag = mfmTagIndex !== -1 && !isMfmParam;
@@ -107,20 +110,27 @@ export class Autocomplete {
let opened = false;
- if (isMention && this.onlyType.includes('user')) {
+ if (maybeMention && this.onlyType.includes('user')) {
// ユーザã®ã‚µã‚¸ã‚§ã‚¹ãƒˆä¸­ã«@を入力ã™ã‚‹ã¨ã€ãã®ä½ç½®ã‹ã‚‰æ–°ãŸã«ãƒ¦ãƒ¼ã‚¶åã‚’å–りãªãŠãã†ã¨ã—ã¦ã—ã¾ã†
// ã“ã®å‹•ãã¯ãƒªãƒ¢ãƒ¼ãƒˆãƒ¦ãƒ¼ã‚¶ã®ã‚µã‚¸ã‚§ã‚¹ãƒˆã‚’阻害ã™ã‚‹ã®ã§ã€@を検知ã—ãŸã‚‰ãã®ä½ç½®ã‚ˆã‚Šã‚‚å‰ã®@を探ã—ã€
// ホストåã‚’å«ã‚€ãƒªãƒ¢ãƒ¼ãƒˆã®ãƒ¦ãƒ¼ã‚¶åã‚’å…¨ã¦æ‹¾ãˆã‚‹ã‚ˆã†ã«ã™ã‚‹
- const mentionIndexAlt = text.lastIndexOf('@', mentionIndex - 1);
- const username = mentionIndexAlt === -1
- ? text.substring(mentionIndex + 1)
- : text.substring(mentionIndexAlt + 1);
- if (username !== '' && username.match(/^[a-zA-Z0-9_@.]+$/)) {
- this.open('user', username);
- opened = true;
- } else if (username === '') {
- this.open('user', null);
- opened = true;
+ const mentionIndexAlt = mentionCandidate.lastIndexOf('@', mentionIndex - 1);
+
+ // @ãŒé€£ç¶šã—ã¦ã„ã‚‹å ´åˆã€1ã¤ç›®ã‚’無視ã™ã‚‹
+ const mentionIndexLeft = (mentionIndexAlt !== -1 && mentionIndexAlt !== mentionIndex - 1) ? mentionIndexAlt : mentionIndex;
+
+ // メンションを構æˆã™ã‚‹æ¡ä»¶ã‚’満ãŸã—ã¦ã„ã‚‹ã‹ç¢ºèªã™ã‚‹
+ const isMention = mentionIndexLeft === 0 || '_@.-'.includes(mentionCandidate[mentionIndexLeft - 1]);
+
+ if (isMention) {
+ const username = mentionCandidate.substring(mentionIndexLeft + 1);
+ if (username !== '' && username.match(/^[a-zA-Z0-9_@.\-]+$/)) {
+ this.open('user', username);
+ opened = true;
+ } else if (username === '') {
+ this.open('user', null);
+ opened = true;
+ }
}
}
diff --git a/packages/frontend/src/utility/chart-legend.ts b/packages/frontend/src/utility/chart-legend.ts
index e701d18dd2..fcbddf5669 100644
--- a/packages/frontend/src/utility/chart-legend.ts
+++ b/packages/frontend/src/utility/chart-legend.ts
@@ -10,7 +10,7 @@ export const chartLegend = (legend: InstanceType<typeof MkChartLegend>) => ({
id: 'htmlLegend',
afterUpdate(chart, args, options) {
// Reuse the built-in legendItems generator
- const items = chart.options.plugins.legend.labels.generateLabels(chart);
+ const items = chart.options.plugins!.legend!.labels!.generateLabels!(chart);
legend.update(chart, items);
},
diff --git a/packages/frontend/src/utility/chart-vline.ts b/packages/frontend/src/utility/chart-vline.ts
index 465ca591c6..2fe4bdb83b 100644
--- a/packages/frontend/src/utility/chart-vline.ts
+++ b/packages/frontend/src/utility/chart-vline.ts
@@ -8,9 +8,10 @@ import type { Plugin } from 'chart.js';
export const chartVLine = (vLineColor: string) => ({
id: 'vLine',
beforeDraw(chart, args, options) {
- if (chart.tooltip?._active?.length) {
+ const tooltip = chart.tooltip as any;
+ if (tooltip?._active?.length) {
const ctx = chart.ctx;
- const xs = chart.tooltip._active.map(a => a.element.x);
+ const xs = tooltip._active.map(a => a.element.x);
const x = xs.reduce((a, b) => a + b, 0) / xs.length;
const topY = chart.scales.y.top;
const bottomY = chart.scales.y.bottom;
diff --git a/packages/frontend/src/utility/check-permissions.ts b/packages/frontend/src/utility/check-permissions.ts
index 2de8fd2cd1..4f55556e0a 100644
--- a/packages/frontend/src/utility/check-permissions.ts
+++ b/packages/frontend/src/utility/check-permissions.ts
@@ -17,3 +17,11 @@ export const notesSearchAvailable = (
export const canSearchNonLocalNotes = (
instance.noteSearchableScope === 'global'
);
+
+export const usersSearchAvailable = (
+ // FIXME: instance.policies would be null in Vitest
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+ ($i == null && instance.policies != null && instance.policies.canSearchUsers) ||
+ ($i != null && $i.policies.canSearchUsers) ||
+ false
+);
diff --git a/packages/frontend/src/utility/clear-cache.ts b/packages/frontend/src/utility/clear-cache.ts
index b6ae254727..8a62265438 100644
--- a/packages/frontend/src/utility/clear-cache.ts
+++ b/packages/frontend/src/utility/clear-cache.ts
@@ -13,8 +13,6 @@ export async function clearCache() {
os.waiting();
miLocalStorage.removeItem('instance');
miLocalStorage.removeItem('instanceCachedAt');
- miLocalStorage.removeItem('locale');
- miLocalStorage.removeItem('localeVersion');
miLocalStorage.removeItem('theme');
miLocalStorage.removeItem('emojis');
miLocalStorage.removeItem('lastEmojisFetchedAt');
diff --git a/packages/frontend/src/utility/clicker-game.ts b/packages/frontend/src/utility/clicker-game.ts
index 0544be7757..4360c58455 100644
--- a/packages/frontend/src/utility/clicker-game.ts
+++ b/packages/frontend/src/utility/clicker-game.ts
@@ -27,7 +27,7 @@ export async function load() {
scope: ['clickerGame'],
key: 'saveData',
});
- } catch (err) {
+ } catch (err: any) {
if (err.code === 'NO_SUCH_KEY') {
saveData.value = {
gameVersion: 2,
@@ -43,20 +43,6 @@ export async function load() {
}
throw err;
}
-
- // migration
- if (saveData.value.gameVersion === 1) {
- saveData.value = {
- gameVersion: 2,
- cookies: saveData.value.cookies,
- totalCookies: saveData.value.cookies,
- totalHandmadeCookies: saveData.value.cookies,
- clicked: saveData.value.clicked,
- achievements: [],
- facilities: [],
- };
- save();
- }
}
export async function save() {
diff --git a/packages/frontend/src/utility/drive.ts b/packages/frontend/src/utility/drive.ts
index f2f491b3fd..fb2825e7f7 100644
--- a/packages/frontend/src/utility/drive.ts
+++ b/packages/frontend/src/utility/drive.ts
@@ -233,7 +233,7 @@ function select(anchorElement: HTMLElement | EventTarget | null, label: string |
os.popupMenu([label ? {
text: label,
type: 'label',
- } : undefined, {
+ } : null, {
text: i18n.ts.upload,
icon: 'ti ti-upload',
action: () => chooseFileFromPcAndUpload({ multiple, features }).then(files => res(files)),
@@ -300,15 +300,13 @@ export async function createCroppedImageDriveFileFromImageDriveFile(imageDriveFi
});
}
-export async function selectDriveFolder(initialFolder: Misskey.entities.DriveFolder['id'] | null): Promise<Misskey.entities.DriveFolder[]> {
+export async function selectDriveFolder(initialFolder: Misskey.entities.DriveFolder['id'] | null): Promise<(Misskey.entities.DriveFolder | null)[]> {
return new Promise(async resolve => {
const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkDriveFolderSelectDialog.vue').then(x => x.default), {
initialFolder,
}, {
done: folders => {
- if (folders) {
- resolve(folders);
- }
+ resolve(folders);
},
closed: () => dispose(),
});
diff --git a/packages/frontend/src/utility/extract-mentions.ts b/packages/frontend/src/utility/extract-mentions.ts
index d518562053..2ec9349718 100644
--- a/packages/frontend/src/utility/extract-mentions.ts
+++ b/packages/frontend/src/utility/extract-mentions.ts
@@ -9,7 +9,7 @@ import * as mfm from 'mfm-js';
export function extractMentions(nodes: mfm.MfmNode[]): mfm.MfmMention['props'][] {
// TODO: é‡è¤‡ã‚’削除
- const mentionNodes = mfm.extract(nodes, (node) => node.type === 'mention');
+ const mentionNodes = mfm.extract(nodes, (node) => node.type === 'mention') as mfm.MfmMention[];
const mentions = mentionNodes.map(x => x.props);
return mentions;
diff --git a/packages/frontend/src/utility/extract-url-from-mfm.ts b/packages/frontend/src/utility/extract-url-from-mfm.ts
index 570823d5b5..48243dff36 100644
--- a/packages/frontend/src/utility/extract-url-from-mfm.ts
+++ b/packages/frontend/src/utility/extract-url-from-mfm.ts
@@ -13,7 +13,7 @@ const removeHash = (x: string) => x.replace(/#[^#]*$/, '');
export function extractUrlFromMfm(nodes: mfm.MfmNode[], respectSilentFlag = true): string[] {
const urlNodes = mfm.extract(nodes, (node) => {
return (node.type === 'url') || (node.type === 'link' && (!respectSilentFlag || !node.props.silent));
- });
+ }) as mfm.MfmUrl[];
const urls: string[] = unique(urlNodes.map(x => x.props.url));
return urls.reduce((array, url) => {
diff --git a/packages/frontend/src/utility/get-note-menu.ts b/packages/frontend/src/utility/get-note-menu.ts
index 11c87dc653..90de952a91 100644
--- a/packages/frontend/src/utility/get-note-menu.ts
+++ b/packages/frontend/src/utility/get-note-menu.ts
@@ -39,7 +39,7 @@ export async function getNoteClipMenu(props: {
}
}
- const appearNote = getAppearNote(props.note);
+ const appearNote = getAppearNote(props.note) ?? props.note;
const clips = await clipsCache.fetch();
const menu: MenuItem[] = [...clips.map(clip => ({
@@ -179,7 +179,7 @@ export function getNoteMenu(props: {
translating: Ref<boolean>;
currentClip?: Misskey.entities.Clip;
}) {
- const appearNote = getAppearNote(props.note);
+ const appearNote = getAppearNote(props.note) ?? props.note;
const link = appearNote.url ?? appearNote.uri;
const cleanups = [] as (() => void)[];
@@ -554,7 +554,7 @@ export function getRenoteMenu(props: {
renoteButton: ShallowRef<HTMLElement | null | undefined>;
mock?: boolean;
}) {
- const appearNote = getAppearNote(props.note);
+ const appearNote = getAppearNote(props.note) ?? props.note;
const channelRenoteItems: MenuItem[] = [];
const normalRenoteItems: MenuItem[] = [];
diff --git a/packages/frontend/src/utility/get-user-menu.ts b/packages/frontend/src/utility/get-user-menu.ts
index ad0864019b..d4407dadec 100644
--- a/packages/frontend/src/utility/get-user-menu.ts
+++ b/packages/frontend/src/utility/get-user-menu.ts
@@ -158,7 +158,11 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
icon: 'ti ti-user-exclamation',
text: i18n.ts.moderation,
action: () => {
- router.push(`/admin/user/${user.id}`);
+ router.push('/admin/user/:userId', {
+ params: {
+ userId: user.id,
+ },
+ });
},
}, { type: 'divider' });
}
@@ -216,7 +220,12 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
icon: 'ti ti-search',
text: i18n.ts.searchThisUsersNotes,
action: () => {
- router.push(`/search?username=${encodeURIComponent(user.username)}${user.host != null ? '&host=' + encodeURIComponent(user.host) : ''}`);
+ router.push('/search', {
+ query: {
+ username: user.username,
+ host: user.host ?? undefined,
+ },
+ });
},
});
}
diff --git a/packages/frontend/src/utility/haptic.ts b/packages/frontend/src/utility/haptic.ts
new file mode 100644
index 0000000000..6f4706d202
--- /dev/null
+++ b/packages/frontend/src/utility/haptic.ts
@@ -0,0 +1,13 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { haptic as _haptic } from 'ios-haptics';
+import { prefer } from '@/preferences.js';
+
+export function haptic() {
+ if (prefer.s['experimental.enableHapticFeedback']) {
+ _haptic();
+ }
+}
diff --git a/packages/frontend/src/utility/image-effector/ImageEffector.ts b/packages/frontend/src/utility/image-effector/ImageEffector.ts
index 1028c57f35..66b4d1026c 100644
--- a/packages/frontend/src/utility/image-effector/ImageEffector.ts
+++ b/packages/frontend/src/utility/image-effector/ImageEffector.ts
@@ -6,22 +6,78 @@
import { getProxiedImageUrl } from '../media-proxy.js';
import { initShaderProgram } from '../webgl.js';
+export type ImageEffectorRGB = [r: number, g: number, b: number];
+
type ParamTypeToPrimitive = {
- 'number': number;
- 'number:enum': number;
- 'boolean': boolean;
- 'align': { x: 'left' | 'center' | 'right'; y: 'top' | 'center' | 'bottom'; };
- 'seed': number;
- 'texture': { type: 'text'; text: string | null; } | { type: 'url'; url: string | null; } | null;
- 'color': [r: number, g: number, b: number];
+ [K in ImageEffectorFxParamDef['type']]: (ImageEffectorFxParamDef & { type: K })['default'];
};
-type ImageEffectorFxParamDefs = Record<string, {
- type: keyof ParamTypeToPrimitive;
- default: any;
+interface CommonParamDef {
+ type: string;
label?: string;
- toViewValue?: (v: any) => string;
-}>;
+ caption?: string;
+ default: any;
+}
+
+interface NumberParamDef extends CommonParamDef {
+ type: 'number';
+ default: number;
+ min: number;
+ max: number;
+ step?: number;
+ toViewValue?: (v: number) => string;
+};
+
+interface NumberEnumParamDef extends CommonParamDef {
+ type: 'number:enum';
+ enum: {
+ value: number;
+ label?: string;
+ icon?: string;
+ }[];
+ default: number;
+};
+
+interface BooleanParamDef extends CommonParamDef {
+ type: 'boolean';
+ default: boolean;
+};
+
+interface AlignParamDef extends CommonParamDef {
+ type: 'align';
+ default: {
+ x: 'left' | 'center' | 'right';
+ y: 'top' | 'center' | 'bottom';
+ };
+};
+
+interface SeedParamDef extends CommonParamDef {
+ type: 'seed';
+ default: number;
+};
+
+interface TextureParamDef extends CommonParamDef {
+ type: 'texture';
+ default: { type: 'text'; text: string | null; } | { type: 'url'; url: string | null; } | null;
+};
+
+interface ColorParamDef extends CommonParamDef {
+ type: 'color';
+ default: ImageEffectorRGB;
+};
+
+type ImageEffectorFxParamDef = NumberParamDef | NumberEnumParamDef | BooleanParamDef | AlignParamDef | SeedParamDef | TextureParamDef | ColorParamDef;
+
+export type ImageEffectorFxParamDefs = Record<string, ImageEffectorFxParamDef>;
+
+export type GetParamType<T extends ImageEffectorFxParamDef> =
+ T extends NumberEnumParamDef
+ ? T['enum'][number]['value']
+ : ParamTypeToPrimitive[T['type']];
+
+export type ParamsRecordTypeToDefRecord<PS extends ImageEffectorFxParamDefs> = {
+ [K in keyof PS]: GetParamType<PS[K]>;
+};
export function defineImageEffectorFx<ID extends string, PS extends ImageEffectorFxParamDefs, US extends string[]>(fx: ImageEffectorFx<ID, PS, US>) {
return fx;
@@ -36,9 +92,7 @@ export type ImageEffectorFx<ID extends string = string, PS extends ImageEffector
main: (ctx: {
gl: WebGL2RenderingContext;
program: WebGLProgram;
- params: {
- [key in keyof PS]: ParamTypeToPrimitive[PS[key]['type']];
- };
+ params: ParamsRecordTypeToDefRecord<PS>;
u: Record<US[number], WebGLUniformLocation>;
width: number;
height: number;
diff --git a/packages/frontend/src/utility/image-effector/fxs/blockNoise.ts b/packages/frontend/src/utility/image-effector/fxs/blockNoise.ts
index bf7eaa8bda..7e09524c10 100644
--- a/packages/frontend/src/utility/image-effector/fxs/blockNoise.ts
+++ b/packages/frontend/src/utility/image-effector/fxs/blockNoise.ts
@@ -48,20 +48,22 @@ void main() {
`;
export const FX_blockNoise = defineImageEffectorFx({
- id: 'blockNoise' as const,
+ id: 'blockNoise',
name: i18n.ts._imageEffector._fxs.glitch + ': ' + i18n.ts._imageEffector._fxs.blockNoise,
shader,
uniforms: ['amount', 'channelShift'] as const,
params: {
amount: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.amount,
+ type: 'number',
default: 50,
min: 1,
max: 100,
step: 1,
},
strength: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.strength,
+ type: 'number',
default: 0.05,
min: -1,
max: 1,
@@ -69,7 +71,8 @@ export const FX_blockNoise = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
width: {
- type: 'number' as const,
+ label: i18n.ts.width,
+ type: 'number',
default: 0.05,
min: 0.01,
max: 1,
@@ -77,7 +80,8 @@ export const FX_blockNoise = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
height: {
- type: 'number' as const,
+ label: i18n.ts.height,
+ type: 'number',
default: 0.01,
min: 0.01,
max: 1,
@@ -85,7 +89,8 @@ export const FX_blockNoise = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
channelShift: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.glitchChannelShift,
+ type: 'number',
default: 0,
min: 0,
max: 10,
@@ -93,7 +98,8 @@ export const FX_blockNoise = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
seed: {
- type: 'seed' as const,
+ label: i18n.ts._imageEffector._fxProps.seed,
+ type: 'seed',
default: 100,
},
},
diff --git a/packages/frontend/src/utility/image-effector/fxs/checker.ts b/packages/frontend/src/utility/image-effector/fxs/checker.ts
index c426308951..c48f73acbd 100644
--- a/packages/frontend/src/utility/image-effector/fxs/checker.ts
+++ b/packages/frontend/src/utility/image-effector/fxs/checker.ts
@@ -47,13 +47,14 @@ void main() {
`;
export const FX_checker = defineImageEffectorFx({
- id: 'checker' as const,
+ id: 'checker',
name: i18n.ts._imageEffector._fxs.checker,
shader,
uniforms: ['angle', 'scale', 'color', 'opacity'] as const,
params: {
angle: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.angle,
+ type: 'number',
default: 0,
min: -1.0,
max: 1.0,
@@ -61,18 +62,21 @@ export const FX_checker = defineImageEffectorFx({
toViewValue: v => Math.round(v * 90) + '°',
},
scale: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.scale,
+ type: 'number',
default: 3.0,
min: 1.0,
max: 10.0,
step: 0.1,
},
color: {
- type: 'color' as const,
+ label: i18n.ts._imageEffector._fxProps.color,
+ type: 'color',
default: [1, 1, 1],
},
opacity: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.opacity,
+ type: 'number',
default: 0.5,
min: 0.0,
max: 1.0,
diff --git a/packages/frontend/src/utility/image-effector/fxs/chromaticAberration.ts b/packages/frontend/src/utility/image-effector/fxs/chromaticAberration.ts
index 82d7d883aa..4adb7ce91e 100644
--- a/packages/frontend/src/utility/image-effector/fxs/chromaticAberration.ts
+++ b/packages/frontend/src/utility/image-effector/fxs/chromaticAberration.ts
@@ -52,17 +52,19 @@ void main() {
`;
export const FX_chromaticAberration = defineImageEffectorFx({
- id: 'chromaticAberration' as const,
+ id: 'chromaticAberration',
name: i18n.ts._imageEffector._fxs.chromaticAberration,
shader,
uniforms: ['amount', 'start', 'normalize'] as const,
params: {
normalize: {
- type: 'boolean' as const,
+ label: i18n.ts._imageEffector._fxProps.normalize,
+ type: 'boolean',
default: false,
},
amount: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.amount,
+ type: 'number',
default: 0.1,
min: 0.0,
max: 1.0,
diff --git a/packages/frontend/src/utility/image-effector/fxs/colorAdjust.ts b/packages/frontend/src/utility/image-effector/fxs/colorAdjust.ts
index c38490e198..8cfbbcb516 100644
--- a/packages/frontend/src/utility/image-effector/fxs/colorAdjust.ts
+++ b/packages/frontend/src/utility/image-effector/fxs/colorAdjust.ts
@@ -85,13 +85,14 @@ void main() {
`;
export const FX_colorAdjust = defineImageEffectorFx({
- id: 'colorAdjust' as const,
+ id: 'colorAdjust',
name: i18n.ts._imageEffector._fxs.colorAdjust,
shader,
uniforms: ['lightness', 'contrast', 'hue', 'brightness', 'saturation'] as const,
params: {
lightness: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.lightness,
+ type: 'number',
default: 0,
min: -1,
max: 1,
@@ -99,7 +100,8 @@ export const FX_colorAdjust = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
contrast: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.contrast,
+ type: 'number',
default: 1,
min: 0,
max: 4,
@@ -107,7 +109,8 @@ export const FX_colorAdjust = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
hue: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.hue,
+ type: 'number',
default: 0,
min: -1,
max: 1,
@@ -115,7 +118,8 @@ export const FX_colorAdjust = defineImageEffectorFx({
toViewValue: v => Math.round(v * 180) + '°',
},
brightness: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.brightness,
+ type: 'number',
default: 1,
min: 0,
max: 4,
@@ -123,7 +127,8 @@ export const FX_colorAdjust = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
saturation: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.saturation,
+ type: 'number',
default: 1,
min: 0,
max: 4,
diff --git a/packages/frontend/src/utility/image-effector/fxs/colorClamp.ts b/packages/frontend/src/utility/image-effector/fxs/colorClamp.ts
index ae0d92b8ae..4f18eb63c4 100644
--- a/packages/frontend/src/utility/image-effector/fxs/colorClamp.ts
+++ b/packages/frontend/src/utility/image-effector/fxs/colorClamp.ts
@@ -26,13 +26,14 @@ void main() {
`;
export const FX_colorClamp = defineImageEffectorFx({
- id: 'colorClamp' as const,
+ id: 'colorClamp',
name: i18n.ts._imageEffector._fxs.colorClamp,
shader,
uniforms: ['max', 'min'] as const,
params: {
max: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.max,
+ type: 'number',
default: 1.0,
min: 0.0,
max: 1.0,
@@ -40,7 +41,8 @@ export const FX_colorClamp = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
min: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.min,
+ type: 'number',
default: -1.0,
min: -1.0,
max: 0.0,
diff --git a/packages/frontend/src/utility/image-effector/fxs/colorClampAdvanced.ts b/packages/frontend/src/utility/image-effector/fxs/colorClampAdvanced.ts
index b9387900fb..7e793061cf 100644
--- a/packages/frontend/src/utility/image-effector/fxs/colorClampAdvanced.ts
+++ b/packages/frontend/src/utility/image-effector/fxs/colorClampAdvanced.ts
@@ -30,13 +30,14 @@ void main() {
`;
export const FX_colorClampAdvanced = defineImageEffectorFx({
- id: 'colorClampAdvanced' as const,
+ id: 'colorClampAdvanced',
name: i18n.ts._imageEffector._fxs.colorClampAdvanced,
shader,
uniforms: ['rMax', 'rMin', 'gMax', 'gMin', 'bMax', 'bMin'] as const,
params: {
rMax: {
- type: 'number' as const,
+ label: `${i18n.ts._imageEffector._fxProps.max} (${i18n.ts._imageEffector._fxProps.redComponent})`,
+ type: 'number',
default: 1.0,
min: 0.0,
max: 1.0,
@@ -44,7 +45,8 @@ export const FX_colorClampAdvanced = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
rMin: {
- type: 'number' as const,
+ label: `${i18n.ts._imageEffector._fxProps.min} (${i18n.ts._imageEffector._fxProps.redComponent})`,
+ type: 'number',
default: -1.0,
min: -1.0,
max: 0.0,
@@ -52,7 +54,8 @@ export const FX_colorClampAdvanced = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
gMax: {
- type: 'number' as const,
+ label: `${i18n.ts._imageEffector._fxProps.max} (${i18n.ts._imageEffector._fxProps.greenComponent})`,
+ type: 'number',
default: 1.0,
min: 0.0,
max: 1.0,
@@ -60,7 +63,8 @@ export const FX_colorClampAdvanced = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
gMin: {
- type: 'number' as const,
+ label: `${i18n.ts._imageEffector._fxProps.min} (${i18n.ts._imageEffector._fxProps.greenComponent})`,
+ type: 'number',
default: -1.0,
min: -1.0,
max: 0.0,
@@ -68,7 +72,8 @@ export const FX_colorClampAdvanced = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
bMax: {
- type: 'number' as const,
+ label: `${i18n.ts._imageEffector._fxProps.max} (${i18n.ts._imageEffector._fxProps.blueComponent})`,
+ type: 'number',
default: 1.0,
min: 0.0,
max: 1.0,
@@ -76,7 +81,8 @@ export const FX_colorClampAdvanced = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
bMin: {
- type: 'number' as const,
+ label: `${i18n.ts._imageEffector._fxProps.min} (${i18n.ts._imageEffector._fxProps.blueComponent})`,
+ type: 'number',
default: -1.0,
min: -1.0,
max: 0.0,
diff --git a/packages/frontend/src/utility/image-effector/fxs/distort.ts b/packages/frontend/src/utility/image-effector/fxs/distort.ts
index 4b1aefc159..7b5ec45f4b 100644
--- a/packages/frontend/src/utility/image-effector/fxs/distort.ts
+++ b/packages/frontend/src/utility/image-effector/fxs/distort.ts
@@ -34,18 +34,23 @@ void main() {
`;
export const FX_distort = defineImageEffectorFx({
- id: 'distort' as const,
+ id: 'distort',
name: i18n.ts._imageEffector._fxs.distort,
shader,
uniforms: ['phase', 'frequency', 'strength', 'direction'] as const,
params: {
direction: {
- type: 'number:enum' as const,
- enum: [{ value: 0, label: 'v' }, { value: 1, label: 'h' }],
+ label: i18n.ts._imageEffector._fxProps.direction,
+ type: 'number:enum',
+ enum: [
+ { value: 0 as const, label: i18n.ts.horizontal },
+ { value: 1 as const, label: i18n.ts.vertical },
+ ],
default: 1,
},
phase: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.phase,
+ type: 'number',
default: 0.0,
min: -1.0,
max: 1.0,
@@ -53,14 +58,16 @@ export const FX_distort = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
frequency: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.frequency,
+ type: 'number',
default: 30,
min: 0,
max: 100,
step: 0.1,
},
strength: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.strength,
+ type: 'number',
default: 0.05,
min: 0,
max: 1,
diff --git a/packages/frontend/src/utility/image-effector/fxs/grayscale.ts b/packages/frontend/src/utility/image-effector/fxs/grayscale.ts
index 8f33706ae7..e1a288fc85 100644
--- a/packages/frontend/src/utility/image-effector/fxs/grayscale.ts
+++ b/packages/frontend/src/utility/image-effector/fxs/grayscale.ts
@@ -26,7 +26,7 @@ void main() {
`;
export const FX_grayscale = defineImageEffectorFx({
- id: 'grayscale' as const,
+ id: 'grayscale',
name: i18n.ts._imageEffector._fxs.grayscale,
shader,
uniforms: [] as const,
diff --git a/packages/frontend/src/utility/image-effector/fxs/invert.ts b/packages/frontend/src/utility/image-effector/fxs/invert.ts
index 220a2dea30..1c662ae849 100644
--- a/packages/frontend/src/utility/image-effector/fxs/invert.ts
+++ b/packages/frontend/src/utility/image-effector/fxs/invert.ts
@@ -27,21 +27,24 @@ void main() {
`;
export const FX_invert = defineImageEffectorFx({
- id: 'invert' as const,
+ id: 'invert',
name: i18n.ts._imageEffector._fxs.invert,
shader,
uniforms: ['r', 'g', 'b'] as const,
params: {
r: {
- type: 'boolean' as const,
+ label: i18n.ts._imageEffector._fxProps.redComponent,
+ type: 'boolean',
default: true,
},
g: {
- type: 'boolean' as const,
+ label: i18n.ts._imageEffector._fxProps.greenComponent,
+ type: 'boolean',
default: true,
},
b: {
- type: 'boolean' as const,
+ label: i18n.ts._imageEffector._fxProps.blueComponent,
+ type: 'boolean',
default: true,
},
},
diff --git a/packages/frontend/src/utility/image-effector/fxs/mirror.ts b/packages/frontend/src/utility/image-effector/fxs/mirror.ts
index 5946a2e0dc..3d7893f8b0 100644
--- a/packages/frontend/src/utility/image-effector/fxs/mirror.ts
+++ b/packages/frontend/src/utility/image-effector/fxs/mirror.ts
@@ -35,19 +35,29 @@ void main() {
`;
export const FX_mirror = defineImageEffectorFx({
- id: 'mirror' as const,
+ id: 'mirror',
name: i18n.ts._imageEffector._fxs.mirror,
shader,
uniforms: ['h', 'v'] as const,
params: {
h: {
- type: 'number:enum' as const,
- enum: [{ value: -1, label: '<-' }, { value: 0, label: '|' }, { value: 1, label: '->' }],
+ label: i18n.ts.horizontal,
+ type: 'number:enum',
+ enum: [
+ { value: -1 as const, icon: 'ti ti-arrow-bar-right' },
+ { value: 0 as const, icon: 'ti ti-minus-vertical' },
+ { value: 1 as const, icon: 'ti ti-arrow-bar-left' }
+ ],
default: -1,
},
v: {
- type: 'number:enum' as const,
- enum: [{ value: -1, label: '^' }, { value: 0, label: '-' }, { value: 1, label: 'v' }],
+ label: i18n.ts.vertical,
+ type: 'number:enum',
+ enum: [
+ { value: -1 as const, icon: 'ti ti-arrow-bar-down' },
+ { value: 0 as const, icon: 'ti ti-minus' },
+ { value: 1 as const, icon: 'ti ti-arrow-bar-up' }
+ ],
default: 0,
},
},
diff --git a/packages/frontend/src/utility/image-effector/fxs/polkadot.ts b/packages/frontend/src/utility/image-effector/fxs/polkadot.ts
index 14f6f91148..1685601bd2 100644
--- a/packages/frontend/src/utility/image-effector/fxs/polkadot.ts
+++ b/packages/frontend/src/utility/image-effector/fxs/polkadot.ts
@@ -78,14 +78,16 @@ void main() {
}
`;
+// Primarily used for watermark
export const FX_polkadot = defineImageEffectorFx({
- id: 'polkadot' as const,
+ id: 'polkadot',
name: i18n.ts._imageEffector._fxs.polkadot,
shader,
uniforms: ['angle', 'scale', 'major_radius', 'major_opacity', 'minor_divisions', 'minor_radius', 'minor_opacity', 'color'] as const,
params: {
angle: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.angle,
+ type: 'number',
default: 0,
min: -1.0,
max: 1.0,
@@ -93,21 +95,24 @@ export const FX_polkadot = defineImageEffectorFx({
toViewValue: v => Math.round(v * 90) + '°',
},
scale: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.scale,
+ type: 'number',
default: 3.0,
min: 1.0,
max: 10.0,
step: 0.1,
},
majorRadius: {
- type: 'number' as const,
+ label: i18n.ts._watermarkEditor.polkadotMainDotRadius,
+ type: 'number',
default: 0.1,
min: 0.0,
max: 1.0,
step: 0.01,
},
majorOpacity: {
- type: 'number' as const,
+ label: i18n.ts._watermarkEditor.polkadotMainDotOpacity,
+ type: 'number',
default: 0.75,
min: 0.0,
max: 1.0,
@@ -115,21 +120,24 @@ export const FX_polkadot = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
minorDivisions: {
- type: 'number' as const,
+ label: i18n.ts._watermarkEditor.polkadotSubDotDivisions,
+ type: 'number',
default: 4,
min: 0,
max: 16,
step: 1,
},
minorRadius: {
- type: 'number' as const,
+ label: i18n.ts._watermarkEditor.polkadotSubDotRadius,
+ type: 'number',
default: 0.25,
min: 0.0,
max: 1.0,
step: 0.01,
},
minorOpacity: {
- type: 'number' as const,
+ label: i18n.ts._watermarkEditor.polkadotSubDotOpacity,
+ type: 'number',
default: 0.5,
min: 0.0,
max: 1.0,
@@ -137,7 +145,8 @@ export const FX_polkadot = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
color: {
- type: 'color' as const,
+ label: i18n.ts._imageEffector._fxProps.color,
+ type: 'color',
default: [1, 1, 1],
},
},
diff --git a/packages/frontend/src/utility/image-effector/fxs/stripe.ts b/packages/frontend/src/utility/image-effector/fxs/stripe.ts
index f6c1d2278d..1c054c1aaa 100644
--- a/packages/frontend/src/utility/image-effector/fxs/stripe.ts
+++ b/packages/frontend/src/utility/image-effector/fxs/stripe.ts
@@ -48,14 +48,16 @@ void main() {
}
`;
+// Primarily used for watermark
export const FX_stripe = defineImageEffectorFx({
- id: 'stripe' as const,
+ id: 'stripe',
name: i18n.ts._imageEffector._fxs.stripe,
shader,
uniforms: ['angle', 'frequency', 'phase', 'threshold', 'color', 'opacity'] as const,
params: {
angle: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.angle,
+ type: 'number',
default: 0.5,
min: -1.0,
max: 1.0,
@@ -63,14 +65,16 @@ export const FX_stripe = defineImageEffectorFx({
toViewValue: v => Math.round(v * 90) + '°',
},
frequency: {
- type: 'number' as const,
+ label: i18n.ts._watermarkEditor.stripeFrequency,
+ type: 'number',
default: 10.0,
min: 1.0,
max: 30.0,
step: 0.1,
},
threshold: {
- type: 'number' as const,
+ label: i18n.ts._watermarkEditor.stripeWidth,
+ type: 'number',
default: 0.1,
min: 0.0,
max: 1.0,
@@ -78,11 +82,13 @@ export const FX_stripe = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
color: {
- type: 'color' as const,
+ label: i18n.ts._imageEffector._fxProps.color,
+ type: 'color',
default: [1, 1, 1],
},
opacity: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.opacity,
+ type: 'number',
default: 0.5,
min: 0.0,
max: 1.0,
diff --git a/packages/frontend/src/utility/image-effector/fxs/tearing.ts b/packages/frontend/src/utility/image-effector/fxs/tearing.ts
index d5f1e062ec..a1d5178d24 100644
--- a/packages/frontend/src/utility/image-effector/fxs/tearing.ts
+++ b/packages/frontend/src/utility/image-effector/fxs/tearing.ts
@@ -38,20 +38,22 @@ void main() {
`;
export const FX_tearing = defineImageEffectorFx({
- id: 'tearing' as const,
+ id: 'tearing',
name: i18n.ts._imageEffector._fxs.glitch + ': ' + i18n.ts._imageEffector._fxs.tearing,
shader,
uniforms: ['amount', 'channelShift'] as const,
params: {
amount: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.amount,
+ type: 'number',
default: 3,
min: 1,
max: 100,
step: 1,
},
strength: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.strength,
+ type: 'number',
default: 0.05,
min: -1,
max: 1,
@@ -59,7 +61,8 @@ export const FX_tearing = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
size: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.size,
+ type: 'number',
default: 0.2,
min: 0,
max: 1,
@@ -67,7 +70,8 @@ export const FX_tearing = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
channelShift: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.glitchChannelShift,
+ type: 'number',
default: 0.5,
min: 0,
max: 10,
@@ -75,7 +79,8 @@ export const FX_tearing = defineImageEffectorFx({
toViewValue: v => Math.round(v * 100) + '%',
},
seed: {
- type: 'seed' as const,
+ label: i18n.ts._imageEffector._fxProps.seed,
+ type: 'seed',
default: 100,
},
},
diff --git a/packages/frontend/src/utility/image-effector/fxs/threshold.ts b/packages/frontend/src/utility/image-effector/fxs/threshold.ts
index f2b8b107fd..3e591fc939 100644
--- a/packages/frontend/src/utility/image-effector/fxs/threshold.ts
+++ b/packages/frontend/src/utility/image-effector/fxs/threshold.ts
@@ -27,27 +27,30 @@ void main() {
`;
export const FX_threshold = defineImageEffectorFx({
- id: 'threshold' as const,
+ id: 'threshold',
name: i18n.ts._imageEffector._fxs.threshold,
shader,
uniforms: ['r', 'g', 'b'] as const,
params: {
r: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.redComponent,
+ type: 'number',
default: 0.5,
min: 0.0,
max: 1.0,
step: 0.01,
},
g: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.greenComponent,
+ type: 'number',
default: 0.5,
min: 0.0,
max: 1.0,
step: 0.01,
},
b: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.blueComponent,
+ type: 'number',
default: 0.5,
min: 0.0,
max: 1.0,
diff --git a/packages/frontend/src/utility/image-effector/fxs/watermarkPlacement.ts b/packages/frontend/src/utility/image-effector/fxs/watermarkPlacement.ts
index 1c1c95b0c5..9b79e2bf94 100644
--- a/packages/frontend/src/utility/image-effector/fxs/watermarkPlacement.ts
+++ b/packages/frontend/src/utility/image-effector/fxs/watermarkPlacement.ts
@@ -83,46 +83,46 @@ void main() {
`;
export const FX_watermarkPlacement = defineImageEffectorFx({
- id: 'watermarkPlacement' as const,
+ id: 'watermarkPlacement',
name: '(internal)',
shader,
uniforms: ['texture_watermark', 'resolution_watermark', 'scale', 'angle', 'opacity', 'repeat', 'alignX', 'alignY', 'fitMode'] as const,
params: {
cover: {
- type: 'boolean' as const,
+ type: 'boolean',
default: false,
},
repeat: {
- type: 'boolean' as const,
+ type: 'boolean',
default: false,
},
scale: {
- type: 'number' as const,
+ type: 'number',
default: 0.3,
min: 0.0,
max: 1.0,
step: 0.01,
},
angle: {
- type: 'number' as const,
+ type: 'number',
default: 0,
min: -1.0,
max: 1.0,
step: 0.01,
},
align: {
- type: 'align' as const,
+ type: 'align',
default: { x: 'right', y: 'bottom' },
},
opacity: {
- type: 'number' as const,
+ type: 'number',
default: 0.75,
min: 0.0,
max: 1.0,
step: 0.01,
},
watermark: {
- type: 'texture' as const,
+ type: 'texture',
default: null,
},
},
diff --git a/packages/frontend/src/utility/image-effector/fxs/zoomLines.ts b/packages/frontend/src/utility/image-effector/fxs/zoomLines.ts
index 2613362a71..2e16ebea3b 100644
--- a/packages/frontend/src/utility/image-effector/fxs/zoomLines.ts
+++ b/packages/frontend/src/utility/image-effector/fxs/zoomLines.ts
@@ -37,59 +37,68 @@ void main() {
`;
export const FX_zoomLines = defineImageEffectorFx({
- id: 'zoomLines' as const,
+ id: 'zoomLines',
name: i18n.ts._imageEffector._fxs.zoomLines,
shader,
uniforms: ['pos', 'frequency', 'thresholdEnabled', 'threshold', 'maskSize', 'black'] as const,
params: {
x: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.centerX,
+ type: 'number',
default: 0.0,
min: -1.0,
max: 1.0,
step: 0.01,
},
y: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.centerY,
+ type: 'number',
default: 0.0,
min: -1.0,
max: 1.0,
step: 0.01,
},
frequency: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.frequency,
+ type: 'number',
default: 30.0,
min: 1.0,
max: 200.0,
step: 0.1,
},
- thresholdEnabled: {
- type: 'boolean' as const,
- default: true,
+ smoothing: {
+ label: i18n.ts._imageEffector._fxProps.zoomLinesSmoothing,
+ caption: i18n.ts._imageEffector._fxProps.zoomLinesSmoothingDescription,
+ type: 'boolean',
+ default: false,
},
threshold: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.zoomLinesThreshold,
+ type: 'number',
default: 0.2,
min: 0.0,
max: 1.0,
step: 0.01,
},
maskSize: {
- type: 'number' as const,
+ label: i18n.ts._imageEffector._fxProps.zoomLinesMaskSize,
+ type: 'number',
default: 0.5,
min: 0.0,
max: 1.0,
step: 0.01,
},
black: {
- type: 'boolean' as const,
+ label: i18n.ts._imageEffector._fxProps.zoomLinesBlack,
+ type: 'boolean',
default: false,
},
},
main: ({ gl, u, params }) => {
gl.uniform2f(u.pos, (1.0 + params.x) / 2.0, (1.0 + params.y) / 2.0);
gl.uniform1f(u.frequency, params.frequency);
- gl.uniform1i(u.thresholdEnabled, params.thresholdEnabled ? 1 : 0);
+ // thresholdã®èª¿æ•´ãŒæœ‰åйãªé–“ã¯smoothingãŒåˆ©ç”¨ã§ããªã„
+ gl.uniform1i(u.thresholdEnabled, params.smoothing ? 0 : 1);
gl.uniform1f(u.threshold, params.threshold);
gl.uniform1f(u.maskSize, params.maskSize);
gl.uniform1i(u.black, params.black ? 1 : 0);
diff --git a/packages/frontend/src/utility/inapp-search.ts b/packages/frontend/src/utility/inapp-search.ts
new file mode 100644
index 0000000000..cbc3d87ff8
--- /dev/null
+++ b/packages/frontend/src/utility/inapp-search.ts
@@ -0,0 +1,37 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import type { GeneratedSearchIndexItem } from 'search-index';
+
+export type SearchIndexItem = {
+ id: string;
+ parentId?: string;
+ path?: string;
+ label: string;
+ keywords: string[];
+ texts: string[];
+ icon?: string;
+};
+
+export function genSearchIndexes(generated: GeneratedSearchIndexItem[]): SearchIndexItem[] {
+ const rootMods = new Map(generated.map(item => [item.id, item]));
+
+ // link inlining here
+ for (const item of generated) {
+ if (item.inlining) {
+ for (const id of item.inlining) {
+ const inline = rootMods.get(id);
+ if (inline) {
+ inline.parentId = item.id;
+ inline.path = item.path;
+ } else {
+ console.log('[Settings Search Index] Failed to inline', id);
+ }
+ }
+ }
+ }
+
+ return generated;
+}
diff --git a/packages/frontend/src/utility/lookup.ts b/packages/frontend/src/utility/lookup.ts
index 90611094fa..9baf40b731 100644
--- a/packages/frontend/src/utility/lookup.ts
+++ b/packages/frontend/src/utility/lookup.ts
@@ -8,6 +8,7 @@ import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { mainRouter } from '@/router.js';
+import { acct } from '@/filters/user';
export async function lookup(router?: Router) {
const _router = router ?? mainRouter;
@@ -19,12 +20,16 @@ export async function lookup(router?: Router) {
if (canceled || query.length <= 1) return;
if (query.startsWith('@') && !query.includes(' ')) {
- _router.push(`/${query}`);
+ _router.pushByPath(`/${query}`);
return;
}
if (query.startsWith('#')) {
- _router.push(`/tags/${encodeURIComponent(query.substring(1))}`);
+ _router.push('/tags/:tag', {
+ params: {
+ tag: query.substring(1),
+ }
+ });
return;
}
@@ -32,9 +37,17 @@ export async function lookup(router?: Router) {
const res = await apLookup(query);
if (res.type === 'User') {
- _router.push(`/@${res.object.username}@${res.object.host}`);
+ _router.push('/@:acct/:page?', {
+ params: {
+ acct: acct(res.object),
+ },
+ });
} else if (res.type === 'Note') {
- _router.push(`/notes/${res.object.id}`);
+ _router.push('/notes/:noteId/:initialTab?', {
+ params: {
+ noteId: res.object.id,
+ },
+ });
}
return;
diff --git a/packages/frontend/src/utility/paginator.ts b/packages/frontend/src/utility/paginator.ts
index 63525b311a..7db55db7d6 100644
--- a/packages/frontend/src/utility/paginator.ts
+++ b/packages/frontend/src/utility/paginator.ts
@@ -37,6 +37,7 @@ export interface IPaginator<T = unknown, _T = T & MisskeyEntity> {
fetchingOlder: Ref<boolean>;
fetchingNewer: Ref<boolean>;
canFetchOlder: Ref<boolean>;
+ canFetchNewer: Ref<boolean>;
canSearch: boolean;
error: Ref<boolean>;
computedParams: ComputedRef<Misskey.Endpoints[PaginatorCompatibleEndpointPaths]['req'] | null | undefined> | null;
@@ -77,6 +78,7 @@ export class Paginator<
public fetchingOlder = ref(false);
public fetchingNewer = ref(false);
public canFetchOlder = ref(false);
+ public canFetchNewer = ref(false);
public canSearch = false;
public error = ref(false);
private endpoint: Endpoint;
@@ -85,7 +87,12 @@ export class Paginator<
public computedParams: ComputedRef<E['req'] | null | undefined> | null;
public initialId: MisskeyEntity['id'] | null = null;
public initialDate: number | null = null;
+
+ // åˆå›žèª­ã¿è¾¼ã¿æ™‚ã€initialIdを基準ã«ãれより新ã—ã„ã‚‚ã®ã‚’å–å¾—ã™ã‚‹ã‹å¤ã„ã‚‚ã®ã‚’å–å¾—ã™ã‚‹ã‹
+ // newer: initialIdより新ã—ã„ã‚‚ã®ã‚’å–å¾—ã™ã‚‹
+ // older: initialIdよりå¤ã„ã‚‚ã®ã‚’å–å¾—ã™ã‚‹ (default)
public initialDirection: 'newer' | 'older';
+
private offsetMode: boolean;
public noPaging: boolean;
public searchQuery = ref<null | string>('');
@@ -116,6 +123,7 @@ export class Paginator<
initialId?: MisskeyEntity['id'];
initialDate?: number | null;
initialDirection?: 'newer' | 'older';
+
order?: 'newest' | 'oldest';
// 一部ã®APIã¯ã•らã«é¡ã‚Œã‚‹å ´åˆã§ã‚‚パフォーマンス上ã®ç†ç”±ã§limit以下ã®çµæžœã‚’è¿”ã™å ´åˆãŒã‚りã€ãã®å ´åˆã¯safeã€ãれ以外ã¯limitã«ã™ã‚‹ã“ã¨ã‚’推奨
@@ -222,15 +230,15 @@ export class Paginator<
if (this.canFetchDetection === 'limit') {
if (apiRes.length < FIRST_FETCH_LIMIT) {
- this.canFetchOlder.value = false;
+ (this.initialDirection === 'older' ? this.canFetchOlder : this.canFetchNewer).value = false;
} else {
- this.canFetchOlder.value = true;
+ (this.initialDirection === 'older' ? this.canFetchOlder : this.canFetchNewer).value = true;
}
} else if (this.canFetchDetection === 'safe' || this.canFetchDetection == null) {
if (apiRes.length === 0 || this.noPaging) {
- this.canFetchOlder.value = false;
+ (this.initialDirection === 'older' ? this.canFetchOlder : this.canFetchNewer).value = false;
} else {
- this.canFetchOlder.value = true;
+ (this.initialDirection === 'older' ? this.canFetchOlder : this.canFetchNewer).value = true;
}
}
@@ -273,7 +281,11 @@ export class Paginator<
if (i === 10) item._shouldInsertAd_ = true;
}
- this.pushItems(apiRes);
+ if (this.order.value === 'oldest') {
+ this.unshiftItems(apiRes.toReversed(), false);
+ } else {
+ this.pushItems(apiRes);
+ }
if (this.canFetchDetection === 'limit') {
if (apiRes.length < FIRST_FETCH_LIMIT) {
@@ -313,7 +325,11 @@ export class Paginator<
this.fetchingNewer.value = false;
- if (apiRes == null || apiRes.length === 0) return; // ã“れやらãªã„ã¨ä½™è¨ˆãªre-renderãŒèµ°ã‚‹
+ if (apiRes == null || apiRes.length === 0) {
+ this.canFetchNewer.value = false;
+ // 余計ãªre-renderを防止ã™ã‚‹ãŸã‚ã“ã“ã§çµ‚了
+ return;
+ }
if (options.toQueue) {
this.aheadQueue.unshift(...apiRes.toReversed());
@@ -325,9 +341,19 @@ export class Paginator<
if (this.order.value === 'oldest') {
this.pushItems(apiRes);
} else {
- this.unshiftItems(apiRes.toReversed());
+ this.unshiftItems(apiRes.toReversed(), false);
}
}
+
+ if (this.canFetchDetection === 'limit') {
+ if (apiRes.length < FIRST_FETCH_LIMIT) {
+ this.canFetchNewer.value = false;
+ } else {
+ this.canFetchNewer.value = true;
+ }
+ }
+ // canFetchDetectionãŒ'safe'ã®å ´åˆãƒ»apiRes.length === 0 ã®å ´åˆã¯ apiRes.length === 0 ã®å ´åˆã« canFetchNewer.value = false ã«ãªã‚‹ãŒã€
+ // 余計㪠re-render を防ããŸã‚ã«ä¸Šéƒ¨ã§å‡¦ç†ã—ã¦ã„る。ãã®ãŸã‚ã€ã“ã“ã§ã¯ä½•ã‚‚ã—ãªã„
}
public trim(trigger = true): void {
@@ -336,10 +362,10 @@ export class Paginator<
if (this.useShallowRef && trigger) triggerRef(this.items);
}
- public unshiftItems(newItems: T[]): void {
+ public unshiftItems(newItems: T[], trim = true): void {
if (newItems.length === 0) return; // ã“れやらãªã„ã¨ä½™è¨ˆãªre-renderãŒèµ°ã‚‹
this.items.value.unshift(...newItems.filter(x => !this.items.value.some(y => y.id === x.id))); // ストリーミングやãƒãƒ¼ãƒªãƒ³ã‚°ã®ã‚¿ã‚¤ãƒŸãƒ³ã‚°ã«ã‚ˆã£ã¦ã¯é‡è¤‡ã™ã‚‹ã“ã¨ãŒã‚ã‚‹ãŸã‚
- this.trim(false);
+ if (trim) this.trim(true);
if (this.useShallowRef) triggerRef(this.items);
}
diff --git a/packages/frontend/src/utility/popout.ts b/packages/frontend/src/utility/popout.ts
index 5b141222e8..7e0222c459 100644
--- a/packages/frontend/src/utility/popout.ts
+++ b/packages/frontend/src/utility/popout.ts
@@ -20,8 +20,8 @@ export function popout(path: string, w?: HTMLElement) {
} else {
const width = 400;
const height = 500;
- const x = window.top.outerHeight / 2 + window.top.screenY - (height / 2);
- const y = window.top.outerWidth / 2 + window.top.screenX - (width / 2);
+ const x = window.top == null ? 0 : window.top.outerHeight / 2 + window.top.screenY - (height / 2);
+ const y = window.top == null ? 0 : window.top.outerWidth / 2 + window.top.screenX - (width / 2);
window.open(url, url,
`width=${width}, height=${height}, top=${x}, left=${y}`);
}
diff --git a/packages/frontend/src/utility/popup-position.ts b/packages/frontend/src/utility/popup-position.ts
index 676dfb7507..0ba0d8e897 100644
--- a/packages/frontend/src/utility/popup-position.ts
+++ b/packages/frontend/src/utility/popup-position.ts
@@ -29,8 +29,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
left = rect.left + window.scrollX + (props.anchorElement.offsetWidth / 2);
top = (rect.top + window.scrollY - contentHeight) - props.innerMargin;
} else {
- left = props.x;
- top = (props.y - contentHeight) - props.innerMargin;
+ left = props.x!;
+ top = (props.y! - contentHeight) - props.innerMargin;
}
left -= (el.offsetWidth / 2);
@@ -54,8 +54,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
left = rect.left + window.scrollX + (props.anchorElement.offsetWidth / 2);
top = (rect.top + window.scrollY + props.anchorElement.offsetHeight) + props.innerMargin;
} else {
- left = props.x;
- top = (props.y) + props.innerMargin;
+ left = props.x!;
+ top = (props.y!) + props.innerMargin;
}
left -= (el.offsetWidth / 2);
@@ -79,8 +79,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
left = (rect.left + window.scrollX - contentWidth) - props.innerMargin;
top = rect.top + window.scrollY + (props.anchorElement.offsetHeight / 2);
} else {
- left = (props.x - contentWidth) - props.innerMargin;
- top = props.y;
+ left = (props.x! - contentWidth) - props.innerMargin;
+ top = props.y!;
}
top -= (el.offsetHeight / 2);
@@ -97,8 +97,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
};
const calcPosWhenRight = () => {
- let left: number;
- let top: number;
+ let left = 0; // TSを黙らã™ãŸã‚ã¨ã‚Šã‚ãˆãšåˆæœŸå€¤ã‚’0ã«
+ let top = 0; // TSを黙らã™ãŸã‚ã¨ã‚Šã‚ãˆãšåˆæœŸå€¤ã‚’0ã«
if (props.anchorElement) {
left = (rect.left + props.anchorElement.offsetWidth + window.scrollX) + props.innerMargin;
@@ -113,8 +113,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
top -= (el.offsetHeight / 2);
}
} else {
- left = props.x + props.innerMargin;
- top = props.y;
+ left = props.x! + props.innerMargin;
+ top = props.y!;
top -= (el.offsetHeight / 2);
}
diff --git a/packages/frontend/src/utility/reload-ask.ts b/packages/frontend/src/utility/reload-ask.ts
deleted file mode 100644
index 7c7ea113d4..0000000000
--- a/packages/frontend/src/utility/reload-ask.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { i18n } from '@/i18n.js';
-import * as os from '@/os.js';
-import { unisonReload } from '@/utility/unison-reload.js';
-
-let isReloadConfirming = false;
-
-export async function reloadAsk(opts: {
- unison?: boolean;
- reason?: string;
-}) {
- if (isReloadConfirming) {
- return;
- }
-
- isReloadConfirming = true;
-
- const { canceled } = await os.confirm(opts.reason == null ? {
- type: 'info',
- text: i18n.ts.reloadConfirm,
- } : {
- type: 'info',
- title: i18n.ts.reloadConfirm,
- text: opts.reason,
- }).finally(() => {
- isReloadConfirming = false;
- });
-
- if (canceled) return;
-
- if (opts.unison) {
- unisonReload();
- } else {
- window.location.reload();
- }
-}
diff --git a/packages/frontend/src/utility/reload-suggest.ts b/packages/frontend/src/utility/reload-suggest.ts
new file mode 100644
index 0000000000..f1888f721e
--- /dev/null
+++ b/packages/frontend/src/utility/reload-suggest.ts
@@ -0,0 +1,12 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ref } from 'vue';
+
+export const shouldSuggestReload = ref(false);
+
+export function suggestReload() {
+ shouldSuggestReload.value = true;
+}
diff --git a/packages/frontend/src/utility/settings-search-index.ts b/packages/frontend/src/utility/settings-search-index.ts
deleted file mode 100644
index 7ed97ed34f..0000000000
--- a/packages/frontend/src/utility/settings-search-index.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { searchIndexes as generated } from 'search-index:settings';
-import type { GeneratedSearchIndexItem } from 'search-index:settings';
-
-export type SearchIndexItem = {
- id: string;
- parentId?: string;
- path?: string;
- label: string;
- keywords: string[];
- icon?: string;
-};
-
-const rootMods = new Map(generated.map(item => [item.id, item]));
-
-// link inlining here
-for (const item of generated) {
- if (item.inlining) {
- for (const id of item.inlining) {
- const inline = rootMods.get(id);
- if (inline) {
- inline.parentId = item.id;
- } else {
- console.log('[Settings Search Index] Failed to inline', id);
- }
- }
- }
-}
-
-export const searchIndexes: SearchIndexItem[] = generated;
-
diff --git a/packages/frontend/src/utility/sticky-sidebar.ts b/packages/frontend/src/utility/sticky-sidebar.ts
index 867c9b8324..435555896f 100644
--- a/packages/frontend/src/utility/sticky-sidebar.ts
+++ b/packages/frontend/src/utility/sticky-sidebar.ts
@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
+/*
export class StickySidebar {
private lastScrollTop = 0;
private container: HTMLElement;
@@ -53,3 +54,4 @@ export class StickySidebar {
this.lastScrollTop = scrollTop <= 0 ? 0 : scrollTop;
}
}
+*/
diff --git a/packages/frontend/src/utility/virtual.d.ts b/packages/frontend/src/utility/virtual.d.ts
index 63dc4372b7..00f01992aa 100644
--- a/packages/frontend/src/utility/virtual.d.ts
+++ b/packages/frontend/src/utility/virtual.d.ts
@@ -3,16 +3,25 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
+type XGeneratedSearchIndexItem = {
+ id: string;
+ parentId?: string;
+ path?: string;
+ label: string;
+ keywords: string[];
+ texts: string[];
+ icon?: string;
+ inlining?: string[];
+};
+
+declare module 'search-index' {
+ export type GeneratedSearchIndexItem = XGeneratedSearchIndexItem;
+}
+
declare module 'search-index:settings' {
- export type GeneratedSearchIndexItem = {
- id: string;
- parentId?: string;
- path?: string;
- label: string;
- keywords: string[];
- icon?: string;
- inlining?: string[];
- };
+ export const searchIndexes: XGeneratedSearchIndexItem[];
+}
- export const searchIndexes: GeneratedSearchIndexItem[];
+declare module 'search-index:admin' {
+ export const searchIndexes: XGeneratedSearchIndexItem[];
}
diff --git a/packages/frontend/src/utility/watermark.ts b/packages/frontend/src/utility/watermark.ts
index f0b38684f0..75807b30c4 100644
--- a/packages/frontend/src/utility/watermark.ts
+++ b/packages/frontend/src/utility/watermark.ts
@@ -150,7 +150,6 @@ export class WatermarkRenderer {
minorRadius: layer.minorRadius,
minorOpacity: layer.minorOpacity,
color: layer.color,
- opacity: layer.opacity,
},
};
} else if (layer.type === 'checker') {
diff --git a/packages/frontend/src/widgets/WidgetInstanceInfo.vue b/packages/frontend/src/widgets/WidgetInstanceInfo.vue
index 722e6fadb2..7e6a779cf0 100644
--- a/packages/frontend/src/widgets/WidgetInstanceInfo.vue
+++ b/packages/frontend/src/widgets/WidgetInstanceInfo.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_panel">
<div :class="$style.container" :style="{ backgroundImage: instance.bannerUrl ? `url(${ instance.bannerUrl })` : undefined }">
<div :class="$style.iconContainer">
- <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.icon"/>
+ <img :src="instance.iconUrl ?? '/favicon.ico'" alt="" :class="$style.icon"/>
</div>
<div :class="$style.bodyContainer">
<div :class="$style.body">
@@ -20,10 +20,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
+import { host } from '@@/js/config.js';
import { useWidgetPropsManager } from './widget.js';
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
-import { host } from '@@/js/config.js';
import { instance } from '@/instance.js';
const name = 'instanceInfo';
diff --git a/packages/frontend/src/workers/draw-blurhash.ts b/packages/frontend/src/workers/draw-blurhash.ts
index 22de6cd3a8..6e49f6bf66 100644
--- a/packages/frontend/src/workers/draw-blurhash.ts
+++ b/packages/frontend/src/workers/draw-blurhash.ts
@@ -18,5 +18,5 @@ onmessage = (event) => {
render(event.data.hash, canvas);
const bitmap = canvas.transferToImageBitmap();
- postMessage({ id: event.data.id, bitmap });
+ postMessage({ id: event.data.id, bitmap }, [bitmap]);
};
diff --git a/packages/frontend/test/aiscript/api.test.ts b/packages/frontend/test/aiscript/api.test.ts
index ad24625b96..34f880286c 100644
--- a/packages/frontend/test/aiscript/api.test.ts
+++ b/packages/frontend/test/aiscript/api.test.ts
@@ -37,6 +37,17 @@ let $iMock = vi.hoisted<Partial<typeof import('@/i.js').$i> | null >(
() => null
);
+function errorWithPos<T extends errors.AiScriptError>(
+ error: T,
+ line: number,
+ column: number,
+): T {
+ const pos = { line, column };
+ error.pos = pos;
+ error.message = error.message + `\n at <root> (Line ${pos.line}, Column ${pos.column})`;
+ return error;
+}
+
vi.mock('@/i.js', () => {
return {
get $i() {
@@ -316,7 +327,7 @@ describe('AiScript common API', () => {
await expect(() => exe(`
Mk:api('https://example.com/api/ping', {})
`)).rejects.toStrictEqual(
- new errors.AiScriptRuntimeError('invalid endpoint'),
+ errorWithPos(new errors.AiScriptRuntimeError('invalid endpoint'), 2, 11),
);
expect(misskeyApiMock).not.toHaveBeenCalled();
});
@@ -325,7 +336,7 @@ describe('AiScript common API', () => {
await expect(() => exe(`
Mk:api('ping')
`)).rejects.toStrictEqual(
- new errors.AiScriptRuntimeError('expected param'),
+ errorWithPos(new errors.AiScriptRuntimeError('expected param'), 2, 11),
);
expect(misskeyApiMock).not.toHaveBeenCalled();
});
@@ -353,7 +364,7 @@ describe('AiScript common API', () => {
await expect(() => exe(`
Mk:save('key')
`)).rejects.toStrictEqual(
- new errors.AiScriptRuntimeError('Expect anything, but got nothing.'),
+ errorWithPos(new errors.AiScriptRuntimeError('Expect anything, but got nothing.'), 2, 12),
);
});
diff --git a/packages/frontend/test/home.test.ts b/packages/frontend/test/home.test.ts
index a67ea40176..f3fa89d6ae 100644
--- a/packages/frontend/test/home.test.ts
+++ b/packages/frontend/test/home.test.ts
@@ -15,7 +15,7 @@ import 'intersection-observer';
describe('XHome', () => {
const renderHome = (user: Partial<Misskey.entities.UserDetailed>): RenderResult => {
return render(XHome, {
- props: { user, disableNotes: true },
+ props: { user: user as Misskey.entities.UserDetailed, disableNotes: true },
global: { directives, components },
});
};
diff --git a/packages/frontend/test/note.test.ts b/packages/frontend/test/note.test.ts
index fda2d9ad7b..2d5d2712d6 100644
--- a/packages/frontend/test/note.test.ts
+++ b/packages/frontend/test/note.test.ts
@@ -29,7 +29,7 @@ describe('MkMediaImage', () => {
comment: null,
properties: {},
...image,
- } as DriveFile,
+ } as Misskey.entities.DriveFile,
},
global: { directives, components },
});
diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json
index 9c7c611e5b..662dc6ed4e 100644
--- a/packages/frontend/tsconfig.json
+++ b/packages/frontend/tsconfig.json
@@ -46,6 +46,7 @@
},
"compileOnSave": false,
"include": [
+ "./build.ts",
"./lib/**/*.ts",
"./src/**/*.ts",
"./src/**/*.vue",
@@ -54,6 +55,7 @@
"./@types/**/*.ts"
],
"exclude": [
- ".storybook/**/*"
+ ".storybook/**/*",
+ "./src/**/*.stories.ts"
]
}
diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts
index 77b5b48a18..9b54014b54 100644
--- a/packages/frontend/vite.config.ts
+++ b/packages/frontend/vite.config.ts
@@ -14,6 +14,7 @@ import pluginJson5 from './vite.json5.js';
import pluginCreateSearchIndex from './lib/vite-plugin-create-search-index.js';
import type { Options as SearchIndexOptions } from './lib/vite-plugin-create-search-index.js';
import pluginWatchLocales from './lib/vite-plugin-watch-locales.js';
+import { pluginRemoveUnrefI18n } from '../frontend-builder/rollup-plugin-remove-unref-i18n.js';
const url = process.env.NODE_ENV === 'development' ? yaml.load(await fsp.readFile('../../.config/default.yml', 'utf-8')).url : null;
const host = url ? (new URL(url)).hostname : undefined;
@@ -28,6 +29,11 @@ export const searchIndexes = [{
mainVirtualModule: 'search-index:settings',
modulesToHmrOnUpdate: ['src/pages/settings/index.vue'],
verbose: process.env.FRONTEND_SEARCH_INDEX_VERBOSE === 'true',
+}, {
+ targetFilePaths: ['src/pages/admin/*.vue'],
+ mainVirtualModule: 'search-index:admin',
+ modulesToHmrOnUpdate: ['src/pages/admin/index.vue'],
+ verbose: process.env.FRONTEND_SEARCH_INDEX_VERBOSE === 'true',
}] satisfies SearchIndexOptions[];
/**
@@ -106,6 +112,7 @@ export function getConfig(): UserConfig {
pluginWatchLocales(),
...searchIndexes.map(options => pluginCreateSearchIndex(options)),
pluginVue(),
+ pluginRemoveUnrefI18n(),
pluginUnwindCssModuleClassName(),
pluginJson5(),
...process.env.NODE_ENV === 'production'
@@ -169,16 +176,21 @@ export function getConfig(): UserConfig {
manifest: 'manifest.json',
rollupOptions: {
input: {
- app: './src/_boot_.ts',
+ i18n: './src/i18n.ts',
+ entry: './src/_boot_.ts',
},
external: externalPackages.map(p => p.match),
+ preserveEntrySignatures: 'allow-extension',
output: {
manualChunks: {
vue: ['vue'],
photoswipe: ['photoswipe', 'photoswipe/lightbox', 'photoswipe/style.css'],
+ // dependencies of i18n.ts
+ 'config': ['@@/js/config.js'],
},
- chunkFileNames: process.env.NODE_ENV === 'production' ? '[hash:8].js' : '[name]-[hash:8].js',
- assetFileNames: process.env.NODE_ENV === 'production' ? '[hash:8][extname]' : '[name]-[hash:8][extname]',
+ entryFileNames: 'scripts/[hash:8].js',
+ chunkFileNames: 'scripts/[hash:8].js',
+ assetFileNames: 'assets/[hash:8][extname]',
paths(id) {
for (const p of externalPackages) {
if (p.match.test(id)) {
diff --git a/packages/icons-subsetter/package.json b/packages/icons-subsetter/package.json
index a0ac7b3435..fb536991fb 100644
--- a/packages/icons-subsetter/package.json
+++ b/packages/icons-subsetter/package.json
@@ -11,17 +11,17 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
- "@types/node": "22.16.4",
+ "@types/node": "22.17.2",
"@types/wawoff2": "1.0.2",
- "@typescript-eslint/eslint-plugin": "8.37.0",
- "@typescript-eslint/parser": "8.37.0"
+ "@typescript-eslint/eslint-plugin": "8.40.0",
+ "@typescript-eslint/parser": "8.40.0"
},
"dependencies": {
- "@tabler/icons-webfont": "3.34.0",
- "harfbuzzjs": "0.4.7",
+ "@tabler/icons-webfont": "3.34.1",
+ "harfbuzzjs": "0.4.9",
"tiny-glob": "0.2.9",
- "tsx": "4.20.3",
- "typescript": "5.8.3",
+ "tsx": "4.20.4",
+ "typescript": "5.9.2",
"wawoff2": "2.0.1"
},
"files": [
diff --git a/packages/misskey-bubble-game/package.json b/packages/misskey-bubble-game/package.json
index 73208be12f..2f445e7d4a 100644
--- a/packages/misskey-bubble-game/package.json
+++ b/packages/misskey-bubble-game/package.json
@@ -22,15 +22,15 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
- "@types/matter-js": "0.19.8",
+ "@types/matter-js": "0.20.0",
"@types/seedrandom": "3.0.8",
- "@types/node": "22.16.4",
- "@typescript-eslint/eslint-plugin": "8.37.0",
- "@typescript-eslint/parser": "8.37.0",
+ "@types/node": "22.17.2",
+ "@typescript-eslint/eslint-plugin": "8.40.0",
+ "@typescript-eslint/parser": "8.40.0",
"nodemon": "3.1.10",
"execa": "9.6.0",
- "typescript": "5.8.3",
- "esbuild": "0.25.6",
+ "typescript": "5.9.2",
+ "esbuild": "0.25.9",
"glob": "11.0.3"
},
"files": [
diff --git a/packages/misskey-bubble-game/src/game.ts b/packages/misskey-bubble-game/src/game.ts
index 7f230e39cb..33a753b87e 100644
--- a/packages/misskey-bubble-game/src/game.ts
+++ b/packages/misskey-bubble-game/src/game.ts
@@ -186,7 +186,7 @@ export class DropAndFusionGame extends EventEmitter<{
}
private createBody(mono: Mono, x: number, y: number) {
- const options: Matter.IBodyDefinition = {
+ const options = {
label: mono.id,
density: this.gameMode === 'space' ? 0.01 : ((mono.sizeX * mono.sizeY) / 10000),
restitution: this.gameMode === 'space' ? 0.5 : 0.2,
@@ -196,7 +196,7 @@ export class DropAndFusionGame extends EventEmitter<{
slop: this.gameMode === 'space' ? 0.01 : 0.7,
//mass: 0,
render: this.getMonoRenderOptions ? this.getMonoRenderOptions(mono) : undefined,
- };
+ } satisfies Matter.IChamferableBodyDefinition;
if (mono.shape === 'circle') {
return Matter.Bodies.circle(x, y, mono.sizeX / 2, options);
} else if (mono.shape === 'rectangle') {
diff --git a/packages/misskey-js/.swcrc b/packages/misskey-js/.swcrc
deleted file mode 100644
index 6fd7486370..0000000000
--- a/packages/misskey-js/.swcrc
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "$schema": "https://swc.rs/schema.json",
- "jsc": {
- "parser": {
- "syntax": "typescript",
- "dynamicImport": true,
- "decorators": true
- },
- "transform": {
- "legacyDecorator": true,
- "decoratorMetadata": true
- },
- "experimental": {
- "keepImportAssertions": true
- },
- "baseUrl": "src",
- "paths": {
- "@/*": ["*"]
- },
- "target": "es2022"
- },
- "minify": false
-}
diff --git a/packages/misskey-js/eslint.config.js b/packages/misskey-js/eslint.config.js
index 496892d897..5a1d4315b5 100644
--- a/packages/misskey-js/eslint.config.js
+++ b/packages/misskey-js/eslint.config.js
@@ -9,7 +9,7 @@ export default [
'**/node_modules',
'built',
'coverage',
- 'jest.config.ts',
+ 'vitest.config.ts',
'test',
'test-d',
'generator',
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index d584efe731..3388a3d9a9 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -297,6 +297,12 @@ type AdminQueueRemoveJobRequest = operations['admin___queue___remove-job']['requ
type AdminQueueRetryJobRequest = operations['admin___queue___retry-job']['requestBody']['content']['application/json'];
// @public (undocumented)
+type AdminQueueShowJobLogsRequest = operations['admin___queue___show-job-logs']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminQueueShowJobLogsResponse = operations['admin___queue___show-job-logs']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
type AdminQueueShowJobRequest = operations['admin___queue___show-job']['requestBody']['content']['application/json'];
// @public (undocumented)
@@ -1191,6 +1197,9 @@ type ClipsDeleteRequest = operations['clips___delete']['requestBody']['content']
type ClipsFavoriteRequest = operations['clips___favorite']['requestBody']['content']['application/json'];
// @public (undocumented)
+type ClipsListRequest = operations['clips___list']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
type ClipsListResponse = operations['clips___list']['responses']['200']['content']['application/json'];
// @public (undocumented)
@@ -1559,6 +1568,8 @@ declare namespace entities {
AdminQueueRetryJobRequest,
AdminQueueShowJobRequest,
AdminQueueShowJobResponse,
+ AdminQueueShowJobLogsRequest,
+ AdminQueueShowJobLogsResponse,
AdminQueueStatsResponse,
AdminRelaysAddRequest,
AdminRelaysAddResponse,
@@ -1733,6 +1744,7 @@ declare namespace entities {
ClipsCreateResponse,
ClipsDeleteRequest,
ClipsFavoriteRequest,
+ ClipsListRequest,
ClipsListResponse,
ClipsMyFavoritesResponse,
ClipsNotesRequest,
@@ -2121,6 +2133,7 @@ declare namespace entities {
UsersUpdateMemoRequest,
V2AdminEmojiListRequest,
V2AdminEmojiListResponse,
+ VerifyEmailRequest,
Error_2 as Error,
UserLite,
UserDetailedNotMeOnly,
@@ -2138,6 +2151,7 @@ declare namespace entities {
Note,
NoteDraft,
NoteReaction,
+ NoteReactionWithNote,
NoteFavorite,
Notification_2 as Notification,
DriveFile,
@@ -2179,6 +2193,7 @@ declare namespace entities {
MetaLite,
MetaDetailedOnly,
MetaDetailed,
+ UserWebhook,
SystemWebhook,
AbuseReportNotificationRecipient,
ChatMessage,
@@ -2794,7 +2809,7 @@ type ModerationLog = {
id: ID;
createdAt: DateString;
userId: User['id'];
- user: UserDetailedNotMe | null;
+ user: UserDetailedNotMe;
} & ({
type: 'updateServerSettings';
info: ModerationLogPayloads['updateServerSettings'];
@@ -2948,10 +2963,13 @@ type ModerationLog = {
} | {
type: 'deleteChatRoom';
info: ModerationLogPayloads['deleteChatRoom'];
+} | {
+ type: 'updateProxyAccountDescription';
+ info: ModerationLogPayloads['updateProxyAccountDescription'];
});
// @public (undocumented)
-export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "updateRemoteInstanceNote", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "forwardAbuseReport", "updateAbuseReportNote", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner", "createSystemWebhook", "updateSystemWebhook", "deleteSystemWebhook", "createAbuseReportNotificationRecipient", "updateAbuseReportNotificationRecipient", "deleteAbuseReportNotificationRecipient", "deleteAccount", "deletePage", "deleteFlash", "deleteGalleryPost", "deleteChatRoom"];
+export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "updateRemoteInstanceNote", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "forwardAbuseReport", "updateAbuseReportNote", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner", "createSystemWebhook", "updateSystemWebhook", "deleteSystemWebhook", "createAbuseReportNotificationRecipient", "updateAbuseReportNotificationRecipient", "deleteAbuseReportNotificationRecipient", "deleteAccount", "deletePage", "deleteFlash", "deleteGalleryPost", "deleteChatRoom", "updateProxyAccountDescription"];
// @public (undocumented)
type MuteCreateRequest = operations['mute___create']['requestBody']['content']['application/json'];
@@ -2997,6 +3015,9 @@ type NoteFavorite = components['schemas']['NoteFavorite'];
type NoteReaction = components['schemas']['NoteReaction'];
// @public (undocumented)
+type NoteReactionWithNote = components['schemas']['NoteReactionWithNote'];
+
+// @public (undocumented)
type NotesChildrenRequest = operations['notes___children']['requestBody']['content']['application/json'];
// @public (undocumented)
@@ -3271,7 +3292,7 @@ type PromoReadRequest = operations['promo___read']['requestBody']['content']['ap
type PureRenote = Omit<Note, 'renote' | 'renoteId' | 'reply' | 'replyId' | 'text' | 'cw' | 'files' | 'fileIds' | 'poll'> & AllNullRecord<Pick<Note, 'text'>> & AllNullOrOptionalRecord<Pick<Note, 'reply' | 'replyId' | 'cw' | 'poll'>> & {
files: [];
fileIds: [];
-} & NonNullableRecord<Pick<Note, 'renote' | 'renoteId'>>;
+} & NonNullableRecord<Pick<Note, 'renoteId'>> & Pick<Note, 'renote'>;
// @public (undocumented)
type QueueCount = components['schemas']['QueueCount'];
@@ -3794,14 +3815,20 @@ type UsersShowResponse = operations['users___show']['responses']['200']['content
type UsersUpdateMemoRequest = operations['users___update-memo']['requestBody']['content']['application/json'];
// @public (undocumented)
+type UserWebhook = components['schemas']['UserWebhook'];
+
+// @public (undocumented)
type V2AdminEmojiListRequest = operations['v2___admin___emoji___list']['requestBody']['content']['application/json'];
// @public (undocumented)
type V2AdminEmojiListResponse = operations['v2___admin___emoji___list']['responses']['200']['content']['application/json'];
+// @public (undocumented)
+type VerifyEmailRequest = operations['verify-email']['requestBody']['content']['application/json'];
+
// Warnings were encountered during analysis:
//
-// src/entities.ts:54:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
+// src/entities.ts:55:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
// src/streaming.ts:57:3 - (ae-forgotten-export) The symbol "ReconnectingWebSocket" needs to be exported by the entry point index.d.ts
// src/streaming.types.ts:218:4 - (ae-forgotten-export) The symbol "ReversiUpdateKey" needs to be exported by the entry point index.d.ts
// src/streaming.types.ts:228:4 - (ae-forgotten-export) The symbol "ReversiUpdateSettings" needs to be exported by the entry point index.d.ts
diff --git a/packages/misskey-js/generator/package.json b/packages/misskey-js/generator/package.json
index f566d6dc21..877af79369 100644
--- a/packages/misskey-js/generator/package.json
+++ b/packages/misskey-js/generator/package.json
@@ -7,15 +7,15 @@
"generate": "tsx src/generator.ts && eslint ./built/**/*.ts --fix"
},
"devDependencies": {
- "@readme/openapi-parser": "2.7.0",
- "@types/node": "22.16.4",
- "@typescript-eslint/eslint-plugin": "8.37.0",
- "@typescript-eslint/parser": "8.37.0",
+ "@readme/openapi-parser": "5.0.1",
+ "@types/node": "22.17.2",
+ "@typescript-eslint/eslint-plugin": "8.40.0",
+ "@typescript-eslint/parser": "8.40.0",
"openapi-types": "12.1.3",
- "openapi-typescript": "7.8.0",
+ "openapi-typescript": "7.9.1",
"ts-case-convert": "2.1.0",
- "tsx": "4.20.3",
- "typescript": "5.8.3"
+ "tsx": "4.20.5",
+ "typescript": "5.9.2"
},
"files": [
"built"
diff --git a/packages/misskey-js/generator/src/generator.ts b/packages/misskey-js/generator/src/generator.ts
index a95fc962a8..55f2ea9443 100644
--- a/packages/misskey-js/generator/src/generator.ts
+++ b/packages/misskey-js/generator/src/generator.ts
@@ -2,7 +2,7 @@ import assert from 'assert';
import { mkdir, readFile, writeFile } from 'fs/promises';
import type { OpenAPIV3_1 } from 'openapi-types';
import { toPascal } from 'ts-case-convert';
-import OpenAPIParser from '@readme/openapi-parser';
+import { parse } from '@readme/openapi-parser';
import openapiTS, { astToString } from 'openapi-typescript';
import type { OpenAPI3, OperationObject, PathItemObject } from 'openapi-typescript';
import ts from 'typescript';
@@ -401,7 +401,7 @@ async function main() {
await mkdir(generatePath, { recursive: true });
const openApiJsonPath = './api.json';
- const openApiDocs = await OpenAPIParser.parse(openApiJsonPath) as OpenAPIV3_1.Document;
+ const openApiDocs = await parse(openApiJsonPath) as OpenAPIV3_1.Document;
const typeFileName = './built/autogen/types.ts';
await generateBaseTypes(openApiDocs, openApiJsonPath, typeFileName);
diff --git a/packages/misskey-js/jest.config.cjs b/packages/misskey-js/jest.config.cjs
deleted file mode 100644
index 1230a4b5e2..0000000000
--- a/packages/misskey-js/jest.config.cjs
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
-* For a detailed explanation regarding each configuration property and type check, visit:
-* https://jestjs.io/docs/en/configuration.html
-*/
-
-module.exports = {
- // All imported modules in your tests should be mocked automatically
- // automock: false,
-
- // Stop running tests after `n` failures
- // bail: 0,
-
- // The directory where Jest should store its cached dependency information
- // cacheDirectory: "C:\\Users\\ai\\AppData\\Local\\Temp\\jest",
-
- // Automatically clear mock calls and instances between every test
- // clearMocks: false,
-
- // Indicates whether the coverage information should be collected while executing the test
- // collectCoverage: false,
-
- // An array of glob patterns indicating a set of files for which coverage information should be collected
- // collectCoverageFrom: undefined,
-
- // The directory where Jest should output its coverage files
- coverageDirectory: "coverage",
-
- // An array of regexp pattern strings used to skip coverage collection
- // coveragePathIgnorePatterns: [
- // "\\\\node_modules\\\\"
- // ],
-
- // Indicates which provider should be used to instrument code for coverage
- coverageProvider: "v8",
-
- // A list of reporter names that Jest uses when writing coverage reports
- // coverageReporters: [
- // "json",
- // "text",
- // "lcov",
- // "clover"
- // ],
-
- // An object that configures minimum threshold enforcement for coverage results
- // coverageThreshold: undefined,
-
- // A path to a custom dependency extractor
- // dependencyExtractor: undefined,
-
- // Make calling deprecated APIs throw helpful error messages
- // errorOnDeprecated: false,
-
- // Force coverage collection from ignored files using an array of glob patterns
- // forceCoverageMatch: [],
-
- // A path to a module which exports an async function that is triggered once before all test suites
- // globalSetup: undefined,
-
- // A path to a module which exports an async function that is triggered once after all test suites
- // globalTeardown: undefined,
-
- // A set of global variables that need to be available in all test environments
- // globals: {},
-
- // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
- // maxWorkers: "50%",
-
- // An array of directory names to be searched recursively up from the requiring module's location
- // moduleDirectories: [
- // "node_modules"
- // ],
-
- // An array of file extensions your modules use
- // moduleFileExtensions: [
- // "js",
- // "json",
- // "jsx",
- // "ts",
- // "tsx",
- // "node"
- // ],
-
- // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
- moduleNameMapper: {
- // Do not resolve .wasm.js to .wasm by the rule below
- '^(.+)\\.wasm\\.js$': '$1.wasm.js',
- // SWC converts @/foo/bar.js to `../../src/foo/bar.js`, and then this rule
- // converts it again to `../../src/foo/bar` which then can be resolved to
- // `.ts` files.
- // See https://github.com/swc-project/jest/issues/64#issuecomment-1029753225
- // TODO: Use `--allowImportingTsExtensions` on TypeScript 5.0 so that we can
- // directly import `.ts` files without this hack.
- '^((?:\\.{1,2}|[A-Z:])*/.*)\\.js$': '$1',
- },
-
- // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
- // modulePathIgnorePatterns: [],
-
- // Activates notifications for test results
- // notify: false,
-
- // An enum that specifies notification mode. Requires { notify: true }
- // notifyMode: "failure-change",
-
- // A preset that is used as a base for Jest's configuration
- // preset: undefined,
-
- // Run tests from one or more projects
- // projects: undefined,
-
- // Use this configuration option to add custom reporters to Jest
- // reporters: undefined,
-
- // Automatically reset mock state between every test
- // resetMocks: false,
-
- // Reset the module registry before running each individual test
- // resetModules: false,
-
- // A path to a custom resolver
- // resolver: undefined,
-
- // Automatically restore mock state between every test
- // restoreMocks: false,
-
- // The root directory that Jest should scan for tests and modules within
- // rootDir: undefined,
-
- // A list of paths to directories that Jest should use to search for files in
- roots: [
- "<rootDir>"
- ],
-
- // Allows you to use a custom runner instead of Jest's default test runner
- // runner: "jest-runner",
-
- // The paths to modules that run some code to configure or set up the testing environment before each test
- // setupFiles: [],
-
- // A list of paths to modules that run some code to configure or set up the testing framework before each test
- // setupFilesAfterEnv: [],
-
- // The number of seconds after which a test is considered as slow and reported as such in the results.
- // slowTestThreshold: 5,
-
- // A list of paths to snapshot serializer modules Jest should use for snapshot testing
- // snapshotSerializers: [],
-
- // The test environment that will be used for testing
- testEnvironment: "node",
-
- // Options that will be passed to the testEnvironment
- // testEnvironmentOptions: {},
-
- // Adds a location field to test results
- // testLocationInResults: false,
-
- // The glob patterns Jest uses to detect test files
- testMatch: [
- "**/__tests__/**/*.[jt]s?(x)",
- "**/?(*.)+(spec|test).[tj]s?(x)",
- "<rootDir>/test/**/*"
- ],
-
- // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
- // testPathIgnorePatterns: [
- // "\\\\node_modules\\\\"
- // ],
-
- // The regexp pattern or array of patterns that Jest uses to detect test files
- // testRegex: [],
-
- // This option allows the use of a custom results processor
- // testResultsProcessor: undefined,
-
- // This option allows use of a custom test runner
- // testRunner: "jasmine2",
-
- // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
- // testURL: "http://localhost",
-
- // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
- // timers: "real",
-
- // A map from regular expressions to paths to transformers
- transform: {
- "^.+\\.(t|j)sx?$": ["@swc/jest"],
- },
-
- // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
- // transformIgnorePatterns: [
- // "\\\\node_modules\\\\",
- // "\\.pnp\\.[^\\\\]+$"
- // ],
-
- // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
- // unmockedModulePathPatterns: undefined,
-
- // Indicates whether each individual test should be reported during the run
- // verbose: undefined,
-
- // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
- // watchPathIgnorePatterns: [],
-
- // Whether to use watchman for file crawling
- // watchman: true,
-};
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 59ed29eeab..be26cb6b9d 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
{
"type": "module",
"name": "misskey-js",
- "version": "2025.7.0",
+ "version": "2025.8.0",
"description": "Misskey SDK for JavaScript",
"license": "MIT",
"main": "./built/index.js",
@@ -25,8 +25,8 @@
"eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
"typecheck": "tsc --noEmit",
"lint": "pnpm typecheck && pnpm eslint",
- "jest": "jest --coverage --detectOpenHandles",
- "test": "pnpm jest && pnpm tsd",
+ "vitest": "vitest run --coverage",
+ "test": "pnpm vitest && pnpm tsd",
"update-autogen-code": "pnpm --filter misskey-js-type-generator generate && ncp generator/built/autogen src/autogen"
},
"repository": {
@@ -35,23 +35,20 @@
"directory": "packages/misskey-js"
},
"devDependencies": {
- "@microsoft/api-extractor": "7.52.8",
- "@swc/jest": "0.2.39",
- "@types/jest": "29.5.14",
- "@types/node": "22.16.4",
- "@typescript-eslint/eslint-plugin": "8.37.0",
- "@typescript-eslint/parser": "8.37.0",
- "jest": "29.7.0",
- "jest-fetch-mock": "3.0.3",
- "jest-websocket-mock": "2.5.0",
- "mock-socket": "9.3.1",
+ "@microsoft/api-extractor": "7.52.11",
+ "@types/node": "22.17.2",
+ "@typescript-eslint/eslint-plugin": "8.40.0",
+ "@typescript-eslint/parser": "8.40.0",
+ "@vitest/coverage-v8": "3.2.4",
+ "esbuild": "0.25.9",
+ "execa": "9.6.0",
+ "glob": "11.0.3",
"ncp": "2.0.0",
"nodemon": "3.1.10",
- "execa": "8.0.1",
- "tsd": "0.32.0",
- "typescript": "5.8.3",
- "esbuild": "0.25.6",
- "glob": "11.0.3"
+ "tsd": "0.33.0",
+ "typescript": "5.9.2",
+ "vitest": "3.2.4",
+ "vitest-websocket-mock": "0.5.0"
},
"files": [
"built"
diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
index 4a13045592..c4428efcc2 100644
--- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts
+++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
@@ -716,6 +716,17 @@ declare module '../api.js' {
/**
* No description provided.
*
+ * **Credential required**: *Yes* / **Permission**: *read:admin:queue*
+ */
+ request<E extends 'admin/queue/show-job-logs', P extends Endpoints[E]['req']>(
+ endpoint: E,
+ params: P,
+ credential?: string | null,
+ ): Promise<SwitchCaseResponseType<E, P>>;
+
+ /**
+ * No description provided.
+ *
* **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
*/
request<E extends 'admin/queue/stats', P extends Endpoints[E]['req']>(
@@ -4751,5 +4762,16 @@ declare module '../api.js' {
params: P,
credential?: string | null,
): Promise<SwitchCaseResponseType<E, P>>;
+
+ /**
+ * No description provided.
+ *
+ * **Credential required**: *No*
+ */
+ request<E extends 'verify-email', P extends Endpoints[E]['req']>(
+ endpoint: E,
+ params: P,
+ credential?: string | null,
+ ): Promise<SwitchCaseResponseType<E, P>>;
}
}
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index 5ef493946c..4b83a9dd9b 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -88,6 +88,8 @@ import type {
AdminQueueRetryJobRequest,
AdminQueueShowJobRequest,
AdminQueueShowJobResponse,
+ AdminQueueShowJobLogsRequest,
+ AdminQueueShowJobLogsResponse,
AdminQueueStatsResponse,
AdminRelaysAddRequest,
AdminRelaysAddResponse,
@@ -262,6 +264,7 @@ import type {
ClipsCreateResponse,
ClipsDeleteRequest,
ClipsFavoriteRequest,
+ ClipsListRequest,
ClipsListResponse,
ClipsMyFavoritesResponse,
ClipsNotesRequest,
@@ -650,6 +653,7 @@ import type {
UsersUpdateMemoRequest,
V2AdminEmojiListRequest,
V2AdminEmojiListResponse,
+ VerifyEmailRequest,
} from './entities.js';
export type Endpoints = {
@@ -717,6 +721,7 @@ export type Endpoints = {
'admin/queue/remove-job': { req: AdminQueueRemoveJobRequest; res: EmptyResponse };
'admin/queue/retry-job': { req: AdminQueueRetryJobRequest; res: EmptyResponse };
'admin/queue/show-job': { req: AdminQueueShowJobRequest; res: AdminQueueShowJobResponse };
+ 'admin/queue/show-job-logs': { req: AdminQueueShowJobLogsRequest; res: AdminQueueShowJobLogsResponse };
'admin/queue/stats': { req: EmptyRequest; res: AdminQueueStatsResponse };
'admin/relays/add': { req: AdminRelaysAddRequest; res: AdminRelaysAddResponse };
'admin/relays/list': { req: EmptyRequest; res: AdminRelaysListResponse };
@@ -826,7 +831,7 @@ export type Endpoints = {
'clips/create': { req: ClipsCreateRequest; res: ClipsCreateResponse };
'clips/delete': { req: ClipsDeleteRequest; res: EmptyResponse };
'clips/favorite': { req: ClipsFavoriteRequest; res: EmptyResponse };
- 'clips/list': { req: EmptyRequest; res: ClipsListResponse };
+ 'clips/list': { req: ClipsListRequest; res: ClipsListResponse };
'clips/my-favorites': { req: EmptyRequest; res: ClipsMyFavoritesResponse };
'clips/notes': { req: ClipsNotesRequest; res: ClipsNotesResponse };
'clips/remove-note': { req: ClipsRemoveNoteRequest; res: EmptyResponse };
@@ -1080,6 +1085,7 @@ export type Endpoints = {
'users/show': { req: UsersShowRequest; res: UsersShowResponse };
'users/update-memo': { req: UsersUpdateMemoRequest; res: EmptyResponse };
'v2/admin/emoji/list': { req: V2AdminEmojiListRequest; res: V2AdminEmojiListResponse };
+ 'verify-email': { req: VerifyEmailRequest; res: EmptyResponse };
};
/**
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index a11bbefde5..4ebe9a5155 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -91,6 +91,8 @@ export type AdminQueueRemoveJobRequest = operations['admin___queue___remove-job'
export type AdminQueueRetryJobRequest = operations['admin___queue___retry-job']['requestBody']['content']['application/json'];
export type AdminQueueShowJobRequest = operations['admin___queue___show-job']['requestBody']['content']['application/json'];
export type AdminQueueShowJobResponse = operations['admin___queue___show-job']['responses']['200']['content']['application/json'];
+export type AdminQueueShowJobLogsRequest = operations['admin___queue___show-job-logs']['requestBody']['content']['application/json'];
+export type AdminQueueShowJobLogsResponse = operations['admin___queue___show-job-logs']['responses']['200']['content']['application/json'];
export type AdminQueueStatsResponse = operations['admin___queue___stats']['responses']['200']['content']['application/json'];
export type AdminRelaysAddRequest = operations['admin___relays___add']['requestBody']['content']['application/json'];
export type AdminRelaysAddResponse = operations['admin___relays___add']['responses']['200']['content']['application/json'];
@@ -265,6 +267,7 @@ export type ClipsCreateRequest = operations['clips___create']['requestBody']['co
export type ClipsCreateResponse = operations['clips___create']['responses']['200']['content']['application/json'];
export type ClipsDeleteRequest = operations['clips___delete']['requestBody']['content']['application/json'];
export type ClipsFavoriteRequest = operations['clips___favorite']['requestBody']['content']['application/json'];
+export type ClipsListRequest = operations['clips___list']['requestBody']['content']['application/json'];
export type ClipsListResponse = operations['clips___list']['responses']['200']['content']['application/json'];
export type ClipsMyFavoritesResponse = operations['clips___my-favorites']['responses']['200']['content']['application/json'];
export type ClipsNotesRequest = operations['clips___notes']['requestBody']['content']['application/json'];
@@ -653,3 +656,4 @@ export type UsersShowResponse = operations['users___show']['responses']['200']['
export type UsersUpdateMemoRequest = operations['users___update-memo']['requestBody']['content']['application/json'];
export type V2AdminEmojiListRequest = operations['v2___admin___emoji___list']['requestBody']['content']['application/json'];
export type V2AdminEmojiListResponse = operations['v2___admin___emoji___list']['responses']['200']['content']['application/json'];
+export type VerifyEmailRequest = operations['verify-email']['requestBody']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
index c3c54b3bbb..01b48442d6 100644
--- a/packages/misskey-js/src/autogen/models.ts
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -16,6 +16,7 @@ export type App = components['schemas']['App'];
export type Note = components['schemas']['Note'];
export type NoteDraft = components['schemas']['NoteDraft'];
export type NoteReaction = components['schemas']['NoteReaction'];
+export type NoteReactionWithNote = components['schemas']['NoteReactionWithNote'];
export type NoteFavorite = components['schemas']['NoteFavorite'];
export type Notification = components['schemas']['Notification'];
export type DriveFile = components['schemas']['DriveFile'];
@@ -57,6 +58,7 @@ export type ReversiGameDetailed = components['schemas']['ReversiGameDetailed'];
export type MetaLite = components['schemas']['MetaLite'];
export type MetaDetailedOnly = components['schemas']['MetaDetailedOnly'];
export type MetaDetailed = components['schemas']['MetaDetailed'];
+export type UserWebhook = components['schemas']['UserWebhook'];
export type SystemWebhook = components['schemas']['SystemWebhook'];
export type AbuseReportNotificationRecipient = components['schemas']['AbuseReportNotificationRecipient'];
export type ChatMessage = components['schemas']['ChatMessage'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 7594117deb..d92ef599f3 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -584,6 +584,15 @@ export type paths = {
*/
post: operations['admin___queue___show-job'];
};
+ '/admin/queue/show-job-logs': {
+ /**
+ * admin/queue/show-job-logs
+ * @description No description provided.
+ *
+ * **Credential required**: *Yes* / **Permission**: *read:admin:queue*
+ */
+ post: operations['admin___queue___show-job-logs'];
+ };
'/admin/queue/stats': {
/**
* admin/queue/stats
@@ -3897,6 +3906,15 @@ export type paths = {
*/
post: operations['v2___admin___emoji___list'];
};
+ '/verify-email': {
+ /**
+ * verify-email
+ * @description No description provided.
+ *
+ * **Credential required**: *No*
+ */
+ post: operations['verify-email'];
+ };
};
export type webhooks = Record<string, never>;
export type components = {
@@ -3931,7 +3949,7 @@ export type components = {
*/
host: string | null;
/** Format: url */
- avatarUrl: string | null;
+ avatarUrl: string;
avatarBlurhash: string | null;
avatarDecorations: {
/** Format: id */
@@ -4040,8 +4058,8 @@ export type components = {
/** Format: id */
bannerId: string | null;
followedMessage: string | null;
- isModerator: boolean | null;
- isAdmin: boolean | null;
+ isModerator: boolean;
+ isAdmin: boolean;
injectFeaturedNote: boolean;
receiveAnnouncementEmail: boolean;
alwaysMarkNsfw: boolean;
@@ -4067,7 +4085,7 @@ export type components = {
unreadNotificationsCount: number;
mutedWords: string[][];
hardMutedWords: string[][];
- mutedInstances: string[] | null;
+ mutedInstances: string[];
notificationRecieveConfig: {
note?: {
/** @enum {string} */
@@ -4436,16 +4454,22 @@ export type components = {
reactionAcceptance: 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote' | null;
};
NoteReaction: {
- /**
- * Format: id
- * @example xxxxxxxxxx
- */
+ /** Format: id */
id: string;
/** Format: date-time */
createdAt: string;
user: components['schemas']['UserLite'];
type: string;
};
+ NoteReactionWithNote: {
+ /** Format: id */
+ id: string;
+ /** Format: date-time */
+ createdAt: string;
+ user: components['schemas']['UserLite'];
+ type: string;
+ note: components['schemas']['Note'];
+ };
NoteFavorite: {
/**
* Format: id
@@ -5202,6 +5226,7 @@ export type components = {
canManageCustomEmojis: boolean;
canManageAvatarDecorations: boolean;
canSearchNotes: boolean;
+ canSearchUsers: boolean;
canUseTranslator: boolean;
canHideAds: boolean;
driveCapacityMb: number;
@@ -5320,6 +5345,7 @@ export type components = {
feedbackUrl: string | null;
defaultDarkTheme: string | null;
defaultLightTheme: string | null;
+ clientOptions: Record<string, never>;
disableRegistration: boolean;
emailRequiredForSignup: boolean;
enableHcaptcha: boolean;
@@ -5417,6 +5443,20 @@ export type components = {
cacheRemoteSensitiveFiles: boolean;
};
MetaDetailed: components['schemas']['MetaLite'] & components['schemas']['MetaDetailedOnly'];
+ UserWebhook: {
+ /** Format: id */
+ id: string;
+ /** Format: id */
+ userId: string;
+ name: string;
+ on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[];
+ url: string;
+ secret: string;
+ active: boolean;
+ /** Format: date-time */
+ latestSentAt: string | null;
+ latestStatus: number | null;
+ };
SystemWebhook: {
id: string;
isActive: boolean;
@@ -6674,6 +6714,13 @@ export interface operations {
updatedAt: string | null;
text: string;
title: string;
+ icon: string | null;
+ display: string;
+ isActive: boolean;
+ forExistingUsers: boolean;
+ silence: boolean;
+ needConfirmationToRead: boolean;
+ userId: string | null;
imageUrl: string | null;
reads: number;
}[];
@@ -7635,6 +7682,10 @@ export interface operations {
folderId: string | null;
isSensitive: boolean;
isLink: boolean;
+ maybeSensitive: boolean;
+ maybePorn: boolean;
+ requestIp: string | null;
+ requestHeaders: Record<string, never> | null;
};
};
};
@@ -9278,8 +9329,10 @@ export interface operations {
mcaptchaSecretKey: string | null;
recaptchaSecretKey: string | null;
turnstileSecretKey: string | null;
- sensitiveMediaDetection: string;
- sensitiveMediaDetectionSensitivity: string;
+ /** @enum {string} */
+ sensitiveMediaDetection: 'none' | 'all' | 'local' | 'remote';
+ /** @enum {string} */
+ sensitiveMediaDetectionSensitivity: 'medium' | 'low' | 'high' | 'veryLow' | 'veryHigh';
setSensitiveFlagAutomatically: boolean;
enableSensitiveMediaDetectionForVideos: boolean;
/** Format: id */
@@ -9330,6 +9383,7 @@ export interface operations {
deeplIsPro: boolean;
defaultDarkTheme: string | null;
defaultLightTheme: string | null;
+ clientOptions: Record<string, never>;
description: string | null;
disableRegistration: boolean;
impressumUrl: string | null;
@@ -9341,6 +9395,7 @@ export interface operations {
privacyPolicyUrl: string | null;
inquiryUrl: string | null;
repositoryUrl: string | null;
+ feedbackUrl: string | null;
/**
* @deprecated
* @description [Deprecated] Use "urlPreviewSummaryProxyUrl" instead.
@@ -9370,6 +9425,9 @@ export interface operations {
proxyRemoteFiles: boolean;
signToActivityPubGet: boolean;
allowExternalApRedirect: boolean;
+ enableRemoteNotesCleaning: boolean;
+ remoteNotesCleaningExpiryDaysForEachNotes: number;
+ remoteNotesCleaningMaxProcessingDurationInMinutes: number;
};
};
};
@@ -10164,6 +10222,73 @@ export interface operations {
};
};
};
+ 'admin___queue___show-job-logs': {
+ requestBody: {
+ content: {
+ 'application/json': {
+ /** @enum {string} */
+ queue: 'system' | 'endedPollNotification' | 'deliver' | 'inbox' | 'db' | 'relationship' | 'objectStorage' | 'userWebhookDeliver' | 'systemWebhookDeliver';
+ jobId: string;
+ };
+ };
+ };
+ responses: {
+ /** @description OK (with results) */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': string[];
+ };
+ };
+ /** @description Client error */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Authentication error */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Forbidden error */
+ 403: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description I'm Ai */
+ 418: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Internal server error */
+ 500: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ };
+ };
admin___queue___stats: {
responses: {
/** @description OK (with results) */
@@ -11808,7 +11933,8 @@ export interface operations {
name: string;
on: ('abuseReport' | 'abuseReportResolved' | 'userCreated' | 'inactiveModeratorsWarning' | 'inactiveModeratorsInvitationOnlyChanged')[];
url: string;
- secret: string;
+ /** @default */
+ secret?: string;
};
};
};
@@ -12152,7 +12278,8 @@ export interface operations {
name: string;
on: ('abuseReport' | 'abuseReportResolved' | 'userCreated' | 'inactiveModeratorsWarning' | 'inactiveModeratorsInvitationOnlyChanged')[];
url: string;
- secret: string;
+ /** @default */
+ secret?: string;
};
};
};
@@ -12493,6 +12620,7 @@ export interface operations {
description?: string | null;
defaultLightTheme?: string | null;
defaultDarkTheme?: string | null;
+ clientOptions?: Record<string, never>;
cacheRemoteFiles?: boolean;
cacheRemoteSensitiveFiles?: boolean;
emailRequiredForSignup?: boolean;
@@ -12599,6 +12727,9 @@ export interface operations {
proxyRemoteFiles?: boolean;
signToActivityPubGet?: boolean;
allowExternalApRedirect?: boolean;
+ enableRemoteNotesCleaning?: boolean;
+ remoteNotesCleaningExpiryDaysForEachNotes?: number;
+ remoteNotesCleaningMaxProcessingDurationInMinutes?: number;
};
};
};
@@ -18166,6 +18297,20 @@ export interface operations {
};
};
clips___list: {
+ requestBody: {
+ content: {
+ 'application/json': {
+ /** @default 10 */
+ limit?: number;
+ /** Format: misskey:id */
+ sinceId?: string;
+ /** Format: misskey:id */
+ untilId?: string;
+ sinceDate?: number;
+ untilDate?: number;
+ };
+ };
+ };
responses: {
/** @description OK (with results) */
200: {
@@ -24290,6 +24435,8 @@ export interface operations {
/** Format: date-time */
lastUsedAt?: string;
permission: string[];
+ iconUrl?: string | null;
+ description?: string | null;
}[];
};
};
@@ -27525,20 +27672,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- 'application/json': {
- /** Format: misskey:id */
- id: string;
- /** Format: misskey:id */
- userId: string;
- name: string;
- on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[];
- url: string;
- secret: string;
- active: boolean;
- /** Format: date-time */
- latestSentAt: string | null;
- latestStatus: number | null;
- }[];
+ 'application/json': components['schemas']['UserWebhook'][];
};
};
/** @description Client error */
@@ -27604,20 +27738,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- 'application/json': {
- /** Format: misskey:id */
- id: string;
- /** Format: misskey:id */
- userId: string;
- name: string;
- on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[];
- url: string;
- secret: string;
- active: boolean;
- /** Format: date-time */
- latestSentAt: string | null;
- latestStatus: number | null;
- };
+ 'application/json': components['schemas']['UserWebhook'];
};
};
/** @description Client error */
@@ -35641,7 +35762,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- 'application/json': components['schemas']['NoteReaction'][];
+ 'application/json': components['schemas']['NoteReactionWithNote'][];
};
};
/** @description Client error */
@@ -36299,5 +36420,67 @@ export interface operations {
};
};
};
+ 'verify-email': {
+ requestBody: {
+ content: {
+ 'application/json': {
+ code: string;
+ };
+ };
+ };
+ responses: {
+ /** @description OK (without any results) */
+ 204: {
+ headers: {
+ [name: string]: unknown;
+ };
+ };
+ /** @description Client error */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Authentication error */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Forbidden error */
+ 403: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description I'm Ai */
+ 418: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Internal server error */
+ 500: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ };
+ };
}
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index a868f32f28..5cc725d3dd 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -167,6 +167,7 @@ export const moderationLogTypes = [
'deleteFlash',
'deleteGalleryPost',
'deleteChatRoom',
+ 'updateProxyAccountDescription',
] as const;
export const queueTypes = [
@@ -193,7 +194,15 @@ export const reversiUpdateKeys = [
export type ReversiUpdateKey = typeof reversiUpdateKeys[number];
-type AvatarDecoration = UserLite['avatarDecorations'][number];
+type AvatarDecoration = {
+ id: string;
+ name: string;
+ url: string;
+ angle?: number;
+ flipH?: boolean;
+ offsetX?: number;
+ offsetY?: number;
+};
type ReceivedAbuseReport = {
reportId: AbuseReportNotificationRecipient['id'];
@@ -455,4 +464,8 @@ export type ModerationLogPayloads = {
roomId: string;
room: ChatRoom;
};
+ updateProxyAccountDescription: {
+ before: string | null;
+ after: string | null;
+ }
};
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 9bc9aa6668..88b806ed07 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -33,7 +33,8 @@ export type PureRenote =
& AllNullRecord<Pick<Note, 'text'>>
& AllNullOrOptionalRecord<Pick<Note, 'reply' | 'replyId' | 'cw' | 'poll'>>
& { files: []; fileIds: []; }
- & NonNullableRecord<Pick<Note, 'renote' | 'renoteId'>>;
+ & NonNullableRecord<Pick<Note, 'renoteId'>>
+ & Pick<Note, 'renote'>; // リノート対象ãŒå‰Šé™¤ã•れãŸå ´åˆã€renoteIdã¯ã‚ã‚‹ãŒrenoteã¯nullã«ãªã‚‹
export type PageEvent = {
pageId: Page['id'];
@@ -48,7 +49,7 @@ export type ModerationLog = {
id: ID;
createdAt: DateString;
userId: User['id'];
- user: UserDetailedNotMe | null;
+ user: UserDetailedNotMe;
} & ({
type: 'updateServerSettings';
info: ModerationLogPayloads['updateServerSettings'];
@@ -202,6 +203,9 @@ export type ModerationLog = {
} | {
type: 'deleteChatRoom';
info: ModerationLogPayloads['deleteChatRoom'];
+} | {
+ type: 'updateProxyAccountDescription';
+ info: ModerationLogPayloads['updateProxyAccountDescription'];
});
export type ServerStats = {
diff --git a/packages/misskey-js/src/note.ts b/packages/misskey-js/src/note.ts
index 5c8298c7e4..c1c0f38a71 100644
--- a/packages/misskey-js/src/note.ts
+++ b/packages/misskey-js/src/note.ts
@@ -2,8 +2,8 @@ import type { Note, PureRenote } from './entities.js';
export function isPureRenote(note: Note): note is PureRenote {
return (
- note.renote != null &&
- note.reply == null &&
+ note.renoteId != null &&
+ note.replyId == null &&
note.text == null &&
note.cw == null &&
(note.fileIds == null || note.fileIds.length === 0) &&
diff --git a/packages/misskey-js/test-d/api.ts b/packages/misskey-js/test-d/api.ts
index ca6d8dcb88..e71a3e2b8b 100644
--- a/packages/misskey-js/test-d/api.ts
+++ b/packages/misskey-js/test-d/api.ts
@@ -1,3 +1,4 @@
+import { describe, test } from 'vitest';
import { expectType } from 'tsd';
import * as Misskey from '../src/index.js';
diff --git a/packages/misskey-js/test-d/streaming.ts b/packages/misskey-js/test-d/streaming.ts
index b46b06e4df..7ce737261d 100644
--- a/packages/misskey-js/test-d/streaming.ts
+++ b/packages/misskey-js/test-d/streaming.ts
@@ -1,3 +1,4 @@
+import { describe, test } from 'vitest';
import { expectType } from 'tsd';
import * as Misskey from '../src/index.js';
diff --git a/packages/misskey-js/test/api.ts b/packages/misskey-js/test/api.ts
index b8ad2225c8..c0f3a47f77 100644
--- a/packages/misskey-js/test/api.ts
+++ b/packages/misskey-js/test/api.ts
@@ -1,41 +1,24 @@
-import { enableFetchMocks } from 'jest-fetch-mock';
+import { vi, describe, test, expect } from 'vitest';
import { APIClient, isAPIError } from '../src/api.js';
-enableFetchMocks();
-
-function getFetchCall(call: any[]) {
- const { body, method } = call[1];
- const contentType = call[1].headers['Content-Type'];
- if (
- body == null ||
- (contentType === 'application/json' && typeof body !== 'string') ||
- (contentType === 'multipart/form-data' && !(body instanceof FormData))
- ) {
- throw new Error('invalid body');
- }
- return {
- url: call[0],
- method: method,
- contentType: contentType,
- body: body instanceof FormData ? Object.fromEntries(body.entries()) : JSON.parse(body),
- };
-}
-
describe('API', () => {
test('success', async () => {
- fetchMock.resetMocks();
- fetchMock.mockResponse(async (req) => {
- const body = await req.json();
- if (req.method == 'POST' && req.url == 'https://misskey.test/api/i') {
- if (body.i === 'TOKEN') {
- return JSON.stringify({ id: 'foo' });
- } else {
- return { status: 400 };
+ const fetchMock = vi
+ .spyOn(globalThis, 'fetch')
+ .mockImplementation(async (url, options) => {
+ if (url === 'https://misskey.test/api/i' && options?.method === 'POST') {
+ if (options.body) {
+ const body = JSON.parse(options.body as string);
+ if (body.i === 'TOKEN') {
+ return new Response(JSON.stringify({ id: 'foo' }), { status: 200 });
+ }
+ }
+
+ return new Response(null, { status: 400 });
}
- } else {
- return { status: 404 };
- }
- });
+
+ return new Response(null, { status: 404 });
+ });
const cli = new APIClient({
origin: 'https://misskey.test',
@@ -48,28 +31,38 @@ describe('API', () => {
id: 'foo'
});
- expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({
- url: 'https://misskey.test/api/i',
+ fetch('https://misskey.test/api/i', {
+ method: 'POST',
+ })
+
+ expect(fetchMock).toHaveBeenCalledWith('https://misskey.test/api/i', {
method: 'POST',
- contentType: 'application/json',
- body: { i: 'TOKEN' }
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ credentials: 'omit',
+ cache: 'no-cache',
+ body: JSON.stringify({ i: 'TOKEN' }),
});
+
+ fetchMock.mockRestore();
});
test('with params', async () => {
- fetchMock.resetMocks();
- fetchMock.mockResponse(async (req) => {
- const body = await req.json();
- if (req.method == 'POST' && req.url == 'https://misskey.test/api/notes/show') {
- if (body.i === 'TOKEN' && body.noteId === 'aaaaa') {
- return JSON.stringify({ id: 'foo' });
- } else {
- return { status: 400 };
+ const fetchMock = vi
+ .spyOn(globalThis, 'fetch')
+ .mockImplementation(async (url, options) => {
+ if (url === 'https://misskey.test/api/notes/show' && options?.method === 'POST') {
+ if (options.body) {
+ const body = JSON.parse(options.body as string);
+ if (body.i === 'TOKEN' && body.noteId === 'aaaaa') {
+ return new Response(JSON.stringify({ id: 'foo' }), { status: 200 });
+ }
+ }
+ return new Response(null, { status: 400 });
}
- } else {
- return { status: 404 };
- }
- });
+ return new Response(null, { status: 404 });
+ });
const cli = new APIClient({
origin: 'https://misskey.test',
@@ -82,23 +75,34 @@ describe('API', () => {
id: 'foo'
});
- expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({
- url: 'https://misskey.test/api/notes/show',
+ expect(fetchMock).toHaveBeenCalledWith('https://misskey.test/api/notes/show', {
method: 'POST',
- contentType: 'application/json',
- body: { i: 'TOKEN', noteId: 'aaaaa' }
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ credentials: 'omit',
+ cache: 'no-cache',
+ body: JSON.stringify({ noteId: 'aaaaa', i: 'TOKEN' }),
});
+
+ fetchMock.mockRestore();
});
test('multipart/form-data', async () => {
- fetchMock.resetMocks();
- fetchMock.mockResponse(async (req) => {
- if (req.method == 'POST' && req.url == 'https://misskey.test/api/drive/files/create') {
- return JSON.stringify({ id: 'foo' });
- } else {
- return { status: 404 };
- }
- });
+ const fetchMock = vi
+ .spyOn(globalThis, 'fetch')
+ .mockImplementation(async (url, options) => {
+ if (url === 'https://misskey.test/api/drive/files/create' && options?.method === 'POST') {
+ if (options.body instanceof FormData) {
+ const file = options.body.get('file');
+ if (file instanceof File && file.name === 'foo.txt') {
+ return new Response(JSON.stringify({ id: 'foo' }), { status: 200 });
+ }
+ }
+ return new Response(null, { status: 400 });
+ }
+ return new Response(null, { status: 404 });
+ });
const cli = new APIClient({
origin: 'https://misskey.test',
@@ -116,26 +120,26 @@ describe('API', () => {
id: 'foo'
});
- expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({
- url: 'https://misskey.test/api/drive/files/create',
+ expect(fetchMock).toHaveBeenCalledWith('https://misskey.test/api/drive/files/create', {
method: 'POST',
- contentType: undefined,
- body: {
- i: 'TOKEN',
- file: testFile,
- }
+ body: expect.any(FormData),
+ headers: {},
+ credentials: 'omit',
+ cache: 'no-cache',
});
+
+ fetchMock.mockRestore();
});
test('204 No Content ã§ null ãŒè¿”ã‚‹', async () => {
- fetchMock.resetMocks();
- fetchMock.mockResponse(async (req) => {
- if (req.method == 'POST' && req.url == 'https://misskey.test/api/reset-password') {
- return { status: 204 };
- } else {
- return { status: 404 };
- }
- });
+ const fetchMock = vi
+ .spyOn(globalThis, 'fetch')
+ .mockImplementation(async (url, options) => {
+ if (url === 'https://misskey.test/api/reset-password' && options?.method === 'POST') {
+ return new Response(null, { status: 204 });
+ }
+ return new Response(null, { status: 404 });
+ });
const cli = new APIClient({
origin: 'https://misskey.test',
@@ -146,37 +150,42 @@ describe('API', () => {
expect(res).toEqual(null);
- expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({
- url: 'https://misskey.test/api/reset-password',
+ expect(fetchMock).toHaveBeenCalledWith('https://misskey.test/api/reset-password', {
method: 'POST',
- contentType: 'application/json',
- body: { i: 'TOKEN', token: 'aaa', password: 'aaa' }
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ credentials: 'omit',
+ cache: 'no-cache',
+ body: JSON.stringify({ token: 'aaa', password: 'aaa', i: 'TOKEN' }),
});
+
+ fetchMock.mockRestore();
});
test('インスタンス㮠credential ãŒæŒ‡å®šã•れã¦ã„ã¦ã‚‚引数㧠credential ㌠null ãªã‚‰ã° null ã¨ã—ã¦ãƒªã‚¯ã‚¨ã‚¹ãƒˆã•れる', async () => {
- fetchMock.resetMocks();
- fetchMock.mockResponse(async (req) => {
- const body = await req.json();
- if (req.method == 'POST' && req.url == 'https://misskey.test/api/i') {
- if (typeof body.i === 'string') {
- return JSON.stringify({ id: 'foo' });
- } else {
- return {
- status: 401,
- body: JSON.stringify({
- error: {
- message: 'Credential required.',
- code: 'CREDENTIAL_REQUIRED',
- id: '1384574d-a912-4b81-8601-c7b1c4085df1',
- }
- })
- };
+ const fetchMock = vi
+ .spyOn(globalThis, 'fetch')
+ .mockImplementation(async (url, options) => {
+ if (url === 'https://misskey.test/api/i' && options?.method === 'POST') {
+ if (options.body) {
+ const body = JSON.parse(options.body as string);
+ if (typeof body.i === 'string') {
+ return new Response(JSON.stringify({ id: 'foo' }), { status: 200 });
+ } else {
+ return new Response(JSON.stringify({
+ error: {
+ message: 'Credential required.',
+ code: 'CREDENTIAL_REQUIRED',
+ id: '1384574d-a912-4b81-8601-c7b1c4085df1',
+ }
+ }), { status: 401 });
+ }
+ }
+ return new Response(null, { status: 400 });
}
- } else {
- return { status: 404 };
- }
- });
+ return new Response(null, { status: 404 });
+ });
try {
const cli = new APIClient({
@@ -187,24 +196,24 @@ describe('API', () => {
await cli.request('i', {}, null);
} catch (e) {
expect(isAPIError(e)).toEqual(true);
+ } finally {
+ fetchMock.mockRestore();
}
});
test('api error', async () => {
- fetchMock.resetMocks();
- fetchMock.mockResponse(async (req) => {
- return {
- status: 500,
- body: JSON.stringify({
+ const fetchMock = vi
+ .spyOn(globalThis, 'fetch')
+ .mockImplementation(async () => {
+ return new Response(JSON.stringify({
error: {
message: 'Internal error occurred. Please contact us if the error persists.',
code: 'INTERNAL_ERROR',
id: '5d37dbcb-891e-41ca-a3d6-e690c97775ac',
kind: 'server',
},
- })
- };
- });
+ }), { status: 500 });
+ });
try {
const cli = new APIClient({
@@ -216,12 +225,17 @@ describe('API', () => {
} catch (e: any) {
expect(isAPIError(e)).toEqual(true);
expect(e.id).toEqual('5d37dbcb-891e-41ca-a3d6-e690c97775ac');
+ } finally {
+ fetchMock.mockRestore();
}
});
test('network error', async () => {
- fetchMock.resetMocks();
- fetchMock.mockAbort();
+ const fetchMock = vi
+ .spyOn(globalThis, 'fetch')
+ .mockImplementation(async () => {
+ throw new Error('Network error');
+ });
try {
const cli = new APIClient({
@@ -232,17 +246,17 @@ describe('API', () => {
await cli.request('i');
} catch (e) {
expect(isAPIError(e)).toEqual(false);
+ } finally {
+ fetchMock.mockRestore();
}
});
test('json parse error', async () => {
- fetchMock.resetMocks();
- fetchMock.mockResponse(async (req) => {
- return {
- status: 500,
- body: '<html>I AM NOT JSON</html>'
- };
- });
+ const fetchMock = vi
+ .spyOn(globalThis, 'fetch')
+ .mockImplementation(async () => {
+ return new Response('<html>I AM NOT JSON</html>', { status: 500 });
+ });
try {
const cli = new APIClient({
@@ -253,18 +267,18 @@ describe('API', () => {
await cli.request('i');
} catch (e) {
expect(isAPIError(e)).toEqual(false);
+ } finally {
+ fetchMock.mockRestore();
}
});
test('admin/roles/create ã®åž‹ãŒåˆã†', async() => {
- fetchMock.resetMocks();
- fetchMock.mockResponse(async () => {
- return {
+ const fetchMock = vi
+ .spyOn(globalThis, 'fetch')
+ .mockImplementation(async () => {
// 本æ¥è¿”ã™ã¹ã値ã¯`Role`åž‹ã ãŒã€ãƒ†ã‚¹ãƒˆãªã®ã§ãŠèŒ¶ã‚’æ¿ã™
- status: 200,
- body: '{}'
- };
- });
+ return new Response('{}', { status: 200 });
+ });
const cli = new APIClient({
origin: 'https://misskey.test',
@@ -292,5 +306,7 @@ describe('API', () => {
},
target: 'manual',
});
+
+ fetchMock.mockRestore();
})
});
diff --git a/packages/misskey-js/test/streaming.ts b/packages/misskey-js/test/streaming.ts
index 7e784cd20c..c42cbced88 100644
--- a/packages/misskey-js/test/streaming.ts
+++ b/packages/misskey-js/test/streaming.ts
@@ -1,4 +1,5 @@
-import WS from 'jest-websocket-mock';
+import { describe, test, expect } from 'vitest';
+import WS from 'vitest-websocket-mock';
import Stream from '../src/streaming.js';
describe('Streaming', () => {
diff --git a/packages/misskey-js/vitest.config.ts b/packages/misskey-js/vitest.config.ts
new file mode 100644
index 0000000000..e6b15b621b
--- /dev/null
+++ b/packages/misskey-js/vitest.config.ts
@@ -0,0 +1,17 @@
+import { defineConfig, configDefaults } from 'vitest/config';
+
+export default defineConfig({
+ test: {
+ include: ['test/**/*.ts'],
+ coverage: {
+ exclude: [
+ ...configDefaults.coverage.exclude!,
+ 'src/autogen/**/*',
+ 'generator/**/*',
+ 'built/**/*',
+ 'test-d/**/*',
+ 'build.js',
+ ],
+ }
+ },
+});
diff --git a/packages/misskey-reversi/package.json b/packages/misskey-reversi/package.json
index e7158fa4f4..028f191313 100644
--- a/packages/misskey-reversi/package.json
+++ b/packages/misskey-reversi/package.json
@@ -22,13 +22,13 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
- "@types/node": "22.16.4",
- "@typescript-eslint/eslint-plugin": "8.37.0",
- "@typescript-eslint/parser": "8.37.0",
+ "@types/node": "22.17.2",
+ "@typescript-eslint/eslint-plugin": "8.40.0",
+ "@typescript-eslint/parser": "8.40.0",
"execa": "9.6.0",
"nodemon": "3.1.10",
- "typescript": "5.8.3",
- "esbuild": "0.25.6",
+ "typescript": "5.9.2",
+ "esbuild": "0.25.9",
"glob": "11.0.3"
},
"files": [
diff --git a/packages/sw/package.json b/packages/sw/package.json
index 8ebd8f44ae..f943de0d5a 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -9,16 +9,16 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"dependencies": {
- "esbuild": "0.25.6",
+ "esbuild": "0.25.9",
"idb-keyval": "6.2.2",
"misskey-js": "workspace:*"
},
"devDependencies": {
- "@typescript-eslint/parser": "8.37.0",
+ "@typescript-eslint/parser": "8.40.0",
"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.74",
"eslint-plugin-import": "2.32.0",
"nodemon": "3.1.10",
- "typescript": "5.8.3"
+ "typescript": "5.9.2"
},
"type": "module"
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 08711cac71..db2ba1719d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -19,11 +19,11 @@ importers:
.:
dependencies:
cssnano:
- specifier: 7.1.0
- version: 7.1.0(postcss@8.5.6)
+ specifier: 7.1.1
+ version: 7.1.1(postcss@8.5.6)
esbuild:
- specifier: 0.25.6
- version: 0.25.6
+ specifier: 0.25.9
+ version: 0.25.9
execa:
specifier: 9.6.0
version: 9.6.0
@@ -49,30 +49,30 @@ importers:
specifier: 5.43.1
version: 5.43.1
typescript:
- specifier: 5.8.3
- version: 5.8.3
+ specifier: 5.9.2
+ version: 5.9.2
devDependencies:
'@misskey-dev/eslint-plugin':
specifier: 2.1.0
- version: 2.1.0(@eslint/compat@1.1.1)(@stylistic/eslint-plugin@2.13.0(eslint@9.31.0)(typescript@5.8.3))(@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3))(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0))(eslint@9.31.0)(globals@16.3.0)
+ version: 2.1.0(@eslint/compat@1.1.1)(@stylistic/eslint-plugin@2.13.0(eslint@9.34.0)(typescript@5.9.2))(@typescript-eslint/eslint-plugin@8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2))(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0))(eslint@9.34.0)(globals@16.3.0)
'@types/node':
- specifier: 22.16.4
- version: 22.16.4
+ specifier: 22.17.2
+ version: 22.17.2
'@typescript-eslint/eslint-plugin':
- specifier: 8.37.0
- version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)
'@typescript-eslint/parser':
- specifier: 8.37.0
- version: 8.37.0(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(eslint@9.34.0)(typescript@5.9.2)
cross-env:
specifier: 7.0.3
version: 7.0.3
cypress:
- specifier: 14.5.2
- version: 14.5.2
+ specifier: 14.5.4
+ version: 14.5.4
eslint:
- specifier: 9.31.0
- version: 9.31.0
+ specifier: 9.34.0
+ version: 9.34.0
globals:
specifier: 16.3.0
version: 16.3.0
@@ -80,11 +80,11 @@ importers:
specifier: 2.0.0
version: 2.0.0
pnpm:
- specifier: 10.13.1
- version: 10.13.1
+ specifier: 10.15.0
+ version: 10.15.0
start-server-and-test:
- specifier: 2.0.12
- version: 2.0.12
+ specifier: 2.0.13
+ version: 2.0.13
optionalDependencies:
'@tensorflow/tfjs-core':
specifier: 4.22.0
@@ -93,14 +93,14 @@ importers:
packages/backend:
dependencies:
'@aws-sdk/client-s3':
- specifier: 3.826.0
- version: 3.826.0
+ specifier: 3.873.0
+ version: 3.873.0
'@aws-sdk/lib-storage':
- specifier: 3.826.0
- version: 3.826.0(@aws-sdk/client-s3@3.826.0)
+ specifier: 3.873.0
+ version: 3.873.0(@aws-sdk/client-s3@3.873.0)
'@discordapp/twemoji':
- specifier: 15.1.0
- version: 15.1.0
+ specifier: 16.0.1
+ version: 16.0.1
'@fastify/accepts':
specifier: 5.0.2
version: 5.0.2
@@ -129,20 +129,20 @@ importers:
specifier: 1.2.0
version: 1.2.0
'@misskey-dev/summaly':
- specifier: 5.2.1
- version: 5.2.1
+ specifier: 5.2.3
+ version: 5.2.3
'@napi-rs/canvas':
- specifier: 0.1.71
- version: 0.1.71
+ specifier: 0.1.77
+ version: 0.1.77
'@nestjs/common':
- specifier: 11.1.3
- version: 11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ specifier: 11.1.6
+ version: 11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nestjs/core':
- specifier: 11.1.3
- version: 11.1.3(@nestjs/common@11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.19)(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ specifier: 11.1.6
+ version: 11.1.6(@nestjs/common@11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nestjs/testing':
- specifier: 11.1.3
- version: 11.1.3(@nestjs/common@11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)(@nestjs/platform-express@10.4.19)
+ specifier: 11.1.6
+ version: 11.1.6(@nestjs/common@11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@nestjs/platform-express@10.4.20)
'@peertube/http-signature':
specifier: 1.7.0
version: 1.7.0
@@ -162,14 +162,14 @@ importers:
specifier: 2.5.0
version: 2.5.0
'@swc/cli':
- specifier: 0.7.7
- version: 0.7.7(@swc/core@1.12.0)(chokidar@4.0.3)
+ specifier: 0.7.8
+ version: 0.7.8(@swc/core@1.13.4)(chokidar@4.0.3)
'@swc/core':
- specifier: 1.12.0
- version: 1.12.0
+ specifier: 1.13.4
+ version: 1.13.4
'@twemoji/parser':
- specifier: 15.1.1
- version: 15.1.1
+ specifier: 16.0.0
+ version: 16.0.0
'@types/redis-info':
specifier: 3.0.3
version: 3.0.3
@@ -195,8 +195,8 @@ importers:
specifier: 1.20.3
version: 1.20.3
bullmq:
- specifier: 5.53.2
- version: 5.53.2
+ specifier: 5.58.1
+ version: 5.58.1
cacheable-lookup:
specifier: 7.0.0
version: 7.0.0
@@ -204,8 +204,8 @@ importers:
specifier: 9.0.2
version: 9.0.2
chalk:
- specifier: 5.4.1
- version: 5.4.1
+ specifier: 5.6.0
+ version: 5.6.0
chalk-template:
specifier: 1.1.0
version: 1.1.0
@@ -228,8 +228,8 @@ importers:
specifier: 0.1.21
version: 0.1.21
fastify:
- specifier: 5.3.3
- version: 5.3.3
+ specifier: 5.5.0
+ version: 5.5.0
fastify-raw-body:
specifier: 5.0.0
version: 5.0.0
@@ -243,8 +243,8 @@ importers:
specifier: 2.1.3
version: 2.1.3
form-data:
- specifier: 4.0.3
- version: 4.0.3
+ specifier: 4.0.4
+ version: 4.0.4
got:
specifier: 14.4.7
version: 14.4.7
@@ -261,8 +261,8 @@ importers:
specifier: 1.1.3
version: 1.1.3
ioredis:
- specifier: 5.6.1
- version: 5.6.1
+ specifier: 5.7.0
+ version: 5.7.0
ip-cidr:
specifier: 4.0.2
version: 4.0.2
@@ -291,14 +291,14 @@ importers:
specifier: 11.0.1
version: 11.0.1
meilisearch:
- specifier: 0.51.0
- version: 0.51.0
+ specifier: 0.52.0
+ version: 0.52.0
mfm-js:
- specifier: 0.24.0
- version: 0.24.0
+ specifier: 0.25.0
+ version: 0.25.0
microformats-parser:
- specifier: 2.0.3
- version: 2.0.3
+ specifier: 2.0.4
+ version: 2.0.4
mime-types:
specifier: 2.1.35
version: 2.1.35
@@ -339,14 +339,14 @@ importers:
specifier: 0.0.14
version: 0.0.14
otpauth:
- specifier: 9.4.0
- version: 9.4.0
+ specifier: 9.4.1
+ version: 9.4.1
parse5:
specifier: 7.3.0
version: 7.3.0
pg:
- specifier: 8.16.0
- version: 8.16.0
+ specifier: 8.16.3
+ version: 8.16.3
pkce-challenge:
specifier: 4.1.0
version: 4.1.0
@@ -411,14 +411,14 @@ importers:
specifier: 2.1.0
version: 2.1.0
systeminformation:
- specifier: 5.27.1
- version: 5.27.1
+ specifier: 5.27.7
+ version: 5.27.7
tinycolor2:
specifier: 1.6.0
version: 1.6.0
tmp:
- specifier: 0.2.3
- version: 0.2.3
+ specifier: 0.2.5
+ version: 0.2.5
tsc-alias:
specifier: 1.8.16
version: 1.8.16
@@ -426,11 +426,11 @@ importers:
specifier: 4.2.0
version: 4.2.0
typeorm:
- specifier: 0.3.24
- version: 0.3.24(patch_hash=2677b97a423e157945c154e64183d3ae2eb44dfa9cb0e5ce731a7612f507bb56)(ioredis@5.6.1)(pg@8.16.0)(reflect-metadata@0.2.2)
+ specifier: 0.3.26
+ version: 0.3.26(patch_hash=2677b97a423e157945c154e64183d3ae2eb44dfa9cb0e5ce731a7612f507bb56)(ioredis@5.7.0)(pg@8.16.3)(reflect-metadata@0.2.2)
typescript:
- specifier: 5.8.3
- version: 5.8.3
+ specifier: 5.9.2
+ version: 5.9.2
ulid:
specifier: 2.4.0
version: 2.4.0
@@ -441,8 +441,8 @@ importers:
specifier: 3.6.7
version: 3.6.7
ws:
- specifier: 8.18.2
- version: 8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)
+ specifier: 8.18.3
+ version: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)
xev:
specifier: 3.0.2
version: 3.0.2
@@ -451,17 +451,17 @@ importers:
specifier: 29.7.0
version: 29.7.0
'@nestjs/platform-express':
- specifier: 10.4.19
- version: 10.4.19(@nestjs/common@11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)
+ specifier: 10.4.20
+ version: 10.4.20(@nestjs/common@11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)
'@sentry/vue':
- specifier: 9.28.0
- version: 9.28.0(vue@3.5.17(typescript@5.8.3))
+ specifier: 9.46.0
+ version: 9.46.0(vue@3.5.19(typescript@5.9.2))
'@simplewebauthn/types':
specifier: 12.0.0
version: 12.0.0
'@swc/jest':
- specifier: 0.2.38
- version: 0.2.38(@swc/core@1.12.0)
+ specifier: 0.2.39
+ version: 0.2.39(@swc/core@1.13.4)
'@types/accepts':
specifier: 1.3.7
version: 1.3.7
@@ -511,11 +511,11 @@ importers:
specifier: 0.7.34
version: 0.7.34
'@types/node':
- specifier: 22.15.31
- version: 22.15.31
+ specifier: 22.17.2
+ version: 22.17.2
'@types/nodemailer':
- specifier: 6.4.17
- version: 6.4.17
+ specifier: 6.4.19
+ version: 6.4.19
'@types/oauth':
specifier: 0.9.6
version: 0.9.6
@@ -526,8 +526,8 @@ importers:
specifier: 0.1.2
version: 0.1.2
'@types/pg':
- specifier: 8.15.4
- version: 8.15.4
+ specifier: 8.15.5
+ version: 8.15.5
'@types/pug':
specifier: 2.0.10
version: 2.0.10
@@ -574,11 +574,11 @@ importers:
specifier: 8.18.1
version: 8.18.1
'@typescript-eslint/eslint-plugin':
- specifier: 8.34.0
- version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)
'@typescript-eslint/parser':
- specifier: 8.34.0
- version: 8.34.0(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(eslint@9.34.0)(typescript@5.9.2)
aws-sdk-client-mock:
specifier: 4.1.0
version: 4.1.0
@@ -586,8 +586,8 @@ importers:
specifier: 7.0.3
version: 7.0.3
eslint-plugin-import:
- specifier: 2.31.0
- version: 2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)
+ specifier: 2.32.0
+ version: 2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)
execa:
specifier: 8.0.1
version: 8.0.1
@@ -596,7 +596,7 @@ importers:
version: 9.0.0
jest:
specifier: 29.7.0
- version: 29.7.0(@types/node@22.15.31)
+ version: 29.7.0(@types/node@22.17.2)
jest-mock:
specifier: 29.7.0
version: 29.7.0
@@ -610,45 +610,45 @@ importers:
specifier: 5.1.0
version: 5.1.0
supertest:
- specifier: 7.1.1
- version: 7.1.1
+ specifier: 7.1.4
+ version: 7.1.4
optionalDependencies:
'@swc/core-android-arm64':
specifier: 1.3.11
version: 1.3.11
'@swc/core-darwin-arm64':
- specifier: 1.12.0
- version: 1.12.0
+ specifier: 1.13.4
+ version: 1.13.4
'@swc/core-darwin-x64':
- specifier: 1.12.0
- version: 1.12.0
+ specifier: 1.13.4
+ version: 1.13.4
'@swc/core-freebsd-x64':
specifier: 1.3.11
version: 1.3.11
'@swc/core-linux-arm-gnueabihf':
- specifier: 1.12.0
- version: 1.12.0
+ specifier: 1.13.4
+ version: 1.13.4
'@swc/core-linux-arm64-gnu':
- specifier: 1.12.0
- version: 1.12.0
+ specifier: 1.13.4
+ version: 1.13.4
'@swc/core-linux-arm64-musl':
- specifier: 1.12.0
- version: 1.12.0
+ specifier: 1.13.4
+ version: 1.13.4
'@swc/core-linux-x64-gnu':
- specifier: 1.12.0
- version: 1.12.0
+ specifier: 1.13.4
+ version: 1.13.4
'@swc/core-linux-x64-musl':
- specifier: 1.12.0
- version: 1.12.0
+ specifier: 1.13.4
+ version: 1.13.4
'@swc/core-win32-arm64-msvc':
- specifier: 1.12.0
- version: 1.12.0
+ specifier: 1.13.4
+ version: 1.13.4
'@swc/core-win32-ia32-msvc':
- specifier: 1.12.0
- version: 1.12.0
+ specifier: 1.13.4
+ version: 1.13.4
'@swc/core-win32-x64-msvc':
- specifier: 1.12.0
- version: 1.12.0
+ specifier: 1.13.4
+ version: 1.13.4
'@tensorflow/tfjs':
specifier: 4.22.0
version: 4.22.0(encoding@0.1.13)(seedrandom@3.0.5)
@@ -707,8 +707,8 @@ importers:
specifier: 1.1.0
version: 1.1.0
'@discordapp/twemoji':
- specifier: 15.1.0
- version: 15.1.0
+ specifier: 16.0.1
+ version: 16.0.1
'@github/webauthn-json':
specifier: 2.1.1
version: 2.1.1
@@ -720,34 +720,37 @@ importers:
version: 2024.1.0
'@rollup/plugin-json':
specifier: 6.1.0
- version: 6.1.0(rollup@4.45.1)
+ version: 6.1.0(rollup@4.48.0)
'@rollup/plugin-replace':
specifier: 6.0.2
- version: 6.0.2(rollup@4.45.1)
+ version: 6.0.2(rollup@4.48.0)
'@rollup/pluginutils':
specifier: 5.2.0
- version: 5.2.0(rollup@4.45.1)
+ version: 5.2.0(rollup@4.48.0)
'@sentry/vue':
- specifier: 9.39.0
- version: 9.39.0(vue@3.5.17(typescript@5.8.3))
+ specifier: 10.5.0
+ version: 10.5.0(vue@3.5.19(typescript@5.9.2))
'@syuilo/aiscript':
- specifier: 0.19.0
- version: 0.19.0
+ specifier: 1.1.0
+ version: 1.1.0
+ '@syuilo/aiscript-0-19-0':
+ specifier: npm:@syuilo/aiscript@^0.19.0
+ version: '@syuilo/aiscript@0.19.0'
'@twemoji/parser':
- specifier: 15.1.1
- version: 15.1.1
+ specifier: 16.0.0
+ version: 16.0.0
'@vitejs/plugin-vue':
- specifier: 5.2.4
- version: 5.2.4(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.17(typescript@5.8.3))
+ specifier: 6.0.1
+ version: 6.0.1(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))(vue@3.5.19(typescript@5.9.2))
'@vue/compiler-sfc':
- specifier: 3.5.17
- version: 3.5.17
+ specifier: 3.5.19
+ version: 3.5.19
aiscript-vscode:
specifier: github:aiscript-dev/aiscript-vscode#v0.1.15
version: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/c3cde89e79a41d93540cf8a48cd619c3f2dcb1b7
analytics:
- specifier: 0.8.16
- version: 0.8.16(@types/dlv@1.1.5)
+ specifier: 0.8.19
+ version: 0.8.19(@types/dlv@1.1.5)
astring:
specifier: 1.9.0
version: 1.9.0
@@ -767,8 +770,8 @@ importers:
specifier: 3.0.0
version: 3.0.0(chart.js@4.5.0)(date-fns@4.1.0)
chartjs-chart-matrix:
- specifier: 2.1.1
- version: 2.1.1(chart.js@4.5.0)
+ specifier: 3.0.0
+ version: 3.0.0(chart.js@4.5.0)
chartjs-plugin-gradient:
specifier: 0.6.1
version: 0.6.1(chart.js@4.5.0)
@@ -776,14 +779,14 @@ importers:
specifier: 2.2.0
version: 2.2.0(chart.js@4.5.0)
chromatic:
- specifier: 11.29.0
- version: 11.29.0
+ specifier: 13.1.3
+ version: 13.1.3
compare-versions:
specifier: 6.1.1
version: 6.1.1
cropperjs:
- specifier: 2.0.0
- version: 2.0.0
+ specifier: 2.0.1
+ version: 2.0.1
date-fns:
specifier: 4.1.0
version: 4.1.0
@@ -793,6 +796,9 @@ importers:
eventemitter3:
specifier: 5.0.1
version: 5.0.1
+ execa:
+ specifier: 9.6.0
+ version: 9.6.0
frontend-shared:
specifier: workspace:*
version: link:../frontend-shared
@@ -805,6 +811,9 @@ importers:
insert-text-at-cursor:
specifier: 0.3.0
version: 0.3.0
+ ios-haptics:
+ specifier: 0.1.0
+ version: 0.1.0
is-file-animated:
specifier: 1.0.2
version: 1.0.2
@@ -812,14 +821,14 @@ importers:
specifier: 2.2.3
version: 2.2.3
magic-string:
- specifier: 0.30.17
- version: 0.30.17
+ specifier: 0.30.18
+ version: 0.30.18
matter-js:
specifier: 0.20.0
version: 0.20.0
mfm-js:
- specifier: 0.24.0
- version: 0.24.0
+ specifier: 0.25.0
+ version: 0.25.0
misskey-bubble-game:
specifier: workspace:*
version: link:../misskey-bubble-game
@@ -836,17 +845,17 @@ importers:
specifier: 2.3.1
version: 2.3.1
rollup:
- specifier: 4.45.1
- version: 4.45.1
+ specifier: 4.48.0
+ version: 4.48.0
sanitize-html:
specifier: 2.17.0
version: 2.17.0
sass:
- specifier: 1.89.2
- version: 1.89.2
+ specifier: 1.90.0
+ version: 1.90.0
shiki:
- specifier: 3.8.0
- version: 3.8.0
+ specifier: 3.11.0
+ version: 3.11.0
strict-event-emitter-types:
specifier: 2.0.0
version: 2.0.0
@@ -854,8 +863,8 @@ importers:
specifier: 3.1.0
version: 3.1.0
three:
- specifier: 0.178.0
- version: 0.178.0
+ specifier: 0.179.1
+ version: 0.179.1
throttle-debounce:
specifier: 5.0.2
version: 5.0.2
@@ -869,87 +878,84 @@ importers:
specifier: 4.2.0
version: 4.2.0
typescript:
- specifier: 5.8.3
- version: 5.8.3
+ specifier: 5.9.2
+ version: 5.9.2
v-code-diff:
specifier: 1.13.1
- version: 1.13.1(vue@3.5.17(typescript@5.8.3))
+ version: 1.13.1(vue@3.5.19(typescript@5.9.2))
vite:
- specifier: 6.3.5
- version: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
+ specifier: 7.1.3
+ version: 7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)
vue:
- specifier: 3.5.17
- version: 3.5.17(typescript@5.8.3)
+ specifier: 3.5.19
+ version: 3.5.19(typescript@5.9.2)
vuedraggable:
specifier: next
- version: 4.1.0(vue@3.5.17(typescript@5.8.3))
+ version: 4.1.0(vue@3.5.19(typescript@5.9.2))
wanakana:
specifier: 5.3.1
version: 5.3.1
devDependencies:
'@misskey-dev/summaly':
- specifier: 5.2.2
- version: 5.2.2
- '@storybook/addon-actions':
- specifier: 8.6.14
- version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ specifier: 5.2.3
+ version: 5.2.3
'@storybook/addon-essentials':
specifier: 8.6.14
- version: 8.6.14(@types/react@18.0.28)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ version: 8.6.14(@types/react@18.0.28)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
'@storybook/addon-interactions':
specifier: 8.6.14
- version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ version: 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
'@storybook/addon-links':
- specifier: 8.6.14
- version: 8.6.14(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ specifier: 9.1.3
+ version: 9.1.3(react@19.1.1)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
'@storybook/addon-mdx-gfm':
specifier: 8.6.14
- version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ version: 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
'@storybook/addon-storysource':
specifier: 8.6.14
- version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ version: 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
'@storybook/blocks':
specifier: 8.6.14
- version: 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ version: 8.6.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
'@storybook/components':
specifier: 8.6.14
- version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ version: 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
'@storybook/core-events':
specifier: 8.6.14
- version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ version: 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
'@storybook/manager-api':
specifier: 8.6.14
- version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ version: 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
'@storybook/preview-api':
specifier: 8.6.14
- version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ version: 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
'@storybook/react':
- specifier: 8.6.14
- version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(typescript@5.8.3)
+ specifier: 9.1.3
+ version: 9.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))(typescript@5.9.2)
'@storybook/react-vite':
- specifier: 8.6.14
- version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.45.1)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))
+ specifier: 9.1.3
+ version: 9.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(rollup@4.48.0)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))(typescript@5.9.2)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
'@storybook/test':
specifier: 8.6.14
- version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ version: 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
'@storybook/theming':
specifier: 8.6.14
- version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ version: 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
'@storybook/types':
specifier: 8.6.14
- version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ version: 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
'@storybook/vue3':
- specifier: 8.6.14
- version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(vue@3.5.17(typescript@5.8.3))
+ specifier: 9.1.3
+ version: 9.1.3(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))(vue@3.5.19(typescript@5.9.2))
'@storybook/vue3-vite':
- specifier: 8.6.14
- version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.17(typescript@5.8.3))
+ specifier: 9.1.3
+ version: 9.1.3(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))(vue@3.5.19(typescript@5.9.2))
'@tabler/icons-webfont':
- specifier: 3.34.0
- version: 3.34.0
+ specifier: 3.34.1
+ version: 3.34.1
'@testing-library/vue':
specifier: 8.1.0
- version: 8.1.0(@vue/compiler-sfc@3.5.17)(@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))
+ version: 8.1.0(@vue/compiler-sfc@3.5.19)(@vue/server-renderer@3.5.19(vue@3.5.19(typescript@5.9.2)))(vue@3.5.19(typescript@5.9.2))
'@types/canvas-confetti':
specifier: 1.9.0
version: 1.9.0
@@ -957,14 +963,14 @@ importers:
specifier: 1.0.8
version: 1.0.8
'@types/matter-js':
- specifier: 0.19.8
- version: 0.19.8
+ specifier: 0.20.0
+ version: 0.20.0
'@types/micromatch':
specifier: 4.0.9
version: 4.0.9
'@types/node':
- specifier: 22.16.4
- version: 22.16.4
+ specifier: 22.17.2
+ version: 22.17.2
'@types/punycode.js':
specifier: npm:@types/punycode@2.1.4
version: '@types/punycode@2.1.4'
@@ -984,41 +990,41 @@ importers:
specifier: 8.18.1
version: 8.18.1
'@typescript-eslint/eslint-plugin':
- specifier: 8.37.0
- version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)
'@typescript-eslint/parser':
- specifier: 8.37.0
- version: 8.37.0(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(eslint@9.34.0)(typescript@5.9.2)
'@vitest/coverage-v8':
specifier: 3.2.4
- version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))
+ version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
'@vue/compiler-core':
- specifier: 3.5.17
- version: 3.5.17
+ specifier: 3.5.19
+ version: 3.5.19
'@vue/runtime-core':
- specifier: 3.5.17
- version: 3.5.17
+ specifier: 3.5.19
+ version: 3.5.19
acorn:
specifier: 8.15.0
version: 8.15.0
cross-env:
- specifier: 7.0.3
- version: 7.0.3
+ specifier: 10.0.0
+ version: 10.0.0
cypress:
- specifier: 14.5.2
- version: 14.5.2
+ specifier: 14.5.4
+ version: 14.5.4
eslint-plugin-import:
specifier: 2.32.0
- version: 2.32.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)
+ version: 2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)
eslint-plugin-vue:
- specifier: 10.3.0
- version: 10.3.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(vue-eslint-parser@10.2.0(eslint@9.31.0))
+ specifier: 10.4.0
+ version: 10.4.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(vue-eslint-parser@10.2.0(eslint@9.34.0))
fast-glob:
specifier: 3.3.3
version: 3.3.3
happy-dom:
- specifier: 17.6.3
- version: 17.6.3
+ specifier: 18.0.1
+ version: 18.0.1
intersection-observer:
specifier: 0.12.2
version: 0.12.2
@@ -1029,11 +1035,11 @@ importers:
specifier: 10.0.3
version: 10.0.3
msw:
- specifier: 2.10.4
- version: 2.10.4(@types/node@22.16.4)(typescript@5.8.3)
+ specifier: 2.10.5
+ version: 2.10.5(@types/node@22.17.2)(typescript@5.9.2)
msw-storybook-addon:
specifier: 2.0.5
- version: 2.0.5(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))
+ version: 2.0.5(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))
nodemon:
specifier: 3.1.10
version: 3.1.10
@@ -1041,65 +1047,99 @@ importers:
specifier: 3.6.2
version: 3.6.2
react:
- specifier: 19.1.0
- version: 19.1.0
+ specifier: 19.1.1
+ version: 19.1.1
react-dom:
- specifier: 19.1.0
- version: 19.1.0(react@19.1.0)
+ specifier: 19.1.1
+ version: 19.1.1(react@19.1.1)
seedrandom:
specifier: 3.0.5
version: 3.0.5
start-server-and-test:
- specifier: 2.0.12
- version: 2.0.12
+ specifier: 2.0.13
+ version: 2.0.13
storybook:
- specifier: 8.6.14
- version: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ specifier: 9.1.3
+ version: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
storybook-addon-misskey-theme:
specifier: github:misskey-dev/storybook-addon-misskey-theme
- version: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/components@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/core-events@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/manager-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/preview-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/theming@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/types@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ version: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(259986330117e371e80190ddf50a33b9)
+ tsx:
+ specifier: 4.20.4
+ version: 4.20.4
vite-plugin-turbosnap:
specifier: 1.0.3
version: 1.0.3
vitest:
specifier: 3.2.4
- version: 3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
+ version: 3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)
vitest-fetch-mock:
specifier: 0.4.5
- version: 0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))
+ version: 0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
vue-component-type-helpers:
- specifier: 2.2.12
- version: 2.2.12
+ specifier: 3.0.6
+ version: 3.0.6
vue-eslint-parser:
specifier: 10.2.0
- version: 10.2.0(eslint@9.31.0)
+ version: 10.2.0(eslint@9.34.0)
vue-tsc:
- specifier: 2.2.12
- version: 2.2.12(typescript@5.8.3)
+ specifier: 3.0.6
+ version: 3.0.6(typescript@5.9.2)
+
+ packages/frontend-builder:
+ dependencies:
+ estree-walker:
+ specifier: 3.0.3
+ version: 3.0.3
+ magic-string:
+ specifier: 0.30.17
+ version: 0.30.17
+ vite:
+ specifier: 7.0.6
+ version: 7.0.6(@types/node@22.17.0)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5)
+ devDependencies:
+ '@types/estree':
+ specifier: 1.0.8
+ version: 1.0.8
+ '@types/node':
+ specifier: 22.17.0
+ version: 22.17.0
+ '@typescript-eslint/eslint-plugin':
+ specifier: 8.38.0
+ version: 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)
+ '@typescript-eslint/parser':
+ specifier: 8.38.0
+ version: 8.38.0(eslint@9.34.0)(typescript@5.9.2)
+ rollup:
+ specifier: 4.46.2
+ version: 4.46.2
+ typescript:
+ specifier: 5.9.2
+ version: 5.9.2
packages/frontend-embed:
dependencies:
'@discordapp/twemoji':
- specifier: 15.1.0
- version: 15.1.0
+ specifier: 16.0.1
+ version: 16.0.1
'@rollup/plugin-json':
specifier: 6.1.0
- version: 6.1.0(rollup@4.45.1)
+ version: 6.1.0(rollup@4.48.0)
'@rollup/plugin-replace':
specifier: 6.0.2
- version: 6.0.2(rollup@4.45.1)
+ version: 6.0.2(rollup@4.48.0)
'@rollup/pluginutils':
specifier: 5.2.0
- version: 5.2.0(rollup@4.45.1)
+ version: 5.2.0(rollup@4.48.0)
'@twemoji/parser':
- specifier: 15.1.1
- version: 15.1.1
+ specifier: 16.0.0
+ version: 16.0.0
'@vitejs/plugin-vue':
- specifier: 5.2.4
- version: 5.2.4(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.17(typescript@5.8.3))
+ specifier: 6.0.1
+ version: 6.0.1(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))(vue@3.5.19(typescript@5.9.2))
'@vue/compiler-sfc':
- specifier: 3.5.17
- version: 3.5.17
+ specifier: 3.5.19
+ version: 3.5.19
astring:
specifier: 1.9.0
version: 1.9.0
@@ -1119,8 +1159,8 @@ importers:
specifier: 2.2.3
version: 2.2.3
mfm-js:
- specifier: 0.24.0
- version: 0.24.0
+ specifier: 0.25.0
+ version: 0.25.0
misskey-js:
specifier: workspace:*
version: link:../misskey-js
@@ -1128,14 +1168,14 @@ importers:
specifier: 2.3.1
version: 2.3.1
rollup:
- specifier: 4.45.1
- version: 4.45.1
+ specifier: 4.48.0
+ version: 4.48.0
sass:
- specifier: 1.89.2
- version: 1.89.2
+ specifier: 1.90.0
+ version: 1.90.0
shiki:
- specifier: 3.8.0
- version: 3.8.0
+ specifier: 3.11.0
+ version: 3.11.0
tinycolor2:
specifier: 1.6.0
version: 1.6.0
@@ -1146,27 +1186,27 @@ importers:
specifier: 4.2.0
version: 4.2.0
typescript:
- specifier: 5.8.3
- version: 5.8.3
+ specifier: 5.9.2
+ version: 5.9.2
uuid:
specifier: 11.1.0
version: 11.1.0
vite:
- specifier: 6.3.5
- version: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
+ specifier: 7.1.3
+ version: 7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)
vue:
- specifier: 3.5.17
- version: 3.5.17(typescript@5.8.3)
+ specifier: 3.5.19
+ version: 3.5.19(typescript@5.9.2)
devDependencies:
'@misskey-dev/summaly':
- specifier: 5.2.2
- version: 5.2.2
+ specifier: 5.2.3
+ version: 5.2.3
'@tabler/icons-webfont':
- specifier: 3.34.0
- version: 3.34.0
+ specifier: 3.34.1
+ version: 3.34.1
'@testing-library/vue':
specifier: 8.1.0
- version: 8.1.0(@vue/compiler-sfc@3.5.17)(@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))
+ version: 8.1.0(@vue/compiler-sfc@3.5.19)(@vue/server-renderer@3.5.19(vue@3.5.19(typescript@5.9.2)))(vue@3.5.19(typescript@5.9.2))
'@types/estree':
specifier: 1.0.8
version: 1.0.8
@@ -1174,8 +1214,8 @@ importers:
specifier: 4.0.9
version: 4.0.9
'@types/node':
- specifier: 22.16.4
- version: 22.16.4
+ specifier: 22.17.2
+ version: 22.17.2
'@types/punycode.js':
specifier: npm:@types/punycode@2.1.4
version: '@types/punycode@2.1.4'
@@ -1186,35 +1226,35 @@ importers:
specifier: 8.18.1
version: 8.18.1
'@typescript-eslint/eslint-plugin':
- specifier: 8.37.0
- version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)
'@typescript-eslint/parser':
- specifier: 8.37.0
- version: 8.37.0(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(eslint@9.34.0)(typescript@5.9.2)
'@vitest/coverage-v8':
specifier: 3.2.4
- version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))
+ version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
'@vue/runtime-core':
- specifier: 3.5.17
- version: 3.5.17
+ specifier: 3.5.19
+ version: 3.5.19
acorn:
specifier: 8.15.0
version: 8.15.0
cross-env:
- specifier: 7.0.3
- version: 7.0.3
+ specifier: 10.0.0
+ version: 10.0.0
eslint-plugin-import:
specifier: 2.32.0
- version: 2.32.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)
+ version: 2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)
eslint-plugin-vue:
- specifier: 10.3.0
- version: 10.3.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(vue-eslint-parser@10.2.0(eslint@9.31.0))
+ specifier: 10.4.0
+ version: 10.4.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(vue-eslint-parser@10.2.0(eslint@9.34.0))
fast-glob:
specifier: 3.3.3
version: 3.3.3
happy-dom:
- specifier: 17.6.3
- version: 17.6.3
+ specifier: 18.0.1
+ version: 18.0.1
intersection-observer:
specifier: 0.12.2
version: 0.12.2
@@ -1222,8 +1262,8 @@ importers:
specifier: 4.0.8
version: 4.0.8
msw:
- specifier: 2.10.4
- version: 2.10.4(@types/node@22.16.4)(typescript@5.8.3)
+ specifier: 2.10.5
+ version: 2.10.5(@types/node@22.17.2)(typescript@5.9.2)
nodemon:
specifier: 3.1.10
version: 3.1.10
@@ -1231,20 +1271,23 @@ importers:
specifier: 3.6.2
version: 3.6.2
start-server-and-test:
- specifier: 2.0.12
- version: 2.0.12
+ specifier: 2.0.13
+ version: 2.0.13
+ tsx:
+ specifier: 4.20.4
+ version: 4.20.4
vite-plugin-turbosnap:
specifier: 1.0.3
version: 1.0.3
vue-component-type-helpers:
- specifier: 2.2.12
- version: 2.2.12
+ specifier: 3.0.6
+ version: 3.0.6
vue-eslint-parser:
specifier: 10.2.0
- version: 10.2.0(eslint@9.31.0)
+ version: 10.2.0(eslint@9.34.0)
vue-tsc:
- specifier: 2.2.12
- version: 2.2.12(typescript@5.8.3)
+ specifier: 3.0.6
+ version: 3.0.6(typescript@5.9.2)
packages/frontend-shared:
dependencies:
@@ -1252,67 +1295,67 @@ importers:
specifier: workspace:*
version: link:../misskey-js
vue:
- specifier: 3.5.17
- version: 3.5.17(typescript@5.8.3)
+ specifier: 3.5.19
+ version: 3.5.19(typescript@5.9.2)
devDependencies:
'@types/node':
- specifier: 22.16.4
- version: 22.16.4
+ specifier: 22.17.2
+ version: 22.17.2
'@typescript-eslint/eslint-plugin':
- specifier: 8.37.0
- version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)
'@typescript-eslint/parser':
- specifier: 8.37.0
- version: 8.37.0(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(eslint@9.34.0)(typescript@5.9.2)
esbuild:
- specifier: 0.25.6
- version: 0.25.6
+ specifier: 0.25.9
+ version: 0.25.9
eslint-plugin-vue:
- specifier: 10.3.0
- version: 10.3.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(vue-eslint-parser@10.2.0(eslint@9.31.0))
+ specifier: 10.4.0
+ version: 10.4.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(vue-eslint-parser@10.2.0(eslint@9.34.0))
nodemon:
specifier: 3.1.10
version: 3.1.10
typescript:
- specifier: 5.8.3
- version: 5.8.3
+ specifier: 5.9.2
+ version: 5.9.2
vue-eslint-parser:
specifier: 10.2.0
- version: 10.2.0(eslint@9.31.0)
+ version: 10.2.0(eslint@9.34.0)
packages/icons-subsetter:
dependencies:
'@tabler/icons-webfont':
- specifier: 3.34.0
- version: 3.34.0
+ specifier: 3.34.1
+ version: 3.34.1
harfbuzzjs:
- specifier: 0.4.7
- version: 0.4.7
+ specifier: 0.4.9
+ version: 0.4.9
tiny-glob:
specifier: 0.2.9
version: 0.2.9
tsx:
- specifier: 4.20.3
- version: 4.20.3
+ specifier: 4.20.4
+ version: 4.20.4
typescript:
- specifier: 5.8.3
- version: 5.8.3
+ specifier: 5.9.2
+ version: 5.9.2
wawoff2:
specifier: 2.0.1
version: 2.0.1
devDependencies:
'@types/node':
- specifier: 22.16.4
- version: 22.16.4
+ specifier: 22.17.2
+ version: 22.17.2
'@types/wawoff2':
specifier: 1.0.2
version: 1.0.2
'@typescript-eslint/eslint-plugin':
- specifier: 8.37.0
- version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)
'@typescript-eslint/parser':
- specifier: 8.37.0
- version: 8.37.0(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(eslint@9.34.0)(typescript@5.9.2)
packages/misskey-bubble-game:
dependencies:
@@ -1327,23 +1370,23 @@ importers:
version: 3.0.5
devDependencies:
'@types/matter-js':
- specifier: 0.19.8
- version: 0.19.8
+ specifier: 0.20.0
+ version: 0.20.0
'@types/node':
- specifier: 22.16.4
- version: 22.16.4
+ specifier: 22.17.2
+ version: 22.17.2
'@types/seedrandom':
specifier: 3.0.8
version: 3.0.8
'@typescript-eslint/eslint-plugin':
- specifier: 8.37.0
- version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)
'@typescript-eslint/parser':
- specifier: 8.37.0
- version: 8.37.0(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(eslint@9.34.0)(typescript@5.9.2)
esbuild:
- specifier: 0.25.6
- version: 0.25.6
+ specifier: 0.25.9
+ version: 0.25.9
execa:
specifier: 9.6.0
version: 9.6.0
@@ -1354,8 +1397,8 @@ importers:
specifier: 3.1.10
version: 3.1.10
typescript:
- specifier: 5.8.3
- version: 5.8.3
+ specifier: 5.9.2
+ version: 5.9.2
packages/misskey-js:
dependencies:
@@ -1370,44 +1413,29 @@ importers:
version: 4.4.0
devDependencies:
'@microsoft/api-extractor':
- specifier: 7.52.8
- version: 7.52.8(@types/node@22.16.4)
- '@swc/jest':
- specifier: 0.2.39
- version: 0.2.39(@swc/core@1.12.0)
- '@types/jest':
- specifier: 29.5.14
- version: 29.5.14
+ specifier: 7.52.11
+ version: 7.52.11(@types/node@22.17.2)
'@types/node':
- specifier: 22.16.4
- version: 22.16.4
+ specifier: 22.17.2
+ version: 22.17.2
'@typescript-eslint/eslint-plugin':
- specifier: 8.37.0
- version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)
'@typescript-eslint/parser':
- specifier: 8.37.0
- version: 8.37.0(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(eslint@9.34.0)(typescript@5.9.2)
+ '@vitest/coverage-v8':
+ specifier: 3.2.4
+ version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5))
esbuild:
- specifier: 0.25.6
- version: 0.25.6
+ specifier: 0.25.9
+ version: 0.25.9
execa:
- specifier: 8.0.1
- version: 8.0.1
+ specifier: 9.6.0
+ version: 9.6.0
glob:
specifier: 11.0.3
version: 11.0.3
- jest:
- specifier: 29.7.0
- version: 29.7.0(@types/node@22.16.4)
- jest-fetch-mock:
- specifier: 3.0.3
- version: 3.0.3(encoding@0.1.13)
- jest-websocket-mock:
- specifier: 2.5.0
- version: 2.5.0
- mock-socket:
- specifier: 9.3.1
- version: 9.3.1
ncp:
specifier: 2.0.0
version: 2.0.0
@@ -1415,41 +1443,47 @@ importers:
specifier: 3.1.10
version: 3.1.10
tsd:
- specifier: 0.32.0
- version: 0.32.0
+ specifier: 0.33.0
+ version: 0.33.0
typescript:
- specifier: 5.8.3
- version: 5.8.3
+ specifier: 5.9.2
+ version: 5.9.2
+ vitest:
+ specifier: 3.2.4
+ version: 3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5)
+ vitest-websocket-mock:
+ specifier: 0.5.0
+ version: 0.5.0(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5))
packages/misskey-js/generator:
devDependencies:
'@readme/openapi-parser':
- specifier: 2.7.0
- version: 2.7.0(openapi-types@12.1.3)
+ specifier: 5.0.1
+ version: 5.0.1(openapi-types@12.1.3)
'@types/node':
- specifier: 22.16.4
- version: 22.16.4
+ specifier: 22.17.2
+ version: 22.17.2
'@typescript-eslint/eslint-plugin':
- specifier: 8.37.0
- version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)
'@typescript-eslint/parser':
- specifier: 8.37.0
- version: 8.37.0(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(eslint@9.34.0)(typescript@5.9.2)
openapi-types:
specifier: 12.1.3
version: 12.1.3
openapi-typescript:
- specifier: 7.8.0
- version: 7.8.0(typescript@5.8.3)
+ specifier: 7.9.1
+ version: 7.9.1(typescript@5.9.2)
ts-case-convert:
specifier: 2.1.0
version: 2.1.0
tsx:
- specifier: 4.20.3
- version: 4.20.3
+ specifier: 4.20.5
+ version: 4.20.5
typescript:
- specifier: 5.8.3
- version: 5.8.3
+ specifier: 5.9.2
+ version: 5.9.2
packages/misskey-reversi:
dependencies:
@@ -1458,17 +1492,17 @@ importers:
version: 1.2.2
devDependencies:
'@types/node':
- specifier: 22.16.4
- version: 22.16.4
+ specifier: 22.17.2
+ version: 22.17.2
'@typescript-eslint/eslint-plugin':
- specifier: 8.37.0
- version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)
'@typescript-eslint/parser':
- specifier: 8.37.0
- version: 8.37.0(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(eslint@9.34.0)(typescript@5.9.2)
esbuild:
- specifier: 0.25.6
- version: 0.25.6
+ specifier: 0.25.9
+ version: 0.25.9
execa:
specifier: 9.6.0
version: 9.6.0
@@ -1479,14 +1513,14 @@ importers:
specifier: 3.1.10
version: 3.1.10
typescript:
- specifier: 5.8.3
- version: 5.8.3
+ specifier: 5.9.2
+ version: 5.9.2
packages/sw:
dependencies:
esbuild:
- specifier: 0.25.6
- version: 0.25.6
+ specifier: 0.25.9
+ version: 0.25.9
idb-keyval:
specifier: 6.2.2
version: 6.2.2
@@ -1495,20 +1529,20 @@ importers:
version: link:../misskey-js
devDependencies:
'@typescript-eslint/parser':
- specifier: 8.37.0
- version: 8.37.0(eslint@9.31.0)(typescript@5.8.3)
+ specifier: 8.40.0
+ version: 8.40.0(eslint@9.34.0)(typescript@5.9.2)
'@typescript/lib-webworker':
specifier: npm:@types/serviceworker@0.0.74
version: '@types/serviceworker@0.0.74'
eslint-plugin-import:
specifier: 2.32.0
- version: 2.32.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)
+ version: 2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)
nodemon:
specifier: 3.1.10
version: 3.1.10
typescript:
- specifier: 5.8.3
- version: 5.8.3
+ specifier: 5.9.2
+ version: 5.9.2
packages:
@@ -1519,32 +1553,33 @@ packages:
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
- '@analytics/cookie-utils@0.2.12':
- resolution: {integrity: sha512-2h/yuIu3kmu+ZJlKmlT6GoRvUEY2k1BbQBezEv5kGhnn9KpmzPz715Y3GmM2i+m7Y0QmBdVUoA260dQZkofs2A==}
+ '@analytics/cookie-utils@0.2.14':
+ resolution: {integrity: sha512-x51x2cLqvP5Fb1ydgNvTCX+SVv0ALK/yTNwp/53++yk4kLhxb850krWtQ4aASN0612oXrIGotwfmdJIttnLiPQ==}
- '@analytics/core@0.12.17':
- resolution: {integrity: sha512-GMxRm5Dp3Wam/w5NNvqNKMO6zWecozbVv21Kn4WhftCx6OjJI7zMlVtiLpjGjxa0RRZfVG80YhupF0Qh9XL2gw==}
+ '@analytics/core@0.13.2':
+ resolution: {integrity: sha512-ejvfoPP8TEh2hA2szMEq9c4TdeX8FAeY1j/7MxJVZjzDaq8BDHOyaAAQzTFiLMHvV0WcU2YC0smJ5Ids5Ll5ng==}
- '@analytics/global-storage-utils@0.1.7':
- resolution: {integrity: sha512-V+spzGLZYm4biZT4uefaylm80SrLXf8WOTv9hCgA46cLcyxx3LD4GCpssp1lj+RcWLl/uXJQBRO4Mnn/o1x6Gw==}
+ '@analytics/global-storage-utils@0.1.9':
+ resolution: {integrity: sha512-+xm6CDnWsVOQIKkqbPRPRdYDXKk3PNgr/bCZWSI+7tEDT5PCDgI0QSBZe+FqCVkCRtTkgOrjFOY7wOM8Gq+ndA==}
'@analytics/google-analytics@1.1.0':
resolution: {integrity: sha512-i8uGyELMtwEUAf3GNWNLNBzhRvReDn1RUxvMdMhjUA7+GNGxPOM4kkzFfv3giQXKNxTEjfsh75kqNcscbJsuaA==}
- '@analytics/localstorage-utils@0.1.10':
- resolution: {integrity: sha512-uJS+Jp1yLG5VFCgA5T82ZODYBS0xuDQx0NtAZrgbqt9j51BX3TcgmOez5LVkrUNu/lpbxjCLq35I4TKj78VmOQ==}
+ '@analytics/localstorage-utils@0.1.12':
+ resolution: {integrity: sha512-BL3vuZUwWgMqdkQsE0GKsED5SPLC6daI4K4LE0a/BkKv+4Cae5JLLqpO5gju2HUGOjJxIvw8U/G5EcglNY5+1w==}
- '@analytics/session-storage-utils@0.0.7':
- resolution: {integrity: sha512-PSv40UxG96HVcjY15e3zOqU2n8IqXnH8XvTkg1X43uXNTKVSebiI2kUjA3Q7ESFbw5DPwcLbJhV7GforpuBLDw==}
+ '@analytics/session-storage-utils@0.0.9':
+ resolution: {integrity: sha512-fhP9QCpyq45rZKsXaAxyz+VTmOUWljIW08CWSkFzpwOHkDM4Xy5tymc1YcWqSBBaLjHldo3HlY4qfqEIS4Aj1A==}
- '@analytics/storage-utils@0.4.2':
- resolution: {integrity: sha512-AXObwyVQw9h2uJh1t2hUgabtVxzYpW+7uKVbdHQK80vr3Td5rrmCxrCxarh7HUuAgSDZ0bZWqmYxVgmwKceaLg==}
+ '@analytics/storage-utils@0.4.4':
+ resolution: {integrity: sha512-873P4wDIunbOnBqADc2AhTVsLbluUv1dP6k9UrK8FIeV8WXv5+fG12HdwwaniUIxq6QLgZJfKEaCwtWSKrrV0g==}
- '@analytics/type-utils@0.6.2':
- resolution: {integrity: sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg==}
+ '@analytics/type-utils@0.6.4':
+ resolution: {integrity: sha512-Ou1gQxFakOWLcPnbFVsrPb8g1wLLUZYYJXDPjHkG07+5mustGs5yqACx42UAu4A6NszNN6Z5gGxhyH45zPWRxw==}
- '@apidevtools/swagger-methods@3.0.2':
- resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==}
+ '@apidevtools/json-schema-ref-parser@14.1.1':
+ resolution: {integrity: sha512-uGF1YGOzzD50L7HLNWclXmsEhQflw8/zZHIz0/AzkJrKL5r9PceUipZxR/cp/8veTk4TVfdDJLyIwXLjaP5ePg==}
+ engines: {node: '>= 20'}
'@asamuzakjp/css-color@2.8.3':
resolution: {integrity: sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==}
@@ -1572,129 +1607,133 @@ packages:
'@aws-crypto/util@5.2.0':
resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==}
- '@aws-sdk/client-s3@3.826.0':
- resolution: {integrity: sha512-odX3C3CEbcBoxB06vgBjJ9jQheFsIFwHmvCIMXn8duuVyIL/klgp14+ICzbEwIgPv7xVjSlycaiURcKS876QHA==}
+ '@aws-sdk/client-s3@3.873.0':
+ resolution: {integrity: sha512-b+1lSEf+obcC508blw5qEDR1dyTiHViZXbf8G6nFospyqLJS0Vu2py+e+LG2VDVdAouZ8+RvW+uAi73KgsWl0w==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/client-sso@3.826.0':
- resolution: {integrity: sha512-/FEKnUC3xPkLL4RuRydwzx+y4b55HIX6qLPbGnyIs+sNmCUyc/62ijtV1Ml+b++YzEF6jWNBsJOxeyZdgrJ3Ig==}
+ '@aws-sdk/client-ses@3.873.0':
+ resolution: {integrity: sha512-m133jrCWPtMLSRYadK5gc6B4HqSP9EKZjCyq+sTlerN0vUBI/Uf5eA+6ZG3/04lIA7nOje+wCB8dTSZmiId55w==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/core@3.826.0':
- resolution: {integrity: sha512-BGbQYzWj3ps+dblq33FY5tz/SsgJCcXX0zjQlSC07tYvU1jHTUvsefphyig+fY38xZ4wdKjbTop+KUmXUYrOXw==}
+ '@aws-sdk/client-sso@3.873.0':
+ resolution: {integrity: sha512-EmcrOgFODWe7IsLKFTeSXM9TlQ80/BO1MBISlr7w2ydnOaUYIiPGRRJnDpeIgMaNqT4Rr2cRN2RiMrbFO7gDdA==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/credential-provider-env@3.826.0':
- resolution: {integrity: sha512-DK3pQY8+iKK3MGDdC3uOZQ2psU01obaKlTYhEwNu4VWzgwQL4Vi3sWj4xSWGEK41vqZxiRLq6fOq7ysRI+qEZA==}
+ '@aws-sdk/core@3.873.0':
+ resolution: {integrity: sha512-WrROjp8X1VvmnZ4TBzwM7RF+EB3wRaY9kQJLXw+Aes0/3zRjUXvGIlseobGJMqMEGnM0YekD2F87UaVfot1xeQ==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/credential-provider-http@3.826.0':
- resolution: {integrity: sha512-N+IVZBh+yx/9GbMZTKO/gErBi/FYZQtcFRItoLbY+6WU+0cSWyZYfkoeOxHmQV3iX9k65oljERIWUmL9x6OSQg==}
+ '@aws-sdk/credential-provider-env@3.873.0':
+ resolution: {integrity: sha512-FWj1yUs45VjCADv80JlGshAttUHBL2xtTAbJcAxkkJZzLRKVkdyrepFWhv/95MvDyzfbT6PgJiWMdW65l/8ooA==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/credential-provider-ini@3.826.0':
- resolution: {integrity: sha512-g7n+qSklq/Lzjxe2Ke5QFNCgYn26a3ydZnbFIk8QqYin4pzG+qiunaqJjpV3c/EeHMlfK8bBc7MXAylKzGRccQ==}
+ '@aws-sdk/credential-provider-http@3.873.0':
+ resolution: {integrity: sha512-0sIokBlXIsndjZFUfr3Xui8W6kPC4DAeBGAXxGi9qbFZ9PWJjn1vt2COLikKH3q2snchk+AsznREZG8NW6ezSg==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/credential-provider-node@3.826.0':
- resolution: {integrity: sha512-UfIJXxHjmSxH6bea00HBPLkjNI2D04enQA/xNLZvB+4xtzt1/gYdCis1P4/73f5aGVVVB4/zQMobBbnjkrmbQw==}
+ '@aws-sdk/credential-provider-ini@3.873.0':
+ resolution: {integrity: sha512-bQdGqh47Sk0+2S3C+N46aNQsZFzcHs7ndxYLARH/avYXf02Nl68p194eYFaAHJSQ1re5IbExU1+pbums7FJ9fA==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/credential-provider-process@3.826.0':
- resolution: {integrity: sha512-kURrc4amu3NLtw1yZw7EoLNEVhmOMRUTs+chaNcmS+ERm3yK0nKjaJzmKahmwlTQTSl3wJ8jjK7x962VPo+zWw==}
+ '@aws-sdk/credential-provider-node@3.873.0':
+ resolution: {integrity: sha512-+v/xBEB02k2ExnSDL8+1gD6UizY4Q/HaIJkNSkitFynRiiTQpVOSkCkA0iWxzksMeN8k1IHTE5gzeWpkEjNwbA==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/credential-provider-sso@3.826.0':
- resolution: {integrity: sha512-F19J3zcfoom6OnQ0MyAtvduVKQXPgkz9i5ExSO01J2CzjbyMhCDA99qAjHYe+LwhW+W7P/jzBPd0+uOQ2Nhh9Q==}
+ '@aws-sdk/credential-provider-process@3.873.0':
+ resolution: {integrity: sha512-ycFv9WN+UJF7bK/ElBq1ugWA4NMbYS//1K55bPQZb2XUpAM2TWFlEjG7DIyOhLNTdl6+CbHlCdhlKQuDGgmm0A==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/credential-provider-web-identity@3.826.0':
- resolution: {integrity: sha512-o27GZ6Hy7qhuvMFVUL2eFEpBzf33Jaa/x3u3SHwU0nL7ko7jmbpeF0x4+wmagpI9X2IvVlUxIs0VaQ3YayPLEA==}
+ '@aws-sdk/credential-provider-sso@3.873.0':
+ resolution: {integrity: sha512-SudkAOZmjEEYgUrqlUUjvrtbWJeI54/0Xo87KRxm4kfBtMqSx0TxbplNUAk8Gkg4XQNY0o7jpG8tK7r2Wc2+uw==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/lib-storage@3.826.0':
- resolution: {integrity: sha512-NmZJVnP09ZGTVVz8ZCD8sQeVMfvyX5c2/NEJHSdavmWi2sJHuln09i/YQg90LFGL4eCFslzME/mP3pMtLQEeKQ==}
+ '@aws-sdk/credential-provider-web-identity@3.873.0':
+ resolution: {integrity: sha512-Gw2H21+VkA6AgwKkBtTtlGZ45qgyRZPSKWs0kUwXVlmGOiPz61t/lBX0vG6I06ZIz2wqeTJ5OA1pWZLqw1j0JQ==}
+ engines: {node: '>=18.0.0'}
+
+ '@aws-sdk/lib-storage@3.873.0':
+ resolution: {integrity: sha512-TcR15G+DOzniMProb+JtifLyAPORVcRw5hks6VPZg/KVOXGtOyXEG7yqnXV+pidc1xWLVvKlG3K+4r72f+zjLw==}
engines: {node: '>=18.0.0'}
peerDependencies:
- '@aws-sdk/client-s3': ^3.826.0
+ '@aws-sdk/client-s3': ^3.873.0
- '@aws-sdk/middleware-bucket-endpoint@3.821.0':
- resolution: {integrity: sha512-cebgeytKlWOgGczLo3BPvNY9XlzAzGZQANSysgJ2/8PSldmUpXRIF+GKPXDVhXeInWYHIfB8zZi3RqrPoXcNYQ==}
+ '@aws-sdk/middleware-bucket-endpoint@3.873.0':
+ resolution: {integrity: sha512-b4bvr0QdADeTUs+lPc9Z48kXzbKHXQKgTvxx/jXDgSW9tv4KmYPO1gIj6Z9dcrBkRWQuUtSW3Tu2S5n6pe+zeg==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/middleware-expect-continue@3.821.0':
- resolution: {integrity: sha512-zAOoSZKe1njOrtynvK6ZORU57YGv5I7KP4+rwOvUN3ZhJbQ7QPf8gKtFUCYAPRMegaXCKF/ADPtDZBAmM+zZ9g==}
+ '@aws-sdk/middleware-expect-continue@3.873.0':
+ resolution: {integrity: sha512-GIqoc8WgRcf/opBOZXFLmplJQKwOMjiOMmDz9gQkaJ8FiVJoAp8EGVmK2TOWZMQUYsavvHYsHaor5R2xwPoGVg==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/middleware-flexible-checksums@3.826.0':
- resolution: {integrity: sha512-Fz9w8CFYPfSlHEB6feSsi06hdS+s+FB8k5pO4L7IV0tUa78mlhxF/VNlAJaVWYyOkZXl4HPH2K48aapACSQOXw==}
+ '@aws-sdk/middleware-flexible-checksums@3.873.0':
+ resolution: {integrity: sha512-NNiy2Y876P5cgIhsDlHopbPZS3ugdfBW1va0WdpVBviwAs6KT4irPNPAOyF1/33N/niEDKx0fKQV7ROB70nNPA==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/middleware-host-header@3.821.0':
- resolution: {integrity: sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==}
+ '@aws-sdk/middleware-host-header@3.873.0':
+ resolution: {integrity: sha512-KZ/W1uruWtMOs7D5j3KquOxzCnV79KQW9MjJFZM/M0l6KI8J6V3718MXxFHsTjUE4fpdV6SeCNLV1lwGygsjJA==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/middleware-location-constraint@3.821.0':
- resolution: {integrity: sha512-sKrm80k0t3R0on8aA/WhWFoMaAl4yvdk+riotmMElLUpcMcRXAd1+600uFVrxJqZdbrKQ0mjX0PjT68DlkYXLg==}
+ '@aws-sdk/middleware-location-constraint@3.873.0':
+ resolution: {integrity: sha512-r+hIaORsW/8rq6wieDordXnA/eAu7xAPLue2InhoEX6ML7irP52BgiibHLpt9R0psiCzIHhju8qqKa4pJOrmiw==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/middleware-logger@3.821.0':
- resolution: {integrity: sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==}
+ '@aws-sdk/middleware-logger@3.873.0':
+ resolution: {integrity: sha512-QhNZ8X7pW68kFez9QxUSN65Um0Feo18ZmHxszQZNUhKDsXew/EG9NPQE/HgYcekcon35zHxC4xs+FeNuPurP2g==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/middleware-recursion-detection@3.821.0':
- resolution: {integrity: sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==}
+ '@aws-sdk/middleware-recursion-detection@3.873.0':
+ resolution: {integrity: sha512-OtgY8EXOzRdEWR//WfPkA/fXl0+WwE8hq0y9iw2caNyKPtca85dzrrZWnPqyBK/cpImosrpR1iKMYr41XshsCg==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/middleware-sdk-s3@3.826.0':
- resolution: {integrity: sha512-8F0qWaYKfvD/de1AKccXuigM+gb/IZSncCqxdnFWqd+TFzo9qI9Hh+TpUhWOMYSgxsMsYQ8ipmLzlD/lDhjrmA==}
+ '@aws-sdk/middleware-sdk-s3@3.873.0':
+ resolution: {integrity: sha512-bOoWGH57ORK2yKOqJMmxBV4b3yMK8Pc0/K2A98MNPuQedXaxxwzRfsT2Qw+PpfYkiijrrNFqDYmQRGntxJ2h8A==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/middleware-ssec@3.821.0':
- resolution: {integrity: sha512-YYi1Hhr2AYiU/24cQc8HIB+SWbQo6FBkMYojVuz/zgrtkFmALxENGF/21OPg7f/QWd+eadZJRxCjmRwh5F2Cxg==}
+ '@aws-sdk/middleware-ssec@3.873.0':
+ resolution: {integrity: sha512-AF55J94BoiuzN7g3hahy0dXTVZahVi8XxRBLgzNp6yQf0KTng+hb/V9UQZVYY1GZaDczvvvnqC54RGe9OZZ9zQ==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/middleware-user-agent@3.826.0':
- resolution: {integrity: sha512-j404+EcfBbtTlAhyObjXbdKwwDXO1pCxHvR5Fw8FXNvp/H330j6YnXgs3SJ6d3bZUwUJ/ztPx2S5AlBbLVLDFw==}
+ '@aws-sdk/middleware-user-agent@3.873.0':
+ resolution: {integrity: sha512-gHqAMYpWkPhZLwqB3Yj83JKdL2Vsb64sryo8LN2UdpElpS+0fT4yjqSxKTfp7gkhN6TCIxF24HQgbPk5FMYJWw==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/nested-clients@3.826.0':
- resolution: {integrity: sha512-p7olPq0uTtHqGuXI1GSc/gzKDvV55PMbLtnmupEDfnY9SoRu+QatbWQ6da9sI1lhOcNmRMgiNQBXFzaUFrG+SQ==}
+ '@aws-sdk/nested-clients@3.873.0':
+ resolution: {integrity: sha512-yg8JkRHuH/xO65rtmLOWcd9XQhxX1kAonp2CliXT44eA/23OBds6XoheY44eZeHfCTgutDLTYitvy3k9fQY6ZA==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/region-config-resolver@3.821.0':
- resolution: {integrity: sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==}
+ '@aws-sdk/region-config-resolver@3.873.0':
+ resolution: {integrity: sha512-q9sPoef+BBG6PJnc4x60vK/bfVwvRWsPgcoQyIra057S/QGjq5VkjvNk6H8xedf6vnKlXNBwq9BaANBXnldUJg==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/signature-v4-multi-region@3.826.0':
- resolution: {integrity: sha512-3fEi/zy6tpMzomYosksGtu7jZqGFcdBXoL7YRsG7OEeQzBbOW9B+fVaQZ4jnsViSjzA/yKydLahMrfPnt+iaxg==}
+ '@aws-sdk/signature-v4-multi-region@3.873.0':
+ resolution: {integrity: sha512-FQ5OIXw1rmDud7f/VO9y2Mg9rX1o4MnngRKUOD8mS9ALK4uxKrTczb4jA+uJLSLwTqMGs3bcB1RzbMW1zWTMwQ==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/token-providers@3.826.0':
- resolution: {integrity: sha512-iCOcVAqGPSHtQL8ZBXifZMEcHyUl9wJ8HvLZ5l1ohA/3ZNP+dqEPGi7jfhR5jZKs+xyp2jxByFqfil9PjI9c5A==}
+ '@aws-sdk/token-providers@3.873.0':
+ resolution: {integrity: sha512-BWOCeFeV/Ba8fVhtwUw/0Hz4wMm9fjXnMb4Z2a5he/jFlz5mt1/rr6IQ4MyKgzOaz24YrvqsJW2a0VUKOaYDvg==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/types@3.821.0':
- resolution: {integrity: sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA==}
+ '@aws-sdk/types@3.862.0':
+ resolution: {integrity: sha512-Bei+RL0cDxxV+lW2UezLbCYYNeJm6Nzee0TpW0FfyTRBhH9C1XQh4+x+IClriXvgBnRquTMMYsmJfvx8iyLKrg==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/util-arn-parser@3.804.0':
- resolution: {integrity: sha512-wmBJqn1DRXnZu3b4EkE6CWnoWMo1ZMvlfkqU5zPz67xx1GMaXlDCchFvKAXMjk4jn/L1O3tKnoFDNsoLV1kgNQ==}
+ '@aws-sdk/util-arn-parser@3.873.0':
+ resolution: {integrity: sha512-qag+VTqnJWDn8zTAXX4wiVioa0hZDQMtbZcGRERVnLar4/3/VIKBhxX2XibNQXFu1ufgcRn4YntT/XEPecFWcg==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/util-endpoints@3.821.0':
- resolution: {integrity: sha512-Uknt/zUZnLE76zaAAPEayOeF5/4IZ2puTFXvcSCWHsi9m3tqbb9UozlnlVqvCZLCRWfQryZQoG2W4XSS3qgk5A==}
+ '@aws-sdk/util-endpoints@3.873.0':
+ resolution: {integrity: sha512-YByHrhjxYdjKRf/RQygRK1uh0As1FIi9+jXTcIEX/rBgN8mUByczr2u4QXBzw7ZdbdcOBMOkPnLRjNOWW1MkFg==}
engines: {node: '>=18.0.0'}
'@aws-sdk/util-locate-window@3.208.0':
resolution: {integrity: sha512-iua1A2+P7JJEDHVgvXrRJSvsnzG7stYSGQnBVphIUlemwl6nN5D+QrgbjECtrbxRz8asYFHSzhdhECqN+tFiBg==}
engines: {node: '>=14.0.0'}
- '@aws-sdk/util-user-agent-browser@3.821.0':
- resolution: {integrity: sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==}
+ '@aws-sdk/util-user-agent-browser@3.873.0':
+ resolution: {integrity: sha512-AcRdbK6o19yehEcywI43blIBhOCSo6UgyWcuOJX5CFF8k39xm1ILCjQlRRjchLAxWrm0lU0Q7XV90RiMMFMZtA==}
- '@aws-sdk/util-user-agent-node@3.826.0':
- resolution: {integrity: sha512-wHw6bZQWIMcFF/8r03aY9Itp6JLBYY4absGGhCDK1dc3tPEfi8NVSdb05a/Oz+g4TVaDdxLo0OQ/OKMS1DFRHQ==}
+ '@aws-sdk/util-user-agent-node@3.873.0':
+ resolution: {integrity: sha512-9MivTP+q9Sis71UxuBaIY3h5jxH0vN3/ZWGxO8ADL19S2OIfknrYSAfzE5fpoKROVBu0bS4VifHOFq4PY1zsxw==}
engines: {node: '>=18.0.0'}
peerDependencies:
aws-crt: '>=1.0.0'
@@ -1702,14 +1741,10 @@ packages:
aws-crt:
optional: true
- '@aws-sdk/xml-builder@3.821.0':
- resolution: {integrity: sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==}
+ '@aws-sdk/xml-builder@3.873.0':
+ resolution: {integrity: sha512-kLO7k7cGJ6KaHiExSJWojZurF7SnGMDHXRuQunFnEoD0n1yB6Lqy/S/zHiQ7oJnBhPr9q0TW9qFkrsZb1Uc54w==}
engines: {node: '>=18.0.0'}
- '@babel/code-frame@7.24.7':
- resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==}
- engines: {node: '>=6.9.0'}
-
'@babel/code-frame@7.27.1':
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
engines: {node: '>=6.9.0'}
@@ -1764,18 +1799,10 @@ packages:
resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==}
engines: {node: '>=6.9.0'}
- '@babel/helper-string-parser@7.24.8':
- resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==}
- engines: {node: '>=6.9.0'}
-
'@babel/helper-string-parser@7.27.1':
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
engines: {node: '>=6.9.0'}
- '@babel/helper-validator-identifier@7.24.7':
- resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==}
- engines: {node: '>=6.9.0'}
-
'@babel/helper-validator-identifier@7.27.1':
resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
engines: {node: '>=6.9.0'}
@@ -1788,17 +1815,13 @@ packages:
resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==}
engines: {node: '>=6.9.0'}
- '@babel/highlight@7.24.7':
- resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==}
- engines: {node: '>=6.9.0'}
-
- '@babel/parser@7.27.2':
- resolution: {integrity: sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==}
+ '@babel/parser@7.28.0':
+ resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==}
engines: {node: '>=6.0.0'}
hasBin: true
- '@babel/parser@7.28.0':
- resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==}
+ '@babel/parser@7.28.3':
+ resolution: {integrity: sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==}
engines: {node: '>=6.0.0'}
hasBin: true
@@ -1887,18 +1910,14 @@ packages:
resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==}
engines: {node: '>=6.9.0'}
- '@babel/types@7.25.6':
- resolution: {integrity: sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==}
- engines: {node: '>=6.9.0'}
-
- '@babel/types@7.27.1':
- resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==}
- engines: {node: '>=6.9.0'}
-
'@babel/types@7.28.1':
resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==}
engines: {node: '>=6.9.0'}
+ '@babel/types@7.28.2':
+ resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
+ engines: {node: '>=6.9.0'}
+
'@bcoe/v8-coverage@0.2.3':
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
@@ -1921,38 +1940,38 @@ packages:
'@chainsafe/is-ip@2.1.0':
resolution: {integrity: sha512-KIjt+6IfysQ4GCv66xihEitBjvhU/bixbbbFxdJ1sqCp4uJ0wuZiYBPhksZoy4lfaF0k9cwNzY5upEW/VWdw3w==}
- '@cropper/element-canvas@2.0.0':
- resolution: {integrity: sha512-GPtGJgSm92crJhhhwUsaMw3rz2KfJWWSz7kRAlufFEV/EHTP5+6r6/Z1BCGRna830i+Avqbm435XLOtA7PVJwA==}
+ '@cropper/element-canvas@2.0.1':
+ resolution: {integrity: sha512-OKxq/O0HL9W2JegOsc2zh1NRpERZcLM5+M8aQ/eXdmMcfi1lzosPftag3Irp6pTsVpwV6B6ypIxKESzJ4ci9Fw==}
- '@cropper/element-crosshair@2.0.0':
- resolution: {integrity: sha512-KfPfyrdeFvUC31Ws7ATtcalWWSaMtrC6bMoCipZhqbUOE7wZoL4ecDSL6BUOZxPa74awZUqfzirCDjHvheBfyw==}
+ '@cropper/element-crosshair@2.0.1':
+ resolution: {integrity: sha512-bS5msU9cTU/jf1/kDw+QJmEM9/rw8IgOdpolR85iMVUCR8sRcLa0wgom42MBHcpBYB6hvL5YfiOeXZ7lHIYMpw==}
- '@cropper/element-grid@2.0.0':
- resolution: {integrity: sha512-i78SQ0IJTLFveKX6P7svkfMYVdgHrQ8ZmmEw8keFy9n1ZVbK+SK0UHK5FNMRNI/gtVhKJOGEnK/zeyjUdj4Iyw==}
+ '@cropper/element-grid@2.0.1':
+ resolution: {integrity: sha512-ayqCvYQJ+GVT31HhFpttzHabW1T/LsIwLJY5PLTMG0cEZLw/E8ihg8mxctjZbo852D7oEePbz6/2SeuCb1018Q==}
- '@cropper/element-handle@2.0.0':
- resolution: {integrity: sha512-ZJvW+0MkK9E8xYymGdoruaQn2kwjSHFpNSWinjyq6csuVQiCPxlX5ovAEDldmZ9MWePPtWEi3vLKQOo2Yb0T8g==}
+ '@cropper/element-handle@2.0.1':
+ resolution: {integrity: sha512-fdifyyPIaR9S2eQ7qPHuM8fX8uToAfBsi8vQlR9EM+oJkDNil0uO4rWyArLWEtlr0/q7U0OvsufcuJ7ffqfmpg==}
- '@cropper/element-image@2.0.0':
- resolution: {integrity: sha512-9BxiTS/aHRmrjopaFQb9mQQXmx4ruhYHGkDZMVz24AXpMFjUY6OpqrWse/WjzD9tfhMFvEdu17b3VAekcAgpeg==}
+ '@cropper/element-image@2.0.1':
+ resolution: {integrity: sha512-gPj5Sl2T8Cno198Cz3F3TDfcYoALW3yJ3fV6PHXmhMnX8sBkL7J441do7Vwkg0mEd2CogCtTLAf+p7ljdV0kgA==}
- '@cropper/element-selection@2.0.0':
- resolution: {integrity: sha512-ensNnbIfJsJ8bhbJTH/RXtk2URFvTOO4TvfRk461n2FPEC588D7rwBmUJxQg74IiTi4y1JbCI+6j+4LyzYBLCQ==}
+ '@cropper/element-selection@2.0.1':
+ resolution: {integrity: sha512-atv+Aeq2N2eWawelIRPGh1kYFdNrpb0QkUPPheGxz1ImfxpLdcHO9gb9T5noQijizUW2G0pNvts4ZaITQ0I71Q==}
- '@cropper/element-shade@2.0.0':
- resolution: {integrity: sha512-jv/2bbNZnhU4W+T4G0c8ADocLIZvQFTXgCf2RFDNhI5UVxurzWBnDdb8Mx8LnVplnkTqO+xUmHZYve0CwgWo+Q==}
+ '@cropper/element-shade@2.0.1':
+ resolution: {integrity: sha512-YIYgJ690NdFQ6wJLRFh/EySNVxGFKArncQ4FrsJ3yHU+ShgtOKz4FpjFLpqJRJB9swoVbD3WKTimGyzXrwjZrQ==}
- '@cropper/element-viewer@2.0.0':
- resolution: {integrity: sha512-zY+3VRN5TvpM8twlphYtXw0tzJL2VgzeK7ufhL1BixVqOdRxwP13TprYIhqwGt9EW/SyJZUiaIu396T89kRX8A==}
+ '@cropper/element-viewer@2.0.1':
+ resolution: {integrity: sha512-HDj25l08pWi/AO6El/OqfQHBpBC4Lh5NEnQN1SOldsmxEwt27Ubv6ndDsF8LkTK7XPwjjZRpyQPyfig4w8L2JQ==}
- '@cropper/element@2.0.0':
- resolution: {integrity: sha512-lsthn0nQq73GExUE7Mg/ss6Q3RXADGDv055hxoLFwvl/wGHgy6ZkYlfLZ/VmgBHC6jDK5IgPBFnqrPqlXWSGBA==}
+ '@cropper/element@2.0.1':
+ resolution: {integrity: sha512-Jn1hR7XWzWQM/QfXRGMGzdkJ2gG/UcLdQPZQ7OKs0JiFfRzKpzu4u/nYrXHeH3MM2iOslLqh2kqYju6mjZLMJQ==}
- '@cropper/elements@2.0.0':
- resolution: {integrity: sha512-PQkPo1nUjxLFUQuHYu+6atfHxpX9B41Xribao6wpvmvmNIFML6LQdNqqWYb6LyM7ujsu71CZdBiMT5oetjJVoQ==}
+ '@cropper/elements@2.0.1':
+ resolution: {integrity: sha512-paFbBLXTKXNngn1yDi2ZIf+FO1pIEQXyBntmqOjuxqtG73KuEKv633wsJPFpj958bgcfSakgBbF80j+3nHbPug==}
- '@cropper/utils@2.0.0':
- resolution: {integrity: sha512-cprLYr+7kK3faGgoOsTW9gIn5sefDr2KwOmgyjzIXk+8PLpW8FgFKEg5FoWfRD5zMAmkCBuX6rGKDK3VdUEGrg==}
+ '@cropper/utils@2.0.1':
+ resolution: {integrity: sha512-A9RnAFmgNF5aZk5q2VZnFnHtXWu1kPyEN0LVsX8wJ2LBRu2nyETKwz+ZXVsVWliktToCaYojHKrS+6/HODyEZA==}
'@csstools/color-helpers@5.0.1':
resolution: {integrity: sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==}
@@ -1982,8 +2001,8 @@ packages:
resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==}
engines: {node: '>=18'}
- '@cypress/request@3.0.8':
- resolution: {integrity: sha512-h0NFgh1mJmm1nr4jCwkGHwKneVYKghUyWe6TMNrk0B9zsjAJxpg8C4/+BAcmLgCPa1vj1V8rNUaILl+zYRUWBQ==}
+ '@cypress/request@3.0.9':
+ resolution: {integrity: sha512-I3l7FdGRXluAS44/0NguwWlO83J18p0vlr2FYHrJkWdNYhgVoiYo61IXPqaOsL+vNxU1ZqMACzItGK3/KKDsdw==}
engines: {node: '>= 6'}
'@cypress/xvfb@1.2.4':
@@ -1993,317 +2012,323 @@ packages:
resolution: {integrity: sha512-Ahk1N+s7urkgj7WvvUND5f8GiWEPfUw0D41hdElaqLgu8wZScI8gdI0q+qWw5N1d35x7GCRH2uk9mi+Uzo9M3g==}
engines: {node: '>=14.0'}
- '@discordapp/twemoji@15.1.0':
- resolution: {integrity: sha512-QdpV4ifTONAXvDjRrMohausZeGrQ1ac/Ox6togUh6Xl3XKJ/KAaMMuAEi0qsb0wDwoVTSZBll5Y6+N3hB2ktBw==}
-
- '@emnapi/runtime@1.4.0':
- resolution: {integrity: sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw==}
+ '@discordapp/twemoji@16.0.1':
+ resolution: {integrity: sha512-figLiBWzjS5cyrAjLaGjM8AAaowO3qvK8rg5bA2dElB4qsaPMvBVlFDMO2d3x+nC1igt7kgWH4dvNmvvUHUF8w==}
'@emnapi/runtime@1.4.3':
resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==}
- '@esbuild/aix-ppc64@0.25.4':
- resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==}
+ '@epic-web/invariant@1.0.0':
+ resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==}
+
+ '@esbuild/aix-ppc64@0.25.8':
+ resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
- '@esbuild/aix-ppc64@0.25.6':
- resolution: {integrity: sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==}
+ '@esbuild/aix-ppc64@0.25.9':
+ resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
- '@esbuild/android-arm64@0.25.4':
- resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==}
+ '@esbuild/android-arm64@0.25.8':
+ resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
- '@esbuild/android-arm64@0.25.6':
- resolution: {integrity: sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==}
+ '@esbuild/android-arm64@0.25.9':
+ resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
- '@esbuild/android-arm@0.25.4':
- resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==}
+ '@esbuild/android-arm@0.25.8':
+ resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
- '@esbuild/android-arm@0.25.6':
- resolution: {integrity: sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==}
+ '@esbuild/android-arm@0.25.9':
+ resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
- '@esbuild/android-x64@0.25.4':
- resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==}
+ '@esbuild/android-x64@0.25.8':
+ resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
- '@esbuild/android-x64@0.25.6':
- resolution: {integrity: sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==}
+ '@esbuild/android-x64@0.25.9':
+ resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
- '@esbuild/darwin-arm64@0.25.4':
- resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==}
+ '@esbuild/darwin-arm64@0.25.8':
+ resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
- '@esbuild/darwin-arm64@0.25.6':
- resolution: {integrity: sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==}
+ '@esbuild/darwin-arm64@0.25.9':
+ resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
- '@esbuild/darwin-x64@0.25.4':
- resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==}
+ '@esbuild/darwin-x64@0.25.8':
+ resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
- '@esbuild/darwin-x64@0.25.6':
- resolution: {integrity: sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==}
+ '@esbuild/darwin-x64@0.25.9':
+ resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
- '@esbuild/freebsd-arm64@0.25.4':
- resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==}
+ '@esbuild/freebsd-arm64@0.25.8':
+ resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
- '@esbuild/freebsd-arm64@0.25.6':
- resolution: {integrity: sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==}
+ '@esbuild/freebsd-arm64@0.25.9':
+ resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
- '@esbuild/freebsd-x64@0.25.4':
- resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==}
+ '@esbuild/freebsd-x64@0.25.8':
+ resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
- '@esbuild/freebsd-x64@0.25.6':
- resolution: {integrity: sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==}
+ '@esbuild/freebsd-x64@0.25.9':
+ resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
- '@esbuild/linux-arm64@0.25.4':
- resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==}
+ '@esbuild/linux-arm64@0.25.8':
+ resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
- '@esbuild/linux-arm64@0.25.6':
- resolution: {integrity: sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==}
+ '@esbuild/linux-arm64@0.25.9':
+ resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
- '@esbuild/linux-arm@0.25.4':
- resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==}
+ '@esbuild/linux-arm@0.25.8':
+ resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
- '@esbuild/linux-arm@0.25.6':
- resolution: {integrity: sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==}
+ '@esbuild/linux-arm@0.25.9':
+ resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
- '@esbuild/linux-ia32@0.25.4':
- resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==}
+ '@esbuild/linux-ia32@0.25.8':
+ resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
- '@esbuild/linux-ia32@0.25.6':
- resolution: {integrity: sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==}
+ '@esbuild/linux-ia32@0.25.9':
+ resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
- '@esbuild/linux-loong64@0.25.4':
- resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==}
+ '@esbuild/linux-loong64@0.25.8':
+ resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
- '@esbuild/linux-loong64@0.25.6':
- resolution: {integrity: sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==}
+ '@esbuild/linux-loong64@0.25.9':
+ resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
- '@esbuild/linux-mips64el@0.25.4':
- resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==}
+ '@esbuild/linux-mips64el@0.25.8':
+ resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
- '@esbuild/linux-mips64el@0.25.6':
- resolution: {integrity: sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==}
+ '@esbuild/linux-mips64el@0.25.9':
+ resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
- '@esbuild/linux-ppc64@0.25.4':
- resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==}
+ '@esbuild/linux-ppc64@0.25.8':
+ resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
- '@esbuild/linux-ppc64@0.25.6':
- resolution: {integrity: sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==}
+ '@esbuild/linux-ppc64@0.25.9':
+ resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
- '@esbuild/linux-riscv64@0.25.4':
- resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==}
+ '@esbuild/linux-riscv64@0.25.8':
+ resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
- '@esbuild/linux-riscv64@0.25.6':
- resolution: {integrity: sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==}
+ '@esbuild/linux-riscv64@0.25.9':
+ resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
- '@esbuild/linux-s390x@0.25.4':
- resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==}
+ '@esbuild/linux-s390x@0.25.8':
+ resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
- '@esbuild/linux-s390x@0.25.6':
- resolution: {integrity: sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==}
+ '@esbuild/linux-s390x@0.25.9':
+ resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
- '@esbuild/linux-x64@0.25.4':
- resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==}
+ '@esbuild/linux-x64@0.25.8':
+ resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
- '@esbuild/linux-x64@0.25.6':
- resolution: {integrity: sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==}
+ '@esbuild/linux-x64@0.25.9':
+ resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
- '@esbuild/netbsd-arm64@0.25.4':
- resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==}
+ '@esbuild/netbsd-arm64@0.25.8':
+ resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
- '@esbuild/netbsd-arm64@0.25.6':
- resolution: {integrity: sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==}
+ '@esbuild/netbsd-arm64@0.25.9':
+ resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
- '@esbuild/netbsd-x64@0.25.4':
- resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==}
+ '@esbuild/netbsd-x64@0.25.8':
+ resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
- '@esbuild/netbsd-x64@0.25.6':
- resolution: {integrity: sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==}
+ '@esbuild/netbsd-x64@0.25.9':
+ resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
- '@esbuild/openbsd-arm64@0.25.4':
- resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==}
+ '@esbuild/openbsd-arm64@0.25.8':
+ resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
- '@esbuild/openbsd-arm64@0.25.6':
- resolution: {integrity: sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==}
+ '@esbuild/openbsd-arm64@0.25.9':
+ resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
- '@esbuild/openbsd-x64@0.25.4':
- resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==}
+ '@esbuild/openbsd-x64@0.25.8':
+ resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
- '@esbuild/openbsd-x64@0.25.6':
- resolution: {integrity: sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==}
+ '@esbuild/openbsd-x64@0.25.9':
+ resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
- '@esbuild/openharmony-arm64@0.25.6':
- resolution: {integrity: sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==}
+ '@esbuild/openharmony-arm64@0.25.8':
+ resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
- '@esbuild/sunos-x64@0.25.4':
- resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==}
+ '@esbuild/openharmony-arm64@0.25.9':
+ resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.25.8':
+ resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
- '@esbuild/sunos-x64@0.25.6':
- resolution: {integrity: sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==}
+ '@esbuild/sunos-x64@0.25.9':
+ resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
- '@esbuild/win32-arm64@0.25.4':
- resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==}
+ '@esbuild/win32-arm64@0.25.8':
+ resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
- '@esbuild/win32-arm64@0.25.6':
- resolution: {integrity: sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==}
+ '@esbuild/win32-arm64@0.25.9':
+ resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
- '@esbuild/win32-ia32@0.25.4':
- resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==}
+ '@esbuild/win32-ia32@0.25.8':
+ resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
- '@esbuild/win32-ia32@0.25.6':
- resolution: {integrity: sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==}
+ '@esbuild/win32-ia32@0.25.9':
+ resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
- '@esbuild/win32-x64@0.25.4':
- resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==}
+ '@esbuild/win32-x64@0.25.8':
+ resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
- '@esbuild/win32-x64@0.25.6':
- resolution: {integrity: sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==}
+ '@esbuild/win32-x64@0.25.9':
+ resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
@@ -2326,32 +2351,28 @@ packages:
resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/config-helpers@0.3.0':
- resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
- '@eslint/core@0.14.0':
- resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==}
+ '@eslint/config-helpers@0.3.1':
+ resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/core@0.15.1':
- resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==}
+ '@eslint/core@0.15.2':
+ resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/eslintrc@3.3.1':
resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/js@9.31.0':
- resolution: {integrity: sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==}
+ '@eslint/js@9.34.0':
+ resolution: {integrity: sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/object-schema@2.1.6':
resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/plugin-kit@0.3.1':
- resolution: {integrity: sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==}
+ '@eslint/plugin-kit@0.3.5':
+ resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@fastify/accept-negotiator@2.0.0':
@@ -2705,8 +2726,8 @@ packages:
peerDependencies:
'@types/node': '>=18'
- '@ioredis/commands@1.2.0':
- resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==}
+ '@ioredis/commands@1.3.0':
+ resolution: {integrity: sha512-M/T6Zewn7sDaBQEqIZ8Rb+i9y8qfGmq+5SDFSf9sA2lUZTmdDLVdOiQaeDp+Q4wElZ9HG1GAX5KhDaidp6LQsQ==}
'@isaacs/balanced-match@4.0.1':
resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
@@ -2745,12 +2766,8 @@ packages:
node-notifier:
optional: true
- '@jest/create-cache-key-function@29.7.0':
- resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==}
- engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-
- '@jest/create-cache-key-function@30.0.2':
- resolution: {integrity: sha512-AwlDAHwEHDi+etw9vKWx9HeIApVos8GD/sSTpHtDkqhm9OWuEUPKKPP6EaS17yv0GSzBB3TeeJFLyJ5LPjRqWg==}
+ '@jest/create-cache-key-function@30.0.5':
+ resolution: {integrity: sha512-W1kmkwPq/WTMQWgvbzWSCbXSqvjI6rkqBQCxuvYmd+g6o4b5gHP98ikfh/Ei0SKzHvWdI84TOXp0hRcbpr8Q0w==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
'@jest/environment@29.7.0':
@@ -2790,8 +2807,8 @@ packages:
resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
- '@jest/schemas@30.0.1':
- resolution: {integrity: sha512-+g/1TKjFuGrf1Hh0QPCv0gISwBxJ+MQSNXmG9zjHy7BmFhtoJ9fdNhWJp3qUKRi93AOZHXtdxZgJ1vAtz6z65w==}
+ '@jest/schemas@30.0.5':
+ resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
'@jest/source-map@29.6.3':
@@ -2814,42 +2831,37 @@ packages:
resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
- '@jest/types@30.0.1':
- resolution: {integrity: sha512-HGwoYRVF0QSKJu1ZQX0o5ZrUrrhj0aOOFA8hXrumD7SIzjouevhawbTjmXdwOmURdGluU9DM/XvGm3NyFoiQjw==}
+ '@jest/types@30.0.5':
+ resolution: {integrity: sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
- '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0':
- resolution: {integrity: sha512-qYDdL7fPwLRI+bJNurVcis+tNgJmvWjH4YTBGXTA8xMuxFrnAz6E5o35iyzyKbq5J5Lr8mJGfrR5GXl+WGwhgQ==}
+ '@joshwooding/vite-plugin-react-docgen-typescript@0.6.1':
+ resolution: {integrity: sha512-J4BaTocTOYFkMHIra1JDWrMWpNmBl4EkplIwHEsV8aeUOtdWjwSnln9U7twjMFTAEB7mptNtSKyVi1Y2W9sDJw==}
peerDependencies:
typescript: '>= 4.3.x'
- vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0
+ vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
peerDependenciesMeta:
typescript:
optional: true
- '@jridgewell/gen-mapping@0.3.5':
- resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
- engines: {node: '>=6.0.0'}
+ '@jridgewell/gen-mapping@0.3.12':
+ resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==}
'@jridgewell/resolve-uri@3.1.0':
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
engines: {node: '>=6.0.0'}
- '@jridgewell/set-array@1.2.1':
- resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
- engines: {node: '>=6.0.0'}
-
'@jridgewell/source-map@0.3.6':
resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
'@jridgewell/sourcemap-codec@1.5.0':
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
- '@jridgewell/trace-mapping@0.3.25':
- resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
- '@jsdevtools/ono@7.1.3':
- resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
+ '@jridgewell/trace-mapping@0.3.29':
+ resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==}
'@kurkle/color@0.3.2':
resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==}
@@ -2881,11 +2893,11 @@ packages:
'@types/react': '>=16'
react: '>=16'
- '@microsoft/api-extractor-model@7.30.6':
- resolution: {integrity: sha512-znmFn69wf/AIrwHya3fxX6uB5etSIn6vg4Q4RB/tb5VDDs1rqREc+AvMC/p19MUN13CZ7+V/8pkYPTj7q8tftg==}
+ '@microsoft/api-extractor-model@7.30.7':
+ resolution: {integrity: sha512-TBbmSI2/BHpfR9YhQA7nH0nqVmGgJ0xH0Ex4D99/qBDAUpnhA2oikGmdXanbw9AWWY/ExBYIpkmY8dBHdla3YQ==}
- '@microsoft/api-extractor@7.52.8':
- resolution: {integrity: sha512-cszYIcjiNscDoMB1CIKZ3My61+JOhpERGlGr54i6bocvGLrcL/wo9o+RNXMBrb7XgLtKaizZWUpqRduQuHQLdg==}
+ '@microsoft/api-extractor@7.52.11':
+ resolution: {integrity: sha512-IKQ7bHg6f/Io3dQds6r9QPYk4q0OlR9A4nFDtNhUt3UUIhyitbxAqRN1CLjUVtk6IBk3xzyCMOdwwtIXQ7AlGg==}
hasBin: true
'@microsoft/tsdoc-config@0.17.1':
@@ -2911,11 +2923,8 @@ packages:
'@misskey-dev/sharp-read-bmp@1.2.0':
resolution: {integrity: sha512-er4pRakXzHYfEgOFAFfQagqDouG+wLm+kwNq1I30oSdIHDa0wM3KjFpfIGQ25Fks4GcmOl1s7Zh6xoQu5dNjTw==}
- '@misskey-dev/summaly@5.2.1':
- resolution: {integrity: sha512-fcFd7ssHAghRntewRROOpRxv+VH18uz85Kzg6pZK1EFyqPOXxf39ErRA9HnJSzPYQT6KJTqBWuKHbCGoFlceXg==}
-
- '@misskey-dev/summaly@5.2.2':
- resolution: {integrity: sha512-GIKnosbTJ/me2LgqQbxk35tsbzSJUMRwlTeJGxPylGcOFlDYdHeAuiF/qFfCzlzpVzO+oisKvazZkTMY9cGShA==}
+ '@misskey-dev/summaly@5.2.3':
+ resolution: {integrity: sha512-TvJMrF2VKTK9fkD8nKNHp8PBHUwJqKOhFs4S7uwhoXpkpxpI/w4RBWbr7Gjg9xV0k9TL288N39OML+XaJCaL9Q==}
'@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2':
resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==}
@@ -2951,72 +2960,72 @@ packages:
resolution: {integrity: sha512-RuzCup9Ct91Y7V79xwCb146RaBRHZ7NBbrIUySumd1rpKqHL5OonaqrGIbug5hNwP/fRyxFMA6ISgw4FTtYFYg==}
engines: {node: '>=18'}
- '@napi-rs/canvas-android-arm64@0.1.71':
- resolution: {integrity: sha512-cxi3VCotIOS9kNFQI7dcysbVJi106pxryVY1Hi85pX+ZeqahRyeqc/NsLaZ998Ae99+F3HI5X/39G1Y/Byrf0A==}
+ '@napi-rs/canvas-android-arm64@0.1.77':
+ resolution: {integrity: sha512-jC8YX0rbAnu9YrLK1A52KM2HX9EDjrJSCLVuBf9Dsov4IC6GgwMLS2pwL9GFLJnSZBFgdwnA84efBehHT9eshA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
- '@napi-rs/canvas-darwin-arm64@0.1.71':
- resolution: {integrity: sha512-7Y4D/6vIuMLYsVNtRM/w2j0+fB1GyqeOxc7I0BTx8eLP1S6BZE2Rj6zJfdG+zmLEOW0IlHa+VQq1q2MUAjW84w==}
+ '@napi-rs/canvas-darwin-arm64@0.1.77':
+ resolution: {integrity: sha512-VFaCaCgAV0+hPwXajDIiHaaGx4fVCuUVYp/CxCGXmTGz699ngIEBx3Sa2oDp0uk3X+6RCRLueb7vD44BKBiPIg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
- '@napi-rs/canvas-darwin-x64@0.1.71':
- resolution: {integrity: sha512-Z0IUqxclrYdfVt/SK9nKCzUHTOXKTWiygtO71YCzs0OtxKdNI7GJRJdYG48wXZEDQ/pqTF4F7Ifgtidfc2tYpg==}
+ '@napi-rs/canvas-darwin-x64@0.1.77':
+ resolution: {integrity: sha512-uD2NSkf6I4S3o0POJDwweK85FE4rfLNA2N714MgiEEMMw5AmupfSJGgpYzcyEXtPzdaca6rBfKcqNvzR1+EyLQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
- '@napi-rs/canvas-linux-arm-gnueabihf@0.1.71':
- resolution: {integrity: sha512-KlpqqCASak5ruY+UIolJgmhMZ9Pa2o1QyaNu648L8sz4WNBbNa+aOT60XCLCL1VIKLv11B3MlNgiOHoYNmDhXQ==}
+ '@napi-rs/canvas-linux-arm-gnueabihf@0.1.77':
+ resolution: {integrity: sha512-03GxMMZGhHRQxiA4gyoKT6iQSz8xnA6T9PAfg/WNJnbkVMFZG782DwUJUb39QIZ1uE1euMCPnDgWAJ092MmgJQ==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
- '@napi-rs/canvas-linux-arm64-gnu@0.1.71':
- resolution: {integrity: sha512-bdGZCGu8YQNAiu3nkIVVUp6nIn6fPd36IuZsLXTG027E52KyIuZ3obCxehSwjDIUNkFWvmff5D6JYfWwAoioEw==}
+ '@napi-rs/canvas-linux-arm64-gnu@0.1.77':
+ resolution: {integrity: sha512-ZO+d2gRU9JU1Bb7SgJcJ1k9wtRMCpSWjJAJ+2phhu0Lw5As8jYXXXmLKmMTGs1bOya2dBMYDLzwp7KS/S/+aCA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@napi-rs/canvas-linux-arm64-musl@0.1.71':
- resolution: {integrity: sha512-1R5sMWe9ur8uM+hAeylBwG0b6UHDR+iWQNgzXmF9vbBYRooQvmDWqpcgytKLJAC0vnWhIkKwqd7yExn7cwczmg==}
+ '@napi-rs/canvas-linux-arm64-musl@0.1.77':
+ resolution: {integrity: sha512-S1KtnP1+nWs2RApzNkdNf8X4trTLrHaY7FivV61ZRaL8NvuGOkSkKa+gWN2iedIGFEDz6gecpl/JAUSewwFXYg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@napi-rs/canvas-linux-riscv64-gnu@0.1.71':
- resolution: {integrity: sha512-xjjKsipueuG+LdKIk6/uAlqdo+rzGcmNpTZPXdakIT1sHX4NNSnQTzjRaj9Gh96Czjd9G89UWR0KIlE7fwOgFA==}
+ '@napi-rs/canvas-linux-riscv64-gnu@0.1.77':
+ resolution: {integrity: sha512-A4YIKFYUwDtrSzCtdCAO5DYmRqlhCVKHdpq0+dBGPnIEhOQDFkPBTfoTAjO3pjlEnorlfKmNMOH21sKQg2esGA==}
engines: {node: '>= 10'}
cpu: [riscv64]
os: [linux]
- '@napi-rs/canvas-linux-x64-gnu@0.1.71':
- resolution: {integrity: sha512-3s6YpklXDB4OeeULG1XTRyKrKAOo7c3HHEqM9A6N4STSjMaJtzmpp7tB/JTvAFeOeFte6gWN8IwC+7AjGJ6MpQ==}
+ '@napi-rs/canvas-linux-x64-gnu@0.1.77':
+ resolution: {integrity: sha512-Lt6Sef5l0+5O1cSZ8ysO0JI+x+rSrqZyXs5f7+kVkCAOVq8X5WTcDVbvWvEs2aRhrWTp5y25Jf2Bn+3IcNHOuQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@napi-rs/canvas-linux-x64-musl@0.1.71':
- resolution: {integrity: sha512-5v9aCLzCXw7u10ray5juQMdl7TykZSn1X5AIGYwBvTAcKSgrqaR9QkRxp1Lqk3njQmFekOW1SFN9bZ/i/6y6kA==}
+ '@napi-rs/canvas-linux-x64-musl@0.1.77':
+ resolution: {integrity: sha512-NiNFvC+D+omVeJ3IjYlIbyt/igONSABVe9z0ZZph29epHgZYu4eHwV9osfpRt1BGGOAM8LkFrHk4LBdn2EDymA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@napi-rs/canvas-win32-x64-msvc@0.1.71':
- resolution: {integrity: sha512-oJughk6xjsRIr0Rd9EqjmZmhIMkvcPuXgr3MNn2QexTqn+YFOizrwHS5ha0BDfFl7TEGRvwaDUXBQtu8JKXb8A==}
+ '@napi-rs/canvas-win32-x64-msvc@0.1.77':
+ resolution: {integrity: sha512-fP6l0hZiWykyjvpZTS3sI46iib8QEflbPakNoUijtwyxRuOPTTBfzAWZUz5z2vKpJJ/8r305wnZeZ8lhsBHY5A==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
- '@napi-rs/canvas@0.1.71':
- resolution: {integrity: sha512-92ybDocKl6JM48ZpYbj+A7Qt45IaTABDk0y3sDecEQfgdhfNzJtEityqNHoCZ4Vty2dldPkJhxgvOnbrQMXTTA==}
+ '@napi-rs/canvas@0.1.77':
+ resolution: {integrity: sha512-N9w2DkEKE1AXGp3q55GBOP6BEoFrqChDiFqJtKViTpQCWNOSVuMz7LkoGehbnpxtidppbsC36P0kCZNqJKs29w==}
engines: {node: '>= 10'}
- '@nestjs/common@11.1.3':
- resolution: {integrity: sha512-ogEK+GriWodIwCw6buQ1rpcH4Kx+G7YQ9EwuPySI3rS05pSdtQ++UhucjusSI9apNidv+QURBztJkRecwwJQXg==}
+ '@nestjs/common@11.1.6':
+ resolution: {integrity: sha512-krKwLLcFmeuKDqngG2N/RuZHCs2ycsKcxWIDgcm7i1lf3sQ0iG03ci+DsP/r3FcT/eJDFsIHnKtNta2LIi7PzQ==}
peerDependencies:
class-transformer: '>=0.4.1'
class-validator: '>=0.13.2'
@@ -3028,8 +3037,8 @@ packages:
class-validator:
optional: true
- '@nestjs/core@11.1.3':
- resolution: {integrity: sha512-5lTni0TCh8x7bXETRD57pQFnKnEg1T6M+VLE7wAmyQRIecKQU+2inRGZD+A4v2DC1I04eA0WffP0GKLxjOKlzw==}
+ '@nestjs/core@11.1.6':
+ resolution: {integrity: sha512-siWX7UDgErisW18VTeJA+x+/tpNZrJewjTBsRPF3JVxuWRuAB1kRoiJcxHgln8Lb5UY9NdvklITR84DUEXD0Cg==}
engines: {node: '>= 20'}
peerDependencies:
'@nestjs/common': ^11.0.0
@@ -3046,14 +3055,14 @@ packages:
'@nestjs/websockets':
optional: true
- '@nestjs/platform-express@10.4.19':
- resolution: {integrity: sha512-IeQkBZUtPeJoO4E0QqSLwkB+60KcThw8/s4gGvAwIRJ5ViuXoxnwU59eBDy84PUuVbNe4VdKjfAF9fuQOEh11Q==}
+ '@nestjs/platform-express@10.4.20':
+ resolution: {integrity: sha512-rh97mX3rimyf4xLMLHuTOBKe6UD8LOJ14VlJ1F/PTd6C6ZK9Ak6EHuJvdaGcSFQhd3ZMBh3I6CuujKGW9pNdIg==}
peerDependencies:
'@nestjs/common': ^10.0.0
'@nestjs/core': ^10.0.0
- '@nestjs/testing@11.1.3':
- resolution: {integrity: sha512-CeXG6/eEqgFIkPkmU00y18Dd3DLOIDFhPItzJK1SWckKo6IhcnfoRJzGx75bmuvUMjb51j6An96S/+MJ2ty9jA==}
+ '@nestjs/testing@11.1.6':
+ resolution: {integrity: sha512-srYzzDNxGvVCe1j0SpTS9/ix75PKt6Sn6iMaH1rpJ6nj2g8vwNrhK0CoJJXvpCYgrnI+2WES2pprYnq8rAMYHA==}
peerDependencies:
'@nestjs/common': ^11.0.0
'@nestjs/core': ^11.0.0
@@ -3069,6 +3078,10 @@ packages:
resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==}
engines: {node: ^14.21.3 || >=16}
+ '@noble/hashes@1.8.0':
+ resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
+ engines: {node: ^14.21.3 || >=16}
+
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -3437,19 +3450,15 @@ packages:
'@prisma/instrumentation@5.22.0':
resolution: {integrity: sha512-LxccF392NN37ISGxIurUljZSh1YWnphO34V5a0+T7FVQG2u9bhAXRTJpgmQ3483woVhkraQZFF7cbRrpbw/F4Q==}
- '@readme/better-ajv-errors@2.2.2':
- resolution: {integrity: sha512-YBWor3QZhavGHqTEHJfYfk2e0UczQaImc0MIIJ5JNJrns4HKm94pwUdjkPVZO2eZJBt/Rzs/D8ZrdvFFwRh0zQ==}
+ '@readme/better-ajv-errors@2.3.2':
+ resolution: {integrity: sha512-T4GGnRAlY3C339NhoUpgJJFsMYko9vIgFAlhgV+/vEGFw66qEY4a4TRJIAZBcX/qT1pq5DvXSme+SQODHOoBrw==}
engines: {node: '>=18'}
peerDependencies:
ajv: 4.11.8 - 8
- '@readme/json-schema-ref-parser@1.2.0':
- resolution: {integrity: sha512-Bt3QVovFSua4QmHa65EHUmh2xS0XJ3rgTEUPH998f4OW4VVJke3BuS16f+kM0ZLOGdvIrzrPRqwihuv5BAjtrA==}
- deprecated: This package is no longer maintained. Please use `@apidevtools/json-schema-ref-parser` instead.
-
- '@readme/openapi-parser@2.7.0':
- resolution: {integrity: sha512-P8WSr8WTOxilnT89tcCRKWYsG/II4sAwt1a/DIWub8xTtkrG9cCBBy/IUcvc5X8oGWN82MwcTA3uEkDrXZd/7A==}
- engines: {node: '>=18'}
+ '@readme/openapi-parser@5.0.1':
+ resolution: {integrity: sha512-qLHoqkBcqHmCFqLNuK9vH2DiOg+RM4Jk4ncgm6BkW1h796NAPqY6i4ux90PsgM19c4LVcZKgrEp+/oEGAAmCmA==}
+ engines: {node: '>=20'}
peerDependencies:
openapi-types: '>=7'
@@ -3463,10 +3472,13 @@ packages:
'@redocly/config@0.22.2':
resolution: {integrity: sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==}
- '@redocly/openapi-core@1.34.3':
- resolution: {integrity: sha512-3arRdUp1fNx55itnjKiUhO6t4Mf91TsrTIYINDNLAZPS0TPd5YpiXRctwjel0qqWoOOhjA34cZ3m4dksLDFUYg==}
+ '@redocly/openapi-core@1.34.5':
+ resolution: {integrity: sha512-0EbE8LRbkogtcCXU7liAyC00n9uNG9hJ+eMyHFdUsy9lB/WGqnEBgwjA9q2cyzAVcdTkQqTBBU1XePNnN3OijA==}
engines: {node: '>=18.17.0', npm: '>=9.5.0'}
+ '@rolldown/pluginutils@1.0.0-beta.29':
+ resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==}
+
'@rollup/plugin-json@6.1.0':
resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==}
engines: {node: '>=14.0.0'}
@@ -3494,111 +3506,211 @@ packages:
rollup:
optional: true
- '@rollup/rollup-android-arm-eabi@4.45.1':
- resolution: {integrity: sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==}
+ '@rollup/rollup-android-arm-eabi@4.46.2':
+ resolution: {integrity: sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==}
cpu: [arm]
os: [android]
- '@rollup/rollup-android-arm64@4.45.1':
- resolution: {integrity: sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==}
+ '@rollup/rollup-android-arm-eabi@4.48.0':
+ resolution: {integrity: sha512-aVzKH922ogVAWkKiyKXorjYymz2084zrhrZRXtLrA5eEx5SO8Dj0c/4FpCHZyn7MKzhW2pW4tK28vVr+5oQ2xw==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.46.2':
+ resolution: {integrity: sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==}
cpu: [arm64]
os: [android]
- '@rollup/rollup-darwin-arm64@4.45.1':
- resolution: {integrity: sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==}
+ '@rollup/rollup-android-arm64@4.48.0':
+ resolution: {integrity: sha512-diOdQuw43xTa1RddAFbhIA8toirSzFMcnIg8kvlzRbK26xqEnKJ/vqQnghTAajy2Dcy42v+GMPMo6jq67od+Dw==}
cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.46.2':
+ resolution: {integrity: sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-arm64@4.48.0':
+ resolution: {integrity: sha512-QhR2KA18fPlJWFefySJPDYZELaVqIUVnYgAOdtJ+B/uH96CFg2l1TQpX19XpUMWUqMyIiyY45wje8K6F4w4/CA==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.46.2':
+ resolution: {integrity: sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==}
+ cpu: [x64]
os: [darwin]
- '@rollup/rollup-darwin-x64@4.45.1':
- resolution: {integrity: sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==}
+ '@rollup/rollup-darwin-x64@4.48.0':
+ resolution: {integrity: sha512-Q9RMXnQVJ5S1SYpNSTwXDpoQLgJ/fbInWOyjbCnnqTElEyeNvLAB3QvG5xmMQMhFN74bB5ZZJYkKaFPcOG8sGg==}
cpu: [x64]
os: [darwin]
- '@rollup/rollup-freebsd-arm64@4.45.1':
- resolution: {integrity: sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==}
+ '@rollup/rollup-freebsd-arm64@4.46.2':
+ resolution: {integrity: sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-arm64@4.48.0':
+ resolution: {integrity: sha512-3jzOhHWM8O8PSfyft+ghXZfBkZawQA0PUGtadKYxFqpcYlOYjTi06WsnYBsbMHLawr+4uWirLlbhcYLHDXR16w==}
cpu: [arm64]
os: [freebsd]
- '@rollup/rollup-freebsd-x64@4.45.1':
- resolution: {integrity: sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==}
+ '@rollup/rollup-freebsd-x64@4.46.2':
+ resolution: {integrity: sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==}
cpu: [x64]
os: [freebsd]
- '@rollup/rollup-linux-arm-gnueabihf@4.45.1':
- resolution: {integrity: sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==}
+ '@rollup/rollup-freebsd-x64@4.48.0':
+ resolution: {integrity: sha512-NcD5uVUmE73C/TPJqf78hInZmiSBsDpz3iD5MF/BuB+qzm4ooF2S1HfeTChj5K4AV3y19FFPgxonsxiEpy8v/A==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.46.2':
+ resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.48.0':
+ resolution: {integrity: sha512-JWnrj8qZgLWRNHr7NbpdnrQ8kcg09EBBq8jVOjmtlB3c8C6IrynAJSMhMVGME4YfTJzIkJqvSUSVJRqkDnu/aA==}
cpu: [arm]
os: [linux]
- '@rollup/rollup-linux-arm-musleabihf@4.45.1':
- resolution: {integrity: sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==}
+ '@rollup/rollup-linux-arm-musleabihf@4.46.2':
+ resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==}
cpu: [arm]
os: [linux]
- '@rollup/rollup-linux-arm64-gnu@4.45.1':
- resolution: {integrity: sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==}
+ '@rollup/rollup-linux-arm-musleabihf@4.48.0':
+ resolution: {integrity: sha512-9xu92F0TxuMH0tD6tG3+GtngwdgSf8Bnz+YcsPG91/r5Vgh5LNofO48jV55priA95p3c92FLmPM7CvsVlnSbGQ==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.46.2':
+ resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.48.0':
+ resolution: {integrity: sha512-NLtvJB5YpWn7jlp1rJiY0s+G1Z1IVmkDuiywiqUhh96MIraC0n7XQc2SZ1CZz14shqkM+XN2UrfIo7JB6UufOA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.46.2':
+ resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==}
cpu: [arm64]
os: [linux]
- '@rollup/rollup-linux-arm64-musl@4.45.1':
- resolution: {integrity: sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==}
+ '@rollup/rollup-linux-arm64-musl@4.48.0':
+ resolution: {integrity: sha512-QJ4hCOnz2SXgCh+HmpvZkM+0NSGcZACyYS8DGbWn2PbmA0e5xUk4bIP8eqJyNXLtyB4gZ3/XyvKtQ1IFH671vQ==}
cpu: [arm64]
os: [linux]
- '@rollup/rollup-linux-loongarch64-gnu@4.45.1':
- resolution: {integrity: sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==}
+ '@rollup/rollup-linux-loongarch64-gnu@4.46.2':
+ resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==}
cpu: [loong64]
os: [linux]
- '@rollup/rollup-linux-powerpc64le-gnu@4.45.1':
- resolution: {integrity: sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==}
+ '@rollup/rollup-linux-loongarch64-gnu@4.48.0':
+ resolution: {integrity: sha512-Pk0qlGJnhILdIC5zSKQnprFjrGmjfDM7TPZ0FKJxRkoo+kgMRAg4ps1VlTZf8u2vohSicLg7NP+cA5qE96PaFg==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.46.2':
+ resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.48.0':
+ resolution: {integrity: sha512-/dNFc6rTpoOzgp5GKoYjT6uLo8okR/Chi2ECOmCZiS4oqh3mc95pThWma7Bgyk6/WTEvjDINpiBCuecPLOgBLQ==}
cpu: [ppc64]
os: [linux]
- '@rollup/rollup-linux-riscv64-gnu@4.45.1':
- resolution: {integrity: sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==}
+ '@rollup/rollup-linux-riscv64-gnu@4.46.2':
+ resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==}
cpu: [riscv64]
os: [linux]
- '@rollup/rollup-linux-riscv64-musl@4.45.1':
- resolution: {integrity: sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==}
+ '@rollup/rollup-linux-riscv64-gnu@4.48.0':
+ resolution: {integrity: sha512-YBwXsvsFI8CVA4ej+bJF2d9uAeIiSkqKSPQNn0Wyh4eMDY4wxuSp71BauPjQNCKK2tD2/ksJ7uhJ8X/PVY9bHQ==}
cpu: [riscv64]
os: [linux]
- '@rollup/rollup-linux-s390x-gnu@4.45.1':
- resolution: {integrity: sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==}
+ '@rollup/rollup-linux-riscv64-musl@4.46.2':
+ resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-musl@4.48.0':
+ resolution: {integrity: sha512-FI3Rr2aGAtl1aHzbkBIamsQyuauYtTF9SDUJ8n2wMXuuxwchC3QkumZa1TEXYIv/1AUp1a25Kwy6ONArvnyeVQ==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.46.2':
+ resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==}
cpu: [s390x]
os: [linux]
- '@rollup/rollup-linux-x64-gnu@4.45.1':
- resolution: {integrity: sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==}
+ '@rollup/rollup-linux-s390x-gnu@4.48.0':
+ resolution: {integrity: sha512-Dx7qH0/rvNNFmCcIRe1pyQ9/H0XO4v/f0SDoafwRYwc2J7bJZ5N4CHL/cdjamISZ5Cgnon6iazAVRFlxSoHQnQ==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.46.2':
+ resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.48.0':
+ resolution: {integrity: sha512-GUdZKTeKBq9WmEBzvFYuC88yk26vT66lQV8D5+9TgkfbewhLaTHRNATyzpQwwbHIfJvDJ3N9WJ90wK/uR3cy3Q==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.46.2':
+ resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==}
cpu: [x64]
os: [linux]
- '@rollup/rollup-linux-x64-musl@4.45.1':
- resolution: {integrity: sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==}
+ '@rollup/rollup-linux-x64-musl@4.48.0':
+ resolution: {integrity: sha512-ao58Adz/v14MWpQgYAb4a4h3fdw73DrDGtaiF7Opds5wNyEQwtO6M9dBh89nke0yoZzzaegq6J/EXs7eBebG8A==}
cpu: [x64]
os: [linux]
- '@rollup/rollup-win32-arm64-msvc@4.45.1':
- resolution: {integrity: sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==}
+ '@rollup/rollup-win32-arm64-msvc@4.46.2':
+ resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==}
cpu: [arm64]
os: [win32]
- '@rollup/rollup-win32-ia32-msvc@4.45.1':
- resolution: {integrity: sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==}
+ '@rollup/rollup-win32-arm64-msvc@4.48.0':
+ resolution: {integrity: sha512-kpFno46bHtjZVdRIOxqaGeiABiToo2J+st7Yce+aiAoo1H0xPi2keyQIP04n2JjDVuxBN6bSz9R6RdTK5hIppw==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.46.2':
+ resolution: {integrity: sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.48.0':
+ resolution: {integrity: sha512-rFYrk4lLk9YUTIeihnQMiwMr6gDhGGSbWThPEDfBoU/HdAtOzPXeexKi7yU8jO+LWRKnmqPN9NviHQf6GDwBcQ==}
cpu: [ia32]
os: [win32]
- '@rollup/rollup-win32-x64-msvc@4.45.1':
- resolution: {integrity: sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==}
+ '@rollup/rollup-win32-x64-msvc@4.46.2':
+ resolution: {integrity: sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==}
+ cpu: [x64]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.48.0':
+ resolution: {integrity: sha512-sq0hHLTgdtwOPDB5SJOuaoHyiP1qSwg+71TQWk8iDS04bW1wIE0oQ6otPiRj2ZvLYNASLMaTp8QRGUVZ+5OL5A==}
cpu: [x64]
os: [win32]
'@rtsao/scc@1.1.0':
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
- '@rushstack/node-core-library@5.13.1':
- resolution: {integrity: sha512-5yXhzPFGEkVc9Fu92wsNJ9jlvdwz4RNb2bMso+/+TH0nMm1jDDDsOIf4l8GAkPxGuwPw5DH24RliWVfSPhlW/Q==}
+ '@rushstack/node-core-library@5.14.0':
+ resolution: {integrity: sha512-eRong84/rwQUlATGFW3TMTYVyqL1vfW9Lf10PH+mVGfIb9HzU3h5AASNIw+axnBLjnD0n3rT5uQBwu9fvzATrg==}
peerDependencies:
'@types/node': '*'
peerDependenciesMeta:
@@ -3608,70 +3720,70 @@ packages:
'@rushstack/rig-package@0.5.3':
resolution: {integrity: sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==}
- '@rushstack/terminal@0.15.3':
- resolution: {integrity: sha512-DGJ0B2Vm69468kZCJkPj3AH5nN+nR9SPmC0rFHtzsS4lBQ7/dgOwtwVxYP7W9JPDMuRBkJ4KHmWKr036eJsj9g==}
+ '@rushstack/terminal@0.15.4':
+ resolution: {integrity: sha512-OQSThV0itlwVNHV6thoXiAYZlQh4Fgvie2CzxFABsbO2MWQsI4zOh3LRNigYSTrmS+ba2j0B3EObakPzf/x6Zg==}
peerDependencies:
'@types/node': '*'
peerDependenciesMeta:
'@types/node':
optional: true
- '@rushstack/ts-command-line@5.0.1':
- resolution: {integrity: sha512-bsbUucn41UXrQK7wgM8CNM/jagBytEyJqXw/umtI8d68vFm1Jwxh1OtLrlW7uGZgjCWiiPH6ooUNa1aVsuVr3Q==}
+ '@rushstack/ts-command-line@5.0.2':
+ resolution: {integrity: sha512-+AkJDbu1GFMPIU8Sb7TLVXDv/Q7Mkvx+wAjEl8XiXVVq+p1FmWW6M3LYpJMmoHNckSofeMecgWg5lfMwNAAsEQ==}
'@sec-ant/readable-stream@0.4.1':
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
- '@sentry-internal/browser-utils@9.28.0':
- resolution: {integrity: sha512-SqntPnIXudP3FoKj4mQ1BVPC1RNzo4CGtAxJnLpbIUpdT/khJVM6Q59zrGl2MgZ7URZCI986L5jXihQeferf6g==}
+ '@sentry-internal/browser-utils@10.5.0':
+ resolution: {integrity: sha512-4KIJdEj/8Ip9yqJleVSFe68r/U5bn5o/lYUwnFNEnDNxmpUbOlr7x3DXYuRFi1sfoMUxK9K1DrjnBkR7YYF00g==}
+ engines: {node: '>=18'}
+
+ '@sentry-internal/browser-utils@9.46.0':
+ resolution: {integrity: sha512-Q0CeHym9wysku8mYkORXmhtlBE0IrafAI+NiPSqxOBKXGOCWKVCvowHuAF56GwPFic2rSrRnub5fWYv7T1jfEQ==}
engines: {node: '>=18'}
- '@sentry-internal/browser-utils@9.39.0':
- resolution: {integrity: sha512-f1qUAftQ/aMTJHJDCG9jDJIwLEkSVZ6xWGn2xQMj7PfD1uuYlTy3KHn+wPy1VlPJTZQ44WclTPnY2U/OBIPmsg==}
+ '@sentry-internal/feedback@10.5.0':
+ resolution: {integrity: sha512-x79P4VZwUxb1EGZb9OQ5EEgrDWFCUlrbzHBwV/oocQA5Ss1SFz5u6cP5Ak7yJtILiJtdGzAyAoQOy4GKD13D4Q==}
engines: {node: '>=18'}
- '@sentry-internal/feedback@9.28.0':
- resolution: {integrity: sha512-z2jShmVENsesmDnShEOv841Saw0zXe1tX6GHNgkK9f6NrUMbL970JvGKByBFTffhQH6uQ0WeNPnXJ5L/YKnfDg==}
+ '@sentry-internal/feedback@9.46.0':
+ resolution: {integrity: sha512-KLRy3OolDkGdPItQ3obtBU2RqDt9+KE8z7r7Gsu7c6A6A89m8ZVlrxee3hPQt6qp0YY0P8WazpedU3DYTtaT8w==}
engines: {node: '>=18'}
- '@sentry-internal/feedback@9.39.0':
- resolution: {integrity: sha512-IvD2LDKFderPn6pXN6N8cFJWWIXUADys9WRtfh1CK8DIuU1fXYi61tO23Z+sCsvXReQgr/mdJg5dHa0qY/vDww==}
+ '@sentry-internal/replay-canvas@10.5.0':
+ resolution: {integrity: sha512-5nrRKd5swefd9+sFXFZ/NeL3bz/VxBls3ubAQ3afak15FikkSyHq3oKRKpMOtDsiYKXE3Bc0y3rF5A+y3OXjIA==}
engines: {node: '>=18'}
- '@sentry-internal/replay-canvas@9.28.0':
- resolution: {integrity: sha512-Bv4mbtUrRV3p6PpFQPseLv3+Uaen+3AlfX02Z6QHY1sMa4lpt+U8OHfRGLprnzb6Rarw6fK2LNVL5rnV9LNMwA==}
+ '@sentry-internal/replay-canvas@9.46.0':
+ resolution: {integrity: sha512-QcBjrdRWFJrrrjbmrr2bbrp2R9RYj1KMEbhHNT2Lm1XplIQw+tULEKOHxNtkUFSLR1RNje7JQbxhzM1j95FxVQ==}
engines: {node: '>=18'}
- '@sentry-internal/replay-canvas@9.39.0':
- resolution: {integrity: sha512-rTw0WZuAwzo36/hXRSuftu7e3qtwXc+bSs1q7M+w7Tb98aoOyXs3GlnYmgP6q3t2v/h7zoIiQcfjnPqo6+DVtQ==}
+ '@sentry-internal/replay@10.5.0':
+ resolution: {integrity: sha512-Dp4coE/nPzhFrYH3iVrpVKmhNJ1m/jGXMEDBCNg3wJZRszI41Hrj0jCAM0Y2S3Q4IxYOmFYaFbGtVpAznRyOHg==}
engines: {node: '>=18'}
- '@sentry-internal/replay@9.28.0':
- resolution: {integrity: sha512-BVGVBlmcpJdT55d/vywjfK1u6zMC5ycjJBxU1wUCNgCU3cSKRDBnvmYgk/+Ay23bFryT28Q4hM1p5qBBAOfxjQ==}
+ '@sentry-internal/replay@9.46.0':
+ resolution: {integrity: sha512-+8JUblxSSnN0FXcmOewbN+wIc1dt6/zaSeAvt2xshrfrLooVullcGsuLAiPhY0d/e++Fk06q1SAl9g4V0V13gg==}
engines: {node: '>=18'}
- '@sentry-internal/replay@9.39.0':
- resolution: {integrity: sha512-6mweqQtfcVG2+FEPjmgjrpCThgs9bR2cmQM2Ew/SlINj8ETq5P/7Z3rdIp+Zs7LArXFIJUA7KX9ZW+noVPI8Gg==}
+ '@sentry/browser@10.5.0':
+ resolution: {integrity: sha512-o5pEJeZ/iZ7Fmaz2sIirThfnmSVNiP5ZYhacvcDi0qc288TmBbikCX3fXxq3xiSkhXfe1o5QIbNyovzfutyuVw==}
engines: {node: '>=18'}
- '@sentry/browser@9.28.0':
- resolution: {integrity: sha512-ttqiv3D9sIB43nZnJTTln1nXw1p4C5BDSh+sHmGUOiqdCH6ND3HByDITYMYIOz1lACSISTT4V+MEpqx0V25Tlw==}
+ '@sentry/browser@9.46.0':
+ resolution: {integrity: sha512-NOnCTQCM0NFuwbyt4DYWDNO2zOTj1mCf43hJqGDFb1XM9F++7zAmSNnCx4UrEoBTiFOy40McJwBBk9D1blSktA==}
engines: {node: '>=18'}
- '@sentry/browser@9.39.0':
- resolution: {integrity: sha512-RZ4Zp22ohFLwkR3y6pmSWDrPVP7ErX0ClNOh2Kal3TNWlOGb4WPBPaWj2tiOvc4n/l6EDYaFvQ0Hh8RhesB98A==}
+ '@sentry/core@10.5.0':
+ resolution: {integrity: sha512-jTJ8NhZSKB2yj3QTVRXfCCngQzAOLThQUxCl9A7Mv+XF10tP7xbH/88MVQ5WiOr2IzcmrB9r2nmUe36BnMlLjA==}
engines: {node: '>=18'}
'@sentry/core@8.55.0':
resolution: {integrity: sha512-6g7jpbefjHYs821Z+EBJ8r4Z7LT5h80YSWRJaylGS4nW5W5Z2KXzpdnyFarv37O7QjauzVC2E+PABmpkw5/JGA==}
engines: {node: '>=14.18'}
- '@sentry/core@9.28.0':
- resolution: {integrity: sha512-vzD9xhg9S864jxfCpq77feCE4y7iP2cZYsNMoTupl1vTUlmXlhp7XgF832fEMjEZq4vrPhaqCNsde7Sc3PAbaQ==}
- engines: {node: '>=18'}
-
- '@sentry/core@9.39.0':
- resolution: {integrity: sha512-9Z32C64rUGEI0ROxXXOtpJDaldPYtkzOQyGzToVgq5LefXkdgQInd8BhCt6PIBALfj7n25lLOYllTrHlboFJug==}
+ '@sentry/core@9.46.0':
+ resolution: {integrity: sha512-it7JMFqxVproAgEtbLgCVBYtQ9fIb+Bu0JD+cEplTN/Ukpe6GaolyYib5geZqslVxhp2sQgT+58aGvfd/k0N8Q==}
engines: {node: '>=18'}
'@sentry/node@8.55.0':
@@ -3694,8 +3806,8 @@ packages:
engines: {node: '>=14.18'}
hasBin: true
- '@sentry/vue@9.28.0':
- resolution: {integrity: sha512-k3TG9yuta5pbmX5If5BhaL+GCs1hKrepYf0XdDW+XSMB5gXJ7BzeCNrAGbGWfRlr+pioiRz4V9nxTTBY1yX5mQ==}
+ '@sentry/vue@10.5.0':
+ resolution: {integrity: sha512-UbQF7F2C6WaUaDuZEfePiCblZH11nGZfs7/+U/Qt4BRKWL74I0JtXy28tZ3cYI/cM2EenxpnUkjUAMATeZ5/Xg==}
engines: {node: '>=18'}
peerDependencies:
pinia: 2.x || 3.x
@@ -3704,8 +3816,8 @@ packages:
pinia:
optional: true
- '@sentry/vue@9.39.0':
- resolution: {integrity: sha512-BRCdfj4qsaJyoqqEPUB3DIcJOTTzGJd73dBGGwc3N2HAwWI5TyTLP3lHhbhE43ZowBO0aE/ogVT1N6dzQYt7Yg==}
+ '@sentry/vue@9.46.0':
+ resolution: {integrity: sha512-xFeZevR2nG+4tdvZcVgO6U1YiTQJZJTtV8aKRsCEh4yYpBO3FrfLxbSMTUeipILfKxpFf2iu1lwmqNyQtEllkA==}
engines: {node: '>=18'}
peerDependencies:
pinia: 2.x || 3.x
@@ -3714,23 +3826,23 @@ packages:
pinia:
optional: true
- '@shikijs/core@3.8.0':
- resolution: {integrity: sha512-gWt8NNZFurL6FMESO4lEsmspDh0H1fyUibhx1NnEH/S3kOXgYiWa6ZFqy+dcjBLhZqCXsepuUaL1QFXk6PrpsQ==}
+ '@shikijs/core@3.11.0':
+ resolution: {integrity: sha512-oJwU+DxGqp6lUZpvtQgVOXNZcVsirN76tihOLBmwILkKuRuwHteApP8oTXmL4tF5vS5FbOY0+8seXmiCoslk4g==}
- '@shikijs/engine-javascript@3.8.0':
- resolution: {integrity: sha512-IBULFFpQ1N5Cg/C7jPCGnjIKz72CcRtD0BIbNhSuXPUOxLG0bF1URsP/uLfxQFQ9ORfunCQwL7UuSX1RSRBwUQ==}
+ '@shikijs/engine-javascript@3.11.0':
+ resolution: {integrity: sha512-6/ov6pxrSvew13k9ztIOnSBOytXeKs5kfIR7vbhdtVRg+KPzvp2HctYGeWkqv7V6YIoLicnig/QF3iajqyElZA==}
- '@shikijs/engine-oniguruma@3.8.0':
- resolution: {integrity: sha512-Tx7kR0oFzqa+rY7t80LjN8ZVtHO3a4+33EUnBVx2qYP3fGxoI9H0bvnln5ySelz9SIUTsS0/Qn+9dg5zcUMsUw==}
+ '@shikijs/engine-oniguruma@3.11.0':
+ resolution: {integrity: sha512-4DwIjIgETK04VneKbfOE4WNm4Q7WC1wo95wv82PoHKdqX4/9qLRUwrfKlmhf0gAuvT6GHy0uc7t9cailk6Tbhw==}
- '@shikijs/langs@3.8.0':
- resolution: {integrity: sha512-mfGYuUgjQ5GgXinB5spjGlBVhG2crKRpKkfADlp8r9k/XvZhtNXxyOToSnCEnF0QNiZnJjlt5MmU9PmhRdwAbg==}
+ '@shikijs/langs@3.11.0':
+ resolution: {integrity: sha512-Njg/nFL4HDcf/ObxcK2VeyidIq61EeLmocrwTHGGpOQx0BzrPWM1j55XtKQ1LvvDWH15cjQy7rg96aJ1/l63uw==}
- '@shikijs/themes@3.8.0':
- resolution: {integrity: sha512-yaZiLuyO23sXe16JFU76KyUMTZCJi4EMQKIrdQt7okoTzI4yAaJhVXT2Uy4k8yBIEFRiia5dtD7gC1t8m6y3oQ==}
+ '@shikijs/themes@3.11.0':
+ resolution: {integrity: sha512-BhhWRzCTEk2CtWt4S4bgsOqPJRkapvxdsifAwqP+6mk5uxboAQchc0etiJ0iIasxnMsb764qGD24DK9albcU9Q==}
- '@shikijs/types@3.8.0':
- resolution: {integrity: sha512-I/b/aNg0rP+kznVDo7s3UK8jMcqEGTtoPDdQ+JlQ2bcJIyu/e2iRvl42GLIDMK03/W1YOHOuhlhQ7aM+XbKUeg==}
+ '@shikijs/types@3.11.0':
+ resolution: {integrity: sha512-RB7IMo2E7NZHyfkqAuaf4CofyY8bPzjWPjJRzn6SEak3b46fIQyG6Vx5fG/obqkfppQ+g8vEsiD7Uc6lqQt32Q==}
'@shikijs/vscode-textmate@10.0.2':
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
@@ -3754,8 +3866,8 @@ packages:
'@sinclair/typebox@0.27.8':
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
- '@sinclair/typebox@0.34.37':
- resolution: {integrity: sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw==}
+ '@sinclair/typebox@0.34.38':
+ resolution: {integrity: sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==}
'@sindresorhus/is@5.6.0':
resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==}
@@ -3797,8 +3909,8 @@ packages:
resolution: {integrity: sha512-wRlta7GuLWpTqtFfGo+nZyOO1vEvewdNR1R4rTxpC8XU6vG/NDyrFBhwLZsqg1NUoR1noVaXJPC/7ZK47QCySw==}
engines: {node: '>=14.0.0'}
- '@smithy/abort-controller@4.0.4':
- resolution: {integrity: sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==}
+ '@smithy/abort-controller@4.0.5':
+ resolution: {integrity: sha512-jcrqdTQurIrBbUm4W2YdLVMQDoL0sA9DTxYd2s+R/y+2U9NLOP7Xf/YqfSg1FZhlZIYEnvk2mwbyvIfdLEPo8g==}
engines: {node: '>=18.0.0'}
'@smithy/chunked-blob-reader-native@4.0.0':
@@ -3809,56 +3921,56 @@ packages:
resolution: {integrity: sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==}
engines: {node: '>=18.0.0'}
- '@smithy/config-resolver@4.1.4':
- resolution: {integrity: sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==}
+ '@smithy/config-resolver@4.1.5':
+ resolution: {integrity: sha512-viuHMxBAqydkB0AfWwHIdwf/PRH2z5KHGUzqyRtS/Wv+n3IHI993Sk76VCA7dD/+GzgGOmlJDITfPcJC1nIVIw==}
engines: {node: '>=18.0.0'}
- '@smithy/core@3.5.3':
- resolution: {integrity: sha512-xa5byV9fEguZNofCclv6v9ra0FYh5FATQW/da7FQUVTic94DfrN/NvmKZjrMyzbpqfot9ZjBaO8U1UeTbmSLuA==}
+ '@smithy/core@3.8.0':
+ resolution: {integrity: sha512-EYqsIYJmkR1VhVE9pccnk353xhs+lB6btdutJEtsp7R055haMJp2yE16eSxw8fv+G0WUY6vqxyYOP8kOqawxYQ==}
engines: {node: '>=18.0.0'}
- '@smithy/credential-provider-imds@4.0.6':
- resolution: {integrity: sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==}
+ '@smithy/credential-provider-imds@4.0.7':
+ resolution: {integrity: sha512-dDzrMXA8d8riFNiPvytxn0mNwR4B3h8lgrQ5UjAGu6T9z/kRg/Xncf4tEQHE/+t25sY8IH3CowcmWi+1U5B1Gw==}
engines: {node: '>=18.0.0'}
- '@smithy/eventstream-codec@4.0.4':
- resolution: {integrity: sha512-7XoWfZqWb/QoR/rAU4VSi0mWnO2vu9/ltS6JZ5ZSZv0eovLVfDfu0/AX4ub33RsJTOth3TiFWSHS5YdztvFnig==}
+ '@smithy/eventstream-codec@4.0.5':
+ resolution: {integrity: sha512-miEUN+nz2UTNoRYRhRqVTJCx7jMeILdAurStT2XoS+mhokkmz1xAPp95DFW9Gxt4iF2VBqpeF9HbTQ3kY1viOA==}
engines: {node: '>=18.0.0'}
- '@smithy/eventstream-serde-browser@4.0.4':
- resolution: {integrity: sha512-3fb/9SYaYqbpy/z/H3yIi0bYKyAa89y6xPmIqwr2vQiUT2St+avRt8UKwsWt9fEdEasc5d/V+QjrviRaX1JRFA==}
+ '@smithy/eventstream-serde-browser@4.0.5':
+ resolution: {integrity: sha512-LCUQUVTbM6HFKzImYlSB9w4xafZmpdmZsOh9rIl7riPC3osCgGFVP+wwvYVw6pXda9PPT9TcEZxaq3XE81EdJQ==}
engines: {node: '>=18.0.0'}
- '@smithy/eventstream-serde-config-resolver@4.1.2':
- resolution: {integrity: sha512-JGtambizrWP50xHgbzZI04IWU7LdI0nh/wGbqH3sJesYToMi2j/DcoElqyOcqEIG/D4tNyxgRuaqBXWE3zOFhQ==}
+ '@smithy/eventstream-serde-config-resolver@4.1.3':
+ resolution: {integrity: sha512-yTTzw2jZjn/MbHu1pURbHdpjGbCuMHWncNBpJnQAPxOVnFUAbSIUSwafiphVDjNV93TdBJWmeVAds7yl5QCkcA==}
engines: {node: '>=18.0.0'}
- '@smithy/eventstream-serde-node@4.0.4':
- resolution: {integrity: sha512-RD6UwNZ5zISpOWPuhVgRz60GkSIp0dy1fuZmj4RYmqLVRtejFqQ16WmfYDdoSoAjlp1LX+FnZo+/hkdmyyGZ1w==}
+ '@smithy/eventstream-serde-node@4.0.5':
+ resolution: {integrity: sha512-lGS10urI4CNzz6YlTe5EYG0YOpsSp3ra8MXyco4aqSkQDuyZPIw2hcaxDU82OUVtK7UY9hrSvgWtpsW5D4rb4g==}
engines: {node: '>=18.0.0'}
- '@smithy/eventstream-serde-universal@4.0.4':
- resolution: {integrity: sha512-UeJpOmLGhq1SLox79QWw/0n2PFX+oPRE1ZyRMxPIaFEfCqWaqpB7BU9C8kpPOGEhLF7AwEqfFbtwNxGy4ReENA==}
+ '@smithy/eventstream-serde-universal@4.0.5':
+ resolution: {integrity: sha512-JFnmu4SU36YYw3DIBVao3FsJh4Uw65vVDIqlWT4LzR6gXA0F3KP0IXFKKJrhaVzCBhAuMsrUUaT5I+/4ZhF7aw==}
engines: {node: '>=18.0.0'}
- '@smithy/fetch-http-handler@5.0.4':
- resolution: {integrity: sha512-AMtBR5pHppYMVD7z7G+OlHHAcgAN7v0kVKEpHuTO4Gb199Gowh0taYi9oDStFeUhetkeP55JLSVlTW1n9rFtUw==}
+ '@smithy/fetch-http-handler@5.1.1':
+ resolution: {integrity: sha512-61WjM0PWmZJR+SnmzaKI7t7G0UkkNFboDpzIdzSoy7TByUzlxo18Qlh9s71qug4AY4hlH/CwXdubMtkcNEb/sQ==}
engines: {node: '>=18.0.0'}
- '@smithy/hash-blob-browser@4.0.4':
- resolution: {integrity: sha512-WszRiACJiQV3QG6XMV44i5YWlkrlsM5Yxgz4jvsksuu7LDXA6wAtypfPajtNTadzpJy3KyJPoWehYpmZGKUFIQ==}
+ '@smithy/hash-blob-browser@4.0.5':
+ resolution: {integrity: sha512-F7MmCd3FH/Q2edhcKd+qulWkwfChHbc9nhguBlVjSUE6hVHhec3q6uPQ+0u69S6ppvLtR3eStfCuEKMXBXhvvA==}
engines: {node: '>=18.0.0'}
- '@smithy/hash-node@4.0.4':
- resolution: {integrity: sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==}
+ '@smithy/hash-node@4.0.5':
+ resolution: {integrity: sha512-cv1HHkKhpyRb6ahD8Vcfb2Hgz67vNIXEp2vnhzfxLFGRukLCNEA5QdsorbUEzXma1Rco0u3rx5VTqbM06GcZqQ==}
engines: {node: '>=18.0.0'}
- '@smithy/hash-stream-node@4.0.4':
- resolution: {integrity: sha512-wHo0d8GXyVmpmMh/qOR0R7Y46/G1y6OR8U+bSTB4ppEzRxd1xVAQ9xOE9hOc0bSjhz0ujCPAbfNLkLrpa6cevg==}
+ '@smithy/hash-stream-node@4.0.5':
+ resolution: {integrity: sha512-IJuDS3+VfWB67UC0GU0uYBG/TA30w+PlOaSo0GPm9UHS88A6rCP6uZxNjNYiyRtOcjv7TXn/60cW8ox1yuZsLg==}
engines: {node: '>=18.0.0'}
- '@smithy/invalid-dependency@4.0.4':
- resolution: {integrity: sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==}
+ '@smithy/invalid-dependency@4.0.5':
+ resolution: {integrity: sha512-IVnb78Qtf7EJpoEVo7qJ8BEXQwgC4n3igeJNNKEj/MLYtapnx8A67Zt/J3RXAj2xSO1910zk0LdFiygSemuLow==}
engines: {node: '>=18.0.0'}
'@smithy/is-array-buffer@2.0.0':
@@ -3869,92 +3981,92 @@ packages:
resolution: {integrity: sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==}
engines: {node: '>=18.0.0'}
- '@smithy/md5-js@4.0.4':
- resolution: {integrity: sha512-uGLBVqcOwrLvGh/v/jw423yWHq/ofUGK1W31M2TNspLQbUV1Va0F5kTxtirkoHawODAZcjXTSGi7JwbnPcDPJg==}
+ '@smithy/md5-js@4.0.5':
+ resolution: {integrity: sha512-8n2XCwdUbGr8W/XhMTaxILkVlw2QebkVTn5tm3HOcbPbOpWg89zr6dPXsH8xbeTsbTXlJvlJNTQsKAIoqQGbdA==}
engines: {node: '>=18.0.0'}
- '@smithy/middleware-content-length@4.0.4':
- resolution: {integrity: sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==}
+ '@smithy/middleware-content-length@4.0.5':
+ resolution: {integrity: sha512-l1jlNZoYzoCC7p0zCtBDE5OBXZ95yMKlRlftooE5jPWQn4YBPLgsp+oeHp7iMHaTGoUdFqmHOPa8c9G3gBsRpQ==}
engines: {node: '>=18.0.0'}
- '@smithy/middleware-endpoint@4.1.11':
- resolution: {integrity: sha512-zDogwtRLzKl58lVS8wPcARevFZNBOOqnmzWWxVe9XiaXU2CADFjvJ9XfNibgkOWs08sxLuSr81NrpY4mgp9OwQ==}
+ '@smithy/middleware-endpoint@4.1.18':
+ resolution: {integrity: sha512-ZhvqcVRPZxnZlokcPaTwb+r+h4yOIOCJmx0v2d1bpVlmP465g3qpVSf7wxcq5zZdu4jb0H4yIMxuPwDJSQc3MQ==}
engines: {node: '>=18.0.0'}
- '@smithy/middleware-retry@4.1.12':
- resolution: {integrity: sha512-wvIH70c4e91NtRxdaLZF+mbLZ/HcC6yg7ySKUiufL6ESp6zJUSnJucZ309AvG9nqCFHSRB5I6T3Ez1Q9wCh0Ww==}
+ '@smithy/middleware-retry@4.1.19':
+ resolution: {integrity: sha512-X58zx/NVECjeuUB6A8HBu4bhx72EoUz+T5jTMIyeNKx2lf+Gs9TmWPNNkH+5QF0COjpInP/xSpJGJ7xEnAklQQ==}
engines: {node: '>=18.0.0'}
- '@smithy/middleware-serde@4.0.8':
- resolution: {integrity: sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==}
+ '@smithy/middleware-serde@4.0.9':
+ resolution: {integrity: sha512-uAFFR4dpeoJPGz8x9mhxp+RPjo5wW0QEEIPPPbLXiRRWeCATf/Km3gKIVR5vaP8bN1kgsPhcEeh+IZvUlBv6Xg==}
engines: {node: '>=18.0.0'}
- '@smithy/middleware-stack@4.0.4':
- resolution: {integrity: sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==}
+ '@smithy/middleware-stack@4.0.5':
+ resolution: {integrity: sha512-/yoHDXZPh3ocRVyeWQFvC44u8seu3eYzZRveCMfgMOBcNKnAmOvjbL9+Cp5XKSIi9iYA9PECUuW2teDAk8T+OQ==}
engines: {node: '>=18.0.0'}
- '@smithy/node-config-provider@4.1.3':
- resolution: {integrity: sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==}
+ '@smithy/node-config-provider@4.1.4':
+ resolution: {integrity: sha512-+UDQV/k42jLEPPHSn39l0Bmc4sB1xtdI9Gd47fzo/0PbXzJ7ylgaOByVjF5EeQIumkepnrJyfx86dPa9p47Y+w==}
engines: {node: '>=18.0.0'}
'@smithy/node-http-handler@2.5.0':
resolution: {integrity: sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==}
engines: {node: '>=14.0.0'}
- '@smithy/node-http-handler@4.0.6':
- resolution: {integrity: sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==}
+ '@smithy/node-http-handler@4.1.1':
+ resolution: {integrity: sha512-RHnlHqFpoVdjSPPiYy/t40Zovf3BBHc2oemgD7VsVTFFZrU5erFFe0n52OANZZ/5sbshgD93sOh5r6I35Xmpaw==}
engines: {node: '>=18.0.0'}
- '@smithy/property-provider@4.0.4':
- resolution: {integrity: sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==}
+ '@smithy/property-provider@4.0.5':
+ resolution: {integrity: sha512-R/bswf59T/n9ZgfgUICAZoWYKBHcsVDurAGX88zsiUtOTA/xUAPyiT+qkNCPwFn43pZqN84M4MiUsbSGQmgFIQ==}
engines: {node: '>=18.0.0'}
'@smithy/protocol-http@3.3.0':
resolution: {integrity: sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ==}
engines: {node: '>=14.0.0'}
- '@smithy/protocol-http@5.1.2':
- resolution: {integrity: sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==}
+ '@smithy/protocol-http@5.1.3':
+ resolution: {integrity: sha512-fCJd2ZR7D22XhDY0l+92pUag/7je2BztPRQ01gU5bMChcyI0rlly7QFibnYHzcxDvccMjlpM/Q1ev8ceRIb48w==}
engines: {node: '>=18.0.0'}
'@smithy/querystring-builder@2.2.0':
resolution: {integrity: sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==}
engines: {node: '>=14.0.0'}
- '@smithy/querystring-builder@4.0.4':
- resolution: {integrity: sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==}
+ '@smithy/querystring-builder@4.0.5':
+ resolution: {integrity: sha512-NJeSCU57piZ56c+/wY+AbAw6rxCCAOZLCIniRE7wqvndqxcKKDOXzwWjrY7wGKEISfhL9gBbAaWWgHsUGedk+A==}
engines: {node: '>=18.0.0'}
- '@smithy/querystring-parser@4.0.4':
- resolution: {integrity: sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==}
+ '@smithy/querystring-parser@4.0.5':
+ resolution: {integrity: sha512-6SV7md2CzNG/WUeTjVe6Dj8noH32r4MnUeFKZrnVYsQxpGSIcphAanQMayi8jJLZAWm6pdM9ZXvKCpWOsIGg0w==}
engines: {node: '>=18.0.0'}
- '@smithy/service-error-classification@4.0.5':
- resolution: {integrity: sha512-LvcfhrnCBvCmTee81pRlh1F39yTS/+kYleVeLCwNtkY8wtGg8V/ca9rbZZvYIl8OjlMtL6KIjaiL/lgVqHD2nA==}
+ '@smithy/service-error-classification@4.0.7':
+ resolution: {integrity: sha512-XvRHOipqpwNhEjDf2L5gJowZEm5nsxC16pAZOeEcsygdjv9A2jdOh3YoDQvOXBGTsaJk6mNWtzWalOB9976Wlg==}
engines: {node: '>=18.0.0'}
- '@smithy/shared-ini-file-loader@4.0.4':
- resolution: {integrity: sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==}
+ '@smithy/shared-ini-file-loader@4.0.5':
+ resolution: {integrity: sha512-YVVwehRDuehgoXdEL4r1tAAzdaDgaC9EQvhK0lEbfnbrd0bd5+CTQumbdPryX3J2shT7ZqQE+jPW4lmNBAB8JQ==}
engines: {node: '>=18.0.0'}
- '@smithy/signature-v4@5.1.2':
- resolution: {integrity: sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==}
+ '@smithy/signature-v4@5.1.3':
+ resolution: {integrity: sha512-mARDSXSEgllNzMw6N+mC+r1AQlEBO3meEAkR/UlfAgnMzJUB3goRBWgip1EAMG99wh36MDqzo86SfIX5Y+VEaw==}
engines: {node: '>=18.0.0'}
- '@smithy/smithy-client@4.4.3':
- resolution: {integrity: sha512-xxzNYgA0HD6ETCe5QJubsxP0hQH3QK3kbpJz3QrosBCuIWyEXLR/CO5hFb2OeawEKUxMNhz3a1nuJNN2np2RMA==}
+ '@smithy/smithy-client@4.4.10':
+ resolution: {integrity: sha512-iW6HjXqN0oPtRS0NK/zzZ4zZeGESIFcxj2FkWed3mcK8jdSdHzvnCKXSjvewESKAgGKAbJRA+OsaqKhkdYRbQQ==}
engines: {node: '>=18.0.0'}
'@smithy/types@2.12.0':
resolution: {integrity: sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==}
engines: {node: '>=14.0.0'}
- '@smithy/types@4.3.1':
- resolution: {integrity: sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==}
+ '@smithy/types@4.3.2':
+ resolution: {integrity: sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw==}
engines: {node: '>=18.0.0'}
- '@smithy/url-parser@4.0.4':
- resolution: {integrity: sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==}
+ '@smithy/url-parser@4.0.5':
+ resolution: {integrity: sha512-j+733Um7f1/DXjYhCbvNXABV53NyCRRA54C7bNEIxNPs0YjfRxeMKjjgm2jvTYrciZyCjsicHwQ6Q0ylo+NAUw==}
engines: {node: '>=18.0.0'}
'@smithy/util-base64@4.0.0':
@@ -3981,32 +4093,32 @@ packages:
resolution: {integrity: sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==}
engines: {node: '>=18.0.0'}
- '@smithy/util-defaults-mode-browser@4.0.19':
- resolution: {integrity: sha512-mvLMh87xSmQrV5XqnUYEPoiFFeEGYeAKIDDKdhE2ahqitm8OHM3aSvhqL6rrK6wm1brIk90JhxDf5lf2hbrLbQ==}
+ '@smithy/util-defaults-mode-browser@4.0.26':
+ resolution: {integrity: sha512-xgl75aHIS/3rrGp7iTxQAOELYeyiwBu+eEgAk4xfKwJJ0L8VUjhO2shsDpeil54BOFsqmk5xfdesiewbUY5tKQ==}
engines: {node: '>=18.0.0'}
- '@smithy/util-defaults-mode-node@4.0.19':
- resolution: {integrity: sha512-8tYnx+LUfj6m+zkUUIrIQJxPM1xVxfRBvoGHua7R/i6qAxOMjqR6CpEpDwKoIs1o0+hOjGvkKE23CafKL0vJ9w==}
+ '@smithy/util-defaults-mode-node@4.0.26':
+ resolution: {integrity: sha512-z81yyIkGiLLYVDetKTUeCZQ8x20EEzvQjrqJtb/mXnevLq2+w3XCEWTJ2pMp401b6BkEkHVfXb/cROBpVauLMQ==}
engines: {node: '>=18.0.0'}
- '@smithy/util-endpoints@3.0.6':
- resolution: {integrity: sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==}
+ '@smithy/util-endpoints@3.0.7':
+ resolution: {integrity: sha512-klGBP+RpBp6V5JbrY2C/VKnHXn3d5V2YrifZbmMY8os7M6m8wdYFoO6w/fe5VkP+YVwrEktW3IWYaSQVNZJ8oQ==}
engines: {node: '>=18.0.0'}
'@smithy/util-hex-encoding@4.0.0':
resolution: {integrity: sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==}
engines: {node: '>=18.0.0'}
- '@smithy/util-middleware@4.0.4':
- resolution: {integrity: sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==}
+ '@smithy/util-middleware@4.0.5':
+ resolution: {integrity: sha512-N40PfqsZHRSsByGB81HhSo+uvMxEHT+9e255S53pfBw/wI6WKDI7Jw9oyu5tJTLwZzV5DsMha3ji8jk9dsHmQQ==}
engines: {node: '>=18.0.0'}
- '@smithy/util-retry@4.0.5':
- resolution: {integrity: sha512-V7MSjVDTlEt/plmOFBn1762Dyu5uqMrV2Pl2X0dYk4XvWfdWJNe9Bs5Bzb56wkCuiWjSfClVMGcsuKrGj7S/yg==}
+ '@smithy/util-retry@4.0.7':
+ resolution: {integrity: sha512-TTO6rt0ppK70alZpkjwy+3nQlTiqNfoXja+qwuAchIEAIoSZW8Qyd76dvBv3I5bCpE38APafG23Y/u270NspiQ==}
engines: {node: '>=18.0.0'}
- '@smithy/util-stream@4.2.2':
- resolution: {integrity: sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w==}
+ '@smithy/util-stream@4.2.4':
+ resolution: {integrity: sha512-vSKnvNZX2BXzl0U2RgCLOwWaAP9x/ddd/XobPK02pCbzRm5s55M53uwb1rl/Ts7RXZvdJZerPkA+en2FDghLuQ==}
engines: {node: '>=18.0.0'}
'@smithy/util-uri-escape@2.2.0':
@@ -4025,8 +4137,8 @@ packages:
resolution: {integrity: sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==}
engines: {node: '>=18.0.0'}
- '@smithy/util-waiter@4.0.5':
- resolution: {integrity: sha512-4QvC49HTteI1gfemu0I1syWovJgPvGn7CVUoN9ZFkdvr/cCFkrEL7qNCdx/2eICqDWEGnnr68oMdSIPCLAriSQ==}
+ '@smithy/util-waiter@4.0.7':
+ resolution: {integrity: sha512-mYqtQXPmrwvUljaHyGxYUIIRI3qjBTEb/f5QFi3A6VlxhpmZd5mWXn9W+qUkf2pVE1Hv3SqxefiZOPGdxmO64A==}
engines: {node: '>=18.0.0'}
'@sqltools/formatter@1.2.5':
@@ -4067,11 +4179,11 @@ packages:
peerDependencies:
storybook: ^8.6.14
- '@storybook/addon-links@8.6.14':
- resolution: {integrity: sha512-DRlXHIyZzOruAZkxmXfVgTF+4d6K27pFcH4cUsm3KT1AXuZbr23lb5iZHpUZoG6lmU85Sru4xCEgewSTXBIe1w==}
+ '@storybook/addon-links@9.1.3':
+ resolution: {integrity: sha512-zLpOf9UYtxEaCQgbpOqOAW0f/puumwzTNA/AHDkqwe/BAOtBXyS/bocQIO6Oxj8z4kp+bYqxW6c2OA0/EyII0Q==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
- storybook: ^8.6.14
+ storybook: ^9.1.3
peerDependenciesMeta:
react:
optional: true
@@ -4118,11 +4230,11 @@ packages:
react-dom:
optional: true
- '@storybook/builder-vite@8.6.14':
- resolution: {integrity: sha512-ajWYhy32ksBWxwWHrjwZzyC0Ii5ZTeu5lsqA95Q/EQBB0P5qWlHWGM3AVyv82Mz/ND03ebGy123uVwgf6olnYQ==}
+ '@storybook/builder-vite@9.1.3':
+ resolution: {integrity: sha512-bstS/GsVJ5zVkRKAJociocA2omxU4CaNAP58fxS280JiRYgcrRaydDd7vwk6iGJ3xWbzwV0wH8SP54LVNyRY6Q==}
peerDependencies:
- storybook: ^8.6.14
- vite: ^4.0.0 || ^5.0.0 || ^6.0.0
+ storybook: ^9.1.3
+ vite: ^5.0.0 || ^6.0.0 || ^7.0.0
'@storybook/components@8.6.14':
resolution: {integrity: sha512-HNR2mC5I4Z5ek8kTrVZlIY/B8gJGs5b3XdZPBPBopTIN6U/YHXiDyOjY3JlaS4fSG1fVhp/Qp1TpMn1w/9m1pw==}
@@ -4134,19 +4246,16 @@ packages:
peerDependencies:
storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0
- '@storybook/core@8.6.14':
- resolution: {integrity: sha512-1P/w4FSNRqP8j3JQBOi3yGt8PVOgSRbP66Ok520T78eJBeqx9ukCfl912PQZ7SPbW3TIunBwLXMZOjZwBB/JmA==}
- peerDependencies:
- prettier: ^2 || ^3
- peerDependenciesMeta:
- prettier:
- optional: true
-
'@storybook/csf-plugin@8.6.14':
resolution: {integrity: sha512-dErtc9teAuN+eelN8FojzFE635xlq9cNGGGEu0WEmMUQ4iJ8pingvBO1N8X3scz4Ry7KnxX++NNf3J3gpxS8qQ==}
peerDependencies:
storybook: ^8.6.14
+ '@storybook/csf-plugin@9.1.3':
+ resolution: {integrity: sha512-wqh+tTCX2WZqVDVjhk/a6upsyYj/Kc85Wf6ywPx4pcFYxQZxiKF/wtuM9yzEpZC6fZHNvlKkzXWvP4wJOnm+zg==}
+ peerDependencies:
+ storybook: ^9.1.3
+
'@storybook/global@5.0.0':
resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==}
@@ -4179,31 +4288,31 @@ packages:
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
storybook: ^8.6.14
- '@storybook/react-vite@8.6.14':
- resolution: {integrity: sha512-FZU0xMPxa4/TO87FgcWwappOxLBHZV5HSRK5K+2bJD7rFJAoNorbHvB4Q1zvIAk7eCMjkr2GPCPHx9PRB9vJFg==}
- engines: {node: '>=18.0.0'}
+ '@storybook/react-dom-shim@9.1.3':
+ resolution: {integrity: sha512-zIgFwZqV8cvE+lzJDcD13rItxoWyYNUWu7eJQAnHz5RnyHhpu6rFgQej7i6J3rPmy9xVe+Rq6XsXgDNs6pIekQ==}
peerDependencies:
- '@storybook/test': 8.6.14
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
- storybook: ^8.6.14
- vite: ^4.0.0 || ^5.0.0 || ^6.0.0
- peerDependenciesMeta:
- '@storybook/test':
- optional: true
+ storybook: ^9.1.3
- '@storybook/react@8.6.14':
- resolution: {integrity: sha512-BOepx5bBFwl/CPI+F+LnmMmsG1wQYmrX/UQXgUbHQUU9Tj7E2ndTnNbpIuSLc8IrM03ru+DfwSg1Co3cxWtT+g==}
- engines: {node: '>=18.0.0'}
+ '@storybook/react-vite@9.1.3':
+ resolution: {integrity: sha512-iNRRxA5G9Yaw5etbRdCMnJtjI1VkzA7juc+/caVhKKut25sI8cOF4GRPLCbotLz9xmitQR2X7beZMPPVIYk86A==}
+ engines: {node: '>=20.0.0'}
peerDependencies:
- '@storybook/test': 8.6.14
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
- storybook: ^8.6.14
- typescript: '>= 4.2.x'
+ storybook: ^9.1.3
+ vite: ^5.0.0 || ^6.0.0 || ^7.0.0
+
+ '@storybook/react@9.1.3':
+ resolution: {integrity: sha512-CgJMk4Y8EfoFxWiTB53QxnN+nQbAkw+NBaNjsaaeDNOE1R0ximP/fn5b2jcLvM+b5ojjJiJL1QCzFyoPWImHIQ==}
+ engines: {node: '>=20.0.0'}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+ storybook: ^9.1.3
+ typescript: '>= 4.9.x'
peerDependenciesMeta:
- '@storybook/test':
- optional: true
typescript:
optional: true
@@ -4227,18 +4336,18 @@ packages:
peerDependencies:
storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0
- '@storybook/vue3-vite@8.6.14':
- resolution: {integrity: sha512-3BclEv7SzHuw8eC9mFsAuH3EjEf4eCb0FxY3SoyTagNX14WjCE5cV2AK9RpWh6e5kQZiTzF8NiYq6AJqi5ebbw==}
- engines: {node: '>=18.0.0'}
+ '@storybook/vue3-vite@9.1.3':
+ resolution: {integrity: sha512-YBSI4tuPH+Gt011BwKLjehYfpyGfA8QLWO4BKANNfay45gwWZy+EvtkQebVpdiLxOFwexfVHOqjK2wHFvfMriA==}
+ engines: {node: '>=20.0.0'}
peerDependencies:
- storybook: ^8.6.14
- vite: ^4.0.0 || ^5.0.0 || ^6.0.0
+ storybook: ^9.1.3
+ vite: ^5.0.0 || ^6.0.0 || ^7.0.0
- '@storybook/vue3@8.6.14':
- resolution: {integrity: sha512-T9ORF734iBqYf2Sw/L/6qQL3FvBH9q6dHh8AFGkqTL/cluy0VxW55B6QLBvLAMS2OeMFB5dXRli5MFfw5njjQw==}
- engines: {node: '>=18.0.0'}
+ '@storybook/vue3@9.1.3':
+ resolution: {integrity: sha512-1xRFP1qyE6cBo9RlR92nnHekpGvJZ06DpnVszsJo0T7MRV+odEDw04KNfQr/SXt6vQtDN+5qszjSx+ZQlPRvTA==}
+ engines: {node: '>=20.0.0'}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^9.1.3
vue: ^3.0.0
'@stylistic/eslint-plugin@2.13.0':
@@ -4247,8 +4356,8 @@ packages:
peerDependencies:
eslint: '>=8.40.0'
- '@swc/cli@0.7.7':
- resolution: {integrity: sha512-j4yYm9bx3pxWofaJKX1BFwj/3ngUDynN4UIQ2Xd2h0h/7Gt7zkReBTpDN7g5S13mgAYxacaTHTOUsz18097E8w==}
+ '@swc/cli@0.7.8':
+ resolution: {integrity: sha512-27Ov4rm0s2C6LLX+NDXfDVB69LGs8K94sXtFhgeUyQ4DBywZuCgTBu2loCNHRr8JhT9DeQvJM5j9FAu/THbo4w==}
engines: {node: '>= 16.14.0'}
hasBin: true
peerDependencies:
@@ -4264,14 +4373,14 @@ packages:
cpu: [arm64]
os: [android]
- '@swc/core-darwin-arm64@1.12.0':
- resolution: {integrity: sha512-usLr8kC80GDv3pwH2zoEaS279kxtWY0MY3blbMFw7zA8fAjqxa8IDxm3WcgyNLNWckWn4asFfguEwz/Weem3nA==}
+ '@swc/core-darwin-arm64@1.13.4':
+ resolution: {integrity: sha512-CGbTu9dGBwgklUj+NAQAYyPjBuoHaNRWK4QXJRv1QNIkhtE27aY7QA9uEON14SODxsio3t8+Pjjl2Mzx1Pxf+g==}
engines: {node: '>=10'}
cpu: [arm64]
os: [darwin]
- '@swc/core-darwin-x64@1.12.0':
- resolution: {integrity: sha512-Cvv4sqDcTY7QF2Dh1vn2Xbt/1ENYQcpmrGHzITJrXzxA2aBopsz/n4yQDiyRxTR0t802m4xu0CzMoZIHvVruWQ==}
+ '@swc/core-darwin-x64@1.13.4':
+ resolution: {integrity: sha512-qLFwYmLrqHNCf+JO9YLJT6IP/f9LfbXILTaqyfluFLW1GCfJyvUrSt3CWaL2lwwyT1EbBh6BVaAAecXiJIo3vg==}
engines: {node: '>=10'}
cpu: [x64]
os: [darwin]
@@ -4282,56 +4391,56 @@ packages:
cpu: [x64]
os: [freebsd]
- '@swc/core-linux-arm-gnueabihf@1.12.0':
- resolution: {integrity: sha512-seM4/XMJMOupkzfLfHl8sRa3NdhsVZp+XgwA/vVeYZYJE4wuWUxVzhCYzwmNftVY32eF2IiRaWnhG6ho6jusnQ==}
+ '@swc/core-linux-arm-gnueabihf@1.13.4':
+ resolution: {integrity: sha512-y7SeNIA9em3+smNMpr781idKuNwJNAqewiotv+pIR5FpXdXXNjHWW+jORbqQYd61k6YirA5WQv+Af4UzqEX17g==}
engines: {node: '>=10'}
cpu: [arm]
os: [linux]
- '@swc/core-linux-arm64-gnu@1.12.0':
- resolution: {integrity: sha512-Al0x33gUVxNY5tutEYpSyv7mze6qQS1ONa0HEwoRxcK9WXsX0NHLTiOSGZoCUS1SsXM37ONlbA6/Bsp1MQyP+g==}
+ '@swc/core-linux-arm64-gnu@1.13.4':
+ resolution: {integrity: sha512-u0c51VdzRmXaphLgghY9+B2Frzler6nIv+J788nqIh6I0ah3MmMW8LTJKZfdaJa3oFxzGNKXsJiaU2OFexNkug==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
- '@swc/core-linux-arm64-musl@1.12.0':
- resolution: {integrity: sha512-OeFHz/5Hl9v75J9TYA5jQxNIYAZMqaiPpd9dYSTK2Xyqa/ZGgTtNyPhIwVfxx+9mHBf6+9c1mTlXUtACMtHmaQ==}
+ '@swc/core-linux-arm64-musl@1.13.4':
+ resolution: {integrity: sha512-Z92GJ98x8yQHn4I/NPqwAQyHNkkMslrccNVgFcnY1msrb6iGSw5uFg2H2YpvQ5u2/Yt6CRpLIUVVh8SGg1+gFA==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
- '@swc/core-linux-x64-gnu@1.12.0':
- resolution: {integrity: sha512-ltIvqNi7H0c5pRawyqjeYSKEIfZP4vv/datT3mwT6BW7muJtd1+KIDCPFLMIQ4wm/h76YQwPocsin3fzmnFdNA==}
+ '@swc/core-linux-x64-gnu@1.13.4':
+ resolution: {integrity: sha512-rSUcxgpFF0L8Fk1CbUf946XCX1CRp6eaHfKqplqFNWCHv8HyqAtSFvgCHhT+bXru6Ca/p3sLC775SUeSWhsJ9w==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
- '@swc/core-linux-x64-musl@1.12.0':
- resolution: {integrity: sha512-Z/DhpjehaTK0uf+MhNB7mV9SuewpGs3P/q9/8+UsJeYoFr7yuOoPbAvrD6AqZkf6Bh7MRZ5OtG+KQgG5L+goiA==}
+ '@swc/core-linux-x64-musl@1.13.4':
+ resolution: {integrity: sha512-qY77eFUvmdXNSmTW+I1fsz4enDuB0I2fE7gy6l9O4koSfjcCxkXw2X8x0lmKLm3FRiINS1XvZSg2G+q4NNQCRQ==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
- '@swc/core-win32-arm64-msvc@1.12.0':
- resolution: {integrity: sha512-wHnvbfHIh2gfSbvuFT7qP97YCMUDh+fuiso+pcC6ug8IsMxuViNapHET4o0ZdFNWHhXJ7/s0e6w7mkOalsqQiQ==}
+ '@swc/core-win32-arm64-msvc@1.13.4':
+ resolution: {integrity: sha512-xjPeDrOf6elCokxuyxwoskM00JJFQMTT2hTQZE24okjG3JiXzSFV+TmzYSp+LWNxPpnufnUUy/9Ee8+AcpslGw==}
engines: {node: '>=10'}
cpu: [arm64]
os: [win32]
- '@swc/core-win32-ia32-msvc@1.12.0':
- resolution: {integrity: sha512-88umlXwK+7J2p4DjfWHXQpmlZgCf1ayt6Ssj+PYlAfMCR0aBiJoAMwHWrvDXEozyOrsyP1j2X6WxbmA861vL5Q==}
+ '@swc/core-win32-ia32-msvc@1.13.4':
+ resolution: {integrity: sha512-Ta+Bblc9tE9X9vQlpa3r3+mVnHYdKn09QsZ6qQHvuXGKWSS99DiyxKTYX2vxwMuoTObR0BHvnhNbaGZSV1VwNA==}
engines: {node: '>=10'}
cpu: [ia32]
os: [win32]
- '@swc/core-win32-x64-msvc@1.12.0':
- resolution: {integrity: sha512-KR9TSRp+FEVOhbgTU6c94p/AYpsyBk7dIvlKQiDp8oKScUoyHG5yjmMBFN/BqUyTq4kj6zlgsY2rFE4R8/yqWg==}
+ '@swc/core-win32-x64-msvc@1.13.4':
+ resolution: {integrity: sha512-pHnb4QwGiuWs4Z9ePSgJ48HP3NZIno6l75SB8YLCiPVDiLhvCLKEjz/caPRsFsmet9BEP8e3bAf2MV8MXgaTSg==}
engines: {node: '>=10'}
cpu: [x64]
os: [win32]
- '@swc/core@1.12.0':
- resolution: {integrity: sha512-/C0kiMHPY/HnLfqXYGMGxGck3A5Y3mqwxfv+EwHTPHGjAVRfHpWAEEBTSTF5C88vVY6CvwBEkhR2TX7t8Mahcw==}
+ '@swc/core@1.13.4':
+ resolution: {integrity: sha512-bCq2GCuKV16DSOOEdaRqHMm1Ok4YEoLoNdgdzp8BS/Hxxr/0NVCHBUgRLLRy/TlJGv20Idx+djd5FIDvsnqMaw==}
engines: {node: '>=10'}
peerDependencies:
'@swc/helpers': '>=0.5.17'
@@ -4342,20 +4451,14 @@ packages:
'@swc/counter@0.1.3':
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
- '@swc/jest@0.2.38':
- resolution: {integrity: sha512-HMoZgXWMqChJwffdDjvplH53g9G2ALQes3HKXDEdliB/b85OQ0CTSbxG8VSeCwiAn7cOaDVEt4mwmZvbHcS52w==}
- engines: {npm: '>= 7.0.0'}
- peerDependencies:
- '@swc/core': '*'
-
'@swc/jest@0.2.39':
resolution: {integrity: sha512-eyokjOwYd0Q8RnMHri+8/FS1HIrIUKK/sRrFp8c1dThUOfNeCWbLmBP1P5VsKdvmkd25JaH+OKYwEYiAYg9YAA==}
engines: {npm: '>= 7.0.0'}
peerDependencies:
'@swc/core': '*'
- '@swc/types@0.1.22':
- resolution: {integrity: sha512-D13mY/ZA4PPEFSy6acki9eBT/3WgjMoRqNcdpIvjaYLQ44Xk5BdaL7UkDxAh6Z9UOe7tCCp67BVmZCojYp9owg==}
+ '@swc/types@0.1.24':
+ resolution: {integrity: sha512-tjTMh3V4vAORHtdTprLlfoMptu1WfTZG9Rsca6yOKyNYsRr+MUXutKmliB17orgSZk5DpnDxs8GUdd/qwYxOng==}
'@swc/wasm@1.2.130':
resolution: {integrity: sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==}
@@ -4363,15 +4466,18 @@ packages:
'@syuilo/aiscript@0.19.0':
resolution: {integrity: sha512-ZWG4s1m6RrFjE7NeIMaxFz769YO1jW5ReTrOROrEO4IHheOrjxxJ/Ffe2TUNqX9/XxDloMwfWplKhfSzx8LGMA==}
+ '@syuilo/aiscript@1.1.0':
+ resolution: {integrity: sha512-3S6+tWC6f8WD8nnCgXSkqzPlEL9iKH9cfjERrk3OEuVQy+qP3wSLRszjy9FEHeYtvg90erUCGhTuYUy4XCNnmg==}
+
'@szmarczak/http-timer@5.0.1':
resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
engines: {node: '>=14.16'}
- '@tabler/icons-webfont@3.34.0':
- resolution: {integrity: sha512-ft2oePnW2YTXZtHWdgnlNtFqRbhwnizPdaE1393qXfb1xS+d2K79cN7//IkGbnpttA3p5EJG6C6F3Ar60L1bhw==}
+ '@tabler/icons-webfont@3.34.1':
+ resolution: {integrity: sha512-sDivdBYt4lTY7pAv+MV1t9bzIWe5d0BLBal6dmk/lqnjsMSVBoi6ESLsSWVKGs7/ux5QHwz+/Ai9m5znZ9cw8g==}
- '@tabler/icons@3.34.0':
- resolution: {integrity: sha512-jtVqv0JC1WU2TTEBN32D9+R6mc1iEBuPwLnBsWaR02SIEciu9aq5806AWkCHuObhQ4ERhhXErLEK7Fs+tEZxiA==}
+ '@tabler/icons@3.34.1':
+ resolution: {integrity: sha512-9gTnUvd7Fd/DmQgr3MKY+oJLa1RfNsQo8c/ir3TJAWghOuZXodbtbVp0QBY2DxWuuvrSZFys0HEbv1CoiI5y6A==}
'@tensorflow/tfjs-backend-cpu@4.22.0':
resolution: {integrity: sha512-1u0FmuLGuRAi8D2c3cocHTASGXOmHc/4OvoVDENJayjYkS119fcTcQf4iHrtLthWyDIPy3JiPhRrZQC9EwnhLw==}
@@ -4425,12 +4531,22 @@ packages:
resolution: {integrity: sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==}
engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
+ '@testing-library/jest-dom@6.6.4':
+ resolution: {integrity: sha512-xDXgLjVunjHqczScfkCJ9iyjdNOVHvvCdqHSSxwM9L0l/wHkTRum67SDc020uAlCoqktJplgO2AAQeLP1wgqDQ==}
+ engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
+
'@testing-library/user-event@14.5.2':
resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==}
engines: {node: '>=12', npm: '>=6'}
peerDependencies:
'@testing-library/dom': '>=7.21.4'
+ '@testing-library/user-event@14.6.1':
+ resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==}
+ engines: {node: '>=12', npm: '>=6'}
+ peerDependencies:
+ '@testing-library/dom': '>=7.21.4'
+
'@testing-library/vue@8.1.0':
resolution: {integrity: sha512-ls4RiHO1ta4mxqqajWRh8158uFObVrrtAPoxk7cIp4HrnQUj/ScKzqz53HxYpG3X6Zb7H2v+0eTGLSoy8HQ2nA==}
engines: {node: '>=14'}
@@ -4448,18 +4564,12 @@ packages:
'@tokenizer/token@0.3.0':
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
- '@tsd/typescript@5.8.3':
- resolution: {integrity: sha512-oKarNCN1QUhG148M88mtZdOlBZWWGcInquef+U8QL7gwJkRuNo5WS45Fjsd+3hM9cDJWGpqSZ4Oo097KDx4IWA==}
+ '@tsd/typescript@5.9.2':
+ resolution: {integrity: sha512-mSMM0QtEPdMd+rdMDd17yCUYD4yI3pKHap89+jEZrZ3KIO5PhDofBjER0OtgHdvOXF74KMLO3fyD6k3Hz0v03A==}
engines: {node: '>=14.17'}
- '@twemoji/parser@15.0.0':
- resolution: {integrity: sha512-lh9515BNsvKSNvyUqbj5yFu83iIDQ77SwVcsN/SnEGawczhsKU6qWuogewN1GweTi5Imo5ToQ9s+nNTf97IXvg==}
-
- '@twemoji/parser@15.1.0':
- resolution: {integrity: sha512-3HTiSxPvkWUJ4kZeCvwyKlIwkpTUfBOk6igpBBRQni58ceQMv5YK4smkc8vX/eqOlMMNER/9qobv+Q6Q8LVrqA==}
-
- '@twemoji/parser@15.1.1':
- resolution: {integrity: sha512-CChRzIu6ngkCJOmURBlYEdX5DZSu+bBTtqR60XjBkFrmvplKW7OQsea+i8XwF4bLVlUXBO7ZmHhRPDzfQyLwwg==}
+ '@twemoji/parser@16.0.0':
+ resolution: {integrity: sha512-jmuIjkp3OIaEemwMy3sArBwZSuZkRqmueGwRe2Zk4cFzbUJISFBJSZLDUUBNIgq3c+nY49ideYN2OiII6JUqwA==}
'@types/accepts@1.3.7':
resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
@@ -4566,18 +4676,12 @@ packages:
'@types/http-link-header@1.0.7':
resolution: {integrity: sha512-snm5oLckop0K3cTDAiBnZDy6ncx9DJ3mCRDvs42C884MbVYPP74Tiq2hFsSDRTyjK6RyDYDIulPiW23ge+g5Lw==}
- '@types/istanbul-lib-coverage@2.0.4':
- resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==}
-
'@types/istanbul-lib-coverage@2.0.6':
resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
'@types/istanbul-lib-report@3.0.0':
resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==}
- '@types/istanbul-reports@3.0.1':
- resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==}
-
'@types/istanbul-reports@3.0.4':
resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==}
@@ -4605,8 +4709,8 @@ packages:
'@types/long@4.0.2':
resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==}
- '@types/matter-js@0.19.8':
- resolution: {integrity: sha512-W2ZWG58Lijv/4v768NgpeyFqqiOyslmAU7qqM1Lhz4XBoUgGtZtPz4CjcOKYtqHIak14dvPldslQhltqLTWwsw==}
+ '@types/matter-js@0.20.0':
+ resolution: {integrity: sha512-jOroeU1wEoizJ3rUnhS2HWzqbQrMHEmShTPUf8sbAghuZCAEQT7y3ojXAzrcQOWYkP93KhegDZI4gvMu5odfWw==}
'@types/mdast@4.0.3':
resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==}
@@ -4638,14 +4742,17 @@ packages:
'@types/node-fetch@2.6.11':
resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==}
- '@types/node@22.15.31':
- resolution: {integrity: sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==}
+ '@types/node@20.19.9':
+ resolution: {integrity: sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==}
+
+ '@types/node@22.17.0':
+ resolution: {integrity: sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==}
- '@types/node@22.16.4':
- resolution: {integrity: sha512-PYRhNtZdm2wH/NT2k/oAJ6/f2VD2N2Dag0lGlx2vWgMSJXGNmlce5MiTQzoWAiIJtso30mjnfQCOKVH+kAQC/g==}
+ '@types/node@22.17.2':
+ resolution: {integrity: sha512-gL6z5N9Jm9mhY+U2KXZpteb+09zyffliRkZyZOHODGATyC5B1Jt/7TzuuiLkFsSUMLbS1OLmlj/E+/3KF4Q/4w==}
- '@types/nodemailer@6.4.17':
- resolution: {integrity: sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==}
+ '@types/nodemailer@6.4.19':
+ resolution: {integrity: sha512-Fi8DwmuAduTk1/1MpkR9EwS0SsDvYXx5RxivAVII1InDCIxmhj/iQm3W8S3EVb/0arnblr6PK0FK4wYa7bwdLg==}
'@types/normalize-package-data@2.4.1':
resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
@@ -4668,8 +4775,8 @@ packages:
'@types/pg-pool@2.0.6':
resolution: {integrity: sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==}
- '@types/pg@8.15.4':
- resolution: {integrity: sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg==}
+ '@types/pg@8.15.5':
+ resolution: {integrity: sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ==}
'@types/pg@8.6.1':
resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==}
@@ -4794,147 +4901,147 @@ packages:
'@types/web-push@3.6.4':
resolution: {integrity: sha512-GnJmSr40H3RAnj0s34FNTcJi1hmWFV5KXugE0mYWnYhgTAHLJ/dJKAwDmvPJYMke0RplY2XE9LnM4hqSqKIjhQ==}
+ '@types/whatwg-mimetype@3.0.2':
+ resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==}
+
'@types/ws@8.18.1':
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
'@types/yargs-parser@21.0.0':
resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==}
- '@types/yargs@17.0.19':
- resolution: {integrity: sha512-cAx3qamwaYX9R0fzOIZAlFpo4A+1uBVCxqpKz9D26uTF4srRXaGTTsikQmaotCtNdbhzyUH7ft6p9ktz9s6UNQ==}
-
'@types/yargs@17.0.33':
resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==}
'@types/yauzl@2.10.0':
resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
- '@typescript-eslint/eslint-plugin@8.34.0':
- resolution: {integrity: sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==}
+ '@typescript-eslint/eslint-plugin@8.38.0':
+ resolution: {integrity: sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- '@typescript-eslint/parser': ^8.34.0
+ '@typescript-eslint/parser': ^8.38.0
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
- '@typescript-eslint/eslint-plugin@8.37.0':
- resolution: {integrity: sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==}
+ '@typescript-eslint/eslint-plugin@8.40.0':
+ resolution: {integrity: sha512-w/EboPlBwnmOBtRbiOvzjD+wdiZdgFeo17lkltrtn7X37vagKKWJABvyfsJXTlHe6XBzugmYgd4A4nW+k8Mixw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- '@typescript-eslint/parser': ^8.37.0
+ '@typescript-eslint/parser': ^8.40.0
eslint: ^8.57.0 || ^9.0.0
- typescript: '>=4.8.4 <5.9.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/parser@8.34.0':
- resolution: {integrity: sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==}
+ '@typescript-eslint/parser@8.38.0':
+ resolution: {integrity: sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
- '@typescript-eslint/parser@8.37.0':
- resolution: {integrity: sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==}
+ '@typescript-eslint/parser@8.40.0':
+ resolution: {integrity: sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
- typescript: '>=4.8.4 <5.9.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/project-service@8.34.0':
- resolution: {integrity: sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==}
+ '@typescript-eslint/project-service@8.38.0':
+ resolution: {integrity: sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <5.9.0'
- '@typescript-eslint/project-service@8.37.0':
- resolution: {integrity: sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==}
+ '@typescript-eslint/project-service@8.40.0':
+ resolution: {integrity: sha512-/A89vz7Wf5DEXsGVvcGdYKbVM9F7DyFXj52lNYUDS1L9yJfqjW/fIp5PgMuEJL/KeqVTe2QSbXAGUZljDUpArw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- typescript: '>=4.8.4 <5.9.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/scope-manager@8.34.0':
- resolution: {integrity: sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==}
+ '@typescript-eslint/scope-manager@8.38.0':
+ resolution: {integrity: sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/scope-manager@8.37.0':
- resolution: {integrity: sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==}
+ '@typescript-eslint/scope-manager@8.40.0':
+ resolution: {integrity: sha512-y9ObStCcdCiZKzwqsE8CcpyuVMwRouJbbSrNuThDpv16dFAj429IkM6LNb1dZ2m7hK5fHyzNcErZf7CEeKXR4w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/tsconfig-utils@8.34.0':
- resolution: {integrity: sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==}
+ '@typescript-eslint/tsconfig-utils@8.38.0':
+ resolution: {integrity: sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <5.9.0'
- '@typescript-eslint/tsconfig-utils@8.37.0':
- resolution: {integrity: sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==}
+ '@typescript-eslint/tsconfig-utils@8.40.0':
+ resolution: {integrity: sha512-jtMytmUaG9d/9kqSl/W3E3xaWESo4hFDxAIHGVW/WKKtQhesnRIJSAJO6XckluuJ6KDB5woD1EiqknriCtAmcw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- typescript: '>=4.8.4 <5.9.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/type-utils@8.34.0':
- resolution: {integrity: sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==}
+ '@typescript-eslint/type-utils@8.38.0':
+ resolution: {integrity: sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
- '@typescript-eslint/type-utils@8.37.0':
- resolution: {integrity: sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==}
+ '@typescript-eslint/type-utils@8.40.0':
+ resolution: {integrity: sha512-eE60cK4KzAc6ZrzlJnflXdrMqOBaugeukWICO2rB0KNvwdIMaEaYiywwHMzA1qFpTxrLhN9Lp4E/00EgWcD3Ow==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
- typescript: '>=4.8.4 <5.9.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/types@8.34.0':
- resolution: {integrity: sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==}
+ '@typescript-eslint/types@8.38.0':
+ resolution: {integrity: sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/types@8.37.0':
- resolution: {integrity: sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==}
+ '@typescript-eslint/types@8.40.0':
+ resolution: {integrity: sha512-ETdbFlgbAmXHyFPwqUIYrfc12ArvpBhEVgGAxVYSwli26dn8Ko+lIo4Su9vI9ykTZdJn+vJprs/0eZU0YMAEQg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/typescript-estree@8.34.0':
- resolution: {integrity: sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==}
+ '@typescript-eslint/typescript-estree@8.38.0':
+ resolution: {integrity: sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <5.9.0'
- '@typescript-eslint/typescript-estree@8.37.0':
- resolution: {integrity: sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==}
+ '@typescript-eslint/typescript-estree@8.40.0':
+ resolution: {integrity: sha512-k1z9+GJReVVOkc1WfVKs1vBrR5MIKKbdAjDTPvIK3L8De6KbFfPFt6BKpdkdk7rZS2GtC/m6yI5MYX+UsuvVYQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- typescript: '>=4.8.4 <5.9.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/utils@8.34.0':
- resolution: {integrity: sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==}
+ '@typescript-eslint/utils@8.38.0':
+ resolution: {integrity: sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
- '@typescript-eslint/utils@8.37.0':
- resolution: {integrity: sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==}
+ '@typescript-eslint/utils@8.40.0':
+ resolution: {integrity: sha512-Cgzi2MXSZyAUOY+BFwGs17s7ad/7L+gKt6Y8rAVVWS+7o6wrjeFN4nVfTpbE25MNcxyJ+iYUXflbs2xR9h4UBg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
- typescript: '>=4.8.4 <5.9.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/visitor-keys@8.34.0':
- resolution: {integrity: sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==}
+ '@typescript-eslint/visitor-keys@8.38.0':
+ resolution: {integrity: sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/visitor-keys@8.37.0':
- resolution: {integrity: sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==}
+ '@typescript-eslint/visitor-keys@8.40.0':
+ resolution: {integrity: sha512-8CZ47QwalyRjsypfwnbI3hKy5gJDPmrkLjkgMxhi0+DZZ2QNx2naS6/hWoVYUHU7LU2zleF68V9miaVZvhFfTA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@ungap/structured-clone@1.2.0':
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
- '@vitejs/plugin-vue@5.2.4':
- resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==}
- engines: {node: ^18.0.0 || >=20.0.0}
+ '@vitejs/plugin-vue@6.0.1':
+ resolution: {integrity: sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
peerDependencies:
- vite: ^5.0.0 || ^6.0.0
+ vite: ^5.0.0 || ^6.0.0 || ^7.0.0
vue: ^3.2.25
'@vitest/coverage-v8@3.2.4':
@@ -4996,44 +5103,38 @@ packages:
'@volar/language-core@2.2.0':
resolution: {integrity: sha512-a8WG9+4OdeNDW4ywABZIM6S6UN7em8uIlM/BZ2pWQUYrVmX+m8sj/X+QadvO+Li/t/LjAqbWJQtVgxdpEWLALQ==}
- '@volar/language-core@2.4.15':
- resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==}
+ '@volar/language-core@2.4.23':
+ resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==}
'@volar/source-map@2.2.0':
resolution: {integrity: sha512-HQlPRlHOVqCCHK8wI76ZldHkEwKsjp7E6idUc36Ekni+KJDNrqgSqPvyHQixybXPHNU7CI9Uxd9/IkxO7LuNBw==}
- '@volar/source-map@2.4.15':
- resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==}
+ '@volar/source-map@2.4.23':
+ resolution: {integrity: sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==}
'@volar/typescript@2.2.0':
resolution: {integrity: sha512-wC6l4zLiiCLxF+FGaHCbWlQYf4vMsnRxYhcI6WgvaNppOD6r1g+Ef1RKRJUApALWU46Yy/JDU/TbdV6w/X6Liw==}
- '@volar/typescript@2.4.15':
- resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==}
-
- '@vue/compiler-core@3.5.13':
- resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==}
+ '@volar/typescript@2.4.23':
+ resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==}
- '@vue/compiler-core@3.5.16':
- resolution: {integrity: sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==}
+ '@vue/compiler-core@3.5.18':
+ resolution: {integrity: sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==}
- '@vue/compiler-core@3.5.17':
- resolution: {integrity: sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==}
+ '@vue/compiler-core@3.5.19':
+ resolution: {integrity: sha512-/afpyvlkrSNYbPo94Qu8GtIOWS+g5TRdOvs6XZNw6pWQQmj5pBgSZvEPOIZlqWq0YvoUhDDQaQ2TnzuJdOV4hA==}
- '@vue/compiler-dom@3.5.13':
- resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==}
+ '@vue/compiler-dom@3.5.18':
+ resolution: {integrity: sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==}
- '@vue/compiler-dom@3.5.16':
- resolution: {integrity: sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==}
+ '@vue/compiler-dom@3.5.19':
+ resolution: {integrity: sha512-Drs6rPHQZx/pN9S6ml3Z3K/TWCIRPvzG2B/o5kFK9X0MNHt8/E+38tiRfojufrYBfA6FQUFB2qBBRXlcSXWtOA==}
- '@vue/compiler-dom@3.5.17':
- resolution: {integrity: sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==}
+ '@vue/compiler-sfc@3.5.19':
+ resolution: {integrity: sha512-YWCm1CYaJ+2RvNmhCwI7t3I3nU+hOrWGWMsn+Z/kmm1jy5iinnVtlmkiZwbLlbV1SRizX7vHsc0/bG5dj0zRTg==}
- '@vue/compiler-sfc@3.5.17':
- resolution: {integrity: sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==}
-
- '@vue/compiler-ssr@3.5.17':
- resolution: {integrity: sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==}
+ '@vue/compiler-ssr@3.5.19':
+ resolution: {integrity: sha512-/wx0VZtkWOPdiQLWPeQeqpHWR/LuNC7bHfSX7OayBTtUy8wur6vT6EQIX6Et86aED6J+y8tTw43qo2uoqGg5sw==}
'@vue/compiler-vue2@2.7.16':
resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
@@ -5046,36 +5147,33 @@ packages:
typescript:
optional: true
- '@vue/language-core@2.2.12':
- resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==}
+ '@vue/language-core@3.0.6':
+ resolution: {integrity: sha512-e2RRzYWm+qGm8apUHW1wA5RQxzNhkqbbKdbKhiDUcmMrNAZGyM8aTiL3UrTqkaFI5s7wJRGGrp4u3jgusuBp2A==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
- '@vue/reactivity@3.5.17':
- resolution: {integrity: sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==}
+ '@vue/reactivity@3.5.19':
+ resolution: {integrity: sha512-4bueZg2qs5MSsK2dQk3sssV0cfvxb/QZntTC8v7J448GLgmfPkQ+27aDjlt40+XFqOwUq5yRxK5uQh14Fc9eVA==}
- '@vue/runtime-core@3.5.17':
- resolution: {integrity: sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==}
+ '@vue/runtime-core@3.5.19':
+ resolution: {integrity: sha512-TaooCr8Hge1sWjLSyhdubnuofs3shhzZGfyD11gFolZrny76drPwBVQj28/z/4+msSFb18tOIg6VVVgf9/IbIA==}
- '@vue/runtime-dom@3.5.17':
- resolution: {integrity: sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==}
+ '@vue/runtime-dom@3.5.19':
+ resolution: {integrity: sha512-qmahqeok6ztuUTmV8lqd7N9ymbBzctNF885n8gL3xdCC1u2RnM/coX16Via0AiONQXUoYpxPojL3U1IsDgSWUQ==}
- '@vue/server-renderer@3.5.17':
- resolution: {integrity: sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==}
+ '@vue/server-renderer@3.5.19':
+ resolution: {integrity: sha512-ZJ/zV9SQuaIO+BEEVq/2a6fipyrSYfjKMU3267bPUk+oTx/hZq3RzV7VCh0Unlppt39Bvh6+NzxeopIFv4HJNg==}
peerDependencies:
- vue: 3.5.17
-
- '@vue/shared@3.5.13':
- resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
+ vue: 3.5.19
- '@vue/shared@3.5.16':
- resolution: {integrity: sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==}
+ '@vue/shared@3.5.18':
+ resolution: {integrity: sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==}
- '@vue/shared@3.5.17':
- resolution: {integrity: sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==}
+ '@vue/shared@3.5.19':
+ resolution: {integrity: sha512-IhXCOn08wgKrLQxRFKKlSacWg4Goi1BolrdEeLYn6tgHjJNXVrWJ5nzoxZqNwl5p88aLlQ8LOaoMa3AYvaKJ/Q==}
'@vue/test-utils@2.4.1':
resolution: {integrity: sha512-VO8nragneNzUZUah6kOjiFmD/gwRjUauG9DROh6oaOeFwX1cZRUNHhdeogE8635cISigXFTtGLUQWx5KCb0xeg==}
@@ -5224,16 +5322,16 @@ packages:
ajv@8.17.1:
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
- alien-signals@1.0.3:
- resolution: {integrity: sha512-zQOh3wAYK5ujENxvBBR3CFGF/b6afaSzZ/c9yNhJ1ENrGHETvpUuKQsa93Qrclp0+PzTF93MaZ7scVp1uUozhA==}
+ alien-signals@2.0.6:
+ resolution: {integrity: sha512-P3TxJSe31bUHBiblg59oU1PpaWPtmxF9GhJ/cB7OkgJ0qN/ifFSKUI25/v8ZhsT+lIG6ac8DpTOplXxORX6F3Q==}
- analytics-utils@1.0.14:
- resolution: {integrity: sha512-9v0kPd8v0GuBvfQcg5BO48AElaEAr9IXMAfJWXYMAhrD3QprgozEIUgMp/de0vS136PUOBB+10XQH9eBgBmfMw==}
+ analytics-utils@1.1.1:
+ resolution: {integrity: sha512-nRybjTpRAcHVhWb1cvYaOLJaI3R79r8XjMbu5c0wd2jKmANNqSrYwybiU0X3mp+CQQdm4YiAggTXb2cIA8XhUg==}
peerDependencies:
'@types/dlv': ^1.0.0
- analytics@0.8.16:
- resolution: {integrity: sha512-LEFQ47G9V1zVp9WIh2xhnbmSFEJq+WEzSv6voJ5uba88lefiIIYeG2nq87gFu83ocz1qtb9u7XgeaKKVBbbgWA==}
+ analytics@0.8.19:
+ resolution: {integrity: sha512-JFgasxpWFiAoqm5UHaGQv9j9OGz+f1KAeQkABYr3Z7YGhiqhQrBpPhIVAIEyttBRJZmew1QwMhN9/bOGGBnpJA==}
ansi-colors@4.1.3:
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
@@ -5251,10 +5349,6 @@ packages:
resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
engines: {node: '>=12'}
- ansi-styles@3.2.1:
- resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
- engines: {node: '>=4'}
-
ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
@@ -5322,10 +5416,6 @@ packages:
aria-query@5.3.0:
resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
- array-buffer-byte-length@1.0.1:
- resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
- engines: {node: '>= 0.4'}
-
array-buffer-byte-length@1.0.2:
resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
engines: {node: '>= 0.4'}
@@ -5333,10 +5423,6 @@ packages:
array-flatten@1.1.1:
resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
- array-includes@3.1.8:
- resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==}
- engines: {node: '>= 0.4'}
-
array-includes@3.1.9:
resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==}
engines: {node: '>= 0.4'}
@@ -5345,34 +5431,18 @@ packages:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
engines: {node: '>=8'}
- array.prototype.findlastindex@1.2.5:
- resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==}
- engines: {node: '>= 0.4'}
-
array.prototype.findlastindex@1.2.6:
resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==}
engines: {node: '>= 0.4'}
- array.prototype.flat@1.3.2:
- resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
- engines: {node: '>= 0.4'}
-
array.prototype.flat@1.3.3:
resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==}
engines: {node: '>= 0.4'}
- array.prototype.flatmap@1.3.2:
- resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==}
- engines: {node: '>= 0.4'}
-
array.prototype.flatmap@1.3.3:
resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==}
engines: {node: '>= 0.4'}
- arraybuffer.prototype.slice@1.0.3:
- resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==}
- engines: {node: '>= 0.4'}
-
arraybuffer.prototype.slice@1.0.4:
resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
engines: {node: '>= 0.4'}
@@ -5463,8 +5533,8 @@ packages:
axios@0.24.0:
resolution: {integrity: sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==}
- axios@1.8.4:
- resolution: {integrity: sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==}
+ axios@1.11.0:
+ resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==}
b4a@1.6.4:
resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==}
@@ -5563,14 +5633,6 @@ packages:
broadcast-channel@7.1.0:
resolution: {integrity: sha512-InJljddsYWbEL8LBnopnCg+qMQp9KcowvYWOt4YWrjD5HmxzDYKdVbDS1w/ji5rFZdRD58V5UxJPtBdpEbEJYw==}
- browser-assert@1.2.1:
- resolution: {integrity: sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==}
-
- browserslist@4.24.5:
- resolution: {integrity: sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==}
- engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
- hasBin: true
-
browserslist@4.25.1:
resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@@ -5605,8 +5667,8 @@ packages:
resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==}
engines: {node: '>=6.14.2'}
- bullmq@5.53.2:
- resolution: {integrity: sha512-xHgxrP/yNJHD7VCw1h+eRBh+2TCPBCM39uC9gCyksYc6ufcJP+HTZ/A2lzB2x7qMFWrvsX7tM40AT2BmdkYL/Q==}
+ bullmq@5.58.1:
+ resolution: {integrity: sha512-ySnO1Yn+J7oVkXn4cvPQQK7RFEP3hlqwnIJRxumODyLk+1o4/3H5qwB7s7e9h5W6tZdktvysNZsE/e5/VdKvuA==}
buraha@0.0.1:
resolution: {integrity: sha512-G563A0mTbzknm2jDaNxfZuNKIdeArs8T+XQN6t+KbmgnOoevXSXhKDkyf8Md/36Jrx99ikwbCag37VGe3myExQ==}
@@ -5647,10 +5709,6 @@ packages:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
- call-bind@1.0.7:
- resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
- engines: {node: '>= 0.4'}
-
call-bind@1.0.8:
resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
engines: {node: '>= 0.4'}
@@ -5659,9 +5717,6 @@ packages:
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
engines: {node: '>= 0.4'}
- call-me-maybe@1.0.2:
- resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
-
callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
@@ -5681,9 +5736,6 @@ packages:
caniuse-api@3.0.0:
resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==}
- caniuse-lite@1.0.30001718:
- resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==}
-
caniuse-lite@1.0.30001727:
resolution: {integrity: sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==}
@@ -5715,10 +5767,6 @@ packages:
resolution: {integrity: sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==}
engines: {node: '>=14.16'}
- chalk@2.4.2:
- resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
- engines: {node: '>=4'}
-
chalk@3.0.0:
resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==}
engines: {node: '>=8'}
@@ -5727,8 +5775,8 @@ packages:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
- chalk@5.4.1:
- resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==}
+ chalk@5.6.0:
+ resolution: {integrity: sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
change-case@5.4.4:
@@ -5760,8 +5808,8 @@ packages:
chart.js: '>=2.8.0'
date-fns: '>=2.0.0'
- chartjs-chart-matrix@2.1.1:
- resolution: {integrity: sha512-hJ5NKGYqfM37mnkr3XXIJDn9Eij4G7mbNsNxY1zEmtoVLu/k6HO9yL8sL8vFgVnJbhWqAJdlrb+dlTOFKh8xfA==}
+ chartjs-chart-matrix@3.0.0:
+ resolution: {integrity: sha512-lUWC1UaWkxGdG02dBJ5r1ppbSYB/uWmwAh11VEs7V3ZQItNCk4am+rmacwkgeb+SQeEj2hP9Qq4oGsUmPl/1lQ==}
peerDependencies:
chart.js: '>=3.0.0'
@@ -5786,10 +5834,6 @@ packages:
cheerio-select@2.1.0:
resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
- cheerio@1.0.0:
- resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==}
- engines: {node: '>=18.17'}
-
cheerio@1.1.0:
resolution: {integrity: sha512-+0hMx9eYhJvWbgpKV9hN7jg0JcwydpopZE4hgi+KvQtByZXPp04NiCWU0LzcAbP63abZckIHkTQaXVF52mX3xQ==}
engines: {node: '>=18.17'}
@@ -5809,8 +5853,8 @@ packages:
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
engines: {node: '>=18'}
- chromatic@11.29.0:
- resolution: {integrity: sha512-yisBlntp9hHVj19lIQdpTlcYIXuU9H/DbFuu6tyWHmj6hWT2EtukCCcxYXL78XdQt1vm2GfIrtgtKpj/Rzmo4A==}
+ chromatic@13.1.3:
+ resolution: {integrity: sha512-aOZDwg1PsDe9/UhiXqS6EJPoCGK91hYbj3HaunV/0Ij492eWLkXIzku/e5cF1t7Ma7cAuGpCQDo0vGHg0UO91w==}
hasBin: true
peerDependencies:
'@chromatic-com/cypress': ^0.*.* || ^1.0.0
@@ -5882,16 +5926,10 @@ packages:
collect-v8-coverage@1.0.1:
resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==}
- color-convert@1.9.3:
- resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
-
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
- color-name@1.1.3:
- resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
-
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
@@ -6054,17 +6092,19 @@ packages:
resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==}
engines: {node: '>=12.0.0'}
- cropperjs@2.0.0:
- resolution: {integrity: sha512-TO2j0Qre01kPHbow4FuTrbdEB4jTmGRySxW49jyEIqlJZuEBfrvCTT0vC3eRB2WBXudDfKi1Onako6DKWKxeAQ==}
+ cropperjs@2.0.1:
+ resolution: {integrity: sha512-hiJwk2SCPZqxMA7aR3byzLpYUqOrQo+ihMk8k/WRm/xe/LX8wNzAIzMwEB/NEGJYA6sbewxW9TUlrRUYi/2Ipg==}
+
+ cross-env@10.0.0:
+ resolution: {integrity: sha512-aU8qlEK/nHYtVuN4p7UQgAwVljzMg8hB4YK5ThRqD2l/ziSnryncPNn7bMLt5cFYsKVKBh8HqLqyCoTupEUu7Q==}
+ engines: {node: '>=20'}
+ hasBin: true
cross-env@7.0.3:
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
hasBin: true
- cross-fetch@3.1.6:
- resolution: {integrity: sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==}
-
cross-fetch@4.1.0:
resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==}
@@ -6101,8 +6141,8 @@ packages:
engines: {node: '>=4'}
hasBin: true
- cssnano-preset-default@7.0.8:
- resolution: {integrity: sha512-d+3R2qwrUV3g4LEMOjnndognKirBZISylDZAF/TPeCWVjEwlXS2e4eN4ICkoobRe7pD3H6lltinKVyS1AJhdjQ==}
+ cssnano-preset-default@7.0.9:
+ resolution: {integrity: sha512-tCD6AAFgYBOVpMBX41KjbvRh9c2uUjLXRyV7KHSIrwHiq5Z9o0TFfUCoM3TwVrRsRteN3sVXGNvjVNxYzkpTsA==}
engines: {node: ^18.12.0 || ^20.9.0 || >=22.0}
peerDependencies:
postcss: ^8.4.32
@@ -6113,8 +6153,8 @@ packages:
peerDependencies:
postcss: ^8.4.32
- cssnano@7.1.0:
- resolution: {integrity: sha512-Pu3rlKkd0ZtlCUzBrKL1Z4YmhKppjC1H9jo7u1o4qaKqyhvixFgu5qLyNIAOjSTg9DjVPtUqdROq2EfpVMEe+w==}
+ cssnano@7.1.1:
+ resolution: {integrity: sha512-fm4D8ti0dQmFPeF8DXSAA//btEmqCOgAc/9Oa3C1LW94h5usNrJEfrON7b4FkPZgnDEn6OUs5NdxiJZmAtGOpQ==}
engines: {node: ^18.12.0 || ^20.9.0 || >=22.0}
peerDependencies:
postcss: ^8.4.32
@@ -6130,8 +6170,8 @@ packages:
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
- cypress@14.5.2:
- resolution: {integrity: sha512-O4E4CEBqDHLDrJD/dfStHPcM+8qFgVVZ89Li7xDU0yL/JxO/V0PEcfF2I8aGa7uA2MGNLkNUAnghPM83UcHOJw==}
+ cypress@14.5.4:
+ resolution: {integrity: sha512-0Dhm4qc9VatOcI1GiFGVt8osgpPdqJLHzRwcAB5MSD/CAAts3oybvPUPawHyvJZUd8osADqZe/xzMsZ8sDTjXw==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
@@ -6147,26 +6187,14 @@ packages:
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
engines: {node: '>=18'}
- data-view-buffer@1.0.1:
- resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==}
- engines: {node: '>= 0.4'}
-
data-view-buffer@1.0.2:
resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
engines: {node: '>= 0.4'}
- data-view-byte-length@1.0.1:
- resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==}
- engines: {node: '>= 0.4'}
-
data-view-byte-length@1.0.2:
resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==}
engines: {node: '>= 0.4'}
- data-view-byte-offset@1.0.0:
- resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==}
- engines: {node: '>= 0.4'}
-
data-view-byte-offset@1.0.1:
resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
engines: {node: '>= 0.4'}
@@ -6391,9 +6419,6 @@ packages:
domutils@2.8.0:
resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==}
- domutils@3.1.0:
- resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==}
-
domutils@3.2.2:
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
@@ -6425,9 +6450,6 @@ packages:
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
- electron-to-chromium@1.5.155:
- resolution: {integrity: sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==}
-
electron-to-chromium@1.5.186:
resolution: {integrity: sha512-lur7L4BFklgepaJxj4DqPk7vKbTEl0pajNlg2QjE5shefmlmBLm2HvQ7PMf1R/GvlevT/581cop33/quQcfX3A==}
@@ -6483,10 +6505,6 @@ packages:
error-ex@1.3.2:
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
- es-abstract@1.23.3:
- resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==}
- engines: {node: '>= 0.4'}
-
es-abstract@1.24.0:
resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==}
engines: {node: '>= 0.4'}
@@ -6513,17 +6531,10 @@ packages:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
- es-shim-unscopables@1.0.2:
- resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
-
es-shim-unscopables@1.1.0:
resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==}
engines: {node: '>= 0.4'}
- es-to-primitive@1.2.1:
- resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
- engines: {node: '>= 0.4'}
-
es-to-primitive@1.3.0:
resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
engines: {node: '>= 0.4'}
@@ -6542,13 +6553,13 @@ packages:
peerDependencies:
esbuild: '>=0.12 <1'
- esbuild@0.25.4:
- resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==}
+ esbuild@0.25.8:
+ resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==}
engines: {node: '>=18'}
hasBin: true
- esbuild@0.25.6:
- resolution: {integrity: sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==}
+ esbuild@0.25.9:
+ resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==}
engines: {node: '>=18'}
hasBin: true
@@ -6589,27 +6600,6 @@ packages:
eslint-import-resolver-node@0.3.9:
resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
- eslint-module-utils@2.12.0:
- resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==}
- engines: {node: '>=4'}
- peerDependencies:
- '@typescript-eslint/parser': '*'
- eslint: '*'
- eslint-import-resolver-node: '*'
- eslint-import-resolver-typescript: '*'
- eslint-import-resolver-webpack: '*'
- peerDependenciesMeta:
- '@typescript-eslint/parser':
- optional: true
- eslint:
- optional: true
- eslint-import-resolver-node:
- optional: true
- eslint-import-resolver-typescript:
- optional: true
- eslint-import-resolver-webpack:
- optional: true
-
eslint-module-utils@2.12.1:
resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==}
engines: {node: '>=4'}
@@ -6631,16 +6621,6 @@ packages:
eslint-import-resolver-webpack:
optional: true
- eslint-plugin-import@2.31.0:
- resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==}
- engines: {node: '>=4'}
- peerDependencies:
- '@typescript-eslint/parser': '*'
- eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9
- peerDependenciesMeta:
- '@typescript-eslint/parser':
- optional: true
-
eslint-plugin-import@2.32.0:
resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==}
engines: {node: '>=4'}
@@ -6651,8 +6631,8 @@ packages:
'@typescript-eslint/parser':
optional: true
- eslint-plugin-vue@10.3.0:
- resolution: {integrity: sha512-A0u9snqjCfYaPnqqOaH6MBLVWDUIN4trXn8J3x67uDcXvR7X6Ut8p16N+nYhMCQ9Y7edg2BIRGzfyZsY0IdqoQ==}
+ eslint-plugin-vue@10.4.0:
+ resolution: {integrity: sha512-K6tP0dW8FJVZLQxa2S7LcE1lLw3X8VvB3t887Q6CLrFVxHYBXGANbXvwNzYIu6Ughx1bSJ5BDT0YB3ybPT39lw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@typescript-eslint/parser': ^7.0.0 || ^8.0.0
@@ -6673,16 +6653,12 @@ packages:
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- eslint-visitor-keys@4.2.0:
- resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
eslint-visitor-keys@4.2.1:
resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- eslint@9.31.0:
- resolution: {integrity: sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==}
+ eslint@9.34.0:
+ resolution: {integrity: sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true
peerDependencies:
@@ -6854,14 +6830,14 @@ packages:
fast-uri@3.0.1:
resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==}
- fast-xml-parser@4.4.1:
- resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==}
- hasBin: true
-
fast-xml-parser@4.5.0:
resolution: {integrity: sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==}
hasBin: true
+ fast-xml-parser@5.2.5:
+ resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==}
+ hasBin: true
+
fastify-plugin@4.5.1:
resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==}
@@ -6872,8 +6848,8 @@ packages:
resolution: {integrity: sha512-2qfoaQ3BQDhZ1gtbkKZd6n0kKxJISJGM6u/skD9ljdWItAscjXrtZ1lnjr7PavmXX9j4EyCPmBDiIsLn07d5vA==}
engines: {node: '>= 10'}
- fastify@5.3.3:
- resolution: {integrity: sha512-nCBiBCw9q6jPx+JJNVgO8JVnTXeUyrGcyTKPQikRkA/PanrFcOIo4R+ZnLeOLPZPGgzjomqfVarzE0kYx7qWiQ==}
+ fastify@5.5.0:
+ resolution: {integrity: sha512-ZWSWlzj3K/DcULCnCjEiC2zn2FBPdlZsSA/pnPa/dbUfLvxkD/Nqmb0XXMXLrWkeM4uQPUvjdJpwtXmTfriXqw==}
fastq@1.17.1:
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
@@ -6884,8 +6860,17 @@ packages:
fd-slicer@1.1.0:
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
- fdir@6.4.4:
- resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==}
+ fdir@6.4.6:
+ resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
@@ -6954,6 +6939,10 @@ packages:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
+ find-up@7.0.0:
+ resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==}
+ engines: {node: '>=18'}
+
find-versions@5.1.0:
resolution: {integrity: sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==}
engines: {node: '>=12'}
@@ -6983,17 +6972,10 @@ packages:
debug:
optional: true
- for-each@0.3.3:
- resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
-
for-each@0.3.5:
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
engines: {node: '>= 0.4'}
- foreground-child@3.1.1:
- resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
- engines: {node: '>=14'}
-
foreground-child@3.3.1:
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
engines: {node: '>=14'}
@@ -7009,8 +6991,8 @@ packages:
resolution: {integrity: sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==}
engines: {node: '>= 18'}
- form-data@4.0.3:
- resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==}
+ form-data@4.0.4:
+ resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
engines: {node: '>= 6'}
formdata-polyfill@4.0.10:
@@ -7069,10 +7051,6 @@ packages:
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
- function.prototype.name@1.1.6:
- resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==}
- engines: {node: '>= 0.4'}
-
function.prototype.name@1.1.8:
resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==}
engines: {node: '>= 0.4'}
@@ -7121,17 +7099,10 @@ packages:
resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==}
engines: {node: '>=18'}
- get-symbol-description@1.0.2:
- resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
- engines: {node: '>= 0.4'}
-
get-symbol-description@1.1.0:
resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
engines: {node: '>= 0.4'}
- get-tsconfig@4.10.0:
- resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==}
-
get-tsconfig@4.10.1:
resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
@@ -7156,11 +7127,6 @@ packages:
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
hasBin: true
- glob@11.0.2:
- resolution: {integrity: sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==}
- engines: {node: 20 || >=22}
- hasBin: true
-
glob@11.0.3:
resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==}
engines: {node: 20 || >=22}
@@ -7191,10 +7157,6 @@ packages:
resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==}
engines: {node: '>=18'}
- globalthis@1.0.3:
- resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==}
- engines: {node: '>= 0.4'}
-
globalthis@1.0.4:
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
engines: {node: '>= 0.4'}
@@ -7242,16 +7204,16 @@ packages:
resolution: {integrity: sha512-n0QrmT9lD81rbpKsyhnlz3DgnMZlaOkJPpgi746doA+HvaMC79bdWkwjrNnGJRvDrWTI8iOcJiVTJ5CdT/AZRw==}
engines: {node: '>=18.0.0'}
- happy-dom@17.6.3:
- resolution: {integrity: sha512-UVIHeVhxmxedbWPCfgS55Jg2rDfwf2BCKeylcPSqazLz5w3Kri7Q4xdBJubsr/+VUzFLh0VjIvh13RaDA2/Xug==}
+ happy-dom@18.0.1:
+ resolution: {integrity: sha512-qn+rKOW7KWpVTtgIUi6RVmTBZJSe2k0Db0vh1f7CWrWclkkc7/Q+FrOfkZIb2eiErLyqu5AXEzE7XthO9JVxRA==}
engines: {node: '>=20.0.0'}
hard-rejection@2.1.0:
resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==}
engines: {node: '>=6'}
- harfbuzzjs@0.4.7:
- resolution: {integrity: sha512-fGrMB7gk+x1ye++cN+OgDnHLLz8wg6aW26VPb8Q14V6XeZKGC0BCALe+ZEnwASU/b+YprBnRTELMJAlwy9jrLw==}
+ harfbuzzjs@0.4.9:
+ resolution: {integrity: sha512-ry24HoE67NmjDJ0tfSeRFOAntfZxF/hUgcy1tkc5uu65rXgdqEne4NlZ1+akSlkdZdQknKfxws98UhEbHYXBvQ==}
has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
@@ -7267,10 +7229,6 @@ packages:
has-property-descriptors@1.0.2:
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
- has-proto@1.0.3:
- resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==}
- engines: {node: '>= 0.4'}
-
has-proto@1.2.0:
resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==}
engines: {node: '>= 0.4'}
@@ -7332,9 +7290,6 @@ packages:
resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
engines: {node: '>=18'}
- html-entities@2.5.2:
- resolution: {integrity: sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==}
-
html-entities@2.6.0:
resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==}
@@ -7357,9 +7312,6 @@ packages:
htmlparser2@8.0.1:
resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==}
- htmlparser2@9.1.0:
- resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==}
-
http-cache-semantics@4.1.1:
resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
@@ -7507,10 +7459,6 @@ packages:
resolution: {integrity: sha512-+y6WywKZREw5rq7U2jvr2nmZpT7cbWbQQ0N/qfcseYnzHFz2cZz1Et52oY+XttYuYeTkI8Y+R2JNWj68MpQFSg==}
hasBin: true
- internal-slot@1.0.7:
- resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
- engines: {node: '>= 0.4'}
-
internal-slot@1.1.0:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'}
@@ -7518,10 +7466,13 @@ packages:
intersection-observer@0.12.2:
resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==}
- ioredis@5.6.1:
- resolution: {integrity: sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==}
+ ioredis@5.7.0:
+ resolution: {integrity: sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==}
engines: {node: '>=12.22.0'}
+ ios-haptics@0.1.0:
+ resolution: {integrity: sha512-Fk0RApBYJeZNZ9pW3Wx3WcunhdLlpEnVNy/BOn85tx39eZDOHLGhXEb7medoIURGBUjXatOZf5Ozy0+OG466YA==}
+
ip-address@9.0.5:
resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==}
engines: {node: '>= 12'}
@@ -7550,10 +7501,6 @@ packages:
resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
engines: {node: '>= 0.4'}
- is-array-buffer@3.0.4:
- resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
- engines: {node: '>= 0.4'}
-
is-array-buffer@3.0.5:
resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
engines: {node: '>= 0.4'}
@@ -7568,17 +7515,10 @@ packages:
resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==}
engines: {node: '>= 0.4'}
- is-bigint@1.0.4:
- resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
-
is-bigint@1.1.0:
resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==}
engines: {node: '>= 0.4'}
- is-boolean-object@1.1.2:
- resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
- engines: {node: '>= 0.4'}
-
is-boolean-object@1.2.2:
resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==}
engines: {node: '>= 0.4'}
@@ -7587,26 +7527,14 @@ packages:
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
engines: {node: '>= 0.4'}
- is-core-module@2.15.1:
- resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==}
- engines: {node: '>= 0.4'}
-
is-core-module@2.16.1:
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
engines: {node: '>= 0.4'}
- is-data-view@1.0.1:
- resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==}
- engines: {node: '>= 0.4'}
-
is-data-view@1.0.2:
resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==}
engines: {node: '>= 0.4'}
- is-date-object@1.0.5:
- resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
- engines: {node: '>= 0.4'}
-
is-date-object@1.1.0:
resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
engines: {node: '>= 0.4'}
@@ -7650,9 +7578,6 @@ packages:
resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==}
engines: {node: '>=10'}
- is-map@2.0.2:
- resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
-
is-map@2.0.3:
resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
engines: {node: '>= 0.4'}
@@ -7664,10 +7589,6 @@ packages:
is-node-process@1.2.0:
resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==}
- is-number-object@1.0.7:
- resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
- engines: {node: '>= 0.4'}
-
is-number-object@1.1.1:
resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
engines: {node: '>= 0.4'}
@@ -7698,25 +7619,14 @@ packages:
is-promise@2.2.2:
resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==}
- is-regex@1.1.4:
- resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
- engines: {node: '>= 0.4'}
-
is-regex@1.2.1:
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
engines: {node: '>= 0.4'}
- is-set@2.0.2:
- resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==}
-
is-set@2.0.3:
resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
engines: {node: '>= 0.4'}
- is-shared-array-buffer@1.0.3:
- resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==}
- engines: {node: '>= 0.4'}
-
is-shared-array-buffer@1.0.4:
resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
engines: {node: '>= 0.4'}
@@ -7733,10 +7643,6 @@ packages:
resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==}
engines: {node: '>=18'}
- is-string@1.0.7:
- resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
- engines: {node: '>= 0.4'}
-
is-string@1.1.1:
resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
engines: {node: '>= 0.4'}
@@ -7745,18 +7651,10 @@ packages:
resolution: {integrity: sha512-uVg5yifaTxHoefNf5Jcx+i9RZe2OBYd/UStp1umx+EERa4xGRa3LLGXjoEph43qUORC0qkafUgrXZ6zzK89yGA==}
engines: {node: '>=14.16'}
- is-symbol@1.0.4:
- resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
- engines: {node: '>= 0.4'}
-
is-symbol@1.1.1:
resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==}
engines: {node: '>= 0.4'}
- is-typed-array@1.1.13:
- resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==}
- engines: {node: '>= 0.4'}
-
is-typed-array@1.1.15:
resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
engines: {node: '>= 0.4'}
@@ -7772,23 +7670,14 @@ packages:
resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==}
engines: {node: '>=18'}
- is-weakmap@2.0.1:
- resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==}
-
is-weakmap@2.0.2:
resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
engines: {node: '>= 0.4'}
- is-weakref@1.0.2:
- resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
-
is-weakref@1.1.1:
resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==}
engines: {node: '>= 0.4'}
- is-weakset@2.0.2:
- resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==}
-
is-weakset@2.0.4:
resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
engines: {node: '>= 0.4'}
@@ -7848,10 +7737,6 @@ packages:
jackspeak@3.4.3:
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
- jackspeak@4.0.1:
- resolution: {integrity: sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==}
- engines: {node: 20 || >=22}
-
jackspeak@4.1.1:
resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==}
engines: {node: 20 || >=22}
@@ -7902,9 +7787,6 @@ packages:
resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
- jest-fetch-mock@3.0.3:
- resolution: {integrity: sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==}
-
jest-get-type@29.6.3:
resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -7978,9 +7860,6 @@ packages:
resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
- jest-websocket-mock@2.5.0:
- resolution: {integrity: sha512-a+UJGfowNIWvtIKIQBHoEWIUqRxxQHFx4CXT+R5KxxKBtEQ5rS3pPOV/5299sHzqbmeCzxxY5qE4+yfXePePig==}
-
jest-worker@29.7.0:
resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -8037,10 +7916,6 @@ packages:
resolution: {integrity: sha512-/kmVISmrwVwtyYU40iQUOp3SUPk2dhNCMsZBQX0R1/jZ8maaXJ/oZIzUOiyOqcgtLnETFKYChbJ5iDC/eWmFHg==}
engines: {node: '>=0.1.90'}
- jsdoc-type-pratt-parser@4.1.0:
- resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==}
- engines: {node: '>=12.0.0'}
-
jsdom@26.1.0:
resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==}
engines: {node: '>=18'}
@@ -8208,6 +8083,10 @@ packages:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
+ locate-path@7.2.0:
+ resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
lodash.defaults@4.2.0:
resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
@@ -8247,9 +8126,6 @@ packages:
longest-streak@3.1.0:
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
- loupe@3.1.3:
- resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==}
-
loupe@3.1.4:
resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==}
@@ -8283,13 +8159,12 @@ packages:
resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
hasBin: true
- magic-string@0.27.0:
- resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==}
- engines: {node: '>=12'}
-
magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+ magic-string@0.30.18:
+ resolution: {integrity: sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==}
+
magicast@0.3.5:
resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==}
@@ -8381,8 +8256,8 @@ packages:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'}
- meilisearch@0.51.0:
- resolution: {integrity: sha512-IuNsYyT8r/QLhU33XDZdXWUT6cA/nACCHHZc+NHkNuaU55LELRxff1uBJhpJcwjYaAPEEmeWrzBhYl2XlhJdAg==}
+ meilisearch@0.52.0:
+ resolution: {integrity: sha512-RqPsB4a78sXf/ATB7PIVvKCG7yf0y1M+uCj8Z9Wku44WmCy3iz0C1PHjVV5xphQolo09CdhdyFoRxHQSJkOdpg==}
memoizerific@1.11.3:
resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==}
@@ -8408,11 +8283,11 @@ packages:
resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
engines: {node: '>= 0.6'}
- mfm-js@0.24.0:
- resolution: {integrity: sha512-6m8N0ElH9/4CA1izhVqmxTfLj5Z9RspdqM/lMew4xU/UTgm4Pf//VpDunpasxbRFjeJSVW+zoVwL4ZPfPtfiQg==}
+ mfm-js@0.25.0:
+ resolution: {integrity: sha512-JoK5TOtswXIvZSZ9hUEL+ZkcNV4onu/DtkaKeXK846+sJBBF8DvxYmPutt7nPaRDsUMmJGr64PNVMFpMGdk3hw==}
- microformats-parser@2.0.3:
- resolution: {integrity: sha512-MhZoQXkvWrvI3iu1IJ7Zk2VglphPt5P482E5rXvHalvtxhuS/QioMB8gEr8omt7Fd0K/qQEAbTmabvNpCbi3EQ==}
+ microformats-parser@2.0.4:
+ resolution: {integrity: sha512-DA2yt3uz2JjupBGoNvaG9ngBP5vSTI1ky2yhxBai/RnQrlzo+gEzuCdvwIIjj2nh3uVPDybTP5u7uua7pOa6LA==}
engines: {node: '>=18'}
micromark-core-commonmark@2.0.0:
@@ -8553,9 +8428,6 @@ packages:
resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==}
engines: {node: 20 || >=22}
- minimatch@3.0.8:
- resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==}
-
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
@@ -8667,8 +8539,8 @@ packages:
peerDependencies:
msw: ^2.0.0
- msw@2.10.4:
- resolution: {integrity: sha512-6R1or/qyele7q3RyPwNuvc0IxO8L8/Aim6Sz5ncXEgcWUNxSKE+udriTOWHtpMwmfkLYlacA2y7TIx4cL5lgHA==}
+ msw@2.10.5:
+ resolution: {integrity: sha512-0EsQCrCI1HbhpBWd89DvmxY6plmvrM96b0sCIztnvcNHQbXn5vqwm1KlXslo6u4wN9LFGLC1WFjjgljcQhe40A==}
engines: {node: '>=18'}
hasBin: true
peerDependencies:
@@ -8680,8 +8552,8 @@ packages:
muggle-string@0.4.1:
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
- multer@2.0.1:
- resolution: {integrity: sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==}
+ multer@2.0.2:
+ resolution: {integrity: sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==}
engines: {node: '>= 10.16.0'}
mute-stream@2.0.0:
@@ -8703,11 +8575,6 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
- nanoid@3.3.8:
- resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
- engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
- hasBin: true
-
nanoid@5.1.5:
resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==}
engines: {node: ^18 || >=20}
@@ -8750,10 +8617,6 @@ packages:
nise@6.1.1:
resolution: {integrity: sha512-aMSAzLVY7LyeM60gvBS423nBmIPP+Wy7St7hsb+8/fc1HmeoHJfLO8CKse4u3BtOZvQLJghYPI2i/1WZrEj5/g==}
- node-abi@3.74.0:
- resolution: {integrity: sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==}
- engines: {node: '>=10'}
-
node-abi@3.75.0:
resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==}
engines: {node: '>=10'}
@@ -8914,10 +8777,6 @@ packages:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
engines: {node: '>= 0.4'}
- object.assign@4.1.5:
- resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
- engines: {node: '>= 0.4'}
-
object.assign@4.1.7:
resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
engines: {node: '>= 0.4'}
@@ -8930,10 +8789,6 @@ packages:
resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==}
engines: {node: '>= 0.4'}
- object.values@1.2.0:
- resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==}
- engines: {node: '>= 0.4'}
-
object.values@1.2.1:
resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
engines: {node: '>= 0.4'}
@@ -8976,8 +8831,8 @@ packages:
openapi-types@12.1.3:
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
- openapi-typescript@7.8.0:
- resolution: {integrity: sha512-1EeVWmDzi16A+siQlo/SwSGIT7HwaFAVjvMA7/jG5HMLSnrUOzPL7uSTRZZa4v/LCRxHTApHKtNY6glApEoiUQ==}
+ openapi-typescript@7.9.1:
+ resolution: {integrity: sha512-9gJtoY04mk6iPMbToPjPxEAtfXZ0dTsMZtsgUI8YZta0btPPig9DJFP4jlerQD/7QOwYgb0tl+zLUpDf7vb7VA==}
hasBin: true
peerDependencies:
typescript: ^5.x
@@ -8992,8 +8847,8 @@ packages:
ospath@1.2.2:
resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==}
- otpauth@9.4.0:
- resolution: {integrity: sha512-fHIfzIG5RqCkK9cmV8WU+dPQr9/ebR5QOwGZn2JAr1RQF+lmAuLL2YdtdqvmBjNmgJlYk3KZ4a0XokaEhg1Jsw==}
+ otpauth@9.4.1:
+ resolution: {integrity: sha512-+iVvys36CFsyXEqfNftQm1II7SW23W1wx9RwNk0Cd97lbvorqAhBDksb/0bYry087QMxjiuBS0wokdoZ0iUeAw==}
outvariant@1.4.3:
resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==}
@@ -9022,6 +8877,10 @@ packages:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'}
+ p-limit@4.0.0:
+ resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
p-locate@4.1.0:
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
engines: {node: '>=8'}
@@ -9030,6 +8889,10 @@ packages:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'}
+ p-locate@6.0.0:
+ resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
p-map@4.0.0:
resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
engines: {node: '>=10'}
@@ -9075,9 +8938,6 @@ packages:
parse5-htmlparser2-tree-adapter@6.0.1:
resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==}
- parse5-htmlparser2-tree-adapter@7.0.0:
- resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==}
-
parse5-htmlparser2-tree-adapter@7.1.0:
resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==}
@@ -9104,6 +8964,10 @@ packages:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
+ path-exists@5.0.0:
+ resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
path-is-absolute@1.0.1:
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
engines: {node: '>=0.10.0'}
@@ -9165,31 +9029,34 @@ packages:
performance-now@2.1.0:
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
- pg-cloudflare@1.2.5:
- resolution: {integrity: sha512-OOX22Vt0vOSRrdoUPKJ8Wi2OpE/o/h9T8X1s4qSkCedbNah9ei2W2765be8iMVxQUsvgT7zIAT2eIa9fs5+vtg==}
+ pg-cloudflare@1.2.7:
+ resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==}
- pg-connection-string@2.9.0:
- resolution: {integrity: sha512-P2DEBKuvh5RClafLngkAuGe9OUlFV7ebu8w1kmaaOgPcpJd1RIFh7otETfI6hAR8YupOLFTY7nuvvIn7PLciUQ==}
+ pg-connection-string@2.9.1:
+ resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==}
pg-int8@1.0.1:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
engines: {node: '>=4.0.0'}
- pg-pool@3.10.0:
- resolution: {integrity: sha512-DzZ26On4sQ0KmqnO34muPcmKbhrjmyiO4lCCR0VwEd7MjmiKf5NTg/6+apUEu0NF7ESa37CGzFxH513CoUmWnA==}
+ pg-pool@3.10.1:
+ resolution: {integrity: sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==}
peerDependencies:
pg: '>=8.0'
pg-protocol@1.10.0:
resolution: {integrity: sha512-IpdytjudNuLv8nhlHs/UrVBhU0e78J0oIS/0AVdTbWxSOkFUVdsHC/NrorO6nXsQNDTT1kzDSOMJubBQviX18Q==}
+ pg-protocol@1.10.3:
+ resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==}
+
pg-types@2.2.0:
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
engines: {node: '>=4'}
- pg@8.16.0:
- resolution: {integrity: sha512-7SKfdvP8CTNXjMUzfcVTaI+TDzBEeaUnVwiVGZQD1Hh33Kpev7liQba9uLd4CfN8r9mCVsD0JIpq03+Unpz+kg==}
- engines: {node: '>= 8.0.0'}
+ pg@8.16.3:
+ resolution: {integrity: sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==}
+ engines: {node: '>= 16.0.0'}
peerDependencies:
pg-native: '>=3.0.1'
peerDependenciesMeta:
@@ -9210,8 +9077,8 @@ packages:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
- picomatch@4.0.2:
- resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
+ picomatch@4.0.3:
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
pid-port@1.0.2:
@@ -9262,8 +9129,8 @@ packages:
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
engines: {node: '>=10.13.0'}
- pnpm@10.13.1:
- resolution: {integrity: sha512-N+vxpcejDV+r4MXfRO6NpMllygxa89urKMOhaBtwolYhjQXIHJwNz3Z+9rhVHrW5YAQrntQwDFkkIzY3fgHPrQ==}
+ pnpm@10.15.0:
+ resolution: {integrity: sha512-SG68JZ0+mZpOhpHOA7XKxKccvso5Nyqbdiy1AM/fCHPiyxar49lRse4s8BJQPwJ7mLZYTk3yJSTgx0UNnseqew==}
engines: {node: '>=18.12'}
hasBin: true
@@ -9287,8 +9154,8 @@ packages:
peerDependencies:
postcss: ^8.4.32
- postcss-convert-values@7.0.6:
- resolution: {integrity: sha512-MD/eb39Mr60hvgrqpXsgbiqluawYg/8K4nKsqRsuDX9f+xN1j6awZCUv/5tLH8ak3vYp/EMXwdcnXvfZYiejCQ==}
+ postcss-convert-values@7.0.7:
+ resolution: {integrity: sha512-HR9DZLN04Xbe6xugRH6lS4ZQH2zm/bFh/ZyRkpedZozhvh+awAfbA0P36InO4fZfDhvYfNJeNvlTf1sjwGbw/A==}
engines: {node: ^18.12.0 || ^20.9.0 || >=22.0}
peerDependencies:
postcss: ^8.4.32
@@ -9448,10 +9315,6 @@ packages:
postcss-value-parser@4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
- postcss@8.5.3:
- resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
- engines: {node: ^10 || ^12 || >=14}
-
postcss@8.5.6:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
engines: {node: ^10 || ^12 || >=14}
@@ -9540,9 +9403,6 @@ packages:
promise-limit@2.7.0:
resolution: {integrity: sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==}
- promise-polyfill@8.3.0:
- resolution: {integrity: sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==}
-
promise-retry@2.0.1:
resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==}
engines: {node: '>=10'}
@@ -9713,14 +9573,14 @@ packages:
peerDependencies:
typescript: '>= 4.3.x'
- react-docgen@7.0.1:
- resolution: {integrity: sha512-rCz0HBIT0LWbIM+///LfRrJoTKftIzzwsYDf0ns5KwaEjejMHQRtphcns+IXFHDNY9pnz6G8l/JbbI6pD4EAIA==}
- engines: {node: '>=16.14.0'}
+ react-docgen@8.0.0:
+ resolution: {integrity: sha512-kmob/FOTwep7DUWf9KjuenKX0vyvChr3oTdvvPt09V60Iz75FJp+T/0ZeHMbAfJj2WaVWqAPP5Hmm3PYzSPPKg==}
+ engines: {node: ^20.9.0 || >=22}
- react-dom@19.1.0:
- resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==}
+ react-dom@19.1.1:
+ resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==}
peerDependencies:
- react: ^19.1.0
+ react: ^19.1.1
react-is@17.0.2:
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
@@ -9728,8 +9588,8 @@ packages:
react-is@18.2.0:
resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
- react@19.1.0:
- resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
+ react@19.1.1:
+ resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==}
engines: {node: '>=0.10.0'}
read-pkg-up@7.0.1:
@@ -9810,10 +9670,6 @@ packages:
regex@6.0.1:
resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==}
- regexp.prototype.flags@1.5.2:
- resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==}
- engines: {node: '>= 0.4'}
-
regexp.prototype.flags@1.5.4:
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
engines: {node: '>= 0.4'}
@@ -9914,8 +9770,13 @@ packages:
resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==}
hasBin: true
- rollup@4.45.1:
- resolution: {integrity: sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==}
+ rollup@4.46.2:
+ resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ rollup@4.48.0:
+ resolution: {integrity: sha512-BXHRqK1vyt9XVSEHZ9y7xdYtuYbwVod2mLwOMFP7t/Eqoc1pHRlG/WdV2qNeNvZHRQdLedaFycljaYYM96RqJQ==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
@@ -9931,10 +9792,6 @@ packages:
rxjs@7.8.2:
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
- safe-array-concat@1.1.2:
- resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==}
- engines: {node: '>=0.4'}
-
safe-array-concat@1.1.3:
resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
engines: {node: '>=0.4'}
@@ -9949,10 +9806,6 @@ packages:
resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
engines: {node: '>= 0.4'}
- safe-regex-test@1.0.3:
- resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==}
- engines: {node: '>= 0.4'}
-
safe-regex-test@1.1.0:
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
engines: {node: '>= 0.4'}
@@ -9970,14 +9823,11 @@ packages:
sanitize-html@2.17.0:
resolution: {integrity: sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==}
- sass@1.89.2:
- resolution: {integrity: sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==}
+ sass@1.90.0:
+ resolution: {integrity: sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==}
engines: {node: '>=14.0.0'}
hasBin: true
- sax@1.2.4:
- resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==}
-
sax@1.4.1:
resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
@@ -10082,8 +9932,8 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
- shiki@3.8.0:
- resolution: {integrity: sha512-yPqK0y68t20aakv+3aMTpUMJZd6UHaBY2/SBUDowh9M70gVUwqT0bf7Kz5CWG0AXfHtFvXCHhBBHVAzdp0ILoQ==}
+ shiki@3.11.0:
+ resolution: {integrity: sha512-VgKumh/ib38I1i3QkMn6mAQA6XjjQubqaAYhfge71glAll0/4xnt8L2oSuC45Qcr/G5Kbskj4RliMQddGmy/Og==}
shimmer@1.2.1:
resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==}
@@ -10327,8 +10177,8 @@ packages:
standard-as-callback@2.1.0:
resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
- start-server-and-test@2.0.12:
- resolution: {integrity: sha512-U6QiS5qsz+DN5RfJJrkAXdooxMDnLZ+n5nR8kaX//ZH19SilF6b58Z3zM9zTfrNIkJepzauHo4RceSgvgUSX9w==}
+ start-server-and-test@2.0.13:
+ resolution: {integrity: sha512-G42GCIUjBv/nDoK+QsO+nBdX2Cg3DSAKhSic2DN0GLlK4Q+63TkOeN1cV9PHZKnVOzDKGNVZGCREjpvAIAOdiQ==}
engines: {node: '>=16'}
hasBin: true
@@ -10339,10 +10189,6 @@ packages:
std-env@3.9.0:
resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==}
- stop-iteration-iterator@1.0.0:
- resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==}
- engines: {node: '>= 0.4'}
-
stop-iteration-iterator@1.1.0:
resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
engines: {node: '>= 0.4'}
@@ -10366,8 +10212,8 @@ packages:
react-dom:
optional: true
- storybook@8.6.14:
- resolution: {integrity: sha512-sVKbCj/OTx67jhmauhxc2dcr1P+yOgz/x3h0krwjyMgdc5Oubvxyg4NYDZmzAw+ym36g/lzH8N0Ccp4dwtdfxw==}
+ storybook@9.1.3:
+ resolution: {integrity: sha512-Sm+qP3iGb/QKx/jTYdfE0mIeTmA2HF+5k9fD70S9oOJq3F9UdW8MLgs+5PE+E/xAfDjZU4OWAKEOyA6EYIvQHg==}
hasBin: true
peerDependencies:
prettier: ^2 || ^3
@@ -10417,13 +10263,6 @@ packages:
resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==}
engines: {node: '>= 0.4'}
- string.prototype.trim@1.2.9:
- resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==}
- engines: {node: '>= 0.4'}
-
- string.prototype.trimend@1.0.8:
- resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==}
-
string.prototype.trimend@1.0.9:
resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==}
engines: {node: '>= 0.4'}
@@ -10497,6 +10336,9 @@ packages:
strnum@1.0.5:
resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==}
+ strnum@2.1.1:
+ resolution: {integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==}
+
strtok3@10.2.2:
resolution: {integrity: sha512-Xt18+h4s7Z8xyZ0tmBoRmzxcop97R4BAh+dXouUDCYn+Em+1P3qpkUfI5ueWLT8ynC5hZ+q4iPEmGG1urvQGBg==}
engines: {node: '>=18'}
@@ -10511,18 +10353,16 @@ packages:
peerDependencies:
postcss: ^8.4.32
- superagent@10.2.1:
- resolution: {integrity: sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==}
+ superagent@10.2.3:
+ resolution: {integrity: sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==}
engines: {node: '>=14.18.0'}
- deprecated: Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net
- supertest@7.1.1:
- resolution: {integrity: sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==}
+ supertest@7.1.4:
+ resolution: {integrity: sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==}
engines: {node: '>=14.18.0'}
- deprecated: Please upgrade to supertest v7.1.3+, see release notes at https://github.com/forwardemail/supertest/releases/tag/v7.1.3 - maintenance is supported by Forward Email @ https://forwardemail.net
- supports-color@10.0.0:
- resolution: {integrity: sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==}
+ supports-color@10.2.0:
+ resolution: {integrity: sha512-5eG9FQjEjDbAlI5+kdpdyPIBMRH4GfTVDGREVupaZHmVoppknhM29b/S9BkQz7cathp85BVgRi/As3Siln7e0Q==}
engines: {node: '>=18'}
supports-color@5.5.0:
@@ -10553,8 +10393,8 @@ packages:
symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
- systeminformation@5.27.1:
- resolution: {integrity: sha512-FgkVpT6GgATtNvADgtEzDxI/SVaBisfnQ4fmgQZhCJ4335noTgt9q6O81ioHwzs9HgnJaaFSdHSEMIkneZ55iA==}
+ systeminformation@5.27.7:
+ resolution: {integrity: sha512-saaqOoVEEFaux4v0K8Q7caiauRwjXC4XbD2eH60dxHXbpKxQ8kH9Rf7Jh+nryKpOUSEFxtCdBlSUx0/lO6rwRg==}
engines: {node: '>=8.0.0'}
os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
hasBin: true
@@ -10607,8 +10447,8 @@ packages:
thread-stream@3.1.0:
resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==}
- three@0.178.0:
- resolution: {integrity: sha512-ybFIB0+x8mz0wnZgSGy2MO/WCO6xZhQSZnmfytSPyNpM0sBafGRVhdaj+erYh5U+RhQOAg/eXqw5uVDiM2BjhQ==}
+ three@0.179.1:
+ resolution: {integrity: sha512-5y/elSIQbrvKOISxpwXCR4sQqHtGiOI+MKLc3SsBdDXA2hz3Mdp3X59aUp8DyybMa34aeBwbFTpdoLJaUDEWSw==}
throttle-debounce@5.0.2:
resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
@@ -10635,10 +10475,6 @@ packages:
tinyexec@0.3.2:
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
- tinyglobby@0.2.13:
- resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==}
- engines: {node: '>=12.0.0'}
-
tinyglobby@0.2.14:
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
engines: {node: '>=12.0.0'}
@@ -10670,8 +10506,8 @@ packages:
resolution: {integrity: sha512-rv8LUyez4Ygkopqn+M6OLItAOT9FF3REpPQDkdMx5ix8w4qkuE7Vo2o/vw1nxKQYmJDV8JpAMJQr1b+lTKf0FA==}
hasBin: true
- tmp@0.2.3:
- resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==}
+ tmp@0.2.5:
+ resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==}
engines: {node: '>=14.14'}
tmpl@1.0.5:
@@ -10680,10 +10516,6 @@ packages:
to-data-view@1.1.0:
resolution: {integrity: sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ==}
- to-fast-properties@2.0.0:
- resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
- engines: {node: '>=4'}
-
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
@@ -10764,16 +10596,21 @@ packages:
resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==}
engines: {node: '>=6'}
- tsd@0.32.0:
- resolution: {integrity: sha512-R5lBZCbxGBowOcW0gpQaiIjGYrG5NmU+PfFDKcc3zbtzWjML1o/zAwzdDnS2ZheSlPu9GW51azpFqEPUBq9DoQ==}
+ tsd@0.33.0:
+ resolution: {integrity: sha512-/PQtykJFVw90QICG7zyPDMIyueOXKL7jOJVoX5pILnb3Ux+7QqynOxfVvarE+K+yi7BZyOSY4r+OZNWSWRiEwQ==}
engines: {node: '>=14.16'}
hasBin: true
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
- tsx@4.20.3:
- resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==}
+ tsx@4.20.4:
+ resolution: {integrity: sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==}
+ engines: {node: '>=18.0.0'}
+ hasBin: true
+
+ tsx@4.20.5:
+ resolution: {integrity: sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==}
engines: {node: '>=18.0.0'}
hasBin: true
@@ -10811,10 +10648,6 @@ packages:
resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
engines: {node: '>=12.20'}
- type-fest@4.26.1:
- resolution: {integrity: sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==}
- engines: {node: '>=16'}
-
type-fest@4.41.0:
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
engines: {node: '>=16'}
@@ -10823,34 +10656,18 @@ packages:
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
engines: {node: '>= 0.6'}
- typed-array-buffer@1.0.2:
- resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==}
- engines: {node: '>= 0.4'}
-
typed-array-buffer@1.0.3:
resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
engines: {node: '>= 0.4'}
- typed-array-byte-length@1.0.1:
- resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==}
- engines: {node: '>= 0.4'}
-
typed-array-byte-length@1.0.3:
resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==}
engines: {node: '>= 0.4'}
- typed-array-byte-offset@1.0.2:
- resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==}
- engines: {node: '>= 0.4'}
-
typed-array-byte-offset@1.0.4:
resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==}
engines: {node: '>= 0.4'}
- typed-array-length@1.0.6:
- resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==}
- engines: {node: '>= 0.4'}
-
typed-array-length@1.0.7:
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
engines: {node: '>= 0.4'}
@@ -10858,15 +10675,14 @@ packages:
typedarray@0.0.6:
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
- typeorm@0.3.24:
- resolution: {integrity: sha512-4IrHG7A0tY8l5gEGXfW56VOMfUVWEkWlH/h5wmcyZ+V8oCiLj7iTPp0lEjMEZVrxEkGSdP9ErgTKHKXQApl/oA==}
+ typeorm@0.3.26:
+ resolution: {integrity: sha512-o2RrBNn3lczx1qv4j+JliVMmtkPSqEGpG0UuZkt9tCfWkoXKu8MZnjvp2GjWPll1SehwemQw6xrbVRhmOglj8Q==}
engines: {node: '>=16.13.0'}
hasBin: true
peerDependencies:
'@google-cloud/spanner': ^5.18.0 || ^6.0.0 || ^7.0.0
- '@sap/hana-client': ^2.12.25
- better-sqlite3: ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0
- hdb-pool: ^0.1.6
+ '@sap/hana-client': ^2.14.22
+ better-sqlite3: ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0
ioredis: ^5.0.4
mongodb: ^5.8.0 || ^6.0.0
mssql: ^9.1.1 || ^10.0.1 || ^11.0.1
@@ -10875,7 +10691,7 @@ packages:
pg: ^8.5.1
pg-native: ^3.0.0
pg-query-stream: ^4.0.0
- redis: ^3.1.1 || ^4.0.0
+ redis: ^3.1.1 || ^4.0.0 || ^5.0.14
reflect-metadata: ^0.1.14 || ^0.2.0
sql.js: ^1.4.0
sqlite3: ^5.0.3
@@ -10888,8 +10704,6 @@ packages:
optional: true
better-sqlite3:
optional: true
- hdb-pool:
- optional: true
ioredis:
optional: true
mongodb:
@@ -10922,8 +10736,8 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
- typescript@5.8.3:
- resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
+ typescript@5.9.2:
+ resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
engines: {node: '>=14.17'}
hasBin: true
@@ -10942,9 +10756,6 @@ packages:
resolution: {integrity: sha512-fIRiVTJNcSRmXKPZtGzFQv9WRrZ3M9eoptl/teFJvjOzmpU+/K/JH6HZ8deBfb5vMEpicJcLn7JmvdknlMq7Zg==}
hasBin: true
- unbox-primitive@1.0.2:
- resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
-
unbox-primitive@1.1.0:
resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
engines: {node: '>= 0.4'}
@@ -10970,6 +10781,10 @@ packages:
resolution: {integrity: sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==}
engines: {node: '>=20.18.1'}
+ unicorn-magic@0.1.0:
+ resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==}
+ engines: {node: '>=18'}
+
unicorn-magic@0.3.0:
resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==}
engines: {node: '>=18'}
@@ -11048,9 +10863,6 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
- util@0.12.5:
- resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==}
-
utils-merge@1.0.1:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
engines: {node: '>= 0.4.0'}
@@ -11109,19 +10921,59 @@ packages:
vite-plugin-turbosnap@1.0.3:
resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
- vite@6.3.5:
- resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==}
- engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ vite@7.0.6:
+ resolution: {integrity: sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
- '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+ '@types/node': ^20.19.0 || >=22.12.0
+ jiti: '>=1.21.0'
+ less: ^4.0.0
+ lightningcss: ^1.21.0
+ sass: ^1.70.0
+ sass-embedded: ^1.70.0
+ stylus: '>=0.54.8'
+ sugarss: ^5.0.0
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ vite@7.1.3:
+ resolution: {integrity: sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^20.19.0 || >=22.12.0
jiti: '>=1.21.0'
- less: '*'
+ less: ^4.0.0
lightningcss: ^1.21.0
- sass: '*'
- sass-embedded: '*'
- stylus: '*'
- sugarss: '*'
+ sass: ^1.70.0
+ sass-embedded: ^1.70.0
+ stylus: '>=0.54.8'
+ sugarss: ^5.0.0
terser: ^5.16.0
tsx: ^4.8.1
yaml: ^2.4.2
@@ -11155,6 +11007,11 @@ packages:
peerDependencies:
vitest: '>=2.0.0'
+ vitest-websocket-mock@0.5.0:
+ resolution: {integrity: sha512-vzBWeuF/kD/OCOFzB7WAclb7PxfI105qPkZtdOkPMwZdilBskQjJL4l319JtPtmeovDU7ZVhO3hTfGPjM4txQQ==}
+ peerDependencies:
+ vitest: '>=3'
+
vitest@3.2.4:
resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@@ -11218,11 +11075,8 @@ packages:
vue-component-type-helpers@2.0.16:
resolution: {integrity: sha512-qisL/iAfdO++7w+SsfYQJVPj6QKvxp4i1MMxvsNO41z/8zu3KuAw9LkhKUfP/kcOWGDxESp+pQObWppXusejCA==}
- vue-component-type-helpers@2.2.12:
- resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==}
-
- vue-component-type-helpers@3.0.1:
- resolution: {integrity: sha512-j23mCB5iEbGsyIhnVdXdWUOg+UdwmVxpKnYYf2j+4ppCt5VSFXKjwu9YFt0QYxUaf5G99PuHsVfRScjHCRSsGQ==}
+ vue-component-type-helpers@3.0.6:
+ resolution: {integrity: sha512-6CRM8X7EJqWCJOiKPvSLQG+hJPb/Oy2gyJx3pLjUEhY7PuaCthQu3e0zAGI1lqUBobrrk9IT0K8sG2GsCluxoQ==}
vue-demi@0.14.7:
resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
@@ -11254,14 +11108,14 @@ packages:
vue-template-compiler@2.7.14:
resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==}
- vue-tsc@2.2.12:
- resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==}
+ vue-tsc@3.0.6:
+ resolution: {integrity: sha512-Tbs8Whd43R2e2nxez4WXPvvdjGbW24rOSgRhLOHXzWiT4pcP4G7KeWh0YCn18rF4bVwv7tggLLZ6MJnO6jXPBg==}
hasBin: true
peerDependencies:
typescript: '>=5.0.0'
- vue@3.5.17:
- resolution: {integrity: sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==}
+ vue@3.5.19:
+ resolution: {integrity: sha512-ZRh0HTmw6KChRYWgN8Ox/wi7VhpuGlvMPrHjIsdRbzKNgECFLzy+dKL5z9yGaBSjCpmcfJCbh3I1tNSRmBz2tg==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
@@ -11277,8 +11131,8 @@ packages:
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
engines: {node: '>=18'}
- wait-on@8.0.3:
- resolution: {integrity: sha512-nQFqAFzZDeRxsu7S3C7LbuxslHhk+gnJZHyethuGKAn2IVleIbTB9I3vJSQiSR+DifUqmdzfPMoMPJfLqMF2vw==}
+ wait-on@8.0.4:
+ resolution: {integrity: sha512-8f9LugAGo4PSc0aLbpKVCVtzayd36sSCp4WLpVngkYq6PK87H79zt77/tlCU6eKCLqR46iFvcl0PU5f+DmtkwA==}
engines: {node: '>=12.0.0'}
hasBin: true
@@ -11343,9 +11197,6 @@ packages:
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
- which-boxed-primitive@1.0.2:
- resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
-
which-boxed-primitive@1.1.1:
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
engines: {node: '>= 0.4'}
@@ -11354,9 +11205,6 @@ packages:
resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==}
engines: {node: '>= 0.4'}
- which-collection@1.0.1:
- resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==}
-
which-collection@1.0.2:
resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
engines: {node: '>= 0.4'}
@@ -11364,10 +11212,6 @@ packages:
which-module@2.0.0:
resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==}
- which-typed-array@1.1.15:
- resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==}
- engines: {node: '>= 0.4'}
-
which-typed-array@1.1.19:
resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
engines: {node: '>= 0.4'}
@@ -11421,8 +11265,8 @@ packages:
resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
- ws@8.18.2:
- resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==}
+ ws@8.18.3:
+ resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
@@ -11518,6 +11362,10 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
+ yocto-queue@1.2.1:
+ resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==}
+ engines: {node: '>=12.20'}
+
yoctocolors-cjs@2.1.2:
resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==}
engines: {node: '>=18'}
@@ -11539,46 +11387,49 @@ snapshots:
'@ampproject/remapping@2.3.0':
dependencies:
- '@jridgewell/gen-mapping': 0.3.5
- '@jridgewell/trace-mapping': 0.3.25
+ '@jridgewell/gen-mapping': 0.3.12
+ '@jridgewell/trace-mapping': 0.3.29
- '@analytics/cookie-utils@0.2.12':
+ '@analytics/cookie-utils@0.2.14':
dependencies:
- '@analytics/global-storage-utils': 0.1.7
+ '@analytics/global-storage-utils': 0.1.9
- '@analytics/core@0.12.17(@types/dlv@1.1.5)':
+ '@analytics/core@0.13.2(@types/dlv@1.1.5)':
dependencies:
- '@analytics/global-storage-utils': 0.1.7
- '@analytics/type-utils': 0.6.2
- analytics-utils: 1.0.14(@types/dlv@1.1.5)
+ '@analytics/global-storage-utils': 0.1.9
+ '@analytics/type-utils': 0.6.4
+ analytics-utils: 1.1.1(@types/dlv@1.1.5)
transitivePeerDependencies:
- '@types/dlv'
- '@analytics/global-storage-utils@0.1.7':
+ '@analytics/global-storage-utils@0.1.9':
dependencies:
- '@analytics/type-utils': 0.6.2
+ '@analytics/type-utils': 0.6.4
'@analytics/google-analytics@1.1.0': {}
- '@analytics/localstorage-utils@0.1.10':
+ '@analytics/localstorage-utils@0.1.12':
dependencies:
- '@analytics/global-storage-utils': 0.1.7
+ '@analytics/global-storage-utils': 0.1.9
- '@analytics/session-storage-utils@0.0.7':
+ '@analytics/session-storage-utils@0.0.9':
dependencies:
- '@analytics/global-storage-utils': 0.1.7
+ '@analytics/global-storage-utils': 0.1.9
- '@analytics/storage-utils@0.4.2':
+ '@analytics/storage-utils@0.4.4':
dependencies:
- '@analytics/cookie-utils': 0.2.12
- '@analytics/global-storage-utils': 0.1.7
- '@analytics/localstorage-utils': 0.1.10
- '@analytics/session-storage-utils': 0.0.7
- '@analytics/type-utils': 0.6.2
+ '@analytics/cookie-utils': 0.2.14
+ '@analytics/global-storage-utils': 0.1.9
+ '@analytics/localstorage-utils': 0.1.12
+ '@analytics/session-storage-utils': 0.0.9
+ '@analytics/type-utils': 0.6.4
- '@analytics/type-utils@0.6.2': {}
+ '@analytics/type-utils@0.6.4': {}
- '@apidevtools/swagger-methods@3.0.2': {}
+ '@apidevtools/json-schema-ref-parser@14.1.1':
+ dependencies:
+ '@types/json-schema': 7.0.15
+ js-yaml: 4.1.0
'@asamuzakjp/css-color@2.8.3':
dependencies:
@@ -11591,20 +11442,20 @@ snapshots:
'@aws-crypto/crc32@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
- '@aws-sdk/types': 3.821.0
+ '@aws-sdk/types': 3.862.0
tslib: 2.8.1
'@aws-crypto/crc32c@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
- '@aws-sdk/types': 3.821.0
+ '@aws-sdk/types': 3.862.0
tslib: 2.8.1
'@aws-crypto/sha1-browser@5.2.0':
dependencies:
'@aws-crypto/supports-web-crypto': 5.2.0
'@aws-crypto/util': 5.2.0
- '@aws-sdk/types': 3.821.0
+ '@aws-sdk/types': 3.862.0
'@aws-sdk/util-locate-window': 3.208.0
'@smithy/util-utf8': 2.0.0
tslib: 2.8.1
@@ -11614,7 +11465,7 @@ snapshots:
'@aws-crypto/sha256-js': 5.2.0
'@aws-crypto/supports-web-crypto': 5.2.0
'@aws-crypto/util': 5.2.0
- '@aws-sdk/types': 3.821.0
+ '@aws-sdk/types': 3.862.0
'@aws-sdk/util-locate-window': 3.208.0
'@smithy/util-utf8': 2.0.0
tslib: 2.8.1
@@ -11622,7 +11473,7 @@ snapshots:
'@aws-crypto/sha256-js@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
- '@aws-sdk/types': 3.821.0
+ '@aws-sdk/types': 3.862.0
tslib: 2.8.1
'@aws-crypto/supports-web-crypto@5.2.0':
@@ -11631,442 +11482,485 @@ snapshots:
'@aws-crypto/util@5.2.0':
dependencies:
- '@aws-sdk/types': 3.821.0
+ '@aws-sdk/types': 3.862.0
'@smithy/util-utf8': 2.0.0
tslib: 2.8.1
- '@aws-sdk/client-s3@3.826.0':
+ '@aws-sdk/client-s3@3.873.0':
dependencies:
'@aws-crypto/sha1-browser': 5.2.0
'@aws-crypto/sha256-browser': 5.2.0
'@aws-crypto/sha256-js': 5.2.0
- '@aws-sdk/core': 3.826.0
- '@aws-sdk/credential-provider-node': 3.826.0
- '@aws-sdk/middleware-bucket-endpoint': 3.821.0
- '@aws-sdk/middleware-expect-continue': 3.821.0
- '@aws-sdk/middleware-flexible-checksums': 3.826.0
- '@aws-sdk/middleware-host-header': 3.821.0
- '@aws-sdk/middleware-location-constraint': 3.821.0
- '@aws-sdk/middleware-logger': 3.821.0
- '@aws-sdk/middleware-recursion-detection': 3.821.0
- '@aws-sdk/middleware-sdk-s3': 3.826.0
- '@aws-sdk/middleware-ssec': 3.821.0
- '@aws-sdk/middleware-user-agent': 3.826.0
- '@aws-sdk/region-config-resolver': 3.821.0
- '@aws-sdk/signature-v4-multi-region': 3.826.0
- '@aws-sdk/types': 3.821.0
- '@aws-sdk/util-endpoints': 3.821.0
- '@aws-sdk/util-user-agent-browser': 3.821.0
- '@aws-sdk/util-user-agent-node': 3.826.0
- '@aws-sdk/xml-builder': 3.821.0
- '@smithy/config-resolver': 4.1.4
- '@smithy/core': 3.5.3
- '@smithy/eventstream-serde-browser': 4.0.4
- '@smithy/eventstream-serde-config-resolver': 4.1.2
- '@smithy/eventstream-serde-node': 4.0.4
- '@smithy/fetch-http-handler': 5.0.4
- '@smithy/hash-blob-browser': 4.0.4
- '@smithy/hash-node': 4.0.4
- '@smithy/hash-stream-node': 4.0.4
- '@smithy/invalid-dependency': 4.0.4
- '@smithy/md5-js': 4.0.4
- '@smithy/middleware-content-length': 4.0.4
- '@smithy/middleware-endpoint': 4.1.11
- '@smithy/middleware-retry': 4.1.12
- '@smithy/middleware-serde': 4.0.8
- '@smithy/middleware-stack': 4.0.4
- '@smithy/node-config-provider': 4.1.3
- '@smithy/node-http-handler': 4.0.6
- '@smithy/protocol-http': 5.1.2
- '@smithy/smithy-client': 4.4.3
- '@smithy/types': 4.3.1
- '@smithy/url-parser': 4.0.4
+ '@aws-sdk/core': 3.873.0
+ '@aws-sdk/credential-provider-node': 3.873.0
+ '@aws-sdk/middleware-bucket-endpoint': 3.873.0
+ '@aws-sdk/middleware-expect-continue': 3.873.0
+ '@aws-sdk/middleware-flexible-checksums': 3.873.0
+ '@aws-sdk/middleware-host-header': 3.873.0
+ '@aws-sdk/middleware-location-constraint': 3.873.0
+ '@aws-sdk/middleware-logger': 3.873.0
+ '@aws-sdk/middleware-recursion-detection': 3.873.0
+ '@aws-sdk/middleware-sdk-s3': 3.873.0
+ '@aws-sdk/middleware-ssec': 3.873.0
+ '@aws-sdk/middleware-user-agent': 3.873.0
+ '@aws-sdk/region-config-resolver': 3.873.0
+ '@aws-sdk/signature-v4-multi-region': 3.873.0
+ '@aws-sdk/types': 3.862.0
+ '@aws-sdk/util-endpoints': 3.873.0
+ '@aws-sdk/util-user-agent-browser': 3.873.0
+ '@aws-sdk/util-user-agent-node': 3.873.0
+ '@aws-sdk/xml-builder': 3.873.0
+ '@smithy/config-resolver': 4.1.5
+ '@smithy/core': 3.8.0
+ '@smithy/eventstream-serde-browser': 4.0.5
+ '@smithy/eventstream-serde-config-resolver': 4.1.3
+ '@smithy/eventstream-serde-node': 4.0.5
+ '@smithy/fetch-http-handler': 5.1.1
+ '@smithy/hash-blob-browser': 4.0.5
+ '@smithy/hash-node': 4.0.5
+ '@smithy/hash-stream-node': 4.0.5
+ '@smithy/invalid-dependency': 4.0.5
+ '@smithy/md5-js': 4.0.5
+ '@smithy/middleware-content-length': 4.0.5
+ '@smithy/middleware-endpoint': 4.1.18
+ '@smithy/middleware-retry': 4.1.19
+ '@smithy/middleware-serde': 4.0.9
+ '@smithy/middleware-stack': 4.0.5
+ '@smithy/node-config-provider': 4.1.4
+ '@smithy/node-http-handler': 4.1.1
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/smithy-client': 4.4.10
+ '@smithy/types': 4.3.2
+ '@smithy/url-parser': 4.0.5
+ '@smithy/util-base64': 4.0.0
+ '@smithy/util-body-length-browser': 4.0.0
+ '@smithy/util-body-length-node': 4.0.0
+ '@smithy/util-defaults-mode-browser': 4.0.26
+ '@smithy/util-defaults-mode-node': 4.0.26
+ '@smithy/util-endpoints': 3.0.7
+ '@smithy/util-middleware': 4.0.5
+ '@smithy/util-retry': 4.0.7
+ '@smithy/util-stream': 4.2.4
+ '@smithy/util-utf8': 4.0.0
+ '@smithy/util-waiter': 4.0.7
+ '@types/uuid': 9.0.8
+ tslib: 2.8.1
+ uuid: 9.0.1
+ transitivePeerDependencies:
+ - aws-crt
+
+ '@aws-sdk/client-ses@3.873.0':
+ dependencies:
+ '@aws-crypto/sha256-browser': 5.2.0
+ '@aws-crypto/sha256-js': 5.2.0
+ '@aws-sdk/core': 3.873.0
+ '@aws-sdk/credential-provider-node': 3.873.0
+ '@aws-sdk/middleware-host-header': 3.873.0
+ '@aws-sdk/middleware-logger': 3.873.0
+ '@aws-sdk/middleware-recursion-detection': 3.873.0
+ '@aws-sdk/middleware-user-agent': 3.873.0
+ '@aws-sdk/region-config-resolver': 3.873.0
+ '@aws-sdk/types': 3.862.0
+ '@aws-sdk/util-endpoints': 3.873.0
+ '@aws-sdk/util-user-agent-browser': 3.873.0
+ '@aws-sdk/util-user-agent-node': 3.873.0
+ '@smithy/config-resolver': 4.1.5
+ '@smithy/core': 3.8.0
+ '@smithy/fetch-http-handler': 5.1.1
+ '@smithy/hash-node': 4.0.5
+ '@smithy/invalid-dependency': 4.0.5
+ '@smithy/middleware-content-length': 4.0.5
+ '@smithy/middleware-endpoint': 4.1.18
+ '@smithy/middleware-retry': 4.1.19
+ '@smithy/middleware-serde': 4.0.9
+ '@smithy/middleware-stack': 4.0.5
+ '@smithy/node-config-provider': 4.1.4
+ '@smithy/node-http-handler': 4.1.1
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/smithy-client': 4.4.10
+ '@smithy/types': 4.3.2
+ '@smithy/url-parser': 4.0.5
'@smithy/util-base64': 4.0.0
'@smithy/util-body-length-browser': 4.0.0
'@smithy/util-body-length-node': 4.0.0
- '@smithy/util-defaults-mode-browser': 4.0.19
- '@smithy/util-defaults-mode-node': 4.0.19
- '@smithy/util-endpoints': 3.0.6
- '@smithy/util-middleware': 4.0.4
- '@smithy/util-retry': 4.0.5
- '@smithy/util-stream': 4.2.2
+ '@smithy/util-defaults-mode-browser': 4.0.26
+ '@smithy/util-defaults-mode-node': 4.0.26
+ '@smithy/util-endpoints': 3.0.7
+ '@smithy/util-middleware': 4.0.5
+ '@smithy/util-retry': 4.0.7
'@smithy/util-utf8': 4.0.0
- '@smithy/util-waiter': 4.0.5
+ '@smithy/util-waiter': 4.0.7
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/client-sso@3.826.0':
+ '@aws-sdk/client-sso@3.873.0':
dependencies:
'@aws-crypto/sha256-browser': 5.2.0
'@aws-crypto/sha256-js': 5.2.0
- '@aws-sdk/core': 3.826.0
- '@aws-sdk/middleware-host-header': 3.821.0
- '@aws-sdk/middleware-logger': 3.821.0
- '@aws-sdk/middleware-recursion-detection': 3.821.0
- '@aws-sdk/middleware-user-agent': 3.826.0
- '@aws-sdk/region-config-resolver': 3.821.0
- '@aws-sdk/types': 3.821.0
- '@aws-sdk/util-endpoints': 3.821.0
- '@aws-sdk/util-user-agent-browser': 3.821.0
- '@aws-sdk/util-user-agent-node': 3.826.0
- '@smithy/config-resolver': 4.1.4
- '@smithy/core': 3.5.3
- '@smithy/fetch-http-handler': 5.0.4
- '@smithy/hash-node': 4.0.4
- '@smithy/invalid-dependency': 4.0.4
- '@smithy/middleware-content-length': 4.0.4
- '@smithy/middleware-endpoint': 4.1.11
- '@smithy/middleware-retry': 4.1.12
- '@smithy/middleware-serde': 4.0.8
- '@smithy/middleware-stack': 4.0.4
- '@smithy/node-config-provider': 4.1.3
- '@smithy/node-http-handler': 4.0.6
- '@smithy/protocol-http': 5.1.2
- '@smithy/smithy-client': 4.4.3
- '@smithy/types': 4.3.1
- '@smithy/url-parser': 4.0.4
+ '@aws-sdk/core': 3.873.0
+ '@aws-sdk/middleware-host-header': 3.873.0
+ '@aws-sdk/middleware-logger': 3.873.0
+ '@aws-sdk/middleware-recursion-detection': 3.873.0
+ '@aws-sdk/middleware-user-agent': 3.873.0
+ '@aws-sdk/region-config-resolver': 3.873.0
+ '@aws-sdk/types': 3.862.0
+ '@aws-sdk/util-endpoints': 3.873.0
+ '@aws-sdk/util-user-agent-browser': 3.873.0
+ '@aws-sdk/util-user-agent-node': 3.873.0
+ '@smithy/config-resolver': 4.1.5
+ '@smithy/core': 3.8.0
+ '@smithy/fetch-http-handler': 5.1.1
+ '@smithy/hash-node': 4.0.5
+ '@smithy/invalid-dependency': 4.0.5
+ '@smithy/middleware-content-length': 4.0.5
+ '@smithy/middleware-endpoint': 4.1.18
+ '@smithy/middleware-retry': 4.1.19
+ '@smithy/middleware-serde': 4.0.9
+ '@smithy/middleware-stack': 4.0.5
+ '@smithy/node-config-provider': 4.1.4
+ '@smithy/node-http-handler': 4.1.1
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/smithy-client': 4.4.10
+ '@smithy/types': 4.3.2
+ '@smithy/url-parser': 4.0.5
'@smithy/util-base64': 4.0.0
'@smithy/util-body-length-browser': 4.0.0
'@smithy/util-body-length-node': 4.0.0
- '@smithy/util-defaults-mode-browser': 4.0.19
- '@smithy/util-defaults-mode-node': 4.0.19
- '@smithy/util-endpoints': 3.0.6
- '@smithy/util-middleware': 4.0.4
- '@smithy/util-retry': 4.0.5
+ '@smithy/util-defaults-mode-browser': 4.0.26
+ '@smithy/util-defaults-mode-node': 4.0.26
+ '@smithy/util-endpoints': 3.0.7
+ '@smithy/util-middleware': 4.0.5
+ '@smithy/util-retry': 4.0.7
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/core@3.826.0':
+ '@aws-sdk/core@3.873.0':
dependencies:
- '@aws-sdk/types': 3.821.0
- '@aws-sdk/xml-builder': 3.821.0
- '@smithy/core': 3.5.3
- '@smithy/node-config-provider': 4.1.3
- '@smithy/property-provider': 4.0.4
- '@smithy/protocol-http': 5.1.2
- '@smithy/signature-v4': 5.1.2
- '@smithy/smithy-client': 4.4.3
- '@smithy/types': 4.3.1
+ '@aws-sdk/types': 3.862.0
+ '@aws-sdk/xml-builder': 3.873.0
+ '@smithy/core': 3.8.0
+ '@smithy/node-config-provider': 4.1.4
+ '@smithy/property-provider': 4.0.5
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/signature-v4': 5.1.3
+ '@smithy/smithy-client': 4.4.10
+ '@smithy/types': 4.3.2
'@smithy/util-base64': 4.0.0
'@smithy/util-body-length-browser': 4.0.0
- '@smithy/util-middleware': 4.0.4
+ '@smithy/util-middleware': 4.0.5
'@smithy/util-utf8': 4.0.0
- fast-xml-parser: 4.4.1
+ fast-xml-parser: 5.2.5
tslib: 2.8.1
- '@aws-sdk/credential-provider-env@3.826.0':
+ '@aws-sdk/credential-provider-env@3.873.0':
dependencies:
- '@aws-sdk/core': 3.826.0
- '@aws-sdk/types': 3.821.0
- '@smithy/property-provider': 4.0.4
- '@smithy/types': 4.3.1
+ '@aws-sdk/core': 3.873.0
+ '@aws-sdk/types': 3.862.0
+ '@smithy/property-provider': 4.0.5
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@aws-sdk/credential-provider-http@3.826.0':
+ '@aws-sdk/credential-provider-http@3.873.0':
dependencies:
- '@aws-sdk/core': 3.826.0
- '@aws-sdk/types': 3.821.0
- '@smithy/fetch-http-handler': 5.0.4
- '@smithy/node-http-handler': 4.0.6
- '@smithy/property-provider': 4.0.4
- '@smithy/protocol-http': 5.1.2
- '@smithy/smithy-client': 4.4.3
- '@smithy/types': 4.3.1
- '@smithy/util-stream': 4.2.2
+ '@aws-sdk/core': 3.873.0
+ '@aws-sdk/types': 3.862.0
+ '@smithy/fetch-http-handler': 5.1.1
+ '@smithy/node-http-handler': 4.1.1
+ '@smithy/property-provider': 4.0.5
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/smithy-client': 4.4.10
+ '@smithy/types': 4.3.2
+ '@smithy/util-stream': 4.2.4
tslib: 2.8.1
- '@aws-sdk/credential-provider-ini@3.826.0':
+ '@aws-sdk/credential-provider-ini@3.873.0':
dependencies:
- '@aws-sdk/core': 3.826.0
- '@aws-sdk/credential-provider-env': 3.826.0
- '@aws-sdk/credential-provider-http': 3.826.0
- '@aws-sdk/credential-provider-process': 3.826.0
- '@aws-sdk/credential-provider-sso': 3.826.0
- '@aws-sdk/credential-provider-web-identity': 3.826.0
- '@aws-sdk/nested-clients': 3.826.0
- '@aws-sdk/types': 3.821.0
- '@smithy/credential-provider-imds': 4.0.6
- '@smithy/property-provider': 4.0.4
- '@smithy/shared-ini-file-loader': 4.0.4
- '@smithy/types': 4.3.1
+ '@aws-sdk/core': 3.873.0
+ '@aws-sdk/credential-provider-env': 3.873.0
+ '@aws-sdk/credential-provider-http': 3.873.0
+ '@aws-sdk/credential-provider-process': 3.873.0
+ '@aws-sdk/credential-provider-sso': 3.873.0
+ '@aws-sdk/credential-provider-web-identity': 3.873.0
+ '@aws-sdk/nested-clients': 3.873.0
+ '@aws-sdk/types': 3.862.0
+ '@smithy/credential-provider-imds': 4.0.7
+ '@smithy/property-provider': 4.0.5
+ '@smithy/shared-ini-file-loader': 4.0.5
+ '@smithy/types': 4.3.2
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/credential-provider-node@3.826.0':
+ '@aws-sdk/credential-provider-node@3.873.0':
dependencies:
- '@aws-sdk/credential-provider-env': 3.826.0
- '@aws-sdk/credential-provider-http': 3.826.0
- '@aws-sdk/credential-provider-ini': 3.826.0
- '@aws-sdk/credential-provider-process': 3.826.0
- '@aws-sdk/credential-provider-sso': 3.826.0
- '@aws-sdk/credential-provider-web-identity': 3.826.0
- '@aws-sdk/types': 3.821.0
- '@smithy/credential-provider-imds': 4.0.6
- '@smithy/property-provider': 4.0.4
- '@smithy/shared-ini-file-loader': 4.0.4
- '@smithy/types': 4.3.1
+ '@aws-sdk/credential-provider-env': 3.873.0
+ '@aws-sdk/credential-provider-http': 3.873.0
+ '@aws-sdk/credential-provider-ini': 3.873.0
+ '@aws-sdk/credential-provider-process': 3.873.0
+ '@aws-sdk/credential-provider-sso': 3.873.0
+ '@aws-sdk/credential-provider-web-identity': 3.873.0
+ '@aws-sdk/types': 3.862.0
+ '@smithy/credential-provider-imds': 4.0.7
+ '@smithy/property-provider': 4.0.5
+ '@smithy/shared-ini-file-loader': 4.0.5
+ '@smithy/types': 4.3.2
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/credential-provider-process@3.826.0':
+ '@aws-sdk/credential-provider-process@3.873.0':
dependencies:
- '@aws-sdk/core': 3.826.0
- '@aws-sdk/types': 3.821.0
- '@smithy/property-provider': 4.0.4
- '@smithy/shared-ini-file-loader': 4.0.4
- '@smithy/types': 4.3.1
+ '@aws-sdk/core': 3.873.0
+ '@aws-sdk/types': 3.862.0
+ '@smithy/property-provider': 4.0.5
+ '@smithy/shared-ini-file-loader': 4.0.5
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@aws-sdk/credential-provider-sso@3.826.0':
+ '@aws-sdk/credential-provider-sso@3.873.0':
dependencies:
- '@aws-sdk/client-sso': 3.826.0
- '@aws-sdk/core': 3.826.0
- '@aws-sdk/token-providers': 3.826.0
- '@aws-sdk/types': 3.821.0
- '@smithy/property-provider': 4.0.4
- '@smithy/shared-ini-file-loader': 4.0.4
- '@smithy/types': 4.3.1
+ '@aws-sdk/client-sso': 3.873.0
+ '@aws-sdk/core': 3.873.0
+ '@aws-sdk/token-providers': 3.873.0
+ '@aws-sdk/types': 3.862.0
+ '@smithy/property-provider': 4.0.5
+ '@smithy/shared-ini-file-loader': 4.0.5
+ '@smithy/types': 4.3.2
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/credential-provider-web-identity@3.826.0':
+ '@aws-sdk/credential-provider-web-identity@3.873.0':
dependencies:
- '@aws-sdk/core': 3.826.0
- '@aws-sdk/nested-clients': 3.826.0
- '@aws-sdk/types': 3.821.0
- '@smithy/property-provider': 4.0.4
- '@smithy/types': 4.3.1
+ '@aws-sdk/core': 3.873.0
+ '@aws-sdk/nested-clients': 3.873.0
+ '@aws-sdk/types': 3.862.0
+ '@smithy/property-provider': 4.0.5
+ '@smithy/types': 4.3.2
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/lib-storage@3.826.0(@aws-sdk/client-s3@3.826.0)':
+ '@aws-sdk/lib-storage@3.873.0(@aws-sdk/client-s3@3.873.0)':
dependencies:
- '@aws-sdk/client-s3': 3.826.0
- '@smithy/abort-controller': 4.0.4
- '@smithy/middleware-endpoint': 4.1.11
- '@smithy/smithy-client': 4.4.3
+ '@aws-sdk/client-s3': 3.873.0
+ '@smithy/abort-controller': 4.0.5
+ '@smithy/middleware-endpoint': 4.1.18
+ '@smithy/smithy-client': 4.4.10
buffer: 5.6.0
events: 3.3.0
stream-browserify: 3.0.0
tslib: 2.8.1
- '@aws-sdk/middleware-bucket-endpoint@3.821.0':
+ '@aws-sdk/middleware-bucket-endpoint@3.873.0':
dependencies:
- '@aws-sdk/types': 3.821.0
- '@aws-sdk/util-arn-parser': 3.804.0
- '@smithy/node-config-provider': 4.1.3
- '@smithy/protocol-http': 5.1.2
- '@smithy/types': 4.3.1
+ '@aws-sdk/types': 3.862.0
+ '@aws-sdk/util-arn-parser': 3.873.0
+ '@smithy/node-config-provider': 4.1.4
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/types': 4.3.2
'@smithy/util-config-provider': 4.0.0
tslib: 2.8.1
- '@aws-sdk/middleware-expect-continue@3.821.0':
+ '@aws-sdk/middleware-expect-continue@3.873.0':
dependencies:
- '@aws-sdk/types': 3.821.0
- '@smithy/protocol-http': 5.1.2
- '@smithy/types': 4.3.1
+ '@aws-sdk/types': 3.862.0
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@aws-sdk/middleware-flexible-checksums@3.826.0':
+ '@aws-sdk/middleware-flexible-checksums@3.873.0':
dependencies:
'@aws-crypto/crc32': 5.2.0
'@aws-crypto/crc32c': 5.2.0
'@aws-crypto/util': 5.2.0
- '@aws-sdk/core': 3.826.0
- '@aws-sdk/types': 3.821.0
+ '@aws-sdk/core': 3.873.0
+ '@aws-sdk/types': 3.862.0
'@smithy/is-array-buffer': 4.0.0
- '@smithy/node-config-provider': 4.1.3
- '@smithy/protocol-http': 5.1.2
- '@smithy/types': 4.3.1
- '@smithy/util-middleware': 4.0.4
- '@smithy/util-stream': 4.2.2
+ '@smithy/node-config-provider': 4.1.4
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/types': 4.3.2
+ '@smithy/util-middleware': 4.0.5
+ '@smithy/util-stream': 4.2.4
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
- '@aws-sdk/middleware-host-header@3.821.0':
+ '@aws-sdk/middleware-host-header@3.873.0':
dependencies:
- '@aws-sdk/types': 3.821.0
- '@smithy/protocol-http': 5.1.2
- '@smithy/types': 4.3.1
+ '@aws-sdk/types': 3.862.0
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@aws-sdk/middleware-location-constraint@3.821.0':
+ '@aws-sdk/middleware-location-constraint@3.873.0':
dependencies:
- '@aws-sdk/types': 3.821.0
- '@smithy/types': 4.3.1
+ '@aws-sdk/types': 3.862.0
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@aws-sdk/middleware-logger@3.821.0':
+ '@aws-sdk/middleware-logger@3.873.0':
dependencies:
- '@aws-sdk/types': 3.821.0
- '@smithy/types': 4.3.1
+ '@aws-sdk/types': 3.862.0
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@aws-sdk/middleware-recursion-detection@3.821.0':
+ '@aws-sdk/middleware-recursion-detection@3.873.0':
dependencies:
- '@aws-sdk/types': 3.821.0
- '@smithy/protocol-http': 5.1.2
- '@smithy/types': 4.3.1
+ '@aws-sdk/types': 3.862.0
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@aws-sdk/middleware-sdk-s3@3.826.0':
+ '@aws-sdk/middleware-sdk-s3@3.873.0':
dependencies:
- '@aws-sdk/core': 3.826.0
- '@aws-sdk/types': 3.821.0
- '@aws-sdk/util-arn-parser': 3.804.0
- '@smithy/core': 3.5.3
- '@smithy/node-config-provider': 4.1.3
- '@smithy/protocol-http': 5.1.2
- '@smithy/signature-v4': 5.1.2
- '@smithy/smithy-client': 4.4.3
- '@smithy/types': 4.3.1
+ '@aws-sdk/core': 3.873.0
+ '@aws-sdk/types': 3.862.0
+ '@aws-sdk/util-arn-parser': 3.873.0
+ '@smithy/core': 3.8.0
+ '@smithy/node-config-provider': 4.1.4
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/signature-v4': 5.1.3
+ '@smithy/smithy-client': 4.4.10
+ '@smithy/types': 4.3.2
'@smithy/util-config-provider': 4.0.0
- '@smithy/util-middleware': 4.0.4
- '@smithy/util-stream': 4.2.2
+ '@smithy/util-middleware': 4.0.5
+ '@smithy/util-stream': 4.2.4
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
- '@aws-sdk/middleware-ssec@3.821.0':
+ '@aws-sdk/middleware-ssec@3.873.0':
dependencies:
- '@aws-sdk/types': 3.821.0
- '@smithy/types': 4.3.1
+ '@aws-sdk/types': 3.862.0
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@aws-sdk/middleware-user-agent@3.826.0':
+ '@aws-sdk/middleware-user-agent@3.873.0':
dependencies:
- '@aws-sdk/core': 3.826.0
- '@aws-sdk/types': 3.821.0
- '@aws-sdk/util-endpoints': 3.821.0
- '@smithy/core': 3.5.3
- '@smithy/protocol-http': 5.1.2
- '@smithy/types': 4.3.1
+ '@aws-sdk/core': 3.873.0
+ '@aws-sdk/types': 3.862.0
+ '@aws-sdk/util-endpoints': 3.873.0
+ '@smithy/core': 3.8.0
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@aws-sdk/nested-clients@3.826.0':
+ '@aws-sdk/nested-clients@3.873.0':
dependencies:
'@aws-crypto/sha256-browser': 5.2.0
'@aws-crypto/sha256-js': 5.2.0
- '@aws-sdk/core': 3.826.0
- '@aws-sdk/middleware-host-header': 3.821.0
- '@aws-sdk/middleware-logger': 3.821.0
- '@aws-sdk/middleware-recursion-detection': 3.821.0
- '@aws-sdk/middleware-user-agent': 3.826.0
- '@aws-sdk/region-config-resolver': 3.821.0
- '@aws-sdk/types': 3.821.0
- '@aws-sdk/util-endpoints': 3.821.0
- '@aws-sdk/util-user-agent-browser': 3.821.0
- '@aws-sdk/util-user-agent-node': 3.826.0
- '@smithy/config-resolver': 4.1.4
- '@smithy/core': 3.5.3
- '@smithy/fetch-http-handler': 5.0.4
- '@smithy/hash-node': 4.0.4
- '@smithy/invalid-dependency': 4.0.4
- '@smithy/middleware-content-length': 4.0.4
- '@smithy/middleware-endpoint': 4.1.11
- '@smithy/middleware-retry': 4.1.12
- '@smithy/middleware-serde': 4.0.8
- '@smithy/middleware-stack': 4.0.4
- '@smithy/node-config-provider': 4.1.3
- '@smithy/node-http-handler': 4.0.6
- '@smithy/protocol-http': 5.1.2
- '@smithy/smithy-client': 4.4.3
- '@smithy/types': 4.3.1
- '@smithy/url-parser': 4.0.4
+ '@aws-sdk/core': 3.873.0
+ '@aws-sdk/middleware-host-header': 3.873.0
+ '@aws-sdk/middleware-logger': 3.873.0
+ '@aws-sdk/middleware-recursion-detection': 3.873.0
+ '@aws-sdk/middleware-user-agent': 3.873.0
+ '@aws-sdk/region-config-resolver': 3.873.0
+ '@aws-sdk/types': 3.862.0
+ '@aws-sdk/util-endpoints': 3.873.0
+ '@aws-sdk/util-user-agent-browser': 3.873.0
+ '@aws-sdk/util-user-agent-node': 3.873.0
+ '@smithy/config-resolver': 4.1.5
+ '@smithy/core': 3.8.0
+ '@smithy/fetch-http-handler': 5.1.1
+ '@smithy/hash-node': 4.0.5
+ '@smithy/invalid-dependency': 4.0.5
+ '@smithy/middleware-content-length': 4.0.5
+ '@smithy/middleware-endpoint': 4.1.18
+ '@smithy/middleware-retry': 4.1.19
+ '@smithy/middleware-serde': 4.0.9
+ '@smithy/middleware-stack': 4.0.5
+ '@smithy/node-config-provider': 4.1.4
+ '@smithy/node-http-handler': 4.1.1
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/smithy-client': 4.4.10
+ '@smithy/types': 4.3.2
+ '@smithy/url-parser': 4.0.5
'@smithy/util-base64': 4.0.0
'@smithy/util-body-length-browser': 4.0.0
'@smithy/util-body-length-node': 4.0.0
- '@smithy/util-defaults-mode-browser': 4.0.19
- '@smithy/util-defaults-mode-node': 4.0.19
- '@smithy/util-endpoints': 3.0.6
- '@smithy/util-middleware': 4.0.4
- '@smithy/util-retry': 4.0.5
+ '@smithy/util-defaults-mode-browser': 4.0.26
+ '@smithy/util-defaults-mode-node': 4.0.26
+ '@smithy/util-endpoints': 3.0.7
+ '@smithy/util-middleware': 4.0.5
+ '@smithy/util-retry': 4.0.7
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/region-config-resolver@3.821.0':
+ '@aws-sdk/region-config-resolver@3.873.0':
dependencies:
- '@aws-sdk/types': 3.821.0
- '@smithy/node-config-provider': 4.1.3
- '@smithy/types': 4.3.1
+ '@aws-sdk/types': 3.862.0
+ '@smithy/node-config-provider': 4.1.4
+ '@smithy/types': 4.3.2
'@smithy/util-config-provider': 4.0.0
- '@smithy/util-middleware': 4.0.4
+ '@smithy/util-middleware': 4.0.5
tslib: 2.8.1
- '@aws-sdk/signature-v4-multi-region@3.826.0':
+ '@aws-sdk/signature-v4-multi-region@3.873.0':
dependencies:
- '@aws-sdk/middleware-sdk-s3': 3.826.0
- '@aws-sdk/types': 3.821.0
- '@smithy/protocol-http': 5.1.2
- '@smithy/signature-v4': 5.1.2
- '@smithy/types': 4.3.1
+ '@aws-sdk/middleware-sdk-s3': 3.873.0
+ '@aws-sdk/types': 3.862.0
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/signature-v4': 5.1.3
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@aws-sdk/token-providers@3.826.0':
+ '@aws-sdk/token-providers@3.873.0':
dependencies:
- '@aws-sdk/core': 3.826.0
- '@aws-sdk/nested-clients': 3.826.0
- '@aws-sdk/types': 3.821.0
- '@smithy/property-provider': 4.0.4
- '@smithy/shared-ini-file-loader': 4.0.4
- '@smithy/types': 4.3.1
+ '@aws-sdk/core': 3.873.0
+ '@aws-sdk/nested-clients': 3.873.0
+ '@aws-sdk/types': 3.862.0
+ '@smithy/property-provider': 4.0.5
+ '@smithy/shared-ini-file-loader': 4.0.5
+ '@smithy/types': 4.3.2
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/types@3.821.0':
+ '@aws-sdk/types@3.862.0':
dependencies:
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@aws-sdk/util-arn-parser@3.804.0':
+ '@aws-sdk/util-arn-parser@3.873.0':
dependencies:
tslib: 2.8.1
- '@aws-sdk/util-endpoints@3.821.0':
+ '@aws-sdk/util-endpoints@3.873.0':
dependencies:
- '@aws-sdk/types': 3.821.0
- '@smithy/types': 4.3.1
- '@smithy/util-endpoints': 3.0.6
+ '@aws-sdk/types': 3.862.0
+ '@smithy/types': 4.3.2
+ '@smithy/url-parser': 4.0.5
+ '@smithy/util-endpoints': 3.0.7
tslib: 2.8.1
'@aws-sdk/util-locate-window@3.208.0':
dependencies:
tslib: 2.8.1
- '@aws-sdk/util-user-agent-browser@3.821.0':
+ '@aws-sdk/util-user-agent-browser@3.873.0':
dependencies:
- '@aws-sdk/types': 3.821.0
- '@smithy/types': 4.3.1
+ '@aws-sdk/types': 3.862.0
+ '@smithy/types': 4.3.2
bowser: 2.11.0
tslib: 2.8.1
- '@aws-sdk/util-user-agent-node@3.826.0':
+ '@aws-sdk/util-user-agent-node@3.873.0':
dependencies:
- '@aws-sdk/middleware-user-agent': 3.826.0
- '@aws-sdk/types': 3.821.0
- '@smithy/node-config-provider': 4.1.3
- '@smithy/types': 4.3.1
+ '@aws-sdk/middleware-user-agent': 3.873.0
+ '@aws-sdk/types': 3.862.0
+ '@smithy/node-config-provider': 4.1.4
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@aws-sdk/xml-builder@3.821.0':
+ '@aws-sdk/xml-builder@3.873.0':
dependencies:
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@babel/code-frame@7.24.7':
- dependencies:
- '@babel/highlight': 7.24.7
- picocolors: 1.1.1
-
'@babel/code-frame@7.27.1':
dependencies:
'@babel/helper-validator-identifier': 7.27.1
@@ -12078,17 +11972,17 @@ snapshots:
'@babel/core@7.24.7':
dependencies:
'@ampproject/remapping': 2.3.0
- '@babel/code-frame': 7.24.7
+ '@babel/code-frame': 7.27.1
'@babel/generator': 7.24.7
'@babel/helper-compilation-targets': 7.24.7
'@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
'@babel/helpers': 7.24.7
- '@babel/parser': 7.27.2
+ '@babel/parser': 7.28.0
'@babel/template': 7.24.7
'@babel/traverse': 7.24.7
- '@babel/types': 7.27.1
+ '@babel/types': 7.28.1
convert-source-map: 2.0.0
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@5.5.0)
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -12097,36 +11991,36 @@ snapshots:
'@babel/generator@7.24.7':
dependencies:
- '@babel/types': 7.27.1
- '@jridgewell/gen-mapping': 0.3.5
- '@jridgewell/trace-mapping': 0.3.25
+ '@babel/types': 7.28.1
+ '@jridgewell/gen-mapping': 0.3.12
+ '@jridgewell/trace-mapping': 0.3.29
jsesc: 2.5.2
'@babel/helper-compilation-targets@7.24.7':
dependencies:
'@babel/compat-data': 7.24.7
'@babel/helper-validator-option': 7.24.7
- browserslist: 4.24.5
+ browserslist: 4.25.1
lru-cache: 5.1.1
semver: 6.3.1
'@babel/helper-environment-visitor@7.24.7':
dependencies:
- '@babel/types': 7.27.1
+ '@babel/types': 7.28.1
'@babel/helper-function-name@7.24.7':
dependencies:
'@babel/template': 7.24.7
- '@babel/types': 7.27.1
+ '@babel/types': 7.28.1
'@babel/helper-hoist-variables@7.24.7':
dependencies:
- '@babel/types': 7.27.1
+ '@babel/types': 7.28.1
'@babel/helper-module-imports@7.24.7':
dependencies:
'@babel/traverse': 7.24.7
- '@babel/types': 7.27.1
+ '@babel/types': 7.28.1
transitivePeerDependencies:
- supports-color
@@ -12146,20 +12040,16 @@ snapshots:
'@babel/helper-simple-access@7.24.7':
dependencies:
'@babel/traverse': 7.24.7
- '@babel/types': 7.27.1
+ '@babel/types': 7.28.1
transitivePeerDependencies:
- supports-color
'@babel/helper-split-export-declaration@7.24.7':
dependencies:
- '@babel/types': 7.27.1
-
- '@babel/helper-string-parser@7.24.8': {}
+ '@babel/types': 7.28.1
'@babel/helper-string-parser@7.27.1': {}
- '@babel/helper-validator-identifier@7.24.7': {}
-
'@babel/helper-validator-identifier@7.27.1': {}
'@babel/helper-validator-option@7.24.7': {}
@@ -12167,23 +12057,16 @@ snapshots:
'@babel/helpers@7.24.7':
dependencies:
'@babel/template': 7.24.7
- '@babel/types': 7.27.1
-
- '@babel/highlight@7.24.7':
- dependencies:
- '@babel/helper-validator-identifier': 7.24.7
- chalk: 2.4.2
- js-tokens: 4.0.0
- picocolors: 1.1.1
-
- '@babel/parser@7.27.2':
- dependencies:
- '@babel/types': 7.27.1
+ '@babel/types': 7.28.1
'@babel/parser@7.28.0':
dependencies:
'@babel/types': 7.28.1
+ '@babel/parser@7.28.3':
+ dependencies:
+ '@babel/types': 7.28.2
+
'@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)':
dependencies:
'@babel/core': 7.24.7
@@ -12260,37 +12143,31 @@ snapshots:
'@babel/template@7.24.7':
dependencies:
- '@babel/code-frame': 7.24.7
- '@babel/parser': 7.27.2
- '@babel/types': 7.27.1
+ '@babel/code-frame': 7.27.1
+ '@babel/parser': 7.28.0
+ '@babel/types': 7.28.1
'@babel/traverse@7.24.7':
dependencies:
- '@babel/code-frame': 7.24.7
+ '@babel/code-frame': 7.27.1
'@babel/generator': 7.24.7
'@babel/helper-environment-visitor': 7.24.7
'@babel/helper-function-name': 7.24.7
'@babel/helper-hoist-variables': 7.24.7
'@babel/helper-split-export-declaration': 7.24.7
- '@babel/parser': 7.27.2
- '@babel/types': 7.27.1
- debug: 4.4.1(supports-color@10.0.0)
+ '@babel/parser': 7.28.0
+ '@babel/types': 7.28.1
+ debug: 4.4.1(supports-color@5.5.0)
globals: 11.12.0
transitivePeerDependencies:
- supports-color
- '@babel/types@7.25.6':
- dependencies:
- '@babel/helper-string-parser': 7.24.8
- '@babel/helper-validator-identifier': 7.24.7
- to-fast-properties: 2.0.0
-
- '@babel/types@7.27.1':
+ '@babel/types@7.28.1':
dependencies:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.27.1
- '@babel/types@7.28.1':
+ '@babel/types@7.28.2':
dependencies:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.27.1
@@ -12316,71 +12193,71 @@ snapshots:
'@chainsafe/is-ip@2.1.0': {}
- '@cropper/element-canvas@2.0.0':
+ '@cropper/element-canvas@2.0.1':
dependencies:
- '@cropper/element': 2.0.0
- '@cropper/utils': 2.0.0
+ '@cropper/element': 2.0.1
+ '@cropper/utils': 2.0.1
- '@cropper/element-crosshair@2.0.0':
+ '@cropper/element-crosshair@2.0.1':
dependencies:
- '@cropper/element': 2.0.0
- '@cropper/utils': 2.0.0
+ '@cropper/element': 2.0.1
+ '@cropper/utils': 2.0.1
- '@cropper/element-grid@2.0.0':
+ '@cropper/element-grid@2.0.1':
dependencies:
- '@cropper/element': 2.0.0
- '@cropper/utils': 2.0.0
+ '@cropper/element': 2.0.1
+ '@cropper/utils': 2.0.1
- '@cropper/element-handle@2.0.0':
+ '@cropper/element-handle@2.0.1':
dependencies:
- '@cropper/element': 2.0.0
- '@cropper/utils': 2.0.0
+ '@cropper/element': 2.0.1
+ '@cropper/utils': 2.0.1
- '@cropper/element-image@2.0.0':
+ '@cropper/element-image@2.0.1':
dependencies:
- '@cropper/element': 2.0.0
- '@cropper/element-canvas': 2.0.0
- '@cropper/utils': 2.0.0
+ '@cropper/element': 2.0.1
+ '@cropper/element-canvas': 2.0.1
+ '@cropper/utils': 2.0.1
- '@cropper/element-selection@2.0.0':
+ '@cropper/element-selection@2.0.1':
dependencies:
- '@cropper/element': 2.0.0
- '@cropper/element-canvas': 2.0.0
- '@cropper/element-image': 2.0.0
- '@cropper/utils': 2.0.0
+ '@cropper/element': 2.0.1
+ '@cropper/element-canvas': 2.0.1
+ '@cropper/element-image': 2.0.1
+ '@cropper/utils': 2.0.1
- '@cropper/element-shade@2.0.0':
+ '@cropper/element-shade@2.0.1':
dependencies:
- '@cropper/element': 2.0.0
- '@cropper/element-canvas': 2.0.0
- '@cropper/element-selection': 2.0.0
- '@cropper/utils': 2.0.0
+ '@cropper/element': 2.0.1
+ '@cropper/element-canvas': 2.0.1
+ '@cropper/element-selection': 2.0.1
+ '@cropper/utils': 2.0.1
- '@cropper/element-viewer@2.0.0':
+ '@cropper/element-viewer@2.0.1':
dependencies:
- '@cropper/element': 2.0.0
- '@cropper/element-canvas': 2.0.0
- '@cropper/element-image': 2.0.0
- '@cropper/element-selection': 2.0.0
- '@cropper/utils': 2.0.0
+ '@cropper/element': 2.0.1
+ '@cropper/element-canvas': 2.0.1
+ '@cropper/element-image': 2.0.1
+ '@cropper/element-selection': 2.0.1
+ '@cropper/utils': 2.0.1
- '@cropper/element@2.0.0':
+ '@cropper/element@2.0.1':
dependencies:
- '@cropper/utils': 2.0.0
+ '@cropper/utils': 2.0.1
- '@cropper/elements@2.0.0':
+ '@cropper/elements@2.0.1':
dependencies:
- '@cropper/element': 2.0.0
- '@cropper/element-canvas': 2.0.0
- '@cropper/element-crosshair': 2.0.0
- '@cropper/element-grid': 2.0.0
- '@cropper/element-handle': 2.0.0
- '@cropper/element-image': 2.0.0
- '@cropper/element-selection': 2.0.0
- '@cropper/element-shade': 2.0.0
- '@cropper/element-viewer': 2.0.0
+ '@cropper/element': 2.0.1
+ '@cropper/element-canvas': 2.0.1
+ '@cropper/element-crosshair': 2.0.1
+ '@cropper/element-grid': 2.0.1
+ '@cropper/element-handle': 2.0.1
+ '@cropper/element-image': 2.0.1
+ '@cropper/element-selection': 2.0.1
+ '@cropper/element-shade': 2.0.1
+ '@cropper/element-viewer': 2.0.1
- '@cropper/utils@2.0.0': {}
+ '@cropper/utils@2.0.1': {}
'@csstools/color-helpers@5.0.1': {}
@@ -12402,7 +12279,7 @@ snapshots:
'@csstools/css-tokenizer@3.0.3': {}
- '@cypress/request@3.0.8':
+ '@cypress/request@3.0.9':
dependencies:
aws-sign2: 0.7.0
aws4: 1.12.0
@@ -12410,7 +12287,7 @@ snapshots:
combined-stream: 1.0.8
extend: 3.0.2
forever-agent: 0.6.1
- form-data: 4.0.3
+ form-data: 4.0.4
http-signature: 1.4.0
is-typedarray: 1.0.0
isstream: 0.1.2
@@ -12438,179 +12315,179 @@ snapshots:
transitivePeerDependencies:
- web-streams-polyfill
- '@discordapp/twemoji@15.1.0':
+ '@discordapp/twemoji@16.0.1':
dependencies:
- '@twemoji/parser': 15.1.0
+ '@twemoji/parser': 16.0.0
fs-extra: 8.1.0
jsonfile: 5.0.0
universalify: 0.1.2
- '@emnapi/runtime@1.4.0':
+ '@emnapi/runtime@1.4.3':
dependencies:
tslib: 2.8.1
optional: true
- '@emnapi/runtime@1.4.3':
- dependencies:
- tslib: 2.8.1
+ '@epic-web/invariant@1.0.0': {}
+
+ '@esbuild/aix-ppc64@0.25.8':
optional: true
- '@esbuild/aix-ppc64@0.25.4':
+ '@esbuild/aix-ppc64@0.25.9':
optional: true
- '@esbuild/aix-ppc64@0.25.6':
+ '@esbuild/android-arm64@0.25.8':
optional: true
- '@esbuild/android-arm64@0.25.4':
+ '@esbuild/android-arm64@0.25.9':
optional: true
- '@esbuild/android-arm64@0.25.6':
+ '@esbuild/android-arm@0.25.8':
optional: true
- '@esbuild/android-arm@0.25.4':
+ '@esbuild/android-arm@0.25.9':
optional: true
- '@esbuild/android-arm@0.25.6':
+ '@esbuild/android-x64@0.25.8':
optional: true
- '@esbuild/android-x64@0.25.4':
+ '@esbuild/android-x64@0.25.9':
optional: true
- '@esbuild/android-x64@0.25.6':
+ '@esbuild/darwin-arm64@0.25.8':
optional: true
- '@esbuild/darwin-arm64@0.25.4':
+ '@esbuild/darwin-arm64@0.25.9':
optional: true
- '@esbuild/darwin-arm64@0.25.6':
+ '@esbuild/darwin-x64@0.25.8':
optional: true
- '@esbuild/darwin-x64@0.25.4':
+ '@esbuild/darwin-x64@0.25.9':
optional: true
- '@esbuild/darwin-x64@0.25.6':
+ '@esbuild/freebsd-arm64@0.25.8':
optional: true
- '@esbuild/freebsd-arm64@0.25.4':
+ '@esbuild/freebsd-arm64@0.25.9':
optional: true
- '@esbuild/freebsd-arm64@0.25.6':
+ '@esbuild/freebsd-x64@0.25.8':
optional: true
- '@esbuild/freebsd-x64@0.25.4':
+ '@esbuild/freebsd-x64@0.25.9':
optional: true
- '@esbuild/freebsd-x64@0.25.6':
+ '@esbuild/linux-arm64@0.25.8':
optional: true
- '@esbuild/linux-arm64@0.25.4':
+ '@esbuild/linux-arm64@0.25.9':
optional: true
- '@esbuild/linux-arm64@0.25.6':
+ '@esbuild/linux-arm@0.25.8':
optional: true
- '@esbuild/linux-arm@0.25.4':
+ '@esbuild/linux-arm@0.25.9':
optional: true
- '@esbuild/linux-arm@0.25.6':
+ '@esbuild/linux-ia32@0.25.8':
optional: true
- '@esbuild/linux-ia32@0.25.4':
+ '@esbuild/linux-ia32@0.25.9':
optional: true
- '@esbuild/linux-ia32@0.25.6':
+ '@esbuild/linux-loong64@0.25.8':
optional: true
- '@esbuild/linux-loong64@0.25.4':
+ '@esbuild/linux-loong64@0.25.9':
optional: true
- '@esbuild/linux-loong64@0.25.6':
+ '@esbuild/linux-mips64el@0.25.8':
optional: true
- '@esbuild/linux-mips64el@0.25.4':
+ '@esbuild/linux-mips64el@0.25.9':
optional: true
- '@esbuild/linux-mips64el@0.25.6':
+ '@esbuild/linux-ppc64@0.25.8':
optional: true
- '@esbuild/linux-ppc64@0.25.4':
+ '@esbuild/linux-ppc64@0.25.9':
optional: true
- '@esbuild/linux-ppc64@0.25.6':
+ '@esbuild/linux-riscv64@0.25.8':
optional: true
- '@esbuild/linux-riscv64@0.25.4':
+ '@esbuild/linux-riscv64@0.25.9':
optional: true
- '@esbuild/linux-riscv64@0.25.6':
+ '@esbuild/linux-s390x@0.25.8':
optional: true
- '@esbuild/linux-s390x@0.25.4':
+ '@esbuild/linux-s390x@0.25.9':
optional: true
- '@esbuild/linux-s390x@0.25.6':
+ '@esbuild/linux-x64@0.25.8':
optional: true
- '@esbuild/linux-x64@0.25.4':
+ '@esbuild/linux-x64@0.25.9':
optional: true
- '@esbuild/linux-x64@0.25.6':
+ '@esbuild/netbsd-arm64@0.25.8':
optional: true
- '@esbuild/netbsd-arm64@0.25.4':
+ '@esbuild/netbsd-arm64@0.25.9':
optional: true
- '@esbuild/netbsd-arm64@0.25.6':
+ '@esbuild/netbsd-x64@0.25.8':
optional: true
- '@esbuild/netbsd-x64@0.25.4':
+ '@esbuild/netbsd-x64@0.25.9':
optional: true
- '@esbuild/netbsd-x64@0.25.6':
+ '@esbuild/openbsd-arm64@0.25.8':
optional: true
- '@esbuild/openbsd-arm64@0.25.4':
+ '@esbuild/openbsd-arm64@0.25.9':
optional: true
- '@esbuild/openbsd-arm64@0.25.6':
+ '@esbuild/openbsd-x64@0.25.8':
optional: true
- '@esbuild/openbsd-x64@0.25.4':
+ '@esbuild/openbsd-x64@0.25.9':
optional: true
- '@esbuild/openbsd-x64@0.25.6':
+ '@esbuild/openharmony-arm64@0.25.8':
optional: true
- '@esbuild/openharmony-arm64@0.25.6':
+ '@esbuild/openharmony-arm64@0.25.9':
optional: true
- '@esbuild/sunos-x64@0.25.4':
+ '@esbuild/sunos-x64@0.25.8':
optional: true
- '@esbuild/sunos-x64@0.25.6':
+ '@esbuild/sunos-x64@0.25.9':
optional: true
- '@esbuild/win32-arm64@0.25.4':
+ '@esbuild/win32-arm64@0.25.8':
optional: true
- '@esbuild/win32-arm64@0.25.6':
+ '@esbuild/win32-arm64@0.25.9':
optional: true
- '@esbuild/win32-ia32@0.25.4':
+ '@esbuild/win32-ia32@0.25.8':
optional: true
- '@esbuild/win32-ia32@0.25.6':
+ '@esbuild/win32-ia32@0.25.9':
optional: true
- '@esbuild/win32-x64@0.25.4':
+ '@esbuild/win32-x64@0.25.8':
optional: true
- '@esbuild/win32-x64@0.25.6':
+ '@esbuild/win32-x64@0.25.9':
optional: true
- '@eslint-community/eslint-utils@4.7.0(eslint@9.31.0)':
+ '@eslint-community/eslint-utils@4.7.0(eslint@9.34.0)':
dependencies:
- eslint: 9.31.0
+ eslint: 9.34.0
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.1': {}
@@ -12620,25 +12497,21 @@ snapshots:
'@eslint/config-array@0.21.0':
dependencies:
'@eslint/object-schema': 2.1.6
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@5.5.0)
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
- '@eslint/config-helpers@0.3.0': {}
-
- '@eslint/core@0.14.0':
- dependencies:
- '@types/json-schema': 7.0.15
+ '@eslint/config-helpers@0.3.1': {}
- '@eslint/core@0.15.1':
+ '@eslint/core@0.15.2':
dependencies:
'@types/json-schema': 7.0.15
'@eslint/eslintrc@3.3.1':
dependencies:
ajv: 6.12.6
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@5.5.0)
espree: 10.4.0
globals: 14.0.0
ignore: 5.3.1
@@ -12649,13 +12522,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@eslint/js@9.31.0': {}
+ '@eslint/js@9.34.0': {}
'@eslint/object-schema@2.1.6': {}
- '@eslint/plugin-kit@0.3.1':
+ '@eslint/plugin-kit@0.3.5':
dependencies:
- '@eslint/core': 0.14.0
+ '@eslint/core': 0.15.2
levn: 0.4.1
'@fastify/accept-negotiator@2.0.0': {}
@@ -12707,7 +12580,7 @@ snapshots:
'@fastify/reply-from': 11.0.0
fast-querystring: 1.1.2
fastify-plugin: 5.0.0
- ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)
+ ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)
transitivePeerDependencies:
- bufferutil
- utf-8-validate
@@ -12754,7 +12627,7 @@ snapshots:
content-disposition: 0.5.4
fastify-plugin: 5.0.0
fastq: 1.17.1
- glob: 11.0.2
+ glob: 11.0.3
'@fastify/view@10.0.2':
dependencies:
@@ -12933,7 +12806,7 @@ snapshots:
'@img/sharp-wasm32@0.33.5':
dependencies:
- '@emnapi/runtime': 1.4.0
+ '@emnapi/runtime': 1.4.3
optional: true
'@img/sharp-wasm32@0.34.2':
@@ -12956,16 +12829,16 @@ snapshots:
'@img/sharp-win32-x64@0.34.2':
optional: true
- '@inquirer/confirm@5.0.2(@types/node@22.16.4)':
+ '@inquirer/confirm@5.0.2(@types/node@22.17.2)':
dependencies:
- '@inquirer/core': 10.1.0(@types/node@22.16.4)
- '@inquirer/type': 3.0.1(@types/node@22.16.4)
- '@types/node': 22.16.4
+ '@inquirer/core': 10.1.0(@types/node@22.17.2)
+ '@inquirer/type': 3.0.1(@types/node@22.17.2)
+ '@types/node': 22.17.2
- '@inquirer/core@10.1.0(@types/node@22.16.4)':
+ '@inquirer/core@10.1.0(@types/node@22.17.2)':
dependencies:
'@inquirer/figures': 1.0.8
- '@inquirer/type': 3.0.1(@types/node@22.16.4)
+ '@inquirer/type': 3.0.1(@types/node@22.17.2)
ansi-escapes: 4.3.2
cli-width: 4.1.0
mute-stream: 2.0.0
@@ -12978,11 +12851,11 @@ snapshots:
'@inquirer/figures@1.0.8': {}
- '@inquirer/type@3.0.1(@types/node@22.16.4)':
+ '@inquirer/type@3.0.1(@types/node@22.17.2)':
dependencies:
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
- '@ioredis/commands@1.2.0': {}
+ '@ioredis/commands@1.3.0': {}
'@isaacs/balanced-match@4.0.1': {}
@@ -13016,7 +12889,7 @@ snapshots:
'@jest/console@29.7.0':
dependencies:
'@jest/types': 29.6.3
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
chalk: 4.1.2
jest-message-util: 29.7.0
jest-util: 29.7.0
@@ -13029,14 +12902,14 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
ansi-escapes: 4.3.2
chalk: 4.1.2
ci-info: 3.7.1
exit: 0.1.2
graceful-fs: 4.2.11
jest-changed-files: 29.7.0
- jest-config: 29.7.0(@types/node@22.16.4)
+ jest-config: 29.7.0(@types/node@22.17.2)
jest-haste-map: 29.7.0
jest-message-util: 29.7.0
jest-regex-util: 29.6.3
@@ -13057,19 +12930,15 @@ snapshots:
- supports-color
- ts-node
- '@jest/create-cache-key-function@29.7.0':
- dependencies:
- '@jest/types': 29.6.3
-
- '@jest/create-cache-key-function@30.0.2':
+ '@jest/create-cache-key-function@30.0.5':
dependencies:
- '@jest/types': 30.0.1
+ '@jest/types': 30.0.5
'@jest/environment@29.7.0':
dependencies:
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
jest-mock: 29.7.0
'@jest/expect-utils@29.7.0':
@@ -13087,7 +12956,7 @@ snapshots:
dependencies:
'@jest/types': 29.6.3
'@sinonjs/fake-timers': 10.3.0
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
jest-message-util: 29.7.0
jest-mock: 29.7.0
jest-util: 29.7.0
@@ -13103,7 +12972,7 @@ snapshots:
'@jest/pattern@30.0.1':
dependencies:
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
jest-regex-util: 30.0.1
'@jest/reporters@29.7.0':
@@ -13113,8 +12982,8 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- '@jridgewell/trace-mapping': 0.3.25
- '@types/node': 22.16.4
+ '@jridgewell/trace-mapping': 0.3.29
+ '@types/node': 22.17.2
chalk: 4.1.2
collect-v8-coverage: 1.0.1
exit: 0.1.2
@@ -13139,13 +13008,13 @@ snapshots:
dependencies:
'@sinclair/typebox': 0.27.8
- '@jest/schemas@30.0.1':
+ '@jest/schemas@30.0.5':
dependencies:
- '@sinclair/typebox': 0.34.37
+ '@sinclair/typebox': 0.34.38
'@jest/source-map@29.6.3':
dependencies:
- '@jridgewell/trace-mapping': 0.3.25
+ '@jridgewell/trace-mapping': 0.3.29
callsites: 3.1.0
graceful-fs: 4.2.11
@@ -13153,7 +13022,7 @@ snapshots:
dependencies:
'@jest/console': 29.7.0
'@jest/types': 29.6.3
- '@types/istanbul-lib-coverage': 2.0.4
+ '@types/istanbul-lib-coverage': 2.0.6
collect-v8-coverage: 1.0.1
'@jest/test-sequencer@29.7.0':
@@ -13167,7 +13036,7 @@ snapshots:
dependencies:
'@babel/core': 7.24.7
'@jest/types': 29.6.3
- '@jridgewell/trace-mapping': 0.3.25
+ '@jridgewell/trace-mapping': 0.3.29
babel-plugin-istanbul: 6.1.1
chalk: 4.1.2
convert-source-map: 2.0.0
@@ -13186,55 +13055,52 @@ snapshots:
'@jest/types@29.6.3':
dependencies:
'@jest/schemas': 29.6.3
- '@types/istanbul-lib-coverage': 2.0.4
- '@types/istanbul-reports': 3.0.1
- '@types/node': 22.16.4
- '@types/yargs': 17.0.19
+ '@types/istanbul-lib-coverage': 2.0.6
+ '@types/istanbul-reports': 3.0.4
+ '@types/node': 22.17.2
+ '@types/yargs': 17.0.33
chalk: 4.1.2
- '@jest/types@30.0.1':
+ '@jest/types@30.0.5':
dependencies:
'@jest/pattern': 30.0.1
- '@jest/schemas': 30.0.1
+ '@jest/schemas': 30.0.5
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
'@types/yargs': 17.0.33
chalk: 4.1.2
- '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))':
+ '@joshwooding/vite-plugin-react-docgen-typescript@0.6.1(typescript@5.9.2)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))':
dependencies:
glob: 10.4.5
- magic-string: 0.27.0
- react-docgen-typescript: 2.2.2(typescript@5.8.3)
- vite: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
+ magic-string: 0.30.18
+ react-docgen-typescript: 2.2.2(typescript@5.9.2)
+ vite: 7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)
optionalDependencies:
- typescript: 5.8.3
+ typescript: 5.9.2
- '@jridgewell/gen-mapping@0.3.5':
+ '@jridgewell/gen-mapping@0.3.12':
dependencies:
- '@jridgewell/set-array': 1.2.1
'@jridgewell/sourcemap-codec': 1.5.0
- '@jridgewell/trace-mapping': 0.3.25
+ '@jridgewell/trace-mapping': 0.3.29
'@jridgewell/resolve-uri@3.1.0': {}
- '@jridgewell/set-array@1.2.1': {}
-
'@jridgewell/source-map@0.3.6':
dependencies:
- '@jridgewell/gen-mapping': 0.3.5
- '@jridgewell/trace-mapping': 0.3.25
+ '@jridgewell/gen-mapping': 0.3.12
+ '@jridgewell/trace-mapping': 0.3.29
'@jridgewell/sourcemap-codec@1.5.0': {}
- '@jridgewell/trace-mapping@0.3.25':
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.29':
dependencies:
'@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.5.0
- '@jsdevtools/ono@7.1.3': {}
-
'@kurkle/color@0.3.2': {}
'@levischuck/tiny-cbor@0.2.2': {}
@@ -13265,31 +13131,31 @@ snapshots:
dependencies:
'@mcaptcha/core-glue': 0.1.0-alpha-5
- '@mdx-js/react@3.0.1(@types/react@18.0.28)(react@19.1.0)':
+ '@mdx-js/react@3.0.1(@types/react@18.0.28)(react@19.1.1)':
dependencies:
'@types/mdx': 2.0.3
'@types/react': 18.0.28
- react: 19.1.0
+ react: 19.1.1
- '@microsoft/api-extractor-model@7.30.6(@types/node@22.16.4)':
+ '@microsoft/api-extractor-model@7.30.7(@types/node@22.17.2)':
dependencies:
'@microsoft/tsdoc': 0.15.1
'@microsoft/tsdoc-config': 0.17.1
- '@rushstack/node-core-library': 5.13.1(@types/node@22.16.4)
+ '@rushstack/node-core-library': 5.14.0(@types/node@22.17.2)
transitivePeerDependencies:
- '@types/node'
- '@microsoft/api-extractor@7.52.8(@types/node@22.16.4)':
+ '@microsoft/api-extractor@7.52.11(@types/node@22.17.2)':
dependencies:
- '@microsoft/api-extractor-model': 7.30.6(@types/node@22.16.4)
+ '@microsoft/api-extractor-model': 7.30.7(@types/node@22.17.2)
'@microsoft/tsdoc': 0.15.1
'@microsoft/tsdoc-config': 0.17.1
- '@rushstack/node-core-library': 5.13.1(@types/node@22.16.4)
+ '@rushstack/node-core-library': 5.14.0(@types/node@22.17.2)
'@rushstack/rig-package': 0.5.3
- '@rushstack/terminal': 0.15.3(@types/node@22.16.4)
- '@rushstack/ts-command-line': 5.0.1(@types/node@22.16.4)
+ '@rushstack/terminal': 0.15.4(@types/node@22.17.2)
+ '@rushstack/ts-command-line': 5.0.2(@types/node@22.17.2)
lodash: 4.17.21
- minimatch: 3.0.8
+ minimatch: 10.0.3
resolve: 1.22.8
semver: 7.5.4
source-map: 0.6.1
@@ -13308,14 +13174,14 @@ snapshots:
'@misskey-dev/browser-image-resizer@2024.1.0': {}
- '@misskey-dev/eslint-plugin@2.1.0(@eslint/compat@1.1.1)(@stylistic/eslint-plugin@2.13.0(eslint@9.31.0)(typescript@5.8.3))(@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3))(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0))(eslint@9.31.0)(globals@16.3.0)':
+ '@misskey-dev/eslint-plugin@2.1.0(@eslint/compat@1.1.1)(@stylistic/eslint-plugin@2.13.0(eslint@9.34.0)(typescript@5.9.2))(@typescript-eslint/eslint-plugin@8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2))(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0))(eslint@9.34.0)(globals@16.3.0)':
dependencies:
'@eslint/compat': 1.1.1
- '@stylistic/eslint-plugin': 2.13.0(eslint@9.31.0)(typescript@5.8.3)
- '@typescript-eslint/eslint-plugin': 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)
- '@typescript-eslint/parser': 8.37.0(eslint@9.31.0)(typescript@5.8.3)
- eslint: 9.31.0
- eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)
+ '@stylistic/eslint-plugin': 2.13.0(eslint@9.34.0)(typescript@5.9.2)
+ '@typescript-eslint/eslint-plugin': 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)
+ '@typescript-eslint/parser': 8.40.0(eslint@9.34.0)(typescript@5.9.2)
+ eslint: 9.34.0
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)
globals: 16.3.0
'@misskey-dev/sharp-read-bmp@1.2.0':
@@ -13324,17 +13190,7 @@ snapshots:
decode-ico: 0.4.1
sharp: 0.33.5
- '@misskey-dev/summaly@5.2.1':
- dependencies:
- cheerio: 1.0.0
- escape-regexp: 0.0.1
- got: 14.4.7
- html-entities: 2.5.2
- iconv-lite: 0.6.3
- jschardet: 3.1.4
- private-ip: 3.0.2
-
- '@misskey-dev/summaly@5.2.2':
+ '@misskey-dev/summaly@5.2.3':
dependencies:
cheerio: 1.1.0
escape-regexp: 0.0.1
@@ -13371,50 +13227,50 @@ snapshots:
outvariant: 1.4.3
strict-event-emitter: 0.5.1
- '@napi-rs/canvas-android-arm64@0.1.71':
+ '@napi-rs/canvas-android-arm64@0.1.77':
optional: true
- '@napi-rs/canvas-darwin-arm64@0.1.71':
+ '@napi-rs/canvas-darwin-arm64@0.1.77':
optional: true
- '@napi-rs/canvas-darwin-x64@0.1.71':
+ '@napi-rs/canvas-darwin-x64@0.1.77':
optional: true
- '@napi-rs/canvas-linux-arm-gnueabihf@0.1.71':
+ '@napi-rs/canvas-linux-arm-gnueabihf@0.1.77':
optional: true
- '@napi-rs/canvas-linux-arm64-gnu@0.1.71':
+ '@napi-rs/canvas-linux-arm64-gnu@0.1.77':
optional: true
- '@napi-rs/canvas-linux-arm64-musl@0.1.71':
+ '@napi-rs/canvas-linux-arm64-musl@0.1.77':
optional: true
- '@napi-rs/canvas-linux-riscv64-gnu@0.1.71':
+ '@napi-rs/canvas-linux-riscv64-gnu@0.1.77':
optional: true
- '@napi-rs/canvas-linux-x64-gnu@0.1.71':
+ '@napi-rs/canvas-linux-x64-gnu@0.1.77':
optional: true
- '@napi-rs/canvas-linux-x64-musl@0.1.71':
+ '@napi-rs/canvas-linux-x64-musl@0.1.77':
optional: true
- '@napi-rs/canvas-win32-x64-msvc@0.1.71':
+ '@napi-rs/canvas-win32-x64-msvc@0.1.77':
optional: true
- '@napi-rs/canvas@0.1.71':
+ '@napi-rs/canvas@0.1.77':
optionalDependencies:
- '@napi-rs/canvas-android-arm64': 0.1.71
- '@napi-rs/canvas-darwin-arm64': 0.1.71
- '@napi-rs/canvas-darwin-x64': 0.1.71
- '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.71
- '@napi-rs/canvas-linux-arm64-gnu': 0.1.71
- '@napi-rs/canvas-linux-arm64-musl': 0.1.71
- '@napi-rs/canvas-linux-riscv64-gnu': 0.1.71
- '@napi-rs/canvas-linux-x64-gnu': 0.1.71
- '@napi-rs/canvas-linux-x64-musl': 0.1.71
- '@napi-rs/canvas-win32-x64-msvc': 0.1.71
+ '@napi-rs/canvas-android-arm64': 0.1.77
+ '@napi-rs/canvas-darwin-arm64': 0.1.77
+ '@napi-rs/canvas-darwin-x64': 0.1.77
+ '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.77
+ '@napi-rs/canvas-linux-arm64-gnu': 0.1.77
+ '@napi-rs/canvas-linux-arm64-musl': 0.1.77
+ '@napi-rs/canvas-linux-riscv64-gnu': 0.1.77
+ '@napi-rs/canvas-linux-x64-gnu': 0.1.77
+ '@napi-rs/canvas-linux-x64-musl': 0.1.77
+ '@napi-rs/canvas-win32-x64-msvc': 0.1.77
- '@nestjs/common@11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2)':
+ '@nestjs/common@11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2)':
dependencies:
file-type: 21.0.0
iterare: 1.2.1
@@ -13426,9 +13282,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@nestjs/core@11.1.3(@nestjs/common@11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.19)(reflect-metadata@0.2.2)(rxjs@7.8.2)':
+ '@nestjs/core@11.1.6(@nestjs/common@11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(reflect-metadata@0.2.2)(rxjs@7.8.2)':
dependencies:
- '@nestjs/common': 11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/common': 11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nuxt/opencollective': 0.4.1
fast-safe-stringify: 2.1.1
iterare: 1.2.1
@@ -13438,30 +13294,32 @@ snapshots:
tslib: 2.8.1
uid: 2.0.2
optionalDependencies:
- '@nestjs/platform-express': 10.4.19(@nestjs/common@11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)
+ '@nestjs/platform-express': 10.4.20(@nestjs/common@11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)
- '@nestjs/platform-express@10.4.19(@nestjs/common@11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)':
+ '@nestjs/platform-express@10.4.20(@nestjs/common@11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)':
dependencies:
- '@nestjs/common': 11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2)
- '@nestjs/core': 11.1.3(@nestjs/common@11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.19)(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/common': 11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(reflect-metadata@0.2.2)(rxjs@7.8.2)
body-parser: 1.20.3
cors: 2.8.5
express: 4.21.2
- multer: 2.0.1
+ multer: 2.0.2
tslib: 2.8.1
transitivePeerDependencies:
- supports-color
- '@nestjs/testing@11.1.3(@nestjs/common@11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)(@nestjs/platform-express@10.4.19)':
+ '@nestjs/testing@11.1.6(@nestjs/common@11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@nestjs/platform-express@10.4.20)':
dependencies:
- '@nestjs/common': 11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2)
- '@nestjs/core': 11.1.3(@nestjs/common@11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.19)(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/common': 11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(reflect-metadata@0.2.2)(rxjs@7.8.2)
tslib: 2.8.1
optionalDependencies:
- '@nestjs/platform-express': 10.4.19(@nestjs/common@11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)
+ '@nestjs/platform-express': 10.4.20(@nestjs/common@11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)
'@noble/hashes@1.7.1': {}
+ '@noble/hashes@1.8.0': {}
+
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -13478,7 +13336,7 @@ snapshots:
dependencies:
agent-base: 7.1.3
http-proxy-agent: 7.0.2
- https-proxy-agent: 7.0.6(supports-color@10.0.0)
+ https-proxy-agent: 7.0.6(supports-color@10.2.0)
lru-cache: 10.4.3
socks-proxy-agent: 8.0.5
transitivePeerDependencies:
@@ -13910,9 +13768,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@readme/better-ajv-errors@2.2.2(ajv@8.17.1)':
+ '@readme/better-ajv-errors@2.3.2(ajv@8.17.1)':
dependencies:
- '@babel/code-frame': 7.24.7
+ '@babel/code-frame': 7.27.1
'@babel/runtime': 7.27.0
'@humanwhocodes/momoa': 2.0.4
ajv: 8.17.1
@@ -13920,23 +13778,14 @@ snapshots:
leven: 3.1.0
picocolors: 1.1.1
- '@readme/json-schema-ref-parser@1.2.0':
- dependencies:
- '@jsdevtools/ono': 7.1.3
- '@types/json-schema': 7.0.15
- call-me-maybe: 1.0.2
- js-yaml: 4.1.0
-
- '@readme/openapi-parser@2.7.0(openapi-types@12.1.3)':
+ '@readme/openapi-parser@5.0.1(openapi-types@12.1.3)':
dependencies:
- '@apidevtools/swagger-methods': 3.0.2
- '@jsdevtools/ono': 7.1.3
- '@readme/better-ajv-errors': 2.2.2(ajv@8.17.1)
- '@readme/json-schema-ref-parser': 1.2.0
+ '@apidevtools/json-schema-ref-parser': 14.1.1
+ '@readme/better-ajv-errors': 2.3.2(ajv@8.17.1)
'@readme/openapi-schemas': 3.1.0
+ '@types/json-schema': 7.0.15
ajv: 8.17.1
ajv-draft-04: 1.0.0(ajv@8.17.1)
- call-me-maybe: 1.0.2
openapi-types: 12.1.3
'@readme/openapi-schemas@3.1.0': {}
@@ -13950,12 +13799,12 @@ snapshots:
'@redocly/config@0.22.2': {}
- '@redocly/openapi-core@1.34.3(supports-color@10.0.0)':
+ '@redocly/openapi-core@1.34.5(supports-color@10.2.0)':
dependencies:
'@redocly/ajv': 8.11.2
'@redocly/config': 0.22.2
colorette: 1.4.0
- https-proxy-agent: 7.0.6(supports-color@10.0.0)
+ https-proxy-agent: 7.0.6(supports-color@10.2.0)
js-levenshtein: 1.1.6
js-yaml: 4.1.0
minimatch: 5.1.6
@@ -13964,90 +13813,152 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@rollup/plugin-json@6.1.0(rollup@4.45.1)':
+ '@rolldown/pluginutils@1.0.0-beta.29': {}
+
+ '@rollup/plugin-json@6.1.0(rollup@4.48.0)':
dependencies:
- '@rollup/pluginutils': 5.2.0(rollup@4.45.1)
+ '@rollup/pluginutils': 5.2.0(rollup@4.48.0)
optionalDependencies:
- rollup: 4.45.1
+ rollup: 4.48.0
- '@rollup/plugin-replace@6.0.2(rollup@4.45.1)':
+ '@rollup/plugin-replace@6.0.2(rollup@4.48.0)':
dependencies:
- '@rollup/pluginutils': 5.2.0(rollup@4.45.1)
- magic-string: 0.30.17
+ '@rollup/pluginutils': 5.2.0(rollup@4.48.0)
+ magic-string: 0.30.18
optionalDependencies:
- rollup: 4.45.1
+ rollup: 4.48.0
- '@rollup/pluginutils@5.2.0(rollup@4.45.1)':
+ '@rollup/pluginutils@5.2.0(rollup@4.48.0)':
dependencies:
'@types/estree': 1.0.8
estree-walker: 2.0.2
- picomatch: 4.0.2
+ picomatch: 4.0.3
optionalDependencies:
- rollup: 4.45.1
+ rollup: 4.48.0
+
+ '@rollup/rollup-android-arm-eabi@4.46.2':
+ optional: true
+
+ '@rollup/rollup-android-arm-eabi@4.48.0':
+ optional: true
- '@rollup/rollup-android-arm-eabi@4.45.1':
+ '@rollup/rollup-android-arm64@4.46.2':
optional: true
- '@rollup/rollup-android-arm64@4.45.1':
+ '@rollup/rollup-android-arm64@4.48.0':
optional: true
- '@rollup/rollup-darwin-arm64@4.45.1':
+ '@rollup/rollup-darwin-arm64@4.46.2':
optional: true
- '@rollup/rollup-darwin-x64@4.45.1':
+ '@rollup/rollup-darwin-arm64@4.48.0':
optional: true
- '@rollup/rollup-freebsd-arm64@4.45.1':
+ '@rollup/rollup-darwin-x64@4.46.2':
optional: true
- '@rollup/rollup-freebsd-x64@4.45.1':
+ '@rollup/rollup-darwin-x64@4.48.0':
optional: true
- '@rollup/rollup-linux-arm-gnueabihf@4.45.1':
+ '@rollup/rollup-freebsd-arm64@4.46.2':
optional: true
- '@rollup/rollup-linux-arm-musleabihf@4.45.1':
+ '@rollup/rollup-freebsd-arm64@4.48.0':
optional: true
- '@rollup/rollup-linux-arm64-gnu@4.45.1':
+ '@rollup/rollup-freebsd-x64@4.46.2':
optional: true
- '@rollup/rollup-linux-arm64-musl@4.45.1':
+ '@rollup/rollup-freebsd-x64@4.48.0':
optional: true
- '@rollup/rollup-linux-loongarch64-gnu@4.45.1':
+ '@rollup/rollup-linux-arm-gnueabihf@4.46.2':
optional: true
- '@rollup/rollup-linux-powerpc64le-gnu@4.45.1':
+ '@rollup/rollup-linux-arm-gnueabihf@4.48.0':
optional: true
- '@rollup/rollup-linux-riscv64-gnu@4.45.1':
+ '@rollup/rollup-linux-arm-musleabihf@4.46.2':
optional: true
- '@rollup/rollup-linux-riscv64-musl@4.45.1':
+ '@rollup/rollup-linux-arm-musleabihf@4.48.0':
optional: true
- '@rollup/rollup-linux-s390x-gnu@4.45.1':
+ '@rollup/rollup-linux-arm64-gnu@4.46.2':
optional: true
- '@rollup/rollup-linux-x64-gnu@4.45.1':
+ '@rollup/rollup-linux-arm64-gnu@4.48.0':
optional: true
- '@rollup/rollup-linux-x64-musl@4.45.1':
+ '@rollup/rollup-linux-arm64-musl@4.46.2':
optional: true
- '@rollup/rollup-win32-arm64-msvc@4.45.1':
+ '@rollup/rollup-linux-arm64-musl@4.48.0':
optional: true
- '@rollup/rollup-win32-ia32-msvc@4.45.1':
+ '@rollup/rollup-linux-loongarch64-gnu@4.46.2':
optional: true
- '@rollup/rollup-win32-x64-msvc@4.45.1':
+ '@rollup/rollup-linux-loongarch64-gnu@4.48.0':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.48.0':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.48.0':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.48.0':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.48.0':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.48.0':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.48.0':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.46.2':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.48.0':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.46.2':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.48.0':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.46.2':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.48.0':
optional: true
'@rtsao/scc@1.1.0': {}
- '@rushstack/node-core-library@5.13.1(@types/node@22.16.4)':
+ '@rushstack/node-core-library@5.14.0(@types/node@22.17.2)':
dependencies:
ajv: 8.13.0
ajv-draft-04: 1.0.0(ajv@8.13.0)
@@ -14058,23 +13969,23 @@ snapshots:
resolve: 1.22.8
semver: 7.5.4
optionalDependencies:
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
'@rushstack/rig-package@0.5.3':
dependencies:
resolve: 1.22.8
strip-json-comments: 3.1.1
- '@rushstack/terminal@0.15.3(@types/node@22.16.4)':
+ '@rushstack/terminal@0.15.4(@types/node@22.17.2)':
dependencies:
- '@rushstack/node-core-library': 5.13.1(@types/node@22.16.4)
+ '@rushstack/node-core-library': 5.14.0(@types/node@22.17.2)
supports-color: 8.1.1
optionalDependencies:
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
- '@rushstack/ts-command-line@5.0.1(@types/node@22.16.4)':
+ '@rushstack/ts-command-line@5.0.2(@types/node@22.17.2)':
dependencies:
- '@rushstack/terminal': 0.15.3(@types/node@22.16.4)
+ '@rushstack/terminal': 0.15.4(@types/node@22.17.2)
'@types/argparse': 1.0.38
argparse: 1.0.10
string-argv: 0.3.1
@@ -14083,63 +13994,63 @@ snapshots:
'@sec-ant/readable-stream@0.4.1': {}
- '@sentry-internal/browser-utils@9.28.0':
+ '@sentry-internal/browser-utils@10.5.0':
dependencies:
- '@sentry/core': 9.28.0
+ '@sentry/core': 10.5.0
- '@sentry-internal/browser-utils@9.39.0':
+ '@sentry-internal/browser-utils@9.46.0':
dependencies:
- '@sentry/core': 9.39.0
+ '@sentry/core': 9.46.0
- '@sentry-internal/feedback@9.28.0':
+ '@sentry-internal/feedback@10.5.0':
dependencies:
- '@sentry/core': 9.28.0
+ '@sentry/core': 10.5.0
- '@sentry-internal/feedback@9.39.0':
+ '@sentry-internal/feedback@9.46.0':
dependencies:
- '@sentry/core': 9.39.0
+ '@sentry/core': 9.46.0
- '@sentry-internal/replay-canvas@9.28.0':
+ '@sentry-internal/replay-canvas@10.5.0':
dependencies:
- '@sentry-internal/replay': 9.28.0
- '@sentry/core': 9.28.0
+ '@sentry-internal/replay': 10.5.0
+ '@sentry/core': 10.5.0
- '@sentry-internal/replay-canvas@9.39.0':
+ '@sentry-internal/replay-canvas@9.46.0':
dependencies:
- '@sentry-internal/replay': 9.39.0
- '@sentry/core': 9.39.0
+ '@sentry-internal/replay': 9.46.0
+ '@sentry/core': 9.46.0
- '@sentry-internal/replay@9.28.0':
+ '@sentry-internal/replay@10.5.0':
dependencies:
- '@sentry-internal/browser-utils': 9.28.0
- '@sentry/core': 9.28.0
+ '@sentry-internal/browser-utils': 10.5.0
+ '@sentry/core': 10.5.0
- '@sentry-internal/replay@9.39.0':
+ '@sentry-internal/replay@9.46.0':
dependencies:
- '@sentry-internal/browser-utils': 9.39.0
- '@sentry/core': 9.39.0
+ '@sentry-internal/browser-utils': 9.46.0
+ '@sentry/core': 9.46.0
- '@sentry/browser@9.28.0':
+ '@sentry/browser@10.5.0':
dependencies:
- '@sentry-internal/browser-utils': 9.28.0
- '@sentry-internal/feedback': 9.28.0
- '@sentry-internal/replay': 9.28.0
- '@sentry-internal/replay-canvas': 9.28.0
- '@sentry/core': 9.28.0
+ '@sentry-internal/browser-utils': 10.5.0
+ '@sentry-internal/feedback': 10.5.0
+ '@sentry-internal/replay': 10.5.0
+ '@sentry-internal/replay-canvas': 10.5.0
+ '@sentry/core': 10.5.0
- '@sentry/browser@9.39.0':
+ '@sentry/browser@9.46.0':
dependencies:
- '@sentry-internal/browser-utils': 9.39.0
- '@sentry-internal/feedback': 9.39.0
- '@sentry-internal/replay': 9.39.0
- '@sentry-internal/replay-canvas': 9.39.0
- '@sentry/core': 9.39.0
+ '@sentry-internal/browser-utils': 9.46.0
+ '@sentry-internal/feedback': 9.46.0
+ '@sentry-internal/replay': 9.46.0
+ '@sentry-internal/replay-canvas': 9.46.0
+ '@sentry/core': 9.46.0
- '@sentry/core@8.55.0': {}
+ '@sentry/core@10.5.0': {}
- '@sentry/core@9.28.0': {}
+ '@sentry/core@8.55.0': {}
- '@sentry/core@9.39.0': {}
+ '@sentry/core@9.46.0': {}
'@sentry/node@8.55.0':
dependencies:
@@ -14196,49 +14107,49 @@ snapshots:
'@sentry/core': 8.55.0
'@sentry/node': 8.55.0
detect-libc: 2.0.4
- node-abi: 3.74.0
+ node-abi: 3.75.0
transitivePeerDependencies:
- supports-color
- '@sentry/vue@9.28.0(vue@3.5.17(typescript@5.8.3))':
+ '@sentry/vue@10.5.0(vue@3.5.19(typescript@5.9.2))':
dependencies:
- '@sentry/browser': 9.28.0
- '@sentry/core': 9.28.0
- vue: 3.5.17(typescript@5.8.3)
+ '@sentry/browser': 10.5.0
+ '@sentry/core': 10.5.0
+ vue: 3.5.19(typescript@5.9.2)
- '@sentry/vue@9.39.0(vue@3.5.17(typescript@5.8.3))':
+ '@sentry/vue@9.46.0(vue@3.5.19(typescript@5.9.2))':
dependencies:
- '@sentry/browser': 9.39.0
- '@sentry/core': 9.39.0
- vue: 3.5.17(typescript@5.8.3)
+ '@sentry/browser': 9.46.0
+ '@sentry/core': 9.46.0
+ vue: 3.5.19(typescript@5.9.2)
- '@shikijs/core@3.8.0':
+ '@shikijs/core@3.11.0':
dependencies:
- '@shikijs/types': 3.8.0
+ '@shikijs/types': 3.11.0
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
hast-util-to-html: 9.0.5
- '@shikijs/engine-javascript@3.8.0':
+ '@shikijs/engine-javascript@3.11.0':
dependencies:
- '@shikijs/types': 3.8.0
+ '@shikijs/types': 3.11.0
'@shikijs/vscode-textmate': 10.0.2
oniguruma-to-es: 4.3.3
- '@shikijs/engine-oniguruma@3.8.0':
+ '@shikijs/engine-oniguruma@3.11.0':
dependencies:
- '@shikijs/types': 3.8.0
+ '@shikijs/types': 3.11.0
'@shikijs/vscode-textmate': 10.0.2
- '@shikijs/langs@3.8.0':
+ '@shikijs/langs@3.11.0':
dependencies:
- '@shikijs/types': 3.8.0
+ '@shikijs/types': 3.11.0
- '@shikijs/themes@3.8.0':
+ '@shikijs/themes@3.11.0':
dependencies:
- '@shikijs/types': 3.8.0
+ '@shikijs/types': 3.11.0
- '@shikijs/types@3.8.0':
+ '@shikijs/types@3.11.0':
dependencies:
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
@@ -14271,7 +14182,7 @@ snapshots:
'@sinclair/typebox@0.27.8': {}
- '@sinclair/typebox@0.34.37': {}
+ '@sinclair/typebox@0.34.38': {}
'@sindresorhus/is@5.6.0': {}
@@ -14316,9 +14227,9 @@ snapshots:
'@smithy/types': 2.12.0
tslib: 2.8.1
- '@smithy/abort-controller@4.0.4':
+ '@smithy/abort-controller@4.0.5':
dependencies:
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
tslib: 2.8.1
'@smithy/chunked-blob-reader-native@4.0.0':
@@ -14330,95 +14241,97 @@ snapshots:
dependencies:
tslib: 2.8.1
- '@smithy/config-resolver@4.1.4':
+ '@smithy/config-resolver@4.1.5':
dependencies:
- '@smithy/node-config-provider': 4.1.3
- '@smithy/types': 4.3.1
+ '@smithy/node-config-provider': 4.1.4
+ '@smithy/types': 4.3.2
'@smithy/util-config-provider': 4.0.0
- '@smithy/util-middleware': 4.0.4
+ '@smithy/util-middleware': 4.0.5
tslib: 2.8.1
- '@smithy/core@3.5.3':
+ '@smithy/core@3.8.0':
dependencies:
- '@smithy/middleware-serde': 4.0.8
- '@smithy/protocol-http': 5.1.2
- '@smithy/types': 4.3.1
+ '@smithy/middleware-serde': 4.0.9
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/types': 4.3.2
'@smithy/util-base64': 4.0.0
'@smithy/util-body-length-browser': 4.0.0
- '@smithy/util-middleware': 4.0.4
- '@smithy/util-stream': 4.2.2
+ '@smithy/util-middleware': 4.0.5
+ '@smithy/util-stream': 4.2.4
'@smithy/util-utf8': 4.0.0
+ '@types/uuid': 9.0.8
tslib: 2.8.1
+ uuid: 9.0.1
- '@smithy/credential-provider-imds@4.0.6':
+ '@smithy/credential-provider-imds@4.0.7':
dependencies:
- '@smithy/node-config-provider': 4.1.3
- '@smithy/property-provider': 4.0.4
- '@smithy/types': 4.3.1
- '@smithy/url-parser': 4.0.4
+ '@smithy/node-config-provider': 4.1.4
+ '@smithy/property-provider': 4.0.5
+ '@smithy/types': 4.3.2
+ '@smithy/url-parser': 4.0.5
tslib: 2.8.1
- '@smithy/eventstream-codec@4.0.4':
+ '@smithy/eventstream-codec@4.0.5':
dependencies:
'@aws-crypto/crc32': 5.2.0
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
'@smithy/util-hex-encoding': 4.0.0
tslib: 2.8.1
- '@smithy/eventstream-serde-browser@4.0.4':
+ '@smithy/eventstream-serde-browser@4.0.5':
dependencies:
- '@smithy/eventstream-serde-universal': 4.0.4
- '@smithy/types': 4.3.1
+ '@smithy/eventstream-serde-universal': 4.0.5
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@smithy/eventstream-serde-config-resolver@4.1.2':
+ '@smithy/eventstream-serde-config-resolver@4.1.3':
dependencies:
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@smithy/eventstream-serde-node@4.0.4':
+ '@smithy/eventstream-serde-node@4.0.5':
dependencies:
- '@smithy/eventstream-serde-universal': 4.0.4
- '@smithy/types': 4.3.1
+ '@smithy/eventstream-serde-universal': 4.0.5
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@smithy/eventstream-serde-universal@4.0.4':
+ '@smithy/eventstream-serde-universal@4.0.5':
dependencies:
- '@smithy/eventstream-codec': 4.0.4
- '@smithy/types': 4.3.1
+ '@smithy/eventstream-codec': 4.0.5
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@smithy/fetch-http-handler@5.0.4':
+ '@smithy/fetch-http-handler@5.1.1':
dependencies:
- '@smithy/protocol-http': 5.1.2
- '@smithy/querystring-builder': 4.0.4
- '@smithy/types': 4.3.1
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/querystring-builder': 4.0.5
+ '@smithy/types': 4.3.2
'@smithy/util-base64': 4.0.0
tslib: 2.8.1
- '@smithy/hash-blob-browser@4.0.4':
+ '@smithy/hash-blob-browser@4.0.5':
dependencies:
'@smithy/chunked-blob-reader': 5.0.0
'@smithy/chunked-blob-reader-native': 4.0.0
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@smithy/hash-node@4.0.4':
+ '@smithy/hash-node@4.0.5':
dependencies:
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
'@smithy/util-buffer-from': 4.0.0
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
- '@smithy/hash-stream-node@4.0.4':
+ '@smithy/hash-stream-node@4.0.5':
dependencies:
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
- '@smithy/invalid-dependency@4.0.4':
+ '@smithy/invalid-dependency@4.0.5':
dependencies:
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
tslib: 2.8.1
'@smithy/is-array-buffer@2.0.0':
@@ -14429,57 +14342,58 @@ snapshots:
dependencies:
tslib: 2.8.1
- '@smithy/md5-js@4.0.4':
+ '@smithy/md5-js@4.0.5':
dependencies:
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
- '@smithy/middleware-content-length@4.0.4':
+ '@smithy/middleware-content-length@4.0.5':
dependencies:
- '@smithy/protocol-http': 5.1.2
- '@smithy/types': 4.3.1
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@smithy/middleware-endpoint@4.1.11':
+ '@smithy/middleware-endpoint@4.1.18':
dependencies:
- '@smithy/core': 3.5.3
- '@smithy/middleware-serde': 4.0.8
- '@smithy/node-config-provider': 4.1.3
- '@smithy/shared-ini-file-loader': 4.0.4
- '@smithy/types': 4.3.1
- '@smithy/url-parser': 4.0.4
- '@smithy/util-middleware': 4.0.4
+ '@smithy/core': 3.8.0
+ '@smithy/middleware-serde': 4.0.9
+ '@smithy/node-config-provider': 4.1.4
+ '@smithy/shared-ini-file-loader': 4.0.5
+ '@smithy/types': 4.3.2
+ '@smithy/url-parser': 4.0.5
+ '@smithy/util-middleware': 4.0.5
tslib: 2.8.1
- '@smithy/middleware-retry@4.1.12':
+ '@smithy/middleware-retry@4.1.19':
dependencies:
- '@smithy/node-config-provider': 4.1.3
- '@smithy/protocol-http': 5.1.2
- '@smithy/service-error-classification': 4.0.5
- '@smithy/smithy-client': 4.4.3
- '@smithy/types': 4.3.1
- '@smithy/util-middleware': 4.0.4
- '@smithy/util-retry': 4.0.5
+ '@smithy/node-config-provider': 4.1.4
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/service-error-classification': 4.0.7
+ '@smithy/smithy-client': 4.4.10
+ '@smithy/types': 4.3.2
+ '@smithy/util-middleware': 4.0.5
+ '@smithy/util-retry': 4.0.7
+ '@types/uuid': 9.0.8
tslib: 2.8.1
uuid: 9.0.1
- '@smithy/middleware-serde@4.0.8':
+ '@smithy/middleware-serde@4.0.9':
dependencies:
- '@smithy/protocol-http': 5.1.2
- '@smithy/types': 4.3.1
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@smithy/middleware-stack@4.0.4':
+ '@smithy/middleware-stack@4.0.5':
dependencies:
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@smithy/node-config-provider@4.1.3':
+ '@smithy/node-config-provider@4.1.4':
dependencies:
- '@smithy/property-provider': 4.0.4
- '@smithy/shared-ini-file-loader': 4.0.4
- '@smithy/types': 4.3.1
+ '@smithy/property-provider': 4.0.5
+ '@smithy/shared-ini-file-loader': 4.0.5
+ '@smithy/types': 4.3.2
tslib: 2.8.1
'@smithy/node-http-handler@2.5.0':
@@ -14490,17 +14404,17 @@ snapshots:
'@smithy/types': 2.12.0
tslib: 2.8.1
- '@smithy/node-http-handler@4.0.6':
+ '@smithy/node-http-handler@4.1.1':
dependencies:
- '@smithy/abort-controller': 4.0.4
- '@smithy/protocol-http': 5.1.2
- '@smithy/querystring-builder': 4.0.4
- '@smithy/types': 4.3.1
+ '@smithy/abort-controller': 4.0.5
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/querystring-builder': 4.0.5
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@smithy/property-provider@4.0.4':
+ '@smithy/property-provider@4.0.5':
dependencies:
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
tslib: 2.8.1
'@smithy/protocol-http@3.3.0':
@@ -14508,9 +14422,9 @@ snapshots:
'@smithy/types': 2.12.0
tslib: 2.8.1
- '@smithy/protocol-http@5.1.2':
+ '@smithy/protocol-http@5.1.3':
dependencies:
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
tslib: 2.8.1
'@smithy/querystring-builder@2.2.0':
@@ -14519,59 +14433,59 @@ snapshots:
'@smithy/util-uri-escape': 2.2.0
tslib: 2.8.1
- '@smithy/querystring-builder@4.0.4':
+ '@smithy/querystring-builder@4.0.5':
dependencies:
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
'@smithy/util-uri-escape': 4.0.0
tslib: 2.8.1
- '@smithy/querystring-parser@4.0.4':
+ '@smithy/querystring-parser@4.0.5':
dependencies:
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@smithy/service-error-classification@4.0.5':
+ '@smithy/service-error-classification@4.0.7':
dependencies:
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
- '@smithy/shared-ini-file-loader@4.0.4':
+ '@smithy/shared-ini-file-loader@4.0.5':
dependencies:
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@smithy/signature-v4@5.1.2':
+ '@smithy/signature-v4@5.1.3':
dependencies:
'@smithy/is-array-buffer': 4.0.0
- '@smithy/protocol-http': 5.1.2
- '@smithy/types': 4.3.1
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/types': 4.3.2
'@smithy/util-hex-encoding': 4.0.0
- '@smithy/util-middleware': 4.0.4
+ '@smithy/util-middleware': 4.0.5
'@smithy/util-uri-escape': 4.0.0
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
- '@smithy/smithy-client@4.4.3':
+ '@smithy/smithy-client@4.4.10':
dependencies:
- '@smithy/core': 3.5.3
- '@smithy/middleware-endpoint': 4.1.11
- '@smithy/middleware-stack': 4.0.4
- '@smithy/protocol-http': 5.1.2
- '@smithy/types': 4.3.1
- '@smithy/util-stream': 4.2.2
+ '@smithy/core': 3.8.0
+ '@smithy/middleware-endpoint': 4.1.18
+ '@smithy/middleware-stack': 4.0.5
+ '@smithy/protocol-http': 5.1.3
+ '@smithy/types': 4.3.2
+ '@smithy/util-stream': 4.2.4
tslib: 2.8.1
'@smithy/types@2.12.0':
dependencies:
tslib: 2.8.1
- '@smithy/types@4.3.1':
+ '@smithy/types@4.3.2':
dependencies:
tslib: 2.8.1
- '@smithy/url-parser@4.0.4':
+ '@smithy/url-parser@4.0.5':
dependencies:
- '@smithy/querystring-parser': 4.0.4
- '@smithy/types': 4.3.1
+ '@smithy/querystring-parser': 4.0.5
+ '@smithy/types': 4.3.2
tslib: 2.8.1
'@smithy/util-base64@4.0.0':
@@ -14602,50 +14516,50 @@ snapshots:
dependencies:
tslib: 2.8.1
- '@smithy/util-defaults-mode-browser@4.0.19':
+ '@smithy/util-defaults-mode-browser@4.0.26':
dependencies:
- '@smithy/property-provider': 4.0.4
- '@smithy/smithy-client': 4.4.3
- '@smithy/types': 4.3.1
+ '@smithy/property-provider': 4.0.5
+ '@smithy/smithy-client': 4.4.10
+ '@smithy/types': 4.3.2
bowser: 2.11.0
tslib: 2.8.1
- '@smithy/util-defaults-mode-node@4.0.19':
+ '@smithy/util-defaults-mode-node@4.0.26':
dependencies:
- '@smithy/config-resolver': 4.1.4
- '@smithy/credential-provider-imds': 4.0.6
- '@smithy/node-config-provider': 4.1.3
- '@smithy/property-provider': 4.0.4
- '@smithy/smithy-client': 4.4.3
- '@smithy/types': 4.3.1
+ '@smithy/config-resolver': 4.1.5
+ '@smithy/credential-provider-imds': 4.0.7
+ '@smithy/node-config-provider': 4.1.4
+ '@smithy/property-provider': 4.0.5
+ '@smithy/smithy-client': 4.4.10
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@smithy/util-endpoints@3.0.6':
+ '@smithy/util-endpoints@3.0.7':
dependencies:
- '@smithy/node-config-provider': 4.1.3
- '@smithy/types': 4.3.1
+ '@smithy/node-config-provider': 4.1.4
+ '@smithy/types': 4.3.2
tslib: 2.8.1
'@smithy/util-hex-encoding@4.0.0':
dependencies:
tslib: 2.8.1
- '@smithy/util-middleware@4.0.4':
+ '@smithy/util-middleware@4.0.5':
dependencies:
- '@smithy/types': 4.3.1
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@smithy/util-retry@4.0.5':
+ '@smithy/util-retry@4.0.7':
dependencies:
- '@smithy/service-error-classification': 4.0.5
- '@smithy/types': 4.3.1
+ '@smithy/service-error-classification': 4.0.7
+ '@smithy/types': 4.3.2
tslib: 2.8.1
- '@smithy/util-stream@4.2.2':
+ '@smithy/util-stream@4.2.4':
dependencies:
- '@smithy/fetch-http-handler': 5.0.4
- '@smithy/node-http-handler': 4.0.6
- '@smithy/types': 4.3.1
+ '@smithy/fetch-http-handler': 5.1.1
+ '@smithy/node-http-handler': 4.1.1
+ '@smithy/types': 4.3.2
'@smithy/util-base64': 4.0.0
'@smithy/util-buffer-from': 4.0.0
'@smithy/util-hex-encoding': 4.0.0
@@ -14670,317 +14584,292 @@ snapshots:
'@smithy/util-buffer-from': 4.0.0
tslib: 2.8.1
- '@smithy/util-waiter@4.0.5':
+ '@smithy/util-waiter@4.0.7':
dependencies:
- '@smithy/abort-controller': 4.0.4
- '@smithy/types': 4.3.1
+ '@smithy/abort-controller': 4.0.5
+ '@smithy/types': 4.3.2
tslib: 2.8.1
'@sqltools/formatter@1.2.5': {}
- '@storybook/addon-actions@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/addon-actions@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
'@storybook/global': 5.0.0
'@types/uuid': 9.0.8
dequal: 2.0.3
polished: 4.2.2
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
uuid: 9.0.1
- '@storybook/addon-backgrounds@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/addon-backgrounds@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
'@storybook/global': 5.0.0
memoizerific: 1.11.3
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
ts-dedent: 2.2.0
- '@storybook/addon-controls@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/addon-controls@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
'@storybook/global': 5.0.0
dequal: 2.0.3
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
ts-dedent: 2.2.0
- '@storybook/addon-docs@8.6.14(@types/react@18.0.28)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/addon-docs@8.6.14(@types/react@18.0.28)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
- '@mdx-js/react': 3.0.1(@types/react@18.0.28)(react@19.1.0)
- '@storybook/blocks': 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/csf-plugin': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/react-dom-shim': 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- react: 19.1.0
- react-dom: 19.1.0(react@19.1.0)
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ '@mdx-js/react': 3.0.1(@types/react@18.0.28)(react@19.1.1)
+ '@storybook/blocks': 8.6.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/csf-plugin': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/react-dom-shim': 8.6.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ react: 19.1.1
+ react-dom: 19.1.1(react@19.1.1)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
ts-dedent: 2.2.0
transitivePeerDependencies:
- '@types/react'
- '@storybook/addon-essentials@8.6.14(@types/react@18.0.28)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/addon-essentials@8.6.14(@types/react@18.0.28)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
- '@storybook/addon-actions': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/addon-backgrounds': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/addon-controls': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/addon-docs': 8.6.14(@types/react@18.0.28)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/addon-highlight': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/addon-measure': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/addon-outline': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/addon-toolbars': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/addon-viewport': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ '@storybook/addon-actions': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/addon-backgrounds': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/addon-controls': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/addon-docs': 8.6.14(@types/react@18.0.28)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/addon-highlight': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/addon-measure': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/addon-outline': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/addon-toolbars': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/addon-viewport': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
ts-dedent: 2.2.0
transitivePeerDependencies:
- '@types/react'
- '@storybook/addon-highlight@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/addon-highlight@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
'@storybook/global': 5.0.0
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
- '@storybook/addon-interactions@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/addon-interactions@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
'@storybook/global': 5.0.0
- '@storybook/instrumenter': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/test': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ '@storybook/instrumenter': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/test': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
polished: 4.2.2
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
ts-dedent: 2.2.0
- '@storybook/addon-links@8.6.14(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/addon-links@9.1.3(react@19.1.1)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
'@storybook/global': 5.0.0
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
- ts-dedent: 2.2.0
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
optionalDependencies:
- react: 19.1.0
+ react: 19.1.1
- '@storybook/addon-mdx-gfm@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/addon-mdx-gfm@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
remark-gfm: 4.0.0
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
ts-dedent: 2.2.0
transitivePeerDependencies:
- supports-color
- '@storybook/addon-measure@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/addon-measure@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
'@storybook/global': 5.0.0
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
tiny-invariant: 1.3.3
- '@storybook/addon-outline@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/addon-outline@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
'@storybook/global': 5.0.0
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
ts-dedent: 2.2.0
- '@storybook/addon-storysource@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/addon-storysource@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
- '@storybook/source-loader': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ '@storybook/source-loader': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
estraverse: 5.3.0
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
tiny-invariant: 1.3.3
- '@storybook/addon-toolbars@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/addon-toolbars@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
- '@storybook/addon-viewport@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/addon-viewport@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
memoizerific: 1.11.3
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
- '@storybook/blocks@8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/blocks@8.6.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
- '@storybook/icons': 1.2.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ '@storybook/icons': 1.2.12(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
ts-dedent: 2.2.0
optionalDependencies:
- react: 19.1.0
- react-dom: 19.1.0(react@19.1.0)
+ react: 19.1.1
+ react-dom: 19.1.1(react@19.1.1)
- '@storybook/builder-vite@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))':
+ '@storybook/builder-vite@9.1.3(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))':
dependencies:
- '@storybook/csf-plugin': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- browser-assert: 1.2.1
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ '@storybook/csf-plugin': 9.1.3(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
ts-dedent: 2.2.0
- vite: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
+ vite: 7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)
- '@storybook/components@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/components@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
- '@storybook/core-events@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/core-events@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
- '@storybook/core@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(utf-8-validate@6.0.5)':
+ '@storybook/csf-plugin@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
- '@storybook/theming': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- better-opn: 3.0.2
- browser-assert: 1.2.1
- esbuild: 0.25.6
- esbuild-register: 3.5.0(esbuild@0.25.6)
- jsdoc-type-pratt-parser: 4.1.0
- process: 0.11.10
- recast: 0.23.6
- semver: 7.7.2
- util: 0.12.5
- ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)
- optionalDependencies:
- prettier: 3.6.2
- transitivePeerDependencies:
- - bufferutil
- - storybook
- - supports-color
- - utf-8-validate
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
+ unplugin: 1.4.0
- '@storybook/csf-plugin@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/csf-plugin@9.1.3(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
unplugin: 1.4.0
'@storybook/global@5.0.0': {}
- '@storybook/icons@1.2.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ '@storybook/icons@1.2.12(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
dependencies:
- react: 19.1.0
- react-dom: 19.1.0(react@19.1.0)
+ react: 19.1.1
+ react-dom: 19.1.1(react@19.1.1)
- '@storybook/instrumenter@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/instrumenter@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
'@storybook/global': 5.0.0
'@vitest/utils': 2.1.1
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
- '@storybook/manager-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/manager-api@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
- '@storybook/preview-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/preview-api@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
- '@storybook/react-dom-shim@8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/react-dom-shim@8.6.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
- react: 19.1.0
- react-dom: 19.1.0(react@19.1.0)
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ react: 19.1.1
+ react-dom: 19.1.1(react@19.1.1)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
- '@storybook/react-vite@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.45.1)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))':
+ '@storybook/react-dom-shim@9.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
- '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))
- '@rollup/pluginutils': 5.2.0(rollup@4.45.1)
- '@storybook/builder-vite': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))
- '@storybook/react': 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(typescript@5.8.3)
- find-up: 5.0.0
- magic-string: 0.30.17
- react: 19.1.0
- react-docgen: 7.0.1
- react-dom: 19.1.0(react@19.1.0)
+ react: 19.1.1
+ react-dom: 19.1.1(react@19.1.1)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
+
+ '@storybook/react-vite@9.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(rollup@4.48.0)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))(typescript@5.9.2)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))':
+ dependencies:
+ '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.1(typescript@5.9.2)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
+ '@rollup/pluginutils': 5.2.0(rollup@4.48.0)
+ '@storybook/builder-vite': 9.1.3(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
+ '@storybook/react': 9.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))(typescript@5.9.2)
+ find-up: 7.0.0
+ magic-string: 0.30.18
+ react: 19.1.1
+ react-docgen: 8.0.0
+ react-dom: 19.1.1(react@19.1.1)
resolve: 1.22.8
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
tsconfig-paths: 4.2.0
- vite: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
- optionalDependencies:
- '@storybook/test': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ vite: 7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)
transitivePeerDependencies:
- rollup
- supports-color
- typescript
- '@storybook/react@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(typescript@5.8.3)':
+ '@storybook/react@9.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))(typescript@5.9.2)':
dependencies:
- '@storybook/components': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
'@storybook/global': 5.0.0
- '@storybook/manager-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/preview-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/react-dom-shim': 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/theming': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- react: 19.1.0
- react-dom: 19.1.0(react@19.1.0)
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ '@storybook/react-dom-shim': 9.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ react: 19.1.1
+ react-dom: 19.1.1(react@19.1.1)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
optionalDependencies:
- '@storybook/test': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- typescript: 5.8.3
+ typescript: 5.9.2
- '@storybook/source-loader@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/source-loader@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
es-toolkit: 1.27.0
estraverse: 5.3.0
prettier: 3.6.2
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
- '@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/test@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
'@storybook/global': 5.0.0
- '@storybook/instrumenter': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ '@storybook/instrumenter': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
'@testing-library/dom': 10.4.0
'@testing-library/jest-dom': 6.5.0
'@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0)
'@vitest/expect': 2.0.5
'@vitest/spy': 2.0.5
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
- '@storybook/theming@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/theming@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
- '@storybook/types@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))':
+ '@storybook/types@8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))':
dependencies:
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
- '@storybook/vue3-vite@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.17(typescript@5.8.3))':
+ '@storybook/vue3-vite@9.1.3(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))(vue@3.5.19(typescript@5.9.2))':
dependencies:
- '@storybook/builder-vite': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))
- '@storybook/vue3': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(vue@3.5.17(typescript@5.8.3))
+ '@storybook/builder-vite': 9.1.3(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
+ '@storybook/vue3': 9.1.3(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))(vue@3.5.19(typescript@5.9.2))
find-package-json: 1.2.0
- magic-string: 0.30.17
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
- typescript: 5.8.3
- vite: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
- vue-component-meta: 2.0.16(typescript@5.8.3)
- vue-docgen-api: 4.75.1(vue@3.5.17(typescript@5.8.3))
+ magic-string: 0.30.18
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
+ typescript: 5.9.2
+ vite: 7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)
+ vue-component-meta: 2.0.16(typescript@5.9.2)
+ vue-docgen-api: 4.75.1(vue@3.5.19(typescript@5.9.2))
transitivePeerDependencies:
- vue
- '@storybook/vue3@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(vue@3.5.17(typescript@5.8.3))':
+ '@storybook/vue3@9.1.3(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))(vue@3.5.19(typescript@5.9.2))':
dependencies:
- '@storybook/components': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
'@storybook/global': 5.0.0
- '@storybook/manager-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/preview-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/theming': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@vue/compiler-core': 3.5.17
- storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)
- ts-dedent: 2.2.0
+ storybook: 9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
type-fest: 2.19.0
- vue: 3.5.17(typescript@5.8.3)
- vue-component-type-helpers: 3.0.1
+ vue: 3.5.19(typescript@5.9.2)
+ vue-component-type-helpers: 3.0.6
- '@stylistic/eslint-plugin@2.13.0(eslint@9.31.0)(typescript@5.8.3)':
+ '@stylistic/eslint-plugin@2.13.0(eslint@9.34.0)(typescript@5.9.2)':
dependencies:
- '@typescript-eslint/utils': 8.37.0(eslint@9.31.0)(typescript@5.8.3)
- eslint: 9.31.0
+ '@typescript-eslint/utils': 8.40.0(eslint@9.34.0)(typescript@5.9.2)
+ eslint: 9.34.0
eslint-visitor-keys: 4.2.1
espree: 10.4.0
estraverse: 5.3.0
- picomatch: 4.0.2
+ picomatch: 4.0.3
transitivePeerDependencies:
- supports-color
- typescript
- '@swc/cli@0.7.7(@swc/core@1.12.0)(chokidar@4.0.3)':
+ '@swc/cli@0.7.8(@swc/core@1.13.4)(chokidar@4.0.3)':
dependencies:
- '@swc/core': 1.12.0
+ '@swc/core': 1.13.4
'@swc/counter': 0.1.3
'@xhmikosr/bin-wrapper': 13.0.5
commander: 8.3.0
- fast-glob: 3.3.3
minimatch: 9.0.5
piscina: 4.4.0
semver: 7.7.2
slash: 3.0.0
source-map: 0.7.4
+ tinyglobby: 0.2.14
optionalDependencies:
chokidar: 4.0.3
@@ -14989,10 +14878,10 @@ snapshots:
'@swc/wasm': 1.2.130
optional: true
- '@swc/core-darwin-arm64@1.12.0':
+ '@swc/core-darwin-arm64@1.13.4':
optional: true
- '@swc/core-darwin-x64@1.12.0':
+ '@swc/core-darwin-x64@1.13.4':
optional: true
'@swc/core-freebsd-x64@1.3.11':
@@ -15000,63 +14889,56 @@ snapshots:
'@swc/wasm': 1.2.130
optional: true
- '@swc/core-linux-arm-gnueabihf@1.12.0':
+ '@swc/core-linux-arm-gnueabihf@1.13.4':
optional: true
- '@swc/core-linux-arm64-gnu@1.12.0':
+ '@swc/core-linux-arm64-gnu@1.13.4':
optional: true
- '@swc/core-linux-arm64-musl@1.12.0':
+ '@swc/core-linux-arm64-musl@1.13.4':
optional: true
- '@swc/core-linux-x64-gnu@1.12.0':
+ '@swc/core-linux-x64-gnu@1.13.4':
optional: true
- '@swc/core-linux-x64-musl@1.12.0':
+ '@swc/core-linux-x64-musl@1.13.4':
optional: true
- '@swc/core-win32-arm64-msvc@1.12.0':
+ '@swc/core-win32-arm64-msvc@1.13.4':
optional: true
- '@swc/core-win32-ia32-msvc@1.12.0':
+ '@swc/core-win32-ia32-msvc@1.13.4':
optional: true
- '@swc/core-win32-x64-msvc@1.12.0':
+ '@swc/core-win32-x64-msvc@1.13.4':
optional: true
- '@swc/core@1.12.0':
+ '@swc/core@1.13.4':
dependencies:
'@swc/counter': 0.1.3
- '@swc/types': 0.1.22
+ '@swc/types': 0.1.24
optionalDependencies:
- '@swc/core-darwin-arm64': 1.12.0
- '@swc/core-darwin-x64': 1.12.0
- '@swc/core-linux-arm-gnueabihf': 1.12.0
- '@swc/core-linux-arm64-gnu': 1.12.0
- '@swc/core-linux-arm64-musl': 1.12.0
- '@swc/core-linux-x64-gnu': 1.12.0
- '@swc/core-linux-x64-musl': 1.12.0
- '@swc/core-win32-arm64-msvc': 1.12.0
- '@swc/core-win32-ia32-msvc': 1.12.0
- '@swc/core-win32-x64-msvc': 1.12.0
+ '@swc/core-darwin-arm64': 1.13.4
+ '@swc/core-darwin-x64': 1.13.4
+ '@swc/core-linux-arm-gnueabihf': 1.13.4
+ '@swc/core-linux-arm64-gnu': 1.13.4
+ '@swc/core-linux-arm64-musl': 1.13.4
+ '@swc/core-linux-x64-gnu': 1.13.4
+ '@swc/core-linux-x64-musl': 1.13.4
+ '@swc/core-win32-arm64-msvc': 1.13.4
+ '@swc/core-win32-ia32-msvc': 1.13.4
+ '@swc/core-win32-x64-msvc': 1.13.4
'@swc/counter@0.1.3': {}
- '@swc/jest@0.2.38(@swc/core@1.12.0)':
+ '@swc/jest@0.2.39(@swc/core@1.13.4)':
dependencies:
- '@jest/create-cache-key-function': 29.7.0
- '@swc/core': 1.12.0
+ '@jest/create-cache-key-function': 30.0.5
+ '@swc/core': 1.13.4
'@swc/counter': 0.1.3
jsonc-parser: 3.2.0
- '@swc/jest@0.2.39(@swc/core@1.12.0)':
- dependencies:
- '@jest/create-cache-key-function': 30.0.2
- '@swc/core': 1.12.0
- '@swc/counter': 0.1.3
- jsonc-parser: 3.2.0
-
- '@swc/types@0.1.22':
+ '@swc/types@0.1.24':
dependencies:
'@swc/counter': 0.1.3
@@ -15069,16 +14951,22 @@ snapshots:
stringz: 2.1.0
uuid: 9.0.1
+ '@syuilo/aiscript@1.1.0':
+ dependencies:
+ seedrandom: 3.0.5
+ stringz: 2.1.0
+ uuid: 11.1.0
+
'@szmarczak/http-timer@5.0.1':
dependencies:
defer-to-connect: 2.0.1
- '@tabler/icons-webfont@3.34.0':
+ '@tabler/icons-webfont@3.34.1':
dependencies:
- '@tabler/icons': 3.34.0
+ '@tabler/icons': 3.34.1
sharp: 0.34.2
- '@tabler/icons@3.34.0': {}
+ '@tabler/icons@3.34.1': {}
'@tensorflow/tfjs-backend-cpu@4.22.0(@tensorflow/tfjs-core@4.22.0(encoding@0.1.13))':
dependencies:
@@ -15159,7 +15047,7 @@ snapshots:
'@testing-library/dom@10.4.0':
dependencies:
- '@babel/code-frame': 7.24.7
+ '@babel/code-frame': 7.27.1
'@babel/runtime': 7.27.0
'@types/aria-query': 5.0.1
aria-query: 5.3.0
@@ -15170,7 +15058,7 @@ snapshots:
'@testing-library/dom@9.3.4':
dependencies:
- '@babel/code-frame': 7.24.7
+ '@babel/code-frame': 7.27.1
'@babel/runtime': 7.27.0
'@types/aria-query': 5.0.1
aria-query: 5.1.3
@@ -15189,24 +15077,38 @@ snapshots:
lodash: 4.17.21
redent: 3.0.0
+ '@testing-library/jest-dom@6.6.4':
+ dependencies:
+ '@adobe/css-tools': 4.4.0
+ aria-query: 5.3.0
+ css.escape: 1.5.1
+ dom-accessibility-api: 0.6.3
+ lodash: 4.17.21
+ picocolors: 1.1.1
+ redent: 3.0.0
+
'@testing-library/user-event@14.5.2(@testing-library/dom@10.4.0)':
dependencies:
'@testing-library/dom': 10.4.0
- '@testing-library/vue@8.1.0(@vue/compiler-sfc@3.5.17)(@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))':
+ '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)':
+ dependencies:
+ '@testing-library/dom': 10.4.0
+
+ '@testing-library/vue@8.1.0(@vue/compiler-sfc@3.5.19)(@vue/server-renderer@3.5.19(vue@3.5.19(typescript@5.9.2)))(vue@3.5.19(typescript@5.9.2))':
dependencies:
'@babel/runtime': 7.27.0
'@testing-library/dom': 9.3.4
- '@vue/test-utils': 2.4.1(@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))
- vue: 3.5.17(typescript@5.8.3)
+ '@vue/test-utils': 2.4.1(@vue/server-renderer@3.5.19(vue@3.5.19(typescript@5.9.2)))(vue@3.5.19(typescript@5.9.2))
+ vue: 3.5.19(typescript@5.9.2)
optionalDependencies:
- '@vue/compiler-sfc': 3.5.17
+ '@vue/compiler-sfc': 3.5.19
transitivePeerDependencies:
- '@vue/server-renderer'
'@tokenizer/inflate@0.2.7':
dependencies:
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@10.2.0)
fflate: 0.8.2
token-types: 6.0.0
transitivePeerDependencies:
@@ -15214,17 +15116,13 @@ snapshots:
'@tokenizer/token@0.3.0': {}
- '@tsd/typescript@5.8.3': {}
-
- '@twemoji/parser@15.0.0': {}
-
- '@twemoji/parser@15.1.0': {}
+ '@tsd/typescript@5.9.2': {}
- '@twemoji/parser@15.1.1': {}
+ '@twemoji/parser@16.0.0': {}
'@types/accepts@1.3.7':
dependencies:
- '@types/node': 22.15.31
+ '@types/node': 22.17.2
'@types/archiver@6.0.3':
dependencies:
@@ -15236,31 +15134,31 @@ snapshots:
'@types/babel__core@7.20.0':
dependencies:
- '@babel/parser': 7.27.2
- '@babel/types': 7.27.1
+ '@babel/parser': 7.28.0
+ '@babel/types': 7.28.1
'@types/babel__generator': 7.6.4
'@types/babel__template': 7.4.1
'@types/babel__traverse': 7.20.0
'@types/babel__generator@7.6.4':
dependencies:
- '@babel/types': 7.27.1
+ '@babel/types': 7.28.1
'@types/babel__template@7.4.1':
dependencies:
- '@babel/parser': 7.27.2
- '@babel/types': 7.27.1
+ '@babel/parser': 7.28.0
+ '@babel/types': 7.28.1
'@types/babel__traverse@7.20.0':
dependencies:
- '@babel/types': 7.27.1
+ '@babel/types': 7.28.1
'@types/bcryptjs@2.4.6': {}
'@types/body-parser@1.19.6':
dependencies:
'@types/connect': 3.4.36
- '@types/node': 22.15.31
+ '@types/node': 22.17.2
'@types/braces@3.0.1': {}
@@ -15278,7 +15176,7 @@ snapshots:
'@types/connect@3.4.36':
dependencies:
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
'@types/content-disposition@0.5.9': {}
@@ -15307,7 +15205,7 @@ snapshots:
'@types/express-serve-static-core@4.17.33':
dependencies:
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
'@types/qs': 6.9.7
'@types/range-parser': 1.2.4
@@ -15320,11 +15218,11 @@ snapshots:
'@types/fluent-ffmpeg@2.1.27':
dependencies:
- '@types/node': 22.15.31
+ '@types/node': 22.17.2
'@types/graceful-fs@4.1.6':
dependencies:
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
'@types/hammerjs@2.0.46': {}
@@ -15338,19 +15236,13 @@ snapshots:
'@types/http-link-header@1.0.7':
dependencies:
- '@types/node': 22.15.31
-
- '@types/istanbul-lib-coverage@2.0.4': {}
+ '@types/node': 22.17.2
'@types/istanbul-lib-coverage@2.0.6': {}
'@types/istanbul-lib-report@3.0.0':
dependencies:
- '@types/istanbul-lib-coverage': 2.0.4
-
- '@types/istanbul-reports@3.0.1':
- dependencies:
- '@types/istanbul-lib-report': 3.0.0
+ '@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports@3.0.4':
dependencies:
@@ -15365,7 +15257,7 @@ snapshots:
'@types/jsdom@21.1.7':
dependencies:
- '@types/node': 22.15.31
+ '@types/node': 22.17.2
'@types/tough-cookie': 4.0.5
parse5: 7.3.0
@@ -15379,7 +15271,7 @@ snapshots:
'@types/long@4.0.2': {}
- '@types/matter-js@0.19.8': {}
+ '@types/matter-js@0.20.0': {}
'@types/mdast@4.0.3':
dependencies:
@@ -15403,24 +15295,31 @@ snapshots:
'@types/mysql@2.15.26':
dependencies:
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
'@types/node-fetch@2.6.11':
dependencies:
- '@types/node': 22.16.4
- form-data: 4.0.3
+ '@types/node': 22.17.2
+ form-data: 4.0.4
- '@types/node@22.15.31':
+ '@types/node@20.19.9':
dependencies:
undici-types: 6.21.0
- '@types/node@22.16.4':
+ '@types/node@22.17.0':
dependencies:
undici-types: 6.21.0
- '@types/nodemailer@6.4.17':
+ '@types/node@22.17.2':
dependencies:
- '@types/node': 22.15.31
+ undici-types: 6.21.0
+
+ '@types/nodemailer@6.4.19':
+ dependencies:
+ '@aws-sdk/client-ses': 3.873.0
+ '@types/node': 22.17.2
+ transitivePeerDependencies:
+ - aws-crt
'@types/normalize-package-data@2.4.1': {}
@@ -15431,11 +15330,11 @@ snapshots:
'@types/oauth2orize@1.11.5':
dependencies:
'@types/express': 4.17.17
- '@types/node': 22.15.31
+ '@types/node': 22.17.2
'@types/oauth@0.9.6':
dependencies:
- '@types/node': 22.15.31
+ '@types/node': 22.17.2
'@types/offscreencanvas@2019.3.0': {}
@@ -15443,17 +15342,17 @@ snapshots:
'@types/pg-pool@2.0.6':
dependencies:
- '@types/pg': 8.15.4
+ '@types/pg': 8.15.5
- '@types/pg@8.15.4':
+ '@types/pg@8.15.5':
dependencies:
- '@types/node': 22.15.31
+ '@types/node': 22.17.2
pg-protocol: 1.10.0
pg-types: 2.2.0
'@types/pg@8.6.1':
dependencies:
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
pg-protocol: 1.10.0
pg-types: 2.2.0
@@ -15465,7 +15364,7 @@ snapshots:
'@types/qrcode@1.5.5':
dependencies:
- '@types/node': 22.15.31
+ '@types/node': 22.17.2
'@types/qs@6.9.7': {}
@@ -15483,7 +15382,7 @@ snapshots:
'@types/readdir-glob@1.1.1':
dependencies:
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
'@types/redis-info@3.0.3': {}
@@ -15506,7 +15405,7 @@ snapshots:
'@types/serve-static@1.15.1':
dependencies:
'@types/mime': 3.0.1
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
'@types/serviceworker@0.0.74': {}
@@ -15532,8 +15431,8 @@ snapshots:
dependencies:
'@types/cookiejar': 2.1.5
'@types/methods': 1.1.4
- '@types/node': 22.16.4
- form-data: 4.0.3
+ '@types/node': 22.17.2
+ form-data: 4.0.4
'@types/supertest@6.0.3':
dependencies:
@@ -15542,7 +15441,7 @@ snapshots:
'@types/tedious@4.0.14':
dependencies:
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
'@types/throttle-debounce@5.0.2': {}
@@ -15558,243 +15457,262 @@ snapshots:
'@types/vary@1.1.3':
dependencies:
- '@types/node': 22.15.31
+ '@types/node': 22.17.2
'@types/wawoff2@1.0.2':
dependencies:
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
'@types/web-push@3.6.4':
dependencies:
- '@types/node': 22.15.31
+ '@types/node': 22.17.2
+
+ '@types/whatwg-mimetype@3.0.2': {}
'@types/ws@8.18.1':
dependencies:
- '@types/node': 22.15.31
+ '@types/node': 22.17.2
'@types/yargs-parser@21.0.0': {}
- '@types/yargs@17.0.19':
- dependencies:
- '@types/yargs-parser': 21.0.0
-
'@types/yargs@17.0.33':
dependencies:
'@types/yargs-parser': 21.0.0
'@types/yauzl@2.10.0':
dependencies:
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
optional: true
- '@typescript-eslint/eslint-plugin@8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)':
+ '@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)':
dependencies:
'@eslint-community/regexpp': 4.12.1
- '@typescript-eslint/parser': 8.34.0(eslint@9.31.0)(typescript@5.8.3)
- '@typescript-eslint/scope-manager': 8.34.0
- '@typescript-eslint/type-utils': 8.34.0(eslint@9.31.0)(typescript@5.8.3)
- '@typescript-eslint/utils': 8.34.0(eslint@9.31.0)(typescript@5.8.3)
- '@typescript-eslint/visitor-keys': 8.34.0
- eslint: 9.31.0
+ '@typescript-eslint/parser': 8.38.0(eslint@9.34.0)(typescript@5.9.2)
+ '@typescript-eslint/scope-manager': 8.38.0
+ '@typescript-eslint/type-utils': 8.38.0(eslint@9.34.0)(typescript@5.9.2)
+ '@typescript-eslint/utils': 8.38.0(eslint@9.34.0)(typescript@5.9.2)
+ '@typescript-eslint/visitor-keys': 8.38.0
+ eslint: 9.34.0
graphemer: 1.4.0
ignore: 7.0.4
natural-compare: 1.4.0
- ts-api-utils: 2.1.0(typescript@5.8.3)
- typescript: 5.8.3
+ ts-api-utils: 2.1.0(typescript@5.9.2)
+ typescript: 5.9.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)':
+ '@typescript-eslint/eslint-plugin@8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)':
dependencies:
'@eslint-community/regexpp': 4.12.1
- '@typescript-eslint/parser': 8.37.0(eslint@9.31.0)(typescript@5.8.3)
- '@typescript-eslint/scope-manager': 8.37.0
- '@typescript-eslint/type-utils': 8.37.0(eslint@9.31.0)(typescript@5.8.3)
- '@typescript-eslint/utils': 8.37.0(eslint@9.31.0)(typescript@5.8.3)
- '@typescript-eslint/visitor-keys': 8.37.0
- eslint: 9.31.0
+ '@typescript-eslint/parser': 8.40.0(eslint@9.34.0)(typescript@5.9.2)
+ '@typescript-eslint/scope-manager': 8.40.0
+ '@typescript-eslint/type-utils': 8.40.0(eslint@9.34.0)(typescript@5.9.2)
+ '@typescript-eslint/utils': 8.40.0(eslint@9.34.0)(typescript@5.9.2)
+ '@typescript-eslint/visitor-keys': 8.40.0
+ eslint: 9.34.0
graphemer: 1.4.0
ignore: 7.0.4
natural-compare: 1.4.0
- ts-api-utils: 2.1.0(typescript@5.8.3)
- typescript: 5.8.3
+ ts-api-utils: 2.1.0(typescript@5.9.2)
+ typescript: 5.9.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3)':
+ '@typescript-eslint/parser@8.38.0(eslint@9.34.0)(typescript@5.9.2)':
dependencies:
- '@typescript-eslint/scope-manager': 8.34.0
- '@typescript-eslint/types': 8.34.0
- '@typescript-eslint/typescript-estree': 8.34.0(typescript@5.8.3)
- '@typescript-eslint/visitor-keys': 8.34.0
- debug: 4.4.1(supports-color@10.0.0)
- eslint: 9.31.0
- typescript: 5.8.3
+ '@typescript-eslint/scope-manager': 8.38.0
+ '@typescript-eslint/types': 8.38.0
+ '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2)
+ '@typescript-eslint/visitor-keys': 8.38.0
+ debug: 4.4.1(supports-color@10.2.0)
+ eslint: 9.34.0
+ typescript: 5.9.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3)':
+ '@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2)':
dependencies:
- '@typescript-eslint/scope-manager': 8.37.0
- '@typescript-eslint/types': 8.37.0
- '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3)
- '@typescript-eslint/visitor-keys': 8.37.0
- debug: 4.4.1(supports-color@10.0.0)
- eslint: 9.31.0
- typescript: 5.8.3
+ '@typescript-eslint/scope-manager': 8.40.0
+ '@typescript-eslint/types': 8.40.0
+ '@typescript-eslint/typescript-estree': 8.40.0(typescript@5.9.2)
+ '@typescript-eslint/visitor-keys': 8.40.0
+ debug: 4.4.1(supports-color@5.5.0)
+ eslint: 9.34.0
+ typescript: 5.9.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/project-service@8.34.0(typescript@5.8.3)':
+ '@typescript-eslint/project-service@8.38.0(typescript@5.9.2)':
dependencies:
- '@typescript-eslint/tsconfig-utils': 8.34.0(typescript@5.8.3)
- '@typescript-eslint/types': 8.37.0
- debug: 4.4.1(supports-color@10.0.0)
- typescript: 5.8.3
+ '@typescript-eslint/tsconfig-utils': 8.40.0(typescript@5.9.2)
+ '@typescript-eslint/types': 8.40.0
+ debug: 4.4.1(supports-color@10.2.0)
+ typescript: 5.9.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/project-service@8.37.0(typescript@5.8.3)':
+ '@typescript-eslint/project-service@8.40.0(typescript@5.9.2)':
dependencies:
- '@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.8.3)
- '@typescript-eslint/types': 8.37.0
- debug: 4.4.1(supports-color@10.0.0)
- typescript: 5.8.3
+ '@typescript-eslint/tsconfig-utils': 8.40.0(typescript@5.9.2)
+ '@typescript-eslint/types': 8.40.0
+ debug: 4.4.1(supports-color@5.5.0)
+ typescript: 5.9.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/scope-manager@8.34.0':
+ '@typescript-eslint/scope-manager@8.38.0':
dependencies:
- '@typescript-eslint/types': 8.34.0
- '@typescript-eslint/visitor-keys': 8.34.0
+ '@typescript-eslint/types': 8.38.0
+ '@typescript-eslint/visitor-keys': 8.38.0
- '@typescript-eslint/scope-manager@8.37.0':
+ '@typescript-eslint/scope-manager@8.40.0':
dependencies:
- '@typescript-eslint/types': 8.37.0
- '@typescript-eslint/visitor-keys': 8.37.0
+ '@typescript-eslint/types': 8.40.0
+ '@typescript-eslint/visitor-keys': 8.40.0
- '@typescript-eslint/tsconfig-utils@8.34.0(typescript@5.8.3)':
+ '@typescript-eslint/tsconfig-utils@8.38.0(typescript@5.9.2)':
dependencies:
- typescript: 5.8.3
+ typescript: 5.9.2
- '@typescript-eslint/tsconfig-utils@8.37.0(typescript@5.8.3)':
+ '@typescript-eslint/tsconfig-utils@8.40.0(typescript@5.9.2)':
dependencies:
- typescript: 5.8.3
+ typescript: 5.9.2
- '@typescript-eslint/type-utils@8.34.0(eslint@9.31.0)(typescript@5.8.3)':
+ '@typescript-eslint/type-utils@8.38.0(eslint@9.34.0)(typescript@5.9.2)':
dependencies:
- '@typescript-eslint/typescript-estree': 8.34.0(typescript@5.8.3)
- '@typescript-eslint/utils': 8.34.0(eslint@9.31.0)(typescript@5.8.3)
- debug: 4.4.1(supports-color@10.0.0)
- eslint: 9.31.0
- ts-api-utils: 2.1.0(typescript@5.8.3)
- typescript: 5.8.3
+ '@typescript-eslint/types': 8.38.0
+ '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2)
+ '@typescript-eslint/utils': 8.38.0(eslint@9.34.0)(typescript@5.9.2)
+ debug: 4.4.1(supports-color@10.2.0)
+ eslint: 9.34.0
+ ts-api-utils: 2.1.0(typescript@5.9.2)
+ typescript: 5.9.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/type-utils@8.37.0(eslint@9.31.0)(typescript@5.8.3)':
+ '@typescript-eslint/type-utils@8.40.0(eslint@9.34.0)(typescript@5.9.2)':
dependencies:
- '@typescript-eslint/types': 8.37.0
- '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3)
- '@typescript-eslint/utils': 8.37.0(eslint@9.31.0)(typescript@5.8.3)
- debug: 4.4.1(supports-color@10.0.0)
- eslint: 9.31.0
- ts-api-utils: 2.1.0(typescript@5.8.3)
- typescript: 5.8.3
+ '@typescript-eslint/types': 8.40.0
+ '@typescript-eslint/typescript-estree': 8.40.0(typescript@5.9.2)
+ '@typescript-eslint/utils': 8.40.0(eslint@9.34.0)(typescript@5.9.2)
+ debug: 4.4.1(supports-color@5.5.0)
+ eslint: 9.34.0
+ ts-api-utils: 2.1.0(typescript@5.9.2)
+ typescript: 5.9.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/types@8.34.0': {}
+ '@typescript-eslint/types@8.38.0': {}
- '@typescript-eslint/types@8.37.0': {}
+ '@typescript-eslint/types@8.40.0': {}
- '@typescript-eslint/typescript-estree@8.34.0(typescript@5.8.3)':
+ '@typescript-eslint/typescript-estree@8.38.0(typescript@5.9.2)':
dependencies:
- '@typescript-eslint/project-service': 8.34.0(typescript@5.8.3)
- '@typescript-eslint/tsconfig-utils': 8.34.0(typescript@5.8.3)
- '@typescript-eslint/types': 8.34.0
- '@typescript-eslint/visitor-keys': 8.34.0
- debug: 4.4.1(supports-color@10.0.0)
+ '@typescript-eslint/project-service': 8.38.0(typescript@5.9.2)
+ '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.9.2)
+ '@typescript-eslint/types': 8.38.0
+ '@typescript-eslint/visitor-keys': 8.38.0
+ debug: 4.4.1(supports-color@10.2.0)
fast-glob: 3.3.3
is-glob: 4.0.3
minimatch: 9.0.5
semver: 7.7.2
- ts-api-utils: 2.1.0(typescript@5.8.3)
- typescript: 5.8.3
+ ts-api-utils: 2.1.0(typescript@5.9.2)
+ typescript: 5.9.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/typescript-estree@8.37.0(typescript@5.8.3)':
+ '@typescript-eslint/typescript-estree@8.40.0(typescript@5.9.2)':
dependencies:
- '@typescript-eslint/project-service': 8.37.0(typescript@5.8.3)
- '@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.8.3)
- '@typescript-eslint/types': 8.37.0
- '@typescript-eslint/visitor-keys': 8.37.0
- debug: 4.4.1(supports-color@10.0.0)
+ '@typescript-eslint/project-service': 8.40.0(typescript@5.9.2)
+ '@typescript-eslint/tsconfig-utils': 8.40.0(typescript@5.9.2)
+ '@typescript-eslint/types': 8.40.0
+ '@typescript-eslint/visitor-keys': 8.40.0
+ debug: 4.4.1(supports-color@5.5.0)
fast-glob: 3.3.3
is-glob: 4.0.3
minimatch: 9.0.5
semver: 7.7.2
- ts-api-utils: 2.1.0(typescript@5.8.3)
- typescript: 5.8.3
+ ts-api-utils: 2.1.0(typescript@5.9.2)
+ typescript: 5.9.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.34.0(eslint@9.31.0)(typescript@5.8.3)':
+ '@typescript-eslint/utils@8.38.0(eslint@9.34.0)(typescript@5.9.2)':
dependencies:
- '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0)
- '@typescript-eslint/scope-manager': 8.34.0
- '@typescript-eslint/types': 8.34.0
- '@typescript-eslint/typescript-estree': 8.34.0(typescript@5.8.3)
- eslint: 9.31.0
- typescript: 5.8.3
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0)
+ '@typescript-eslint/scope-manager': 8.38.0
+ '@typescript-eslint/types': 8.38.0
+ '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2)
+ eslint: 9.34.0
+ typescript: 5.9.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.37.0(eslint@9.31.0)(typescript@5.8.3)':
+ '@typescript-eslint/utils@8.40.0(eslint@9.34.0)(typescript@5.9.2)':
dependencies:
- '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0)
- '@typescript-eslint/scope-manager': 8.37.0
- '@typescript-eslint/types': 8.37.0
- '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3)
- eslint: 9.31.0
- typescript: 5.8.3
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0)
+ '@typescript-eslint/scope-manager': 8.40.0
+ '@typescript-eslint/types': 8.40.0
+ '@typescript-eslint/typescript-estree': 8.40.0(typescript@5.9.2)
+ eslint: 9.34.0
+ typescript: 5.9.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/visitor-keys@8.34.0':
+ '@typescript-eslint/visitor-keys@8.38.0':
dependencies:
- '@typescript-eslint/types': 8.34.0
- eslint-visitor-keys: 4.2.0
+ '@typescript-eslint/types': 8.38.0
+ eslint-visitor-keys: 4.2.1
- '@typescript-eslint/visitor-keys@8.37.0':
+ '@typescript-eslint/visitor-keys@8.40.0':
dependencies:
- '@typescript-eslint/types': 8.37.0
+ '@typescript-eslint/types': 8.40.0
eslint-visitor-keys: 4.2.1
'@ungap/structured-clone@1.2.0': {}
- '@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.17(typescript@5.8.3))':
+ '@vitejs/plugin-vue@6.0.1(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))(vue@3.5.19(typescript@5.9.2))':
dependencies:
- vite: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
- vue: 3.5.17(typescript@5.8.3)
+ '@rolldown/pluginutils': 1.0.0-beta.29
+ vite: 7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)
+ vue: 3.5.19(typescript@5.9.2)
- '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))':
+ '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))':
dependencies:
'@ampproject/remapping': 2.3.0
'@bcoe/v8-coverage': 1.0.2
ast-v8-to-istanbul: 0.3.3
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@5.5.0)
istanbul-lib-coverage: 3.2.2
istanbul-lib-report: 3.0.1
istanbul-lib-source-maps: 5.0.6
istanbul-reports: 3.1.7
- magic-string: 0.30.17
+ magic-string: 0.30.18
magicast: 0.3.5
std-env: 3.9.0
test-exclude: 7.0.1
tinyrainbow: 2.0.0
- vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
+ vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5))':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@bcoe/v8-coverage': 1.0.2
+ ast-v8-to-istanbul: 0.3.3
+ debug: 4.4.1(supports-color@10.2.0)
+ istanbul-lib-coverage: 3.2.2
+ istanbul-lib-report: 3.0.1
+ istanbul-lib-source-maps: 5.0.6
+ istanbul-reports: 3.1.7
+ magic-string: 0.30.18
+ magicast: 0.3.5
+ std-env: 3.9.0
+ test-exclude: 7.0.1
+ tinyrainbow: 2.0.0
+ vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5)
transitivePeerDependencies:
- supports-color
@@ -15813,14 +15731,23 @@ snapshots:
chai: 5.2.0
tinyrainbow: 2.0.0
- '@vitest/mocker@3.2.4(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))':
+ '@vitest/mocker@3.2.4(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))':
+ dependencies:
+ '@vitest/spy': 3.2.4
+ estree-walker: 3.0.3
+ magic-string: 0.30.18
+ optionalDependencies:
+ msw: 2.10.5(@types/node@22.17.2)(typescript@5.9.2)
+ vite: 7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)
+
+ '@vitest/mocker@3.2.4(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5))':
dependencies:
'@vitest/spy': 3.2.4
estree-walker: 3.0.3
- magic-string: 0.30.17
+ magic-string: 0.30.18
optionalDependencies:
- msw: 2.10.4(@types/node@22.16.4)(typescript@5.8.3)
- vite: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
+ msw: 2.10.5(@types/node@22.17.2)(typescript@5.9.2)
+ vite: 7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5)
'@vitest/pretty-format@2.0.5':
dependencies:
@@ -15843,7 +15770,7 @@ snapshots:
'@vitest/snapshot@3.2.4':
dependencies:
'@vitest/pretty-format': 3.2.4
- magic-string: 0.30.17
+ magic-string: 0.30.18
pathe: 2.0.3
'@vitest/spy@2.0.5':
@@ -15858,13 +15785,13 @@ snapshots:
dependencies:
'@vitest/pretty-format': 2.0.5
estree-walker: 3.0.3
- loupe: 3.1.3
+ loupe: 3.1.4
tinyrainbow: 1.2.0
'@vitest/utils@2.1.1':
dependencies:
'@vitest/pretty-format': 2.1.1
- loupe: 3.1.3
+ loupe: 3.1.4
tinyrainbow: 1.2.0
'@vitest/utils@3.2.4':
@@ -15877,148 +15804,133 @@ snapshots:
dependencies:
'@volar/source-map': 2.2.0
- '@volar/language-core@2.4.15':
+ '@volar/language-core@2.4.23':
dependencies:
- '@volar/source-map': 2.4.15
+ '@volar/source-map': 2.4.23
'@volar/source-map@2.2.0':
dependencies:
muggle-string: 0.4.1
- '@volar/source-map@2.4.15': {}
+ '@volar/source-map@2.4.23': {}
'@volar/typescript@2.2.0':
dependencies:
'@volar/language-core': 2.2.0
path-browserify: 1.0.1
- '@volar/typescript@2.4.15':
+ '@volar/typescript@2.4.23':
dependencies:
- '@volar/language-core': 2.4.15
+ '@volar/language-core': 2.4.23
path-browserify: 1.0.1
vscode-uri: 3.0.8
- '@vue/compiler-core@3.5.13':
- dependencies:
- '@babel/parser': 7.27.2
- '@vue/shared': 3.5.13
- entities: 4.5.0
- estree-walker: 2.0.2
- source-map-js: 1.2.1
-
- '@vue/compiler-core@3.5.16':
+ '@vue/compiler-core@3.5.18':
dependencies:
- '@babel/parser': 7.27.2
- '@vue/shared': 3.5.16
+ '@babel/parser': 7.28.0
+ '@vue/shared': 3.5.18
entities: 4.5.0
estree-walker: 2.0.2
source-map-js: 1.2.1
- '@vue/compiler-core@3.5.17':
+ '@vue/compiler-core@3.5.19':
dependencies:
- '@babel/parser': 7.28.0
- '@vue/shared': 3.5.17
+ '@babel/parser': 7.28.3
+ '@vue/shared': 3.5.19
entities: 4.5.0
estree-walker: 2.0.2
source-map-js: 1.2.1
- '@vue/compiler-dom@3.5.13':
- dependencies:
- '@vue/compiler-core': 3.5.13
- '@vue/shared': 3.5.13
-
- '@vue/compiler-dom@3.5.16':
+ '@vue/compiler-dom@3.5.18':
dependencies:
- '@vue/compiler-core': 3.5.16
- '@vue/shared': 3.5.16
+ '@vue/compiler-core': 3.5.18
+ '@vue/shared': 3.5.18
- '@vue/compiler-dom@3.5.17':
+ '@vue/compiler-dom@3.5.19':
dependencies:
- '@vue/compiler-core': 3.5.17
- '@vue/shared': 3.5.17
+ '@vue/compiler-core': 3.5.19
+ '@vue/shared': 3.5.19
- '@vue/compiler-sfc@3.5.17':
+ '@vue/compiler-sfc@3.5.19':
dependencies:
- '@babel/parser': 7.28.0
- '@vue/compiler-core': 3.5.17
- '@vue/compiler-dom': 3.5.17
- '@vue/compiler-ssr': 3.5.17
- '@vue/shared': 3.5.17
+ '@babel/parser': 7.28.3
+ '@vue/compiler-core': 3.5.19
+ '@vue/compiler-dom': 3.5.19
+ '@vue/compiler-ssr': 3.5.19
+ '@vue/shared': 3.5.19
estree-walker: 2.0.2
- magic-string: 0.30.17
+ magic-string: 0.30.18
postcss: 8.5.6
source-map-js: 1.2.1
- '@vue/compiler-ssr@3.5.17':
+ '@vue/compiler-ssr@3.5.19':
dependencies:
- '@vue/compiler-dom': 3.5.17
- '@vue/shared': 3.5.17
+ '@vue/compiler-dom': 3.5.19
+ '@vue/shared': 3.5.19
'@vue/compiler-vue2@2.7.16':
dependencies:
de-indent: 1.0.2
he: 1.2.0
- '@vue/language-core@2.0.16(typescript@5.8.3)':
+ '@vue/language-core@2.0.16(typescript@5.9.2)':
dependencies:
'@volar/language-core': 2.2.0
- '@vue/compiler-dom': 3.5.16
- '@vue/shared': 3.5.16
+ '@vue/compiler-dom': 3.5.18
+ '@vue/shared': 3.5.18
computeds: 0.0.1
minimatch: 9.0.5
path-browserify: 1.0.1
vue-template-compiler: 2.7.14
optionalDependencies:
- typescript: 5.8.3
+ typescript: 5.9.2
- '@vue/language-core@2.2.12(typescript@5.8.3)':
+ '@vue/language-core@3.0.6(typescript@5.9.2)':
dependencies:
- '@volar/language-core': 2.4.15
- '@vue/compiler-dom': 3.5.16
+ '@volar/language-core': 2.4.23
+ '@vue/compiler-dom': 3.5.18
'@vue/compiler-vue2': 2.7.16
- '@vue/shared': 3.5.16
- alien-signals: 1.0.3
- minimatch: 9.0.5
+ '@vue/shared': 3.5.18
+ alien-signals: 2.0.6
muggle-string: 0.4.1
path-browserify: 1.0.1
+ picomatch: 4.0.3
optionalDependencies:
- typescript: 5.8.3
+ typescript: 5.9.2
- '@vue/reactivity@3.5.17':
+ '@vue/reactivity@3.5.19':
dependencies:
- '@vue/shared': 3.5.17
+ '@vue/shared': 3.5.19
- '@vue/runtime-core@3.5.17':
+ '@vue/runtime-core@3.5.19':
dependencies:
- '@vue/reactivity': 3.5.17
- '@vue/shared': 3.5.17
+ '@vue/reactivity': 3.5.19
+ '@vue/shared': 3.5.19
- '@vue/runtime-dom@3.5.17':
+ '@vue/runtime-dom@3.5.19':
dependencies:
- '@vue/reactivity': 3.5.17
- '@vue/runtime-core': 3.5.17
- '@vue/shared': 3.5.17
+ '@vue/reactivity': 3.5.19
+ '@vue/runtime-core': 3.5.19
+ '@vue/shared': 3.5.19
csstype: 3.1.3
- '@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.8.3))':
+ '@vue/server-renderer@3.5.19(vue@3.5.19(typescript@5.9.2))':
dependencies:
- '@vue/compiler-ssr': 3.5.17
- '@vue/shared': 3.5.17
- vue: 3.5.17(typescript@5.8.3)
+ '@vue/compiler-ssr': 3.5.19
+ '@vue/shared': 3.5.19
+ vue: 3.5.19(typescript@5.9.2)
- '@vue/shared@3.5.13': {}
+ '@vue/shared@3.5.18': {}
- '@vue/shared@3.5.16': {}
+ '@vue/shared@3.5.19': {}
- '@vue/shared@3.5.17': {}
-
- '@vue/test-utils@2.4.1(@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))':
+ '@vue/test-utils@2.4.1(@vue/server-renderer@3.5.19(vue@3.5.19(typescript@5.9.2)))(vue@3.5.19(typescript@5.9.2))':
dependencies:
js-beautify: 1.14.9
- vue: 3.5.17(typescript@5.8.3)
+ vue: 3.5.19(typescript@5.9.2)
vue-component-type-helpers: 1.8.4
optionalDependencies:
- '@vue/server-renderer': 3.5.17(vue@3.5.17(typescript@5.8.3))
+ '@vue/server-renderer': 3.5.19(vue@3.5.19(typescript@5.9.2))
'@webgpu/types@0.1.38': {}
@@ -16127,7 +16039,7 @@ snapshots:
agent-base@6.0.2:
dependencies:
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@10.2.0)
transitivePeerDependencies:
- supports-color
optional: true
@@ -16192,18 +16104,18 @@ snapshots:
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
- alien-signals@1.0.3: {}
+ alien-signals@2.0.6: {}
- analytics-utils@1.0.14(@types/dlv@1.1.5):
+ analytics-utils@1.1.1(@types/dlv@1.1.5):
dependencies:
- '@analytics/type-utils': 0.6.2
+ '@analytics/type-utils': 0.6.4
'@types/dlv': 1.1.5
dlv: 1.1.3
- analytics@0.8.16(@types/dlv@1.1.5):
+ analytics@0.8.19(@types/dlv@1.1.5):
dependencies:
- '@analytics/core': 0.12.17(@types/dlv@1.1.5)
- '@analytics/storage-utils': 0.4.2
+ '@analytics/core': 0.13.2(@types/dlv@1.1.5)
+ '@analytics/storage-utils': 0.4.4
transitivePeerDependencies:
- '@types/dlv'
@@ -16217,10 +16129,6 @@ snapshots:
ansi-regex@6.0.1: {}
- ansi-styles@3.2.1:
- dependencies:
- color-convert: 1.9.3
-
ansi-styles@4.3.0:
dependencies:
color-convert: 2.0.1
@@ -16291,11 +16199,6 @@ snapshots:
dependencies:
dequal: 2.0.3
- array-buffer-byte-length@1.0.1:
- dependencies:
- call-bind: 1.0.7
- is-array-buffer: 3.0.4
-
array-buffer-byte-length@1.0.2:
dependencies:
call-bound: 1.0.4
@@ -16303,15 +16206,6 @@ snapshots:
array-flatten@1.1.1: {}
- array-includes@3.1.8:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-abstract: 1.23.3
- es-object-atoms: 1.1.1
- get-intrinsic: 1.3.0
- is-string: 1.0.7
-
array-includes@3.1.9:
dependencies:
call-bind: 1.0.8
@@ -16325,15 +16219,6 @@ snapshots:
array-union@2.1.0: {}
- array.prototype.findlastindex@1.2.5:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-abstract: 1.23.3
- es-errors: 1.3.0
- es-object-atoms: 1.1.1
- es-shim-unscopables: 1.0.2
-
array.prototype.findlastindex@1.2.6:
dependencies:
call-bind: 1.0.8
@@ -16344,44 +16229,19 @@ snapshots:
es-object-atoms: 1.1.1
es-shim-unscopables: 1.1.0
- array.prototype.flat@1.3.2:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-abstract: 1.23.3
- es-shim-unscopables: 1.0.2
-
array.prototype.flat@1.3.3:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.24.0
- es-shim-unscopables: 1.0.2
-
- array.prototype.flatmap@1.3.2:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-abstract: 1.23.3
- es-shim-unscopables: 1.0.2
+ es-shim-unscopables: 1.1.0
array.prototype.flatmap@1.3.3:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
es-abstract: 1.24.0
- es-shim-unscopables: 1.0.2
-
- arraybuffer.prototype.slice@1.0.3:
- dependencies:
- array-buffer-byte-length: 1.0.1
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-abstract: 1.23.3
- es-errors: 1.3.0
- get-intrinsic: 1.3.0
- is-array-buffer: 3.0.4
- is-shared-array-buffer: 1.0.3
+ es-shim-unscopables: 1.1.0
arraybuffer.prototype.slice@1.0.4:
dependencies:
@@ -16426,7 +16286,7 @@ snapshots:
ast-v8-to-istanbul@0.3.3:
dependencies:
- '@jridgewell/trace-mapping': 0.3.25
+ '@jridgewell/trace-mapping': 0.3.29
estree-walker: 3.0.3
js-tokens: 9.0.1
@@ -16475,10 +16335,10 @@ snapshots:
transitivePeerDependencies:
- debug
- axios@1.8.4(debug@4.4.1):
+ axios@1.11.0(debug@4.4.1):
dependencies:
follow-redirects: 1.15.9(debug@4.4.1)
- form-data: 4.0.3
+ form-data: 4.0.4
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
@@ -16511,7 +16371,7 @@ snapshots:
babel-plugin-jest-hoist@29.6.3:
dependencies:
'@babel/template': 7.24.7
- '@babel/types': 7.27.1
+ '@babel/types': 7.28.1
'@types/babel__core': 7.20.0
'@types/babel__traverse': 7.20.0
@@ -16539,7 +16399,7 @@ snapshots:
babel-walk@3.0.0-canary-5:
dependencies:
- '@babel/types': 7.27.1
+ '@babel/types': 7.28.1
bail@2.0.2: {}
@@ -16624,15 +16484,6 @@ snapshots:
p-queue: 6.6.2
unload: 2.4.1
- browser-assert@1.2.1: {}
-
- browserslist@4.24.5:
- dependencies:
- caniuse-lite: 1.0.30001718
- electron-to-chromium: 1.5.155
- node-releases: 2.0.19
- update-browserslist-db: 1.1.3(browserslist@4.24.5)
-
browserslist@4.25.1:
dependencies:
caniuse-lite: 1.0.30001727
@@ -16672,10 +16523,10 @@ snapshots:
node-gyp-build: 4.6.0
optional: true
- bullmq@5.53.2:
+ bullmq@5.58.1:
dependencies:
cron-parser: 4.9.0
- ioredis: 5.6.1
+ ioredis: 5.7.0
msgpackr: 1.11.2
node-abort-controller: 3.1.1
semver: 7.7.2
@@ -16738,14 +16589,6 @@ snapshots:
es-errors: 1.3.0
function-bind: 1.1.2
- call-bind@1.0.7:
- dependencies:
- es-define-property: 1.0.1
- es-errors: 1.3.0
- function-bind: 1.1.2
- get-intrinsic: 1.3.0
- set-function-length: 1.2.2
-
call-bind@1.0.8:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -16758,8 +16601,6 @@ snapshots:
call-bind-apply-helpers: 1.0.2
get-intrinsic: 1.3.0
- call-me-maybe@1.0.2: {}
-
callsites@3.1.0: {}
camelcase-keys@6.2.2:
@@ -16775,12 +16616,10 @@ snapshots:
caniuse-api@3.0.0:
dependencies:
browserslist: 4.25.1
- caniuse-lite: 1.0.30001718
+ caniuse-lite: 1.0.30001727
lodash.memoize: 4.1.2
lodash.uniq: 4.5.0
- caniuse-lite@1.0.30001718: {}
-
caniuse-lite@1.0.30001727: {}
canonicalize@1.0.8: {}
@@ -16806,18 +16645,12 @@ snapshots:
assertion-error: 2.0.1
check-error: 2.1.1
deep-eql: 5.0.2
- loupe: 3.1.3
+ loupe: 3.1.4
pathval: 2.0.0
chalk-template@1.1.0:
dependencies:
- chalk: 5.4.1
-
- chalk@2.4.2:
- dependencies:
- ansi-styles: 3.2.1
- escape-string-regexp: 1.0.5
- supports-color: 5.5.0
+ chalk: 5.6.0
chalk@3.0.0:
dependencies:
@@ -16829,7 +16662,7 @@ snapshots:
ansi-styles: 4.3.0
supports-color: 7.2.0
- chalk@5.4.1: {}
+ chalk@5.6.0: {}
change-case@5.4.4: {}
@@ -16843,7 +16676,7 @@ snapshots:
character-parser@2.2.0:
dependencies:
- is-regex: 1.1.4
+ is-regex: 1.2.1
chart.js@4.5.0:
dependencies:
@@ -16854,7 +16687,7 @@ snapshots:
chart.js: 4.5.0
date-fns: 4.1.0
- chartjs-chart-matrix@2.1.1(chart.js@4.5.0):
+ chartjs-chart-matrix@3.0.0(chart.js@4.5.0):
dependencies:
chart.js: 4.5.0
@@ -16879,21 +16712,7 @@ snapshots:
css-what: 6.1.0
domelementtype: 2.3.0
domhandler: 5.0.3
- domutils: 3.1.0
-
- cheerio@1.0.0:
- dependencies:
- cheerio-select: 2.1.0
- dom-serializer: 2.0.0
- domhandler: 5.0.3
- domutils: 3.1.0
- encoding-sniffer: 0.2.0
- htmlparser2: 9.1.0
- parse5: 7.3.0
- parse5-htmlparser2-tree-adapter: 7.0.0
- parse5-parser-stream: 7.1.2
- undici: 6.19.8
- whatwg-mimetype: 4.0.0
+ domutils: 3.2.2
cheerio@1.1.0:
dependencies:
@@ -16921,7 +16740,7 @@ snapshots:
chownr@3.0.0: {}
- chromatic@11.29.0: {}
+ chromatic@13.1.3: {}
ci-info@3.7.1: {}
@@ -16985,16 +16804,10 @@ snapshots:
collect-v8-coverage@1.0.1: {}
- color-convert@1.9.3:
- dependencies:
- color-name: 1.1.3
-
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
- color-name@1.1.3: {}
-
color-name@1.1.4: {}
color-string@1.9.1:
@@ -17076,8 +16889,8 @@ snapshots:
constantinople@4.0.1:
dependencies:
- '@babel/parser': 7.27.2
- '@babel/types': 7.25.6
+ '@babel/parser': 7.28.0
+ '@babel/types': 7.28.1
content-disposition@0.5.4:
dependencies:
@@ -17117,28 +16930,13 @@ snapshots:
crc-32: 1.2.2
readable-stream: 4.3.0
- create-jest@29.7.0(@types/node@22.15.31):
- dependencies:
- '@jest/types': 29.6.3
- chalk: 4.1.2
- exit: 0.1.2
- graceful-fs: 4.2.11
- jest-config: 29.7.0(@types/node@22.15.31)
- jest-util: 29.7.0
- prompts: 2.4.2
- transitivePeerDependencies:
- - '@types/node'
- - babel-plugin-macros
- - supports-color
- - ts-node
-
- create-jest@29.7.0(@types/node@22.16.4):
+ create-jest@29.7.0(@types/node@22.17.2):
dependencies:
'@jest/types': 29.6.3
chalk: 4.1.2
exit: 0.1.2
graceful-fs: 4.2.11
- jest-config: 29.7.0(@types/node@22.16.4)
+ jest-config: 29.7.0(@types/node@22.17.2)
jest-util: 29.7.0
prompts: 2.4.2
transitivePeerDependencies:
@@ -17151,20 +16949,19 @@ snapshots:
dependencies:
luxon: 3.3.0
- cropperjs@2.0.0:
+ cropperjs@2.0.1:
dependencies:
- '@cropper/elements': 2.0.0
- '@cropper/utils': 2.0.0
+ '@cropper/elements': 2.0.1
+ '@cropper/utils': 2.0.1
- cross-env@7.0.3:
+ cross-env@10.0.0:
dependencies:
+ '@epic-web/invariant': 1.0.0
cross-spawn: 7.0.6
- cross-fetch@3.1.6(encoding@0.1.13):
+ cross-env@7.0.3:
dependencies:
- node-fetch: 2.7.0(encoding@0.1.13)
- transitivePeerDependencies:
- - encoding
+ cross-spawn: 7.0.6
cross-fetch@4.1.0(encoding@0.1.13):
dependencies:
@@ -17187,7 +16984,7 @@ snapshots:
boolbase: 1.0.0
css-what: 6.1.0
domhandler: 5.0.3
- domutils: 3.1.0
+ domutils: 3.2.2
nth-check: 2.1.1
css-tree@2.2.1:
@@ -17206,7 +17003,7 @@ snapshots:
cssesc@3.0.0: {}
- cssnano-preset-default@7.0.8(postcss@8.5.6):
+ cssnano-preset-default@7.0.9(postcss@8.5.6):
dependencies:
browserslist: 4.25.1
css-declaration-sorter: 7.2.0(postcss@8.5.6)
@@ -17214,7 +17011,7 @@ snapshots:
postcss: 8.5.6
postcss-calc: 10.1.1(postcss@8.5.6)
postcss-colormin: 7.0.4(postcss@8.5.6)
- postcss-convert-values: 7.0.6(postcss@8.5.6)
+ postcss-convert-values: 7.0.7(postcss@8.5.6)
postcss-discard-comments: 7.0.4(postcss@8.5.6)
postcss-discard-duplicates: 7.0.2(postcss@8.5.6)
postcss-discard-empty: 7.0.1(postcss@8.5.6)
@@ -17244,9 +17041,9 @@ snapshots:
dependencies:
postcss: 8.5.6
- cssnano@7.1.0(postcss@8.5.6):
+ cssnano@7.1.1(postcss@8.5.6):
dependencies:
- cssnano-preset-default: 7.0.8(postcss@8.5.6)
+ cssnano-preset-default: 7.0.9(postcss@8.5.6)
lilconfig: 3.1.3
postcss: 8.5.6
@@ -17261,9 +17058,9 @@ snapshots:
csstype@3.1.3: {}
- cypress@14.5.2:
+ cypress@14.5.4:
dependencies:
- '@cypress/request': 3.0.8
+ '@cypress/request': 3.0.9
'@cypress/xvfb': 1.2.4(supports-color@8.1.1)
'@types/sinonjs__fake-timers': 8.1.1
'@types/sizzle': 2.3.3
@@ -17303,7 +17100,7 @@ snapshots:
request-progress: 3.0.0
semver: 7.7.2
supports-color: 8.1.1
- tmp: 0.2.3
+ tmp: 0.2.5
tree-kill: 1.2.2
untildify: 4.0.0
yauzl: 2.10.0
@@ -17319,36 +17116,18 @@ snapshots:
whatwg-mimetype: 4.0.0
whatwg-url: 14.2.0
- data-view-buffer@1.0.1:
- dependencies:
- call-bind: 1.0.7
- es-errors: 1.3.0
- is-data-view: 1.0.1
-
data-view-buffer@1.0.2:
dependencies:
call-bound: 1.0.4
es-errors: 1.3.0
is-data-view: 1.0.2
- data-view-byte-length@1.0.1:
- dependencies:
- call-bind: 1.0.7
- es-errors: 1.3.0
- is-data-view: 1.0.1
-
data-view-byte-length@1.0.2:
dependencies:
call-bound: 1.0.4
es-errors: 1.3.0
is-data-view: 1.0.2
- data-view-byte-offset@1.0.0:
- dependencies:
- call-bind: 1.0.7
- es-errors: 1.3.0
- is-data-view: 1.0.1
-
data-view-byte-offset@1.0.1:
dependencies:
call-bound: 1.0.4
@@ -17375,11 +17154,11 @@ snapshots:
optionalDependencies:
supports-color: 8.1.1
- debug@4.4.1(supports-color@10.0.0):
+ debug@4.4.1(supports-color@10.2.0):
dependencies:
ms: 2.1.3
optionalDependencies:
- supports-color: 10.0.0
+ supports-color: 10.2.0
debug@4.4.1(supports-color@5.5.0):
dependencies:
@@ -17436,23 +17215,23 @@ snapshots:
deep-equal@2.2.0:
dependencies:
- call-bind: 1.0.7
+ call-bind: 1.0.8
es-get-iterator: 1.1.3
get-intrinsic: 1.3.0
is-arguments: 1.1.1
- is-array-buffer: 3.0.4
- is-date-object: 1.0.5
- is-regex: 1.1.4
- is-shared-array-buffer: 1.0.3
+ is-array-buffer: 3.0.5
+ is-date-object: 1.1.0
+ is-regex: 1.2.1
+ is-shared-array-buffer: 1.0.4
isarray: 2.0.5
object-is: 1.1.5
object-keys: 1.1.1
- object.assign: 4.1.5
- regexp.prototype.flags: 1.5.2
+ object.assign: 4.1.7
+ regexp.prototype.flags: 1.5.4
side-channel: 1.1.0
- which-boxed-primitive: 1.0.2
- which-collection: 1.0.1
- which-typed-array: 1.1.15
+ which-boxed-primitive: 1.1.1
+ which-collection: 1.0.2
+ which-typed-array: 1.1.19
deep-extend@0.6.0:
optional: true
@@ -17570,12 +17349,6 @@ snapshots:
domelementtype: 2.3.0
domhandler: 4.3.1
- domutils@3.1.0:
- dependencies:
- dom-serializer: 2.0.0
- domelementtype: 2.3.0
- domhandler: 5.0.3
-
domutils@3.2.2:
dependencies:
dom-serializer: 2.0.0
@@ -17612,8 +17385,6 @@ snapshots:
ee-first@1.1.1: {}
- electron-to-chromium@1.5.155: {}
-
electron-to-chromium@1.5.186: {}
emittery@0.13.1: {}
@@ -17658,55 +17429,6 @@ snapshots:
dependencies:
is-arrayish: 0.2.1
- es-abstract@1.23.3:
- dependencies:
- array-buffer-byte-length: 1.0.1
- arraybuffer.prototype.slice: 1.0.3
- available-typed-arrays: 1.0.7
- call-bind: 1.0.7
- data-view-buffer: 1.0.1
- data-view-byte-length: 1.0.1
- data-view-byte-offset: 1.0.0
- es-define-property: 1.0.1
- es-errors: 1.3.0
- es-object-atoms: 1.1.1
- es-set-tostringtag: 2.1.0
- es-to-primitive: 1.2.1
- function.prototype.name: 1.1.6
- get-intrinsic: 1.3.0
- get-symbol-description: 1.0.2
- globalthis: 1.0.3
- gopd: 1.2.0
- has-property-descriptors: 1.0.2
- has-proto: 1.0.3
- has-symbols: 1.1.0
- hasown: 2.0.2
- internal-slot: 1.0.7
- is-array-buffer: 3.0.4
- is-callable: 1.2.7
- is-data-view: 1.0.1
- is-negative-zero: 2.0.3
- is-regex: 1.1.4
- is-shared-array-buffer: 1.0.3
- is-string: 1.0.7
- is-typed-array: 1.1.13
- is-weakref: 1.0.2
- object-inspect: 1.13.4
- object-keys: 1.1.1
- object.assign: 4.1.5
- regexp.prototype.flags: 1.5.2
- safe-array-concat: 1.1.2
- safe-regex-test: 1.0.3
- string.prototype.trim: 1.2.9
- string.prototype.trimend: 1.0.8
- string.prototype.trimstart: 1.0.8
- typed-array-buffer: 1.0.2
- typed-array-byte-length: 1.0.1
- typed-array-byte-offset: 1.0.2
- typed-array-length: 1.0.6
- unbox-primitive: 1.0.2
- which-typed-array: 1.1.15
-
es-abstract@1.24.0:
dependencies:
array-buffer-byte-length: 1.0.2
@@ -17770,15 +17492,15 @@ snapshots:
es-get-iterator@1.1.3:
dependencies:
- call-bind: 1.0.7
+ call-bind: 1.0.8
get-intrinsic: 1.3.0
has-symbols: 1.1.0
is-arguments: 1.1.1
- is-map: 2.0.2
- is-set: 2.0.2
- is-string: 1.0.7
+ is-map: 2.0.3
+ is-set: 2.0.3
+ is-string: 1.1.1
isarray: 2.0.5
- stop-iteration-iterator: 1.0.0
+ stop-iteration-iterator: 1.1.0
es-module-lexer@1.7.0: {}
@@ -17793,25 +17515,15 @@ snapshots:
has-tostringtag: 1.0.2
hasown: 2.0.2
- es-shim-unscopables@1.0.2:
- dependencies:
- hasown: 2.0.2
-
es-shim-unscopables@1.1.0:
dependencies:
hasown: 2.0.2
- es-to-primitive@1.2.1:
- dependencies:
- is-callable: 1.2.7
- is-date-object: 1.0.5
- is-symbol: 1.0.4
-
es-to-primitive@1.3.0:
dependencies:
is-callable: 1.2.7
- is-date-object: 1.0.5
- is-symbol: 1.0.4
+ is-date-object: 1.1.0
+ is-symbol: 1.1.1
es-toolkit@1.27.0: {}
@@ -17823,69 +17535,70 @@ snapshots:
es6-promise: 4.2.8
optional: true
- esbuild-register@3.5.0(esbuild@0.25.6):
+ esbuild-register@3.5.0(esbuild@0.25.9):
dependencies:
- debug: 4.4.1(supports-color@10.0.0)
- esbuild: 0.25.6
+ debug: 4.4.1(supports-color@5.5.0)
+ esbuild: 0.25.9
transitivePeerDependencies:
- supports-color
- esbuild@0.25.4:
+ esbuild@0.25.8:
optionalDependencies:
- '@esbuild/aix-ppc64': 0.25.4
- '@esbuild/android-arm': 0.25.4
- '@esbuild/android-arm64': 0.25.4
- '@esbuild/android-x64': 0.25.4
- '@esbuild/darwin-arm64': 0.25.4
- '@esbuild/darwin-x64': 0.25.4
- '@esbuild/freebsd-arm64': 0.25.4
- '@esbuild/freebsd-x64': 0.25.4
- '@esbuild/linux-arm': 0.25.4
- '@esbuild/linux-arm64': 0.25.4
- '@esbuild/linux-ia32': 0.25.4
- '@esbuild/linux-loong64': 0.25.4
- '@esbuild/linux-mips64el': 0.25.4
- '@esbuild/linux-ppc64': 0.25.4
- '@esbuild/linux-riscv64': 0.25.4
- '@esbuild/linux-s390x': 0.25.4
- '@esbuild/linux-x64': 0.25.4
- '@esbuild/netbsd-arm64': 0.25.4
- '@esbuild/netbsd-x64': 0.25.4
- '@esbuild/openbsd-arm64': 0.25.4
- '@esbuild/openbsd-x64': 0.25.4
- '@esbuild/sunos-x64': 0.25.4
- '@esbuild/win32-arm64': 0.25.4
- '@esbuild/win32-ia32': 0.25.4
- '@esbuild/win32-x64': 0.25.4
+ '@esbuild/aix-ppc64': 0.25.8
+ '@esbuild/android-arm': 0.25.8
+ '@esbuild/android-arm64': 0.25.8
+ '@esbuild/android-x64': 0.25.8
+ '@esbuild/darwin-arm64': 0.25.8
+ '@esbuild/darwin-x64': 0.25.8
+ '@esbuild/freebsd-arm64': 0.25.8
+ '@esbuild/freebsd-x64': 0.25.8
+ '@esbuild/linux-arm': 0.25.8
+ '@esbuild/linux-arm64': 0.25.8
+ '@esbuild/linux-ia32': 0.25.8
+ '@esbuild/linux-loong64': 0.25.8
+ '@esbuild/linux-mips64el': 0.25.8
+ '@esbuild/linux-ppc64': 0.25.8
+ '@esbuild/linux-riscv64': 0.25.8
+ '@esbuild/linux-s390x': 0.25.8
+ '@esbuild/linux-x64': 0.25.8
+ '@esbuild/netbsd-arm64': 0.25.8
+ '@esbuild/netbsd-x64': 0.25.8
+ '@esbuild/openbsd-arm64': 0.25.8
+ '@esbuild/openbsd-x64': 0.25.8
+ '@esbuild/openharmony-arm64': 0.25.8
+ '@esbuild/sunos-x64': 0.25.8
+ '@esbuild/win32-arm64': 0.25.8
+ '@esbuild/win32-ia32': 0.25.8
+ '@esbuild/win32-x64': 0.25.8
- esbuild@0.25.6:
+ esbuild@0.25.9:
optionalDependencies:
- '@esbuild/aix-ppc64': 0.25.6
- '@esbuild/android-arm': 0.25.6
- '@esbuild/android-arm64': 0.25.6
- '@esbuild/android-x64': 0.25.6
- '@esbuild/darwin-arm64': 0.25.6
- '@esbuild/darwin-x64': 0.25.6
- '@esbuild/freebsd-arm64': 0.25.6
- '@esbuild/freebsd-x64': 0.25.6
- '@esbuild/linux-arm': 0.25.6
- '@esbuild/linux-arm64': 0.25.6
- '@esbuild/linux-ia32': 0.25.6
- '@esbuild/linux-loong64': 0.25.6
- '@esbuild/linux-mips64el': 0.25.6
- '@esbuild/linux-ppc64': 0.25.6
- '@esbuild/linux-riscv64': 0.25.6
- '@esbuild/linux-s390x': 0.25.6
- '@esbuild/linux-x64': 0.25.6
- '@esbuild/netbsd-arm64': 0.25.6
- '@esbuild/netbsd-x64': 0.25.6
- '@esbuild/openbsd-arm64': 0.25.6
- '@esbuild/openbsd-x64': 0.25.6
- '@esbuild/openharmony-arm64': 0.25.6
- '@esbuild/sunos-x64': 0.25.6
- '@esbuild/win32-arm64': 0.25.6
- '@esbuild/win32-ia32': 0.25.6
- '@esbuild/win32-x64': 0.25.6
+ '@esbuild/aix-ppc64': 0.25.9
+ '@esbuild/android-arm': 0.25.9
+ '@esbuild/android-arm64': 0.25.9
+ '@esbuild/android-x64': 0.25.9
+ '@esbuild/darwin-arm64': 0.25.9
+ '@esbuild/darwin-x64': 0.25.9
+ '@esbuild/freebsd-arm64': 0.25.9
+ '@esbuild/freebsd-x64': 0.25.9
+ '@esbuild/linux-arm': 0.25.9
+ '@esbuild/linux-arm64': 0.25.9
+ '@esbuild/linux-ia32': 0.25.9
+ '@esbuild/linux-loong64': 0.25.9
+ '@esbuild/linux-mips64el': 0.25.9
+ '@esbuild/linux-ppc64': 0.25.9
+ '@esbuild/linux-riscv64': 0.25.9
+ '@esbuild/linux-s390x': 0.25.9
+ '@esbuild/linux-x64': 0.25.9
+ '@esbuild/netbsd-arm64': 0.25.9
+ '@esbuild/netbsd-x64': 0.25.9
+ '@esbuild/openbsd-arm64': 0.25.9
+ '@esbuild/openbsd-x64': 0.25.9
+ '@esbuild/openharmony-arm64': 0.25.9
+ '@esbuild/sunos-x64': 0.25.9
+ '@esbuild/win32-arm64': 0.25.9
+ '@esbuild/win32-ia32': 0.25.9
+ '@esbuild/win32-x64': 0.25.9
escalade@3.2.0: {}
@@ -17917,61 +17630,22 @@ snapshots:
eslint-import-resolver-node@0.3.9:
dependencies:
debug: 3.2.7(supports-color@8.1.1)
- is-core-module: 2.15.1
+ is-core-module: 2.16.1
resolve: 1.22.8
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.12.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.31.0):
- dependencies:
- debug: 3.2.7(supports-color@8.1.1)
- optionalDependencies:
- '@typescript-eslint/parser': 8.34.0(eslint@9.31.0)(typescript@5.8.3)
- eslint: 9.31.0
- eslint-import-resolver-node: 0.3.9
- transitivePeerDependencies:
- - supports-color
-
- eslint-module-utils@2.12.1(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.31.0):
+ eslint-module-utils@2.12.1(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.34.0):
dependencies:
debug: 3.2.7(supports-color@8.1.1)
optionalDependencies:
- '@typescript-eslint/parser': 8.37.0(eslint@9.31.0)(typescript@5.8.3)
- eslint: 9.31.0
+ '@typescript-eslint/parser': 8.40.0(eslint@9.34.0)(typescript@5.9.2)
+ eslint: 9.34.0
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
- supports-color
- eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0):
- dependencies:
- '@rtsao/scc': 1.1.0
- array-includes: 3.1.8
- array.prototype.findlastindex: 1.2.5
- array.prototype.flat: 1.3.2
- array.prototype.flatmap: 1.3.2
- debug: 3.2.7(supports-color@8.1.1)
- doctrine: 2.1.0
- eslint: 9.31.0
- eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.31.0)
- hasown: 2.0.2
- is-core-module: 2.15.1
- is-glob: 4.0.3
- minimatch: 3.1.2
- object.fromentries: 2.0.8
- object.groupby: 1.0.3
- object.values: 1.2.0
- semver: 6.3.1
- string.prototype.trimend: 1.0.8
- tsconfig-paths: 3.15.0
- optionalDependencies:
- '@typescript-eslint/parser': 8.34.0(eslint@9.31.0)(typescript@5.8.3)
- transitivePeerDependencies:
- - eslint-import-resolver-typescript
- - eslint-import-resolver-webpack
- - supports-color
-
- eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0):
+ eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@@ -17980,9 +17654,9 @@ snapshots:
array.prototype.flatmap: 1.3.3
debug: 3.2.7(supports-color@8.1.1)
doctrine: 2.1.0
- eslint: 9.31.0
+ eslint: 9.34.0
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.31.0)
+ eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.34.0)
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -17994,24 +17668,24 @@ snapshots:
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
- '@typescript-eslint/parser': 8.37.0(eslint@9.31.0)(typescript@5.8.3)
+ '@typescript-eslint/parser': 8.40.0(eslint@9.34.0)(typescript@5.9.2)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
- eslint-plugin-vue@10.3.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(vue-eslint-parser@10.2.0(eslint@9.31.0)):
+ eslint-plugin-vue@10.4.0(@typescript-eslint/parser@8.40.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(vue-eslint-parser@10.2.0(eslint@9.34.0)):
dependencies:
- '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0)
- eslint: 9.31.0
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0)
+ eslint: 9.34.0
natural-compare: 1.4.0
nth-check: 2.1.1
postcss-selector-parser: 6.1.2
semver: 7.7.2
- vue-eslint-parser: 10.2.0(eslint@9.31.0)
+ vue-eslint-parser: 10.2.0(eslint@9.34.0)
xml-name-validator: 4.0.0
optionalDependencies:
- '@typescript-eslint/parser': 8.37.0(eslint@9.31.0)(typescript@5.8.3)
+ '@typescript-eslint/parser': 8.40.0(eslint@9.34.0)(typescript@5.9.2)
eslint-rule-docs@1.1.235: {}
@@ -18022,20 +17696,18 @@ snapshots:
eslint-visitor-keys@3.4.3: {}
- eslint-visitor-keys@4.2.0: {}
-
eslint-visitor-keys@4.2.1: {}
- eslint@9.31.0:
+ eslint@9.34.0:
dependencies:
- '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0)
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0)
'@eslint-community/regexpp': 4.12.1
'@eslint/config-array': 0.21.0
- '@eslint/config-helpers': 0.3.0
- '@eslint/core': 0.15.1
+ '@eslint/config-helpers': 0.3.1
+ '@eslint/core': 0.15.2
'@eslint/eslintrc': 3.3.1
- '@eslint/js': 9.31.0
- '@eslint/plugin-kit': 0.3.1
+ '@eslint/js': 9.34.0
+ '@eslint/plugin-kit': 0.3.5
'@humanfs/node': 0.16.6
'@humanwhocodes/module-importer': 1.0.1
'@humanwhocodes/retry': 0.4.2
@@ -18044,7 +17716,7 @@ snapshots:
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.6
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@5.5.0)
escape-string-regexp: 4.0.0
eslint-scope: 8.4.0
eslint-visitor-keys: 4.2.1
@@ -18299,13 +17971,13 @@ snapshots:
fast-uri@3.0.1: {}
- fast-xml-parser@4.4.1:
+ fast-xml-parser@4.5.0:
dependencies:
strnum: 1.0.5
- fast-xml-parser@4.5.0:
+ fast-xml-parser@5.2.5:
dependencies:
- strnum: 1.0.5
+ strnum: 2.1.1
fastify-plugin@4.5.1: {}
@@ -18317,7 +17989,7 @@ snapshots:
raw-body: 3.0.0
secure-json-parse: 2.7.0
- fastify@5.3.3:
+ fastify@5.5.0:
dependencies:
'@fastify/ajv-compiler': 4.0.0
'@fastify/error': 4.0.0
@@ -18347,9 +18019,13 @@ snapshots:
dependencies:
pend: 1.2.0
- fdir@6.4.4(picomatch@4.0.2):
+ fdir@6.4.6(picomatch@4.0.3):
optionalDependencies:
- picomatch: 4.0.2
+ picomatch: 4.0.3
+
+ fdir@6.5.0(picomatch@4.0.3):
+ optionalDependencies:
+ picomatch: 4.0.3
feed@4.2.2:
dependencies:
@@ -18430,6 +18106,12 @@ snapshots:
locate-path: 6.0.0
path-exists: 4.0.0
+ find-up@7.0.0:
+ dependencies:
+ locate-path: 7.2.0
+ path-exists: 5.0.0
+ unicorn-magic: 0.1.0
+
find-versions@5.1.0:
dependencies:
semver-regex: 4.0.5
@@ -18457,21 +18139,12 @@ snapshots:
follow-redirects@1.15.9(debug@4.4.1):
optionalDependencies:
- debug: 4.4.1(supports-color@10.0.0)
-
- for-each@0.3.3:
- dependencies:
- is-callable: 1.2.7
+ debug: 4.4.1(supports-color@5.5.0)
for-each@0.3.5:
dependencies:
is-callable: 1.2.7
- foreground-child@3.1.1:
- dependencies:
- cross-spawn: 7.0.6
- signal-exit: 4.1.0
-
foreground-child@3.3.1:
dependencies:
cross-spawn: 7.0.6
@@ -18483,7 +18156,7 @@ snapshots:
form-data-encoder@4.0.2: {}
- form-data@4.0.3:
+ form-data@4.0.4:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
@@ -18547,13 +18220,6 @@ snapshots:
function-bind@1.1.2: {}
- function.prototype.name@1.1.6:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-abstract: 1.23.3
- functions-have-names: 1.2.3
-
function.prototype.name@1.1.8:
dependencies:
call-bind: 1.0.8
@@ -18615,22 +18281,12 @@ snapshots:
'@sec-ant/readable-stream': 0.4.1
is-stream: 4.0.1
- get-symbol-description@1.0.2:
- dependencies:
- call-bind: 1.0.7
- es-errors: 1.3.0
- get-intrinsic: 1.3.0
-
get-symbol-description@1.1.0:
dependencies:
call-bound: 1.0.4
es-errors: 1.3.0
get-intrinsic: 1.3.0
- get-tsconfig@4.10.0:
- dependencies:
- resolve-pkg-maps: 1.0.0
-
get-tsconfig@4.10.1:
dependencies:
resolve-pkg-maps: 1.0.0
@@ -18656,22 +18312,13 @@ snapshots:
glob@10.4.5:
dependencies:
- foreground-child: 3.1.1
+ foreground-child: 3.3.1
jackspeak: 3.4.3
minimatch: 9.0.5
minipass: 7.1.2
package-json-from-dist: 1.0.0
path-scurry: 1.11.1
- glob@11.0.2:
- dependencies:
- foreground-child: 3.1.1
- jackspeak: 4.0.1
- minimatch: 10.0.3
- minipass: 7.1.2
- package-json-from-dist: 1.0.0
- path-scurry: 2.0.0
-
glob@11.0.3:
dependencies:
foreground-child: 3.3.1
@@ -18708,10 +18355,6 @@ snapshots:
globals@16.3.0: {}
- globalthis@1.0.3:
- dependencies:
- define-properties: 1.2.1
-
globalthis@1.0.4:
dependencies:
define-properties: 1.2.1
@@ -18761,7 +18404,7 @@ snapshots:
lowercase-keys: 3.0.0
p-cancelable: 4.0.1
responselike: 3.0.0
- type-fest: 4.26.1
+ type-fest: 4.41.0
graceful-fs@4.2.11: {}
@@ -18776,14 +18419,15 @@ snapshots:
webidl-conversions: 7.0.0
whatwg-mimetype: 3.0.0
- happy-dom@17.6.3:
+ happy-dom@18.0.1:
dependencies:
- webidl-conversions: 7.0.0
+ '@types/node': 20.19.9
+ '@types/whatwg-mimetype': 3.0.2
whatwg-mimetype: 3.0.0
hard-rejection@2.1.0: {}
- harfbuzzjs@0.4.7: {}
+ harfbuzzjs@0.4.9: {}
has-bigints@1.0.2: {}
@@ -18795,8 +18439,6 @@ snapshots:
dependencies:
es-define-property: 1.0.1
- has-proto@1.0.3: {}
-
has-proto@1.2.0:
dependencies:
dunder-proto: 1.0.1
@@ -18859,8 +18501,6 @@ snapshots:
dependencies:
whatwg-encoding: 3.1.1
- html-entities@2.5.2: {}
-
html-entities@2.6.0: {}
html-escaper@2.0.2: {}
@@ -18887,14 +18527,7 @@ snapshots:
dependencies:
domelementtype: 2.3.0
domhandler: 5.0.3
- domutils: 3.1.0
- entities: 4.5.0
-
- htmlparser2@9.1.0:
- dependencies:
- domelementtype: 2.3.0
- domhandler: 5.0.3
- domutils: 3.1.0
+ domutils: 3.2.2
entities: 4.5.0
http-cache-semantics@4.1.1: {}
@@ -18912,7 +18545,7 @@ snapshots:
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.3
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -18940,15 +18573,22 @@ snapshots:
https-proxy-agent@5.0.1:
dependencies:
agent-base: 6.0.2
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@10.2.0)
transitivePeerDependencies:
- supports-color
optional: true
- https-proxy-agent@7.0.6(supports-color@10.0.0):
+ https-proxy-agent@7.0.6:
dependencies:
agent-base: 7.1.3
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@5.5.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ https-proxy-agent@7.0.6(supports-color@10.2.0):
+ dependencies:
+ agent-base: 7.1.3
+ debug: 4.4.1(supports-color@10.2.0)
transitivePeerDependencies:
- supports-color
@@ -19032,12 +18672,6 @@ snapshots:
install-artifact-from-github@1.4.0: {}
- internal-slot@1.0.7:
- dependencies:
- es-errors: 1.3.0
- hasown: 2.0.2
- side-channel: 1.1.0
-
internal-slot@1.1.0:
dependencies:
es-errors: 1.3.0
@@ -19046,11 +18680,11 @@ snapshots:
intersection-observer@0.12.2: {}
- ioredis@5.6.1:
+ ioredis@5.7.0:
dependencies:
- '@ioredis/commands': 1.2.0
+ '@ioredis/commands': 1.3.0
cluster-key-slot: 1.1.2
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@10.2.0)
denque: 2.1.0
lodash.defaults: 4.2.0
lodash.isarguments: 3.1.0
@@ -19060,6 +18694,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ ios-haptics@0.1.0: {}
+
ip-address@9.0.5:
dependencies:
jsbn: 1.1.0
@@ -19079,14 +18715,9 @@ snapshots:
is-arguments@1.1.1:
dependencies:
- call-bind: 1.0.7
+ call-bind: 1.0.8
has-tostringtag: 1.0.2
- is-array-buffer@3.0.4:
- dependencies:
- call-bind: 1.0.7
- get-intrinsic: 1.3.0
-
is-array-buffer@3.0.5:
dependencies:
call-bind: 1.0.8
@@ -19105,19 +18736,10 @@ snapshots:
has-tostringtag: 1.0.2
safe-regex-test: 1.1.0
- is-bigint@1.0.4:
- dependencies:
- has-bigints: 1.0.2
-
is-bigint@1.1.0:
dependencies:
has-bigints: 1.0.2
- is-boolean-object@1.1.2:
- dependencies:
- call-bind: 1.0.7
- has-tostringtag: 1.0.2
-
is-boolean-object@1.2.2:
dependencies:
call-bound: 1.0.4
@@ -19125,28 +18747,16 @@ snapshots:
is-callable@1.2.7: {}
- is-core-module@2.15.1:
- dependencies:
- hasown: 2.0.2
-
is-core-module@2.16.1:
dependencies:
hasown: 2.0.2
- is-data-view@1.0.1:
- dependencies:
- is-typed-array: 1.1.13
-
is-data-view@1.0.2:
dependencies:
call-bound: 1.0.4
get-intrinsic: 1.3.0
is-typed-array: 1.1.15
- is-date-object@1.0.5:
- dependencies:
- has-tostringtag: 1.0.2
-
is-date-object@1.1.0:
dependencies:
call-bound: 1.0.4
@@ -19184,18 +18794,12 @@ snapshots:
global-dirs: 3.0.1
is-path-inside: 3.0.3
- is-map@2.0.2: {}
-
is-map@2.0.3: {}
is-negative-zero@2.0.3: {}
is-node-process@1.2.0: {}
- is-number-object@1.0.7:
- dependencies:
- has-tostringtag: 1.0.2
-
is-number-object@1.1.1:
dependencies:
call-bound: 1.0.4
@@ -19215,11 +18819,6 @@ snapshots:
is-promise@2.2.2: {}
- is-regex@1.1.4:
- dependencies:
- call-bind: 1.0.7
- has-tostringtag: 1.0.2
-
is-regex@1.2.1:
dependencies:
call-bound: 1.0.4
@@ -19227,14 +18826,8 @@ snapshots:
has-tostringtag: 1.0.2
hasown: 2.0.2
- is-set@2.0.2: {}
-
is-set@2.0.3: {}
- is-shared-array-buffer@1.0.3:
- dependencies:
- call-bind: 1.0.7
-
is-shared-array-buffer@1.0.4:
dependencies:
call-bound: 1.0.4
@@ -19245,10 +18838,6 @@ snapshots:
is-stream@4.0.1: {}
- is-string@1.0.7:
- dependencies:
- has-tostringtag: 1.0.2
-
is-string@1.1.1:
dependencies:
call-bound: 1.0.4
@@ -19258,20 +18847,12 @@ snapshots:
dependencies:
fast-xml-parser: 4.5.0
- is-symbol@1.0.4:
- dependencies:
- has-symbols: 1.1.0
-
is-symbol@1.1.1:
dependencies:
call-bound: 1.0.4
has-symbols: 1.1.0
safe-regex-test: 1.1.0
- is-typed-array@1.1.13:
- dependencies:
- which-typed-array: 1.1.15
-
is-typed-array@1.1.15:
dependencies:
which-typed-array: 1.1.19
@@ -19282,23 +18863,12 @@ snapshots:
is-unicode-supported@2.1.0: {}
- is-weakmap@2.0.1: {}
-
is-weakmap@2.0.2: {}
- is-weakref@1.0.2:
- dependencies:
- call-bind: 1.0.7
-
is-weakref@1.1.1:
dependencies:
call-bound: 1.0.4
- is-weakset@2.0.2:
- dependencies:
- call-bind: 1.0.7
- get-intrinsic: 1.3.0
-
is-weakset@2.0.4:
dependencies:
call-bound: 1.0.4
@@ -19323,7 +18893,7 @@ snapshots:
istanbul-lib-instrument@5.2.1:
dependencies:
'@babel/core': 7.24.7
- '@babel/parser': 7.27.2
+ '@babel/parser': 7.28.0
'@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.2
semver: 6.3.1
@@ -19333,7 +18903,7 @@ snapshots:
istanbul-lib-instrument@6.0.0:
dependencies:
'@babel/core': 7.24.7
- '@babel/parser': 7.27.2
+ '@babel/parser': 7.28.0
'@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.2
semver: 7.7.2
@@ -19348,7 +18918,7 @@ snapshots:
istanbul-lib-source-maps@4.0.1:
dependencies:
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@10.2.0)
istanbul-lib-coverage: 3.2.2
source-map: 0.6.1
transitivePeerDependencies:
@@ -19356,8 +18926,8 @@ snapshots:
istanbul-lib-source-maps@5.0.6:
dependencies:
- '@jridgewell/trace-mapping': 0.3.25
- debug: 4.4.1(supports-color@10.0.0)
+ '@jridgewell/trace-mapping': 0.3.29
+ debug: 4.4.1(supports-color@5.5.0)
istanbul-lib-coverage: 3.2.2
transitivePeerDependencies:
- supports-color
@@ -19375,12 +18945,6 @@ snapshots:
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
- jackspeak@4.0.1:
- dependencies:
- '@isaacs/cliui': 8.0.2
- optionalDependencies:
- '@pkgjs/parseargs': 0.11.0
-
jackspeak@4.1.1:
dependencies:
'@isaacs/cliui': 8.0.2
@@ -19397,7 +18961,7 @@ snapshots:
'@jest/expect': 29.7.0
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
chalk: 4.1.2
co: 4.6.0
dedent: 1.6.0
@@ -19417,16 +18981,16 @@ snapshots:
- babel-plugin-macros
- supports-color
- jest-cli@29.7.0(@types/node@22.15.31):
+ jest-cli@29.7.0(@types/node@22.17.2):
dependencies:
'@jest/core': 29.7.0
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
chalk: 4.1.2
- create-jest: 29.7.0(@types/node@22.15.31)
+ create-jest: 29.7.0(@types/node@22.17.2)
exit: 0.1.2
import-local: 3.1.0
- jest-config: 29.7.0(@types/node@22.15.31)
+ jest-config: 29.7.0(@types/node@22.17.2)
jest-util: 29.7.0
jest-validate: 29.7.0
yargs: 17.7.2
@@ -19436,26 +19000,7 @@ snapshots:
- supports-color
- ts-node
- jest-cli@29.7.0(@types/node@22.16.4):
- dependencies:
- '@jest/core': 29.7.0
- '@jest/test-result': 29.7.0
- '@jest/types': 29.6.3
- chalk: 4.1.2
- create-jest: 29.7.0(@types/node@22.16.4)
- exit: 0.1.2
- import-local: 3.1.0
- jest-config: 29.7.0(@types/node@22.16.4)
- jest-util: 29.7.0
- jest-validate: 29.7.0
- yargs: 17.7.2
- transitivePeerDependencies:
- - '@types/node'
- - babel-plugin-macros
- - supports-color
- - ts-node
-
- jest-config@29.7.0(@types/node@22.15.31):
+ jest-config@29.7.0(@types/node@22.17.2):
dependencies:
'@babel/core': 7.24.7
'@jest/test-sequencer': 29.7.0
@@ -19480,37 +19025,7 @@ snapshots:
slash: 3.0.0
strip-json-comments: 3.1.1
optionalDependencies:
- '@types/node': 22.15.31
- transitivePeerDependencies:
- - babel-plugin-macros
- - supports-color
-
- jest-config@29.7.0(@types/node@22.16.4):
- dependencies:
- '@babel/core': 7.24.7
- '@jest/test-sequencer': 29.7.0
- '@jest/types': 29.6.3
- babel-jest: 29.7.0(@babel/core@7.24.7)
- chalk: 4.1.2
- ci-info: 3.7.1
- deepmerge: 4.2.2
- glob: 7.2.3
- graceful-fs: 4.2.11
- jest-circus: 29.7.0
- jest-environment-node: 29.7.0
- jest-get-type: 29.6.3
- jest-regex-util: 29.6.3
- jest-resolve: 29.7.0
- jest-runner: 29.7.0
- jest-util: 29.7.0
- jest-validate: 29.7.0
- micromatch: 4.0.8
- parse-json: 5.2.0
- pretty-format: 29.7.0
- slash: 3.0.0
- strip-json-comments: 3.1.1
- optionalDependencies:
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
@@ -19539,24 +19054,17 @@ snapshots:
'@jest/environment': 29.7.0
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
jest-mock: 29.7.0
jest-util: 29.7.0
- jest-fetch-mock@3.0.3(encoding@0.1.13):
- dependencies:
- cross-fetch: 3.1.6(encoding@0.1.13)
- promise-polyfill: 8.3.0
- transitivePeerDependencies:
- - encoding
-
jest-get-type@29.6.3: {}
jest-haste-map@29.7.0:
dependencies:
'@jest/types': 29.6.3
'@types/graceful-fs': 4.1.6
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
anymatch: 3.1.3
fb-watchman: 2.0.2
graceful-fs: 4.2.11
@@ -19582,7 +19090,7 @@ snapshots:
jest-message-util@29.7.0:
dependencies:
- '@babel/code-frame': 7.24.7
+ '@babel/code-frame': 7.27.1
'@jest/types': 29.6.3
'@types/stack-utils': 2.0.1
chalk: 4.1.2
@@ -19595,7 +19103,7 @@ snapshots:
jest-mock@29.7.0:
dependencies:
'@jest/types': 29.6.3
- '@types/node': 22.15.31
+ '@types/node': 22.17.2
jest-util: 29.7.0
jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
@@ -19632,7 +19140,7 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
chalk: 4.1.2
emittery: 0.13.1
graceful-fs: 4.2.11
@@ -19660,7 +19168,7 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
chalk: 4.1.2
cjs-module-lexer: 1.2.2
collect-v8-coverage: 1.0.1
@@ -19684,7 +19192,7 @@ snapshots:
'@babel/generator': 7.24.7
'@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.7)
'@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.24.7)
- '@babel/types': 7.25.6
+ '@babel/types': 7.28.1
'@jest/expect-utils': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
@@ -19706,7 +19214,7 @@ snapshots:
jest-util@29.7.0:
dependencies:
'@jest/types': 29.6.3
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
chalk: 4.1.2
ci-info: 3.7.1
graceful-fs: 4.2.11
@@ -19725,43 +19233,26 @@ snapshots:
dependencies:
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
ansi-escapes: 4.3.2
chalk: 4.1.2
emittery: 0.13.1
jest-util: 29.7.0
string-length: 4.0.2
- jest-websocket-mock@2.5.0:
- dependencies:
- jest-diff: 29.7.0
- mock-socket: 9.3.1
-
jest-worker@29.7.0:
dependencies:
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
jest-util: 29.7.0
merge-stream: 2.0.0
supports-color: 8.1.1
- jest@29.7.0(@types/node@22.15.31):
+ jest@29.7.0(@types/node@22.17.2):
dependencies:
'@jest/core': 29.7.0
'@jest/types': 29.6.3
import-local: 3.1.0
- jest-cli: 29.7.0(@types/node@22.15.31)
- transitivePeerDependencies:
- - '@types/node'
- - babel-plugin-macros
- - supports-color
- - ts-node
-
- jest@29.7.0(@types/node@22.16.4):
- dependencies:
- '@jest/core': 29.7.0
- '@jest/types': 29.6.3
- import-local: 3.1.0
- jest-cli: 29.7.0(@types/node@22.16.4)
+ jest-cli: 29.7.0(@types/node@22.17.2)
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
@@ -19808,8 +19299,6 @@ snapshots:
jschardet@3.1.4: {}
- jsdoc-type-pratt-parser@4.1.0: {}
-
jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5):
dependencies:
cssstyle: 4.2.1
@@ -19817,7 +19306,7 @@ snapshots:
decimal.js: 10.5.0
html-encoding-sniffer: 4.0.0
http-proxy-agent: 7.0.2
- https-proxy-agent: 7.0.6(supports-color@10.0.0)
+ https-proxy-agent: 7.0.6
is-potential-custom-element-name: 1.0.1
nwsapi: 2.2.16
parse5: 7.3.0
@@ -19830,7 +19319,7 @@ snapshots:
whatwg-encoding: 3.1.1
whatwg-mimetype: 4.0.0
whatwg-url: 14.2.0
- ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)
+ ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)
xml-name-validator: 5.0.0
optionalDependencies:
canvas: 3.1.0
@@ -19917,7 +19406,7 @@ snapshots:
juice@11.0.1:
dependencies:
- cheerio: 1.0.0
+ cheerio: 1.1.0
commander: 12.1.0
entities: 4.5.0
mensch: 0.3.4
@@ -20001,6 +19490,10 @@ snapshots:
dependencies:
p-locate: 5.0.0
+ locate-path@7.2.0:
+ dependencies:
+ p-locate: 6.0.0
+
lodash.defaults@4.2.0: {}
lodash.get@4.4.2: {}
@@ -20033,8 +19526,6 @@ snapshots:
longest-streak@3.1.0: {}
- loupe@3.1.3: {}
-
loupe@3.1.4: {}
lowercase-keys@3.0.0: {}
@@ -20057,18 +19548,18 @@ snapshots:
lz-string@1.5.0: {}
- magic-string@0.27.0:
+ magic-string@0.30.17:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
- magic-string@0.30.17:
+ magic-string@0.30.18:
dependencies:
- '@jridgewell/sourcemap-codec': 1.5.0
+ '@jridgewell/sourcemap-codec': 1.5.5
magicast@0.3.5:
dependencies:
- '@babel/parser': 7.27.2
- '@babel/types': 7.27.1
+ '@babel/parser': 7.28.0
+ '@babel/types': 7.28.1
source-map-js: 1.2.1
mailcheck@1.1.1: {}
@@ -20235,7 +19726,7 @@ snapshots:
media-typer@0.3.0: {}
- meilisearch@0.51.0: {}
+ meilisearch@0.52.0: {}
memoizerific@1.11.3:
dependencies:
@@ -20266,11 +19757,11 @@ snapshots:
methods@1.1.2: {}
- mfm-js@0.24.0:
+ mfm-js@0.25.0:
dependencies:
- '@twemoji/parser': 15.0.0
+ '@twemoji/parser': 16.0.0
- microformats-parser@2.0.3:
+ microformats-parser@2.0.4:
dependencies:
parse5: 7.3.0
@@ -20446,7 +19937,7 @@ snapshots:
micromark@4.0.0:
dependencies:
'@types/debug': 4.1.12
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@5.5.0)
decode-named-character-reference: 1.0.2
devlop: 1.1.0
micromark-core-commonmark: 2.0.0
@@ -20498,10 +19989,6 @@ snapshots:
dependencies:
'@isaacs/brace-expansion': 5.0.0
- minimatch@3.0.8:
- dependencies:
- brace-expansion: 1.1.11
-
minimatch@3.1.2:
dependencies:
brace-expansion: 1.1.11
@@ -20612,17 +20099,17 @@ snapshots:
optionalDependencies:
msgpackr-extract: 3.0.2
- msw-storybook-addon@2.0.5(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3)):
+ msw-storybook-addon@2.0.5(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2)):
dependencies:
is-node-process: 1.2.0
- msw: 2.10.4(@types/node@22.16.4)(typescript@5.8.3)
+ msw: 2.10.5(@types/node@22.17.2)(typescript@5.9.2)
- msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3):
+ msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2):
dependencies:
'@bundled-es-modules/cookie': 2.0.1
'@bundled-es-modules/statuses': 1.0.1
'@bundled-es-modules/tough-cookie': 0.1.6
- '@inquirer/confirm': 5.0.2(@types/node@22.16.4)
+ '@inquirer/confirm': 5.0.2(@types/node@22.17.2)
'@mswjs/interceptors': 0.39.2
'@open-draft/deferred-promise': 2.2.0
'@open-draft/until': 2.1.0
@@ -20638,13 +20125,13 @@ snapshots:
type-fest: 4.41.0
yargs: 17.7.2
optionalDependencies:
- typescript: 5.8.3
+ typescript: 5.9.2
transitivePeerDependencies:
- '@types/node'
muggle-string@0.4.1: {}
- multer@2.0.1:
+ multer@2.0.2:
dependencies:
append-field: 1.0.0
busboy: 1.6.0
@@ -20668,8 +20155,6 @@ snapshots:
nanoid@3.3.11: {}
- nanoid@3.3.8: {}
-
nanoid@5.1.5: {}
napi-build-utils@2.0.0:
@@ -20683,7 +20168,7 @@ snapshots:
dependencies:
debug: 3.2.7(supports-color@8.1.1)
iconv-lite: 0.4.24
- sax: 1.2.4
+ sax: 1.4.1
transitivePeerDependencies:
- supports-color
@@ -20709,14 +20194,9 @@ snapshots:
just-extend: 6.2.0
path-to-regexp: 8.2.0
- node-abi@3.74.0:
- dependencies:
- semver: 7.7.2
-
node-abi@3.75.0:
dependencies:
semver: 7.7.2
- optional: true
node-abort-controller@3.1.1: {}
@@ -20762,7 +20242,7 @@ snapshots:
proc-log: 5.0.0
semver: 7.7.2
tar: 7.4.3
- tinyglobby: 0.2.13
+ tinyglobby: 0.2.14
which: 5.0.0
transitivePeerDependencies:
- supports-color
@@ -20815,7 +20295,7 @@ snapshots:
normalize-package-data@3.0.3:
dependencies:
hosted-git-info: 4.1.0
- is-core-module: 2.15.1
+ is-core-module: 2.16.1
semver: 7.7.2
validate-npm-package-license: 3.0.4
@@ -20873,18 +20353,11 @@ snapshots:
object-is@1.1.5:
dependencies:
- call-bind: 1.0.7
+ call-bind: 1.0.8
define-properties: 1.2.1
object-keys@1.1.1: {}
- object.assign@4.1.5:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- has-symbols: 1.1.0
- object-keys: 1.1.1
-
object.assign@4.1.7:
dependencies:
call-bind: 1.0.8
@@ -20896,22 +20369,16 @@ snapshots:
object.fromentries@2.0.8:
dependencies:
- call-bind: 1.0.7
+ call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.23.3
+ es-abstract: 1.24.0
es-object-atoms: 1.1.1
object.groupby@1.0.3:
dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-abstract: 1.23.3
-
- object.values@1.2.0:
- dependencies:
- call-bind: 1.0.7
+ call-bind: 1.0.8
define-properties: 1.2.1
- es-object-atoms: 1.1.1
+ es-abstract: 1.24.0
object.values@1.2.1:
dependencies:
@@ -20958,14 +20425,14 @@ snapshots:
openapi-types@12.1.3: {}
- openapi-typescript@7.8.0(typescript@5.8.3):
+ openapi-typescript@7.9.1(typescript@5.9.2):
dependencies:
- '@redocly/openapi-core': 1.34.3(supports-color@10.0.0)
+ '@redocly/openapi-core': 1.34.5(supports-color@10.2.0)
ansi-colors: 4.1.3
change-case: 5.4.4
parse-json: 8.3.0
- supports-color: 10.0.0
- typescript: 5.8.3
+ supports-color: 10.2.0
+ typescript: 5.9.2
yargs-parser: 21.1.1
optionator@0.9.4:
@@ -20981,9 +20448,9 @@ snapshots:
ospath@1.2.2: {}
- otpauth@9.4.0:
+ otpauth@9.4.1:
dependencies:
- '@noble/hashes': 1.7.1
+ '@noble/hashes': 1.8.0
outvariant@1.4.3: {}
@@ -21007,6 +20474,10 @@ snapshots:
dependencies:
yocto-queue: 0.1.0
+ p-limit@4.0.0:
+ dependencies:
+ yocto-queue: 1.2.1
+
p-locate@4.1.0:
dependencies:
p-limit: 2.3.0
@@ -21015,6 +20486,10 @@ snapshots:
dependencies:
p-limit: 3.1.0
+ p-locate@6.0.0:
+ dependencies:
+ p-limit: 4.0.0
+
p-map@4.0.0:
dependencies:
aggregate-error: 3.1.0
@@ -21040,7 +20515,7 @@ snapshots:
parse-json@5.2.0:
dependencies:
- '@babel/code-frame': 7.24.7
+ '@babel/code-frame': 7.27.1
error-ex: 1.3.2
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
@@ -21059,11 +20534,6 @@ snapshots:
dependencies:
parse5: 6.0.1
- parse5-htmlparser2-tree-adapter@7.0.0:
- dependencies:
- domhandler: 5.0.3
- parse5: 7.3.0
-
parse5-htmlparser2-tree-adapter@7.1.0:
dependencies:
domhandler: 5.0.3
@@ -21087,6 +20557,8 @@ snapshots:
path-exists@4.0.0: {}
+ path-exists@5.0.0: {}
+
path-is-absolute@1.0.1: {}
path-key@3.1.1: {}
@@ -21129,19 +20601,21 @@ snapshots:
performance-now@2.1.0: {}
- pg-cloudflare@1.2.5:
+ pg-cloudflare@1.2.7:
optional: true
- pg-connection-string@2.9.0: {}
+ pg-connection-string@2.9.1: {}
pg-int8@1.0.1: {}
- pg-pool@3.10.0(pg@8.16.0):
+ pg-pool@3.10.1(pg@8.16.3):
dependencies:
- pg: 8.16.0
+ pg: 8.16.3
pg-protocol@1.10.0: {}
+ pg-protocol@1.10.3: {}
+
pg-types@2.2.0:
dependencies:
pg-int8: 1.0.1
@@ -21150,15 +20624,15 @@ snapshots:
postgres-date: 1.0.7
postgres-interval: 1.2.0
- pg@8.16.0:
+ pg@8.16.3:
dependencies:
- pg-connection-string: 2.9.0
- pg-pool: 3.10.0(pg@8.16.0)
- pg-protocol: 1.10.0
+ pg-connection-string: 2.9.1
+ pg-pool: 3.10.1(pg@8.16.3)
+ pg-protocol: 1.10.3
pg-types: 2.2.0
pgpass: 1.0.5
optionalDependencies:
- pg-cloudflare: 1.2.5
+ pg-cloudflare: 1.2.7
pgpass@1.0.5:
dependencies:
@@ -21170,7 +20644,7 @@ snapshots:
picomatch@2.3.1: {}
- picomatch@4.0.2: {}
+ picomatch@4.0.3: {}
pid-port@1.0.2:
dependencies:
@@ -21223,7 +20697,7 @@ snapshots:
pngjs@5.0.0: {}
- pnpm@10.13.1: {}
+ pnpm@10.15.0: {}
polished@4.2.2:
dependencies:
@@ -21245,7 +20719,7 @@ snapshots:
postcss: 8.5.6
postcss-value-parser: 4.2.0
- postcss-convert-values@7.0.6(postcss@8.5.6):
+ postcss-convert-values@7.0.7(postcss@8.5.6):
dependencies:
browserslist: 4.25.1
postcss: 8.5.6
@@ -21392,12 +20866,6 @@ snapshots:
postcss-value-parser@4.2.0: {}
- postcss@8.5.3:
- dependencies:
- nanoid: 3.3.8
- picocolors: 1.1.1
- source-map-js: 1.2.1
-
postcss@8.5.6:
dependencies:
nanoid: 3.3.11
@@ -21488,8 +20956,6 @@ snapshots:
promise-limit@2.7.0: {}
- promise-polyfill@8.3.0: {}
-
promise-retry@2.0.1:
dependencies:
err-code: 2.0.3
@@ -21683,15 +21149,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
- react-docgen-typescript@2.2.2(typescript@5.8.3):
+ react-docgen-typescript@2.2.2(typescript@5.9.2):
dependencies:
- typescript: 5.8.3
+ typescript: 5.9.2
- react-docgen@7.0.1:
+ react-docgen@8.0.0:
dependencies:
'@babel/core': 7.24.7
'@babel/traverse': 7.24.7
- '@babel/types': 7.25.6
+ '@babel/types': 7.28.1
'@types/babel__core': 7.20.0
'@types/babel__traverse': 7.20.0
'@types/doctrine': 0.0.9
@@ -21702,16 +21168,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
- react-dom@19.1.0(react@19.1.0):
+ react-dom@19.1.1(react@19.1.1):
dependencies:
- react: 19.1.0
+ react: 19.1.1
scheduler: 0.26.0
react-is@17.0.2: {}
react-is@18.2.0: {}
- react@19.1.0: {}
+ react@19.1.1: {}
read-pkg-up@7.0.1:
dependencies:
@@ -21811,13 +21277,6 @@ snapshots:
dependencies:
regex-utilities: 2.3.0
- regexp.prototype.flags@1.5.2:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-errors: 1.3.0
- set-function-name: 2.0.2
-
regexp.prototype.flags@1.5.4:
dependencies:
call-bind: 1.0.8
@@ -21869,7 +21328,7 @@ snapshots:
require-in-the-middle@7.3.0:
dependencies:
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@10.2.0)
module-details-from-path: 1.0.3
resolve: 1.22.8
transitivePeerDependencies:
@@ -21895,7 +21354,7 @@ snapshots:
resolve@1.22.8:
dependencies:
- is-core-module: 2.15.1
+ is-core-module: 2.16.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
@@ -21930,30 +21389,56 @@ snapshots:
dependencies:
glob: 10.4.5
- rollup@4.45.1:
+ rollup@4.46.2:
+ dependencies:
+ '@types/estree': 1.0.8
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.46.2
+ '@rollup/rollup-android-arm64': 4.46.2
+ '@rollup/rollup-darwin-arm64': 4.46.2
+ '@rollup/rollup-darwin-x64': 4.46.2
+ '@rollup/rollup-freebsd-arm64': 4.46.2
+ '@rollup/rollup-freebsd-x64': 4.46.2
+ '@rollup/rollup-linux-arm-gnueabihf': 4.46.2
+ '@rollup/rollup-linux-arm-musleabihf': 4.46.2
+ '@rollup/rollup-linux-arm64-gnu': 4.46.2
+ '@rollup/rollup-linux-arm64-musl': 4.46.2
+ '@rollup/rollup-linux-loongarch64-gnu': 4.46.2
+ '@rollup/rollup-linux-ppc64-gnu': 4.46.2
+ '@rollup/rollup-linux-riscv64-gnu': 4.46.2
+ '@rollup/rollup-linux-riscv64-musl': 4.46.2
+ '@rollup/rollup-linux-s390x-gnu': 4.46.2
+ '@rollup/rollup-linux-x64-gnu': 4.46.2
+ '@rollup/rollup-linux-x64-musl': 4.46.2
+ '@rollup/rollup-win32-arm64-msvc': 4.46.2
+ '@rollup/rollup-win32-ia32-msvc': 4.46.2
+ '@rollup/rollup-win32-x64-msvc': 4.46.2
+ fsevents: 2.3.3
+
+ rollup@4.48.0:
dependencies:
'@types/estree': 1.0.8
optionalDependencies:
- '@rollup/rollup-android-arm-eabi': 4.45.1
- '@rollup/rollup-android-arm64': 4.45.1
- '@rollup/rollup-darwin-arm64': 4.45.1
- '@rollup/rollup-darwin-x64': 4.45.1
- '@rollup/rollup-freebsd-arm64': 4.45.1
- '@rollup/rollup-freebsd-x64': 4.45.1
- '@rollup/rollup-linux-arm-gnueabihf': 4.45.1
- '@rollup/rollup-linux-arm-musleabihf': 4.45.1
- '@rollup/rollup-linux-arm64-gnu': 4.45.1
- '@rollup/rollup-linux-arm64-musl': 4.45.1
- '@rollup/rollup-linux-loongarch64-gnu': 4.45.1
- '@rollup/rollup-linux-powerpc64le-gnu': 4.45.1
- '@rollup/rollup-linux-riscv64-gnu': 4.45.1
- '@rollup/rollup-linux-riscv64-musl': 4.45.1
- '@rollup/rollup-linux-s390x-gnu': 4.45.1
- '@rollup/rollup-linux-x64-gnu': 4.45.1
- '@rollup/rollup-linux-x64-musl': 4.45.1
- '@rollup/rollup-win32-arm64-msvc': 4.45.1
- '@rollup/rollup-win32-ia32-msvc': 4.45.1
- '@rollup/rollup-win32-x64-msvc': 4.45.1
+ '@rollup/rollup-android-arm-eabi': 4.48.0
+ '@rollup/rollup-android-arm64': 4.48.0
+ '@rollup/rollup-darwin-arm64': 4.48.0
+ '@rollup/rollup-darwin-x64': 4.48.0
+ '@rollup/rollup-freebsd-arm64': 4.48.0
+ '@rollup/rollup-freebsd-x64': 4.48.0
+ '@rollup/rollup-linux-arm-gnueabihf': 4.48.0
+ '@rollup/rollup-linux-arm-musleabihf': 4.48.0
+ '@rollup/rollup-linux-arm64-gnu': 4.48.0
+ '@rollup/rollup-linux-arm64-musl': 4.48.0
+ '@rollup/rollup-linux-loongarch64-gnu': 4.48.0
+ '@rollup/rollup-linux-ppc64-gnu': 4.48.0
+ '@rollup/rollup-linux-riscv64-gnu': 4.48.0
+ '@rollup/rollup-linux-riscv64-musl': 4.48.0
+ '@rollup/rollup-linux-s390x-gnu': 4.48.0
+ '@rollup/rollup-linux-x64-gnu': 4.48.0
+ '@rollup/rollup-linux-x64-musl': 4.48.0
+ '@rollup/rollup-win32-arm64-msvc': 4.48.0
+ '@rollup/rollup-win32-ia32-msvc': 4.48.0
+ '@rollup/rollup-win32-x64-msvc': 4.48.0
fsevents: 2.3.3
rrweb-cssom@0.8.0: {}
@@ -21971,13 +21456,6 @@ snapshots:
dependencies:
tslib: 2.8.1
- safe-array-concat@1.1.2:
- dependencies:
- call-bind: 1.0.7
- get-intrinsic: 1.3.0
- has-symbols: 1.1.0
- isarray: 2.0.5
-
safe-array-concat@1.1.3:
dependencies:
call-bind: 1.0.8
@@ -21995,12 +21473,6 @@ snapshots:
es-errors: 1.3.0
isarray: 2.0.5
- safe-regex-test@1.0.3:
- dependencies:
- call-bind: 1.0.7
- es-errors: 1.3.0
- is-regex: 1.1.4
-
safe-regex-test@1.1.0:
dependencies:
call-bound: 1.0.4
@@ -22022,9 +21494,9 @@ snapshots:
htmlparser2: 8.0.1
is-plain-object: 5.0.0
parse-srcset: 1.0.2
- postcss: 8.5.3
+ postcss: 8.5.6
- sass@1.89.2:
+ sass@1.90.0:
dependencies:
chokidar: 4.0.3
immutable: 5.0.3
@@ -22032,8 +21504,6 @@ snapshots:
optionalDependencies:
'@parcel/watcher': 2.5.0
- sax@1.2.4: {}
-
sax@1.4.1: {}
saxes@6.0.0:
@@ -22192,14 +21662,14 @@ snapshots:
shebang-regex@3.0.0: {}
- shiki@3.8.0:
+ shiki@3.11.0:
dependencies:
- '@shikijs/core': 3.8.0
- '@shikijs/engine-javascript': 3.8.0
- '@shikijs/engine-oniguruma': 3.8.0
- '@shikijs/langs': 3.8.0
- '@shikijs/themes': 3.8.0
- '@shikijs/types': 3.8.0
+ '@shikijs/core': 3.11.0
+ '@shikijs/engine-javascript': 3.11.0
+ '@shikijs/engine-oniguruma': 3.11.0
+ '@shikijs/langs': 3.11.0
+ '@shikijs/themes': 3.11.0
+ '@shikijs/types': 3.11.0
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
@@ -22253,7 +21723,7 @@ snapshots:
dependencies:
'@hapi/hoek': 11.0.4
'@hapi/wreck': 18.0.1
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@10.2.0)
joi: 17.13.3
transitivePeerDependencies:
- supports-color
@@ -22353,7 +21823,7 @@ snapshots:
socks-proxy-agent@8.0.5:
dependencies:
agent-base: 7.1.3
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@10.2.0)
socks: 2.8.4
transitivePeerDependencies:
- supports-color
@@ -22445,16 +21915,16 @@ snapshots:
standard-as-callback@2.1.0: {}
- start-server-and-test@2.0.12:
+ start-server-and-test@2.0.13:
dependencies:
arg: 5.0.2
bluebird: 3.7.2
check-more-types: 2.24.0
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@5.5.0)
execa: 5.1.1
lazy-ass: 1.6.0
ps-tree: 1.2.0
- wait-on: 8.0.3(debug@4.4.1)
+ wait-on: 8.0.4(debug@4.4.1)
transitivePeerDependencies:
- supports-color
@@ -22462,37 +21932,47 @@ snapshots:
std-env@3.9.0: {}
- stop-iteration-iterator@1.0.0:
- dependencies:
- internal-slot: 1.0.7
-
stop-iteration-iterator@1.1.0:
dependencies:
es-errors: 1.3.0
internal-slot: 1.1.0
- storybook-addon-misskey-theme@https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/components@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/core-events@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/manager-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/preview-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/theming@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/types@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ storybook-addon-misskey-theme@https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(259986330117e371e80190ddf50a33b9):
dependencies:
- '@storybook/blocks': 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/components': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/core-events': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/manager-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/preview-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/theming': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
- '@storybook/types': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))
+ '@storybook/blocks': 8.6.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/components': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/core-events': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/manager-api': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/preview-api': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/theming': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
+ '@storybook/types': 8.6.14(storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)))
optionalDependencies:
- react: 19.1.0
- react-dom: 19.1.0(react@19.1.0)
+ react: 19.1.1
+ react-dom: 19.1.1(react@19.1.1)
- storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5):
+ storybook@9.1.3(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(prettier@3.6.2)(utf-8-validate@6.0.5)(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)):
dependencies:
- '@storybook/core': 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(utf-8-validate@6.0.5)
+ '@storybook/global': 5.0.0
+ '@testing-library/jest-dom': 6.6.4
+ '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.0)
+ '@vitest/expect': 3.2.4
+ '@vitest/mocker': 3.2.4(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
+ '@vitest/spy': 3.2.4
+ better-opn: 3.0.2
+ esbuild: 0.25.9
+ esbuild-register: 3.5.0(esbuild@0.25.9)
+ recast: 0.23.6
+ semver: 7.7.2
+ ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)
optionalDependencies:
prettier: 3.6.2
transitivePeerDependencies:
+ - '@testing-library/dom'
- bufferutil
+ - msw
- supports-color
- utf-8-validate
+ - vite
stream-browserify@3.0.0:
dependencies:
@@ -22549,19 +22029,6 @@ snapshots:
es-object-atoms: 1.1.1
has-property-descriptors: 1.0.2
- string.prototype.trim@1.2.9:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-abstract: 1.23.3
- es-object-atoms: 1.1.1
-
- string.prototype.trimend@1.0.8:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-object-atoms: 1.1.1
-
string.prototype.trimend@1.0.9:
dependencies:
call-bind: 1.0.8
@@ -22571,7 +22038,7 @@ snapshots:
string.prototype.trimstart@1.0.8:
dependencies:
- call-bind: 1.0.7
+ call-bind: 1.0.8
define-properties: 1.2.1
es-object-atoms: 1.1.1
@@ -22634,6 +22101,8 @@ snapshots:
strnum@1.0.5: {}
+ strnum@2.1.1: {}
+
strtok3@10.2.2:
dependencies:
'@tokenizer/token': 0.3.0
@@ -22650,13 +22119,13 @@ snapshots:
postcss: 8.5.6
postcss-selector-parser: 7.1.0
- superagent@10.2.1:
+ superagent@10.2.3:
dependencies:
component-emitter: 1.3.1
cookiejar: 2.1.4
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@10.2.0)
fast-safe-stringify: 2.1.1
- form-data: 4.0.3
+ form-data: 4.0.4
formidable: 3.5.4
methods: 1.1.2
mime: 2.6.0
@@ -22664,14 +22133,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
- supertest@7.1.1:
+ supertest@7.1.4:
dependencies:
methods: 1.1.2
- superagent: 10.2.1
+ superagent: 10.2.3
transitivePeerDependencies:
- supports-color
- supports-color@10.0.0: {}
+ supports-color@10.2.0: {}
supports-color@5.5.0:
dependencies:
@@ -22704,7 +22173,7 @@ snapshots:
symbol-tree@3.2.4: {}
- systeminformation@5.27.1: {}
+ systeminformation@5.27.7: {}
tar-fs@2.1.2:
dependencies:
@@ -22785,7 +22254,7 @@ snapshots:
dependencies:
real-require: 0.2.0
- three@0.178.0: {}
+ three@0.179.1: {}
throttle-debounce@5.0.2: {}
@@ -22806,15 +22275,10 @@ snapshots:
tinyexec@0.3.2: {}
- tinyglobby@0.2.13:
- dependencies:
- fdir: 6.4.4(picomatch@4.0.2)
- picomatch: 4.0.2
-
tinyglobby@0.2.14:
dependencies:
- fdir: 6.4.4(picomatch@4.0.2)
- picomatch: 4.0.2
+ fdir: 6.4.6(picomatch@4.0.3)
+ picomatch: 4.0.3
tinypool@1.1.1: {}
@@ -22832,14 +22296,12 @@ snapshots:
dependencies:
tldts-core: 6.1.61
- tmp@0.2.3: {}
+ tmp@0.2.5: {}
tmpl@1.0.5: {}
to-data-view@1.1.0: {}
- to-fast-properties@2.0.0: {}
-
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
@@ -22884,9 +22346,9 @@ snapshots:
trough@2.2.0: {}
- ts-api-utils@2.1.0(typescript@5.8.3):
+ ts-api-utils@2.1.0(typescript@5.9.2):
dependencies:
- typescript: 5.8.3
+ typescript: 5.9.2
ts-case-convert@2.1.0: {}
@@ -22898,7 +22360,7 @@ snapshots:
dependencies:
chokidar: 4.0.3
commander: 9.5.0
- get-tsconfig: 4.10.0
+ get-tsconfig: 4.10.1
globby: 11.1.0
mylas: 2.1.13
normalize-path: 3.0.0
@@ -22917,9 +22379,9 @@ snapshots:
minimist: 1.2.8
strip-bom: 3.0.0
- tsd@0.32.0:
+ tsd@0.33.0:
dependencies:
- '@tsd/typescript': 5.8.3
+ '@tsd/typescript': 5.9.2
eslint-formatter-pretty: 4.1.0
globby: 11.1.0
jest-diff: 29.7.0
@@ -22929,9 +22391,16 @@ snapshots:
tslib@2.8.1: {}
- tsx@4.20.3:
+ tsx@4.20.4:
+ dependencies:
+ esbuild: 0.25.9
+ get-tsconfig: 4.10.1
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ tsx@4.20.5:
dependencies:
- esbuild: 0.25.6
+ esbuild: 0.25.9
get-tsconfig: 4.10.1
optionalDependencies:
fsevents: 2.3.3
@@ -22958,8 +22427,6 @@ snapshots:
type-fest@2.19.0: {}
- type-fest@4.26.1: {}
-
type-fest@4.41.0: {}
type-is@1.6.18:
@@ -22967,66 +22434,34 @@ snapshots:
media-typer: 0.3.0
mime-types: 2.1.35
- typed-array-buffer@1.0.2:
- dependencies:
- call-bind: 1.0.7
- es-errors: 1.3.0
- is-typed-array: 1.1.13
-
typed-array-buffer@1.0.3:
dependencies:
call-bound: 1.0.4
es-errors: 1.3.0
is-typed-array: 1.1.15
- typed-array-byte-length@1.0.1:
- dependencies:
- call-bind: 1.0.7
- for-each: 0.3.3
- gopd: 1.2.0
- has-proto: 1.0.3
- is-typed-array: 1.1.13
-
typed-array-byte-length@1.0.3:
dependencies:
call-bind: 1.0.8
- for-each: 0.3.3
+ for-each: 0.3.5
gopd: 1.2.0
has-proto: 1.2.0
is-typed-array: 1.1.15
- typed-array-byte-offset@1.0.2:
- dependencies:
- available-typed-arrays: 1.0.7
- call-bind: 1.0.7
- for-each: 0.3.3
- gopd: 1.2.0
- has-proto: 1.0.3
- is-typed-array: 1.1.13
-
typed-array-byte-offset@1.0.4:
dependencies:
available-typed-arrays: 1.0.7
call-bind: 1.0.8
- for-each: 0.3.3
+ for-each: 0.3.5
gopd: 1.2.0
has-proto: 1.2.0
is-typed-array: 1.1.15
reflect.getprototypeof: 1.0.10
- typed-array-length@1.0.6:
- dependencies:
- call-bind: 1.0.7
- for-each: 0.3.3
- gopd: 1.2.0
- has-proto: 1.0.3
- is-typed-array: 1.1.13
- possible-typed-array-names: 1.0.0
-
typed-array-length@1.0.7:
dependencies:
call-bind: 1.0.8
- for-each: 0.3.3
+ for-each: 0.3.5
gopd: 1.2.0
is-typed-array: 1.1.15
possible-typed-array-names: 1.0.0
@@ -23034,14 +22469,14 @@ snapshots:
typedarray@0.0.6: {}
- typeorm@0.3.24(patch_hash=2677b97a423e157945c154e64183d3ae2eb44dfa9cb0e5ce731a7612f507bb56)(ioredis@5.6.1)(pg@8.16.0)(reflect-metadata@0.2.2):
+ typeorm@0.3.26(patch_hash=2677b97a423e157945c154e64183d3ae2eb44dfa9cb0e5ce731a7612f507bb56)(ioredis@5.7.0)(pg@8.16.3)(reflect-metadata@0.2.2):
dependencies:
'@sqltools/formatter': 1.2.5
ansis: 3.17.0
app-root-path: 3.1.0
buffer: 6.0.3
dayjs: 1.11.13
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@10.2.0)
dedent: 1.6.0
dotenv: 16.4.7
glob: 10.4.5
@@ -23052,15 +22487,15 @@ snapshots:
uuid: 11.1.0
yargs: 17.7.2
optionalDependencies:
- ioredis: 5.6.1
- pg: 8.16.0
+ ioredis: 5.7.0
+ pg: 8.16.3
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
typescript@5.8.2: {}
- typescript@5.8.3: {}
+ typescript@5.9.2: {}
uid2@0.0.4: {}
@@ -23072,13 +22507,6 @@ snapshots:
ulid@2.4.0: {}
- unbox-primitive@1.0.2:
- dependencies:
- call-bind: 1.0.7
- has-bigints: 1.0.2
- has-symbols: 1.1.0
- which-boxed-primitive: 1.0.2
-
unbox-primitive@1.1.0:
dependencies:
call-bound: 1.0.4
@@ -23103,6 +22531,8 @@ snapshots:
undici@7.11.0: {}
+ unicorn-magic@0.1.0: {}
+
unicorn-magic@0.3.0: {}
unified@11.0.4:
@@ -23165,12 +22595,6 @@ snapshots:
untildify@4.0.0: {}
- update-browserslist-db@1.1.3(browserslist@4.24.5):
- dependencies:
- browserslist: 4.24.5
- escalade: 3.2.0
- picocolors: 1.1.1
-
update-browserslist-db@1.1.3(browserslist@4.25.1):
dependencies:
browserslist: 4.25.1
@@ -23195,14 +22619,6 @@ snapshots:
util-deprecate@1.0.2: {}
- util@0.12.5:
- dependencies:
- inherits: 2.0.4
- is-arguments: 1.1.1
- is-generator-function: 1.0.10
- is-typed-array: 1.1.13
- which-typed-array: 1.1.15
-
utils-merge@1.0.1: {}
uuid@11.1.0: {}
@@ -23211,18 +22627,18 @@ snapshots:
uuid@9.0.1: {}
- v-code-diff@1.13.1(vue@3.5.17(typescript@5.8.3)):
+ v-code-diff@1.13.1(vue@3.5.19(typescript@5.9.2)):
dependencies:
diff: 5.2.0
diff-match-patch: 1.0.5
highlight.js: 11.10.0
- vue: 3.5.17(typescript@5.8.3)
- vue-demi: 0.14.7(vue@3.5.17(typescript@5.8.3))
+ vue: 3.5.19(typescript@5.9.2)
+ vue-demi: 0.14.7(vue@3.5.19(typescript@5.9.2))
v8-to-istanbul@9.2.0:
dependencies:
- '@jridgewell/trace-mapping': 0.3.25
- '@types/istanbul-lib-coverage': 2.0.4
+ '@jridgewell/trace-mapping': 0.3.29
+ '@types/istanbul-lib-coverage': 2.0.6
convert-source-map: 2.0.0
valid-data-url@3.0.1: {}
@@ -23251,13 +22667,34 @@ snapshots:
unist-util-stringify-position: 4.0.0
vfile-message: 4.0.2
- vite-node@3.2.4(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3):
+ vite-node@3.2.4(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4):
+ dependencies:
+ cac: 6.7.14
+ debug: 4.4.1(supports-color@5.5.0)
+ es-module-lexer: 1.7.0
+ pathe: 2.0.3
+ vite: 7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)
+ transitivePeerDependencies:
+ - '@types/node'
+ - jiti
+ - less
+ - lightningcss
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - tsx
+ - yaml
+
+ vite-node@3.2.4(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5):
dependencies:
cac: 6.7.14
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@10.2.0)
es-module-lexer: 1.7.0
pathe: 2.0.3
- vite: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
+ vite: 7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5)
transitivePeerDependencies:
- '@types/node'
- jiti
@@ -23274,54 +22711,134 @@ snapshots:
vite-plugin-turbosnap@1.0.3: {}
- vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3):
+ vite@7.0.6(@types/node@22.17.0)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5):
+ dependencies:
+ esbuild: 0.25.8
+ fdir: 6.4.6(picomatch@4.0.3)
+ picomatch: 4.0.3
+ postcss: 8.5.6
+ rollup: 4.48.0
+ tinyglobby: 0.2.14
+ optionalDependencies:
+ '@types/node': 22.17.0
+ fsevents: 2.3.3
+ sass: 1.90.0
+ terser: 5.43.1
+ tsx: 4.20.5
+
+ vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4):
+ dependencies:
+ esbuild: 0.25.9
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+ postcss: 8.5.6
+ rollup: 4.48.0
+ tinyglobby: 0.2.14
+ optionalDependencies:
+ '@types/node': 22.17.2
+ fsevents: 2.3.3
+ sass: 1.90.0
+ terser: 5.43.1
+ tsx: 4.20.4
+
+ vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5):
dependencies:
- esbuild: 0.25.4
- fdir: 6.4.4(picomatch@4.0.2)
- picomatch: 4.0.2
- postcss: 8.5.3
- rollup: 4.45.1
- tinyglobby: 0.2.13
+ esbuild: 0.25.9
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+ postcss: 8.5.6
+ rollup: 4.48.0
+ tinyglobby: 0.2.14
optionalDependencies:
- '@types/node': 22.16.4
+ '@types/node': 22.17.2
fsevents: 2.3.3
- sass: 1.89.2
+ sass: 1.90.0
terser: 5.43.1
- tsx: 4.20.3
+ tsx: 4.20.5
- vitest-fetch-mock@0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)):
+ vitest-fetch-mock@0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)):
dependencies:
- vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
+ vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)
- vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3):
+ vitest-websocket-mock@0.5.0(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5)):
+ dependencies:
+ '@vitest/utils': 3.2.4
+ mock-socket: 9.3.1
+ vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5)
+
+ vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4):
dependencies:
'@types/chai': 5.2.2
'@vitest/expect': 3.2.4
- '@vitest/mocker': 3.2.4(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))
+ '@vitest/mocker': 3.2.4(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4))
'@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4
'@vitest/spy': 3.2.4
'@vitest/utils': 3.2.4
chai: 5.2.0
- debug: 4.4.1(supports-color@10.0.0)
+ debug: 4.4.1(supports-color@5.5.0)
expect-type: 1.2.1
- magic-string: 0.30.17
+ magic-string: 0.30.18
pathe: 2.0.3
- picomatch: 4.0.2
+ picomatch: 4.0.3
std-env: 3.9.0
tinybench: 2.9.0
tinyexec: 0.3.2
tinyglobby: 0.2.14
tinypool: 1.1.1
tinyrainbow: 2.0.0
- vite: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
- vite-node: 3.2.4(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
+ vite: 7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)
+ vite-node: 3.2.4(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.4)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/debug': 4.1.12
- '@types/node': 22.16.4
- happy-dom: 17.6.3
+ '@types/node': 22.17.2
+ happy-dom: 18.0.1
+ jsdom: 26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5)
+ transitivePeerDependencies:
+ - jiti
+ - less
+ - lightningcss
+ - msw
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - tsx
+ - yaml
+
+ vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(happy-dom@18.0.1)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5):
+ dependencies:
+ '@types/chai': 5.2.2
+ '@vitest/expect': 3.2.4
+ '@vitest/mocker': 3.2.4(msw@2.10.5(@types/node@22.17.2)(typescript@5.9.2))(vite@7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5))
+ '@vitest/pretty-format': 3.2.4
+ '@vitest/runner': 3.2.4
+ '@vitest/snapshot': 3.2.4
+ '@vitest/spy': 3.2.4
+ '@vitest/utils': 3.2.4
+ chai: 5.2.0
+ debug: 4.4.1(supports-color@10.2.0)
+ expect-type: 1.2.1
+ magic-string: 0.30.18
+ pathe: 2.0.3
+ picomatch: 4.0.3
+ std-env: 3.9.0
+ tinybench: 2.9.0
+ tinyexec: 0.3.2
+ tinyglobby: 0.2.14
+ tinypool: 1.1.1
+ tinyrainbow: 2.0.0
+ vite: 7.1.3(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5)
+ vite-node: 3.2.4(@types/node@22.17.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.5)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@types/debug': 4.1.12
+ '@types/node': 22.17.2
+ happy-dom: 18.0.1
jsdom: 26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5)
transitivePeerDependencies:
- jiti
@@ -23356,46 +22873,44 @@ snapshots:
vscode-uri@3.0.8: {}
- vue-component-meta@2.0.16(typescript@5.8.3):
+ vue-component-meta@2.0.16(typescript@5.9.2):
dependencies:
'@volar/typescript': 2.2.0
- '@vue/language-core': 2.0.16(typescript@5.8.3)
+ '@vue/language-core': 2.0.16(typescript@5.9.2)
path-browserify: 1.0.1
vue-component-type-helpers: 2.0.16
optionalDependencies:
- typescript: 5.8.3
+ typescript: 5.9.2
vue-component-type-helpers@1.8.4: {}
vue-component-type-helpers@2.0.16: {}
- vue-component-type-helpers@2.2.12: {}
-
- vue-component-type-helpers@3.0.1: {}
+ vue-component-type-helpers@3.0.6: {}
- vue-demi@0.14.7(vue@3.5.17(typescript@5.8.3)):
+ vue-demi@0.14.7(vue@3.5.19(typescript@5.9.2)):
dependencies:
- vue: 3.5.17(typescript@5.8.3)
+ vue: 3.5.19(typescript@5.9.2)
- vue-docgen-api@4.75.1(vue@3.5.17(typescript@5.8.3)):
+ vue-docgen-api@4.75.1(vue@3.5.19(typescript@5.9.2)):
dependencies:
- '@babel/parser': 7.27.2
- '@babel/types': 7.25.6
- '@vue/compiler-dom': 3.5.13
- '@vue/compiler-sfc': 3.5.17
+ '@babel/parser': 7.28.0
+ '@babel/types': 7.28.1
+ '@vue/compiler-dom': 3.5.18
+ '@vue/compiler-sfc': 3.5.19
ast-types: 0.16.1
hash-sum: 2.0.0
lru-cache: 8.0.4
pug: 3.0.3
recast: 0.23.6
ts-map: 1.0.3
- vue: 3.5.17(typescript@5.8.3)
- vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.5.17(typescript@5.8.3))
+ vue: 3.5.19(typescript@5.9.2)
+ vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.5.19(typescript@5.9.2))
- vue-eslint-parser@10.2.0(eslint@9.31.0):
+ vue-eslint-parser@10.2.0(eslint@9.34.0):
dependencies:
- debug: 4.4.1(supports-color@10.0.0)
- eslint: 9.31.0
+ debug: 4.4.1(supports-color@5.5.0)
+ eslint: 9.34.0
eslint-scope: 8.4.0
eslint-visitor-keys: 4.2.1
espree: 10.4.0
@@ -23404,43 +22919,43 @@ snapshots:
transitivePeerDependencies:
- supports-color
- vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.5.17(typescript@5.8.3)):
+ vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.5.19(typescript@5.9.2)):
dependencies:
- vue: 3.5.17(typescript@5.8.3)
+ vue: 3.5.19(typescript@5.9.2)
vue-template-compiler@2.7.14:
dependencies:
de-indent: 1.0.2
he: 1.2.0
- vue-tsc@2.2.12(typescript@5.8.3):
+ vue-tsc@3.0.6(typescript@5.9.2):
dependencies:
- '@volar/typescript': 2.4.15
- '@vue/language-core': 2.2.12(typescript@5.8.3)
- typescript: 5.8.3
+ '@volar/typescript': 2.4.23
+ '@vue/language-core': 3.0.6(typescript@5.9.2)
+ typescript: 5.9.2
- vue@3.5.17(typescript@5.8.3):
+ vue@3.5.19(typescript@5.9.2):
dependencies:
- '@vue/compiler-dom': 3.5.17
- '@vue/compiler-sfc': 3.5.17
- '@vue/runtime-dom': 3.5.17
- '@vue/server-renderer': 3.5.17(vue@3.5.17(typescript@5.8.3))
- '@vue/shared': 3.5.17
+ '@vue/compiler-dom': 3.5.19
+ '@vue/compiler-sfc': 3.5.19
+ '@vue/runtime-dom': 3.5.19
+ '@vue/server-renderer': 3.5.19(vue@3.5.19(typescript@5.9.2))
+ '@vue/shared': 3.5.19
optionalDependencies:
- typescript: 5.8.3
+ typescript: 5.9.2
- vuedraggable@4.1.0(vue@3.5.17(typescript@5.8.3)):
+ vuedraggable@4.1.0(vue@3.5.19(typescript@5.9.2)):
dependencies:
sortablejs: 1.14.0
- vue: 3.5.17(typescript@5.8.3)
+ vue: 3.5.19(typescript@5.9.2)
w3c-xmlserializer@5.0.0:
dependencies:
xml-name-validator: 5.0.0
- wait-on@8.0.3(debug@4.4.1):
+ wait-on@8.0.4(debug@4.4.1):
dependencies:
- axios: 1.8.4(debug@4.4.1)
+ axios: 1.11.0(debug@4.4.1)
joi: 17.13.3
lodash: 4.17.21
minimist: 1.2.8
@@ -23462,7 +22977,7 @@ snapshots:
dependencies:
asn1.js: 5.4.1
http_ece: 1.2.0
- https-proxy-agent: 7.0.6(supports-color@10.0.0)
+ https-proxy-agent: 7.0.6(supports-color@10.2.0)
jws: 4.0.0
minimist: 1.2.8
transitivePeerDependencies:
@@ -23507,14 +23022,6 @@ snapshots:
tr46: 0.0.3
webidl-conversions: 3.0.1
- which-boxed-primitive@1.0.2:
- dependencies:
- is-bigint: 1.0.4
- is-boolean-object: 1.1.2
- is-number-object: 1.0.7
- is-string: 1.0.7
- is-symbol: 1.0.4
-
which-boxed-primitive@1.1.1:
dependencies:
is-bigint: 1.1.0
@@ -23539,13 +23046,6 @@ snapshots:
which-collection: 1.0.2
which-typed-array: 1.1.19
- which-collection@1.0.1:
- dependencies:
- is-map: 2.0.2
- is-set: 2.0.2
- is-weakmap: 2.0.1
- is-weakset: 2.0.2
-
which-collection@1.0.2:
dependencies:
is-map: 2.0.3
@@ -23555,14 +23055,6 @@ snapshots:
which-module@2.0.0: {}
- which-typed-array@1.1.15:
- dependencies:
- available-typed-arrays: 1.0.7
- call-bind: 1.0.7
- for-each: 0.3.3
- gopd: 1.2.0
- has-tostringtag: 1.0.2
-
which-typed-array@1.1.19:
dependencies:
available-typed-arrays: 1.0.7
@@ -23597,8 +23089,8 @@ snapshots:
with@7.0.2:
dependencies:
- '@babel/parser': 7.27.2
- '@babel/types': 7.25.6
+ '@babel/parser': 7.28.0
+ '@babel/types': 7.28.1
assert-never: 1.2.1
babel-walk: 3.0.0-canary-5
@@ -23629,7 +23121,7 @@ snapshots:
imurmurhash: 0.1.4
signal-exit: 3.0.7
- ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5):
+ ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5):
optionalDependencies:
bufferutil: 4.0.9
utf-8-validate: 6.0.5
@@ -23638,7 +23130,7 @@ snapshots:
xml-js@1.6.11:
dependencies:
- sax: 1.2.4
+ sax: 1.4.1
xml-name-validator@4.0.0: {}
@@ -23646,7 +23138,7 @@ snapshots:
xml2js@0.5.0:
dependencies:
- sax: 1.2.4
+ sax: 1.4.1
xmlbuilder: 11.0.1
xmlbuilder@11.0.1: {}
@@ -23722,6 +23214,8 @@ snapshots:
yocto-queue@0.1.0: {}
+ yocto-queue@1.2.1: {}
+
yoctocolors-cjs@2.1.2: {}
yoctocolors@2.1.1: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 9a034e257b..0fadcc60f7 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -2,6 +2,7 @@ packages:
- packages/backend
- packages/frontend-shared
- packages/frontend
+ - packages/frontend-builder
- packages/frontend-embed
- packages/icons-subsetter
- packages/sw
diff --git a/scripts/changelog-checker/package-lock.json b/scripts/changelog-checker/package-lock.json
index 4ed26757a6..5976ef9d4c 100644
--- a/scripts/changelog-checker/package-lock.json
+++ b/scripts/changelog-checker/package-lock.json
@@ -9,16 +9,16 @@
"version": "1.0.0",
"devDependencies": {
"@types/mdast": "4.0.4",
- "@types/node": "22.15.31",
- "@vitest/coverage-v8": "3.2.3",
+ "@types/node": "22.17.1",
+ "@vitest/coverage-v8": "3.2.4",
"mdast-util-to-string": "4.0.0",
"remark": "15.0.1",
"remark-parse": "11.0.0",
- "typescript": "5.8.3",
+ "typescript": "5.9.2",
"unified": "11.0.5",
"vite": "6.3.5",
- "vite-node": "3.2.3",
- "vitest": "3.2.3"
+ "vite-node": "3.2.4",
+ "vitest": "3.2.4"
}
},
"node_modules/@ampproject/remapping": {
@@ -940,9 +940,9 @@
"dev": true
},
"node_modules/@types/node": {
- "version": "22.15.31",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.31.tgz",
- "integrity": "sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==",
+ "version": "22.17.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.1.tgz",
+ "integrity": "sha512-y3tBaz+rjspDTylNjAX37jEC3TETEFGNJL6uQDxwF9/8GLLIjW1rvVHlynyuUKMnMr1Roq8jOv3vkopBjC4/VA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -956,9 +956,9 @@
"dev": true
},
"node_modules/@vitest/coverage-v8": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.3.tgz",
- "integrity": "sha512-D1QKzngg8PcDoCE8FHSZhREDuEy+zcKmMiMafYse41RZpBE5EDJyKOTdqK3RQfsV2S2nyKor5KCs8PyPRFqKPg==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz",
+ "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -980,8 +980,8 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
- "@vitest/browser": "3.2.3",
- "vitest": "3.2.3"
+ "@vitest/browser": "3.2.4",
+ "vitest": "3.2.4"
},
"peerDependenciesMeta": {
"@vitest/browser": {
@@ -990,15 +990,15 @@
}
},
"node_modules/@vitest/expect": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.3.tgz",
- "integrity": "sha512-W2RH2TPWVHA1o7UmaFKISPvdicFJH+mjykctJFoAkUw+SPTJTGjUNdKscFBrqM7IPnCVu6zihtKYa7TkZS1dkQ==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz",
+ "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/chai": "^5.2.2",
- "@vitest/spy": "3.2.3",
- "@vitest/utils": "3.2.3",
+ "@vitest/spy": "3.2.4",
+ "@vitest/utils": "3.2.4",
"chai": "^5.2.0",
"tinyrainbow": "^2.0.0"
},
@@ -1007,13 +1007,13 @@
}
},
"node_modules/@vitest/mocker": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.3.tgz",
- "integrity": "sha512-cP6fIun+Zx8he4rbWvi+Oya6goKQDZK+Yq4hhlggwQBbrlOQ4qtZ+G4nxB6ZnzI9lyIb+JnvyiJnPC2AGbKSPA==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz",
+ "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/spy": "3.2.3",
+ "@vitest/spy": "3.2.4",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.17"
},
@@ -1034,9 +1034,9 @@
}
},
"node_modules/@vitest/pretty-format": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.3.tgz",
- "integrity": "sha512-yFglXGkr9hW/yEXngO+IKMhP0jxyFw2/qys/CK4fFUZnSltD+MU7dVYGrH8rvPcK/O6feXQA+EU33gjaBBbAng==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz",
+ "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1047,13 +1047,13 @@
}
},
"node_modules/@vitest/runner": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.3.tgz",
- "integrity": "sha512-83HWYisT3IpMaU9LN+VN+/nLHVBCSIUKJzGxC5RWUOsK1h3USg7ojL+UXQR3b4o4UBIWCYdD2fxuzM7PQQ1u8w==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz",
+ "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/utils": "3.2.3",
+ "@vitest/utils": "3.2.4",
"pathe": "^2.0.3",
"strip-literal": "^3.0.0"
},
@@ -1062,13 +1062,13 @@
}
},
"node_modules/@vitest/snapshot": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.3.tgz",
- "integrity": "sha512-9gIVWx2+tysDqUmmM1L0hwadyumqssOL1r8KJipwLx5JVYyxvVRfxvMq7DaWbZZsCqZnu/dZedaZQh4iYTtneA==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz",
+ "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "3.2.3",
+ "@vitest/pretty-format": "3.2.4",
"magic-string": "^0.30.17",
"pathe": "^2.0.3"
},
@@ -1077,9 +1077,9 @@
}
},
"node_modules/@vitest/spy": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.3.tgz",
- "integrity": "sha512-JHu9Wl+7bf6FEejTCREy+DmgWe+rQKbK+y32C/k5f4TBIAlijhJbRBIRIOCEpVevgRsCQR2iHRUH2/qKVM/plw==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz",
+ "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1090,14 +1090,14 @@
}
},
"node_modules/@vitest/utils": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.3.tgz",
- "integrity": "sha512-4zFBCU5Pf+4Z6v+rwnZ1HU1yzOKKvDkMXZrymE2PBlbjKJRlrOxbvpfPSvJTGRIwGoahaOGvp+kbCoxifhzJ1Q==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz",
+ "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "3.2.3",
- "loupe": "^3.1.3",
+ "@vitest/pretty-format": "3.2.4",
+ "loupe": "^3.1.4",
"tinyrainbow": "^2.0.0"
},
"funding": {
@@ -1607,9 +1607,9 @@
}
},
"node_modules/loupe": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz",
- "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz",
+ "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==",
"dev": true,
"license": "MIT"
},
@@ -2672,9 +2672,9 @@
}
},
"node_modules/tinypool": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.0.tgz",
- "integrity": "sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
+ "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2712,9 +2712,9 @@
}
},
"node_modules/typescript": {
- "version": "5.8.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
- "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "version": "5.9.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
+ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -2912,9 +2912,9 @@
}
},
"node_modules/vite-node": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.3.tgz",
- "integrity": "sha512-gc8aAifGuDIpZHrPjuHyP4dpQmYXqWw7D1GmDnWeNWP654UEXzVfQ5IHPSK5HaHkwB/+p1atpYpSdw/2kOv8iQ==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz",
+ "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2935,20 +2935,20 @@
}
},
"node_modules/vitest": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.3.tgz",
- "integrity": "sha512-E6U2ZFXe3N/t4f5BwUaVCKRLHqUpk1CBWeMh78UT4VaTPH/2dyvH6ALl29JTovEPu9dVKr/K/J4PkXgrMbw4Ww==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz",
+ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/chai": "^5.2.2",
- "@vitest/expect": "3.2.3",
- "@vitest/mocker": "3.2.3",
- "@vitest/pretty-format": "^3.2.3",
- "@vitest/runner": "3.2.3",
- "@vitest/snapshot": "3.2.3",
- "@vitest/spy": "3.2.3",
- "@vitest/utils": "3.2.3",
+ "@vitest/expect": "3.2.4",
+ "@vitest/mocker": "3.2.4",
+ "@vitest/pretty-format": "^3.2.4",
+ "@vitest/runner": "3.2.4",
+ "@vitest/snapshot": "3.2.4",
+ "@vitest/spy": "3.2.4",
+ "@vitest/utils": "3.2.4",
"chai": "^5.2.0",
"debug": "^4.4.1",
"expect-type": "^1.2.1",
@@ -2959,10 +2959,10 @@
"tinybench": "^2.9.0",
"tinyexec": "^0.3.2",
"tinyglobby": "^0.2.14",
- "tinypool": "^1.1.0",
+ "tinypool": "^1.1.1",
"tinyrainbow": "^2.0.0",
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0",
- "vite-node": "3.2.3",
+ "vite-node": "3.2.4",
"why-is-node-running": "^2.3.0"
},
"bin": {
@@ -2978,8 +2978,8 @@
"@edge-runtime/vm": "*",
"@types/debug": "^4.1.12",
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
- "@vitest/browser": "3.2.3",
- "@vitest/ui": "3.2.3",
+ "@vitest/browser": "3.2.4",
+ "@vitest/ui": "3.2.4",
"happy-dom": "*",
"jsdom": "*"
},
diff --git a/scripts/changelog-checker/package.json b/scripts/changelog-checker/package.json
index fa6cf09ea1..b02ca46cb9 100644
--- a/scripts/changelog-checker/package.json
+++ b/scripts/changelog-checker/package.json
@@ -10,15 +10,15 @@
},
"devDependencies": {
"@types/mdast": "4.0.4",
- "@types/node": "22.15.31",
- "@vitest/coverage-v8": "3.2.3",
+ "@types/node": "22.17.1",
+ "@vitest/coverage-v8": "3.2.4",
"mdast-util-to-string": "4.0.0",
"remark": "15.0.1",
"remark-parse": "11.0.0",
- "typescript": "5.8.3",
+ "typescript": "5.9.2",
"unified": "11.0.5",
"vite": "6.3.5",
- "vite-node": "3.2.3",
- "vitest": "3.2.3"
+ "vite-node": "3.2.4",
+ "vitest": "3.2.4"
}
}
diff --git a/scripts/clean-all.js b/scripts/clean-all.js
index dc391ecfd8..5a8f9eba23 100644
--- a/scripts/clean-all.js
+++ b/scripts/clean-all.js
@@ -13,6 +13,8 @@ const fs = require('fs');
fs.rmSync(__dirname + '/../packages/frontend-shared/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/frontend-shared/node_modules', { recursive: true, force: true });
+ fs.rmSync(__dirname + '/../packages/frontend-builder/node_modules', { recursive: true, force: true });
+
fs.rmSync(__dirname + '/../packages/frontend/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/frontend/node_modules', { recursive: true, force: true });