summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2023-05-09 09:17:34 +0900
committerGitHub <noreply@github.com>2023-05-09 09:17:34 +0900
commit94690c835e3179e3fd616758ad00a8b66d844a0a (patch)
tree3171356ca8298aa6caae7c95df7232844163f913
parentMerge pull request #10608 from misskey-dev/develop (diff)
parent[ci skip] 13.12.0 (diff)
downloadmisskey-94690c835e3179e3fd616758ad00a8b66d844a0a.tar.gz
misskey-94690c835e3179e3fd616758ad00a8b66d844a0a.tar.bz2
misskey-94690c835e3179e3fd616758ad00a8b66d844a0a.zip
Merge pull request #10774 from misskey-dev/develop
Release: 13.12.0
-rw-r--r--.config/docker_example.yml15
-rw-r--r--.config/example.yml29
-rw-r--r--.devcontainer/devcontainer.json5
-rw-r--r--.devcontainer/devcontainer.yml15
-rw-r--r--.dockerignore1
-rw-r--r--.github/ISSUE_TEMPLATE/01_bug-report.md3
-rw-r--r--.github/labeler.yml21
-rw-r--r--.github/pull_request_template.md1
-rw-r--r--.github/workflows/api-misskey-js.yml2
-rw-r--r--.github/workflows/lint.yml8
-rw-r--r--.github/workflows/storybook.yml40
-rw-r--r--.github/workflows/test-backend.yml2
-rw-r--r--.github/workflows/test-frontend.yml4
-rw-r--r--.github/workflows/test-production.yml42
-rw-r--r--.gitignore2
-rw-r--r--.node-version2
-rw-r--r--.vscode/settings.json1
-rw-r--r--CHANGELOG.md60
-rw-r--r--CONTRIBUTING.md6
-rw-r--r--Dockerfile2
-rw-r--r--chart/files/default.yml13
-rw-r--r--cypress/e2e/basic.cy.js57
-rw-r--r--cypress/e2e/widgets.cy.js18
-rw-r--r--docker-compose.yml.example14
-rw-r--r--locales/ar-SA.yml27
-rw-r--r--locales/bn-BD.yml27
-rw-r--r--locales/ca-ES.yml2
-rw-r--r--locales/cs-CZ.yml6
-rw-r--r--locales/de-DE.yml134
-rw-r--r--locales/el-GR.yml2
-rw-r--r--locales/en-US.yml126
-rw-r--r--locales/es-ES.yml37
-rw-r--r--locales/fr-FR.yml76
-rw-r--r--locales/id-ID.yml30
-rw-r--r--locales/it-IT.yml72
-rw-r--r--locales/ja-JP.yml131
-rw-r--r--locales/ja-KS.yml165
-rw-r--r--locales/ko-KR.yml170
-rw-r--r--locales/lo-LA.yml2
-rw-r--r--locales/nl-NL.yml2
-rw-r--r--locales/no-NO.yml481
-rw-r--r--locales/pl-PL.yml29
-rw-r--r--locales/pt-PT.yml2
-rw-r--r--locales/ro-RO.yml2
-rw-r--r--locales/ru-RU.yml84
-rw-r--r--locales/sk-SK.yml30
-rw-r--r--locales/sv-SE.yml2
-rw-r--r--locales/th-TH.yml98
-rw-r--r--locales/uk-UA.yml29
-rw-r--r--locales/vi-VN.yml29
-rw-r--r--locales/zh-CN.yml91
-rw-r--r--locales/zh-TW.yml161
-rw-r--r--package.json16
-rw-r--r--packages/backend/migration/1680702787050-UserMemo.js18
-rw-r--r--packages/backend/migration/1681400427971-serverRules.js11
-rw-r--r--packages/backend/migration/1681870960239-RoleTLSetting.js12
-rw-r--r--packages/backend/migration/1682190963894-movedAt.js13
-rw-r--r--packages/backend/migration/1682754135458-preservedUsernames.js11
-rw-r--r--packages/backend/migration/1682985520254-channelColor.js11
-rw-r--r--packages/backend/migration/1683328299359-channelArchive.js13
-rw-r--r--packages/backend/package.json100
-rw-r--r--packages/backend/src/GlobalModule.ts28
-rw-r--r--packages/backend/src/boot/common.ts2
-rw-r--r--packages/backend/src/config.ts20
-rw-r--r--packages/backend/src/const.ts5
-rw-r--r--packages/backend/src/core/AccountMoveService.ts327
-rw-r--r--packages/backend/src/core/AchievementService.ts1
-rw-r--r--packages/backend/src/core/AntennaService.ts2
-rw-r--r--packages/backend/src/core/AppLockService.ts2
-rw-r--r--packages/backend/src/core/CacheService.ts2
-rw-r--r--packages/backend/src/core/CoreModule.ts7
-rw-r--r--packages/backend/src/core/CustomEmojiService.ts18
-rw-r--r--packages/backend/src/core/DownloadService.ts10
-rw-r--r--packages/backend/src/core/DriveService.ts46
-rw-r--r--packages/backend/src/core/FederatedInstanceService.ts27
-rw-r--r--packages/backend/src/core/FetchInstanceMetadataService.ts4
-rw-r--r--packages/backend/src/core/FileInfoService.ts25
-rw-r--r--packages/backend/src/core/GlobalEventService.ts2
-rw-r--r--packages/backend/src/core/MetaService.ts2
-rw-r--r--packages/backend/src/core/NoteCreateService.ts18
-rw-r--r--packages/backend/src/core/NotificationService.ts4
-rw-r--r--packages/backend/src/core/PushNotificationService.ts2
-rw-r--r--packages/backend/src/core/QueueModule.ts2
-rw-r--r--packages/backend/src/core/QueueService.ts33
-rw-r--r--packages/backend/src/core/RemoteUserResolveService.ts10
-rw-r--r--packages/backend/src/core/RoleService.ts5
-rw-r--r--packages/backend/src/core/SearchService.ts178
-rw-r--r--packages/backend/src/core/SignupService.ts23
-rw-r--r--packages/backend/src/core/UserFollowingService.ts207
-rw-r--r--packages/backend/src/core/UserKeypairService.ts2
-rw-r--r--packages/backend/src/core/UserSuspendService.ts4
-rw-r--r--packages/backend/src/core/WebfingerService.ts3
-rw-r--r--packages/backend/src/core/WebhookService.ts2
-rw-r--r--packages/backend/src/core/activitypub/ApDbResolverService.ts8
-rw-r--r--packages/backend/src/core/activitypub/ApInboxService.ts60
-rw-r--r--packages/backend/src/core/activitypub/ApRendererService.ts66
-rw-r--r--packages/backend/src/core/activitypub/ApResolverService.ts4
-rw-r--r--packages/backend/src/core/activitypub/models/ApImageService.ts5
-rw-r--r--packages/backend/src/core/activitypub/models/ApNoteService.ts16
-rw-r--r--packages/backend/src/core/activitypub/models/ApPersonService.ts145
-rw-r--r--packages/backend/src/core/entities/ChannelEntityService.ts2
-rw-r--r--packages/backend/src/core/entities/NoteEntityService.ts1
-rw-r--r--packages/backend/src/core/entities/NotificationEntityService.ts14
-rw-r--r--packages/backend/src/core/entities/RoleEntityService.ts1
-rw-r--r--packages/backend/src/core/entities/UserEntityService.ts58
-rw-r--r--packages/backend/src/daemons/ServerStatsService.ts2
-rw-r--r--packages/backend/src/di-symbols.ts2
-rw-r--r--packages/backend/src/misc/cache.ts6
-rw-r--r--packages/backend/src/misc/check-https.ts4
-rw-r--r--packages/backend/src/misc/check-word-mute.ts20
-rw-r--r--packages/backend/src/models/RepositoryModule.ts10
-rw-r--r--packages/backend/src/models/entities/Channel.ts12
-rw-r--r--packages/backend/src/models/entities/Meta.ts12
-rw-r--r--packages/backend/src/models/entities/Role.ts5
-rw-r--r--packages/backend/src/models/entities/User.ts18
-rw-r--r--packages/backend/src/models/entities/UserMemo.ts42
-rw-r--r--packages/backend/src/models/index.ts3
-rw-r--r--packages/backend/src/models/json-schema/channel.ts8
-rw-r--r--packages/backend/src/models/json-schema/user.ts13
-rw-r--r--packages/backend/src/postgres.ts2
-rw-r--r--packages/backend/src/queue/DbQueueProcessorsService.ts6
-rw-r--r--packages/backend/src/queue/QueueProcessorModule.ts4
-rw-r--r--packages/backend/src/queue/RelationshipQueueProcessorsService.ts2
-rw-r--r--packages/backend/src/queue/processors/DeliverProcessorService.ts15
-rw-r--r--packages/backend/src/queue/processors/ExportAntennasProcessorService.ts103
-rw-r--r--packages/backend/src/queue/processors/ExportBlockingProcessorService.ts2
-rw-r--r--packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts2
-rw-r--r--packages/backend/src/queue/processors/ExportFollowingProcessorService.ts2
-rw-r--r--packages/backend/src/queue/processors/ExportMutingProcessorService.ts2
-rw-r--r--packages/backend/src/queue/processors/ExportNotesProcessorService.ts2
-rw-r--r--packages/backend/src/queue/processors/ExportUserListsProcessorService.ts2
-rw-r--r--packages/backend/src/queue/processors/ImportAntennasProcessorService.ts96
-rw-r--r--packages/backend/src/queue/processors/InboxProcessorService.ts11
-rw-r--r--packages/backend/src/queue/processors/RelationshipProcessorService.ts3
-rw-r--r--packages/backend/src/queue/types.ts12
-rw-r--r--packages/backend/src/server/ActivityPubServerService.ts6
-rw-r--r--packages/backend/src/server/FileServerService.ts6
-rw-r--r--packages/backend/src/server/WellKnownServerService.ts4
-rw-r--r--packages/backend/src/server/api/ApiCallService.ts11
-rw-r--r--packages/backend/src/server/api/EndpointsModule.ts20
-rw-r--r--packages/backend/src/server/api/RateLimiterService.ts2
-rw-r--r--packages/backend/src/server/api/SignupApiService.ts7
-rw-r--r--packages/backend/src/server/api/StreamingApiServerService.ts2
-rw-r--r--packages/backend/src/server/api/endpoints.ts18
-rw-r--r--packages/backend/src/server/api/endpoints/admin/accounts/create.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/admin/emoji/list.ts16
-rw-r--r--packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts37
-rw-r--r--packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/admin/meta.ts9
-rw-r--r--packages/backend/src/server/api/endpoints/admin/roles/create.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/admin/roles/update.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/server-info.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/show-user.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/update-meta.ts10
-rw-r--r--packages/backend/src/server/api/endpoints/antennas/create.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/antennas/notes.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/antennas/update.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/channels/create.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/channels/favorite.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/channels/featured.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/channels/follow.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/channels/owned.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/channels/search.ts19
-rw-r--r--packages/backend/src/server/api/endpoints/channels/timeline.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/channels/unfavorite.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/channels/unfollow.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/channels/update.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/clips/add-note.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/clips/create.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/clips/favorite.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/clips/remove-note.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/clips/unfavorite.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/clips/update.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/drive/files/create.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/drive/files/update.ts13
-rw-r--r--packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/flash/create.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/flash/like.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/flash/unlike.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/flash/update.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/following/create.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/gallery/posts/create.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/gallery/posts/like.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/gallery/posts/update.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/hashtags/users.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/i/claim-achievement.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/i/export-antennas.ts30
-rw-r--r--packages/backend/src/server/api/endpoints/i/import-antennas.ts84
-rw-r--r--packages/backend/src/server/api/endpoints/i/import-blocking.ts11
-rw-r--r--packages/backend/src/server/api/endpoints/i/import-following.ts11
-rw-r--r--packages/backend/src/server/api/endpoints/i/import-muting.ts11
-rw-r--r--packages/backend/src/server/api/endpoints/i/import-user-lists.ts11
-rw-r--r--packages/backend/src/server/api/endpoints/i/known-as.ts92
-rw-r--r--packages/backend/src/server/api/endpoints/i/move.ts70
-rw-r--r--packages/backend/src/server/api/endpoints/i/notifications.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/i/pin.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/i/update.ts85
-rw-r--r--packages/backend/src/server/api/endpoints/meta.ts7
-rw-r--r--packages/backend/src/server/api/endpoints/mute/create.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/notes/create.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/notes/favorites/create.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/notes/polls/vote.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/notes/reactions/create.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/notes/search.ts41
-rw-r--r--packages/backend/src/server/api/endpoints/pages/create.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/pages/like.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/pages/unlike.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/pages/update.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/renote-mute/create.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/reset-db.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/roles/notes.ts7
-rw-r--r--packages/backend/src/server/api/endpoints/username/available.ts9
-rw-r--r--packages/backend/src/server/api/endpoints/users.ts5
-rw-r--r--packages/backend/src/server/api/endpoints/users/lists/create.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/users/lists/pull.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/lists/push.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/update-memo.ts85
-rw-r--r--packages/backend/src/server/web/UrlPreviewService.ts4
-rw-r--r--packages/backend/src/server/web/views/base.pug2
-rw-r--r--packages/backend/test/e2e/endpoints.ts198
-rw-r--r--packages/backend/test/e2e/move.ts456
-rw-r--r--packages/backend/test/e2e/note.ts66
-rw-r--r--packages/backend/test/e2e/users.ts71
-rw-r--r--packages/backend/test/resources/kick_gaba7.aacbin0 -> 7291 bytes
-rw-r--r--packages/backend/test/resources/kick_gaba7.flacbin0 -> 108793 bytes
-rw-r--r--packages/backend/test/resources/kick_gaba7.mp3bin0 -> 19853 bytes
-rw-r--r--packages/backend/test/resources/kick_gaba7.wavbin0 -> 87630 bytes
-rw-r--r--packages/backend/test/resources/kick_gaba7.webmbin0 -> 8879 bytes
-rw-r--r--packages/backend/test/unit/FileInfoService.ts409
-rw-r--r--packages/backend/test/unit/RelayService.ts10
-rw-r--r--packages/backend/test/unit/misc/check-word-mute.ts49
-rw-r--r--packages/frontend/.storybook/changes.ts1
-rw-r--r--packages/frontend/.storybook/generate.tsx15
-rw-r--r--packages/frontend/.storybook/main.ts6
-rw-r--r--packages/frontend/.storybook/mocks.ts10
-rw-r--r--packages/frontend/.storybook/preview-head.html2
-rw-r--r--packages/frontend/.storybook/preview.ts36
-rw-r--r--packages/frontend/.vscode/storybook.code-snippets84
-rw-r--r--packages/frontend/package.json91
-rw-r--r--packages/frontend/src/components/MkAbuseReport.stories.impl.ts49
-rw-r--r--packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts49
-rw-r--r--packages/frontend/src/components/MkAccountMoved.stories.impl.ts33
-rw-r--r--packages/frontend/src/components/MkAccountMoved.vue16
-rw-r--r--packages/frontend/src/components/MkAchievements.stories.impl.ts56
-rw-r--r--packages/frontend/src/components/MkAnalogClock.stories.impl.ts9
-rw-r--r--packages/frontend/src/components/MkAnalogClock.vue12
-rw-r--r--packages/frontend/src/components/MkAsUi.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkAutocomplete.stories.impl.ts176
-rw-r--r--packages/frontend/src/components/MkAvatars.stories.impl.ts46
-rw-r--r--packages/frontend/src/components/MkButton.stories.impl.ts53
-rw-r--r--packages/frontend/src/components/MkColorInput.vue110
-rw-r--r--packages/frontend/src/components/MkContainer.vue169
-rw-r--r--packages/frontend/src/components/MkDialog.vue8
-rw-r--r--packages/frontend/src/components/MkFolder.vue10
-rw-r--r--packages/frontend/src/components/MkFollowButton.vue2
-rw-r--r--packages/frontend/src/components/MkGalleryPostPreview.stories.impl.ts8
-rw-r--r--packages/frontend/src/components/MkGalleryPostPreview.vue52
-rw-r--r--packages/frontend/src/components/MkImgWithBlurhash.vue100
-rw-r--r--packages/frontend/src/components/MkInfo.vue1
-rw-r--r--packages/frontend/src/components/MkInput.vue194
-rw-r--r--packages/frontend/src/components/MkMediaImage.vue50
-rw-r--r--packages/frontend/src/components/MkMediaList.vue17
-rw-r--r--packages/frontend/src/components/MkMediaVideo.vue7
-rw-r--r--packages/frontend/src/components/MkModal.vue6
-rw-r--r--packages/frontend/src/components/MkModalWindow.vue9
-rw-r--r--packages/frontend/src/components/MkNote.vue34
-rw-r--r--packages/frontend/src/components/MkNoteDetailed.vue5
-rw-r--r--packages/frontend/src/components/MkNumberDiff.vue51
-rw-r--r--packages/frontend/src/components/MkOmit.vue2
-rw-r--r--packages/frontend/src/components/MkPostForm.vue13
-rw-r--r--packages/frontend/src/components/MkRadio.vue2
-rw-r--r--packages/frontend/src/components/MkRadios.vue14
-rw-r--r--packages/frontend/src/components/MkRange.vue6
-rw-r--r--packages/frontend/src/components/MkReactedUsersDialog.vue4
-rw-r--r--packages/frontend/src/components/MkReactionsViewer.details.vue19
-rw-r--r--packages/frontend/src/components/MkRenotedUsersDialog.vue65
-rw-r--r--packages/frontend/src/components/MkRetentionHeatmap.vue26
-rw-r--r--packages/frontend/src/components/MkSample.vue2
-rw-r--r--packages/frontend/src/components/MkSignup.vue263
-rw-r--r--packages/frontend/src/components/MkSignupDialog.form.vue272
-rw-r--r--packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts94
-rw-r--r--packages/frontend/src/components/MkSignupDialog.rules.vue124
-rw-r--r--packages/frontend/src/components/MkSignupDialog.vue45
-rw-r--r--packages/frontend/src/components/MkSwitch.vue2
-rw-r--r--packages/frontend/src/components/MkUserInfo.vue196
-rw-r--r--packages/frontend/src/components/MkUserList.vue6
-rw-r--r--packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts51
-rw-r--r--packages/frontend/src/components/MkUserSetupDialog.Follow.vue63
-rw-r--r--packages/frontend/src/components/MkUserSetupDialog.Profile.stories.impl.ts31
-rw-r--r--packages/frontend/src/components/MkUserSetupDialog.Profile.vue101
-rw-r--r--packages/frontend/src/components/MkUserSetupDialog.User.stories.impl.ts32
-rw-r--r--packages/frontend/src/components/MkUserSetupDialog.User.vue101
-rw-r--r--packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts51
-rw-r--r--packages/frontend/src/components/MkUserSetupDialog.vue145
-rw-r--r--packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue157
-rw-r--r--packages/frontend/src/components/MkVisitorDashboard.vue227
-rw-r--r--packages/frontend/src/components/MkWidgets.vue4
-rw-r--r--packages/frontend/src/components/MkWindow.vue4
-rw-r--r--packages/frontend/src/components/global/MkAcct.stories.impl.ts32
-rw-r--r--packages/frontend/src/components/global/MkAcct.vue7
-rw-r--r--packages/frontend/src/components/global/MkAvatar.vue4
-rw-r--r--packages/frontend/src/components/global/MkCondensedLine.stories.impl.ts39
-rw-r--r--packages/frontend/src/components/global/MkCondensedLine.vue65
-rw-r--r--packages/frontend/src/components/global/MkError.stories.impl.ts10
-rw-r--r--packages/frontend/src/components/global/MkPageHeader.vue2
-rw-r--r--packages/frontend/src/components/global/MkTime.vue3
-rw-r--r--packages/frontend/src/components/index.ts3
-rw-r--r--packages/frontend/src/config.ts11
-rw-r--r--packages/frontend/src/const.ts6
-rw-r--r--packages/frontend/src/directives/container.ts21
-rw-r--r--packages/frontend/src/directives/index.ts2
-rw-r--r--packages/frontend/src/init.ts61
-rw-r--r--packages/frontend/src/local-storage.ts7
-rw-r--r--packages/frontend/src/os.ts14
-rw-r--r--packages/frontend/src/pages/about-misskey.vue19
-rw-r--r--packages/frontend/src/pages/about.emojis.vue10
-rw-r--r--packages/frontend/src/pages/about.vue84
-rw-r--r--packages/frontend/src/pages/admin/email-settings.vue4
-rw-r--r--packages/frontend/src/pages/admin/moderation.vue45
-rw-r--r--packages/frontend/src/pages/admin/roles.edit.vue1
-rw-r--r--packages/frontend/src/pages/admin/roles.editor.vue34
-rw-r--r--packages/frontend/src/pages/admin/roles.vue8
-rw-r--r--packages/frontend/src/pages/admin/server-rules.vue128
-rw-r--r--packages/frontend/src/pages/admin/settings.vue29
-rw-r--r--packages/frontend/src/pages/channel-editor.vue28
-rw-r--r--packages/frontend/src/pages/channel.vue38
-rw-r--r--packages/frontend/src/pages/channels.vue2
-rw-r--r--packages/frontend/src/pages/custom-emojis-manager.vue15
-rw-r--r--packages/frontend/src/pages/flash/flash-edit.vue19
-rw-r--r--packages/frontend/src/pages/gallery/edit.vue12
-rw-r--r--packages/frontend/src/pages/my-lists/list.vue2
-rw-r--r--packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue4
-rw-r--r--packages/frontend/src/pages/role.vue42
-rw-r--r--packages/frontend/src/pages/settings/account-stats.vue (renamed from packages/frontend/src/pages/settings/account-info.vue)14
-rw-r--r--packages/frontend/src/pages/settings/delete-account.vue52
-rw-r--r--packages/frontend/src/pages/settings/drive.vue7
-rw-r--r--packages/frontend/src/pages/settings/general.vue120
-rw-r--r--packages/frontend/src/pages/settings/import-export.vue33
-rw-r--r--packages/frontend/src/pages/settings/index.vue6
-rw-r--r--packages/frontend/src/pages/settings/migration.vue118
-rw-r--r--packages/frontend/src/pages/settings/other.vue107
-rw-r--r--packages/frontend/src/pages/settings/preferences-backups.vue1
-rw-r--r--packages/frontend/src/pages/settings/profile.vue116
-rw-r--r--packages/frontend/src/pages/timeline.tutorial.vue69
-rw-r--r--packages/frontend/src/pages/timeline.vue2
-rw-r--r--packages/frontend/src/pages/user/home.vue118
-rw-r--r--packages/frontend/src/pages/welcome.entrance.a.vue156
-rw-r--r--packages/frontend/src/pages/welcome.entrance.b.vue239
-rw-r--r--packages/frontend/src/pages/welcome.entrance.c.vue308
-rw-r--r--packages/frontend/src/pages/welcome.setup.vue57
-rw-r--r--packages/frontend/src/pages/welcome.timeline.vue2
-rw-r--r--packages/frontend/src/router.ts14
-rw-r--r--packages/frontend/src/scripts/achievements.ts7
-rw-r--r--packages/frontend/src/scripts/aiscript/api.ts1
-rw-r--r--packages/frontend/src/scripts/get-drive-file-menu.ts12
-rw-r--r--packages/frontend/src/scripts/get-note-menu.ts14
-rw-r--r--packages/frontend/src/scripts/get-user-menu.ts27
-rw-r--r--packages/frontend/src/scripts/please-login.ts2
-rw-r--r--packages/frontend/src/scripts/select-file.ts120
-rw-r--r--packages/frontend/src/scripts/show-moved-dialog.ts16
-rw-r--r--packages/frontend/src/scripts/upload.ts8
-rw-r--r--packages/frontend/src/store.ts30
-rw-r--r--packages/frontend/src/themes/d-botanical.json53
-rw-r--r--packages/frontend/src/themes/d-dark.json53
-rw-r--r--packages/frontend/src/themes/d-future.json53
-rw-r--r--packages/frontend/src/themes/d-green-lime.json53
-rw-r--r--packages/frontend/src/themes/d-green-orange.json53
-rw-r--r--packages/frontend/src/ui/_common_/common.ts2
-rw-r--r--packages/frontend/src/ui/_common_/common.vue108
-rw-r--r--packages/frontend/src/ui/deck/channel-column.vue2
-rw-r--r--packages/frontend/src/ui/deck/column.vue11
-rw-r--r--packages/frontend/src/ui/universal.vue2
-rw-r--r--packages/frontend/src/ui/universal.widgets.vue2
-rw-r--r--packages/frontend/src/ui/visitor.vue289
-rw-r--r--packages/frontend/src/ui/visitor/a.vue263
-rw-r--r--packages/frontend/src/ui/visitor/b.vue266
-rw-r--r--packages/frontend/src/ui/visitor/header.vue211
-rw-r--r--packages/frontend/src/ui/visitor/kanban.vue261
-rw-r--r--packages/frontend/tsconfig.json2
-rw-r--r--packages/frontend/vite.config.ts18
-rw-r--r--packages/misskey-js/etc/misskey-js.api.md10
-rw-r--r--packages/misskey-js/package.json20
-rw-r--r--packages/misskey-js/src/api.types.ts2
-rw-r--r--packages/misskey-js/src/entities.ts5
-rw-r--r--packages/sw/package.json8
-rw-r--r--packages/sw/src/sw.ts4
-rw-r--r--pnpm-lock.yaml4410
389 files changed, 11991 insertions, 6774 deletions
diff --git a/.config/docker_example.yml b/.config/docker_example.yml
index af0a90dc95..39682e1542 100644
--- a/.config/docker_example.yml
+++ b/.config/docker_example.yml
@@ -95,15 +95,14 @@ redis:
# #prefix: example-prefix
# #db: 1
-# ┌─────────────────────────────â”
-#───┘ Elasticsearch configuration └─────────────────────────────
+# ┌───────────────────────────â”
+#───┘ MeiliSearch configuration └─────────────────────────────
-#elasticsearch:
-# host: localhost
-# port: 9200
-# ssl: false
-# user:
-# pass:
+#meilisearch:
+# host: meilisearch
+# port: 7700
+# apiKey: ''
+# ssl: true
# ┌───────────────â”
#───┘ ID generation └───────────────────────────────────────────
diff --git a/.config/example.yml b/.config/example.yml
index 57e2b56b78..ef8373c4da 100644
--- a/.config/example.yml
+++ b/.config/example.yml
@@ -95,15 +95,14 @@ redis:
# #prefix: example-prefix
# #db: 1
-# ┌─────────────────────────────â”
-#───┘ Elasticsearch configuration └─────────────────────────────
+# ┌───────────────────────────â”
+#───┘ MeiliSearch configuration └─────────────────────────────
-#elasticsearch:
+#meilisearch:
# host: localhost
-# port: 9200
-# ssl: false
-# user:
-# pass:
+# port: 7700
+# apiKey: ''
+# ssl: true
# ┌───────────────â”
#───┘ ID generation └───────────────────────────────────────────
@@ -133,16 +132,20 @@ id: 'aid'
#clusterLimit: 1
# Job concurrency per worker
-# deliverJobConcurrency: 128
-# inboxJobConcurrency: 16
+#deliverJobConcurrency: 128
+#inboxJobConcurrency: 16
+#relashionshipJobConcurrency: 16
+# What's relashionshipJob?:
+# Follow, unfollow, block and unblock(ings) while following-imports, etc. or account migrations.
# Job rate limiter
-# deliverJobPerSec: 128
-# inboxJobPerSec: 16
+#deliverJobPerSec: 128
+#inboxJobPerSec: 16
+#relashionshipJobPerSec: 64
# Job attempts
-# deliverJobMaxAttempts: 12
-# inboxJobMaxAttempts: 8
+#deliverJobMaxAttempts: 12
+#inboxJobMaxAttempts: 8
# IP address family used for outgoing request (ipv4, ipv6 or dual)
#outgoingAddressFamily: ipv4
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 6accd43476..a47804ab07 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -4,7 +4,10 @@
"service": "app",
"workspaceFolder": "/workspace",
"features": {
- "ghcr.io/devcontainers-contrib/features/pnpm:2": {}
+ "ghcr.io/devcontainers-contrib/features/pnpm:2": {},
+ "ghcr.io/devcontainers/features/node:1": {
+ "version": "18.16.0"
+ }
},
"forwardPorts": [3000],
"postCreateCommand": "sudo chmod 755 .devcontainer/init.sh && .devcontainer/init.sh",
diff --git a/.devcontainer/devcontainer.yml b/.devcontainer/devcontainer.yml
index 2af306e3da..1d761ae75e 100644
--- a/.devcontainer/devcontainer.yml
+++ b/.devcontainer/devcontainer.yml
@@ -95,15 +95,14 @@ redis:
# #prefix: example-prefix
# #db: 1
-# ┌─────────────────────────────â”
-#───┘ Elasticsearch configuration └─────────────────────────────
+# ┌───────────────────────────â”
+#───┘ MeiliSearch configuration └─────────────────────────────
-#elasticsearch:
-# host: localhost
-# port: 9200
-# ssl: false
-# user:
-# pass:
+#meilisearch:
+# host: meilisearch
+# port: 7700
+# apiKey: ''
+# ssl: true
# ┌───────────────â”
#───┘ ID generation └───────────────────────────────────────────
diff --git a/.dockerignore b/.dockerignore
index 151ede038e..1de0c7982b 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -8,7 +8,6 @@ build/
built/
db/
docker-compose.yml
-elasticsearch/
node_modules/
packages/*/node_modules
redis/
diff --git a/.github/ISSUE_TEMPLATE/01_bug-report.md b/.github/ISSUE_TEMPLATE/01_bug-report.md
index b4a2f69879..f6fd593c85 100644
--- a/.github/ISSUE_TEMPLATE/01_bug-report.md
+++ b/.github/ISSUE_TEMPLATE/01_bug-report.md
@@ -37,7 +37,10 @@ Please include errors from the developer console and/or server log files if you
## 📌 Environment
<!-- Tell us where on the platform it happens -->
+<!-- DO NOT WRITE "latest". Please provide the specific version. -->
Misskey version:
+PostgreSQL version:
+Redis version:
Your OS:
Your browser:
diff --git a/.github/labeler.yml b/.github/labeler.yml
index b4fd0dd5df..137be487c0 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -1,12 +1,21 @@
-'âš™ï¸Server':
+'packages/backend':
- packages/backend/**/*
-'🖥ï¸Client':
+'packages/backend:test':
+- packages/backend/test/**/*
+
+'packages/frontend':
- packages/frontend/**/*
-'🧪Test':
+'packages/frontend:test':
- cypress/**/*
-- packages/backend/test/**/*
-'â€¼ï¸ wrong locales':
-- any: ['locales/*.yml', '!locales/ja-JP.yml']
+'packages/sw':
+- packages/sw/**/*
+
+'packages/misskey-js':
+- packages/misskey-js/**/*
+
+'packages/misskey-js:test':
+- packages/misskey-js/test/**/*
+- packages/misskey-js/test-d/**/*
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 0739fee709..e78b82c47c 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -19,5 +19,6 @@ https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md
## Checklist
- [ ] Read the [contribution guide](https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md)
- [ ] Test working in a local environment
+- [ ] (If needed) Add story of storybook
- [ ] (If needed) Update CHANGELOG.md
- [ ] (If possible) Add tests
diff --git a/.github/workflows/api-misskey-js.yml b/.github/workflows/api-misskey-js.yml
index 6411d63bd5..ed004c78dc 100644
--- a/.github/workflows/api-misskey-js.yml
+++ b/.github/workflows/api-misskey-js.yml
@@ -16,7 +16,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v3.6.0
with:
- node-version: 18.x
+ node-version-file: '.node-version'
cache: 'pnpm'
- name: Install dependencies
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 1c6615e17f..0f3702f958 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -17,11 +17,11 @@ jobs:
submodules: true
- uses: pnpm/action-setup@v2
with:
- version: 7
+ version: 8
run_install: false
- uses: actions/setup-node@v3.6.0
with:
- node-version: 18.x
+ node-version-file: '.node-version'
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
@@ -48,7 +48,7 @@ jobs:
run_install: false
- uses: actions/setup-node@v3.6.0
with:
- node-version: 18.x
+ node-version-file: '.node-version'
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
@@ -74,7 +74,7 @@ jobs:
run_install: false
- uses: actions/setup-node@v3.6.0
with:
- node-version: 18.x
+ node-version-file: '.node-version'
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml
index 6792674d9f..eb6ace27da 100644
--- a/.github/workflows/storybook.yml
+++ b/.github/workflows/storybook.yml
@@ -2,8 +2,10 @@ name: Storybook
on:
push:
- branches-ignore:
- - l10n_develop
+ branches:
+ - master
+ - develop
+ pull_request_target:
jobs:
build:
@@ -17,15 +19,18 @@ jobs:
with:
fetch-depth: 0
submodules: true
+ - name: Checkout HEAD
+ if: github.event_name == 'pull_request'
+ run: git checkout ${{ github.head_ref }}
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
- version: 7
+ version: 8
run_install: false
- name: Use Node.js 18.x
uses: actions/setup-node@v3.6.0
with:
- node-version: 18.x
+ node-version-file: '.node-version'
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
@@ -36,13 +41,13 @@ jobs:
- name: Build storybook
run: pnpm --filter frontend build-storybook
- name: Publish to Chromatic
- if: github.ref == 'refs/heads/master'
+ if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master'
run: pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- name: Publish to Chromatic
- if: github.ref != 'refs/heads/master'
- id: chromatic
+ if: github.event_name != 'pull_request' && github.ref != 'refs/heads/master'
+ id: chromatic_push
run: |
DIFF="${{ github.event.before }} HEAD"
if [ "$DIFF" = "0000000000000000000000000000000000000000 HEAD" ]; then
@@ -55,16 +60,31 @@ jobs:
pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static $(echo "$CHROMATIC_PARAMETER")
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
+ - name: Publish to Chromatic
+ if: github.event_name == 'pull_request'
+ id: chromatic_pull_request
+ run: |
+ DIFF="${{ github.base_ref }} HEAD"
+ if [ "$DIFF" = "0000000000000000000000000000000000000000 HEAD" ]; then
+ DIFF="HEAD"
+ fi
+ CHROMATIC_PARAMETER="$(node packages/frontend/.storybook/changes.js $(git diff-tree --no-commit-id --name-only -r $(echo "$DIFF") | xargs))"
+ if [ "$CHROMATIC_PARAMETER" = " --skip" ]; then
+ echo "skip=true" >> $GITHUB_OUTPUT
+ fi
+ pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static $(echo "$CHROMATIC_PARAMETER")
+ env:
+ CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- name: Notify that Chromatic will skip testing
uses: actions/github-script@v6.4.0
- if: github.ref != 'refs/heads/master' && github.ref != 'refs/heads/develop' && steps.chromatic.outputs.skip == 'true'
+ if: github.event_name == 'pull_request' && steps.chromatic_pull_request.outputs.skip == 'true'
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- github.rest.repos.createCommitComment({
+ github.rest.issues.createComment({
+ issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
- commit_sha: context.sha,
body: 'Chromatic will skip testing but you may still have to [review the changes on Chromatic](https://www.chromatic.com/pullrequests?appId=6428f7d7b962f0b79f97d6e4).'
})
- name: Upload Artifacts
diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
index f1e414dbbc..d7be15bd4f 100644
--- a/.github/workflows/test-backend.yml
+++ b/.github/workflows/test-backend.yml
@@ -35,7 +35,7 @@ jobs:
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
- version: 7
+ version: 8
run_install: false
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.6.0
diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml
index a5505d30d8..4ea4ba4628 100644
--- a/.github/workflows/test-frontend.yml
+++ b/.github/workflows/test-frontend.yml
@@ -22,7 +22,7 @@ jobs:
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
- version: 7
+ version: 8
run_install: false
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.6.0
@@ -106,7 +106,7 @@ jobs:
install: false
start: pnpm start:test
wait-on: 'http://localhost:61812'
- headless: false
+ headed: true
browser: ${{ matrix.browser }}
- uses: actions/upload-artifact@v2
if: failure()
diff --git a/.github/workflows/test-production.yml b/.github/workflows/test-production.yml
new file mode 100644
index 0000000000..5243a83777
--- /dev/null
+++ b/.github/workflows/test-production.yml
@@ -0,0 +1,42 @@
+name: Test (production install and build)
+
+on:
+ push:
+ branches:
+ - master
+ - develop
+ pull_request:
+
+env:
+ NODE_ENV: production
+
+jobs:
+ production:
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ node-version: [18.x]
+
+ steps:
+ - uses: actions/checkout@v3.3.0
+ with:
+ submodules: true
+ - name: Install pnpm
+ uses: pnpm/action-setup@v2
+ with:
+ version: 8
+ run_install: false
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v3.6.0
+ with:
+ node-version: ${{ matrix.node-version }}
+ cache: 'pnpm'
+ - run: corepack enable
+ - run: pnpm i --frozen-lockfile
+ - name: Check pnpm-lock.yaml
+ run: git diff --exit-code pnpm-lock.yaml
+ - name: Copy Configure
+ run: cp .github/misskey/test.yml .config/default.yml
+ - name: Build
+ run: pnpm build
diff --git a/.gitignore b/.gitignore
index fbe2245502..537232d37f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,7 +44,7 @@ built
/data
/.cache-loader
/db
-/elasticsearch
+/meili_data
npm-debug.log
*.pem
run.bat
diff --git a/.node-version b/.node-version
index 0e9dc6b586..6d80269a4f 100644
--- a/.node-version
+++ b/.node-version
@@ -1 +1 @@
-v18.13.0
+18.16.0
diff --git a/.vscode/settings.json b/.vscode/settings.json
index baffbe18ec..71fb02a59d 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -6,5 +6,6 @@
"files.associations": {
"*.test.ts": "typescript"
},
+ "jest.jestCommandLine": "pnpm run jest",
"jest.autoRun": "off"
} \ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 011e1cbccf..003e907c5f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,64 @@
-
-->
+## 13.12.0
+
+### NOTE
+- Node.js 18.6.0以上ãŒå¿…è¦ã«ãªã‚Šã¾ã—ãŸ
+
+### General
+- アカウントã®å¼•ã£è¶Šã—(フォロワー引ãç¶™ãŽï¼‰ã«å¯¾å¿œ
+- Meilisearchを全文検索ã«ä½¿ç”¨ã§ãるよã†ã«ãªã‚Šã¾ã—ãŸ
+- æ–°è¦ç™»éŒ²å‰ã«ç°¡æ½”ãªãƒ«ãƒ¼ãƒ«ã‚’ユーザーã«è¡¨ç¤ºã§ãã‚‹ã€ã‚µãƒ¼ãƒãƒ¼ãƒ«ãƒ¼ãƒ«æ©Ÿèƒ½ã‚’追加
+- ユーザーã¸ã®è‡ªåˆ†ç”¨ãƒ¡ãƒ¢æ©Ÿèƒ½
+ * ユーザーã«å¯¾ã—ã¦ã€è‡ªåˆ†ã ã‘ãŒè¦‹ã‚‰ã‚Œã‚‹ãƒ¡ãƒ¢ã‚’追加ã§ãるよã†ã«ãªã‚Šã¾ã—ãŸã€‚
+ (自分自身ã«å¯¾ã—ã¦ã‚‚メモを追加ã§ãã¾ã™ã€‚)
+ * ユーザーメニューã‹ã‚‰è¿½åŠ ã§ãã¾ã™ã€‚
+ (デスクトップ表示ã§ã¯usernameã®å³å´ã®ãƒœã‚¿ãƒ³ã‹ã‚‰ã‚‚追加å¯èƒ½ï¼‰
+- ãƒãƒ£ãƒ³ãƒãƒ«ã«è‰²ã‚’設定ã§ãるよã†ã«ãªã‚Šã¾ã—ãŸã€‚å„ノートã«è¨­å®šã—ãŸè‰²ã®ã‚¤ãƒ³ã‚¸ã‚±ãƒ¼ã‚¿ãƒ¼ãŒè¡¨ç¤ºã•れã¾ã™ã€‚
+- ãƒãƒ£ãƒ³ãƒãƒ«ã‚’アーカイブã§ãるよã†ã«ãªã‚Šã¾ã—ãŸã€‚
+ * アーカイブã™ã‚‹ã¨ã€ãƒãƒ£ãƒ³ãƒãƒ«ä¸€è¦§ã‚„æ¤œç´¢çµæžœã«è¡¨ç¤ºã•れãªããªã‚Šã€æ–°ãŸãªæ›¸ãè¾¼ã¿ã‚‚ã§ããªããªã‚Šã¾ã™ã€‚
+- アンテナã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆãƒ»ã‚¤ãƒ³ãƒãƒ¼ãƒˆãŒã§ãるよã†ã«ãªã‚Šã¾ã—ãŸ
+- ロールタイムラインをロールã”ã¨ã«è¡¨ç¤ºã™ã‚‹ã‹ã©ã†ã‹ã®é¸æŠžã§ãるよã†ã«ãªã‚Šã¾ã—ãŸã€‚
+ * デフォルトãŒã‚ªãƒ•ã«ãªã‚‹ã®ã§ã€ãƒ­ãƒ¼ãƒ«ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã‚’表示ã™ã‚‹å ´åˆã¯ã‚ªãƒ³ã«ã—ã¦ãã ã•ã„。
+- ロールã«å¼·åˆ¶çš„ã«NSFWを付与ã™ã‚‹ãƒãƒªã‚·ãƒ¼ã‚’追加
+ * アップロード済ã¿ã®ãƒ•ァイルã¯NSFWã«ãªã‚‰ãªã„為注æ„ã—ã¦ãã ã•ã„。
+- モデレーションノートãŒãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ—ロフィールページã‹ã‚‰ã‚‚閲覧ãŠã‚ˆã³ç·¨é›†ã§ãるよã†ã«ãªã‚Šã¾ã—ãŸã€‚
+- カスタム絵文字ã®ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ã‚’複数ã§ã‚»ãƒƒãƒˆã§ãるよã†ã«ãªã‚Šã¾ã—ãŸã€‚
+- 管ç†è€…ãŒäºˆç´„ユーザーåを設定ã§ãるよã†ã«ãªã‚Šã¾ã—ãŸã€‚
+- Fix: フォローリクエストã®é€šçŸ¥ãŒæ®‹ã‚‹å•題を修正
+
+### Client
+- ã‚¢ã‚«ã‚¦ãƒ³ãƒˆä½œæˆæ™‚ã«åˆæœŸè¨­å®šã‚¦ã‚£ã‚¶ãƒ¼ãƒ‰ã‚’表示ã™ã‚‹ã‚ˆã†ã«
+- ãƒãƒ£ãƒ³ãƒãƒ«å†…検索ãŒã§ãるよã†ã«
+- ãƒãƒ£ãƒ³ãƒãƒ«æ¤œç´¢ã§ã™ã¹ã¦ã®ãƒãƒ£ãƒ³ãƒãƒ«ã®å–å¾—/表示ãŒã§ãるよã†ã«
+- 通知ã®è¡¨ç¤ºã‚’カスタマイズã§ãるよã†ã«
+- ドライブã®ãƒ•ァイル一覧ã‹ã‚‰ç›´æŽ¥ãƒŽãƒ¼ãƒˆã‚’作æˆã§ãるよã†ã«
+- ノートメニューã‹ã‚‰Renoteã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ä¸€è¦§ã‚’見れるよã†ã«
+- コントロールパãƒãƒ«ã®ã‚«ã‚¹ã‚¿ãƒ çµµæ–‡å­—ページãŠã‚ˆã³aboutã®ã‚«ã‚¹ã‚¿ãƒ çµµæ–‡å­—ã®æ¤œç´¢ã‚¤ãƒ³ãƒ—ットã§ã€`:emojiname1::emojiname2:`ã®ã‚ˆã†ã«æ¤œç´¢ã—ã¦çµµæ–‡å­—を検索ã§ãるよã†ã«
+ * 絵文字ピッカーã‹ã‚‰å…¥åŠ›å¯èƒ½ã«ãªã‚Šã¾ã™
+- データセーãƒãƒ¼ãƒ¢ãƒ¼ãƒ‰ã‚’追加
+ * ç”»åƒãŒå…¨ã¦éš ã‚ŒãŸçŠ¶æ…‹ã§è¡¨ç¤ºã•れるよã†ã«ãªã‚Šã¾ã™
+- 閲覧注æ„設定ã•れãŸç”»åƒã¯è¡¨ç¤ºã—ãŸçŠ¶æ…‹ã§ã‚‚ãれãŒé–²è¦§æ³¨æ„ã ã¨åˆ†ã‹ã‚‹è¡¨ç¤ºã‚’ã™ã‚‹ã‚ˆã†ã«
+- モデレーターã¯ãƒŽãƒ¼ãƒˆã«æ·»ä»˜ã•れãŸç”»åƒä¸Šã‹ã‚‰ç›´æŽ¥NSFW設定ã§ãるよã†ã«
+- プロフィール設定「追加情報ã€ã®é …ç›®ã®å‰Šé™¤ã¨ä¸¦ã³æ›¿ãˆãŒã§ãるよã†ã«
+- æ–°ã—ã„実績を追加
+- AiScriptã‚’0.13.2ã«æ›´æ–°
+- Fix: AiScript APIã®Mk:dialogã§ä½•ã‚‚è¿”ã—ã¦ã„ãªã‹ã£ãŸã®ã‚’NULLã‚’è¿”ã™ã‚ˆã†ã«ä¿®æ­£
+- Fix: 1:1ã§ã¯ãªã„ç”»åƒã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³é€šçŸ¥ãƒãƒƒã‚¸ãŒå·¦ã‚„上ã«å¯„ã£ã¦ã—ã¾ã£ã¦ã„ãŸã®ã‚’ä¸­å¤®ã«æ¥ã‚‹ã‚ˆã†ã«ä¿®æ­£
+- Fix: リアクションをホãƒãƒ¼ã—ãŸæ™‚ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãƒªã‚¹ãƒˆã§çŒ«è€³ãŒåˆ‡ã‚Œã¦ã—ã¾ã£ã¦ã„ãŸå•題を修正
+- Fix: NSFWメディアã®ä¸Šã«è¡¨ç¤ºã•れãŸï½¢ã‚‚ã£ã¨è¦‹ã‚‹ï½£ãƒœã‚¿ãƒ³ãŒæŠ¼ã—ã¥ã‚‰ã„å•題を修正
+
+### Server
+- channel/searchã®queryãŒç©ºã®å ´åˆã«å…¨ã¦ã®ãƒãƒ£ãƒ³ãƒãƒ«ã‚’è¿”ã™ã‚ˆã†ã«å¤‰æ›´
+- 環境変数MISSKEY_CONFIG_YMLã§è¨­å®šãƒ•ァイルをdefault.ymlã‹ã‚‰å¤‰æ›´å¯èƒ½ã«
+- Fix: ä»–ã®ã‚µãƒ¼ãƒãƒ¼ã®æƒ…å ±ãŒå–å¾—ã§ããªã„ã“ã¨ãŒã‚ã‚‹å•題を修正
+- Fix: エクスãƒãƒ¼ãƒˆãƒ‡ãƒ¼ã‚¿ã®æ‹¡å¼µå­ãŒunknownã«ãªã‚‹å•題を修正
+- Fix: Content-Dispositionã®ãƒ‘ースã§ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ãŸå ´åˆã«ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ãŒå®Œäº†ã—ãªã„å•題を修正
+- Fix: API: i/update avatarIdã¨bannerIdã«nullを渡ã—ãŸæ™‚ã€ç”»åƒãŒãƒªã‚»ãƒƒãƒˆã•れãªã„å•題を修正
+- Fix: .wav, .flacãŒå†ç”Ÿã§ããªã„å•題を修正(新ã—ãアップロードã•れãŸãƒ•ァイルã®ã¿ä¿®æ­£ãŒé©ç”¨ã•れã¾ã™ï¼‰
+- Fix: å‡çµã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒä¸€éƒ¨APIã®ãƒ¬ã‚¹ãƒãƒ³ã‚¹ã«å«ã¾ã‚Œã‚‹å•題を修正
+- Fix: メモリã®ä½¿ç”¨é‡ã‚’`used - buffers - cached`ã§ã¯ãªã`total - available`ã§æ±‚ã‚るよã†ã«ï¼ˆç’°å¢ƒã«ã‚ˆã£ã¦æ­£å¸¸ã«è¨ˆæ¸¬ã§ãã¦ã„ãªã‹ã£ãŸãŸã‚)
## 13.11.3
@@ -107,6 +165,8 @@
- 猫耳ã®ã‚¢ãƒã‚¿ãƒ¼å†…部部分をã¼ã‹ã—ã§ãƒžã‚¹ã‚¯è¡¨ç¤ºã—ã¦ã‚ˆã‚ŠçŒ«è€³ã£ã½ã見ãˆã‚‹ã‚ˆã†ã«
- 「UIã®ã‚¢ãƒ‹ãƒ¡ãƒ¼ã‚·ãƒ§ãƒ³ã‚’減らã™ã€ (`reduceAnimation`) ã§çŒ«è€³ã‚’æ’«ã§ã‚‰ã‚Œãªããªã‚Šã¾ã™
- Add Minimizing ("folding") of windows
+- 「データセーãƒãƒ¼ã€ãƒ¢ãƒ¼ãƒ‰ã‚’追加
+- éžNSFWメディアãŒéš ã‚Œã¦ã„ã‚‹éš›ã«ã‚‚「閲覧注æ„ã€ãŒå‡ºã¦ã—ã¾ã†å•題を修正
### Server
- PostgreSQLã®ãƒ¬ãƒ—リケーション対応
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index fece05d7a9..f6b3804f84 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -165,6 +165,11 @@ pnpm jest -- foo.ts
### e2e tests
TODO
+## Environment Variable
+
+- `MISSKEY_CONFIG_YML`: Specify the file path of config.yml instead of default.yml (e.g. `2nd.yml`).
+- `MISSKEY_WEBFINGER_USE_HTTP`: If it's set true, WebFinger requests will be http instead of https, useful for testing federation between servers in localhost. NEVER USE IN PRODUCTION.
+
## Continuous integration
Misskey uses GitHub Actions for executing automated tests.
Configuration files are located in [`/.github/workflows`](/.github/workflows).
@@ -245,7 +250,6 @@ You can override the default story by creating a impl story file (`MyComponent.s
```ts
/* eslint-disable @typescript-eslint/explicit-function-return-type */
-/* eslint-disable import/no-duplicates */
import { StoryObj } from '@storybook/vue3';
import MyComponent from './MyComponent.vue';
export const Default = {
diff --git a/Dockerfile b/Dockerfile
index 8db7400c9f..fb389659bc 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,6 @@
# syntax = docker/dockerfile:1.4
-ARG NODE_VERSION=18.13.0-bullseye
+ARG NODE_VERSION=18.16.0-bullseye
# build assets & compile TypeScript
diff --git a/chart/files/default.yml b/chart/files/default.yml
index 1888669245..342c6091f0 100644
--- a/chart/files/default.yml
+++ b/chart/files/default.yml
@@ -116,15 +116,14 @@ redis:
# #prefix: example-prefix
# #db: 1
-# ┌─────────────────────────────â”
-#───┘ Elasticsearch configuration └─────────────────────────────
+# ┌───────────────────────────â”
+#───┘ MeiliSearch configuration └─────────────────────────────
-#elasticsearch:
+#meilisearch:
# host: localhost
-# port: 9200
-# ssl: false
-# user:
-# pass:
+# port: 7700
+# apiKey: ''
+# ssl: true
# ┌───────────────â”
#───┘ ID generation └───────────────────────────────────────────
diff --git a/cypress/e2e/basic.cy.js b/cypress/e2e/basic.cy.js
index 8dc07c1800..73f6e7a0f0 100644
--- a/cypress/e2e/basic.cy.js
+++ b/cypress/e2e/basic.cy.js
@@ -52,6 +52,11 @@ describe('After setup instance', () => {
cy.intercept('POST', '/api/signup').as('signup');
cy.get('[data-cy-signup]').click();
+ cy.get('[data-cy-signup-rules-continue]').should('be.disabled');
+ cy.get('[data-cy-signup-rules-notes-agree] [data-cy-switch-toggle]').click();
+ cy.get('[data-cy-signup-rules-continue]').should('not.be.disabled');
+ cy.get('[data-cy-signup-rules-continue]').click();
+
cy.get('[data-cy-signup-submit]').should('be.disabled');
cy.get('[data-cy-signup-username] input').type('alice');
cy.get('[data-cy-signup-submit]').should('be.disabled');
@@ -71,6 +76,11 @@ describe('After setup instance', () => {
// ユーザーåãŒé‡è¤‡ã—ã¦ã„ã‚‹å ´åˆã®æŒ™å‹•確èª
cy.get('[data-cy-signup]').click();
+ cy.get('[data-cy-signup-rules-continue]').should('be.disabled');
+ cy.get('[data-cy-signup-rules-notes-agree] [data-cy-switch-toggle]').click();
+ cy.get('[data-cy-signup-rules-continue]').should('not.be.disabled');
+ cy.get('[data-cy-signup-rules-continue]').click();
+
cy.get('[data-cy-signup-username] input').type('alice');
cy.get('[data-cy-signup-password] input').type('alice1234');
cy.get('[data-cy-signup-password-retype] input').type('alice1234');
@@ -149,10 +159,55 @@ describe('After user signed in', () => {
});
it('successfully loads', () => {
- cy.get('[data-cy-open-post-form]').should('be.visible');
+ cy.get('[data-cy-user-setup-continue]').should('be.visible');
+ });
+
+ it('account setup wizard', () => {
+ cy.get('[data-cy-user-setup-continue]').click();
+
+ cy.get('[data-cy-user-setup-user-name] input').type('ã‚りã™');
+ cy.get('[data-cy-user-setup-user-description] textarea').type('ã»ã’');
+ // TODO: アイコン設定テスト
+
+ cy.get('[data-cy-user-setup-continue]').click();
+
+ // フォローã¯ã‚¹ã‚­ãƒƒãƒ—
+
+ cy.get('[data-cy-user-setup-continue]').click();
+
+ // プッシュ通知設定ã¯ã‚¹ã‚­ãƒƒãƒ—
+
+ cy.get('[data-cy-user-setup-continue]').click();
+
+ cy.get('[data-cy-user-setup-continue]').click();
});
+});
+
+describe('After user setup', () => {
+ beforeEach(() => {
+ cy.resetState();
+
+ // ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹åˆæœŸã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—
+ cy.registerUser('admin', 'pass', true);
+
+ // ユーザー作æˆ
+ cy.registerUser('alice', 'alice1234');
+
+ cy.login('alice', 'alice1234');
+
+ // ã‚¢ã‚«ã‚¦ãƒ³ãƒˆåˆæœŸè¨­å®šã‚¦ã‚£ã‚¶ãƒ¼ãƒ‰
+ cy.get('[data-cy-user-setup] [data-cy-modal-window-close]').click();
+ cy.get('[data-cy-modal-dialog-ok]').click();
+ });
+
+ afterEach(() => {
+ // テスト終了直å‰ã«ãƒšãƒ¼ã‚¸é·ç§»ã™ã‚‹ã‚ˆã†ãªãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹(例ãˆã°ã‚¢ã‚«ã‚¦ãƒ³ãƒˆä½œæˆ)ã ã¨ã€ãŸã¶ã‚“Cypressã®ãƒã‚°ã§ãƒ–ラウザã®å†…å®¹ãŒæ¬¡ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«å¼•ãç¶™ãŒã‚Œã¦ã—ã¾ã†(例ãˆã°ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒä½œæˆã—終ã‚ã£ãŸæ®µéšŽã‹ã‚‰ãƒ†ã‚¹ãƒˆãŒå§‹ã¾ã‚‹)。
+ // waitを入れるã“ã¨ã§ãれを防止ã§ãã‚‹
+ cy.wait(1000);
+ });
it('note', () => {
+ cy.get('[data-cy-open-post-form]').should('be.visible');
cy.get('[data-cy-open-post-form]').click();
cy.get('[data-cy-post-form-text]').type('Hello, Misskey!');
cy.get('[data-cy-open-post-form-submit]').click();
diff --git a/cypress/e2e/widgets.cy.js b/cypress/e2e/widgets.cy.js
index a39ea85e1d..f5a982eb0a 100644
--- a/cypress/e2e/widgets.cy.js
+++ b/cypress/e2e/widgets.cy.js
@@ -10,6 +10,10 @@ describe('After user signed in', () => {
cy.registerUser('alice', 'alice1234');
cy.login('alice', 'alice1234');
+
+ // ã‚¢ã‚«ã‚¦ãƒ³ãƒˆåˆæœŸè¨­å®šã‚¦ã‚£ã‚¶ãƒ¼ãƒ‰
+ cy.get('[data-cy-user-setup] [data-cy-modal-window-close]').click();
+ cy.get('[data-cy-modal-dialog-ok]').click();
});
afterEach(() => {
@@ -19,26 +23,26 @@ describe('After user signed in', () => {
});
it('widget edit toggle is visible', () => {
- cy.get('.mk-widget-edit').should('be.visible');
+ cy.get('[data-cy-widget-edit]').should('be.visible');
});
it('widget select should be visible in edit mode', () => {
- cy.get('.mk-widget-edit').click();
- cy.get('.mk-widget-select').should('be.visible');
+ cy.get('[data-cy-widget-edit]').click();
+ cy.get('[data-cy-widget-select]').should('be.visible');
});
it('first widget should be removed', () => {
- cy.get('.mk-widget-edit').click();
+ cy.get('[data-cy-widget-edit]').click();
cy.get('[data-cy-customize-container]:first-child [data-cy-customize-container-remove]._button').click();
cy.get('[data-cy-customize-container]').should('have.length', 2);
});
function buildWidgetTest(widgetName) {
it(`${widgetName} widget should get added`, () => {
- cy.get('.mk-widget-edit').click();
- cy.get('.mk-widget-select select').select(widgetName, { force: true });
+ cy.get('[data-cy-widget-edit]').click();
+ cy.get('[data-cy-widget-select] select').select(widgetName, { force: true });
cy.get('[data-cy-bg]._modalBg[data-cy-transparent]').click({ multiple: true, force: true });
- cy.get('.mk-widget-add').click({ force: true });
+ cy.get('[data-cy-widget-add]').click({ force: true });
cy.get(`[data-cy-mkw-${widgetName}]`).should('exist');
});
}
diff --git a/docker-compose.yml.example b/docker-compose.yml.example
index b0c4a914d5..a0061c5c20 100644
--- a/docker-compose.yml.example
+++ b/docker-compose.yml.example
@@ -7,7 +7,7 @@ services:
links:
- db
- redis
-# - es
+# - meilisearch
depends_on:
db:
condition: service_healthy
@@ -48,16 +48,18 @@ services:
interval: 5s
retries: 20
-# es:
+# meilisearch:
# restart: always
-# image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.2
+# image: getmeili/meilisearch:v1.1.1
# environment:
-# - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
-# - "TAKE_FILE_OWNERSHIP=111"
+# - MEILI_NO_ANALYTICS=true
+# - MEILI_ENV=production
+# env_file:
+# - .config/meilisearch.env
# networks:
# - internal_network
# volumes:
-# - ./elasticsearch:/usr/share/elasticsearch/data
+# - ./meili_data:/meili_data
networks:
internal_network:
diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml
index 4b9062051b..9a5936660e 100644
--- a/locales/ar-SA.yml
+++ b/locales/ar-SA.yml
@@ -250,12 +250,12 @@ noMoreHistory: "لا يوجد المزيد من التاريخ"
startMessaging: "ابدأ محادثة"
nUsersRead: "قرأه {n}"
agreeTo: "اواÙÙ‚ على {0}"
-tos: "شروط الخدمة"
start: "البداية"
home: "الرئيسي"
remoteUserCaution: "هذه المعلومات قد لا تكون مكتملة بما أن المستخدم من مثيل بعيد."
activity: "النشاط"
images: "الصور"
+image: "الصور"
birthday: "تاريخ الميلاد"
yearsOld: "{age} سنة"
registeredDate: "انضم ÙÙŠ"
@@ -801,6 +801,8 @@ cannotLoad: "تعذر التحميل"
like: "أعجبني"
show: "المظهر"
color: "اللون"
+horizontal: "جانبي"
+youFollowing: "متابَع"
_role:
priority: "الأولوية"
_priority:
@@ -977,29 +979,6 @@ _time:
minute: "د"
hour: "سا"
day: "ÙŠ"
-_tutorial:
- title: "كي٠تستخدم Misskey"
- step1_1: "مرحبًا!"
- step1_2: "تدعى هذه Ø§Ù„ØµÙØ­Ø© 'الخيط الزمني' وهي تحوي ملاحظات الأشخاص الذي تتابعهم مرتبة حسب تاريخ نشرها."
- step1_3: "خيطك الزمني ÙØ§Ø±Øº حاليًا بما أنك لا تتابع أي شخص ولم تنشر أي ملاحظة."
- step2_1: "لننهي إعداد ملÙÙƒ الشخصي قبل كتابة ملاحظة أو متابعة أشخاص."
- step2_2: "أعطاء معلومات عن شخصيتك يمنح من له Ù†ÙØ³ إهتماماتك ÙØ±ØµØ© متابعتك ÙˆØ§Ù„ØªÙØ§Ø¹Ù„ مع ملاحظاتك."
- step3_1: "هل أنهيت إعداد حسابك؟"
- step3_2: "إذا تاليًا لتنشر ملاحظة. أنقر على أيقونة القلم ÙÙŠ أعلى الشاشة"
- step3_3: "املأ النموذج وانقر الزرّ الموجود ÙÙŠ أعلى اليمين للإرسال."
- step3_4: "ليس لديك ما تقوله؟ إذا اكتب \"بدأت٠استخدم ميسكي\"."
- step4_1: "هل نشرت ملاحظتك الأولى؟"
- step4_2: "مرحى! يمكنك الآن رؤية ملاحظتك ÙÙŠ الخيط الزمني."
- step5_1: "والآن، لنجعل الخيط الزمني أكثر حيوية وذلك بمتابعة بعض المستخدمين."
- step5_2: "تعرض ØµÙØ­Ø© {features} الملاحظات المتداولة ÙÙŠ هذا المثيل ويتيح لك {Explore} العثور على المستخدمين الرائدين. اعثر على الأشخاص الذين يثيرون إهتمامك وتابعهم!"
- step5_3: "لمتابعة مستخدمين ادخل ملÙهم الشخصي بالنقر على صورتهم الشخصية ثم اضغط زر 'تابع'."
- step5_4: "إذا كان لدى المستخدم رمز Ù‚ÙÙ„ بجوار اسمه ØŒ وجب عليك انتظاره ليقبل طلب المتابعة يدويًا."
- step6_1: "الآن ستتمكن من رؤية ملاحظات المستخدمين المتابَعين ÙÙŠ الخيط الزمني."
- step6_2: "يمكنك Ø§Ù„ØªÙØ§Ø¹Ù„ بسرعة مع الملاحظات عن طريق Ø¥Ø¶Ø§ÙØ© \"ØªÙØ§Ø¹Ù„\"."
- step6_3: "Ù„Ø¥Ø¶Ø§ÙØ© ØªÙØ§Ø¹Ù„ لملاحظة ØŒ انقر Ùوق علامة \"+\" أسÙÙ„ للملاحظة واختر الإيموجي المطلوب."
- step7_1: "مبارك ! أنهيت الدورة التعليمية الأساسية لاستخدام ميسكي."
- step7_2: "إذا أردت Ù…Ø¹Ø±ÙØ© المزيد عن ميسكي زر {help}."
- step7_3: "حظًا سعيدًا واستمتع بوقتك مع ميسكي! 🚀"
_2fa:
alreadyRegistered: "سجلت سلÙًا جهازًا للاستيثاق بعاملين."
step1: "أولًا ثبّت تطبيق استيثاق على جهازك (مثل {a} و{b})."
diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml
index 734943960c..4b1a9cb758 100644
--- a/locales/bn-BD.yml
+++ b/locales/bn-BD.yml
@@ -252,12 +252,12 @@ noMoreHistory: "আর কোন ইতিহাস নেই"
startMessaging: "চà§à¦¯à¦¾à¦Ÿ শà§à¦°à§ করà§à¦¨"
nUsersRead: "{n} জন পড়েছেন"
agreeTo: "{0} à¦à¦° পà§à¦°à¦¤à¦¿ আমি সমà§à¦®à¦¤"
-tos: "পরিষেবার শরà§à¦¤à¦¾à¦¦à¦¿"
start: "শà§à¦°à§ করà§à¦¨"
home: "মূল পাতা"
remoteUserCaution: "à¦à¦‡ বà§à¦¯à¦¾à¦¬à¦¹à¦¾à¦°à¦•ারী রিমোট ইনà§à¦¸à¦Ÿà§à¦¯à¦¾à¦¨à§à¦¸à§‡à¦°, নিমà§à¦¨à¦•à§à¦¤ তথà§à¦¯ অসমà§à¦ªà§‚রà§à¦£ হতে পারে।"
activity: "কারà§à¦¯à¦•লাপ"
images: "ছবি"
+image: "ছবি"
birthday: "জনà§à¦®à¦¦à¦¿à¦¨"
yearsOld: "{age} বছর"
registeredDate: "যোগদানের তারিখ"
@@ -836,6 +836,8 @@ account: "অà§à¦¯à¦¾à¦•াউনà§à¦Ÿà¦—à§à¦²à¦¿"
like: "পছনà§à¦¦ করা"
show: "পà§à¦°à¦¦à¦°à§à¦¶à¦¨"
color: "রং"
+horizontal: "পাশে"
+youFollowing: "অনà§à¦¸à¦°à¦£ করা হচà§à¦›à§‡"
_role:
priority: "অগà§à¦°à¦¾à¦§à¦¿à¦•ার"
_priority:
@@ -1039,29 +1041,6 @@ _time:
minute: "মিনিট"
hour: "ঘণà§à¦Ÿà¦¾"
day: "দিন"
-_tutorial:
- title: "Misskey কিভাবে বà§à¦¯à¦¾à¦¬à¦¹à¦¾à¦° করবেন"
- step1_1: "সà§à¦¬à¦¾à¦—তম!"
- step1_2: "à¦à¦‡ সà§à¦•à§à¦°à§€à¦¨à¦Ÿà¦¿à¦•ে \"টাইমলাইন\" বলা হয় à¦à¦¬à¦‚ কালানà§à¦•à§à¦°à¦®à¦¿à¦• কà§à¦°à¦®à§‡ আপনার à¦à¦¬à¦‚ আপনি যাদের \"অনà§à¦¸à¦°à¦£ করেন\" তাদের \"নোটগà§à¦²à¦¿\" দেখায়৷"
- step1_3: "আপনি আপনার টাইমলাইনে কিছৠদেখতে পাবেন না কারণ আপনি à¦à¦–নও কোনো নোট পোসà§à¦Ÿ করেননি à¦à¦¬à¦‚ আপনি কাউকে অনà§à¦¸à¦°à¦£ করছেন না৷"
- step2_1: "নোট তৈরি করার আগে বা কাউকে অনà§à¦¸à¦°à¦£ করার আগে পà§à¦°à¦¥à¦®à§‡ আপনার পà§à¦°à§‹à¦«à¦¾à¦‡à¦²à¦Ÿà¦¿ সমà§à¦ªà§‚রà§à¦£ করà§à¦¨à¥¤"
- step2_2: "আপনি কে তা জানা অনেক লোকের জনà§à¦¯ আপনার নোটগà§à¦²à¦¿ দেখা à¦à¦¬à¦‚ অনà§à¦¸à¦°à¦£ করাকে সহজ করে তোলে৷"
- step3_1: "আপনি কি সফলভাবে আপনার পà§à¦°à§‹à¦«à¦¾à¦‡à¦² সেট আপ করেছেন?"
- step3_2: "à¦à¦–ন, কিছৠনোট পোসà§à¦Ÿ করার চেষà§à¦Ÿà¦¾ করà§à¦¨à¥¤ পোসà§à¦Ÿ ফরà§à¦® খà§à¦²à¦¤à§‡ পেনà§à¦¸à¦¿à¦² চিহà§à¦¨à¦¯à§à¦•à§à¦¤ বাটনে কà§à¦²à¦¿à¦• করà§à¦¨à¥¤"
- step3_3: "বিষয়বসà§à¦¤à§ লেখার পরে, আপনি ফরà§à¦®à§‡à¦° উপরের ডানদিকের বাটনে কà§à¦²à¦¿à¦• করে পোসà§à¦Ÿ করতে পারেন।"
- step3_4: "পোসà§à¦Ÿ করার মত কিছৠমনে পরছে না? \"আমি মিসকি সেট আপ করছি\" বললে কেমন হয়?"
- step4_1: "পোসà§à¦Ÿ করেছেন?"
- step4_2: "সাবাশ! à¦à¦–ন আপনার নোট টাইমলাইনে দেখা যাবে।"
- step5_1: "à¦à¦–ন অনà§à¦¯à¦¦à§‡à¦°à¦•ে অনà§à¦¸à¦°à¦£ করে আপনার টাইমলাইনকে পà§à¦°à¦¾à¦£à¦¬à¦¨à§à¦¤ করে তà§à¦²à§à¦¨à¥¤"
- step5_2: "আপনি {featured}-ঠজনপà§à¦°à¦¿à¦¯à¦¼ নোটগà§à¦²à¦¿ দেখতে পারেন, যাতে আপনি যে বà§à¦¯à¦•à§à¦¤à¦¿à¦•ে পছনà§à¦¦ করেন তাকে বেছে নিতে à¦à¦¬à¦‚ অনà§à¦¸à¦°à¦£ করতে পারেন, অথবা {explore}-ঠজনপà§à¦°à¦¿à¦¯à¦¼ বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারীদের দেখতে পারেন৷"
- step5_3: "à¦à¦•জন বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারীকে অনà§à¦¸à¦°à¦£ করতে, বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারীর আইকনে কà§à¦²à¦¿à¦• করà§à¦¨ à¦à¦¬à¦‚ বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারীর পৃষà§à¦ à¦¾à¦¤à§‡ \"অনà§à¦¸à¦°à¦£ করà§à¦¨\" বাটনে কà§à¦²à¦¿à¦• করà§à¦¨à¥¤"
- step5_4: "যদি বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারীর নামের পাশে à¦à¦•টি লক আইকন থাকে তাহলে আপনার অনà§à¦¸à¦°à¦£à§‡à¦° অনà§à¦°à§‹à¦§ গà§à¦°à¦¹à¦£ করার জনà§à¦¯ তারা কিছৠসময় নিতে পারে।"
- step6_1: "সবকিছৠঠিক থাকলে আপনি টাইমলাইনে অনà§à¦¯ বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারীদের নোট দেখতে পাবেন।"
- step6_2: "আপনি সহজেই আপনার পà§à¦°à¦¤à¦¿à¦•à§à¦°à¦¿à¦¯à¦¼à¦¾ জানাতে অনà§à¦¯ বà§à¦¯à¦•à§à¦¤à¦¿à¦° নোটে \"রিঅà§à¦¯à¦¾à¦•শন\" যোগ করতে পারেন।"
- step6_3: "à¦à¦•টি রিঅà§à¦¯à¦¾à¦•শন যোগ করতে, নোটে \"+\" চিহà§à¦¨à§‡ কà§à¦²à¦¿à¦• করà§à¦¨ à¦à¦¬à¦‚ আপনার পছনà§à¦¦à§‡à¦° রিঅà§à¦¯à¦¾à¦•শন নিরà§à¦¬à¦¾à¦šà¦¨ করà§à¦¨à¥¤"
- step7_1: "অভিননà§à¦¦à¦¨! আপনি à¦à¦–ন Misskey-র পà§à¦°à¦¾à¦¥à¦®à¦¿à¦• টিউটোরিয়ালটি শেষ করেছেন।"
- step7_2: "আপনি যদি Misskey সমà§à¦ªà¦°à§à¦•ে আরও জানতে চান, তাহলে {help} ঠদেখà§à¦¨à¥¤"
- step7_3: "à¦à¦–ন Misskey উপভোগ করà§à¦¨ 🚀"
_2fa:
alreadyRegistered: "আপনি ইতিমধà§à¦¯à§‡ à¦à¦•টি 2-ফà§à¦¯à¦¾à¦•à§à¦Ÿà¦° অথেনটিকেশন ডিভাইস নিবনà§à¦§à¦¨ করেছেন৷"
step1: "পà§à¦°à¦¥à¦®à§‡, আপনার ডিভাইসে {a} বা {b} à¦à¦° মতো à¦à¦•টি অথেনটিকেশন অà§à¦¯à¦¾à¦ª ইনসà§à¦Ÿà¦² করà§à¦¨à§·"
diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index 2b1168f780..588a07fd87 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -216,6 +216,7 @@ instanceFollowers: "Seguidors del servidor"
instanceUsers: "Usuaris del servidor"
changePassword: "Canvia la contrasenya"
security: "Seguretat"
+retypedNotMatch: "L'entrada no coincideix"
currentPassword: "Contrasenya actual"
newPassword: "Contrasenya nova"
newPasswordRetype: "Contrasenya nou (repeteix-la)"
@@ -239,6 +240,7 @@ start: "Comença"
home: "Inici"
activity: "Activitat"
images: "Imatges"
+image: "Imatges"
birthday: "Aniversari"
yearsOld: "{age} anys"
registeredDate: "Data de registre"
diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml
index 19815c6f37..d055fde38f 100644
--- a/locales/cs-CZ.yml
+++ b/locales/cs-CZ.yml
@@ -12,12 +12,14 @@ fetchingAsApObject: "NaÄítám data z Fediversu..."
ok: "Potvrdit"
gotIt: "Rozumím!"
cancel: "Zrušit"
+noThankYou: "Ne děkuji"
enterUsername: "Zadej uživatelské jméno"
renotedBy: "{user} přeposla/a"
noNotes: "Žádné poznámky"
noNotifications: "Žádná oznámení"
instance: "Instance"
settings: "Nastavení"
+notificationSettings: "Nastavení oznámení"
basicSettings: "Obecná nastavení"
otherSettings: "Rozšířená nastavení"
openInWindow: "Otevřít v novém okně"
@@ -236,12 +238,12 @@ noMoreHistory: "To je vše"
startMessaging: "Zahájit chat"
nUsersRead: "pÅ™eÄteno {n} uživateli"
agreeTo: "Souhlasím s {0}"
-tos: "Podmínky užívání"
start: "ZaÄít"
home: "Domů"
remoteUserCaution: "Tyto informace nemusí být aktuální jelikož uživatel je ze vzdálené instance."
activity: "Aktivita"
images: "Obrázky"
+image: "Obrázky"
birthday: "Datum narození"
yearsOld: "{age} let"
registeredDate: "Datum registrace"
@@ -664,6 +666,8 @@ _time:
second: "Sekund"
minute: "Minut"
hour: "Hodin"
+_2fa:
+ renewTOTPCancel: "Ne děkuji"
_weekday:
sunday: "Neděle"
monday: "Pondělí"
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index 095c71642a..d678fadd4a 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -263,14 +263,16 @@ noMoreHistory: "Kein weiterer Verlauf vorhanden"
startMessaging: "Neuen Chat erstellen"
nUsersRead: "Von {n} Benutzern gelesen"
agreeTo: "Ich stimme {0} zu"
+agree: "Zustimmen"
agreeBelow: "Ich stimme Untenstehendem zu"
basicNotesBeforeCreateAccount: "Wichtige Infos"
-tos: "Nutzungsbedingungen"
+termsOfService: "Nutzungsbedingungen"
start: "Anfangen"
home: "Startseite"
remoteUserCaution: "Diese Informationen sind möglicherweise unvollständig, da der Benutzer von einer fremden Instanz stammt."
activity: "Aktivität"
images: "Bilder"
+image: "Bild"
birthday: "Geburtstag"
yearsOld: "{age} Jahre alt"
registeredDate: "Registrationsdatum"
@@ -468,12 +470,14 @@ enableAdvancedMfm: "Erweitertes MFM aktivieren"
enableAnimatedMfm: "Animiertes MFM aktivieren"
doing: "In Bearbeitung …"
category: "Kategorie"
-tags: "Schlagwörter"
+tags: "Aliasse"
docSource: "Quellcode dieses Dokuments"
createAccount: "Benutzerkonto erstellen"
existingAccount: "Bestehendes Benutzerkonto"
regenerate: "Regenerieren"
fontSize: "Schriftgröße"
+mediaListWithOneImageAppearance: "Höhe von Medienlisten mit nur einem Bild"
+limitTo: "Auf {x} begrenzen"
noFollowRequests: "Keine ausstehenden Follow-Anfragen vorhanden"
openImageInNewTab: "Bilder in neuem Tab öffnen"
dashboard: "Dashboard"
@@ -556,6 +560,7 @@ accountDeletedDescription: "Dieses Konto wurde gelöscht."
menu: "Menü"
divider: "Trenner"
addItem: "Element hinzufügen"
+rearrange: "Sortieren"
relays: "Relays"
addRelay: "Relay hinzufügen"
inboxUrl: "inbox-URL"
@@ -699,6 +704,8 @@ contact: "Kontakt"
useSystemFont: "Standardschriftart des Systems verwenden"
clips: "Clips"
experimentalFeatures: "Experimentelle Funktionalitäten"
+experimental: "Experimentell"
+thisIsExperimentalFeature: "Dies ist eine experimentelle Funktion. Änderungen an ihrer Funktionsweise sind vorbehalten, zudem kann eine Verwendung zu unerwarteten Effekten führen."
developer: "Entwickler"
makeExplorable: "Benutzerkonto in „Erkunden“ sichtbar machen"
makeExplorableDescription: "Wenn diese Option deaktiviert ist, ist dein Benutzerkonto nicht im „Erkunden“-Bereich sichtbar."
@@ -905,6 +912,7 @@ remoteOnly: "Nur für fremde Instanzen"
failedToUpload: "Hochladen fehlgeschlagen"
cannotUploadBecauseInappropriate: "Diese Datei kann nicht hochgeladen werden, da Anteile der Datei als möglicherweise NSFW festgestellt wurden."
cannotUploadBecauseNoFreeSpace: "Die Datei konnte nicht hochgeladen werden, da dein Drive-Speicherplatz aufgebraucht ist."
+cannotUploadBecauseExceedsFileSizeLimit: "Diese Datei kann wegen Überschreitung der Maximalgröße nicht hochgeladen werden."
beta: "Beta"
enableAutoSensitive: "NSFW-Automarkierung"
enableAutoSensitiveDescription: "Setzt soweit möglich durch Verwendung von Machine Learning automatisch NSFW-Markierungen für Medien, die NSFW-Anteile beinhalten. Auch wenn du diese Option deaktiviert hast, ist sie möglicherweise auf Instanzebene aktiviert."
@@ -918,8 +926,8 @@ subscribePushNotification: "Push-Benachrichtigungen aktivieren"
unsubscribePushNotification: "Push-Benachrichtigungen deaktivieren"
pushNotificationAlreadySubscribed: "Push-Benachrichtigungen sind bereits aktiviert"
pushNotificationNotSupported: "Entweder dein Browser oder deine Instanz unterstützt Push-Benachrichtigungen nicht"
-sendPushNotificationReadMessage: "Push-Benachrichtigungen löschen, sobald die relevanten Benachrichtigungen oder Nachrichten gelesen wurden"
-sendPushNotificationReadMessageCaption: "Eine Push-Benachrichtigungen mit dem Inhalt \"{emptyPushNotificationMessage}\" wird kurz eingeblendet. Dies kann gegebenenfalls den Batterieverbrauch deines Gerätes erhöhen."
+sendPushNotificationReadMessage: "Push-Benachrichtigungen löschen, sobald sie gelesen wurden"
+sendPushNotificationReadMessageCaption: "Dies kann gegebenenfalls den Batterieverbrauch deines Gerätes erhöhen."
windowMaximize: "Maximieren"
windowMinimize: "Minimieren"
windowRestore: "Wiederherstellen"
@@ -938,6 +946,7 @@ didYouLikeMisskey: "Gefällt dir Misskey?"
pleaseDonate: "Misskey ist die kostenlose Software, die von {host} verwendet wird. Wir würden uns über Spenden freuen, damit dessen Entwicklung weitergeführt werden kann!"
roles: "Rollen"
role: "Rolle"
+noRole: "Rolle nicht gefunden"
normalUser: "Standardbenutzer"
undefined: "Undefiniert"
assign: "Zuweisen"
@@ -947,6 +956,10 @@ manageCustomEmojis: "Kann benutzerdefinierte Emojis verwalten"
youCannotCreateAnymore: "Du hast das Erstellungslimit erreicht."
cannotPerformTemporary: "Vorübergehend nicht verfügbar"
cannotPerformTemporaryDescription: "Diese Aktion ist wegen des Überschreitenes des Ausführungslimits temporär nicht verfügbar. Bitte versuche es nach einiger Zeit erneut."
+invalidParamError: "Ungültige Parameter"
+invalidParamErrorDescription: "Die Anfrageparameter sind fehlerhaft. Dies liegt meist an einem Bug, kann aber auch durch eine zu langen Eingabe o.ä. ausgelöst werden."
+permissionDeniedError: "Aktion verweigert"
+permissionDeniedErrorDescription: "Dieses Benutzerkonto besitzt nicht die Berechtigung, um diese Aktion auszuführen."
preset: "Vorlage"
selectFromPresets: "Aus Vorlagen wählen"
achievements: "Errungenschaften"
@@ -990,17 +1003,70 @@ enableChartsForFederatedInstances: "Diagramme für fremde Instanzen erstellen"
showClipButtonInNoteFooter: "\"Clip\" zum Notizmenu hinzufügen"
largeNoteReactions: "Reaktionen vergrößert anzeigen"
noteIdOrUrl: "Notiz-ID oder URL"
-accountMigration: "Konto-Umzug"
-accountMoved: "Dieser Benutzer ist zu einem neuen Konto umgezogen:"
+video: "Video"
+videos: "Videos"
+dataSaver: "Datensparmodus"
+accountMigration: "Kontomigration"
+accountMoved: "Dieser Benutzer ist zu einem neuen Konto migriert:"
+accountMovedShort: "Dieses Konto wurde migriert."
+operationForbidden: "Aktion nicht möglich"
forceShowAds: "Werbung immer anzeigen"
+addMemo: "Bemerkung hinzufügen"
+editMemo: "Bemerkung bearbeiten"
+reactionsList: "Reaktionen"
+renotesList: "Renotes"
+notificationDisplay: "Benachrichtigungen"
+leftTop: "Oben links"
+rightTop: "Oben rechts"
+leftBottom: "Unten links"
+rightBottom: "Unten rechts"
+stackAxis: "Stapelrichtung"
+vertical: "Vertikal"
+horizontal: "Horizontal"
+position: "Position"
+serverRules: "Serverregeln"
+pleaseConfirmBelowBeforeSignup: "Lies bitte Untenstehendes vor der Registration."
+pleaseAgreeAllToContinue: "Zum Fortfahren muss allen obigen Feldern zugestimmt werden."
+continue: "Fortfahren"
+preservedUsernames: "Reservierte Benutzernamen"
+preservedUsernamesDescription: "Gib zu reservierende Benutzernamen durch Zeilenumbrüche getrennt an. Diese werden für die Registrierung gesperrt, können aber von Administratoren zur manuellen Erstellung von Konten verwendet werden. Existierende Konten, die diese Namen bereits verwenden, werden nicht beeinträchtigt."
+createNoteFromTheFile: "Notiz für diese Datei schreiben"
+archive: "Archivieren"
+channelArchiveConfirmTitle: "{name} wirklich archivieren?"
+channelArchiveConfirmDescription: "Ein archivierter Kanal taucht nicht mehr in der Kanalliste oder in Suchergebnissen auf. Zudem können ihm keine Beiträge mehr hinzugefügt werden."
+thisChannelArchived: "Dieser Kanal wurde archiviert."
+displayOfNote: "Anzeige von Notizen"
+initialAccountSetting: "Kontoeinrichtung"
+youFollowing: "Gefolgt"
+_initialAccountSetting:
+ accountCreated: "Dein Konto wurde erfolgreich erstellt!"
+ letsFillYourProfile: "Lass uns zuerst dein Profil einrichten."
+ profileSetting: "Profileinstellungen"
+ theseSettingsCanEditLater: "Diese Einstellungen kannst du jederzeit ändern."
+ youCanEditMoreSettingsInSettingsPageLater: "In den Einstellungen findest du noch viele weitere Optionen. Schau dort später mal vorbei."
+ followUsers: "Folge zuerst ein paar Nutzern, um deine Chronik zu füllen."
+ pushNotificationDescription: "Durch die Aktivierung von Push-Benachrichtigungen kannst du von {name} Benachrichtigungen direkt auf dein Gerät erhalten."
+ initialAccountSettingCompleted: "Kontoeinrichtung abgeschlossen!"
+ haveFun: "Viel Spaß mit {name}!"
+ ifYouNeedLearnMore: "Besuche {link}, falls du mehr über {name} (Misskey) lernen möchtest."
+ skipAreYouSure: "Die Kontoeinrichtung wirklich überspringen?"
+_serverRules:
+ description: "Eine Reihe von Regeln, die vor der Registrierung angezeigt werden. Eine Zusammenfassung der Nutzungsbedingungen anzuzeigen ist empfohlen."
_accountMigration:
- moveTo: "Dieses Konto zu einem neuen umziehen"
+ moveFrom: "Von einem anderen Konto zu diesem migrieren"
+ moveFromSub: "Alias für ein anderes Konto erstellen"
+ moveFromLabel: "Migrationsursprung #{n}"
+ moveFromDescription: "Um von einem anderen Konto zu diesem zu migrieren, muss zuvor hier ein Alias eingerichtet werden.\nGib das Konto, von dem migriert werden soll, in folgendem Format ein: @username@server.example.com\n\nZum Löschen des Alias kann das Feld leergelassen werden (nicht empfohlen)."
+ moveTo: "Dieses Konto zu einem neuen migrieren"
moveToLabel: "Umzugsziel:"
- moveAccountDescription: "Ein Umzug kann nicht rückgängig gemacht werden. Stelle zuerst sicher, dass du auf dem Umzugsziel einen Alias erstellt hast. Gib dann das Umzugsziel in folgendem Format ein: @person@instance.com"
- moveFrom: "Von einem anderen Konto zu diesem umziehen"
- moveFromLabel: "Umzugsursprung:"
- moveFromDescription: "Stelle sicher, dass du auf dem Umzugsursprungskonto einen Alias zu diesem Konto erstellt hast, falls du die Follower des Ursprungskontos übertragen möchtest. Dies muss vor dem Umzug geschehen! Gib dann das Ursprungskonto in folgendem Format an: @person@instance.com"
- migrationConfirm: "Dieses Konto wirklich zu {account} umziehen? Sobald der Umzug beginnt, kann er nicht rückgängig gemacht werden, und dieses Konto nicht wieder im ursprünglichen Zustand verwendet werden.\n\nÜberprüfe zusätzlich, dass du auf dem Umzugsziel einen Alias eingerichtet hast."
+ moveCannotBeUndone: "Die Migration eines Benutzerkontos ist unwiderruflich."
+ moveAccountDescription: "Hierdurch wird dein Konto zu einem anderen migriert.\n ・Follower von diesem Konto werden automatisch auf das neue Konto migriert\n ・Dieses Konto wird allen Nutzern, denen es derzeit folgt, nicht mehr folgen\n ・Mit diesem Konto können keine neuen Notizen usw. erstellt werden\n\nWährend die Migration der Follower automatisch erfolgt, muss die Migration der Konten, denen du folgst, manuell vorbereitet werden. Exportiere hierzu die Liste der gefolgten Nutzer über das Einstellungsmenu, und importiere diese Liste im neuen Konto. Das gleiche Verfahren gilt für erstellte Listen und stummgeschaltete oder blockierte Nutzer.\n\n(Diese Erklärung gilt für Misskey v13.12.0 oder später. Die Funktionsweise andere ActivityPub-Software, beispielsweise Mastodon, kann hiervon abweichen.)"
+ moveAccountHowTo: "Um ein Konto zu migrieren, erstelle zuerst auf dem Umzugsziel einen Alias für dieses Konto.\nGib dann das Umzugsziel in folgendem Format ein: @username@server.example.com"
+ startMigration: "Migrieren"
+ migrationConfirm: "Dieses Konto wirklich zu {account} umziehen? Sobald der Umzug beginnt, kann er nicht rückgängig gemacht werden, und dieses Konto nicht wieder im ursprünglichen Zustand verwendet werden."
+ movedAndCannotBeUndone: "\nDieses Konto wurde migriert.\nDiese Aktion ist unwiderruflich."
+ postMigrationNote: "Dieses Konto wird 24 Stunden nach Abschluss der Migration allen Konten, denen es derzeit folgt, nicht mehr folgen.\n\nSowohl die Anzahl der Follower als auch die der Konten, denen dieses Konto folgt, wird dann auf Null gesetzt. Um zu vermeiden, dass Follower dieses Kontos dessen Beiträge, welche nur für Follower bestimmt sind, nicht mehr sehen können, werden sie diesem Konto jedoch weiterhin folgen."
+ movedTo: "Umzugsziel:"
_achievements:
earnedAt: "Freigeschaltet am"
_types:
@@ -1027,7 +1093,7 @@ _achievements:
title: "Supernotiz"
description: "10.000 Notizen gesendet"
_notes20000:
- title: "Brauche... mehr... Notizen"
+ title: "Brauche... mehr... Notizen..."
description: "20.000 Notizen gesendet"
_notes30000:
title: "Notizen, Notizen, Notizen"
@@ -1171,7 +1237,10 @@ _achievements:
description: "Du hast einen verborgenen Schatz gefunden"
_client30min:
title: "Kurze Pause"
- description: "Habe Misskey für 30 Minuten geöffnet"
+ description: "Habe Misskey für mindestens 30 Minuten geöffnet"
+ _client60min:
+ title: "Munter mit Misskey"
+ description: "Habe Misskey für mindestens 60 Minuten geöffnet"
_noteDeletedWithin1min:
title: "Ups"
description: "Lösche eine Notiz innerhalb von 1 Minute nachdem sie gesendet wurde"
@@ -1260,6 +1329,8 @@ _role:
iconUrl: "Icon-URL"
asBadge: "Als Abzeichen anzeigen"
descriptionOfAsBadge: "Ist dies aktiviert, so wird das Icon dieser Rolle an der Seite der Namen von Benutzern mit dieser Rolle angezeigt."
+ isExplorable: "Rollenchronik veröffentlichen"
+ descriptionOfIsExplorable: "Ist dies aktiviert, so ist die Rollenchronik dieser Rolle frei zugänglich. Die Chronik von Rollen, welche nicht öffentlich sind, wird auch bei Aktivierung nicht veröffentlicht."
displayOrder: "Position"
descriptionOfDisplayOrder: "Je höher die Nummer, desto höher die UI-Position."
canEditMembersByModerator: "Moderatoren können Benutzern diese Rolle zuweisen"
@@ -1276,6 +1347,7 @@ _role:
canInvite: "Erstellung von Einladungscodes für diese Instanz"
canManageCustomEmojis: "Benutzerdefinierte Emojis verwalten"
driveCapacity: "Drive-Kapazität"
+ alwaysMarkNsfw: "Dateien immer als NSFW markieren"
pinMax: "Maximale Anzahl an angehefteten Notizen"
antennaMax: "Maximale Anzahl an Antennen"
wordMuteMax: "Maximale Zeichenlänge für Wortstummschaltungen"
@@ -1527,32 +1599,16 @@ _time:
minute: "Minute(n)"
hour: "Stunde(n)"
day: "Tag(en)"
-_tutorial:
+_timelineTutorial:
title: "Wie du Misskey verwendest"
- step1_1: "Willkommen!"
- step1_2: "Diese Seite ist die „Chronik“. Sie zeigt dir deine geschrieben „Notizen“ sowie die aller Benutzer, denen du „folgst“, in chronologischer Reihenfolge."
- step1_3: "Deine Chronik sollte momentan leer sein, da du bis jetzt noch keine Notizen geschrieben hast und auch noch keinen Benutzern folgst."
- step2_1: "Lass uns zuerst dein Profil vervollständigen, bevor du Notizen schreibst oder jemandem folgst."
- step2_2: "Informationen darüber, was für eine Person du bist, macht es anderen leichter zu wissen, ob sie deine Notizen sehen wollen und ob sie dir folgen möchten."
- step3_1: "Mit dem Einrichten deines Profils fertig?"
- step3_2: "Dann lass uns als nächstes versuchen, eine Notiz zu schreiben. Dies kannst du tun, indem du auf den Knopf mit dem Stift-Icon auf dem Bildschirm drückst."
- step3_3: "Fülle das Fenster aus und drücke auf den Knopf oben rechts zum Senden."
- step3_4: "Fällt dir nichts ein, das du schreiben möchtest? Versuch's mit \"Hallo Misskey!\""
- step4_1: "Fertig mit dem Senden deiner ersten Notiz?"
- step4_2: "Falls deine Notiz nun in deiner Chronik auftaucht, hast du alles richtig gemacht."
- step5_1: "Lass uns nun deiner Chronik etwas mehr Leben einhauchen, indem du einigen anderen Benutzern folgst."
- step5_2: "{featured} zeigt dir beliebte Notizen dieser Instanz. In {explore} kannst du beliebte Benutzer finden. Schau dort, ob du Benutzer findest, die dich interessieren."
- step5_3: "Klicke zum Anzeigen des Profils eines Benutzers auf dessen Profilbild und dann auf den \"Folgen\"-Knopf, um diesem zu folgen."
- step5_4: "Je nach Benutzer kann es etwas Zeit in Anspruch nehmen, bis dieser deine Follow-Anfrage bestätigt."
- step6_1: "Wenn du nun auch die Notizen anderer Benutzer in deiner Chronik siehst, hast du auch diesmal alles richtig gemacht."
- step6_2: "Du kannst ebenso „Reaktionen“ verwenden, um schnell auf Notizen anderer Benutzer zu reagieren."
- step6_3: "Um eine Reaktion anzufügen, klicke auf das „+“-Symbol in der Notiz und wähle ein Emoji aus, mit dem du reagieren möchtest."
- step7_1: "Glückwunsch! Du hast die Einführung in die Verwendung von Misskey abgeschlossen."
- step7_2: "Wenn du mehr über Misskey lernen möchtest, schau dich im {help}-Bereich um."
- step7_3: "Und nun, viel Spaß mit Misskey! 🚀"
- step8_1: "Möchtest du abschließend Push-Benachrichtigungen aktivieren?"
- step8_2: "Push-Benachrichtigungen erlauben es dir, über Reaktionen, Follows oder Erwähnungen usw. zu erfahren, auch wenn Misskey zu dieser Zeit nicht geöffnet ist."
- step8_3: "Diese Einstellung kannst du jederzeit ändern."
+ step1_1: "Dieser Bildschirm ist die \"Chronik\". Hier werden alle \"Notizen\" von {name} angezeigt."
+ step1_2: "Es gibt einige verschiedene Chroniken. Beispielsweise werden in der \"Startseite\" alle Notizen von Nutzern, denen du folgst, angezeigt, und in der \"Lokalen Chronik\" werden Notizen aller Nutzer auf {name} angezeigt."
+ step2_1: "Lass uns als nächstes versuchen, eine Notiz zu schreiben. Dies kannst du tun, indem du auf den Knopf mit dem Stift-Icon drückst."
+ step2_2: "Stell dich den anderen vor oder schreibe einfach \"Hallo {name}!\", wenn du darauf keine Lust hast oder dir nichts einfällt."
+ step3_1: "Fertig mit dem Senden deiner ersten Notiz?"
+ step3_2: "Falls deine Notiz nun in deiner Chronik auftaucht, hast du alles richtig gemacht."
+ step4_1: "Notizen können zusätzlich mit \"Reaktionen\" ausgestattet werden."
+ step4_2: "Um eine Reaktion anzufügen, klicke auf das „+“-Symbol einer Notiz und wähle ein Emoji aus, mit dem du reagieren möchtest."
_2fa:
alreadyRegistered: "Du hast bereits ein Gerät für Zwei-Faktor-Authentifizierung registriert."
registerTOTP: "Authentifizierungs-App registrieren"
diff --git a/locales/el-GR.yml b/locales/el-GR.yml
index 32964e9982..41b1ea7c65 100644
--- a/locales/el-GR.yml
+++ b/locales/el-GR.yml
@@ -172,11 +172,11 @@ explore: "ΕξεÏευνήστε"
messageRead: "Διαβάστηκε"
startMessaging: "Ξεκινήστε μία συνομιλία"
nUsersRead: "διαβάστηκε από {n}"
-tos: "ÎŒÏοι χÏήσης"
start: "Ας αÏχίσουμε"
home: "ΚεντÏικό"
activity: "ΔÏαστηÏιότητα"
images: "Εικόνες"
+image: "Εικόνες"
birthday: "Γενέθλια"
registeredDate: "Έγινε μέλος στις"
location: "Τοποθεσία"
diff --git a/locales/en-US.yml b/locales/en-US.yml
index e2407bd3e7..ea91bcc0e5 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -263,14 +263,16 @@ noMoreHistory: "There is no further history"
startMessaging: "Start a new chat"
nUsersRead: "read by {n}"
agreeTo: "I agree to {0}"
+agree: "Agree"
agreeBelow: "I agree to the below"
basicNotesBeforeCreateAccount: "Important notes"
-tos: "Terms of Service"
+termsOfService: "Terms of Service"
start: "Begin"
home: "Home"
remoteUserCaution: "As this user is from a remote instance, the shown information may be incomplete."
activity: "Activity"
images: "Images"
+image: "Image"
birthday: "Birthday"
yearsOld: "{age} years old"
registeredDate: "Joined on"
@@ -468,12 +470,14 @@ enableAdvancedMfm: "Enable advanced MFM"
enableAnimatedMfm: "Enable animated MFM"
doing: "Processing..."
category: "Category"
-tags: "Tags"
+tags: "Aliases"
docSource: "Source of this document"
createAccount: "Create account"
existingAccount: "Existing account"
regenerate: "Regenerate"
fontSize: "Font size"
+mediaListWithOneImageAppearance: "Height of media lists with one image only"
+limitTo: "Limit to {x}"
noFollowRequests: "You don't have any pending follow requests"
openImageInNewTab: "Open images in new tab"
dashboard: "Dashboard"
@@ -556,6 +560,7 @@ accountDeletedDescription: "This account has been deleted."
menu: "Menu"
divider: "Divider"
addItem: "Add Item"
+rearrange: "Rearrange"
relays: "Relays"
addRelay: "Add Relay"
inboxUrl: "Inbox URL"
@@ -699,6 +704,8 @@ contact: "Contact"
useSystemFont: "Use the system's default font"
clips: "Clips"
experimentalFeatures: "Experimental features"
+experimental: "Experimental"
+thisIsExperimentalFeature: "This is an experimental feature. Its functionality is subject to change, and it may not operate as intended."
developer: "Developer"
makeExplorable: "Make account visible in \"Explore\""
makeExplorableDescription: "If you turn this off, your account will not show up in the \"Explore\" section."
@@ -905,6 +912,7 @@ remoteOnly: "Remote only"
failedToUpload: "Upload failed"
cannotUploadBecauseInappropriate: "This file could not be uploaded because parts of it have been detected as potentially NSFW."
cannotUploadBecauseNoFreeSpace: "Upload failed due to lack of Drive capacity."
+cannotUploadBecauseExceedsFileSizeLimit: "This file cannot be uploaded as it exceeds the file size limit."
beta: "Beta"
enableAutoSensitive: "Automatic NSFW-Marking"
enableAutoSensitiveDescription: "Allows automatic detection and marking of NSFW media through Machine Learning where possible. Even if this option is disabled, it may be enabled instance-wide."
@@ -918,8 +926,8 @@ subscribePushNotification: "Enable push notifications"
unsubscribePushNotification: "Disable push notifications"
pushNotificationAlreadySubscribed: "Push notifications are already enabled"
pushNotificationNotSupported: "Your browser or instance does not support push notifications"
-sendPushNotificationReadMessage: "Delete push notifications once the relevant notifications or messages have been read"
-sendPushNotificationReadMessageCaption: "A notification containing the text \"{emptyPushNotificationMessage}\" will be displayed for a short time. This may increase the power consumption of your device."
+sendPushNotificationReadMessage: "Delete push notifications once they have been read"
+sendPushNotificationReadMessageCaption: "This may increase the power consumption of your device."
windowMaximize: "Maximize"
windowMinimize: "Minimize"
windowRestore: "Restore"
@@ -938,6 +946,7 @@ didYouLikeMisskey: "Have you taken a liking to Misskey?"
pleaseDonate: "{host} uses the free software, Misskey. We would highly appreciate your donations so development of Misskey can continue!"
roles: "Roles"
role: "Role"
+noRole: "Role not found"
normalUser: "Normal user"
undefined: "Undefined"
assign: "Assign"
@@ -947,6 +956,10 @@ manageCustomEmojis: "Manage Custom Emojis"
youCannotCreateAnymore: "You've hit the creation limit."
cannotPerformTemporary: "Temporarily unavailable"
cannotPerformTemporaryDescription: "This action cannot be performed temporarily due to exceeding the execution limit. Please wait for a while and then try again."
+invalidParamError: "Invalid parameters"
+invalidParamErrorDescription: "The request parameters are invalid. This is normally caused by a bug, but may also be due to inputs exceeding size limits or similar."
+permissionDeniedError: "Operation denied"
+permissionDeniedErrorDescription: "This account does not have the permission to perform this action."
preset: "Preset"
selectFromPresets: "Choose from presets"
achievements: "Achievements"
@@ -990,17 +1003,70 @@ enableChartsForFederatedInstances: "Generate remote instance data charts"
showClipButtonInNoteFooter: "Add \"Clip\" to note action menu"
largeNoteReactions: "Enlargen displayed reactions"
noteIdOrUrl: "Note ID or URL"
+video: "Video"
+videos: "Videos"
+dataSaver: "Data Saver"
accountMigration: "Account Migration"
accountMoved: "This user has moved to a new account:"
+accountMovedShort: "This account has been migrated."
+operationForbidden: "Operation forbidden"
forceShowAds: "Always show ads"
+addMemo: "Add memo"
+editMemo: "Edit memo"
+reactionsList: "Reactions"
+renotesList: "Renotes"
+notificationDisplay: "Notifications"
+leftTop: "Top left"
+rightTop: "Top right"
+leftBottom: "Bottom left"
+rightBottom: "Bottom right"
+stackAxis: "Stacking direction"
+vertical: "Vertical"
+horizontal: "Horizontal"
+position: "Position"
+serverRules: "Server rules"
+pleaseConfirmBelowBeforeSignup: "Please confirm the below before signing up."
+pleaseAgreeAllToContinue: "You must agree to all above fields to continue."
+continue: "Continue"
+preservedUsernames: "Reserved usernames"
+preservedUsernamesDescription: "List usernames to reserve separated by linebreaks. These will become unable during normal account creation, but can be used by administrators to manually create accounts. Already existing accounts using these usernames will not be affected."
+createNoteFromTheFile: "Compose note from this file"
+archive: "Archive"
+channelArchiveConfirmTitle: "Really archive {name}?"
+channelArchiveConfirmDescription: "An archived channel won't appear in the channel list or search results anymore. New posts can also not be added to it anymore."
+thisChannelArchived: "This channel has been archived."
+displayOfNote: "Note display"
+initialAccountSetting: "Profile configuration"
+youFollowing: "Followed"
+_initialAccountSetting:
+ accountCreated: "Your account was successfully created!"
+ letsFillYourProfile: "First, let's set up your profile."
+ profileSetting: "Profile settings"
+ theseSettingsCanEditLater: "You can always change these settings later."
+ youCanEditMoreSettingsInSettingsPageLater: "There are many more settings you can configure from the \"Settings\" page. Be sure to visit it later."
+ followUsers: "Try following some users that interest you to build up your timeline."
+ pushNotificationDescription: "Enabling push notifications will allow you to receive notifications from {name} directly on your device."
+ initialAccountSettingCompleted: "Profile configuration complete!"
+ haveFun: "Enjoy {name}!"
+ ifYouNeedLearnMore: "If you'd like to learn more about how to use {name} (Misskey), please visit {link}."
+ skipAreYouSure: "Really skip profile configuration?"
+_serverRules:
+ description: "A set of rules to be displayed before registration. Setting a summary of the Terms of Service is recommended."
_accountMigration:
+ moveFrom: "Migrate another account to this one"
+ moveFromSub: "Create alias to another account"
+ moveFromLabel: "Original Account #{n}"
+ moveFromDescription: "You must create an alias for the account to move from on this account.\nEnter the account to migrate from in the following format: @username@server.example.com\nTo delete the alias, leave the field empty (not recommended)."
moveTo: "Migrate this account to a different one"
moveToLabel: "Account to move to:"
- moveAccountDescription: "This action is irreversible. First, create an alias for this account on the account you wish to move to. Then, enter the account to move to in the following format: @person@instance.com"
- moveFrom: "Migrate another account to this one"
- moveFromLabel: "Account to move from:"
- moveFromDescription: "Create an alias for the account to move from on this account if you wish to transfer its followers. This has to be done before the transfer! Then, enter the account to move to in the following format: @person@instance.com"
- migrationConfirm: "Really migrate this account to {account}? Once started, this process cannot be stopped or taken back, and you will not be able to use this account in its original state anymore.\n\nAlso, confirm you've created an alias at the account to migrate to."
+ moveCannotBeUndone: "Account migration cannot be undone."
+ moveAccountDescription: "This will migrate your account to a different one.\n ・Followers from this account will automatically be migrated to the new account\n ・This account will unfollow all users it is currently following\n ・You will be unable to create new notes etc. on this account\n\nWhile migration of followers is automatic, you must manually prepare some steps to migrate the list of users you are following. To do so, carry out a follows export that you will later import on the new account in the settings menu. The same procedure applies to your lists as well as your muted and blocked users.\n\n(This explanation applies to Misskey v13.12.0 and later. Other ActivityPub software, such as Mastodon, might function differently.)"
+ moveAccountHowTo: "To migrate, first create an alias for this account on the account to move to.\nAfter you have created the alias, enter the account to move to in the following format: @username@server.example.com"
+ startMigration: "Migrate"
+ migrationConfirm: "Really migrate this account to {account}? Once started, this process cannot be stopped or taken back, and you will not be able to use this account in its original state anymore."
+ movedAndCannotBeUndone: "\nThis account has been migrated.\nMigration cannot be reversed."
+ postMigrationNote: "This account will unfollow all accounts it is currently following 24 hours after migration finishes.\nBoth the number of follows and followers will then become zero. To avoid your followers from being unable to see followers only posts of this account, they will however continue following this account."
+ movedTo: "Account to move to:"
_achievements:
earnedAt: "Unlocked at"
_types:
@@ -1171,7 +1237,10 @@ _achievements:
description: "You've found the hidden treasure"
_client30min:
title: "Short break"
- description: "Spend 30 minutes on Misskey"
+ description: "Keep Misskey opened for at least 30 minutes"
+ _client60min:
+ title: "No \"Miss\" in Misskey"
+ description: "Keep Misskey opened for at least 60 minutes"
_noteDeletedWithin1min:
title: "Nevermind"
description: "Delete a note within a minute of posting it"
@@ -1260,6 +1329,8 @@ _role:
iconUrl: "Icon URL"
asBadge: "Show as badge"
descriptionOfAsBadge: "This role's icon will be displayed next to the username of users with this role if turned on."
+ isExplorable: "Role timeline is public"
+ descriptionOfIsExplorable: "This role's timeline will become publicly accessible if enabled. Timelines of non-public roles will not be made public even if set."
displayOrder: "Position"
descriptionOfDisplayOrder: "The higher the number, the higher its UI position."
canEditMembersByModerator: "Allow moderators to edit the list of members for this role"
@@ -1276,6 +1347,7 @@ _role:
canInvite: "Can create instance invite codes"
canManageCustomEmojis: "Can manage custom emojis"
driveCapacity: "Drive capacity"
+ alwaysMarkNsfw: "Always mark files as NSFW"
pinMax: "Maximum number of pinned notes"
antennaMax: "Maximum number of antennas"
wordMuteMax: "Maximum number of characters allowed in word mutes"
@@ -1527,32 +1599,16 @@ _time:
minute: "Minute(s)"
hour: "Hour(s)"
day: "Day(s)"
-_tutorial:
+_timelineTutorial:
title: "How to use Misskey"
- step1_1: "Welcome!"
- step1_2: "This page is called the \"timeline\". It shows chronologically ordered \"notes\" of people who you \"follow\"."
- step1_3: "Your timeline is currently empty, since you have not posted any notes or followed anyone yet."
- step2_1: "Let's finish setting up your profile before writing a note or following anyone."
- step2_2: "Providing some information about who you are will make it easier for others to tell if they want to see your notes or follow you."
- step3_1: "Finished setting up your profile?"
- step3_2: "Then let's try posting a note next. You can do so by pressing the button with a pencil icon on the screen."
- step3_3: "Fill in the modal and press the button on the top right to post."
- step3_4: "Have nothing to say? Try \"just setting up my msky\"!"
- step4_1: "Finished posting your first note?"
- step4_2: "Hurray! Now your first note should be displayed on your timeline."
- step5_1: "Now, let's try making your timeline more lively by following other people."
- step5_2: "{featured} will show you popular notes in this instance. {explore} will let you find popular users. Try finding people you'd like to follow there!"
- step5_3: "To follow other users, click on their icon and press the \"Follow\" button on their profile."
- step5_4: "If the other user has a lock icon next to their name, it may take some time for that user to manually approve your follow request."
- step6_1: "You should be able to see other users' notes on your timeline now."
- step6_2: "You can also put \"reactions\" on other people's notes to quickly respond to them."
- step6_3: "To attach a \"reaction\", press the \"+\" mark on another user's note and choose an emoji you'd like to react with."
- step7_1: "Congratulations! You have now finished Misskey's basic tutorial."
- step7_2: "If you would like to learn more about Misskey, try the {help} section."
- step7_3: "Now then, have fun with Misskey! 🚀"
- step8_1: "Lastly, would you like to enable push notifications?"
- step8_2: "Enabling these will allow you to receive notifications for mentions, reactions, follows, etc. even when Misskey is not opened."
- step8_3: "You can always change this setting later."
+ step1_1: "This is the \"timeline\". All \"notes\" submitted on {name} will be chronologically displayed here."
+ step1_2: "There are a few different timelines. For example, the \"Home timeline\" will contain notes of users you follow, and the \"Local timeline\" will contain notes from all users of {name}."
+ step2_1: "Let's try posting a note next. You can do so by pressing the button with a pencil icon."
+ step2_2: "How about writing a self-introduction, or just \"Hello {name}!\" if you don't feel like it?"
+ step3_1: "Finished posting your first note?"
+ step3_2: "Your first note should now be displayed on your timeline."
+ step4_1: "You can also attach \"Reactions\" to notes."
+ step4_2: "To attach a reaction, press the \"+\" mark on a note and choose an emoji you'd like to react with."
_2fa:
alreadyRegistered: "You have already registered a 2-factor authentication device."
registerTOTP: "Register authenticator app"
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index 29cf9b3ec9..42d52e5a2b 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -264,12 +264,12 @@ nUsersRead: "Leído por {n} personas"
agreeTo: "De acuerdo con {0}"
agreeBelow: "Estoy de acuerdo con lo siguiente"
basicNotesBeforeCreateAccount: "Notas básicas"
-tos: "Términos de uso"
start: "Comenzar"
home: "Inicio"
remoteUserCaution: "Para el usuario remoto, la información está incompleta"
activity: "Actividad"
images: "Imágenes"
+image: "Imágenes"
birthday: "Fecha de nacimiento"
yearsOld: "{age} años"
registeredDate: "Fecha de registro"
@@ -991,14 +991,17 @@ largeNoteReactions: "Agrandar las reacciones de las notas"
noteIdOrUrl: "ID o URL de la nota"
accountMigration: "Migración de cuenta"
accountMoved: "Este usuario se ha mudado a una nueva cuenta:"
+horizontal: "Horizontal"
+youFollowing: "Siguiendo"
_accountMigration:
- moveTo: "Mover esta cuenta a una nueva"
- moveToLabel: "Cuenta destino:"
- moveAccountDescription: "Esta operación no puede deshacerse. En primer lugar, asegúrese de haber creado un alias para esta cuenta en la cuenta a la que se va a trasladar. Después de crear el alias, introduzca la cuenta a la que se está trasladando de la siguiente manera: @person@instance.com"
moveFrom: "Trasladar de otra cuenta a ésta"
moveFromLabel: "Cuenta desde la que se realiza el traslado:"
moveFromDescription: "Si quieres transferir seguidores de otra cuenta a esta cuenta y trasladarlos, tendrás que crear un alias aquí. Asegúrate de crearlo antes de realizar el traslado. Introduce la cuenta desde la que estás moviendo los seguidores así: @person@instance.com"
+ moveTo: "Mover esta cuenta a una nueva"
+ moveToLabel: "Cuenta destino:"
+ moveAccountDescription: "Esta operación no puede deshacerse. En primer lugar, asegúrese de haber creado un alias para esta cuenta en la cuenta a la que se va a trasladar. Después de crear el alias, introduzca la cuenta a la que se está trasladando de la siguiente manera: @person@instance.com"
migrationConfirm: "¿Estás seguro de que quieres mover esta cuenta a {account}? Una vez trasladada, no podrás deshacer el traslado y no podrás volver a utilizar la cuenta original.\n\nAdemás, compruebe que ha configurado un alias en el destino del traslado."
+ movedTo: "Cuenta destino:"
_achievements:
earnedAt: "Desbloqueado el"
_types:
@@ -1523,32 +1526,6 @@ _time:
minute: "Minutos"
hour: "Horas"
day: "Días"
-_tutorial:
- title: "Cómo usar Misskey"
- step1_1: "Bienvenido"
- step1_2: "Esta imagen se llama \"Linea de tiempo\" y muestra en orden cronológico las \"notas\" tuyas y de la gente que \"sigues\""
- step1_3: "Si no estás escribiendo ninguna nota y no estás siguiendo a nadie, es esperable que no se muestre nada en la linea de tiempo"
- step2_1: "Antes de crear notas y seguir a alguien, primero vamos a crear tu perfil"
- step2_2: "Si provees información sobre quien eres, será más fácil para que otros usuarios te sigan"
- step3_1: "¿Has podido crear tu perfil sin problemas?"
- step3_2: "Con esto, prueba hacer una nota. Aprieta el botón con forma de lápiz que está arriba de la imagen y abre el formulario."
- step3_3: "Si has escrito el contenido, aprieta el botón que está arriba a la derecha del formulario para postear."
- step3_4: "¿No se te ocurre un contenido? Prueba con decir \"Empecé a usar Misskey\""
- step4_1: "¿Has posteado?"
- step4_2: "Si tu nota puede verse en la linea de tiempo, fue todo un éxito."
- step5_1: "Luego, ponte a seguir a otra gente y haz que tu linea de tiempo esté más animada."
- step5_2: "Puedes ver las notas destacadas en {featured} y desde allí seguir a usuarios que te importan. También puedes buscar usuario destacados en {explore}."
- step5_3: "Para seguir a un usuario, haz click en su avatar para ver su página de usuario y allí apretar el botón \"seguir\""
- step5_4: "De esa manera, puede pasar un tiempo hasta que el usuario apruebe al seguidor."
- step6_1: "Si puedes ver en la linea de tiempo las notas de otros usuarios, fue todo un éxito."
- step6_2: "En las notas de otros usuarios puedes añadir una \"reacción\", para poder responder rápidamente."
- step6_3: "Para añadir una reacción, haz click en el botón \"+\" de la nota y elige la reacción que prefieras."
- step7_1: "Así terminó la explicación del funcionamiento básico de Misskey. Eso fue todo."
- step7_2: "Si quieres conocer más sobre Misskey, prueba con la sección {help}."
- step7_3: "Así, disfruta de Misskey 🚀"
- step8_1: "Por último, ¿por qué no activar las notificaciones emergentes?"
- step8_2: "Al recibir notificaciones emergentes, estarás al tanto de reacciones, seguimientos y menciones incluso cuando Misskey no esté abierto."
- step8_3: "La configuración de las notificaciones puede modificarse posteriormente."
_2fa:
alreadyRegistered: "Ya has completado la configuración."
registerTOTP: "Registrar aplicación autenticadora"
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index e0767fdc09..e98ee9ff9b 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -20,6 +20,7 @@ noNotes: "Aucune note"
noNotifications: "Aucune notification"
instance: "Instance"
settings: "Paramètres"
+notificationSettings: "Paramètres des notifications "
basicSettings: "Paramètres généraux"
otherSettings: "Paramètres avancés"
openInWindow: "Ouvrir dans une nouvelle fenêtre"
@@ -259,12 +260,12 @@ noMoreHistory: "Il n’y a plus d’historique"
startMessaging: "Commencer à discuter"
nUsersRead: "Lu par {n} personnes"
agreeTo: "J’accepte {0}"
-tos: "les conditions d’utilisation"
start: "Commencer"
home: "Principal"
remoteUserCaution: "Les informations de ce compte risqueraient d’être incomplètes du fait que l’utilisateur·rice provient d’une instance distante."
activity: "Activité"
images: "Images"
+image: "Images"
birthday: "Date de naissance"
yearsOld: "{age} ans"
registeredDate: "Inscrit le"
@@ -838,13 +839,16 @@ tenMinutes: "10 minutes"
oneHour: "1 heure"
oneDay: "1 jour"
oneWeek: "1 semaine"
+oneMonth: "Un mois"
reflectMayTakeTime: "Cela peut prendre un certain temps avant que cela ne se termine."
failedToFetchAccountInformation: "Impossible de récupérer les informations du compte."
rateLimitExceeded: "Limite de taux dépassée"
cropImage: "Recadrer l'image"
cropImageAsk: "Voulez-vous recadrer cette image ?"
+cropYes: "Rogner"
file: "Fichiers"
recentNHours: "Dernières {n} heures"
+recentNDays: "Derniers {n} jours"
noEmailServerWarning: "Serveur de courrier non configuré."
thereIsUnresolvedAbuseReportWarning: "Il n’y a aucun rapport non résolu."
recommended: "Recommandé"
@@ -900,11 +904,47 @@ numberOfLikes: "Favoris"
show: "Affichage"
neverShow: "Ne plus afficher"
remindMeLater: "Peut-être plus tard"
+roles: "Rôles"
+role: "Rôles"
+normalUser: "Simple utilisateur·rice"
+assign: "Attribuer"
color: "Couleur"
+manageCustomEmojis: "Gestion des émojis personnalisés"
+preset: "Préréglage"
+selectFromPresets: "Sélectionner à partir des préréglages"
+license: "Licence"
+video: "Vidéo"
+videos: "Vidéos"
+dataSaver: "Économiseur de données"
+accountMigration: "Migration de compte"
+accountMoved: "Cet·te utilisateur·rice a migré son compte vers :"
+notificationDisplay: "Style des notifications"
+leftTop: "En haut à gauche"
+rightTop: "En haut à droite"
+leftBottom: "En bas à gauche"
+rightBottom: "En bas à droite"
+vertical: "Vertical"
+horizontal: "Latéral"
+youFollowing: "Abonné·e"
_achievements:
_types:
+ _notes1:
+ description: "Publiez votre première note"
+ flavor: "Passez un bon moment avec Misskey !"
_notes100000:
title: "ALL YOUR NOTE ARE BELONG TO US"
+ _login3:
+ description: "Se connecter pour un total de 3 jours"
+ _login7:
+ description: "Se connecter pour un total de 7 jours"
+ _login15:
+ description: "Se connecter pour un total de 15 jours"
+ _login30:
+ description: "Se connecter pour un total de 30 jours"
+ _login60:
+ description: "Se connecter pour un total de 60 jours"
+ _login100:
+ description: "Se connecter pour un total de 100 jours"
_login1000:
flavor: "Merci d'utiliser Misskey !"
_markedAsCat:
@@ -912,12 +952,21 @@ _achievements:
flavor: "Je n'ai pas encore de nom"
_following50:
title: "Beaucoup d'amis"
+ _followers10:
+ title: "Abonnez-moi !"
+ _viewInstanceChart:
+ title: "Analyste"
+ _loggedInOnNewYearsDay:
+ title: "Bonne année !"
_role:
+ assignTarget: "Attribuer"
priority: "Priorité"
_priority:
low: "Basse"
middle: "Moyen"
high: "Haute"
+ _options:
+ canManageCustomEmojis: "Gestion des émojis personnalisés"
_sensitiveMediaDetection:
description: "L'apprentissage automatique peut être utilisé pour détecter automatiquement les médias sensibles à modérer. La sollicitation des serveurs augmente légèrement."
sensitivity: "Sensibilité de la détection"
@@ -1141,31 +1190,6 @@ _time:
minute: "min"
hour: "h"
day: "j"
-_tutorial:
- title: "Comment utiliser Misskey"
- step1_1: "Bienvenue,"
- step1_2: "Cette page est appelée « un fil ». Elle affiche les « notes » des personnes auxquelles vous êtes abonné dans un ordre chronologique."
- step1_3: "Votre fil est actuellement vide vu que vous ne suivez aucun compte et que vous n’avez publié aucune note, pour l’instant."
- step2_1: "Procédons d’abord à la préparation de votre profil avant d’écrire une note et/ou de vous abonner à un compte."
- step2_2: "En fournissant quelques informations sur vous, il sera plus facile pour les autres de s’abonner à votre compte."
- step3_1: "Vous avez fini de créer votre profil ?"
- step3_2: "L’étape suivante consiste à créer une note. Vous pouvez commencer en cliquant sur l’icône crayon sur l’écran."
- step3_3: "Remplissez le cadran et cliquez sur le bouton en haut à droite pour envoyer."
- step3_4: "Vous n’avez rien à dire ? Essayez d’écrire « J’ai commencé à utiliser Misskey »."
- step4_1: "Avez-vous publié votre première note ?"
- step4_2: "Youpi ! Celle-ci est maintenant affichée sur votre fil d’actualité."
- step5_1: "Maintenant, essayons de nous abonner à d’autres personnes afin de rendre votre fil plus vivant."
- step5_2: "La page {featured} affiche les notes en tendance sur la présente instance et {explore} vous permet de trouver des utilisateur·rice·s en tendance. Essayez de vous abonner aux gens que vous aimez !"
- step5_3: "Pour pouvoir suivre d’autres utilisateur·rice, cliquez sur leur avatar afin d’afficher la page du profil utilisateur ensuite appuyez sur le bouton « S’abonner »."
- step5_4: "Si l’autre utilisateur possède une icône sous forme d’un cadenas à côté de son nom, il devra accepter votre demande d’abonnement manuellement."
- step6_1: "Maintenant, vous êtes en mesure de voir s’afficher les notes des autres utilisateur·rice·s sur votre propre fil."
- step6_2: "Vous avez également la possibilité d’intéragir rapidement avec les notes des autres utilisateur·rice·s en ajoutant des « réactions »."
- step6_3: "Pour ajouter une réaction à une note, cliquez sur le signe « + » de celle-ci et sélectionnez l’émoji souhaité."
- step7_1: "Félicitations ! Vous avez atteint la fin du tutoriel de base pour l’utilisation de Misskey."
- step7_2: "Si vous désirez en savoir plus sur Misskey, jetez un œil sur la section {help}."
- step7_3: "Bon courage et amusez-vous bien sur Misskey ! 🚀"
- step8_1: "Enfin, souhaitez-vous activer les notifications push ?"
- step8_2: "En les activant, vous recevrez des notifications pour les mentions, les réactions, les suivis, etc., même lorsque Misskey n'est pas ouvert."
_2fa:
alreadyRegistered: "Configuration déjà achevée."
step1: "Tout d'abord, installez une application d'authentification, telle que {a} ou {b}, sur votre appareil."
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index 97c0b02fe5..5159775fc4 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -263,12 +263,12 @@ nUsersRead: "Dibaca oleh {n}"
agreeTo: "Saya setuju kepada {0}"
agreeBelow: "Saya setuju dengan di bawah ini"
basicNotesBeforeCreateAccount: "Catatan penting"
-tos: "Syarat dan ketentuan"
start: "Mulai"
home: "Beranda"
remoteUserCaution: "Informasi ini mungkin tidak mutakhir, karena pengguna ini berasal dari instansi luar."
activity: "Aktivitas"
images: "Gambar"
+image: "Gambar"
birthday: "Tanggal lahir"
yearsOld: "{age} tahun"
registeredDate: "Bergabung pada"
@@ -953,6 +953,8 @@ letsLookAtTimeline: "LIhat timeline"
disableFederationConfirm: "Matikan federasi?"
disableFederationConfirmWarn: "Mematikan federasi tidak membuat kiriman menjadi privat. Umumnya, mematikan federasi tidak diperlukan."
disableFederationOk: "Matikan federasi"
+horizontal: "Horisontal"
+youFollowing: "Mengikuti"
_achievements:
earnedAt: "Terbuka pada"
_types:
@@ -1472,32 +1474,6 @@ _time:
minute: "menit"
hour: "jam"
day: "hari"
-_tutorial:
- title: "Cara menggunakan Misskey"
- step1_1: "Selamat datang!"
- step1_2: "Halaman ini disebut \"linimasa\". Halaman ini menampilkan \"catatan\" yang diurutkan secara kronologis dari orang-orang yang kamu \"ikuti\"."
- step1_3: "Linimasa kamu kosong, karena kamu belum mencatat catatan apapun atau mengikuti siapapun."
- step2_1: "Selesaikan menyetel profilmu sebelum menulis sebuah catatan atau mengikuti seseorang."
- step2_2: "Menyediakan beberapa informasi tentang siapa kamu akan membuat orang lain mudah untuk mengikutimu kembali."
- step3_1: "Selesai menyetel profil kamu?"
- step3_2: "Langkah selanjutnya adalah membuat catatan. Kamu bisa lakukan ini dengan mengklik ikon pensil pada layar kamu."
- step3_3: "Isilah di dalam modal dan tekan tombol pada atas kanan untuk memcatat catatan kamu."
- step3_4: "Bingung tidak berpikiran untuk mengatakan sesuatu? Coba saja \"baru aja ikutan bikin akun misskey punyaku\"!"
- step4_1: "Selesai mencatat catatan pertamamu?"
- step4_2: "Horee! Sekarang catatan pertamamu sudah ditampilkan di linimasa milikmu."
- step5_1: "Sekarang, mari mencoba untuk membuat linimasamu lebih hidup dengan mengikuti orang lain."
- step5_2: "{featured} akan memperlihatkan catatan yang sedang tren saat ini untuk kamu. {explore} akan membantumu untuk mencari pengguna yang sedang tren juga saat ini. Coba ikuti seseorang yang kamu suka!"
- step5_3: "Untuk mengikuti pengguna lain, klik pada ikon mereka dan tekan tombol follow pada profil mereka."
- step5_4: "Jika pengguna lain memiliki ikon gembok di sebelah nama mereka, maka pengguna rersebut harus menyetujui permintaan mengikuti dari kamu secara manual."
- step6_1: "Sekarang kamu dapat melihat catatan pengguna lain pada linimasamu."
- step6_2: "Kamu juga bisa memberikan \"reaksi\" ke catatan orang lain untuk merespon dengan cepat."
- step6_3: "Untuk memberikan \"reaksi\", tekan tanda \"+\" pada catatan pengguna lain dan pilih emoji yang kamu suka untuk memberikan reaksimu kepada mereka."
- step7_1: "Yay, Selamat! Kamu sudah menyelesaikan tutorial dasar Misskey."
- step7_2: "Jika kamu ingin mempelajari lebih lanjut tentang Misskey, cobalah berkunjung ke bagian {help}."
- step7_3: "Semoga berhasil dan bersenang-senanglah! 🚀"
- step8_1: "Yang terakhir, apakah kamu ingin menyalakam pemberitahuan push?"
- step8_2: "Menyalakan ini akan memungkinkan kamu menerima pemberitahuan untuk sebutan, reaksi, ikuti, dll. Bahkan ketika Misskey sedang tidak dibuka."
- step8_3: "Kamu dapat mengganti pengaturan ini nanti."
_2fa:
alreadyRegistered: "Kamu telah mendaftarkan perangkat otentikasi dua faktor."
step1: "Pertama, pasang aplikasi otentikasi (seperti {a} atau {b}) di perangkat kamu."
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index f43adcb35b..cacd91cac9 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -263,14 +263,16 @@ noMoreHistory: "Non c'è più cronologia da visualizzare"
startMessaging: "Nuovo messaggio"
nUsersRead: "Letto da {n} persone"
agreeTo: "Sono d'accordo con {0}"
+agree: "D'accordo"
agreeBelow: "Accetto quanto riportato sotto"
basicNotesBeforeCreateAccount: "Note importanti"
-tos: "Termini di servizio"
+termsOfService: "Informativa Privacy"
start: "Inizia!"
home: "Home"
remoteUserCaution: "Può darsi che le informazioni siano incomplete perché questo è un utente remoto."
activity: "Attività"
images: "Immagini"
+image: "Immagini"
birthday: "Compleanno"
yearsOld: "{age} anni"
registeredDate: "Iscrizione a.."
@@ -474,6 +476,8 @@ createAccount: "Crea il tuo profilo"
existingAccount: "Profilo esistente"
regenerate: "Generare di nuovo"
fontSize: "Dimensione carattere"
+mediaListWithOneImageAppearance: "Altezza dell'elenco media con una sola immagine "
+limitTo: "Limita a {x}"
noFollowRequests: "Non hai alcuna richiesta di follow"
openImageInNewTab: "Apri le immagini in un nuovo tab"
dashboard: "Pannello di controllo"
@@ -947,6 +951,10 @@ manageCustomEmojis: "Gestisci le emoji personalizzate"
youCannotCreateAnymore: "Non puoi creare, hai raggiunto il limite."
cannotPerformTemporary: "Indisponibilità temporanea"
cannotPerformTemporaryDescription: "L'attività non può essere svolta, poiché si è raggiunto il limite di esecuzioni possibili. Per favore, riprova più tardi."
+invalidParamError: "Errore, parametro non valido"
+invalidParamErrorDescription: "Riscontrato un errore nei parametri della richiesta. Solitamente potrebbe essere un malfunzionamento, ma a volte potrebbe essere causato dalla quantità eccessiva di caratteri nel parametro."
+permissionDeniedError: "Errore, attività non autorizzata"
+permissionDeniedErrorDescription: "Non si dispone dell'autorizzazione per eseguire questa operazione."
preset: "Preimpostato"
selectFromPresets: "Seleziona preimpostato"
achievements: "Obiettivi raggiunti"
@@ -970,7 +978,7 @@ invitationRequiredToRegister: "L'accesso a questa istanza è solo ad invito. PuÃ
emailNotSupported: "L'istanza non supporta l'invio di email"
postToTheChannel: "Pubblica nel canale"
cannotBeChangedLater: "Non sarà più modificabile"
-reactionAcceptance: "Accettazione reazioni"
+reactionAcceptance: "Reazioni consentite"
likeOnly: "Solo i Like"
likeOnlyForRemote: "Solo Like remoti"
rolesAssignedToMe: "I miei ruoli"
@@ -990,17 +998,39 @@ enableChartsForFederatedInstances: "Abilita i grafici per le istanze federate"
showClipButtonInNoteFooter: "Aggiungi il bottone Clip tra le azioni delle Note"
largeNoteReactions: "Ingrandisci le reazioni"
noteIdOrUrl: "ID della Nota o URL"
+video: "Video"
+videos: "Video"
+dataSaver: "Risparmia dati"
accountMigration: "Migrazione del profilo"
accountMoved: "Questo profilo ha migrato altrove:"
forceShowAds: "Mostra sempre i banner"
+addMemo: "Aggiungi Memo"
+editMemo: "Modifica Memo"
+notificationDisplay: "Stile delle notifiche"
+leftTop: "In alto a sinistra"
+rightTop: "In alto a destra"
+leftBottom: "In basso a sinistra"
+rightBottom: "In basso a destra"
+stackAxis: "Allineamento"
+vertical: "Verticale"
+horizontal: "Laterale"
+position: "Posizione"
+serverRules: "Regolamento"
+pleaseConfirmBelowBeforeSignup: "Ai sensi del regolamento EU 679/2016 GDPR, autorizzo il trattamento dati personali come descritto nella informativa Privacy."
+pleaseAgreeAllToContinue: "Per continuare, occorre selezionare ed essere d'accordo su tutto."
+continue: "Continua"
+youFollowing: "Seguiti"
+_serverRules:
+ description: "In Europa è necessario mostrare l'informativa sul trattamento dei dati personali, prima della registrazione al servizio."
_accountMigration:
- moveTo: "Migrare questo profilo verso un un altro"
- moveToLabel: "Profilo verso cui migrare"
- moveAccountDescription: "Questa attività è irreversibile! Innanzitutto, assicurati di aver creato, nella istanza di destinazione, un alias con l'indirizzo di questo profilo. Successivamente, indica qui il profilo di destinazione in questo modo: @persona@istanza.it"
moveFrom: "Migra un altro profilo dentro a questo"
moveFromLabel: "Profilo da cui migrare:"
moveFromDescription: "Se desideri spostare i profili follower da un altro profilo a questo, devi prima creare un alias qui. Assicurati averlo creato PRIMA di eseguire l'attività! Inserisci l'indirizzo del profilo mittente in questo modo: @persona@istanza.it"
+ moveTo: "Migrare questo profilo verso un un altro"
+ moveToLabel: "Profilo verso cui migrare"
+ moveAccountDescription: "Questa attività è irreversibile! Innanzitutto, assicurati di aver creato, nella istanza di destinazione, un alias con l'indirizzo di questo profilo. Successivamente, indica qui il profilo di destinazione in questo modo: @persona@istanza.it"
migrationConfirm: "Vuoi davvero migrare questo profilo su {account}? L'azione è irreversibile e non potrai più utilizzare questo profilo nel suo stato originale.\nInoltre, assicurati di aver già creato un alias sull'account a cui ti stai trasferendo."
+ movedTo: "Profilo verso cui migrare"
_achievements:
earnedAt: "Data di conseguimento"
_types:
@@ -1172,6 +1202,9 @@ _achievements:
_client30min:
title: "Piccola grande pausa"
description: "Hai passato più di 30 minuti su Misskey"
+ _client60min:
+ title: "Misskey negli occhi"
+ description: "Hai letto Misskey almeno per un'ora"
_noteDeletedWithin1min:
title: "Ooops!"
description: "Hai eliminato una nota entro un minuto dalla sua pubblicazione"
@@ -1260,6 +1293,8 @@ _role:
iconUrl: "URL dell'icona"
asBadge: "Mostra come badge"
descriptionOfAsBadge: "Se indicato, accanto al nome utente viene visualizzata l'icona del ruolo."
+ isExplorable: "La timeline del ruolo è pubblica"
+ descriptionOfIsExplorable: "Selezionandolo, la timeline del ruolo diventerà accessibile pubblicamente. Tranne se il ruolo non è pubblico."
displayOrder: "Ordine di visualizzazione"
descriptionOfDisplayOrder: "I valori più alti vengono visualizzati per primi"
canEditMembersByModerator: "Anche i Moderatori assegnano profili a questo ruolo"
@@ -1527,32 +1562,6 @@ _time:
minute: "min"
hour: "ore"
day: "giorni"
-_tutorial:
- title: "Come usare Misskey"
- step1_1: "Eccoci!"
- step1_2: "Questa pagina si chiama \"Timeline \" e mostra in ordine cronologico le \"note\" delle persone che segui."
- step1_3: "Attualmente la tua Timeline è vuota perché non segui alcun profilo e non hai ancora pubblicato alcuna nota."
- step2_1: "Prima di scrivere una «Nota» o di seguire altri profili, prepara il tuo profilo!"
- step2_2: "Se aggiungi informazioni personali aumenterai le tue possibilità di essere seguit@ da altre persone. "
- step3_1: "Hai finito di impostare il tuo profilo?"
- step3_2: "Ora puoi pubblicare una «Nota». Proviamo subito! Premi il bottone con l'icona «penna» per iniziare a scrivere in una finestra di dialogo. "
- step3_3: "Scritto il testo della nota, puoi pubblicarla premendo il pulsante nella parte superiore destra della finestra di dialogo."
- step3_4: "Non ti viene niente in mente? Perché non scrivi semplicemente \"Ho appena iniziato a usare Misskey\"?"
- step4_1: "Hai pubblicato qualcosa?"
- step4_2: "Se puoi visualizzare la tua nota sulla timeline, ce l'hai fatta!"
- step5_1: "Adesso, cerca di seguire altre persone per vivacizzare la tua timeline. "
- step5_2: "La pagina {featured} mostra le note di tendenza su questa istanza, magari ti aiuterà a trovare profili che ti piacciono e che vorrai seguire. Altrimenti potrai trovare utenti popolari anche usando {explore}."
- step5_3: "Clicca l'immagine per aprire il profilo e premere il bottone \"Seguire\""
- step5_4: "Se l'altro profilo ha un lucchetto vicino al nome, significa che occorre un po' di tempo prima che approvi manualmente la tua richiesta di follow."
- step6_1: "Adesso, dovresti essere in grado di vedere le note dagli altri profili sulla tua timeline."
- step6_2: "Puoi anche rispondere alle note con un click, scegliendo le reazioni immediate."
- step6_3: "Per inviare una reazione, premi l'icona + della nota e scegli l'emoji che vuoi mandare."
- step7_1: "Congratulazioni! Hai completato l'esercitazione iniziale su come usare Misskey."
- step7_2: "Se vuoi saperne di più su Misskey, puoi dare un'occhiata alla sezione {help}."
- step7_3: "Da ultimo, buon divertimento su Misskey! 🚀"
- step8_1: "Per concludere, vuoi attivare le notifiche push?"
- step8_2: "Attivandole, otterrai notifiche di follow, reazioni e menzioni anche quando Misskey è chiuso."
- step8_3: "Potrai modificare questa impostazione."
_2fa:
alreadyRegistered: "La configurazione è stata già completata."
registerTOTP: "Registra un'app di autenticazione"
@@ -1890,6 +1899,7 @@ _deck:
channel: "Canale"
mentions: "Menzioni"
direct: "Diretta"
+ roleTimeline: "Timeline Ruolo"
_dialog:
charactersExceeded: "Hai superato il limite di {max} caratteri! ({corrente})"
charactersBelow: "Sei al di sotto del minimo di {min} caratteri! ({corrente})"
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 092a4aed32..8bbf9459f5 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -263,14 +263,16 @@ noMoreHistory: "ã“れよりéŽåŽ»ã®å±¥æ­´ã¯ã‚りã¾ã›ã‚“"
startMessaging: "ãƒãƒ£ãƒƒãƒˆã‚’é–‹å§‹"
nUsersRead: "{n}人ãŒèª­ã¿ã¾ã—ãŸ"
agreeTo: "{0}ã«åŒæ„"
+agree: "åŒæ„ã™ã‚‹"
agreeBelow: "下記ã«åŒæ„ã™ã‚‹"
basicNotesBeforeCreateAccount: "åŸºæœ¬çš„ãªæ³¨æ„事項"
-tos: "利用è¦ç´„"
+termsOfService: "利用è¦ç´„"
start: "å§‹ã‚ã‚‹"
home: "ホーム"
remoteUserCaution: "リモートユーザーã®ãŸã‚ã€æƒ…å ±ãŒä¸å®Œå…¨ã§ã™ã€‚"
activity: "アクティビティ"
images: "ç”»åƒ"
+image: "ç”»åƒ"
birthday: "誕生日"
yearsOld: "{age}æ­³"
registeredDate: "登録日"
@@ -474,6 +476,8 @@ createAccount: "アカウントを作æˆ"
existingAccount: "既存ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ"
regenerate: "å†ç”Ÿæˆ"
fontSize: "フォントサイズ"
+mediaListWithOneImageAppearance: "ç”»åƒãŒ1æžšã®ã¿ã®ãƒ¡ãƒ‡ã‚£ã‚¢ãƒªã‚¹ãƒˆã®é«˜ã•"
+limitTo: "{x}を上é™ã«"
noFollowRequests: "フォロー申請ã¯ã‚りã¾ã›ã‚“"
openImageInNewTab: "ç”»åƒã‚’æ–°ã—ã„タブã§é–‹ã"
dashboard: "ダッシュボード"
@@ -556,6 +560,7 @@ accountDeletedDescription: "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯å‰Šé™¤ã•れã¦ã„ã¾ã™ã€‚"
menu: "メニュー"
divider: "分割線"
addItem: "項目を追加"
+rearrange: "ä¸¦ã³æ›¿ãˆ"
relays: "リレー"
addRelay: "リレーã®è¿½åŠ "
inboxUrl: "inboxã®URL"
@@ -699,6 +704,8 @@ contact: "連絡先"
useSystemFont: "システムã®ãƒ‡ãƒ•ォルトã®ãƒ•ォントを使ã†"
clips: "クリップ"
experimentalFeatures: "実験的機能"
+experimental: "実験的"
+thisIsExperimentalFeature: "ã“れã¯å®Ÿé¨“çš„ãªæ©Ÿèƒ½ã§ã™ã€‚仕様ãŒå¤‰æ›´ã•れãŸã‚Šã€æ­£å¸¸ã«å‹•作ã—ãªã‹ã£ãŸã‚Šã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚"
developer: "開発者"
makeExplorable: "アカウントを見ã¤ã‘ã‚„ã™ãã™ã‚‹"
makeExplorableDescription: "オフã«ã™ã‚‹ã¨ã€ã€Œã¿ã¤ã‘ã‚‹ã€ã«ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒè¼‰ã‚‰ãªããªã‚Šã¾ã™ã€‚"
@@ -905,6 +912,7 @@ remoteOnly: "リモートã®ã¿"
failedToUpload: "アップロード失敗"
cannotUploadBecauseInappropriate: "ä¸é©åˆ‡ãªå†…容をå«ã‚€å¯èƒ½æ€§ãŒã‚ã‚‹ã¨åˆ¤å®šã•れãŸãŸã‚アップロードã§ãã¾ã›ã‚“。"
cannotUploadBecauseNoFreeSpace: "ドライブã®ç©ºã容é‡ãŒç„¡ã„ãŸã‚アップロードã§ãã¾ã›ã‚“。"
+cannotUploadBecauseExceedsFileSizeLimit: "ファイルサイズã®åˆ¶é™ã‚’è¶…ãˆã¦ã„ã‚‹ãŸã‚アップロードã§ãã¾ã›ã‚“。"
beta: "ベータ"
enableAutoSensitive: "自動NSFW判定"
enableAutoSensitiveDescription: "利用å¯èƒ½ãªå ´åˆã¯ã€æ©Ÿæ¢°å­¦ç¿’を利用ã—ã¦è‡ªå‹•ã§ãƒ¡ãƒ‡ã‚£ã‚¢ã«NSFWフラグを設定ã—ã¾ã™ã€‚ã“ã®æ©Ÿèƒ½ã‚’オフã«ã—ã¦ã‚‚ã€ã‚µãƒ¼ãƒãƒ¼ã«ã‚ˆã£ã¦ã¯è‡ªå‹•ã§è¨­å®šã•れるã“ã¨ãŒã‚りã¾ã™ã€‚"
@@ -938,6 +946,7 @@ didYouLikeMisskey: "Misskeyã‚’æ°—ã«å…¥ã£ã¦ã„ãŸã ã‘ã¾ã—ãŸã‹ï¼Ÿ"
pleaseDonate: "Misskeyã¯{host}ãŒä½¿ç”¨ã—ã¦ã„ã‚‹ç„¡æ–™ã®ã‚½ãƒ•トウェアã§ã™ã€‚ã“れã‹ã‚‰ã‚‚開発を続ã‘られるよã†ã«ã€ãœã²å¯„付をãŠé¡˜ã„ã—ã¾ã™ï¼"
roles: "ロール"
role: "ロール"
+noRole: "ロールã¯ã‚りã¾ã›ã‚“"
normalUser: "一般ユーザー"
undefined: "未定義"
assign: "アサイン"
@@ -947,6 +956,10 @@ manageCustomEmojis: "カスタム絵文字ã®ç®¡ç†"
youCannotCreateAnymore: "ã“れ以上作æˆã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。"
cannotPerformTemporary: "一時的ã«åˆ©ç”¨ã§ãã¾ã›ã‚“"
cannotPerformTemporaryDescription: "æ“作回数ãŒåˆ¶é™ã‚’è¶…éŽã™ã‚‹ãŸã‚一時的ã«åˆ©ç”¨ã§ãã¾ã›ã‚“。ã—ã°ã‚‰ã時間を置ã„ã¦ã‹ã‚‰å†åº¦ãŠè©¦ã—ãã ã•ã„。"
+invalidParamError: "パラメータエラー"
+invalidParamErrorDescription: "リクエストパラメータã«å•題ãŒã‚りã¾ã™ã€‚通常ã“れã¯ãƒã‚°ã§ã™ãŒã€å…¥åŠ›ã—ãŸæ–‡å­—æ•°ãŒå¤šã™ãŽã‚‹ç­‰ã®å¯èƒ½æ€§ã‚‚ã‚りã¾ã™ã€‚"
+permissionDeniedError: "æ“ä½œãŒæ‹’å¦ã•れã¾ã—ãŸ"
+permissionDeniedErrorDescription: "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ã¯ã“ã®æ“作を行ã†ãŸã‚ã®æ¨©é™ãŒã‚りã¾ã›ã‚“。"
preset: "プリセット"
selectFromPresets: "プリセットã‹ã‚‰é¸æŠž"
achievements: "実績"
@@ -990,18 +1003,74 @@ enableChartsForFederatedInstances: "リモートサーãƒãƒ¼ã®ãƒãƒ£ãƒ¼ãƒˆã‚’ç”
showClipButtonInNoteFooter: "ノートã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã«ã‚¯ãƒªãƒƒãƒ—を追加"
largeNoteReactions: "ノートã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’大ãã表示"
noteIdOrUrl: "ノートIDã¾ãŸã¯URL"
-accountMigration: "アカウントã®å¼•ã£è¶Šã—"
-accountMoved: "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯æ–°ã—ã„アカウントã«å¼•ã£è¶Šã—ã¾ã—ãŸï¼š"
+video: "å‹•ç”»"
+videos: "å‹•ç”»"
+dataSaver: "データセーãƒãƒ¼"
+accountMigration: "アカウントã®ç§»è¡Œ"
+accountMoved: "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯æ–°ã—ã„アカウントã«ç§»è¡Œã—ã¾ã—ãŸï¼š"
+accountMovedShort: "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ç§»è¡Œã•れã¦ã„ã¾ã™"
+operationForbidden: "ã“ã®æ“作ã¯ã§ãã¾ã›ã‚“"
forceShowAds: "常ã«åºƒå‘Šã‚’表示ã™ã‚‹"
+addMemo: "メモを追加"
+editMemo: "メモを編集"
+reactionsList: "リアクション一覧"
+renotesList: "Renote一覧"
+notificationDisplay: "通知ã®è¡¨ç¤º"
+leftTop: "左上"
+rightTop: "å³ä¸Š"
+leftBottom: "左下"
+rightBottom: "å³ä¸‹"
+stackAxis: "スタック方å‘"
+vertical: "縦"
+horizontal: "横"
+position: "ä½ç½®"
+serverRules: "サーãƒãƒ¼ãƒ«ãƒ¼ãƒ«"
+pleaseConfirmBelowBeforeSignup: "ã“ã®ã‚µãƒ¼ãƒãƒ¼ã«ç™»éŒ²ã™ã‚‹å‰ã«ã€ä»¥ä¸‹ã‚’確èªã—ã¦ãã ã•ã„。"
+pleaseAgreeAllToContinue: "ç¶šã‘ã‚‹ã«ã¯ã€å…¨ã¦ã®ã€ŒåŒæ„ã™ã‚‹ã€ã«ãƒã‚§ãƒƒã‚¯ãŒå…¥ã£ã¦ã„ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚"
+continue: "ç¶šã‘ã‚‹"
+preservedUsernames: "予約ユーザーå"
+preservedUsernamesDescription: "予約ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼åを改行ã§åˆ—挙ã—ã¾ã™ã€‚ã“ã“ã§æŒ‡å®šã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼åã¯ã‚¢ã‚«ã‚¦ãƒ³ãƒˆä½œæˆæ™‚ã«ä½¿ãˆãªããªã‚Šã¾ã™ãŒã€ç®¡ç†è€…ã«ã‚ˆã‚‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆä½œæˆæ™‚ã¯ã“ã®åˆ¶é™ã‚’å—ã‘ã¾ã›ã‚“。ã¾ãŸã€æ—¢ã«å­˜åœ¨ã™ã‚‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚‚影響をå—ã‘ã¾ã›ã‚“。"
+createNoteFromTheFile: "ã“ã®ãƒ•ァイルã‹ã‚‰ãƒŽãƒ¼ãƒˆã‚’作æˆ"
+archive: "アーカイブ"
+channelArchiveConfirmTitle: "{name}をアーカイブã—ã¾ã™ã‹ï¼Ÿ"
+channelArchiveConfirmDescription: "アーカイブã™ã‚‹ã¨ã€ãƒãƒ£ãƒ³ãƒãƒ«ä¸€è¦§ã‚„æ¤œç´¢çµæžœã«è¡¨ç¤ºã•れãªããªã‚Šã€æ–°ãŸãªæ›¸ãè¾¼ã¿ã‚‚ã§ããªããªã‚Šã¾ã™ã€‚"
+thisChannelArchived: "ã“ã®ãƒãƒ£ãƒ³ãƒãƒ«ã¯ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–ã•れã¦ã„ã¾ã™ã€‚"
+displayOfNote: "ノートã®è¡¨ç¤º"
+initialAccountSetting: "åˆæœŸè¨­å®š"
+youFollowing: "フォロー中"
+
+_initialAccountSetting:
+ accountCreated: "アカウントã®ä½œæˆãŒå®Œäº†ã—ã¾ã—ãŸï¼"
+ letsStartAccountSetup: "アカウントã®åˆæœŸè¨­å®šã‚’行ã„ã¾ã—ょã†ã€‚"
+ letsFillYourProfile: "ã¾ãšã¯ã‚ãªãŸã®ãƒ—ロフィールを設定ã—ã¾ã—ょã†ã€‚"
+ profileSetting: "プロフィール設定"
+ theseSettingsCanEditLater: "ã“れらã®è¨­å®šã¯å¾Œã‹ã‚‰å¤‰æ›´ã§ãã¾ã™ã€‚"
+ youCanEditMoreSettingsInSettingsPageLater: "ã“ã®ä»–ã«ã‚‚様々ãªè¨­å®šã‚’「設定ã€ãƒšãƒ¼ã‚¸ã‹ã‚‰è¡Œãˆã¾ã™ã€‚ãœã²å¾Œã§ç¢ºèªã—ã¦ã¿ã¦ãã ã•ã„。"
+ followUsers: "タイムラインを構築ã™ã‚‹ãŸã‚ã€æ°—ã«ãªã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’フォローã—ã¦ã¿ã¾ã—ょã†ã€‚"
+ pushNotificationDescription: "プッシュ通知を有効ã«ã™ã‚‹ã¨{name}ã®é€šçŸ¥ã‚’ãŠä½¿ã„ã®ãƒ‡ãƒã‚¤ã‚¹ã§å—ã‘å–ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
+ initialAccountSettingCompleted: "åˆæœŸè¨­å®šãŒå®Œäº†ã—ã¾ã—ãŸï¼"
+ haveFun: "{name}ã‚’ãŠæ¥½ã—ã¿ãã ã•ã„ï¼"
+ ifYouNeedLearnMore: "{name}(Misskey)ã®ä½¿ã„æ–¹ãªã©ã‚’詳ã—ã知るã«ã¯{link}ã‚’ã”覧ãã ã•ã„。"
+ skipAreYouSure: "åˆæœŸè¨­å®šã‚’スキップã—ã¾ã™ã‹ï¼Ÿ"
+
+_serverRules:
+ description: "æ–°è¦ç™»éŒ²å‰ã«è¡¨ç¤ºã™ã‚‹ã€ã‚µãƒ¼ãƒãƒ¼ã®ç°¡æ½”ãªãƒ«ãƒ¼ãƒ«ã‚’設定ã—ã¾ã™ã€‚内容ã¯åˆ©ç”¨è¦ç´„ã®è¦ç´„ã¨ã™ã‚‹ã“ã¨ã‚’推奨ã—ã¾ã™ã€‚"
_accountMigration:
- moveTo: "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’æ–°ã—ã„アカウントã«å¼•ã£è¶Šã™"
- moveToLabel: "引ã£è¶Šã—å…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆï¼š"
- moveAccountDescription: "ã“ã®æ“作ã¯å–り消ã›ã¾ã›ã‚“。ã¾ãšã¯å¼•ã£è¶Šã—å…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«å¯¾ã—エイリアスを作æˆã—ãŸã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。エイリアス作æˆå¾Œã€å¼•ã£è¶Šã—å…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ã“ã®ã‚ˆã†ã«å…¥åŠ›ã—ã¦ãã ã•ã„:@person@instance.com"
- moveFrom: "別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«å¼•ã£è¶Šã™"
- moveFromLabel: "引ã£è¶Šã—å…ƒã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆï¼š"
- moveFromDescription: "別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ãƒ•ォロワーを引ãç¶™ã„ã§å¼•ã£è¶Šã—ãŸã„å ´åˆã€ã“ã“ã§ã‚¨ã‚¤ãƒªã‚¢ã‚¹ã‚’作æˆã—ã¦ãŠãå¿…è¦ãŒã‚りã¾ã™ã€‚å¿…ãšå¼•ã£è¶Šã—を実行ã™ã‚‹å‰ã«ä½œæˆã—ã¦ãã ã•ã„ï¼å¼•ã£è¶Šã—å…ƒã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ã“ã®ã‚ˆã†ã«å…¥åŠ›ã—ã¦ãã ã•ã„:@person@instance.com"
- migrationConfirm: "本当ã«ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ {account} ã«å¼•ã£è¶Šã—ã¾ã™ã‹ï¼Ÿä¸€åº¦å¼•ã£è¶Šã—を行ã†ã¨å–り消ã›ãšã€äºŒåº¦ã¨ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’å…ƒã®çŠ¶æ…‹ã§ä½¿ç”¨ã§ããªããªã‚Šã¾ã™ã€‚\nã¾ãŸã€å¼•ã£è¶Šã—å…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã‚¨ã‚¤ãƒªã‚¢ã‚¹ã‚’作æˆã—ãŸã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。"
+ moveFrom: "別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ç§»è¡Œ"
+ moveFromSub: "別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¸ã‚¨ã‚¤ãƒªã‚¢ã‚¹ã‚’作æˆ"
+ moveFromLabel: "移行元ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ #{n}"
+ moveFromDescription: "別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ç§»è¡Œã—ãŸã„å ´åˆã€ã“ã“ã§ã‚¨ã‚¤ãƒªã‚¢ã‚¹ã‚’作æˆã—ã¦ãŠãå¿…è¦ãŒã‚りã¾ã™ã€‚\n移行元ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ã“ã®ã‚ˆã†ã«å…¥åŠ›ã—ã¦ãã ã•ã„: @username@server.example.com\n削除ã™ã‚‹ã«ã¯ã€å…¥åŠ›æ¬„ã‚’ç©ºã«ã—ã¦ä¿å­˜ã—ã¾ã™ï¼ˆéžæŽ¨å¥¨ï¼‰ã€‚"
+ moveTo: "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’æ–°ã—ã„アカウントã¸ç§»è¡Œ"
+ moveToLabel: "移行先ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ:"
+ moveCannotBeUndone: "アカウントを移行ã™ã‚‹ã¨ã€å–り消ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“。"
+ moveAccountDescription: "æ–°ã—ã„アカウントã¸ç§»è¡Œã—ã¾ã™ã€‚\nã€€ãƒ»ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒæ–°ã—ã„アカウントを自動ã§ãƒ•ォローã—ã¾ã™\n ・ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã®ãƒ•ォローã¯å…¨ã¦è§£é™¤ã•れã¾ã™\n ・ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã¯ãƒŽãƒ¼ãƒˆã®ä½œæˆãªã©ãŒã§ããªããªã‚Šã¾ã™\n\nフォロワーã®ç§»è¡Œã¯è‡ªå‹•ã§ã™ãŒã€ãƒ•ォローã®ç§»è¡Œã¯æ‰‹å‹•ã§è¡Œã†å¿…è¦ãŒã‚りã¾ã™ã€‚移行å‰ã«ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ãƒ•ォローエクスãƒãƒ¼ãƒˆã—ã€ç§»è¡Œå¾Œã™ãã«ç§»è¡Œå…ˆã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã‚¤ãƒ³ãƒãƒ¼ãƒˆã‚’行ãªã£ã¦ãã ã•ã„。\nリスト・ミュート・ブロックã«ã¤ã„ã¦ã‚‚åŒæ§˜ã§ã™ã®ã§ã€æ‰‹å‹•ã§ç§»è¡Œã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚\n\n(ã“ã®èª¬æ˜Žã¯ã“ã®ã‚µãƒ¼ãƒãƒ¼ï¼ˆMisskey v13.12.0以é™ï¼‰ã®ä»•様ã§ã™ã€‚Mastodonãªã©ã®ä»–ã®ActivityPubソフトウェアã§ã¯æŒ™å‹•ãŒç•°ãªã‚‹å ´åˆãŒã‚りã¾ã™ã€‚)"
+ moveAccountHowTo: "アカウントã®ç§»è¡Œã«ã¯ã€ã¾ãšã¯ç§»è¡Œå…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«å¯¾ã—エイリアスを作æˆã—ã¾ã™ã€‚\nエイリアス作æˆå¾Œã€ç§»è¡Œå…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’次ã®ã‚ˆã†ã«å…¥åŠ›ã—ã¦ãã ã•ã„: @username@server.example.com"
+ startMigration: "移行ã™ã‚‹"
+ migrationConfirm: "本当ã«ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ {account} ã«ç§»è¡Œã—ã¾ã™ã‹ï¼Ÿä¸€åº¦ç§»è¡Œã™ã‚‹ã¨å–り消ã›ãšã€äºŒåº¦ã¨ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’å…ƒã®çŠ¶æ…‹ã§ä½¿ç”¨ã§ããªããªã‚Šã¾ã™ã€‚"
+ movedAndCannotBeUndone: "\nアカウントã¯ç§»è¡Œã•れã¦ã„ã¾ã™ã€‚\n移行をå–り消ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“。"
+ postMigrationNote: "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã®ãƒ•ォロー解除ã¯ç§»è¡Œæ“作ã‹ã‚‰24時間後ã«å®Ÿè¡Œã•れã¾ã™ã€‚\nã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ãƒ•ォロー・フォロワー数ã¯0ã«ãªã£ã¦ã„ã¾ã™ã€‚フォロワーã®è§£é™¤ã¯ã•れãªã„ãŸã‚ã€ã‚ãªãŸã®ãƒ•ォロワーã¯ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ãƒ•ォロワーå‘ã‘æŠ•ç¨¿ã‚’å¼•ãç¶šã閲覧ã§ãã¾ã™ã€‚"
+ movedTo: "移行先ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ:"
_achievements:
earnedAt: "ç²å¾—日時"
@@ -1174,6 +1243,9 @@ _achievements:
_client30min:
title: "ã²ã¨ã‚„ã™ã¿"
description: "クライアントを起動ã—ã¦ã‹ã‚‰30分以上経éŽã—ãŸ"
+ _client60min:
+ title: "Misskeyã®è¦‹ã™ãŽ"
+ description: "クライアントを起動ã—ã¦ã‹ã‚‰60分以上経éŽã—ãŸ"
_noteDeletedWithin1min:
title: "ã„ã¾ã®ãªã—"
description: "投稿ã—ã¦ã‹ã‚‰1分以内ã«ãã®æŠ•ç¨¿ã‚’å‰Šé™¤ã—ãŸ"
@@ -1263,6 +1335,8 @@ _role:
iconUrl: "アイコン画åƒã®URL"
asBadge: "ãƒãƒƒã‚¸ã¨ã—ã¦è¡¨ç¤º"
descriptionOfAsBadge: "オンã«ã™ã‚‹ã¨ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼åã®æ¨ªã«ãƒ­ãƒ¼ãƒ«ã®ã‚¢ã‚¤ã‚³ãƒ³ãŒè¡¨ç¤ºã•れã¾ã™ã€‚"
+ isExplorable: "ロールタイムラインを公開"
+ descriptionOfIsExplorable: "オンã«ã™ã‚‹ã¨ã€ãƒ­ãƒ¼ãƒ«ã®ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã‚’公開ã—ã¾ã™ã€‚ロールã®å…¬é–‹ãŒã‚ªãƒ•ã®å ´åˆã€ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã®å…¬é–‹ã¯ã•れã¾ã›ã‚“。"
displayOrder: "表示順"
descriptionOfDisplayOrder: "数値ãŒå¤§ãã„ã»ã©UI上ã§å…ˆé ­ã«è¡¨ç¤ºã•れã¾ã™ã€‚"
canEditMembersByModerator: "モデレーターã®ãƒ¡ãƒ³ãƒãƒ¼ç·¨é›†ã‚’許å¯"
@@ -1279,6 +1353,7 @@ _role:
canInvite: "サーãƒãƒ¼æ‹›å¾…コードã®ç™ºè¡Œ"
canManageCustomEmojis: "カスタム絵文字ã®ç®¡ç†"
driveCapacity: "ドライブ容é‡"
+ alwaysMarkNsfw: "ファイルã«NSFWを常ã«ä»˜ä¸Ž"
pinMax: "ノートã®ãƒ”ン留ã‚ã®æœ€å¤§æ•°"
antennaMax: "アンテナã®ä½œæˆå¯èƒ½æ•°"
wordMuteMax: "ãƒ¯ãƒ¼ãƒ‰ãƒŸãƒ¥ãƒ¼ãƒˆã®æœ€å¤§æ–‡å­—æ•°"
@@ -1556,32 +1631,16 @@ _time:
hour: "時間"
day: "æ—¥"
-_tutorial:
+_timelineTutorial:
title: "Misskeyã®ä½¿ã„æ–¹"
- step1_1: "よã†ã“ã。"
- step1_2: "ã“ã®ç”»é¢ã¯ã€Œã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã€ã¨å‘¼ã°ã‚Œã€ã‚ãªãŸã‚„ã€ã‚ãªãŸãŒã€Œãƒ•ォローã€ã™ã‚‹äººã®ã€ŒãƒŽãƒ¼ãƒˆã€ãŒæ™‚系列ã§è¡¨ç¤ºã•れã¾ã™ã€‚"
- step1_3: "ã‚ãªãŸã¯ã¾ã ä½•もノートを投稿ã—ã¦ãŠã‚‰ãšã€èª°ã‚‚フォローã—ã¦ã„ãªã„ã®ã§ã€ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã«ã¯ä½•も表示ã•れã¦ã„ãªã„ã¯ãšã§ã™ã€‚"
- step2_1: "ノートを作æˆã—ãŸã‚Šèª°ã‹ã‚’フォローã—ãŸã‚Šã™ã‚‹å‰ã«ã€ã¾ãšã‚ãªãŸã®ãƒ—ロフィールを完æˆã•ã›ã¾ã—ょã†ã€‚"
- step2_2: "ã‚ãªãŸãŒã©ã‚“ãªäººã‹ã‚ã‹ã‚‹ã¨ã€å¤šãã®äººã«ãƒŽãƒ¼ãƒˆã‚’見ã¦ã‚‚らãˆãŸã‚Šã€ãƒ•ォローã—ã¦ã‚‚らã„ã‚„ã™ããªã‚Šã¾ã™ã€‚"
- step3_1: "プロフィール設定ã¯ã†ã¾ãã§ãã¾ã—ãŸã‹ï¼Ÿ"
- step3_2: "ã§ã¯è©¦ã—ã«ã€ä½•ã‹ãƒŽãƒ¼ãƒˆã‚’投稿ã—ã¦ã¿ã¦ãã ã•ã„。画é¢ä¸Šã«ã‚る鉛筆マークã®ãƒœã‚¿ãƒ³ã‚’押ã™ã¨ãƒ•ォームãŒé–‹ãã¾ã™ã€‚"
- step3_3: "内容を書ã„ãŸã‚‰ã€ãƒ•ォームå³ä¸Šã®ãƒœã‚¿ãƒ³ã‚’押ã™ã¨æŠ•稿ã§ãã¾ã™ã€‚"
- step3_4: "å†…å®¹ãŒæ€ã„ã¤ã‹ãªã„?「Misskeyå§‹ã‚ã¾ã—ãŸã€ã¨ã„ã†ã®ã¯ã„ã‹ãŒã§ã—ょã†ã€‚"
- step4_1: "投稿ã§ãã¾ã—ãŸã‹ï¼Ÿ"
- step4_2: "ã‚ãªãŸã®ãƒŽãƒ¼ãƒˆãŒã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã«è¡¨ç¤ºã•れã¦ã„ã‚Œã°æˆåŠŸã§ã™ã€‚"
- step5_1: "次ã¯ã€ä»–ã®äººã‚’フォローã—ã¦ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã‚’賑やã‹ã«ã—ãŸã„ã¨ã“ã‚ã§ã™ã€‚"
- step5_2: "{featured}ã§äººæ°—ã®ãƒŽãƒ¼ãƒˆãŒè¦‹ã‚Œã‚‹ã®ã§ã€ãã®ä¸­ã‹ã‚‰æ°—ã«ãªã£ãŸäººã‚’é¸ã‚“ã§ãƒ•ォローã—ãŸã‚Šã€{explore}ã§äººæ°—ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’探ã™ã“ã¨ã‚‚ã§ãã¾ã™ã€‚"
- step5_3: "ユーザーをフォローã™ã‚‹ã«ã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã‚¢ã‚¤ã‚³ãƒ³ã‚’クリックã—ã¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ãƒšãƒ¼ã‚¸ã‚’表示ã—ã€ã€Œãƒ•ォローã€ãƒœã‚¿ãƒ³ã‚’押ã—ã¾ã™ã€‚"
- step5_4: "ユーザーã«ã‚ˆã£ã¦ã¯ã€ãƒ•ã‚©ãƒ­ãƒ¼ãŒæ‰¿èªã•れるã¾ã§æ™‚é–“ãŒã‹ã‹ã‚‹å ´åˆãŒã‚りã¾ã™ã€‚"
- step6_1: "タイムラインã«ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆãŒè¡¨ç¤ºã•れã¦ã„ã‚Œã°æˆåŠŸã§ã™ã€‚"
- step6_2: "ä»–ã®äººã®ãƒŽãƒ¼ãƒˆã«ã¯ã€ã€Œãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã€ã‚’付ã‘ã‚‹ã“ã¨ãŒã§ãã€ç°¡å˜ã«ã‚ãªãŸã®å応をä¼ãˆã‚‰ã‚Œã¾ã™ã€‚"
- step6_3: "リアクションを付ã‘ã‚‹ã«ã¯ã€ãƒŽãƒ¼ãƒˆã®ã€Œ+ã€ãƒžãƒ¼ã‚¯ã‚’クリックã—ã¦ã€å¥½ããªãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’é¸æŠžã—ã¾ã™ã€‚"
- step7_1: "ã“れã§ã€Misskeyã®åŸºæœ¬çš„ãªä½¿ã„æ–¹ã®èª¬æ˜Žã¯çµ‚ã‚りã¾ã—ãŸã€‚ãŠç–²ã‚Œæ§˜ã§ã—ãŸã€‚"
- step7_2: "ã‚‚ã£ã¨Misskeyã«ã¤ã„ã¦çŸ¥ã‚ŠãŸã„ã¨ãã¯ã€{help}を見ã¦ã¿ã¦ãã ã•ã„。"
- step7_3: "ã§ã¯ã€Misskeyã‚’ãŠæ¥½ã—ã¿ãã ã•ã„🚀"
- step8_1: "最後ã«ã€ãƒ—ッシュ通知を有効化ã—ã¦ã¿ã¾ã›ã‚“ã‹ï¼Ÿ"
- step8_2: "プッシュ通知をå—ã‘å–ã‚‹ã“ã¨ã§ã€Misskeyã‚’é–‹ã„ã¦ã„ãªã„時ã«ã‚‚リアクションやフォローã€ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ãªã©ã«æ°—ã¥ã‘ã¾ã™ã€‚"
- step8_3: "通知ã®è¨­å®šã¯å¾Œã‹ã‚‰å¤‰æ›´ã§ãã¾ã™ã€‚"
+ step1_1: "ã“ã®ç”»é¢ã¯ã€Œã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã€ã§ã™ã€‚{name}ã«æŠ•ç¨¿ã•れãŸã€ŒãƒŽãƒ¼ãƒˆã€ãŒæ™‚系列ã§è¡¨ç¤ºã•れã¾ã™ã€‚"
+ step1_2: "タイムラインã«ã¯ã„ãã¤ã‹ç¨®é¡žãŒã‚りã€ä¾‹ãˆã°ã€Œãƒ›ãƒ¼ãƒ ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã€ã«ã¯ã‚ãªãŸãŒãƒ•ォローã—ã¦ã„る人ã®ãƒŽãƒ¼ãƒˆãŒæµã‚Œã€ã€Œãƒ­ãƒ¼ã‚«ãƒ«ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã€ã«ã¯{name}全体ã®ãƒŽãƒ¼ãƒˆãŒæµã‚Œã¾ã™ã€‚"
+ step2_1: "試ã—ã«ã€ä½•ã‹ãƒŽãƒ¼ãƒˆã‚’投稿ã—ã¦ã¿ã¾ã—ょã†ã€‚ç”»é¢ä¸Šã«ã‚る鉛筆マークã®ãƒœã‚¿ãƒ³ã‚’押ã™ã¨ãƒ•ォームãŒé–‹ãã¾ã™ã€‚"
+ step2_2: "åˆã‚ã¦ã®ãƒŽãƒ¼ãƒˆã®å†…容ã¯ã€ã‚ãªãŸã®è‡ªå·±ç´¹ä»‹ã‚„「{name}å§‹ã‚ã¾ã—ãŸã€ãªã©ãŒãŠã™ã™ã‚ã§ã™ã€‚"
+ step3_1: "投稿ã§ãã¾ã—ãŸã‹ï¼Ÿ"
+ step3_2: "ã‚ãªãŸã®ãƒŽãƒ¼ãƒˆãŒã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã«è¡¨ç¤ºã•れã¦ã„ã‚Œã°æˆåŠŸã§ã™ã€‚"
+ step4_1: "ノートã«ã¯ã€ã€Œãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã€ã‚’付ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
+ step4_2: "リアクションを付ã‘ã‚‹ã«ã¯ã€ãƒŽãƒ¼ãƒˆã®ã€Œ+ã€ãƒžãƒ¼ã‚¯ã‚’クリックã—ã¦ã€å¥½ããªçµµæ–‡å­—ã‚’é¸æŠžã—ã¾ã™ã€‚"
_2fa:
alreadyRegistered: "æ—¢ã«è¨­å®šã¯å®Œäº†ã—ã¦ã„ã¾ã™ã€‚"
@@ -1763,7 +1822,7 @@ _profile:
metadataDescription: "プロフィールã«è¡¨ã¨ã—ã¦è¿½åŠ æƒ…å ±ã‚’è¡¨ç¤ºã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
metadataLabel: "ラベル"
metadataContent: "内容"
- changeAvatar: "ã‚¢ãƒã‚¿ãƒ¼ç”»åƒã‚’変更"
+ changeAvatar: "アイコン画åƒã‚’変更"
changeBanner: "ãƒãƒŠãƒ¼ç”»åƒã‚’変更"
_exportOrImport:
diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml
index 4117a25b0e..512cce1452 100644
--- a/locales/ja-KS.yml
+++ b/locales/ja-KS.yml
@@ -1,7 +1,7 @@
---
_lang_: "日本語 (関西å¼)"
headlineMisskey: "ノートã§ã¤ãªãŒã‚‹ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯"
-introMisskey: "よã†ãŠè¶Šã—ï¼Misskeyã¯ã€ã‚ªãƒ¼ãƒ—ンソースã®åˆ†æ•£åž‹ãƒžã‚¤ã‚¯ãƒ­ãƒ–ログサービスやã­ã‚“。\n「ノートã€ã‚’作ã£ã¦ã€ã„ã¾èµ·ã“ã£ã¨ã‚‹ã“ã¨ã‚’共有ã—ãŸã‚Šã€ã‚ã‚“ãŸã«ã¤ã„ã¦çš†ã«ç™ºä¿¡ã—よã†ðŸ“¡\nã€Œãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã€æ©Ÿèƒ½ã§ã€çš†ã®ãƒŽãƒ¼ãƒˆã«ç´ æ—©ãå応を追加ã—ãŸã‚Šã‚‚ã§ãã‚‹ã§âœŒ\nã»ãªæ–°ã—ã„世界を探検ã—よã‹ðŸš€"
+introMisskey: "よã†ãŠè¶Šã—ï¼Misskeyã¯ã€ã‚ªãƒ¼ãƒ—ンソースã®åˆ†æ•£åž‹ãƒžã‚¤ã‚¯ãƒ­ãƒ–ログサービスやã­ã‚“。\n「ノートã€ã‚’作ã£ã¦ã€ã„ã¾èµ·ã“ã£ã¨ã‚‹ã“ã¨ã‚’共有ã—ãŸã‚Šã€ã‚ã‚“ãŸã«ã¤ã„ã¦çš†ã«ç™ºä¿¡ã—よã†ðŸ“¡\nã€Œãƒ„ãƒƒã‚³ãƒŸã€æ©Ÿèƒ½ã§ã€çš†ã®ãƒŽãƒ¼ãƒˆã«ç´ æ—©ãå応を追加ã—ãŸã‚Šã‚‚ã§ãã‚‹ã§âœŒ\nã»ãªæ–°ã—ã„世界を探検ã—よã‹ðŸš€"
poweredByMisskeyDescription: "{name}ã¯ã€ã‚ªãƒ¼ãƒ—ンソースã®ãƒ—ラットフォーム<b>Misskey</b>ã®ã‚µãƒ¼ãƒãƒ¼ã®ã²ã¨ã¤ãªã‚“ã‚„ã§ã€‚"
monthAndDay: "{month}月 {day}日"
search: "探ã™"
@@ -26,7 +26,7 @@ otherSettings: "ã»ã‹ã®è¨­å®š"
openInWindow: "ウィンドウã§é–‹ãã§"
profile: "プロフィール"
timeline: "タイムライン"
-noAccountDescription: "自己紹介ã¯ã‚らã¸ã‚“"
+noAccountDescription: "自己紹介食ã£ã¦ã‚‚ãŸ"
login: "ログイン"
loggingIn: "ログインã—よるã§"
logout: "ログアウト"
@@ -38,9 +38,9 @@ addUser: "ユーザーを追加や"
favorite: "ãŠæ°—ã«å…¥ã‚Š"
favorites: "ãŠæ°—ã«å…¥ã‚Š"
unfavorite: "ã‚„ã£ã±æ°—ã«å…¥ã‚‰ã‚“"
-favorited: "ãŠæ°—ã«å…¥ã‚Šã«ç™»éŒ²ã—ãŸã§"
+favorited: "ãŠæ°—ã«å…¥ã‚Šã«å…¥ã‚ŒãŸã§"
alreadyFavorited: "ã‚‚ã†ãŠæ°—ã«å…¥ã‚Šã«å…¥ã‚Œã¨ã‚‹ãŒãªã€‚"
-cantFavorite: "アカンã€ãŠæ°—ã«å…¥ã‚Šç™»éŒ²ã§ãã¸ã‚“ã‹ã£ãŸã§ã€‚"
+cantFavorite: "アカンã€ãŠæ°—ã«å…¥ã‚Šã«å…¥ã‚Œã‚Œã‚“ã‹ã£ãŸã‚。"
pin: "ピン留ã‚ã—ã¨ã"
unpin: "ã‚„ã£ã±ãƒ”ン留ã‚ã›ã‚“"
copyContent: "内容をコピー"
@@ -61,7 +61,7 @@ youGotNewFollower: "フォローã•れãŸã§"
receiveFollowRequest: "フォローリクエストã•れãŸã§"
followRequestAccepted: "ãƒ•ã‚©ãƒ­ãƒ¼ãŒæ‰¿èªã•れãŸã§"
mention: "メンション"
-mentions: "ã†ã¡å®›ã¦"
+mentions: "自分宛ã¦"
directNotes: "ダイレクト投稿"
importAndExport: "インãƒãƒ¼ãƒˆã¨ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ"
import: "インãƒãƒ¼ãƒˆ"
@@ -109,12 +109,12 @@ inChannelQuote: "ãƒãƒ£ãƒ³ãƒãƒ«å†…引用"
pinnedNote: "ピン留ã‚ã•れã¨ã‚‹ãƒŽãƒ¼ãƒˆ"
pinned: "ピン留ã‚ã—ã¨ã"
you: "ã‚ã‚“ãŸ"
-clickToShow: "押ã—ãŸã‚‰å‡ºã‚‰"
+clickToShow: "押ã—ãŸã‚‰è¦‹ãˆã‚‹ã§"
sensitive: "æ°—ã„ã¤ã‘ã¦è¦‹ã„ã‚„"
add: "増やã™"
-reaction: "リアクション"
-reactions: "リアクション"
-reactionSetting: "ピッカーã«å‡ºã—ã¨ãリアクション"
+reaction: "ツッコミ"
+reactions: "ツッコミ"
+reactionSetting: "ピッカーã«å‡ºã—ã¨ãツッコミ"
reactionSettingDescription2: "ドラッグã§ä¸¦ã³æ›¿ãˆã€ã‚¯ãƒªãƒƒã‚¯ã§å‰Šé™¤ã€ï¼‹ã‚’押ã—ã¦è¿½åŠ ã‚„ã§ã€‚"
rememberNoteVisibility: "公開範囲覚ãˆã¨ã„ã¦"
attachCancel: "ã®ã£ã‘ã‚‹ã®ã‚„ã‚ã‚‹"
@@ -263,14 +263,16 @@ noMoreHistory: "ã“れより昔ã®ã‚“ã¯ã‚らã¸ã‚“ã§"
startMessaging: "ãƒãƒ£ãƒƒãƒˆã‚„ã‚‹ã§"
nUsersRead: "{n}人ãŒèª­ã‚“ã§ã‚‚ã†ãŸ"
agreeTo: "{0}ã«åŒæ„ã—ãŸã§"
+agree: "ã›ã‚„ãª"
agreeBelow: "下記ã«åŒæ„ã—ãŸã‚‹"
basicNotesBeforeCreateAccount: "よã†èª­ã‚“ã§ã‚„ã£ã¦ã‚„"
-tos: "利用è¦ç´„"
+termsOfService: "使ã†ãŸã‚ã®æ±ºã‚事"
start: "å§‹ã‚ã‚‹"
home: "ホーム"
remoteUserCaution: "リモートユーザーやã‹ã‚‰ã€è¶³ã‚Šã²ã‚“情報ã‚ã‚‹ã‹ã‚‚ã—れã¸ã‚“。"
activity: "アクティビティ"
images: "ç”»åƒ"
+image: "ç”»åƒ"
birthday: "生ã¾ã‚ŒãŸæ—¥"
yearsOld: "{age}æ­³"
registeredDate: "å§‹ã‚ãŸæ—¥"
@@ -322,7 +324,7 @@ instanceName: "サーãƒãƒ¼å"
instanceDescription: "サーãƒãƒ¼ã®ç´¹ä»‹"
maintainerName: "管ç†è€…ã¯ã‚“ã®åå‰"
maintainerEmail: "管ç†è€…ã¯ã‚“ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹"
-tosUrl: "利用è¦ç´„ã®URL"
+tosUrl: "使ã†ãŸã‚ã®æ±ºã‚事ã®URL"
thisYear: "今年"
thisMonth: "今月"
today: "今日"
@@ -460,7 +462,7 @@ uiLanguage: "UIã®è¡¨ç¤ºè¨€èªž"
aboutX: "{x}ã«ã¤ã„ã¦"
emojiStyle: "絵文字ã®ã‚¹ã‚¿ã‚¤ãƒ«"
native: "ãƒã‚¤ãƒ†ã‚£ãƒ–"
-disableDrawer: "メニューをドロワーã§è¡¨ç¤ºã›ã‡ã¸ã‚“"
+disableDrawer: "メニューをドロワーã§è¡¨ç¤ºã›ãˆã¸ã‚“"
showNoteActionsOnlyHover: "ãƒŽãƒ¼ãƒˆã®æ“作部をホãƒãƒ¼æ™‚ã®ã¿è¡¨ç¤ºã™ã‚‹ã§"
noHistory: "履歴ã¯ãªã„ã‚。"
signinHistory: "ログイン履歴"
@@ -474,6 +476,8 @@ createAccount: "アカウントを作るã§"
existingAccount: "å‰ã«ä½œã£ãŸã‚¢ã‚«ã‚¦ãƒ³ãƒˆ"
regenerate: "ã‚‚ã£ãºã‚“生æˆã™ã‚‹ã§"
fontSize: "å­—ã®å¤§ãã•"
+mediaListWithOneImageAppearance: "ç”»åƒãŒ1æžšã®ã¿ã®ãƒ¡ãƒ‡ã‚£ã‚¢ãƒªã‚¹ãƒˆã®é«˜ã•"
+limitTo: "{x}ã‚’ã„ã£ã±ã„ã«"
noFollowRequests: "フォロー申請ã¯ã‚らã¸ã‚“ã§"
openImageInNewTab: "ç”»åƒã‚’æ–°ã—ã„タブã§é–‹ãã§"
dashboard: "ダッシュボード"
@@ -548,7 +552,7 @@ removeAllFollowingDescription: "{host}ã‹ã‚‰ã®ãƒ•ォローをã™ã¹ã¦è§£é™¤ã™
userSuspended: "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯...å‡çµã•れã¨ã‚‹ã€‚"
userSilenced: "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯...サイレンスã•れã¨ã‚‹ã€‚"
yourAccountSuspendedTitle: "ã‚ã‚“ãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆå‡çµã•れã¨ã‚‹ã§"
-yourAccountSuspendedDescription: "ã‚ã‚“ãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ã€ã‚µãƒ¼ãƒãƒ¼ã®åˆ©ç”¨è¦ç´„ã«é•åã—ãŸã¨ã‹ã®ç†ç”±ã§ã€å‡çµã•れã¨ã‚‹ã§ã€‚ç´°ã‹ã„ã“ã¨ã¯ç®¡ç†è€…ã¾ã§ãŠå•ã„åˆã‚ã›ãŸã£ã¦ãªãƒ¼ã€‚çµ¶å¯¾ã«æ–°ã—ã„アカウント作ã£ãŸã‚‰ã‚ã‹ã‚“ã§ã€‚絶対やã§ã€‚"
+yourAccountSuspendedDescription: "ã‚ã‚“ãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ã€ã‚µãƒ¼ãƒãƒ¼ã®ä½¿ã†ãŸã‚ã®æ±ºã‚事ã«é•åã—ãŸã¨ã‹ã®ç†ç”±ã§ã€å‡çµã•れã¨ã‚‹ã§ã€‚ç´°ã‹ã„ã“ã¨ã¯ç®¡ç†è€…ã¾ã§ãŠå•ã„åˆã‚ã›ãŸã£ã¦ãªãƒ¼ã€‚çµ¶å¯¾ã«æ–°ã—ã„アカウント作ã£ãŸã‚‰ã‚ã‹ã‚“ã§ã€‚絶対やã§ã€‚"
tokenRevoked: "トークンãŒç„¡åŠ¹ã‚„ã§"
tokenRevokedDescription: "ログイントークンãŒå¤±åйã—ã¨ã‚‹ã§ã€‚ã‚‚ã£ã‹ã„ログインã—ã¦ã‚‚ã‚ã¦ã‚‚ãˆãˆã‹ï¼Ÿ"
accountDeleted: "アカウントã¯å‰Šé™¤ã•れã¨ã‚‹ã§"
@@ -556,6 +560,7 @@ accountDeletedDescription: "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯å‰Šé™¤ã•れã¨ã‚‹ã§ã€‚"
menu: "メニュー"
divider: "分割線"
addItem: "項目を追加"
+rearrange: "ä¸¦ã³æ›¿ãˆ"
relays: "リレー"
addRelay: "リレーã®è¿½åŠ "
inboxUrl: "inboxã®URL"
@@ -582,7 +587,7 @@ preferencesBackups: "設定ã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—"
deck: "デッキ"
undeck: "デッキ解除"
useBlurEffectForModal: "モーダルã«ã¼ã‹ã—効果を使用"
-useFullReactionPicker: "フル機能ã«ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ãƒ”ッカーを使用"
+useFullReactionPicker: "フル機能ã®çªã£è¾¼ã¿ãƒ”ッカーを使用"
width: "å¹…"
height: "高ã•"
large: "大"
@@ -675,8 +680,8 @@ repliedCount: "返信ã•ã‚ŒãŸæ•°ã‚„ã§"
renotedCount: "Renoteã•ã‚ŒãŸæ•°ã‚„ã§"
followingCount: "フォロー数やã§"
followersCount: "フォロワー数やã§"
-sentReactionsCount: "リアクションã—ãŸæ•°ã‚„ã§"
-receivedReactionsCount: "リアクションã•ã‚ŒãŸæ•°"
+sentReactionsCount: "ãƒ„ãƒƒã‚³ã‚“ã æ•°"
+receivedReactionsCount: "ツッコã¾ã‚ŒãŸæ•°"
pollVotesCount: "ã‚¢ãƒ³ã‚±ãƒ¼ãƒˆã«æŠ•ç¥¨ã—ãŸæ•°"
pollVotedCount: "ã‚¢ãƒ³ã‚±ãƒ¼ãƒˆã«æŠ•ç¥¨ã•ã‚ŒãŸæ•°"
yes: "ãˆãˆã§"
@@ -699,6 +704,8 @@ contact: "連絡先"
useSystemFont: "システムã®ãƒ‡ãƒ•ォルトã®ãƒ•ォントを使ã†ã§"
clips: "クリップ"
experimentalFeatures: "ãŠãŸã‚ã—æ©Ÿèƒ½ã‚„ã§"
+experimental: "実験的"
+thisIsExperimentalFeature: "ã“れã¯å®Ÿé¨“çš„ãªæ©Ÿèƒ½ã‚„ã§ã€‚仕様ãŒå¤‰æ›´ã«ãªã£ãŸã‚Šã¡ã‚ƒã‚“ã¨å‹•ã‹ãªã‹ã£ãŸã‚Šã™ã‚‹ã‹ã‚‚ã‚„ã§ã€‚"
developer: "開発者やã§"
makeExplorable: "アカウントを見ã¤ã‘ã‚„ã™ãã™ã‚‹ã§"
makeExplorableDescription: "オフã«ã™ã‚‹ã¨ã€ã€Œã¿ã¤ã‘ã‚‹ã€ã«ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒè¼‰ã‚‰ã‚“ããªã‚‹ã§ã€‚"
@@ -745,7 +752,7 @@ receiveAnnouncementFromInstance: "サーãƒãƒ¼ã‹ã‚‰ã®ãŠçŸ¥ã‚‰ã›ã‚’å—ã‘å–ã
emailNotification: "メール通知"
publish: "公開"
inChannelSearch: "ãƒãƒ£ãƒ³ãƒãƒ«å†…検索"
-useReactionPickerForContextMenu: "å³ã‚¯ãƒªãƒƒã‚¯ã§ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ãƒ”ッカーを開ãよã†ã«ã™ã‚‹"
+useReactionPickerForContextMenu: "å³ã‚¯ãƒªãƒƒã‚¯ã§ãƒ„ッコミピッカーを開ãよã†ã«ã™ã‚‹"
typingUsers: "{users}ãŒä»Šæ›¸ãよるã§"
jumpToSpecifiedDate: "ç‰¹å®šã®æ—¥ä»˜ã«ã‚¸ãƒ£ãƒ³ãƒ—"
showingPastTimeline: "éŽåŽ»ã®ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã‚’表示ã—ã¦ã‚‹ã§"
@@ -830,8 +837,8 @@ unread: "未読"
filter: "フィルタ"
controlPanel: "コントロールパãƒãƒ«"
manageAccounts: "アカウントを管ç†"
-makeReactionsPublic: "リアクション一覧を公開ã™ã‚‹ã§"
-makeReactionsPublicDescription: "ã‚ã‚“ãŸãŒã—ãŸãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ä¸€è¦§ã‚’誰ã§ã‚‚見れるよã†ã«ã™ã‚‹ã§ã€‚"
+makeReactionsPublic: "ツッコミ一覧を公開ã™ã‚‹ã§"
+makeReactionsPublicDescription: "ã‚ã‚“ãŸãŒã—ãŸãƒ„ッコミ一覧を誰ã§ã‚‚見れるよã†ã«ã™ã‚‹ã§ã€‚"
classic: "クラシック"
muteThread: "スレッドをミュート"
unmuteThread: "スレッドã®ãƒŸãƒ¥ãƒ¼ãƒˆã‚’解除"
@@ -905,6 +912,7 @@ remoteOnly: "リモートã®ã¿"
failedToUpload: "アップロードã«å¤±æ•—ã—ã¦ã‚‚ã†ãŸã‚…"
cannotUploadBecauseInappropriate: "ä¸é©åˆ‡ãªå†…容をå«ã‚€ã‹ã‚‚ã—れã¸ã‚“ã£ã¦åˆ¤å®šã•れãŸã‹ã‚‰ã‚¢ãƒƒãƒ—ロードã§ãã¸ã‚“ã‚。"
cannotUploadBecauseNoFreeSpace: "ドライブã®ç©ºã容é‡ãŒç„¡ã„ã‹ã‚‰ã‚¢ãƒƒãƒ—ロードã§ãã¸ã‚“ã‚。"
+cannotUploadBecauseExceedsFileSizeLimit: "ãƒ•ã‚¡ã‚¤ãƒ«ãŒæ€ã†ãŸã‚ˆã‚Šã‚‚大ãã„ã•ã‹ã„アップロードã§ãã¸ã‚“ã§ã“れ。"
beta: "ベータ"
enableAutoSensitive: "自動NSFW判定"
enableAutoSensitiveDescription: "使ãˆã‚‹æ™‚ã¯ã€æ©Ÿæ¢°å­¦ç¿’を使ã£ã¦è‡ªå‹•ã§ãƒ¡ãƒ‡ã‚£ã‚¢ã«NSFWフラグを設定ã™ã‚‹ã§ã€‚ã“ã®æ©Ÿèƒ½ã‚’オフã«ã—ã¦ã‚‚ã€ã‚µãƒ¼ãƒãƒ¼ã«ã‚ˆã£ã¦ã¯è‡ªå‹•ã§è¨­å®šã•れるã“ã¨ãŒã‚ã‚‹ã§ã€‚"
@@ -938,6 +946,7 @@ didYouLikeMisskey: "Misskeyæ°—ã«å…¥ã£ã¦ãれãŸï¼Ÿ"
pleaseDonate: "Misskeyã¯{host}ãŒä½¿ç”¨ã—ã¦ã„ã‚‹ç„¡æ–™ã®ã‚½ãƒ•トウェアやã§ã€‚ã“れã‹ã‚‰ã‚‚開発を続ã‘れるよã†ã«ã€å¯„付ã—ãŸã£ã¦ãªï½žã€‚"
roles: "ロール"
role: "ロール"
+noRole: "ロールã¯ã‚りã¾ã¸ã‚“"
normalUser: "一般ユーザー"
undefined: "未定義"
assign: "アサイン"
@@ -947,6 +956,10 @@ manageCustomEmojis: "カスタム絵文字ã®ç®¡ç†"
youCannotCreateAnymore: "ã“れ以上作れãªã•ãã†ã‚„ã‚"
cannotPerformTemporary: "一時的ã«åˆ©ç”¨ã§ãã¸ã‚“ã§"
cannotPerformTemporaryDescription: "æ“作回数ãŒåˆ¶é™ã‚’è¶…ãˆãŸã‹ã‚‰ä¸€æ™‚çš„ã«åˆ©ç”¨ã§ãã¸ã‚“ããªã£ãŸã§ã€‚ã¡ã‚‡ã£ã¨æ™‚é–“ç½®ã„ã¦ã‹ã‚‰ã‚‚ã†ä¸€å›žã‚„ã£ã¦ã‚„ー。"
+invalidParamError: "パラメータãŒã‚¨ãƒ©ãƒ¼è¨€ã†ã¨ã‚Šã¾ã™ã‚"
+invalidParamErrorDescription: "リクエストパラメータã«å•題ãŒã‚ã‚“ã­ã‚“。普通ã¯ãƒã‚°ã‚„ã­ã‚“ã‘ã©ã€ã‚‚ã—ã‹ã™ã‚‹ã¨å…¥åŠ›ã—ãŸæ–‡å­—æ•°ãŒå¤šã™ãŽã‚‹ã¨ã‹ã®å¯èƒ½æ€§ã‚‚ã‚ã‚‹ã‹ã‚‰ç¢ºèªã—ã¦ã‚„〜"
+permissionDeniedError: "æ“ä½œãŒæ‹’å¦ã•れã¦ã‚‚ã†ãŸã€‚"
+permissionDeniedErrorDescription: "自分ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ã¯ã“ã®æ“ä½œã‚’è¡Œã†æ¨©é™ãŒã‚らã¸ã‚“ã­ã‚“"
preset: "プリセット"
selectFromPresets: "プリセットã‹ã‚‰é¸ã¶"
achievements: "実績"
@@ -970,7 +983,7 @@ invitationRequiredToRegister: "今ã“ã®ã‚µãƒ¼ãƒãƒ¼æ‹›å¾…制ã«ãªã£ã¦ã‚‚ã†ã
emailNotSupported: "ã“ã®ã‚µãƒ¼ãƒãƒ¼ã¯ãƒ¡ãƒ¼ãƒ«é…ä¿¡ãŒã‚µãƒãƒ¼ãƒˆã•れã¦ã¸ã‚“ã¿ãŸã„ã‚„ã‚"
postToTheChannel: "ãƒãƒ£ãƒ³ãƒãƒ«ã«æŠ•稿"
cannotBeChangedLater: "後ã‹ã‚‰ã¯å¤‰ãˆã‚‰ã‚Œã¸ã‚“ã§ã€‚"
-reactionAcceptance: "リアクションã®å—ã‘入れ"
+reactionAcceptance: "ツッコミã®å—ã‘入れ"
likeOnly: "ã„ã„ã­ã ã‘"
likeOnlyForRemote: "リモートã‹ã‚‰ã¯ã„ã„ã­ã ã‘ãª"
rolesAssignedToMe: "自分ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚ŒãŸãƒ­ãƒ¼ãƒ«"
@@ -988,19 +1001,71 @@ retryAllQueuesConfirmText: "一時的ã«ã‚µãƒ¼ãƒãƒ¼é‡ãªã‚‹ã‹ã‚‚ã—れã¸ã‚“ã
enableChartsForRemoteUser: "リモートユーザーã®ãƒãƒ£ãƒ¼ãƒˆã‚’作る"
enableChartsForFederatedInstances: "リモートサーãƒãƒ¼ã®ãƒãƒ£ãƒ¼ãƒˆã‚’作る"
showClipButtonInNoteFooter: "ノートã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã«ã‚¯ãƒªãƒƒãƒ—を追加"
-largeNoteReactions: "ノートã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’大ãã™ã‚‹"
+largeNoteReactions: "ノートã®ãƒ„ッコミを大ãã™ã‚‹"
noteIdOrUrl: "ノートIDã‹URL"
+video: "å‹•ç”»"
+videos: "å‹•ç”»"
+dataSaver: "データケãƒã‚±ãƒ"
accountMigration: "アカウントã®ãŠå¼•ã£è¶Šã—"
accountMoved: "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã•らã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«å¼•ã£è¶Šã—ãŸã§ï¼š"
+accountMovedShort: "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ç§»è¡Œã•れã¨ã‚‹ã§"
+operationForbidden: "ã“ã®æ“作ã¯ã§ãã¾ã¸ã‚“"
forceShowAds: "常ã«åºƒå‘Šã‚’表示ã—ã¨ã"
+addMemo: "メモを足ã™"
+editMemo: "メモをã„らã†"
+reactionsList: "ツッコミ一覧"
+renotesList: "Renote一覧"
+notificationDisplay: "通知見ã›ã‚‹"
+leftTop: "左上"
+rightTop: "å³ä¸Š"
+leftBottom: "左下"
+rightBottom: "å³ä¸‹"
+stackAxis: "é‡ã­ã‚‹æ–¹å‘"
+vertical: "縦"
+horizontal: "横"
+position: "ä½ç½®"
+serverRules: "サーãƒãƒ¼ãƒ«ãƒ¼ãƒ«"
+pleaseConfirmBelowBeforeSignup: "ã“ã®ã‚µãƒ¼ãƒãƒ¼ã«ç™»éŒ²ã™ã‚‹å‰ã«ã€ä¸‹ã«æ›¸ã„ã¦ã‚‹ã“ã¨ç¢ºèªã—ã¦ãªã€‚"
+pleaseAgreeAllToContinue: "ç¶šã‘ã‚‹ã‚“ã‚„ã£ãŸã‚‰ã€å…¨ã¦ã®ã€Œã›ã‚„ãªã€ã«ãƒã‚§ãƒƒã‚¯å…¥ã‚Œã¦ã‚‹å¿…è¦ãŒã‚ã‚‹ã§ã€‚"
+continue: "ç¶šã‘ã‚‹ã§"
+preservedUsernames: "予約ユーザーå"
+preservedUsernamesDescription: "予約ã—ã¨ãユーザーåを行ã”ã¨ã«æŒ™ã’ã‚‹ã§ã€‚ã“ã“ã§æŒ‡å®šã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼åã¯ã‚¢ã‚«ã‚¦ãƒ³ãƒˆä½œã‚‹ã¨ãã«ä½¿ãˆã¸ã‚“ããªã‚‹ã‘ã©ã€ç®¡ç†è€…ã¯ä¾‹å¤–や。ã‚ã¨ã€ã‚‚ã†ã‚るアカウントも例外やãªã€‚"
+createNoteFromTheFile: "ã“ã®ãƒ•ァイル使ã†ã¦ãƒŽãƒ¼ãƒˆä½œã‚‹ã§"
+archive: "アーカイブ"
+channelArchiveConfirmTitle: "{name}をアーカイブã—ã¦ãˆãˆã‹ï¼Ÿ"
+channelArchiveConfirmDescription: "アーカイブã—ãŸã‚‰ã€ãƒãƒ£ãƒ³ãƒãƒ«ä¸€è¦§ã¨ã‹æ¤œç´¢çµæžœã‹ã‚‰ãªããªã‚‹ã—ã€æ–°ã—ãæ›¸ãè¾¼ã¿ã‚‚ã§ãã¸ã‚“ãªã‚‹ã§ã€‚"
+thisChannelArchived: "ã“ã®ãƒãƒ£ãƒ³ãƒãƒ«ã€ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–ã•れã¨ã‚‹ã§ã€‚"
+displayOfNote: "ノートã®è¡¨ç¤º"
+initialAccountSetting: "åˆæœŸè¨­å®š"
+youFollowing: "フォロー中やã§"
+_initialAccountSetting:
+ accountCreated: "アカウント作り終ã‚ã£ãŸã§ã€‚"
+ letsFillYourProfile: "最åˆã¯ã‚ã‚“ãŸã®ãƒ—ロフィールを設定ã—よã‹ã€‚"
+ profileSetting: "プロフィール設定"
+ theseSettingsCanEditLater: "ã“ã®è¨­å®šã¯ã‚ã¨ã‹ã‚‰å¤‰ãˆã‚Œã‚‹ã§ã€‚"
+ youCanEditMoreSettingsInSettingsPageLater: "ã“れ以外ã«ã‚‚ã„ã‚ã‚“ãªè¨­å®šã‚’「設定ã€ãƒšãƒ¼ã‚¸ã‹ã‚‰ã§ãã‚‹ã§ã€‚後ã§ç¢ºèªã—ã¦ã¿ã¦ãªã€‚"
+ followUsers: "タイムラインを構築ã™ã‚‹ãŸã‚ã«ã€æ°—ã«ãªã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’フォローã—ã¦ã¿ã€‚"
+ pushNotificationDescription: "プッシュ通知を有効ã«ã™ã‚‹ã¨{name}ã®é€šçŸ¥ã‚’ã‚ã‚“ãŸã®ãƒ‡ãƒã‚¤ã‚¹ã§å—ã‘å–れるã§ã€‚"
+ initialAccountSettingCompleted: "åˆæœŸè¨­å®šãŒçµ‚ã‚ã£ãŸã§ã€‚"
+ haveFun: "{name}ã€æ¥½ã—ã‚“ã§ãªï½ž"
+ ifYouNeedLearnMore: "{name}(Misskey)ã®ä½¿ã„æ–¹ã¨ã‹ã‚’よー知りãŸã„ã‚“ã‚„ã£ãŸã‚‰{link}ã‚’ã¿ã¦ãªã€‚"
+ skipAreYouSure: "åˆæœŸè¨­å®šé£›ã°ã™ã‹ï¼Ÿ"
+_serverRules:
+ description: "æ–°è¦ç™»éŒ²å‰ã«è¦‹ã›ã‚‹ã€ã‚µãƒ¼ãƒãƒ¼ã®ç°¡æ½”ãªãƒ«ãƒ¼ãƒ«ã‚’設定ã™ã‚“ã§ã€‚内容ã¯ä½¿ã†ãŸã‚ã®æ±ºã‚事ã®è¦ç´„ã¨ã™ã‚‹ã“ã¨ã‚’推奨ã™ã‚‹ã‚。"
_accountMigration:
- moveTo: "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ã•らã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«å¼•ã£è¶Šã™ã§"
- moveToLabel: "引ã£è¶Šã—å…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆï¼š"
- moveAccountDescription: "ã“ã®æ“ä½œã¯æˆ»ã•れã¸ã‚“ã§ã€‚ã¾ãšå¼•ã£è¶Šã—å…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¸ã®ã‚¨ã‚¤ãƒªã‚¢ã‚¹ãŒä½œã‚ŒãŸã‹ç¢ºèªã—ã¦ããªã¯ã‚Œã‚„。エイリアスãŒã§ãã¦ãŸã‚‰ã€å¼•ã£è¶Šã—å…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ã“ã‚“ãªé¢¨ã«å…¥åŠ›ã—ã¦ãれã¸ã‚“ã‹ï¼Ÿï¼š@person@instance.com"
moveFrom: "別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«å¼•ã£è¶Šã™"
+ moveFromSub: "別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¸ã‚¨ã‚¤ãƒªã‚¢ã‚¹ã‚’作る"
moveFromLabel: "引ã£è¶Šã—å…ƒã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆï¼š"
moveFromDescription: "別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ãƒ•ォロワーを引ãç¶™ã„ã§å¼•ã£è¶Šã—ãŸã‹ã£ãŸã‚‰ã€ã“ã“ã§ã‚¨ã‚¤ãƒªã‚¢ã‚¹ã‚’作ã£ã¨ãå¿…è¦ãŒã‚ã‚‹ã§ã€‚å¿…ãšãŠå¼•ã£è¶Šã—を実行ã™ã‚‹å‰ã«ä½œã£ã¨ã‹ãªã‚ã‹ã‚“ã§ï¼å¼•ã£è¶Šã—å…ƒã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ã“ã‚“ãªé¢¨ã«å…¥åŠ›ã—ã¦ãれã¸ã‚“ã‹ï¼Ÿï¼š@person@instance.com"
+ moveTo: "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ã•らã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«å¼•ã£è¶Šã™ã§"
+ moveToLabel: "引ã£è¶Šã—å…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆï¼š"
+ moveCannotBeUndone: "アカウントを移行ã™ã‚‹ã¨ã€å–り消ã™ã“ã¨ã¯ã§ãã¸ã‚“ããªã‚Šã¾ã™ã€‚"
+ moveAccountDescription: "ã“ã®æ“ä½œã¯æˆ»ã•れã¸ã‚“ã§ã€‚ã¾ãšå¼•ã£è¶Šã—å…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¸ã®ã‚¨ã‚¤ãƒªã‚¢ã‚¹ãŒä½œã‚ŒãŸã‹ç¢ºèªã—ã¦ããªã¯ã‚Œã‚„。エイリアスãŒã§ãã¦ãŸã‚‰ã€å¼•ã£è¶Šã—å…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ã“ã‚“ãªé¢¨ã«å…¥åŠ›ã—ã¦ãれã¸ã‚“ã‹ï¼Ÿï¼š@person@instance.com"
+ moveAccountHowTo: "アカウントã®å¼•ã£è¶Šã—ã«ã¯ã€ã¾ãšå¼•ã£è¶Šã—å…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§è‡ªåˆ†ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«å¯¾ã—エイリアスを作æˆã—ãªã¯ã‚Œã‚„。\nエイリアス作æˆã—ãŸå¾Œã€å¼•ã£è¶Šã—å…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’次ã®ã‚ˆã†ã«å…¥åŠ›ã—ã¦ãれã¸ã‚“ã‹ï¼Ÿ:@username@server.example.com"
+ startMigration: "引ã£è¶Šã—ã™ã‚‹"
migrationConfirm: "ã»ã‚“ã¾ã«ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ {account} ã«å¼•ã£è¶Šã™ã‚“ã‹ï¼Ÿä¸€å›žå¼•ã£è¶Šã—ã¦ã‚‚ã†ãŸã‚‰å–り消ã•れã¸ã‚“ã—ã€äºŒåº¦ã¨ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’å…ƒã«æˆ»ã•れã¸ã‚“ããªã‚‹ã§ã€‚\nãれã¨ã€å¼•ã£è¶Šã—å…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã‚¨ã‚¤ãƒªã‚¢ã‚¹ãŒä½œã‚ŒãŸã‹ã¡ã‚ƒï½žã‚“ã¨ç¢ºèªã—ーや?"
+ movedAndCannotBeUndone: "\nアカウントã¯ã‚‚ã†å¼•ã£è¶Šã•れã¦ã¾ã™ã€‚\n引ã£è¶Šã—ã‚’å–り消ã™ã“ã¨ã¯ã§ãã¾ã¸ã‚“。"
+ movedTo: "引ã£è¶Šã—å…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆï¼š"
_achievements:
earnedAt: "è²°ã£ãŸæ—¥ãƒ"
_types:
@@ -1172,6 +1237,9 @@ _achievements:
_client30min:
title: "ã­ã‚“ã­"
description: "クライアントを起動ã—ã¦ã‹ã‚‰30分以上経éŽã—ãŸ"
+ _client60min:
+ title: "Misskeyã®è¦‹éŽãŽã‚„ï¼"
+ description: "クライアント付ã‘ã¦ã‹ã‚‰ï¼‘時間経ã£ã¦ã‚‚ã†ãŸã§ã€‚"
_noteDeletedWithin1min:
title: "*ãŠãŠã£ã¨ï¼Š"
description: "投稿ã—ã¦ã‹ã‚‰1分以内ã«ãã®æŠ•ç¨¿ã‚’æ¶ˆã—ãŸ"
@@ -1203,7 +1271,7 @@ _achievements:
description: "ドライブã®ãƒ•ォルダをå†å¸°çš„ãªå…¥ã‚Œå­ã«ã—よã†ã¨ã—ãŸ"
_reactWithoutRead:
title: "ã¡ã‚ƒã‚“ã¨èª­ã‚“ã ã‚“ã‹ï¼Ÿ"
- description: "100文字以上ã®ãƒ†ã‚­ã‚¹ãƒˆã‚’å«ã‚€ãƒŽãƒ¼ãƒˆã«æŠ•稿ã•れã¦ã‹ã‚‰3秒以内ã«ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã—ãŸ"
+ description: "100文字以上ã®ãƒ†ã‚­ã‚¹ãƒˆã‚’å«ã‚€ãƒŽãƒ¼ãƒˆã«æŠ•稿ã•れã¦ã‹ã‚‰3秒以内ã«ãƒ„ッコんã "
_clickedClickHere:
title: "ã“ã“をクリック"
description: "ã“ã“をクリックã—ãŸ"
@@ -1260,6 +1328,8 @@ _role:
iconUrl: "アイコン画åƒã®URL"
asBadge: "ãƒãƒƒã‚¸ã¨ã—ã¦è¦‹ã›ã‚‹"
descriptionOfAsBadge: "オンã«ã™ã‚‹ã¨ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼åã®æ¨ªã‚“ã¨ã“ã«ãƒ­ãƒ¼ãƒ«ã®ã‚¢ã‚¤ã‚³ãƒ³ãŒè¡¨ç¤ºã•れるã§ã€‚"
+ isExplorable: "ロールタイムラインを公開ã™ã‚‹ã§ã€œ"
+ descriptionOfIsExplorable: "オンã«ã—ãŸã‚‰ãƒ­ãƒ¼ãƒ«ã®ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã‚’公開ã™ã‚‹ã§ã€œã€‚ã§ã‚‚ロールã®å…¬é–‹ã‚’オフã«ã—ãŸã‚‰å…¬é–‹ã•れã¸ã‚“よ。"
displayOrder: "表示順"
descriptionOfDisplayOrder: "æ•°ãŒã§ã‹ã„ã»ã©ã€UI上ã§å…ˆã«è¡¨ç¤ºã•れるã§ã€‚"
canEditMembersByModerator: "モデレーターã®ãƒ¡ãƒ³ãƒãƒ¼ç·¨é›†ã‚’許å¯"
@@ -1276,6 +1346,7 @@ _role:
canInvite: "サーãƒãƒ¼æ‹›å¾…コードã®ç™ºè¡Œ"
canManageCustomEmojis: "カスタム絵文字ã®ç®¡ç†"
driveCapacity: "ドライブ容é‡"
+ alwaysMarkNsfw: "勿‰‹ã«ãƒ•ァイルã«NSFWã‚’ãã£ã¤ã‘ã‚‹"
pinMax: "ノートã®ãƒ”ン留ã‚ã®æœ€å¤§æ•°"
antennaMax: "アンテナã®ä½œæˆå¯èƒ½æ•°"
wordMuteMax: "ãƒ¯ãƒ¼ãƒ‰ãƒŸãƒ¥ãƒ¼ãƒˆã®æœ€å¤§æ–‡å­—æ•°"
@@ -1527,32 +1598,16 @@ _time:
minute: "分"
hour: "時間"
day: "æ—¥"
-_tutorial:
- title: "Misskeyã®ä½¿ã„æ–¹"
- step1_1: "ã‚ˆã†æ¥ãŸãªã‚"
- step1_2: "ã“ã®ç”»é¢ã¯ã€Œã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã€ã£ã¦è¨€ã£ã¦ã€ã‚ã‚“ãŸã‚„ã€ã‚ã‚“ãŸãŒã€Œãƒ•ォローã€ã™ã‚‹äººã®ã€ŒãƒŽãƒ¼ãƒˆã€ãŒæ™‚系列ã§è¡¨ç¤ºã•れるんやã§ã€‚"
- step1_3: "ã‚ã‚“ãŸã¯ã¾ã ä½•もノートを投稿ã—ã¦ãªãã¦ã€èª°ã‚‚フォローã—ã¦ã¸ã‚“ã‹ã‚‰ã€ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã«ã¯ä½•も表示ã•れã¦ãªã„ã¯ãšã‚„ã§ã€‚"
- step2_1: "ノートを作ã£ãŸã‚Šèª°ã‹ã‚’フォローã—ãŸã‚Šã™ã‚‹å‰ã«ã€ã¾ãšã‚ã‚“ãŸã®ãƒ—ロフィールを完æˆã•ã›ã‚ˆã‹ã€‚"
- step2_2: "ã‚ã‚“ãŸãŒã©ã‚“ãªäººã‹ã‚ã‹ã‚‹ã¨ã€å¤šãã®äººã«ãƒŽãƒ¼ãƒˆã‚’見ã¦ã‚‚らãˆãŸã‚Šã€ãƒ•ォローã—ã¦ã‚‚らã„ã‚„ã™ããªã‚‹ã§ã€‚"
- step3_1: "プロフィール設定ã¯ãˆãˆæ„Ÿã˜ã«ã§ããŸã‹ï¼Ÿ"
- step3_2: "ã»ãªè©¦ã—ã«ã€ä½•ã‹ãƒŽãƒ¼ãƒˆã‚’投稿ã—ã¦ã¿ã¦ã‚„ー。画é¢ä¸Šã«ã‚る鉛筆マークã®ãƒœã‚¿ãƒ³ã‚’押ã™ã¨ãƒ•ォームãŒé–‹ãã¯ãšã‚„ã§ã€‚"
- step3_3: "内容を書ã„ãŸã‚‰ã€ãƒ•ォームå³ä¸Šã®ãƒœã‚¿ãƒ³ã‚’押ã™ã¨æŠ•稿ã§ãã‚‹ã§ã€‚"
- step3_4: "å†…å®¹ãŒæ€ã„ã¤ã‹ã¸ã‚“?ã»ãªã€Œé–¢è¥¿äººãªã‚‰é¢ç™½ã„ã“ã¨è¨€ã†ã¦ãˆã€œã‚„〜ã€ã¨ã‹ã©ã†ã‚„ã‚ã‹ã€‚"
- step4_1: "投稿ã§ããŸã‚“?"
- step4_2: "ã‚ã‚“ãŸã®ãƒŽãƒ¼ãƒˆãŒã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã«è¡¨ç¤ºã•れã¦ã„ã‚Œã°æˆåŠŸã‚„ã§"
- step5_1: "次ã¯ã€ã»ã‹ã®äººã‚’フォローã—ã¦ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã‚’賑やã‹ã«ã—よã‹"
- step5_2: "{featured}ã§äººæ°—ã®ãƒŽãƒ¼ãƒˆãŒè¦‹ã‚Œã‚‹ã‹ã‚‰ã€ãã®ä¸­ã‹ã‚‰æ°—ã«ãªã£ãŸäººã‚’é¸ã‚“ã§ãƒ•ォローã—ãŸã‚Šã€{explore}ã§äººæ°—ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’探ã™ã“ã¨ã‚‚ã§ãã‚‹ã§ã€‚"
- step5_3: "ユーザーをフォローã—ãŸã‹ã£ãŸã‚‰ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã‚¢ã‚¤ã‚³ãƒ³ã‚’クリックã—ã¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ãƒšãƒ¼ã‚¸ã‚’表示ã—ã¦ã€ã€Œãƒ•ォローã€ãƒœã‚¿ãƒ³ã‚’押ã™ã‚“ã‚„ã§ã€‚"
- step5_4: "ユーザーã«ã‚ˆã£ã¦ã¯ã€ãƒ•ã‚©ãƒ­ãƒ¼ãŒæ‰¿èªã•れるã¾ã§ã¡ã‚‡ã£ã¨æ™‚é–“ãŒã‹ã‹ã‚‹ã“ã¨ãŒã‚ã‚‹ã§ã€‚"
- step6_1: "タイムラインã«ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆãŒè¡¨ç¤ºã•れã¦ã„ã‚Œã°æˆåŠŸã‚„ã§ã€‚"
- step6_2: "ä»–ã®äººã®ãƒŽãƒ¼ãƒˆã«ã¯ã€ã€Œãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã€ã‚’付ã‘ã‚‹ã“ã¨ãŒã§ãã¦ã€ç°¡å˜ã«ã‚ã‚“ãŸã®å応をä¼ãˆã‚‰ã‚Œã‚‹ã§ã€‚"
- step6_3: "リアクションを付ã‘ã‚‹ã‚“ã‚„ã£ãŸã‚‰ã€ãƒŽãƒ¼ãƒˆã®ã€Œ+ã€ãƒžãƒ¼ã‚¯ã‚’クリックã—ã¦ã€å¥½ããªãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’é¸æŠžã—ã¦ãªã€‚"
- step7_1: "ã“れã§ã€Misskeyã®åŸºæœ¬çš„ãªä½¿ã„æ–¹ã®èª¬æ˜Žã¯çµ‚ã‚りやã§ã€‚ãŠç–²ã‚Œã•ん。"
- step7_2: "ã‚‚ã£ã¨Misskeyã«ã¤ã„ã¦çŸ¥ã‚ŠãŸã„ã¨ãã¯ã€{help}を見るã¨ãˆãˆã‹ã‚‚ãªã€‚"
- step7_3: "ã»ãªã€Misskeyを楽ã—ã‚“ã§ãªãƒ¼ðŸš€"
- step8_1: "最後ã«ã€ãƒ—ッシュ通知を有効化ã—ã¦ã¿ã‚„ん?"
- step8_2: "プッシュ通知をå—ã‘å–ã‚‹ã“ã¨ã§ã€Misskeyã‚’é–‹ã„ã¦ã„ãªã„時ã§ã‚‚リアクションやフォローã€ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ã¨ã‹ã«æ°—ã¥ã‘ã‚‹ã§ã€‚"
- step8_3: "通知ã®è¨­å®šã¯ã‚ã¨ã‹ã‚‰å¤‰æ›´ã§ãã‚‹ã§"
+_timelineTutorial:
+ title: "Misskeyã£ã¦ãªã‚“や?"
+ step1_1: "ã“れã¯ã€Œã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã€ã‚„。{name}ã«æŠ•ç¨¿ã•れãŸã€ŒãƒŽãƒ¼ãƒˆã€ãŒé †ç•ªã«è¡¨ç¤ºã•れるã§ã€‚"
+ step1_2: "タイムラインã«ã¯ä½•個ã‹ç¨®é¡žãŒã‚ã£ã¦ãªã€ä¾‹ãˆã°ã€Œãƒ›ãƒ¼ãƒ ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã€ã ã£ãŸã‚‰ã‚ã‚“ãŸã®ãƒ•ォローã—ã¦ã‚‹äººã®ãƒŽãƒ¼ãƒˆã€ã€Œãƒ­ãƒ¼ã‚«ãƒ«ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã€ã«ã¯{name}全部ã®ãƒŽãƒ¼ãƒˆãŒæµã‚Œã¦ãã‚‹ã§ã€‚"
+ step2_1: "試ã—ã«ã€ä½•ã‹ãƒŽãƒ¼ãƒˆã‚’投稿ã—ã¦ã¿ã€‚ç”»é¢ã®é‰›ç­†ãƒžãƒ¼ã‚¯ã®ãƒœã‚¿ãƒ³ã§ãƒ•ォームãŒé–‹ãã§ã€‚"
+ step2_2: "最åˆã®ãƒŽãƒ¼ãƒˆã¯ã€è‡ªå·±ç´¹ä»‹ã¨ã‹ã€Œ{name}å§‹ã‚ã¦ã¿ãŸã‚“ã‚„ã€ã¨ã‹ãŒãˆãˆã¨æ€ã†ã§ã€‚"
+ step3_1: "投稿ã§ããŸï¼Ÿ"
+ step3_2: "ã‚ã‚“ãŸã®ãƒŽãƒ¼ãƒˆãŒã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã«å‡ºã¦ããŸã‚‰æˆåŠŸã‚„ã€‚"
+ step4_1: "ノートã«ã¯ã€ã€Œãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã€ã‚’付ã‘れるã§ã€‚"
+ step4_2: "ツッコむんやã£ãŸã‚‰ã€ãƒŽãƒ¼ãƒˆã®ã€Œ+ã€ãƒžãƒ¼ã‚¯ã‚’押ã—ã¦ã€å¥½ããªçµµæ–‡å­—ã‚’é¸ã¶ã§ã€‚"
_2fa:
alreadyRegistered: "ã‚‚ã†è¨­å®šçµ‚ã‚ã£ã¨ã‚‹ã‚。"
registerTOTP: "èªè¨¼ã‚¢ãƒ—リã®è¨­å®šã¯ã˜ã‚ã‚‹"
@@ -1596,8 +1651,8 @@ _permissions:
"write:notes": "ノートを作æˆãƒ»å‰Šé™¤ã™ã‚‹ã§"
"read:notifications": "通知を見るã§"
"write:notifications": "通知をæ“作ã™ã‚‹ã§"
- "read:reactions": "リアクションを見る"
- "write:reactions": "リアクションをæ“作ã™ã‚‹ã§"
+ "read:reactions": "ツッコミを見る"
+ "write:reactions": "ツッコミをæ“作ã™ã‚‹ã§"
"write:votes": "投票ã™ã‚‹"
"read:pages": "ページを見る"
"write:pages": "ページをæ“作ã™ã‚‹ã§"
@@ -1853,7 +1908,7 @@ _notification:
reply: "リプライ"
renote: "Renote"
quote: "引用"
- reaction: "リアクション"
+ reaction: "ツッコミ"
pollEnded: "アンケートãŒçµ‚了ã—ãŸã§"
receiveFollowRequest: "フォロー許å¯ã—ã¦ã»ã—ã„ã¿ãŸã„ã‚„ã§"
followRequestAccepted: "フォローãŒå—ç†ã•れãŸã§"
@@ -1912,5 +1967,5 @@ _webhookSettings:
note: "ノートを投稿ã—ãŸã¨ã~ï¼"
reply: "返信ãŒã‚ã‚‹ã¨ã~ï¼"
renote: "Renoteã•れるã¨ã~ï¼"
- reaction: "リアクションãŒã‚ã‚‹ã¨ã~ï¼"
+ reaction: "ツッコミãŒã‚ã‚‹ã¨ã~ï¼"
mention: "メンションãŒã‚ã‚‹ã¨ã~ï¼"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index 3ff00482d6..4a2fbe2a80 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -2,7 +2,7 @@
_lang_: "한국어"
headlineMisskey: "노트로 ì—°ê²°ë˜ëŠ” 네트워í¬"
introMisskey: "환ì˜í•©ë‹ˆë‹¤! Misskey는 오픈 소스 분산형 마ì´í¬ë¡œ 블로그 서비스입니다.\n'노트'를 작성해서 지금 ì¼ì–´ë‚˜ê³  있는 ì¼ì„ 공유하거나, ë‹¹ì‹ ë§Œì˜ ì´ì•¼ê¸°ë¥¼ 모ë‘ì—게 발신하세요📡\n'리액션' 기능으로 ì¹œêµ¬ì˜ ë…¸íŠ¸ì— ì´ì•Œê°™ì´ ë°˜ì‘ì„ ì¶”ê°€í•  ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤ðŸ‘\n새로운 세계를 íƒí—˜í•´ 보세요🚀"
-poweredByMisskeyDescription: "{name}ì€(는) 오픈소스 플랫í¼<b>Misskey</b>를 사용한 서버 중 하나입니다."
+poweredByMisskeyDescription: "{name}ì€(는) 오픈소스 í”Œëž«í¼ <b>Misskey</b>를 사용한 서버 ê°€ìš´ë° í•˜ë‚˜ìž…ë‹ˆë‹¤."
monthAndDay: "{month}ì›” {day}ì¼"
search: "검색"
notifications: "알림"
@@ -20,6 +20,7 @@ noNotes: "노트가 없습니다"
noNotifications: "표시할 ì•Œë¦¼ì´ ì—†ìŠµë‹ˆë‹¤"
instance: "서버"
settings: "설정"
+notificationSettings: "알림 설정"
basicSettings: "기본 설정"
otherSettings: "기타 설정"
openInWindow: "창으로 열기"
@@ -262,14 +263,16 @@ noMoreHistory: "ì´ê²ƒë³´ë‹¤ ê³¼ê±°ì˜ ê¸°ë¡ì´ 없습니다"
startMessaging: "대화 시작하기"
nUsersRead: "{n}ëª…ì´ ì½ìŒ"
agreeTo: "{0}ì— ë™ì˜"
+agree: "ë™ì˜í•©ë‹ˆë‹¤"
agreeBelow: "아래 ë‚´ìš©ì— ë™ì˜í•©ë‹ˆë‹¤"
basicNotesBeforeCreateAccount: "기본ì ì¸ 주ì˜ì‚¬í•­"
-tos: "ì´ìš© 약관"
+termsOfService: "ì´ìš© 약관"
start: "시작하기"
home: "홈"
remoteUserCaution: "리모트 유저ì´ê¸° 때문ì—, ì •ë³´ê°€ 정확하지 ì•Šì„ ìˆ˜ 있습니다."
activity: "활ë™"
images: "ì´ë¯¸ì§€"
+image: "ì´ë¯¸ì§€"
birthday: "ìƒì¼"
yearsOld: "{age}세"
registeredDate: "등ë¡ì¼"
@@ -473,6 +476,8 @@ createAccount: "계정 만들기"
existingAccount: "기존 계정"
regenerate: "재ìƒì„±"
fontSize: "ê¸€ìž í¬ê¸°"
+mediaListWithOneImageAppearance: "ì´ë¯¸ì§€ê°€ 1ê°œ ë¿ì¸ 미디어 목ë¡ì˜ 높ì´"
+limitTo: "{x}로 제한"
noFollowRequests: "처리ë˜ì§€ ì•Šì€ íŒ”ë¡œìš° ìš”ì²­ì´ ì—†ìŠµë‹ˆë‹¤"
openImageInNewTab: "새 탭ì—서 ì´ë¯¸ì§€ 열기"
dashboard: "대시보드"
@@ -506,6 +511,7 @@ objectStorageUseSSLDesc: "API 호출시 HTTPS 를 사용하지 않는 경우 OFF
objectStorageUseProxy: "ì—°ê²°ì— í”„ë¡ì‹œë¥¼ 사용"
objectStorageUseProxyDesc: "오브ì íЏ 스토리지 API 호출시 프ë¡ì‹œë¥¼ 사용하지 않는 경우 OFF 로 설정해 주세요"
objectStorageSetPublicRead: "업로드할 때 'public-read'를 설정하기"
+s3ForcePathStyleDesc: "s3ForcePathStyleì„ í™œì„±í™”í•˜ë©´, 버킷 ì´ë¦„ì„ URLì˜ í˜¸ìŠ¤íŠ¸ëª…ì´ ì•„ë‹Œ ê²½ë¡œì˜ ì¼ë¶€ë¡œì¨ 취급합니다. 셀프 호스트 Minio와 ê°™ì€ ì„œë¹„ìŠ¤ë¥¼ 사용할 경우 활성화해야 í•  수 있습니다."
serverLogs: "서버 로그"
deleteAll: "ëª¨ë‘ ì‚­ì œ"
showFixedPostForm: "타임ë¼ì¸ ìƒë‹¨ì— 글 ìž‘ì„±ëž€ì„ í‘œì‹œ"
@@ -547,11 +553,14 @@ userSuspended: "ì´ ê³„ì •ì€ ì •ì§€ëœ ìƒíƒœìž…니다."
userSilenced: "ì´ ê³„ì •ì€ ì‚¬ì¼ëŸ°ìŠ¤ëœ ìƒíƒœìž…니다."
yourAccountSuspendedTitle: "ê³„ì •ì´ ì •ì§€ë˜ì—ˆìŠµë‹ˆë‹¤"
yourAccountSuspendedDescription: "ì´ ê³„ì •ì€ ì„œë²„ì˜ ì´ìš© ì•½ê´€ì„ ìœ„ë°˜í•˜ê±°ë‚˜, 기타 다른 ì´ìœ ë¡œ ì¸í•´ ì •ì§€ë˜ì—ˆìŠµë‹ˆë‹¤. ìžì„¸í•œ ì‚¬í•­ì€ ê´€ë¦¬ìžì—게 문ì˜í•´ 주십시오. ê³„ì •ì„ ìƒˆë¡œ ìƒì„±í•˜ì§€ 마십시오."
+tokenRevoked: "유효하지 ì•Šì€ í† í°ìž…니다"
+tokenRevokedDescription: "ë¡œê·¸ì¸ í† í°ì´ 비활성화ë˜ì—ˆìŠµë‹ˆë‹¤. 다시 로그ì¸í•˜ì—¬ 주십시오."
accountDeleted: "ê³„ì •ì´ ì •ì§€ë˜ì—ˆìŠµë‹ˆë‹¤"
accountDeletedDescription: "ì´ ê³„ì •ì´ ì‚­ì œë˜ì—ˆìŠµë‹ˆë‹¤."
menu: "메뉴"
divider: "구분선"
addItem: "항목 추가"
+rearrange: "ì •ë ¬"
relays: "릴레ì´"
addRelay: "ë¦´ë ˆì´ ì¶”ê°€"
inboxUrl: "Inbox 주소"
@@ -695,6 +704,8 @@ contact: "ì—°ë½ì²˜"
useSystemFont: "시스템 기본 ê¸€ê¼´ì„ ì‚¬ìš©"
clips: "í´ë¦½"
experimentalFeatures: "실험실"
+experimental: "실험실"
+thisIsExperimentalFeature: "ì´ ê¸°ëŠ¥ì€ ì‹¤í—˜ì ì¸ 기능입니다. ì‚¬ì–‘ì´ ë³€ê²½ë˜ê±°ë‚˜ ì •ìƒì ìœ¼ë¡œ ë™ìž‘하지 ì•Šì„ ê°€ëŠ¥ì„±ì´ ìžˆìŠµë‹ˆë‹¤."
developer: "개발ìž"
makeExplorable: "\"발견하기\"ì— ë‚´ 계정 ë³´ì´ê¸°"
makeExplorableDescription: "비활성화하면 \"발견하기\"ì— ë‚˜ì˜ ê³„ì •ì„ í‘œì‹œí•˜ì§€ 않습니다."
@@ -901,6 +912,7 @@ remoteOnly: "리모트만"
failedToUpload: "업로드 실패"
cannotUploadBecauseInappropriate: "ì´ íŒŒì¼ì€ ë¶€ì ì ˆí•œ ë‚´ìš©ì„ í¬í•¨í•œë‹¤ê³  íŒë‹¨ë˜ì–´ 업로드할 수 없습니다."
cannotUploadBecauseNoFreeSpace: "드ë¼ì´ë¸Œ ìš©ëŸ‰ì´ ë¶€ì¡±í•˜ì—¬ 업로드할 수 없습니다."
+cannotUploadBecauseExceedsFileSizeLimit: "íŒŒì¼ í¬ê¸°ê°€ 너무 í¬ê¸° ë•Œë¬¸ì— ì—…ë¡œë“œí•  수 없습니다."
beta: "베타"
enableAutoSensitive: "ìžë™ NSFW íƒì§€"
enableAutoSensitiveDescription: "ì´ìš© 가능할 경우 ê¸°ê³„í•™ìŠµì„ í†µí•´ ìžë™ìœ¼ë¡œ 미디어 NSFW를 설정합니다. ì´ ê¸°ëŠ¥ì„ í•´ì œí•˜ë”ë¼ë„, 서버 ì •ì±…ì— ë”°ë¼ ìžë™ìœ¼ë¡œ ì„¤ì •ë  ìˆ˜ 있습니다."
@@ -917,6 +929,7 @@ pushNotificationNotSupported: "브ë¼ìš°ì €ë‚˜ 서버ì—서 푸시 ì•Œë¦¼ì´ ì§€
sendPushNotificationReadMessage: "푸시 알림ì´ë‚˜ 메시지를 ì½ì€ ë’¤ 푸시 ì•Œë¦¼ì„ ì‚­ì œ"
sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}ã€ì´ë¼ëŠ” ì•Œë¦¼ì´ ìž ê¹ í‘œì‹œë©ë‹ˆë‹¤. ê¸°ê¸°ì˜ ì „ë ¥ ì†Œë¹„ëŸ‰ì´ ì¦ê°€í•  수 있습니다."
windowMaximize: "최대화"
+windowMinimize: "최소화"
windowRestore: "복구"
caption: "캡션"
loggedInAsBot: "ë´‡ 계정으로 로그ì¸ì¤‘"
@@ -933,6 +946,7 @@ didYouLikeMisskey: "Misskeyê°€ 마ìŒì— 드시나요?"
pleaseDonate: "{host}ì€(는) 무료 소프트웨어 Misskey를 사용합니다. 후ì›ì„ 통해 ì €í¬ì˜ ê°œë°œì´ ì´ì–´ì§ˆ 수 있게 ë„와주세요!"
roles: "ì—­í• "
role: "ì—­í• "
+noRole: "ì—­í• ì´ ì—†ìŠµë‹ˆë‹¤"
normalUser: "ì¼ë°˜ 사용ìž"
undefined: "ì •ì˜ë˜ì§€ 않ìŒ"
assign: "할당"
@@ -942,6 +956,10 @@ manageCustomEmojis: "커스텀 ì´ëª¨ì§€ 관리"
youCannotCreateAnymore: "ë” ì´ìƒ ìƒì„±í•  수 없습니다."
cannotPerformTemporary: "ì¼ì‹œì ìœ¼ë¡œ 사용할 수 ì—†ìŒ"
cannotPerformTemporaryDescription: "ì¡°ìž‘ 횟수 ì œí•œì„ ì´ˆê³¼í•˜ì—¬ ì¼ì‹œì ìœ¼ë¡œ ì‚¬ìš©ì´ ë¶ˆê°€í•©ë‹ˆë‹¤. 잠시 후 다시 시ë„í•´ 주세요."
+invalidParamError: "매개변수 오류"
+invalidParamErrorDescription: "요청 ë§¤ê°œë³€ìˆ˜ì— ë¬¸ì œê°€ 있습니다. ëŒ€ë¶€ë¶„ì˜ ê²½ìš° Misskeyì˜ ë²„ê·¸ê°€ ì›ì¸ì´ì§€ë§Œ, ìž…ë ¥ 문ìžìˆ˜ê°€ 너무 ë§Žì•˜ì„ ê°€ëŠ¥ì„± ë“±ë„ ìžˆìŠµë‹ˆë‹¤."
+permissionDeniedError: "ìž‘ì—…ì´ ê±°ë¶€ë˜ì—ˆìŠµë‹ˆë‹¤"
+permissionDeniedErrorDescription: "ì´ ìž‘ì—…ì„ ìˆ˜í–‰í•  ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤."
preset: "프리셋"
selectFromPresets: "프리셋ì—서 ì„ íƒ"
achievements: "ë„ì „ 과제"
@@ -958,10 +976,96 @@ copyErrorInfo: "오류 정보 복사"
joinThisServer: "ì´ ì„œë²„ì— ê°€ìž…"
exploreOtherServers: "다른 서버 둘러보기"
letsLookAtTimeline: "타임ë¼ì¸ 구경하기"
+disableFederationConfirm: "ì •ë§ë¡œ ì—°í•©ì„ ë„시겠습니까?"
+disableFederationConfirmWarn: "ì—°í•©ì„ ë„ë”ë¼ë„ ê²Œì‹œë¬¼ì´ ë¹„ê³µê°œë¡œ 전환ë˜ëŠ” ê²ƒì€ ì•„ë‹™ë‹ˆë‹¤. ëŒ€ë¶€ë¶„ì˜ ê²½ìš° ì—°í•©ì„ ë¹„í™œì„±í™”í•  필요가 없습니다."
+disableFederationOk: "ì—°í•©ì„ ë„기"
invitationRequiredToRegister: "현재 ì´ ì„œë²„ëŠ” 비공개입니다. 회ì›ê°€ìž…ì„ í•˜ì‹œë ¤ë©´ 초대 코드가 필요합니다."
emailNotSupported: "ì´ ì„œë²„ì—서는 ë©”ì¼ ì „ì†¡ì„ ì§€ì›í•˜ì§€ 않습니다"
postToTheChannel: "채ë„ì— ê²Œì‹œí•˜ê¸°"
cannotBeChangedLater: "ë‚˜ì¤‘ì— ë³€ê²½í•  수 없습니다."
+reactionAcceptance: "리액션 수신"
+likeOnly: "좋아요만 받기"
+likeOnlyForRemote: "리모트ì—서는 좋아요만 받기"
+rolesAssignedToMe: "나ì—게 í• ë‹¹ëœ ì—­í• "
+resetPasswordConfirm: "비밀번호를 재설정하시겠습니까?"
+sensitiveWords: "민ê°í•œ 단어"
+sensitiveWordsDescription: "설정한 단어가 í¬í•¨ëœ ë…¸íŠ¸ì˜ ê³µê°œ 범위를 '홈'으로 강제합니다. 개행으로 구분하여 여러 개를 지정할 수 있습니다."
+notesSearchNotAvailable: "노트 ê²€ìƒ‰ì„ ì´ìš©í•˜ì‹¤ 수 없습니다."
+license: "ë¼ì´ì„ ìФ"
+unfavoriteConfirm: "ì¦ê²¨ì°¾ê¸°ë¥¼ 해제하시겠습니까?"
+myClips: "ë‚´ í´ë¦½"
+drivecleaner: "드ë¼ì´ë¸Œ 정리"
+retryAllQueuesNow: "모든 í를 다시 시ë„"
+retryAllQueuesConfirmTitle: "지금 다시 시ë„하시겠습니까?"
+retryAllQueuesConfirmText: "ì¼ì‹œì ìœ¼ë¡œ ì„œë²„ì˜ ë¶€í•˜ê°€ ì¦ê°€í•  수 있습니다."
+enableChartsForRemoteUser: "리모트 ìœ ì €ì˜ ì°¨íŠ¸ë¥¼ ìƒì„±"
+enableChartsForFederatedInstances: "리모트 ì„œë²„ì˜ ì°¨íŠ¸ë¥¼ ìƒì„±"
+showClipButtonInNoteFooter: "노트 ë™ìž‘ì— í´ë¦½ì„ 추가"
+largeNoteReactions: "ë…¸íŠ¸ì˜ ë¦¬ì•¡ì…˜ì„ í¬ê²Œ 표시"
+noteIdOrUrl: "노트 ID ë° URL"
+video: "ë™ì˜ìƒ"
+videos: "ë™ì˜ìƒ"
+dataSaver: "ë°ì´í„° 절약 모드"
+accountMigration: "계정 ì´ë™"
+accountMoved: "ì´ ì‚¬ìš©ìžëŠ” ë‹¤ìŒ ê³„ì •ìœ¼ë¡œ ì´ì‚¬í–ˆìŠµë‹ˆë‹¤:"
+accountMovedShort: "ì´ì‚¬í•œ 계정입니다"
+operationForbidden: "사용할 수 없습니다"
+forceShowAds: "광고를 í•­ìƒ í‘œì‹œ"
+addMemo: "메모 추가"
+editMemo: "메모 편집"
+reactionsList: "리액션 목ë¡"
+renotesList: "Renote 목ë¡"
+notificationDisplay: "알림 표시"
+leftTop: "왼쪽 ìƒë‹¨"
+rightTop: "오른쪽 ìƒë‹¨"
+leftBottom: "왼쪽 하단"
+rightBottom: "오른쪽 하단"
+stackAxis: "나열 방향"
+vertical: "세로"
+horizontal: "가로"
+position: "위치"
+serverRules: "서버 규칙"
+pleaseConfirmBelowBeforeSignup: "ì´ ì„œë²„ì— ê°€ìž…í•˜ê¸° ì „ì— ì•„ëž˜ ì‚¬í•­ì„ í™•ì¸í•˜ì—¬ 주십시오."
+pleaseAgreeAllToContinue: "계ì†í•˜ì‹œë ¤ë©´ 모든 í•­ëª©ì— ë™ì˜í•˜ì‹­ì‹œì˜¤."
+continue: "계ì†"
+preservedUsernames: "ì˜ˆì•½ëœ ì‚¬ìš©ìžëª…"
+preservedUsernamesDescription: "예약할 사용ìžëª…ì„ í•œ ì¤„ì— í•˜ë‚˜ì”© 입력합니다. 여기ì—서 지정한 사용ìžëª…으로는 ê³„ì •ì„ ìƒì„±í•  수 없게 ë©ë‹ˆë‹¤. 단, ê´€ë¦¬ìž ê¶Œí•œìœ¼ë¡œ ê³„ì •ì„ ìƒì„±í•  때ì—는 해당ë˜ì§€ 않으며, ì´ë¯¸ 존재하는 ê³„ì •ë„ ì˜í–¥ì„ 받지 않습니다."
+createNoteFromTheFile: "ì´ íŒŒì¼ë¡œ 노트를 작성"
+archive: "ì•„ì¹´ì´ë¸Œ"
+channelArchiveConfirmTitle: "{name} ì„(를) ì•„ì¹´ì´ë¸Œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
+channelArchiveConfirmDescription: "ì•„ì¹´ì´ë¸Œí•œ 채ë„ì€ ì±„ë„ ëª©ë¡ê³¼ 검색 ê²°ê³¼ì— í‘œì‹œë˜ì§€ 않으며, 채ë„ì— ìƒˆë¡œìš´ 노트를 작성할 수 없게 ë©ë‹ˆë‹¤."
+thisChannelArchived: "ì´ ì±„ë„ì€ ì•„ì¹´ì´ë¸Œë˜ì—ˆìŠµë‹ˆë‹¤."
+displayOfNote: "노트 표시"
+initialAccountSetting: "초기 설정"
+youFollowing: "팔로잉"
+_initialAccountSetting:
+ accountCreated: "계정 ìƒì„±ì´ 완료ë˜ì—ˆìŠµë‹ˆë‹¤!"
+ letsFillYourProfile: "ìš°ì„  ë‚˜ì˜ í”„ë¡œí•„ì„ ì„¤ì •í•´ ë³´ì•„ìš”."
+ profileSetting: "프로필 설정"
+ theseSettingsCanEditLater: "ì´ ì„¤ì •ë“¤ì€ ë‚˜ì¤‘ì—ë„ ë³€ê²½í•  수 있습니다."
+ youCanEditMoreSettingsInSettingsPageLater: "ì´ ì™¸ì—ë„ '설정' 페ì´ì§€ì—서 다양한 ì„¤ì •ì„ ë‚˜ì˜ ìž…ë§›ì— ë§›ê²Œ 조절할 수 있습니다. ê¼­ 확ì¸í•´ 보세요!"
+ followUsers: "관심사가 맞는 유저를 팔로우하여 타임ë¼ì¸ì„ 가꾸어 봅시다."
+ pushNotificationDescription: "푸시 ì•Œë¦¼ì„ í™œì„±í™”í•˜ë©´ {name}ì˜ ì•Œë¦¼ì„ ë‚˜ì˜ ê¸°ê¸°ì—서 받아볼 수 있게 ë©ë‹ˆë‹¤."
+ initialAccountSettingCompleted: "초기 ì„¤ì •ì„ ëª¨ë‘ ë§ˆì³¤ìŠµë‹ˆë‹¤!"
+ haveFun: "{name}와 함께 ì¦ê±°ìš´ 시간 보내세요!"
+ ifYouNeedLearnMore: "{name}(Misskey)ì˜ ì‚¬ìš© ë°©ë²•ì— ëŒ€í•´ ìžì„¸ížˆ 알아보려면 {link}를 참고해 주세요."
+_serverRules:
+ description: "íšŒì› ê°€ìž… ì´ì „ì— ê°„ë‹¨í•˜ê²Œ 표시할 서버 규칙입니다. ì´ìš© ì•½ê´€ì˜ ìš”ì•½ìœ¼ë¡œ 구성하는 ê²ƒì„ ì¶”ì²œí•©ë‹ˆë‹¤."
+_accountMigration:
+ moveFrom: "다른 계정ì—서 ì´ ê³„ì •ìœ¼ë¡œ ì´ì‚¬"
+ moveFromSub: "다른 ê³„ì •ì— ëŒ€í•œ ë³„ì¹­ì„ ìƒì„±"
+ moveFromLabel: "기존 계정:"
+ moveFromDescription: "다른 계정ì—서 ì´ ê³„ì •ìœ¼ë¡œ 팔로워를 가져오려면, ìš°ì„  여기ì—서 ë³„ì¹­ì„ ì§€ì •í•´ì•¼ 합니다. 반드시 ì´ì‚¬í•˜ê¸° ì „ì— ì§€ì •í•´ì•¼ 합니다! 기존 ê³„ì •ì„ ë‹¤ìŒê³¼ ê°™ì€ í˜•ì‹ìœ¼ë¡œ 입력해 주십시오: @person@instance.com"
+ moveTo: "ì´ ê³„ì •ì—서 다른 계정으로 ì´ì‚¬"
+ moveToLabel: "ì´ì‚¬í•  계정:"
+ moveCannotBeUndone: "한 번 ì´ì‚¬í•˜ë©´, ë‘ ë²ˆ 다시 ë˜ëŒë¦´ 수 없습니다."
+ moveAccountDescription: "ì´ ìž‘ì—…ì€ ì·¨ì†Œí•  수 없습니다. 먼저 ì´ì‚¬í•  계정ì—서 ì´ ê³„ì •ì— ëŒ€í•œ ë³„ì¹­ì„ ì§€ì •í•˜ì˜€ëŠ”ì§€ 다시 한 번 확ì¸í•´ 주십시오. ë³„ì¹­ì„ ì§€ì •í•œ 다ìŒ, ì´ì‚¬í•  ê³„ì •ì„ ë‹¤ìŒê³¼ ê°™ì€ í˜•ì‹ìœ¼ë¡œ 입력해 주십시오: @person@instance.com"
+ moveAccountHowTo: "ê³„ì •ì„ ì´ì‚¬í•˜ë ¤ë©´ ìš°ì„  ì´ì‚¬ê°ˆ 계정ì—서 ì´ ê³„ì •ì— ëŒ€í•œ ë³„ì¹­ì„ ì§€ì •í•´ì•¼ 합니다.\në³„ì¹­ì„ ìž‘ì„±í•œ 다ìŒ, ì´ì‚¬ê°ˆ ê³„ì •ì„ ë‹¤ìŒê³¼ ê°™ì´ ìž…ë ¥í•˜ì‹­ì‹œì˜¤:\n@username@server.example.com"
+ startMigration: "ì´ì‚¬í•˜ê¸°"
+ migrationConfirm: "ì •ë§ë¡œ ì´ ê³„ì •ì„ {account} 으로 ì´ì „하시겠습니까? 한 번 ì´ì „한 다ìŒì—는 취소할 수 없으며, ë‘ ë²ˆ 다시 ì›ëž˜ ìƒíƒœë¡œ 복구할 수 없습니다.\nì´ì‚¬í•  계정ì—서 계정 ë³„ì¹­ì„ ì§€ì •í•˜ì˜€ëŠ”ì§€ 다시 한 번 확ì¸í•˜ì‹­ì‹œì˜¤."
+ movedAndCannotBeUndone: "\nì´ì‚¬í•œ 계정입니다.\nì´ì‚¬ëŠ” 취소할 수 없습니다."
+ postMigrationNote: "ì´ ê³„ì •ì˜ íŒ”ë¡œìž‰ 해제는 ì´ì‚¬ 후 24시간 ë’¤ì— ì‹¤í–‰ë©ë‹ˆë‹¤.\nì´ ê³„ì •ì˜ íŒ”ë¡œìš° ë° íŒ”ë¡œì›Œ 수는 0으로 표시ë©ë‹ˆë‹¤. 팔로워 해제는 ì´ë£¨ì–´ì§€ì§€ 않으므로, ë‹¹ì‹ ì˜ íŒ”ë¡œì›ŒëŠ” ì´ ê³„ì •ì˜ íŒ”ë¡œì›Œ 한정 ê²Œì‹œë¬¼ì„ ê³„ì†í•´ì„œ 열람할 수 있습니다."
+ movedTo: "ì´ì‚¬í•  계정:"
_achievements:
earnedAt: "달성 ì¼ì‹œ"
_types:
@@ -1133,6 +1237,8 @@ _achievements:
_client30min:
title: "ìž ê¹ ì‰¬ì–´"
description: "í´ë¼ì´ì–¸íŠ¸ë¥¼ 시작하고 30ë¶„ì´ ê²½ê³¼í•˜ì˜€ìŠµë‹ˆë‹¤"
+ _client60min:
+ description: "í´ë¼ì´ì–¸íŠ¸ë¥¼ 시작하고 60ë¶„ì´ ê²½ê³¼í•˜ì˜€ìŠµë‹ˆë‹¤"
_noteDeletedWithin1min:
title: "있었는ë°ìš” 없었습니다"
description: "노트를 í¬ìŠ¤íŠ¸í•œ 후 1ë¶„ ì´ë‚´ì— 삭제했습니다"
@@ -1221,6 +1327,10 @@ _role:
iconUrl: "ì•„ì´ì½˜ URL"
asBadge: "뱃지로 표시"
descriptionOfAsBadge: "활성화하면 유저명 ì˜†ì— ì—­í• ì˜ ì•„ì´ì½˜ì´ 표시ë©ë‹ˆë‹¤."
+ isExplorable: "ì—­í•  타임ë¼ì¸ 공개"
+ descriptionOfIsExplorable: "활성화하면 ì—­í•  타임ë¼ì¸ì„ 공개합니다. 비활성화 시 타임ë¼ì¸ì´ 공개ë˜ì§€ 않습니다."
+ displayOrder: "표시 순서"
+ descriptionOfDisplayOrder: "ê°’ì´ í´ ìˆ˜ë¡ UIì—서 먼저 표시ë©ë‹ˆë‹¤."
canEditMembersByModerator: "모ë”ë ˆì´í„°ì˜ ì—­í•  수정 허용"
descriptionOfCanEditMembersByModerator: "ì´ ì˜µì…˜ì„ ì¼œë©´ 모ë”ë ˆì´í„°ë„ ì´ ì—­í• ì— ì‚¬ìš©ìžë¥¼ 할당하거나 삭제할 수 있습니다. 꺼져 있으면 관리ìžë§Œ í• ë‹¹ì´ ê°€ëŠ¥í•©ë‹ˆë‹¤."
priority: "우선순위"
@@ -1235,6 +1345,7 @@ _role:
canInvite: "서버 초대 코드 발행"
canManageCustomEmojis: "커스텀 ì´ëª¨ì§€ 관리"
driveCapacity: "드ë¼ì´ë¸Œ 용량"
+ alwaysMarkNsfw: "파ì¼ì„ í•­ìƒ NSFW로 지정"
pinMax: "고정할 수 있는 노트 수"
antennaMax: "최대 안테나 ìƒì„± 허용 수"
wordMuteMax: "단어 뮤트할 수 있는 ë¬¸ìž ìˆ˜"
@@ -1246,15 +1357,18 @@ _role:
rateLimitFactor: "요청 ë¹ˆë„ ì œí•œ"
descriptionOfRateLimitFactor: "ìž‘ì„ìˆ˜ë¡ ì œí•œì´ ì™„í™”ë˜ê³ , í´ìˆ˜ë¡ ì œí•œì´ ê°•í™”ë©ë‹ˆë‹¤."
canHideAds: "광고 숨기기"
+ canSearchNotes: "노트 검색 ì´ìš© 가능 여부"
_condition:
isLocal: "로컬 사용ìž"
isRemote: "리모트 사용ìž"
- createdLessThan: "가압한 ì§€ ë‹¤ìŒ ì¼ìˆ˜ ì´ë‚´ì¸ 유저"
+ createdLessThan: "가입한 ì§€ ë‹¤ìŒ ì¼ìˆ˜ ì´ë‚´ì¸ 유저"
createdMoreThan: "가입한 ì§€ ë‹¤ìŒ ì¼ìˆ˜ ì´ìƒì¸ 유저"
followersLessThanOrEq: "팔로워 수가 ë‹¤ìŒ ì´í•˜ì¸ 유저"
followersMoreThanOrEq: "팔로워 수가 ë‹¤ìŒ ì´ìƒì¸ 유저"
followingLessThanOrEq: "팔로잉 수가 ë‹¤ìŒ ì´í•˜ì¸ 유저"
followingMoreThanOrEq: "팔로잉 수가 ë‹¤ìŒ ì´ìƒì¸ 유저"
+ notesLessThanOrEq: "노트 수가 ë‹¤ìŒ ì´í•˜ì¸ 유저"
+ notesMoreThanOrEq: "노트 수가 ë‹¤ìŒ ì´ìƒì¸ 유저"
and: "다ìŒì„ ëª¨ë‘ ë§Œì¡±"
or: "다ìŒì„ 하나ë¼ë„ 만족"
not: "다ìŒì„ 만족하지 않ìŒ"
@@ -1278,8 +1392,8 @@ _ffVisibility:
private: "비공개"
_signup:
almostThere: "ê±°ì˜ ë‹¤ ë났습니다"
- emailAddressInfo: "ë‹¹ì‹ ì´ ì‚¬ìš©í•˜ê³  있는 ì´ë©”ì¼ ì£¼ì†Œë¥¼ 입력해 주세요. ì´ë©”ì¼ ì£¼ì†ŒëŠ” 다른 유저ì—게 공개ë˜ì§€ 않습니다."
- emailSent: "입력하신 ë©”ì¼ ì£¼ì†Œ({email})로 í™•ì¸ ë©”ì¼ì„ 보내드렸습니다. ê°€ìž…ì„ ì™„ë£Œí•˜ì‹œë ¤ë©´ 보내드린 ë©”ì¼ì— 있는 ë§í¬ë¡œ ì ‘ì†í•´ 주세요."
+ emailAddressInfo: "ë‹¹ì‹ ì´ ì‚¬ìš©í•˜ê³  있는 ì´ë©”ì¼ ì£¼ì†Œë¥¼ 입력해 주세요. ì´ë©”ì¼ ì£¼ì†ŒëŠ” 다른 유저ì—게 공개ë˜ì§€ 않습니다."
+ emailSent: "입력하신 ë©”ì¼ ì£¼ì†Œ({email})로 í™•ì¸ ë©”ì¼ì„ 보내드렸습니다. ê°€ìž…ì„ ì™„ë£Œí•˜ì‹œë ¤ë©´ 보내드린 ë©”ì¼ì— 있는 ë§í¬ë¡œ ì ‘ì†í•´ 주세요."
_accountDelete:
accountDelete: "계정 삭제"
mayTakeTime: "계정 삭제는 ì„œë²„ì— ë¶€í•˜ë¥¼ 가하기 때문ì—, 작성한 콘í…츠나 업로드한 파ì¼ì˜ 수가 많으면 완료까지 ì‹œê°„ì´ ê±¸ë¦´ 수 있습니다."
@@ -1364,6 +1478,8 @@ _channel:
following: "팔로잉"
usersCount: "{n}명 참여 중"
notesCount: "{n}노트"
+ nameAndDescription: "ì´ë¦„ê³¼ 설명"
+ nameOnly: "ì´ë¦„ë§Œ"
_menuDisplay:
sideFull: "가로"
sideIcon: "가로(ì•„ì´ì½˜)"
@@ -1481,32 +1597,6 @@ _time:
minute: "ë¶„"
hour: "시간"
day: "ì¼"
-_tutorial:
- title: "Misskeyì˜ ì‚¬ìš© 방법"
- step1_1: "환ì˜í•©ë‹ˆë‹¤!"
- step1_2: "ì´ íŽ˜ì´ì§€ëŠ” \"타임ë¼ì¸\"ì´ë¼ê³  불립니다. ë‹¹ì‹ ì´ \"팔로우\"하고 있는 ì‚¬ëžŒë“¤ì˜ \"노트\"ê°€ 시간순으로 나타납니다."
- step1_3: "ì•„ì§ ì•„ë¬´ ìœ ì €ë„ íŒ”ë¡œìš°í•˜ê³  있지 ì•Šê¸°ì— íƒ€ìž„ë¼ì¸ì€ 비어 ìžˆì„ ê²ƒìž…ë‹ˆë‹¤."
- step2_1: "새 노트를 작성하거나 다른 ì‚¬ëžŒì„ íŒ”ë¡œìš°í•˜ê¸° ì „ì—, 먼저 í”„ë¡œí•„ì„ ì™„ì„±í•´ë³´ë„ë¡ í•©ì‹œë‹¤."
- step2_2: "ë‹¹ì‹ ì´ ì–´ë–¤ 사람ì¸ì§€ë¥¼ 알린다면, 다른 ì‚¬ëžŒë“¤ì´ ë‹¹ì‹ ì„ íŒ”ë¡œìš°í•  í™•ë¥ ì´ ì˜¬ë¼ê°ˆ 것입니다."
- step3_1: "프로필 ì„¤ì •ì€ ìž˜ ë내셨나요?"
- step3_2: "그럼 시험삼아 노트를 작성해 보세요. í™”ë©´ì— ìžˆëŠ” ì—°í•„ ë²„íŠ¼ì„ ëˆ„ë¥´ë©´ 작성 í¼ì´ 열립니다."
- step3_3: "ë‚´ìš©ì„ ìž‘ì„±í•œ 후, í¼ ì˜¤ë¥¸ìª½ ìƒë‹¨ì˜ ë²„íŠ¼ì„ ëˆŒëŸ¬ 노트를 올릴 수 있습니다."
- step3_4: "쓸 ë§ì´ 없나요? \"Misskey 시작했어요!\" ê°™ì€ ê±´ 어떨까요? :>"
- step4_1: "노트 ìž‘ì„±ì„ ë내셨나요?"
- step4_2: "ë‹¹ì‹ ì˜ ë…¸íŠ¸ê°€ 타임ë¼ì¸ì— 표시ë˜ì–´ 있다면 성공입니다."
- step5_1: "ì´ì œ, 다른 ì‚¬ëžŒì„ íŒ”ë¡œìš°í•˜ì—¬ 타임ë¼ì¸ì„ 활기차게 만들어보ë„ë¡ í•©ì‹œë‹¤."
- step5_2: "{featured}ì—서 ì´ ì„œë²„ì˜ ì¸ê¸° 노트를 보실 수 있습니다. {explore}ì—서는 ì¸ê¸° 사용ìžë¥¼ ì°¾ì„ ìˆ˜ 있구요. 마ìŒì— 드는 ì‚¬ëžŒì„ ê³¨ë¼ íŒ”ë¡œìš°í•´ 보세요!"
- step5_3: "다른 유저를 팔로우하려면 해당 ìœ ì €ì˜ ì•„ì´ì½˜ì„ í´ë¦­í•˜ì—¬ 프로필 페ì´ì§€ë¥¼ ë„ìš´ 후, 팔로우 ë²„íŠ¼ì„ ëˆŒëŸ¬ 주세요."
- step5_4: "사용ìžì— ë”°ë¼ íŒ”ë¡œìš°ê°€ 승ì¸ë  때까지 ì‹œê°„ì´ ê±¸ë¦´ 수 있습니다."
- step6_1: "타임ë¼ì¸ì— 다른 사용ìžì˜ 노트가 나타난다면 성공입니다."
- step6_2: "다른 ìœ ì €ì˜ ë…¸íŠ¸ì— \"리액션\"ì„ ë¶™ì—¬ 간단하게 ë‹¹ì‹ ì˜ ë°˜ì‘ì„ ì „ë‹¬í•  ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤."
- step6_3: "ë¦¬ì•¡ì…˜ì„ ë¶™ì´ë ¤ë©´, ë…¸íŠ¸ì˜ \"+\" ë²„íŠ¼ì„ í´ë¦­í•˜ê³  ì›í•˜ëŠ” ì´ëª¨ì§€ë¥¼ ì„ íƒí•©ë‹ˆë‹¤."
- step7_1: "ì´ê²ƒìœ¼ë¡œ Misskeyì˜ ê¸°ë³¸ íŠœí† ë¦¬ì–¼ì„ ë§ˆì¹˜ê² ìŠµë‹ˆë‹¤. 수고하셨습니다!"
- step7_2: "Misskeyì— ëŒ€í•´ ë” ì•Œê³  싶으시다면 {help}를 참고해 주세요."
- step7_3: "그럼 Misskey를 ì¦ê¸°ì„¸ìš”! 🚀"
- step8_1: "마지막으로, 푸시 ì•Œë¦¼ì„ í™œì„±í™”í•´ ë³´ì§€ 않으실래요?"
- step8_2: "푸시 ì•Œë¦¼ì„ í™œì„±í™”í•˜ë©´, Misskey를 ì—´ì§€ ì•Šì•˜ì„ ë•Œì—ë„ ë¦¬ì•¡ì…˜ì´ë‚˜ 팔로우, 멘션 ë“±ì„ í™•ì¸í•  수 있습니다."
- step8_3: "알림 ì„¤ì •ì€ ë‚˜ì¤‘ì—ë„ ë³€ê²½í•  수 있습니다."
_2fa:
alreadyRegistered: "ì´ë¯¸ ì„¤ì •ì´ ì™„ë£Œë˜ì—ˆìŠµë‹ˆë‹¤."
registerTOTP: "ì¸ì¦ 앱 설정 시작"
@@ -1844,9 +1934,27 @@ _deck:
channel: "채ë„"
mentions: "ë°›ì€ ë©˜ì…˜"
direct: "다ì´ë ‰íЏ"
+ roleTimeline: "ì—­í•  타임ë¼ì¸"
_dialog:
charactersExceeded: "최대 글ìžìˆ˜ë¥¼ 초과하였습니다! 현재 {current} / 최대 {min}"
charactersBelow: "최소 글ìžìˆ˜ 미만입니다! 현재 {current} / 최소 {min}"
+_disabledTimeline:
+ title: "ë¹„í™œì„±í™”ëœ íƒ€ìž„ë¼ì¸"
+ description: "현재 ì—­í• ì—서는 ì´ íƒ€ìž„ë¼ì¸ì„ ì´ìš©í•  수 없습니다."
+_drivecleaner:
+ orderBySizeDesc: "í¬ê¸°ê°€ í° ìˆœ"
+ orderByCreatedAtAsc: "등ë¡ì¼ì´ ì˜¤ëž˜ëœ ìˆœ"
_webhookSettings:
+ createWebhook: "Webhook ìƒì„±"
name: "ì´ë¦„"
+ secret: "시í¬ë¦¿"
+ events: "Webhookì„ ì‹¤í–‰í•  타ì´ë°"
active: "활성화"
+ _events:
+ follow: "누군가를 íŒ”ë¡œìš°í–ˆì„ ë•Œ"
+ followed: "누군가 나를 íŒ”ë¡œìš°í–ˆì„ ë•Œ"
+ note: "노트를 게시할 때"
+ reply: "ë‹µê¸€ì„ ë°›ì•˜ì„ ë•Œ"
+ renote: "누군가 ë‚´ ê¸€ì„ Renoteí–ˆì„ ë•Œ"
+ reaction: "누군가 ë‚´ ë…¸íŠ¸ì— ë¦¬ì•¡ì…˜í–ˆì„ ë•Œ"
+ mention: "누군가 나를 ë©˜ì…˜í–ˆì„ ë•Œ"
diff --git a/locales/lo-LA.yml b/locales/lo-LA.yml
index 3f97b3982c..f686b04257 100644
--- a/locales/lo-LA.yml
+++ b/locales/lo-LA.yml
@@ -211,11 +211,11 @@ uploadFromUrlRequested: "ຮ້ອງຂà»àºàº²àº™àº­àº±àºšà»‚ຫລດ"
messageRead: "ອ່ານà»àº¥à»‰àº§"
startMessaging: "ເລີ່ມàºàº²àº™àºªàº»àº™àº—ະນາໃà»à»ˆ"
nUsersRead: "ອ່ານໂດຠ{n}"
-tos: "ເງື່ອນໄຂàºàº²àº™à»ƒàº«à»‰àºšà»àº¥àº´àºàº²àº™"
start: "ເລີ່ມຕົ້ນນຳໃຊ້ເລີàº"
home: "ໜ້າຫຼັàº"
activity: "àºàº´àº”ຈະàºàº³"
images: "ຮູບພາບ"
+image: "ຮູບພາບ"
birthday: "ວັນເàºàºµàº”"
yearsOld: "{age} ປີ"
registeredDate: "ວັນທີ່ເປັນສະມາຊິàº"
diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml
index 31c28a66e3..6bca484f14 100644
--- a/locales/nl-NL.yml
+++ b/locales/nl-NL.yml
@@ -254,12 +254,12 @@ noMoreHistory: "Er is geen verdere geschiedenis"
startMessaging: "Start een gesprek"
nUsersRead: "gelezen door {n}"
agreeTo: "Ik stem in met {0}"
-tos: "Gebruiksvoorwaarden"
start: "Aan de slag"
home: "Startpagina"
remoteUserCaution: "Aangezien deze gebruiker van een externe server afkomstig is, kan de weergegeven informatie onvolledig zijn."
activity: "Activiteit"
images: "Afbeeldingen"
+image: "Afbeeldingen"
birthday: "Geboortedatum"
yearsOld: "{age} jaar"
registeredDate: "Inschrijvingsdatum"
diff --git a/locales/no-NO.yml b/locales/no-NO.yml
index 83e189b9cf..f7f0d442d4 100644
--- a/locales/no-NO.yml
+++ b/locales/no-NO.yml
@@ -1,2 +1,483 @@
---
_lang_: "Norsk Bokmål"
+search: "Søk"
+notifications: "Varsler"
+username: "Brukernavn"
+password: "Passord"
+forgotPassword: "Glemt passord"
+ok: "OK"
+gotIt: "Skjønner"
+cancel: "Avbryt"
+noThankYou: "Avbryt"
+noNotifications: "Ingen varsler"
+instance: "Server"
+settings: "Innstillinger"
+notificationSettings: "Varslingsinnstillinger"
+otherSettings: "Andre innstillinger"
+profile: "Profil"
+timeline: "Tidslinje"
+login: "Logg inn"
+loggingIn: "Logget inn"
+logout: "Logg ut"
+signup: "Bli med"
+uploading: "Laster opp"
+save: "Lagre"
+users: "Brukere"
+addUser: "Legg til bruker"
+favorite: "Favoritt"
+favorites: "Favoritter"
+unfavorite: "Fjern favoritt"
+pin: "Fest"
+unpin: "Opphev festing"
+copyContent: "Kopier innhold"
+copyLink: "Kopier lenke"
+delete: "Slett"
+deleteAndEdit: "Slett og rediger"
+addToList: "Legg til i liste"
+sendMessage: "Send melding"
+copyRSS: "Kopier RSS"
+copyUsername: "Kopier brukernavn"
+searchUser: "Søk brukere"
+reply: "Svar"
+loadMore: "Vis mer"
+showMore: "Vis mer"
+showLess: "Lukk"
+files: "Filer"
+download: "Nedlastinger"
+lists: "Lister"
+noLists: "Ingen lister"
+following: "Følg"
+followers: "Følgere"
+followsYou: "Følger deg"
+createList: "Opprett liste"
+error: "Feil"
+retry: "Prøv igjen"
+pageLoadError: "Kunne ikke hente side."
+privacy: "Personvern"
+follow: "Følg"
+followRequest: "Følgeforespørsel"
+followRequests: "Følgeforespørsel"
+unfollow: "Avfølg"
+followRequestPending: "Venter på godkjenning"
+quote: "Sitat"
+pinned: "Fest"
+you: "Du"
+clickToShow: "Klikk for å vise"
+add: "Legg til"
+reaction: "Reaksjon"
+reactions: "Reaksjoner"
+mute: "Skjul"
+unmute: "Vis"
+block: "Blokker"
+unblock: "Opphev blokkering"
+blockConfirm: "Blokker?"
+selectList: "Velg liste"
+selectChannel: "Velg kanal"
+emoji: "Emoji"
+emojis: "Emojier"
+addEmoji: "Legg til emoji"
+addAccount: "Legg til konto"
+selectUser: "Velg bruker"
+instances: "Server"
+registeredAt: "Registrerte seg"
+perHour: "Per time"
+perDay: "Per dag"
+blockThisInstance: "Blokker denne serveren"
+version: "Versjon"
+statistics: "Statistikk"
+clearQueue: "Tøm kø"
+clearQueueConfirmTitle: "Vil du tømme kø?"
+blockedInstances: "Blokkerte severe"
+muteAndBlock: "Skjul og blokker"
+mutedUsers: "Skjulte brukere"
+blockedUsers: "Blokkerte brukere"
+editProfile: "Rediger profil"
+pinLimitExceeded: "Du kan ikke feste flere."
+noCustomEmojis: "Ingen emoji"
+blocked: "Blokkert"
+all: "Alle"
+changePassword: "Endre passord"
+security: "Sikkerhet"
+newPassword: "Nytt passord"
+newPasswordRetype: "Nytt passord (gjenta)"
+more: "Mer!"
+remove: "Slett"
+removed: "Slettet"
+saved: "Lagret"
+upload: "Laste opp"
+explore: "Utforsk"
+messageRead: "Lest"
+agree: "Jeg godtar"
+home: "Hjem"
+images: "Bilder"
+image: "Bilder"
+birthday: "Bursdag"
+yearsOld: "{age} år gammel"
+light: "Lys"
+dark: "Mørk"
+fileName: "Filnavn"
+selectFile: "Velg fil"
+selectFiles: "Velg fil"
+selectFolder: "Velg mappe"
+selectFolders: "Velg mappe"
+renameFile: "Endre filnavn"
+folderName: "Mappenavn"
+createFolder: "Opprett mappe"
+renameFolder: "Endre mappenavn"
+deleteFolder: "Slett mappe"
+addFile: "Legg til fil"
+emptyFolder: "Denne mappen er tom"
+copyUrl: "Kopier URL"
+rename: "Endre navn"
+doNothing: "Gjør ingenting"
+accept: "Tillatt"
+reject: "Avslå"
+instanceName: "Servernavn"
+thisYear: "I år"
+today: "I dag"
+pages: "Sider"
+pinnedUsers: "Festete brukrere"
+pinnedPages: "Festete sider"
+hcaptcha: "hCaptcha"
+recaptcha: "reCAPTCHA"
+name: "Navn"
+popularUsers: "Populære brukere"
+exploreUsersCount: "Det finnes {count} brukere"
+userList: "Lister"
+about: "Infomasjon"
+aboutMisskey: "Om Misskey"
+share: "Del"
+help: "Hjelp"
+close: "Lukk"
+members: "Medlemmer"
+text: "Tekst"
+next: "Neste"
+retype: "Gjenta"
+available: "Tilgjengelig"
+unavailable: "Utilgjengelig"
+tooShort: "For kort"
+tooLong: "For langt"
+or: "eller"
+language: "Språk"
+aboutX: "Om {x}"
+category: "Kategorier"
+createAccount: "Opprett konto"
+objectStorageRegion: "Region"
+objectStorageUseSSL: "Bruk SSL"
+objectStorageUseProxy: "Bruk Proxy"
+deleteAll: "Slett alt"
+listen: "Lytt"
+none: "Ingen"
+chooseEmoji: "Velg emoji"
+recentUsed: "Sist brukte"
+install: "Installer"
+nothing: "Ingenting"
+deleteAllFiles: "Slett alle filer"
+deleteAllFilesConfirm: "Vil du slette alle filer?"
+accountDeleted: "Kontoen blir slettet"
+accountDeletedDescription: "Denne kontoen blir slettet"
+menu: "Meny"
+poll: "Avstemning"
+description: "Beskrivelse"
+author: "Forfatter"
+height: "Høyde"
+large: "Stor"
+small: "Liten"
+notificationType: "Varseltype"
+edit: "Rediger"
+email: "E-post"
+smtpUser: "Brukernavn"
+smtpPass: "Passord"
+userSaysSomething: "{name} sa noe"
+copy: "Kopier"
+channel: "Kanaler"
+create: "Opprett"
+notificationSetting: "Varslingsinnstillinger"
+other: "Andre"
+behavior: "Oppførsel"
+sample: "Eksempel"
+abuseReports: "Rappoter"
+reportAbuse: "Rappoter"
+send: "Send"
+openInNewTab: "Ã…pne i ny fane"
+waitingFor: "Venter på {x}"
+system: "System"
+followingCount: "Følger"
+followersCount: "Følgere"
+yes: "Ja"
+no: "Nei"
+left: "Venstre"
+saveAs: "Lagre som"
+value: "Verdi"
+deleteConfirm: "Vil du slette?"
+invalidValue: "Verdien er ugyldig."
+emailNotification: "E-postvarsler"
+inChannelSearch: "Søk i kanal"
+clear: "Tøm"
+markAllAsRead: "Merk alt som lest"
+addDescription: "Legg til beskrivelse"
+info: "Infomasjon"
+unknown: "Ukjent"
+selectAccount: "Velg konto"
+user: "Brukere"
+accounts: "Kontoer"
+switch: "Bytt"
+gallery: "Galleri"
+ads: "Annonser"
+high: "Høy"
+low: "Lav"
+sent: "Send"
+learnMore: "Les mer"
+translate: "Oversett"
+unread: "Ulest"
+manageAccounts: "Administrer konto"
+classic: "Klassisk"
+muteThread: "Skjul denne tråden"
+unmuteThread: "Vis denne tråden"
+tablet: "Nettbrett"
+auto: "Automatisk"
+size: "Størrelse"
+searchByGoogle: "Søk"
+tenMinutes: "10 minutter"
+oneHour: "1 time"
+oneDay: "1 dag"
+oneWeek: "1 uke"
+oneMonth: "1 måned"
+file: "Filer"
+recommended: "Anbefalt"
+check: "Sjekk"
+deleteAccount: "Slett konto"
+document: "Dokumenter"
+logoutConfirm: "Vil du logge ut?"
+pleaseSelect: "Vennligst velg"
+type: "Type"
+beta: "Beta"
+account: "Kontoer"
+move: "Flytt"
+pushNotification: "Push-varsler"
+tools: "Verktøy"
+like: "Liker!"
+unlike: "Liker ikke"
+numberOfLikes: "Likerklikk"
+show: "Vis"
+neverShow: "Ikke vis igjen"
+remindMeLater: "Kanskje senere"
+didYouLikeMisskey: "Likte du Misskey?"
+roles: "Roller"
+role: "Rolle"
+color: "Farge"
+youCannotCreateAnymore: "Du kan ikke opprette flere."
+cannotPerformTemporary: "Midlertidig utilgjengelig"
+thisPostMayBeAnnoyingCancel: "Avbryt"
+exploreOtherServers: "Utforsk andre severe"
+letsLookAtTimeline: "La oss se på tidslinje"
+cannotBeChangedLater: "Du kan ikke endre senere."
+likeOnly: "Bare liker"
+retryAllQueuesConfirmTitle: "Vil du prøve igjen akkurat nå?"
+video: "Video"
+videos: "Videoer"
+continue: "Fortsett"
+youFollowing: "Følger"
+_initialAccountSetting:
+ theseSettingsCanEditLater: "Du kan endre disse innstillingene senere."
+_achievements:
+ _types:
+ _notes100000:
+ flavor: "Du har jammen mye å si."
+ _noteFavorited1:
+ title: "Stjernekikker"
+ _myNoteFavorited1:
+ title: "Jeg vil gjerne få en stjerne"
+ _following50:
+ title: "Mange venner"
+ _following100:
+ title: "100 venner"
+ _following300:
+ title: "For mange venner"
+ _followers10:
+ title: "Følg meg!"
+ _followers100:
+ title: "Populær"
+ _postedAtLateNight:
+ flavor: "Det er på tide å gå til sengs."
+ _driveFolderCircularReference:
+ title: "Rundskrivreferanse"
+ _reactWithoutRead:
+ title: "Leste du det virkelig?"
+ _clickedClickHere:
+ title: "Klikk her"
+ description: "Du har klikket her"
+ _justPlainLucky:
+ title: "Rett og slett heldig"
+ _setNameToSyuilo:
+ description: "Du har satt navnet ditt til \"syuilo\""
+ _loggedInOnBirthday:
+ title: "Gratulerer med dagen"
+ _loggedInOnNewYearsDay:
+ title: "Godt nytt år"
+ _brainDiver:
+ title: "Brain Diver"
+ flavor: "Misskey-Misskey La-Tu-Ma"
+_role:
+ options: "Alternativ"
+ _priority:
+ low: "Lav"
+ high: "Høy"
+_emailUnavailable:
+ used: "Allerede brukt"
+_accountDelete:
+ accountDelete: "Slett konto"
+_ad:
+ hide: "Ikke vis"
+_gallery:
+ like: "Liker!"
+ unlike: "Liker ikke"
+_preferencesBackups:
+ saveNew: "Lagre som ny"
+ cannotSave: "Kunne ikke lagre"
+_registry:
+ key: "Nøkkel"
+ keys: "Nøkler"
+_aboutMisskey:
+ about: "Misskey er programvare med åpen kildekode som har blitt utviklet av syuilo siden 2014."
+ translation: "Oversett Misskey"
+_instanceTicker:
+ none: "Ikke vis"
+ always: "Alltid vis"
+_channel:
+ create: "Opprett kanal"
+ edit: "Rediger kanal"
+ featured: "Populært"
+ following: "Følger"
+ nameAndDescription: "Navn og beskrivelse"
+_wordMute:
+ soft: "Myk"
+ hard: "Hard"
+_theme:
+ description: "Beskrivelse"
+ color: "Farge"
+ key: "Nøkkel"
+ keys:
+ link: "Lenke"
+_sfx:
+ notification: "Varsler"
+_ago:
+ future: "Fremitid"
+ justNow: "Akkurat nå"
+ secondsAgo: "{n} sekunder siden"
+ minutesAgo: "{n} minutter siden"
+ hoursAgo: "{n} timer siden"
+ daysAgo: "{n} dager siden"
+ weeksAgo: "{n} uker siden"
+ monthsAgo: "{n} måneder siden"
+ yearsAgo: "{n} år siden"
+ invalid: "Ingenting"
+_time:
+ second: "Sekunder"
+ minute: "Minutter"
+ hour: "Timer"
+ day: "Dager"
+_timelineTutorial:
+ title: "Hvordan bruke Misskey"
+_2fa:
+ renewTOTPCancel: "Avbryt"
+_weekday:
+ sunday: "Søndag"
+ monday: "Mandag"
+ tuesday: "Tirsdag"
+ wednesday: "Onsdag"
+ thursday: "Torsdag"
+ friday: "Fredag"
+ saturday: "Lørdag"
+_widgets:
+ profile: "Profil"
+ notifications: "Varsler"
+ timeline: "Tidslinje"
+ calendar: "Kalender"
+ trends: "Populært"
+ clock: "Klokke"
+ photos: "Bilder"
+ button: "Knapp"
+ aiscriptApp: "AiScript App"
+ userList: "Brukerliste"
+ _userList:
+ chooseList: "Velg liste"
+_cw:
+ show: "Vis mer"
+_poll:
+ noOnlyOneChoice: "Trenger minst to valger."
+ choiceN: "Valg {n}"
+ noMore: "Du kan ikke legge til flere."
+ deadlineTime: "Timer"
+ votesCount: "{n} stemmer"
+ vote: "Stem"
+ showResult: "Vis resultatet"
+ voted: "Stemt"
+ closed: "Avsluttet"
+_visibility:
+ home: "Hjem"
+ followers: "Følgere"
+_postForm:
+ _placeholders:
+ a: "Hva skjer?"
+_profile:
+ name: "Navn"
+ username: "Brukernavn"
+ metadataContent: "Innhold"
+_exportOrImport:
+ followingList: "Følg"
+ muteList: "Skjul"
+ blockingList: "Blokker"
+ userLists: "Lister"
+_charts:
+ filesIncDec: "Forskjell på antall filer"
+_instanceCharts:
+ users: "Forskjell på antall brukere"
+ ff: "Forskjell på antall Følg/Følgere"
+ files: "Forskjell på antall filer"
+_timelines:
+ home: "Hjem"
+_play:
+ new: "Opprett Play"
+ edit: "Rediger Play"
+ featured: "Populært"
+ summary: "Beskrivelse"
+_pages:
+ like: "Liker"
+ unlike: "Liker ikke"
+ my: "Mine sider"
+ featured: "Populært"
+ contents: "Innhold"
+ url: "Side URL"
+ fontSerif: "Serif"
+ fontSansSerif: "Sans Serif"
+ selectType: "Velg type"
+ blocks:
+ text: "Tekst"
+ section: "Seksjon"
+ image: "Bilde"
+ button: "Knapp"
+_notification:
+ _types:
+ follow: "Følg"
+ reply: "Svar"
+ quote: "Sitat"
+ reaction: "Reaksjon"
+ _actions:
+ reply: "Svar"
+_deck:
+ swapLeft: "Flytt til venstre"
+ swapRight: "Flytt til høyre"
+ swapUp: "Flytt opp"
+ swapDown: "Flytt ned"
+ profile: "Profil"
+ newProfile: "Ny profil"
+ deleteProfile: "Slett profil"
+ _columns:
+ notifications: "Varsler"
+ tl: "Tidslinje"
+ list: "Lister"
+ channel: "Kanaler"
+ direct: "Direkte"
+_webhookSettings:
+ name: "Navn"
diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml
index 517e8431f9..24eee60bae 100644
--- a/locales/pl-PL.yml
+++ b/locales/pl-PL.yml
@@ -257,12 +257,12 @@ startMessaging: "Rozpocznij czat"
nUsersRead: "przeczytano przez {n}"
agreeTo: "Wyrażam zgodę na {0}"
agreeBelow: "Zaakceptuj poniżej"
-tos: "Regulamin"
start: "Rozpocznij"
home: "Strona główna"
remoteUserCaution: "Te informacje mogą nie być aktualne, ponieważ użytkownik pochodzi ze zdalnej instancji."
activity: "Aktywność"
images: "Zdjęcia"
+image: "Zdjęcia"
birthday: "Data urodzenia"
yearsOld: "{age} lat"
registeredDate: "Zarejestrowano"
@@ -870,6 +870,7 @@ loggedInAsBot: "JesteÅ› obecnie zalogowany/a jako bot"
like: "Polub"
show: "Wyświetlanie"
color: "Kolor"
+youFollowing: "Åšledzeni"
_role:
priority: "Priorytet"
_priority:
@@ -1086,32 +1087,6 @@ _time:
minute: "minuta"
hour: "godz."
day: "dzień"
-_tutorial:
- title: "Jak korzystać z Misskey"
- step1_1: "Witaj!"
- step1_2: "Ta strona nazywa siÄ™ „oÅ› czasuâ€. Pokazuje chronologicznie uporzÄ…dkowane wpisy osób, które „śledziszâ€."
- step1_3: "Twoja oś czasu jest jeszcze pusta, ponieważ nie opublikowałeś(-aś) jeszcze żadnych wpisów i nie obserwujesz jeszcze nikogo."
- step2_1: "Ukończmy konfigurację profilu zanim utworzymy wpis lub zaczniemy kogoś obserwować."
- step2_2: "Podanie pewnych informacji o tym, kim jesteś, ułatwi innym określenie, czy chcą widzieć Twoje wpisy lub Cię obserwować."
- step3_1: "Zakończyłeś(-aś) konfigurację profilu?"
- step3_2: "Następnie spróbujmy opublikować wpis. Możesz to zrobić, naciskając przycisk z ikoną ołówka na ekranie."
- step3_3: "Wypełnij pole i kliknij przycisk w prawym górnym rogu by wysłać post."
- step3_4: "Nie masz nic do powiedzenia? Spróbuj \"ustawiam swój misskey\"!"
- step4_1: "Zakończyłeś publikowanie pierwszego wpisu?"
- step4_2: "Hurra! Teraz Twój pierwszy wpis powinien być wyświetlany na Twojej osi czasu."
- step5_1: "Teraz spróbujmy ożywić Twoją oś czasu, przez zaobserwowanie innych ludzi."
- step5_2: "{featured} pokaże Ci popularne wpisy na tej instancji. {explore} pozwoli Ci znaleźć popularnych użytkowników. Spróbuj znaleźć tam osoby, które chcesz obserwować!"
- step5_3: "Aby obserwować innych użytkowników, kliknij ich ikonę i naciśnij przycisk \"Obserwuj\" na ich profilu."
- step5_4: "Jeśli inny użytkownik ma ikonę kłódki obok swojej nazwy, może minąć trochę czasu, zanim ten użytkownik ręcznie zatwierdzi Twoją prośbę o obserwowanie."
- step6_1: "Powinieneś teraz widzieć wpisy innych użytkowników na swojej osi czasu."
- step6_2: "Możesz także umieścić „reakcje†na wpisach innych osób, aby szybko na nie odpowiedzieć."
- step6_3: "Aby dodać \"reakcję\", naciśnij znak \"+\" na wpisie innego użytkownika i wybierz emotikonę, którą chcesz zareagować."
- step7_1: "Gratulacje! Ukończyłeś podstawowy samouczek Misskey."
- step7_2: "Jeśli chcesz dowiedzieć się więcej o Misskey, wypróbuj sekcję {help}."
- step7_3: "A teraz powodzenia i baw się dobrze z Misskey! 🚀"
- step8_1: "Na sam koniec, czy nie chciał(a)byś włączyć powiadomień push?"
- step8_2: "Włączenie tej opcji pozwoli ci otrzymywać powiadomienia o reakcjach, śledzeniach i wzmiankach nawet wtedy, gdy Misskey nie będzie otwarty."
- step8_3: "Ustawienia powiadomień można zmienić później."
_2fa:
alreadyRegistered: "Zarejestrowałeś już urządzenie do uwierzytelniania dwuskładnikowego."
step1: "Najpierw, zainstaluj aplikacjÄ™ uwierzytelniajÄ…cÄ… (takÄ… jak {a} lub {b}) na swoim urzÄ…dzeniu."
diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml
index 8a7232fa2c..e7b43f6380 100644
--- a/locales/pt-PT.yml
+++ b/locales/pt-PT.yml
@@ -252,12 +252,12 @@ noMoreHistory: "Sem mais história"
startMessaging: "Iniciar conversação"
nUsersRead: "{n} Pessoas leem"
agreeTo: "Eu concordo com {0}"
-tos: "Termos de serviço"
start: "começar"
home: "casa"
remoteUserCaution: "As informações estão incompletas porque é um utilizador remoto."
activity: "atividade"
images: "imagem"
+image: "imagem"
birthday: "aniversário"
yearsOld: "{age} anos"
registeredDate: "Data de registro"
diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml
index bbf0160de9..6ad45d0636 100644
--- a/locales/ro-RO.yml
+++ b/locales/ro-RO.yml
@@ -252,12 +252,12 @@ noMoreHistory: "Nu există mai mult istoric"
startMessaging: "ÃŽncepe un chat nou"
nUsersRead: "citit de {n}"
agreeTo: "Sunt de acord cu {0}"
-tos: "Termenii de utilizare"
start: "Să începem"
home: "Acasă"
remoteUserCaution: "Deoarece acest utilizator este dintr-o instanță externă, informația afișată poate fi incompletă."
activity: "Activitate"
images: "Imagini"
+image: "Imagini"
birthday: "Zi de naștere"
yearsOld: "{age} ani"
registeredDate: "Data înregistrării"
diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml
index 8a09d30306..55d4b70590 100644
--- a/locales/ru-RU.yml
+++ b/locales/ru-RU.yml
@@ -20,6 +20,7 @@ noNotes: "Ðет ни одной заметки"
noNotifications: "Ðет ни одного уведомлениÑ"
instance: "ИнÑтанÑ"
settings: "ÐаÑтройки"
+notificationSettings: "ÐаÑтройки уведомлений"
basicSettings: "ОÑновные наÑтройки"
otherSettings: "Прочие наÑтройки"
openInWindow: "Открыть в плавающем окне"
@@ -122,6 +123,8 @@ unmarkAsSensitive: "СнÑть отметку «не Ð´Ð»Ñ Ð²Ñех»"
enterFileName: "Введите Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°"
mute: "Скрыть"
unmute: "Отменить Ñкрытие"
+renoteMute: "Заглушить репоÑты"
+renoteUnmute: "Включить репоÑты"
block: "Заблокировать"
unblock: "Разблокировать"
suspend: "Заморозить"
@@ -153,6 +156,7 @@ flagShowTimelineReplies: "Показывать ответы на заметки
flagShowTimelineRepliesDescription: "ЕÑли Ñтот параметр включен, то в ленте, в дополнение к заметкам пользователÑ, отображаютÑÑ Ð¾Ñ‚Ð²ÐµÑ‚Ñ‹ на другие заметки пользователÑ."
autoAcceptFollowed: "Принимать подпиÑчиков автоматичеÑки"
addAccount: "Добавить учётную запиÑÑŒ"
+reloadAccountsList: "Обновить ÑпиÑок учётных запиÑей"
loginFailed: "ÐÐµÑƒÐ´Ð°Ñ‡Ð½Ð°Ñ Ð¿Ð¾Ð¿Ñ‹Ñ‚ÐºÐ° входа"
showOnRemote: "Перейти к оригиналу на Ñайт"
general: "Общее"
@@ -259,14 +263,16 @@ noMoreHistory: "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð·Ð°ÐºÐ¾Ð½Ñ‡Ð¸Ð»Ð°ÑÑŒ"
startMessaging: "Ðачать общение"
nUsersRead: "Прочитали {n}"
agreeTo: "Я ÑоглашаюÑÑŒ Ñ {0}"
+agree: "СоглаÑитьÑÑ"
agreeBelow: "СоглаÑен Ñо Ñледующими"
basicNotesBeforeCreateAccount: "ЗапиÑи, перед Ñозданием аккаунта"
-tos: "ПользовательÑкое Ñоглашение"
+termsOfService: "УÑÐ»Ð¾Ð²Ð¸Ñ Ð¸ÑпользованиÑ"
start: "Ðачать"
home: "ГлавнаÑ"
remoteUserCaution: "Это пользователь Ñ Ð´Ñ€ÑƒÐ³Ð¾Ð³Ð¾ Ñайта, поÑтому Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¼Ð¾Ð¶ÐµÑ‚ быть неточной."
activity: "ÐктивноÑть"
images: "ИзображениÑ"
+image: "ИзображениÑ"
birthday: "День рождениÑ"
yearsOld: "ВозраÑÑ‚: {age}"
registeredDate: "Дата региÑтрации"
@@ -457,6 +463,7 @@ aboutX: "ОпиÑание {x}"
emojiStyle: "Стиль Ñмодзи"
native: "СиÑтемные"
disableDrawer: "Ðе иÑпользовать выдвижные меню"
+showNoteActionsOnlyHover: "Показывать кнопки ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ¾Ð¹ только при наведении"
noHistory: "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð¿Ð¾ÐºÐ° пуÑта"
signinHistory: "Журнал поÑещений"
enableAdvancedMfm: "Включить раÑширенный MFM"
@@ -469,6 +476,8 @@ createAccount: "ÐÐ¾Ð²Ð°Ñ ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ"
existingAccount: "СущеÑÑ‚Ð²ÑƒÑŽÑ‰Ð°Ñ ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ"
regenerate: "Создать повторно"
fontSize: "Размер шрифта"
+mediaListWithOneImageAppearance: "Показывать ÑпиÑок медиа только одним изображением"
+limitTo: "Обрезать до {x}"
noFollowRequests: "ÐераÑÑмотренные запроÑÑ‹ на подпиÑку отÑутÑтвуют"
openImageInNewTab: "Открыть изображение в новой вкладке"
dashboard: "Панель управлениÑ"
@@ -502,9 +511,11 @@ objectStorageUseSSLDesc: "Отключите, еÑли не ÑобираетеÑ
objectStorageUseProxy: "ИÑпользовать прокÑи"
objectStorageUseProxyDesc: "Отключите, еÑли не будете иÑпоьзовать прокÑи Ð´Ð»Ñ Ñоединений по протоколу ObjectStorage."
objectStorageSetPublicRead: "УÑтанавливать public-read при загрузке на Ñервер"
+s3ForcePathStyleDesc: "Включение s3ForcePathStyle принудительно указывает Ð¸Ð¼Ñ ÐºÐ¾Ñ€Ð·Ð¸Ð½Ñ‹ как чаÑть пути в URL-адреÑе вмеÑто имени хоÑта. Может потребоватьÑÑ Ð°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ð¸Ñ Ð¿Ñ€Ð¸ иÑпользовании таких вещей, как локальный Minio."
serverLogs: "Журнал Ñервера"
deleteAll: "Удалить вÑÑ‘"
showFixedPostForm: "Показывать поле Ð´Ð»Ñ Ð²Ð²Ð¾Ð´Ð° новой заметки наверху ленты"
+showFixedPostFormInChannel: "Показывать поле Ð´Ð»Ñ Ð²Ð²Ð¾Ð´Ð° новой заметки наверху ленты (каналы)"
newNoteRecived: "ПоÑвилаÑÑŒ Ð½Ð¾Ð²Ð°Ñ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ°"
sounds: "Звуки"
sound: "Звуки"
@@ -542,6 +553,10 @@ userSuspended: "Эта ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ заморожена"
userSilenced: "Этот пользователь был заглушен"
yourAccountSuspendedTitle: "Эта ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ заблокирована"
yourAccountSuspendedDescription: "Эта ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ была заблокирована из-за Ð½Ð°Ñ€ÑƒÑˆÐµÐ½Ð¸Ñ ÑƒÑловий предоÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÑƒÑлуг Ñервера. СвÑжитеÑÑŒ Ñ Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñтратором, еÑли вы хотите узнать более подробную причину. ПожалуйÑта, не Ñоздавайте новую учетную запиÑÑŒ."
+tokenRevoked: "Токен недейÑтвителен"
+tokenRevokedDescription: "Срок дейÑÑ‚Ð²Ð¸Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ токена входа иÑтек. ПожалуйÑта, войдите Ñнова."
+accountDeleted: "Эта ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ удалена"
+accountDeletedDescription: "Эта ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ удалена"
menu: "Меню"
divider: "ЛиниÑ-разделитель"
addItem: "Добавить Ñлемент"
@@ -688,6 +703,8 @@ contact: "Как ÑвÑзатьÑÑ"
useSystemFont: "ИÑпользовать шрифт, предлагаемый ÑиÑтемой"
clips: "Подборки"
experimentalFeatures: "ЭкÑпериментальные функции"
+experimental: "ЭкÑпериментальные"
+thisIsExperimentalFeature: "Это ÑкÑÐ¿ÐµÑ€Ð¸Ð¼ÐµÐ½Ñ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ. ТехничеÑкие характериÑтики могут изменитьÑÑ Ð¸Ð»Ð¸ он может работать неправильно."
developer: "Разработчик"
makeExplorable: "Опубликовать профиль в «Обзоре»."
makeExplorableDescription: "ЕÑли выключить, ваш профиль не будет показан в разделе «Обзор»."
@@ -852,6 +869,7 @@ tenMinutes: "10 минут"
oneHour: "1 чаÑ"
oneDay: "1 день"
oneWeek: "1 неделÑ"
+oneMonth: "1 меÑÑц"
reflectMayTakeTime: "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ занÑть Ð²Ñ€ÐµÐ¼Ñ Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ"
failedToFetchAccountInformation: "Ðе удалоÑÑŒ получить информацию об аккаунте"
rateLimitExceeded: "Ограничение ÑкороÑти превышено"
@@ -893,6 +911,7 @@ remoteOnly: "Только удалённо"
failedToUpload: "Сбой выгрузки"
cannotUploadBecauseInappropriate: "Файл не может быть загружен, так как было уÑтановлено, что он может Ñодержать неприемлемое Ñодержимое."
cannotUploadBecauseNoFreeSpace: "Файл не может быть загружен, так как не оÑталоÑÑŒ меÑта на диÑке"
+cannotUploadBecauseExceedsFileSizeLimit: "Файл не может быть загружен, так как он превышает лимит размера файла."
beta: "Бета"
enableAutoSensitive: "ÐвтоматичеÑкое определение NSFW"
enableAutoSensitiveDescription: "ЕÑли доÑтупно, иÑпользуйте машинное обучение Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑкой уÑтановки флага NSFW на ноÑителе. Даже еÑли Ñта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½Ð°, она может быть уÑтановлена ​​автоматичеÑки в завиÑимоÑти от инÑтанта."
@@ -909,6 +928,7 @@ pushNotificationNotSupported: "Push-ÑƒÐ²ÐµÐ´Ð¼Ð»ÐµÐ½Ð¸Ñ Ð½Ðµ поддержив
sendPushNotificationReadMessage: "УдалÑть push-ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ ÐºÐ¾Ð³Ð´Ð° Ñообщение или прочитано"
sendPushNotificationReadMessageCaption: "Ðа мгновение поÑвитÑÑ ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ðµ \"{emptyPushNotificationMessage}\". РаÑход зарÑда батареи может увеличитьÑÑ "
windowMaximize: "Развернуть"
+windowMinimize: "Свернуть"
windowRestore: "ВоÑÑтановить"
caption: "ПодпиÑÑŒ (Automatic Translation)"
loggedInAsBot: "Вы под аккаунтом бота!"
@@ -925,6 +945,7 @@ didYouLikeMisskey: "Вам нравитÑÑ Misskey?"
pleaseDonate: "Сайт {host} работает на Misskey. Это беÑплатное программное обеÑпечение, и ваши Ð¿Ð¾Ð¶ÐµÑ€Ñ‚Ð²Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¾Ñ‡ÐµÐ½ÑŒ бы помогли продолжать его разработку!"
roles: "Роли"
role: "Роль"
+noRole: "Ðет роли"
normalUser: "Обычный пользователь"
undefined: "неопределён"
assign: "Ðазначить"
@@ -934,6 +955,10 @@ manageCustomEmojis: "УправлÑть пользовательÑкими ÑмÐ
youCannotCreateAnymore: "Ð’Ñ‹ доÑтигли лимита ÑозданиÑ."
cannotPerformTemporary: "Временно недоÑтупен"
cannotPerformTemporaryDescription: "Это дейÑтвие временно невозможно выполнить из-за Ð¿Ñ€ÐµÐ²Ñ‹ÑˆÐµÐ½Ð¸Ñ Ð»Ð¸Ð¼Ð¸Ñ‚Ð° выполнениÑ."
+invalidParamError: "Ошибка параметра"
+invalidParamErrorDescription: "Проблема Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð°Ð¼Ð¸ запроÑа. Обычно Ñто ошибка, но Ñто также может быть ÑвÑзано Ñ Ñ‚ÐµÐ¼, что вы набрали Ñлишком много Ñимволов."
+permissionDeniedError: "ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð·Ð°Ð¿Ñ€ÐµÑ‰ÐµÐ½Ð°"
+permissionDeniedErrorDescription: "У Ñтой учетной запиÑи нет Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ Ð½Ð° выполнение Ñтой операции."
preset: "Шаблоны"
selectFromPresets: "Выбрать из шаблонов"
achievements: "ДоÑтижениÑ"
@@ -950,6 +975,37 @@ copyErrorInfo: "Скопировать код ошибки"
joinThisServer: "ПриÑоединÑйтеÑÑŒ к Ñтому Ñерверу"
exploreOtherServers: "ИÑкать другие Ñервера"
letsLookAtTimeline: "Давайте поÑмотрим на ленту"
+disableFederationConfirm: "Отключить федерацию?"
+disableFederationConfirmWarn: "Ð”ÐµÑ„ÐµÐ´ÐµÑ€Ð°Ñ†Ð¸Ñ Ð½Ðµ делает заметку приватной. Ð’ большинÑтве Ñлучаев без федерации не обойтиÑÑŒ."
+disableFederationOk: "Ðе федерируетÑÑ"
+invitationRequiredToRegister: "Этот Ñервер в наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ по приглашению. ЗарегиÑтрироватьÑÑ Ð¼Ð¾Ð³ÑƒÑ‚ только те, у кого еÑть код приглашениÑ."
+emailNotSupported: "ДоÑтавка почты не поддерживаетÑÑ Ð½Ð° Ñтом Ñервере"
+postToTheChannel: "Отправить в канал"
+cannotBeChangedLater: "Это Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ð·Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ позже"
+reactionAcceptance: "ПринÑтие реакций"
+likeOnly: "Только лайки"
+likeOnlyForRemote: "Только лайки Ñ ÑƒÐ´Ð°Ð»Ñ‘Ð½Ð½Ñ‹Ñ… Ñерверов"
+rolesAssignedToMe: "Мои роли"
+resetPasswordConfirm: "СброÑить пароль?"
+sensitiveWords: "ЧувÑтвительные Ñлова"
+sensitiveWordsDescription: "УÑтановите общедоÑтупный диапазон заметки, Ñодержащей заданное Ñлово, на домашний. Можно Ñделать неÑколько наÑтроек, разделив их переноÑами Ñтрок."
+notesSearchNotAvailable: "ПоиÑк заметок недоÑтупен"
+license: "ЛицензиÑ"
+unfavoriteConfirm: "Удалить избранное?"
+myClips: "Мои клипы"
+drivecleaner: "ОчиÑтитель диÑков"
+retryAllQueuesNow: "Повторить вÑе очереди ÑейчаÑ"
+retryAllQueuesConfirmTitle: "Хотите попробовать ещё раз?"
+retryAllQueuesConfirmText: "Ðагрузка на Ñервер может увеличитьÑÑ"
+enableChartsForRemoteUser: "Создание диаграмм Ð´Ð»Ñ ÑƒÐ´Ð°Ð»Ñ‘Ð½Ð½Ñ‹Ñ… пользователей"
+enableChartsForFederatedInstances: "Создание диаграмм Ð´Ð»Ñ ÑƒÐ´Ð°Ð»Ñ‘Ð½Ð½Ñ‹Ñ… Ñерверов"
+largeNoteReactions: "Показывать большие реакции на заметки"
+noteIdOrUrl: "ID или ÑÑылка на заметку"
+video: "Видео"
+videos: "Видео"
+dataSaver: "Ð­ÐºÐ¾Ð½Ð¾Ð¼Ð¸Ñ Ñ‚Ñ€Ð°Ñ„Ð¸ÐºÐ°"
+horizontal: "Сбоку"
+youFollowing: "ПодпиÑки"
_achievements:
earnedAt: "Разблокировано в"
_types:
@@ -1469,32 +1525,6 @@ _time:
minute: "мин"
hour: "ч"
day: "Ñут"
-_tutorial:
- title: "Как пользоватьÑÑ Misskey"
- step1_1: "Добро пожаловать!"
- step1_2: "Эта Ñтраница называетÑÑ Â«Ð»ÐµÐ½Ñ‚Ð°Â». ЗдеÑÑŒ будут поÑвлÑтьÑÑ Â«Ð·Ð°Ð¼ÐµÑ‚ÐºÐ¸Â»: ваши личные и тех, на кого вы «подпиÑаны». Они будут раÑполагатьÑÑ Ð² порÑдке времени их поÑвлениÑ."
- step1_3: "Правда, ваша лента пока пуÑта. Она начнёт заполнÑтьÑÑ, когда вы будете пиÑать Ñвои заметки и подпиÑыватьÑÑ Ð½Ð° других."
- step2_1: "Давайте, заполним профиль, прежде чем начать пиÑать заметки и подпиÑыватьÑÑ Ð½Ð° других."
- step2_2: "То, что вы раÑÑкажете в профиле, поможет лучше Ð²Ð°Ñ ÑƒÐ·Ð½Ð°Ñ‚ÑŒ, а значит, многим будет легче приÑоединитьÑÑ â€” вы Ñкорее получите новых подпиÑчиков и читателей."
- step3_1: "УÑпешно заполнили профиль?"
- step3_2: "Что ж, теперь Ñамое Ð²Ñ€ÐµÐ¼Ñ Ð¾Ð¿ÑƒÐ±Ð»Ð¸ÐºÐ¾Ð²Ð°Ñ‚ÑŒ заметку. ЕÑли нажать вверху Ñтраницы на изображение карандаша, поÑвитÑÑ Ñ„Ð¾Ñ€Ð¼Ð° Ð´Ð»Ñ Ñ‚ÐµÐºÑта."
- step3_3: "Ðапишите в неё, что хотите, и нажмите на кнопку в правом верхнем углу."
- step3_4: "Ðичего не приходит в голову? Как наÑчёт: «Я новенький, пока оÑваиваюÑÑŒ в Misskey»?"
- step4_1: "С напиÑанием первой заметки покончено?"
- step4_2: "Отлично, теперь она должна поÑвитьÑÑ Ð² вашей ленте."
- step5_1: "Ртеперь Ñамое Ð²Ñ€ÐµÐ¼Ñ Ð½ÐµÐ¼Ð½Ð¾Ð³Ð¾ оживить ленту, подпиÑавшиÑÑŒ на других."
- step5_2: "Ðа Ñтранице «{featured}» Ñобраны популÑрные ÑÐµÐ³Ð¾Ð´Ð½Ñ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ¸, Ñ‡Ð¸Ñ‚Ð°Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ðµ, вы можете найти кого-то вам интереÑного, а на Ñтранице «{explore}» можно поÑмотреть, кто популÑрен у оÑтальных."
- step5_3: "Чтобы подпиÑатьÑÑ Ð½Ð° кого-нибудь, щёлкните по его аватару и в открывшемÑÑ Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ðµ нажмите кнопку «ПодпиÑатьÑÑ»."
- step5_4: "Ðекоторые пользователи (около их имени «виÑит замок») вручную подтверждают чужие подпиÑки. Так что иногда подпиÑка начинает работать не Ñразу.\n"
- step6_1: "ЕÑли теперь в ленте видны и чужие заметки, значит у Ð²Ð°Ñ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ð»Ð¾ÑÑŒ."
- step6_2: "ЗдеÑÑŒ можно непринуждённо выразить Ñвои чувÑтва к чьей-то заметке, отметив «реакцию» под ней."
- step6_3: "Отмечайте реакции, Ð½Ð°Ð¶Ð¼Ð°Ñ Ð½Ð° Ñимвол «+» под заметкой и Ð²Ñ‹Ð±Ð¸Ñ€Ð°Ñ Ð·Ð½Ð°Ñ‡Ð¾Ðº по душе."
- step7_1: "Ðа Ñтом вводный урок по иÑпользованию Misskey закончен. СпаÑибо, что прошли его до конца!"
- step7_2: "Хотите изучить Misskey глубже — добро пожаловать в раздел «{help}»."
- step7_3: "ПриÑтно вам провеÑти Ð²Ñ€ÐµÐ¼Ñ Ñ Misskey🚀"
- step8_1: "ÐÑ…, да, не хотите ли включить push-уведомлениÑ?"
- step8_2: "С push-уведомлениÑми вы будете в курÑе репоÑтов, ответов, реакций и вÑего такого, даже когда закрыли Misskey."
- step8_3: "Эту наÑтройку вы вÑегда Ñможете поменÑть"
_2fa:
alreadyRegistered: "Ð”Ð²ÑƒÑ…Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð°Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ ÑƒÐ¶Ðµ наÑтроена."
registerTOTP: "Ðачните наÑтраивать приложение-аутентификатор"
diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml
index 7c74060158..3e96119389 100644
--- a/locales/sk-SK.yml
+++ b/locales/sk-SK.yml
@@ -261,12 +261,12 @@ nUsersRead: "preÄítané {n} používateľmi"
agreeTo: "Súhlasím s {0}"
agreeBelow: "Súhlasím s nasledovným"
basicNotesBeforeCreateAccount: "Základné bezpeÄnostné opatrenia"
-tos: "Podmienky používania"
start: "ZaÄaÅ¥"
home: "Domov"
remoteUserCaution: "Tieto informácie nemusia byÅ¥ aktuálne, keÄže používateľ je na vzdialenom serveri."
activity: "Aktivita"
images: "Obrázky"
+image: "Obrázky"
birthday: "Dátum narodenia"
yearsOld: "{age} rokov"
registeredDate: "Dátum registrácie"
@@ -917,6 +917,8 @@ remindMeLater: "Pripomenúť neskôr"
didYouLikeMisskey: "PáÄi sa vám Misskey?"
pleaseDonate: "Misskey je bezplatný softvér, ktorý používa {host}. Prosím, prispejte, aby sme ho mohli Äalej rozvíjaÅ¥!"
color: "Farba"
+horizontal: "Strana"
+youFollowing: "Sledované"
_role:
priority: "Priorita"
_priority:
@@ -1146,32 +1148,6 @@ _time:
minute: "min"
hour: "hod"
day: "dní"
-_tutorial:
- title: "Ako používať Misskey"
- step1_1: "Vitajte!"
- step1_2: "Táto stránka sa volá \"Äasová os\". Zobrazuje chronologicky zoradené \"poznámky\" od ľudí, ktorých sledujete."
- step1_3: "VaÅ¡a Äasová os je teraz prázdna pretože ste nepridali žiadne poznámky ani nikoho zatiaľ nesledujete."
- step2_1: "Podˇme dokonÄiÅ¥ nastavenia vášho profilu pred napísaním poznámky alebo sledovaním niekoho."
- step2_2: "Poskytnutím informácií o vás uľahÄíte ostatným, Äi chcú vidieÅ¥ alebo sledovaÅ¥ vaÅ¡e poznámky."
- step3_1: "DokonÄili ste nastavovanie svojho profilu?"
- step3_2: "PoÄme vyskúšaÅ¥ napísaÅ¥ poznámku. Môžete to spraviÅ¥ stlaÄením ikony ceruzky na vrchu obrazovky."
- step3_3: "Vyplňte polia a stlaÄte tlaÄítko vpravo hore."
- step3_4: "Nemáte Äo povedaÅ¥? Skúste \"len si nastavujem môj msky\"!"
- step4_1: "Napísali ste svoju prvú poznámku?"
- step4_2: "Hurá! Teraz by vaÅ¡a prvá poznámka mala byÅ¥ na vaÅ¡ej Äasovej osi."
- step5_1: "Teraz skúsme oživiÅ¥ Äasovú os sledovaním nejakých ľudí."
- step5_2: "{featured} zobrazí populárne poznámku na tomto serveri. {explore} môžete objavovať populárnych používateľov. Skúste tam nájsť ľudí, ktorých by ste radi sledovali!"
- step5_3: "Ak chcete sledovaÅ¥ Äalších používateľov, kliknite na ich ikonu a stlaÄte tlaÄidlo \"SledovaÅ¥\" na ich profile."
- step5_4: "Ak má niektorý používateľ ikonu zámku vedľa svojho mena, znamená to, že môže trvaÅ¥ urÄitý Äas, kým daný používateľ schváli vaÅ¡u žiadosÅ¥ o sledovanie."
- step6_1: "Teraz by ste mali vidieÅ¥ poznámky Äalších používateľov na svojej Äasovej osi."
- step6_2: "Môžete daÅ¥ \"reakcie\" na poznámky Äalších ľudí ako rýchlu odpoveÄ."
- step6_3: "Reakciu pridáte kliknutím na \"+\" niekoho poznámke a vybratím emoji, ktorou chcete reagovať."
- step7_1: "Gralujeme! DokonÄili ste základného sprievodcu Misskey."
- step7_2: "Ak sa chcete nauÄiÅ¥ viac o Misskey, skúste sekciu {help}."
- step7_3: "A teraz, veľa šťastia, bavte sa s Misskey! 🚀"
- step8_1: "A nakoniec, preÄo si neaktivovaÅ¥ push oznámenia?"
- step8_2: "VÄaka push notifikáciám sa dozviete o reakciách, sledovaniach a zmienkach, aj keÄ Misskey nie je otvorené."
- step8_3: "Nastavenia notifikácií môžete neskôr zmeniť."
_2fa:
alreadyRegistered: "Už ste zaregistrovali 2-faktorové autentifikaÄné zariadenie."
step1: "Najprv si nainÅ¡talujte autentifikaÄnú aplikáciu (napríklad {a} alebo {b}) na svoje zariadenie."
diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml
index 6369adb454..7a00e3fcea 100644
--- a/locales/sv-SE.yml
+++ b/locales/sv-SE.yml
@@ -256,11 +256,11 @@ noMoreHistory: "Det finns ingen mer historik"
startMessaging: "Starta en chatt"
nUsersRead: "läst av {n}"
agreeTo: "Jag accepterar {0}"
-tos: "Användarvillkor"
home: "Hem"
remoteUserCaution: "Då denna användaren kommer från en fjärrinstans, kan informationen visad vara ofullständig."
activity: "Aktivitet"
images: "Bilder"
+image: "Bilder"
birthday: "Födelsedag"
yearsOld: "{age} år gammal"
registeredDate: "Gick med"
diff --git a/locales/th-TH.yml b/locales/th-TH.yml
index a863103e2c..d1f87dbfdb 100644
--- a/locales/th-TH.yml
+++ b/locales/th-TH.yml
@@ -18,8 +18,9 @@ enterUsername: "ใส่ชื่อผู้ใช้"
renotedBy: "รีโน้ตโดย {ผู้ใช้}"
noNotes: "ไม่มีโน้ต"
noNotifications: "ไม่มีà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือน"
-instance: "ตัวอย่าง"
+instance: "อินสà¹à¸•นซ์"
settings: "à¸à¸²à¸£à¸•ั้งค่า"
+notificationSettings: "ตั้งค่าà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือน"
basicSettings: "à¸à¸²à¸£à¸•ั้งค่าพื้นà¸à¸²à¸™"
otherSettings: "à¸à¸²à¸£à¸•ั้งค่าอื่นๆ"
openInWindow: "เปิดในหน้าต่าง"
@@ -99,7 +100,7 @@ followRequestPending: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸­à¸”ำเนินà¸à¸²à¸£à¸£à¹‰à¸­
enterEmoji: "ใส่อีโมจิ"
renote: "รีโน้ต"
unrenote: "เลิà¸à¸£à¸µà¹‚น้ต"
-renoted: "รีโน้ตเอาไว้"
+renoted: "รีโน้ตà¹à¸¥à¹‰à¸§à¸™à¸°"
cantRenote: "โพสต์นี้ไม่สามารถรีโน้ตไว้ใหม่ได้นะ"
cantReRenote: "ไม่สามารถรีโน้ตเอาไว้ใหม่ได้นะ"
quote: "อ้างคำพูด"
@@ -262,14 +263,16 @@ noMoreHistory: "ในนั้นไม่มีประวัติอีà¸à
startMessaging: "เริ่มà¸à¸²à¸£à¸ªà¸™à¸—นา"
nUsersRead: "อ่านโดย {n}"
agreeTo: "ฉันยอมรับที่จะ {0}"
+agree: "ยอมรับ"
agreeBelow: "ฉันยอมรับถึงด้านล่าง"
basicNotesBeforeCreateAccount: "หมายเหตุสำคัà¸"
-tos: "ข้อà¸à¸³à¸«à¸™à¸”à¹à¸¥à¸°à¹€à¸‡à¸·à¹ˆà¸­à¸™à¹„ข"
+termsOfService: "เงื่อนไขà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£"
start: "เริ่มต้น​ใช้งาน​"
home: "หน้าà¹à¸£à¸"
remoteUserCaution: "เนื่องจาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸£à¸²à¸¢à¸™à¸µà¹‰à¸™à¸±à¹‰à¸™ มาจาà¸à¸­à¸´à¸™à¸ªà¹à¸•นซ์ระยะไà¸à¸¥ ข้อมูลที่à¹à¸ªà¸”งดังà¸à¸¥à¹ˆà¸²à¸§à¸™à¸±à¹‰à¸™à¸­à¸²à¸ˆà¸ˆà¸°à¹„ม่สมบูรณ์à¸à¹‡à¹„ด้นะ"
activity: "à¸à¸´à¸ˆà¸à¸£à¸£à¸¡"
images: "รูปภาพ"
+image: "รูปภาพ"
birthday: "วันเà¸à¸´à¸”"
yearsOld: "{age} ปี"
registeredDate: "วันที่สมัครสมาชิà¸"
@@ -467,12 +470,14 @@ enableAdvancedMfm: "เปิดใช้งาน MFM ขั้นสูง"
enableAnimatedMfm: "เปิดà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ MFM ด้วยà¹à¸­à¸™à¸´à¹€à¸¡à¸Šà¸±à¹ˆà¸™"
doing: "à¸à¸³à¸¥à¸±à¸‡à¸›à¸£à¸°à¸¡à¸§à¸¥à¸œà¸¥......"
category: "หมวดหมู่"
-tags: "à¹à¸—็à¸"
+tags: "นามà¹à¸à¸‡"
docSource: "ที่มาของเอà¸à¸ªà¸²à¸£à¸™à¸µà¹‰"
createAccount: "สร้างบัà¸à¸Šà¸µ"
existingAccount: "บัà¸à¸Šà¸µà¸—ี่มีอยู่"
regenerate: "สร้างอีà¸à¸„รั้ง"
fontSize: "ขนาดตัวอัà¸à¸©à¸£"
+mediaListWithOneImageAppearance: "ความสูงของลิสต์สื่อจะต้องมีรูปภาพเดียวเท่านั้น"
+limitTo: "จำà¸à¸±à¸”ไว้ที่ {x}"
noFollowRequests: "คุณไม่มีคำขอติดตามที่รอดำเนินà¸à¸²à¸£"
openImageInNewTab: "เปิดรูปภาพในà¹à¸—็บใหม่"
dashboard: "หน้าà¸à¸£à¸°à¸”านหลัà¸"
@@ -671,7 +676,7 @@ notesCount: "จำนวนของโน้ต"
repliesCount: "จำนวนà¸à¸²à¸£à¸•อบà¸à¸¥à¸±à¸šà¸—ี่ส่ง"
renotesCount: "จำนวนรีโน้ตที่ส่ง"
repliedCount: "จำนวนของà¸à¸²à¸£à¸•อบà¸à¸¥à¸±à¸šà¸—ี่ได้รับ"
-renotedCount: "จำนวนรีโน้ตที่ได้รับ"
+renotedCount: "จำนวนรีโน้ตที่ได้รับà¹à¸¥à¹‰à¸§"
followingCount: "จำนวนบัà¸à¸Šà¸µà¸—ี่ติดตาม"
followersCount: "จำนวนผู้ติดตาม"
sentReactionsCount: "จำนวนปà¸à¸´à¸à¸´à¸£à¸´à¸¢à¸²à¸—ี่ส่ง"
@@ -698,6 +703,8 @@ contact: "ติดต่อ"
useSystemFont: "ใช้ฟอนต์เริ่มต้นของระบบ"
clips: "คลิป"
experimentalFeatures: "ฟังà¸à¹Œà¸Šà¸±à¹ˆà¸™à¸—ดสอบ"
+experimental: "ทดลอง"
+thisIsExperimentalFeature: "นี่คือฟีเจอร์ทดลองนะค่ะ ฟังà¸à¹Œà¸Šà¸±à¸™à¸à¸²à¸£à¸—ำงานบางอย่างอาจเปลี่ยนà¹à¸›à¸¥à¸‡à¹„ด้ à¹à¸¥à¸°à¸­à¸²à¸ˆà¹„ม่ทำงานหรือไม่เสถียรตามที่ตั้งใจไว้นะ"
developer: "สำหรับนัà¸à¸žà¸±à¸’นา"
makeExplorable: "ทำให้บัà¸à¸Šà¸µà¸¡à¸­à¸‡à¹€à¸«à¹‡à¸™à¹ƒà¸™ \"สำรวจ\""
makeExplorableDescription: "ถ้าหาà¸à¸„ุณปิดà¸à¸²à¸£à¸—ำงานนี้ บัà¸à¸Šà¸µà¸‚องคุณนั้นจะไม่à¹à¸ªà¸”งในส่วน \"สำรวจ\" นะ"
@@ -904,6 +911,7 @@ remoteOnly: "รีโมทเท่านั้น"
failedToUpload: "à¸à¸²à¸£à¸­à¸±à¸›à¹‚หลดล้มเหลว"
cannotUploadBecauseInappropriate: "ไม่สามารถอัปโหลดไฟล์นี้ได้เนื่องจาà¸à¸£à¸°à¸šà¸šà¸•รวจพบบางส่วนของไฟล์ว่านี้อาจจะเป็น NSFW"
cannotUploadBecauseNoFreeSpace: "à¸à¸²à¸£à¸­à¸±à¸›à¹‚หลดนั้นล้มเหลวเนื่องจาà¸à¹„ม่มีความจุของไดรฟ์"
+cannotUploadBecauseExceedsFileSizeLimit: "ไม่สามารถอัปโหลดไฟล์นี้ได้à¹à¸¥à¹‰à¸§à¹€à¸™à¸·à¹ˆà¸­à¸‡à¸ˆà¸²à¸à¹€à¸à¸´à¸™à¸‚ีดจำà¸à¸±à¸”ของขนาดไฟล์à¹à¸¥à¹‰à¸§"
beta: "เบต้า"
enableAutoSensitive: "ทำเครื่องหมาย NSFW อัตโนมัติ"
enableAutoSensitiveDescription: "อนุà¸à¸²à¸•ให้ตรวจหาà¹à¸¥à¸°à¸—ำเครื่องหมายสื่อ NSFW โดยอัตโนมัติผ่านà¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸™à¸£à¸¹à¹‰à¸‚องเครื่องหาà¸à¹€à¸›à¹‡à¸™à¹„ปได้ à¹à¸¡à¹‰à¸§à¹ˆà¸²à¸•ัวเลือà¸à¸™à¸µà¹‰à¸ˆà¸°à¸–ูà¸à¸›à¸´à¸”ใช้งาน à¹à¸•่à¸à¹‡à¸ªà¸²à¸¡à¸²à¸£à¸–เปิดใช้งานได้ทั้งอินสà¹à¸•นซ์นี้"
@@ -937,6 +945,7 @@ didYouLikeMisskey: "คุณเคยชอบ Misskey ไหม?"
pleaseDonate: "{host} ใช้ซอฟต์à¹à¸§à¸£à¹Œà¸Ÿà¸£à¸µ Misskey เราขอขอบคุณà¸à¸²à¸£à¸šà¸£à¸´à¸ˆà¸²à¸„ของคุณอย่างสูงเพื่อให้à¸à¸²à¸£à¸žà¸±à¸’นา Misskey สามารถดำเนินต่อไปได้นะ!"
roles: "บทบาท"
role: "บทบาท"
+noRole: "ไม่พบบทบาท"
normalUser: "ผู้ใช้มาตรà¸à¸²à¸™"
undefined: "ไม่ได้à¸à¸³à¸«à¸™à¸”"
assign: "à¸à¸³à¸«à¸™à¸”"
@@ -946,6 +955,10 @@ manageCustomEmojis: "จัดà¸à¸²à¸£à¸­à¸µà¹‚มจิà¹à¸šà¸šà¸à¸³à¸«à¸™à¸
youCannotCreateAnymore: "คุณถึงขีดจà¹à¸²à¸à¸±à¸”à¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¹à¸¥à¹‰à¸§à¸™à¸°"
cannotPerformTemporary: "ไม่สามารถใช้à¸à¸²à¸£à¹„ด้ชั่วคราว"
cannotPerformTemporaryDescription: "à¸à¸²à¸£à¸”à¹à¸²à¹€à¸™à¸´à¸™à¸à¸²à¸£à¸™à¸µà¹‰à¹„ม่สามารถดà¹à¸²à¹€à¸™à¸´à¸™à¸à¸²à¸£à¹„ด้ชั่วคราว เนื่องจาà¸à¹€à¸à¸´à¸™à¸‚ีดจà¹à¸²à¸à¸±à¸”à¸à¸²à¸£à¸”à¹à¸²à¹€à¸™à¸´à¸™à¸à¸²à¸£ à¸à¸£à¸¸à¸“ารอสัà¸à¸„รู่à¹à¸¥à¹‰à¸§à¸¥à¸­à¸‡à¹ƒà¸«à¸¡à¹ˆà¸­à¸µà¸à¸„รั้งนะค่ะ"
+invalidParamError: "ข้อผิดพลาดพารามิเตอร์"
+invalidParamErrorDescription: "คำขอพารามิเตอร์ไม่ถูà¸à¸•้อง สิ่งนี้มัà¸à¸ˆà¸°à¹€à¸à¸´à¸”จาà¸à¸‚้อผิดพลาด à¹à¸•่อาจเà¸à¸´à¸”จาà¸à¸­à¸´à¸™à¸žà¸¸à¸•เà¸à¸´à¸™à¸‚ีดจำà¸à¸±à¸”ของขนาดหรือที่คล้ายà¸à¸±à¸™"
+permissionDeniedError: "à¸à¸²à¸£à¸”ำเนินถูà¸à¸›à¸à¸´à¹€à¸ªà¸˜"
+permissionDeniedErrorDescription: "บัà¸à¸Šà¸µà¸™à¸µà¹‰à¹„ม่มีสิทธิ์อนุà¸à¸²à¸•ในà¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰"
preset: "พรีเซ็ต"
selectFromPresets: "เลือà¸à¸ˆà¸²à¸à¸à¸²à¸£à¸žà¸£à¸µà¹€à¸‹à¹‡à¸•"
achievements: "ความสำเร็จ"
@@ -989,17 +1002,52 @@ enableChartsForFederatedInstances: "สร้างà¹à¸œà¸™à¸ à¸¹à¸¡à¸´à¸‚้à¸
showClipButtonInNoteFooter: "เพิ่ม \"คลิป\" เพื่อบันทึà¸à¹€à¸¡à¸™à¸¹à¸à¸²à¸£à¸—ำงาน"
largeNoteReactions: "ขยายรีà¹à¸­à¸„ชั่นà¸à¸²à¸£à¹à¸ªà¸”งผล"
noteIdOrUrl: "โน้ต ID หรือ URL"
+video: "วีดีโอ"
+videos: "วีดีโอ"
+dataSaver: "ประหยัดข้อมูล"
accountMigration: "à¸à¸²à¸£à¹‚ยà¸à¸¢à¹‰à¸²à¸¢à¸šà¸±à¸à¸Šà¸µ"
accountMoved: "ผู้ใช้รายนี้ได้ย้ายไปยังบัà¸à¸Šà¸µà¹ƒà¸«à¸¡à¹ˆà¹à¸¥à¹‰à¸§:"
+accountMovedShort: "บัà¸à¸Šà¸µà¸™à¸µà¹‰à¸–ูà¸à¹‚อนย้ายไปà¹à¸¥à¹‰à¸§à¸„่ะ"
+operationForbidden: "ห้ามดำเนินà¸à¸²à¸£"
forceShowAds: "à¹à¸ªà¸”งโฆษณาเสมอ"
+addMemo: "เพิ่มมีโม"
+editMemo: "à¹à¸à¹‰à¹„ขมีโม"
+reactionsList: "ปà¸à¸´à¸à¸´à¸£à¸´à¸¢à¸²"
+renotesList: "Renotes รีโน้ต"
+notificationDisplay: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือน"
+leftTop: "บนซ้าย"
+rightTop: "บนขวา"
+leftBottom: "ล่างซ้าย"
+rightBottom: "ล่างขวา"
+stackAxis: "ทิศทางà¸à¸²à¸£à¸‹à¹‰à¸­à¸™"
+vertical: "à¹à¸™à¸§à¸•ั้ง"
+horizontal: "ด้านข้าง"
+position: "ตำà¹à¸«à¸™à¹ˆà¸‡"
+serverRules: "à¸à¸Žà¸‚องเซิฟเวอร์"
+pleaseConfirmBelowBeforeSignup: "โปรดยืนยันด้านล่างà¸à¹ˆà¸­à¸™à¸à¸³à¸¥à¸±à¸‡à¸¥à¸‡à¸Šà¸·à¹ˆà¸­à¸ªà¸¡à¸±à¸„รนะค่ะ"
+pleaseAgreeAllToContinue: "คุณต้องยอมรับทุà¸à¸Šà¹ˆà¸­à¸‡à¸•รงด้านบนเพื่อดำเนินà¸à¸²à¸£à¸•่อค่ะ"
+continue: "ดำเนินà¸à¸²à¸£à¸•่อ"
+preservedUsernames: "ชื่อผู้ใช้ที่สงวนไว้"
+preservedUsernamesDescription: "ลิสต์ชื่อผู้ใช้ที่จะสำรองโดยคั่นด้วยà¸à¸²à¸£à¹à¸šà¹ˆà¸‡à¸šà¸£à¸£à¸—ัดนั้น เพราะสิ่งเหล่านี้จะไม่สามารถทำได้ในระหว่างà¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¸šà¸±à¸à¸Šà¸µà¸•ามปà¸à¸•ิ บัà¸à¸Šà¸µà¸—ี่มีอยู่à¹à¸¥à¹‰à¸§à¸™à¸±à¹‰à¸™à¹‚ดยใช้ชื่อผู้ใช้เหล่านี้จะไม่ได้รับผลà¸à¸£à¸°à¸—บอะไร"
+createNoteFromTheFile: "เรียบเรียงโน้ตจาà¸à¹„ฟล์นี้"
+youFollowing: "ติดตามà¹à¸¥à¹‰à¸§"
+_serverRules:
+ description: "ชุดของà¸à¸Žà¸—ี่จะà¹à¸ªà¸”งà¸à¹ˆà¸­à¸™à¸à¸²à¸£à¸¥à¸‡à¸—ะเบียนเราขอà¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¸•ั้งค่าสรุปข้อà¸à¸³à¸«à¸™à¸”ในà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£"
_accountMigration:
- moveTo: "ย้ายข้อมูลบัà¸à¸Šà¸µà¸™à¸µà¹‰à¹„ปยังบัà¸à¸Šà¸µà¸­à¸µà¸à¸«à¸™à¸¶à¹ˆà¸‡"
- moveToLabel: "บัà¸à¸Šà¸µà¸—ี่จะย้ายไปที่:"
- moveAccountDescription: "à¸à¸²à¸£à¸à¸£à¸°à¸—ำนี้ไม่สามารถย้อนà¸à¸¥à¸±à¸šà¹„ด้นะ ขั้นตอนà¹à¸£à¸ ต้องสร้างนามà¹à¸à¸‡à¸ªà¸³à¸«à¸£à¸±à¸šà¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹ƒà¸™à¸šà¸±à¸à¸Šà¸µà¸—ี่คุณต้องà¸à¸²à¸£à¸¢à¹‰à¸²à¸¢à¹„ป หลังจาà¸à¸™à¸±à¹‰à¸™à¹à¸¥à¹‰à¸§ ป้อนบัà¸à¸Šà¸µà¸—ี่จะย้ายไปในรูปà¹à¸šà¸šà¸”ังต่อไปนี้: @person@instance.com"
moveFrom: "ย้ายข้อมูลบัà¸à¸Šà¸µà¸­à¸·à¹ˆà¸™à¹„ปยังอีà¸à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¸«à¸™à¸¶à¹ˆà¸‡"
+ moveFromSub: "สร้างนามà¹à¸à¸‡à¹„ปยังบัà¸à¸Šà¸µà¸­à¸·à¹ˆà¸™"
moveFromLabel: "บัà¸à¸Šà¸µà¸—ี่จะย้ายจาà¸:"
moveFromDescription: "สร้างนามà¹à¸à¸‡à¸ªà¸³à¸«à¸£à¸±à¸šà¸šà¸±à¸à¸Šà¸µà¸—ี่จะย้ายจาà¸à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰ ถ้าหาà¸à¸„ุณต้องà¸à¸²à¸£à¹‚อนผู้ติดตาม สิ่งนี้ต้องทำà¸à¹ˆà¸­à¸™à¹‚อนà¸à¹ˆà¸­à¸™à¸™à¸°à¸„่ะ! หลังจาà¸à¸™à¸±à¹‰à¸™ ป้อนบัà¸à¸Šà¸µà¸—ี่จะย้ายไปในรูปà¹à¸šà¸šà¸•่อไปนี้: @person@instance.com"
+ moveTo: "ย้ายข้อมูลบัà¸à¸Šà¸µà¸™à¸µà¹‰à¹„ปยังบัà¸à¸Šà¸µà¸­à¸µà¸à¸«à¸™à¸¶à¹ˆà¸‡"
+ moveToLabel: "บัà¸à¸Šà¸µà¸—ี่จะย้ายไปที่:"
+ moveCannotBeUndone: "ไม่สามารถยà¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¹‚อนย้ายบัà¸à¸Šà¸µà¹„ด้"
+ moveAccountDescription: "à¸à¸²à¸£à¸à¸£à¸°à¸—ำนี้ไม่สามารถย้อนà¸à¸¥à¸±à¸šà¹„ด้นะ ขั้นตอนà¹à¸£à¸ ต้องสร้างนามà¹à¸à¸‡à¸ªà¸³à¸«à¸£à¸±à¸šà¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹ƒà¸™à¸šà¸±à¸à¸Šà¸µà¸—ี่คุณต้องà¸à¸²à¸£à¸¢à¹‰à¸²à¸¢à¹„ป หลังจาà¸à¸™à¸±à¹‰à¸™à¹à¸¥à¹‰à¸§ ป้อนบัà¸à¸Šà¸µà¸—ี่จะย้ายไปในรูปà¹à¸šà¸šà¸”ังต่อไปนี้: @person@instance.com"
+ moveAccountHowTo: "หาà¸à¸•้องà¸à¸²à¸£à¸¢à¹‰à¸²à¸¢à¸‚้อมูลà¸à¹ˆà¸­à¸™à¸­à¸·à¹ˆà¸™à¹ƒà¸«à¹‰à¸ªà¸£à¹‰à¸²à¸‡à¸Šà¸·à¹ˆà¸­à¹à¸—นสำหรับบัà¸à¸Šà¸µà¸™à¸µà¹‰ ในบัà¸à¸Šà¸µà¸—ี่จะต้องà¸à¸²à¸£à¸¢à¹‰à¸²à¸¢à¹„ป\nหลังจาà¸à¸—ี่คุณสร้างนามà¹à¸à¸‡à¸™à¸±à¹‰à¸™à¹à¸¥à¹‰à¸§ ให้ป้อนบัà¸à¸Šà¸µà¸—ี่ต้องà¸à¸²à¸£à¸ˆà¸°à¸¢à¹‰à¸²à¸¢à¹„ปในรูปà¹à¸šà¸šà¸”ังต่อไปนี้: @username@server.example.com"
+ startMigration: "โอนย้าย"
migrationConfirm: "ย้ายข้อมูลบัà¸à¸Šà¸µà¸™à¸µà¹‰à¹„ปที่ {account} จริงๆนะ เมื่อมีà¸à¸²à¸£à¹€à¸£à¸´à¹ˆà¸¡à¸•้นà¹à¸¥à¹‰à¸§ à¸à¸£à¸°à¸šà¸§à¸™à¸à¸²à¸£à¸™à¸µà¹‰à¸ˆà¸°à¹„ม่สามารถหยุดหรือนำà¸à¸¥à¸±à¸šà¸„ืนมาได้ à¹à¸¥à¸°à¸„ุณจะไม่สามารถใช้บัà¸à¸Šà¸µà¸™à¸µà¹‰à¹ƒà¸™à¸ªà¸–านะดั้งเดิมได้อีà¸à¸•่อไป\n\nนอà¸à¸ˆà¸²à¸à¸™à¸µà¹‰ เพื่อให้à¹à¸™à¹ˆà¹ƒà¸ˆà¸¢à¸·à¸™à¸¢à¸±à¸™à¸§à¹ˆà¸²à¸„ุณได้สร้างนามà¹à¸à¸‡à¹ƒà¸™à¸šà¸±à¸à¸Šà¸µà¸—ี่จะย้ายข้อมูลนะค่ะ"
+ movedAndCannotBeUndone: "\nบัà¸à¸Šà¸µà¸™à¸µà¹‰à¸–ูà¸à¹‚อนย้ายไปà¹à¸¥à¹‰à¸§\nไม่สามารถย้อนà¸à¸¥à¸±à¸šà¹‚อนย้ายข้อมูลได้"
+ postMigrationNote: "บัà¸à¸Šà¸µà¸™à¸µà¹‰à¸ˆà¸°à¸–ูà¸à¹€à¸¥à¸´à¸à¸•ิดตามบัà¸à¸Šà¸µà¸—ั้งหมดที่à¸à¸³à¸¥à¸±à¸‡à¸•ิดตามภายใน 24 ชั่วโมงหลังจาà¸à¸à¸²à¸£à¸¢à¹‰à¸²à¸¢à¸‚้อมูลนั้นเสร็จสิ้น ทั้งจำนวนผู้ติดตามà¹à¸¥à¸°à¸œà¸¹à¹‰à¸•ิดตามนั้นจะà¸à¸¥à¸²à¸¢à¹€à¸›à¹‡à¸™à¸¨à¸¹à¸™à¸¢à¹Œ เพื่อหลีà¸à¹€à¸¥à¸µà¹ˆà¸¢à¸‡à¸›à¹‰à¸­à¸‡à¸à¸±à¸™à¹„ม่ให้ผู้ติดตามของคุณนั้นไม่สามารถเห็นโพสต์เฉพาะผู้ติดตามของบัà¸à¸Šà¸µà¸™à¸µà¹‰à¹„ด้ à¹à¸•่อย่างไรà¸à¹‡à¸•ามà¹à¸¥à¹‰à¸§à¸žà¸§à¸à¹€à¸‚าจะยังคงติดตามบัà¸à¸Šà¸µà¸™à¸µà¹‰à¸•่อไป"
+ movedTo: "บัà¸à¸Šà¸µà¸—ี่จะย้ายไปที่:"
_achievements:
earnedAt: "ได้รับเมื่อ"
_types:
@@ -1171,6 +1219,9 @@ _achievements:
_client30min:
title: "พัà¸à¸œà¹ˆà¸­à¸™à¸ªà¸±à¸à¸«à¸™à¹ˆà¸­à¸¢"
description: "ใช้เวลา 30 นาทีบน Misskey"
+ _client60min:
+ title: "ไม่มี \"Miss\" ใน Misskey นะค่ะ !"
+ description: "เปิด Misskey ค้างไว้à¹à¸¥à¹‰à¸§à¸­à¸¢à¹ˆà¸²à¸‡à¸™à¹‰à¸­à¸¢ 60 นาที"
_noteDeletedWithin1min:
title: "ไม่เป็นไร"
description: "ลบโน้ตภายในหนึ่งนาทีหลังจาà¸à¸—ี่โพสต์"
@@ -1259,6 +1310,8 @@ _role:
iconUrl: "ไอคอน URL"
asBadge: "à¹à¸ªà¸”งเป็นตรา"
descriptionOfAsBadge: "ไอคอนของบทบาทนี้จะปราà¸à¸à¸–ัดจาà¸à¸Šà¸·à¹ˆà¸­à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‚องผู้ใช้งานด้วยบทบาทนี้ถ้าหาà¸à¹€à¸›à¸´à¸”ใช้งาน"
+ isExplorable: "บทบาทไทม์ไลน์เป็นà¹à¸šà¸šà¸ªà¸²à¸˜à¸²à¸£à¸“ะ"
+ descriptionOfIsExplorable: "ไทม์ไลน์ของบทบาทนี้จะสามารถเข้าถึงได้à¹à¸šà¸šà¸ªà¸²à¸˜à¸²à¸£à¸“ะถ้าหาà¸à¹€à¸›à¸´à¸”ใช้งาน เส้นเวลาของบทบาทนั้นจะไม่ถูà¸à¹€à¸›à¸´à¸”เผยต่อสาธารณะ ถึงà¹à¸¡à¹‰à¸§à¹ˆà¸²à¸ˆà¸°à¹„ม่เปิดเผยต่อสาธารณะà¹à¸¡à¹‰à¹à¸•่ว่า...จะตั้งค่าไว้ยังไงà¸à¹‡à¸•าม"
displayOrder: "ตำà¹à¸«à¸™à¹ˆà¸‡"
descriptionOfDisplayOrder: "ยิ่งตัวเลขสูง ตำà¹à¸«à¸™à¹ˆà¸‡ UI à¸à¹‡à¸¢à¸´à¹ˆà¸‡à¸ªà¸¹à¸‡à¸‚ึ้นนะ"
canEditMembersByModerator: "อนุà¸à¸²à¸•ให้ผู้ดูà¹à¸¥à¹à¸à¹‰à¹„ขสมาชิà¸"
@@ -1407,6 +1460,8 @@ _channel:
following: "ติดตามà¹à¸¥à¹‰à¸§"
usersCount: "{n} ผู้เข้าร่วม"
notesCount: "{n} โน้ต"
+ nameAndDescription: "ชื่อà¹à¸¥à¸°à¸„ำอธิบาย"
+ nameOnly: "ชื่อเท่านั้น"
_menuDisplay:
sideFull: "ด้านข้าง"
sideIcon: "ด้านข้าง (ไอคอน)"
@@ -1524,32 +1579,6 @@ _time:
minute: "นาที"
hour: "ชั่วโมง"
day: "วัน"
-_tutorial:
- title: "วิธีà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ Misskey"
- step1_1: "ยินดีต้อนรับค่ะ!"
- step1_2: "หน้านี้เรียà¸à¸§à¹ˆà¸² \"ไทม์ไลน์\" มันจะà¹à¸ªà¸”ง \"โน้ตย่อ\" ที่เรียงลำดับตามลำดับเวลาของคนที่คุณ \"ติดตาม\""
- step1_3: "ไทม์ไลน์ของคุณนั้นว่างเปล่า เนื่องจาà¸à¸„ุณยังไม่ได้โพสต์โน้ตย่อหรือไม่ได้ติดตามใครเลย"
- step2_1: "มาตั้งค่าโปรไฟล์ของคุณให้เสร็จà¸à¹ˆà¸­à¸™à¹€à¸‚ียนโน้ตย่อหรือติดตามใครà¸à¹‡à¹„ด้"
- step2_2: "à¸à¸²à¸£à¹ƒà¸«à¹‰à¸‚้อมูลบางอย่างเà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸•ัวคุณนั้น จะทำให้ผู้อื่นทราบว่าต้องà¸à¸²à¸£à¸”ูโน้ตย่อของคุณหรือติดตามคุณได้ง่ายขึ้น"
- step3_1: "ตั้งค่าโปรไฟล์ของคุณเสร็จà¹à¸¥à¹‰à¸§?"
- step3_2: "จาà¸à¸™à¸±à¹‰à¸™à¸¥à¸­à¸‡à¹‚พสต์โน้ตà¸à¸±à¸™à¸•่อไป คุณสามารถทำได้โดยà¸à¸”ปุ่มที่มีไอคอนดินสอบนหน้าจอนะ"
- step3_3: "à¸à¸£à¸­à¸à¹‚มดอลà¹à¸¥à¹‰à¸§à¸à¸”ปุ่มด้านบนขวาเพื่อโพสต์"
- step3_4: "ไม่มีอะไรจะพูดงั้นหรอ ลอง \"เพียงà¹à¸„่ตั้งค่าว่า Misskey ของฉัน\"!"
- step4_1: "เสร็จสิ้นà¸à¸²à¸£à¹‚พสต์โน้ตย่อà¹à¸£à¸à¸‚องคุณà¹à¸¥à¹‰à¸§à¸­à¸¢à¹ˆà¸²à¸‡à¸‡à¸±à¹‰à¸™à¸«à¸£à¸­?"
- step4_2: "ไชโย! ตอนนี้โน้ตย่อà¹à¸£à¸à¸‚องคุณได้ปราà¸à¸à¸šà¸™à¹„ทม์ไลน์ของคุณà¹à¸¥à¹‰à¸§à¸™à¸°"
- step5_1: "ตอนนี้ มาลองทำไทม์ไลน์เพิ่มเติมของคุณให้ดูมีชีวิตชีวามาà¸à¸‚ึ้นโดยà¸à¸²à¸£à¸•ิดตามคนอื่น"
- step5_2: "{featured} จะà¹à¸ªà¸”งโน้ตยอดนิยมให้คุณเห็นในà¸à¸£à¸“ีนี้ {explore} จะช่วยให้คุณค้นหาผู้ใช้ยอดนิยมได้ ลองหาคนที่คุณต้องà¸à¸²à¸£à¸•ิดตามที่นั่นสิ!"
- step5_3: "หาà¸à¸•้องà¸à¸²à¸£à¸•ิดตามผู้ใช้รายอื่น ให้คลิà¸à¸—ี่ไอคอนà¹à¸¥à¸°à¸à¸”ปุ่ม \"ติดตาม\" บนโปรไฟล์ของพวà¸à¹€à¸‚าได้เลยจ้า"
- step5_4: "หาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸£à¸²à¸¢à¸­à¸·à¹ˆà¸™à¸¡à¸µà¹„อคอนà¹à¸¡à¹ˆà¸à¸¸à¸à¹à¸ˆà¸—ี่อยู่ข้างชื่อ อาจต้องใช้เวลาสัà¸à¸£à¸°à¸¢à¸°à¸à¸§à¹ˆà¸²à¸—ี่ผู้ใช้รายนั้นจะอนุมัติคำขอติดตามของคุณ"
- step6_1: "คุณสามารถเห็นโน้ตย่อของผู้ใช้รายอื่นบนไทม์ไลน์ของคุณได้à¹à¸¥à¹‰à¸§à¸•อนนี้"
- step6_2: "คุณยังสามารถใส่ \"ปà¸à¸´à¸à¸´à¸£à¸´à¸¢à¸²\" ลงในโน้ตของคนอื่นเพื่อตอบà¸à¸¥à¸±à¸šà¹„ด้อย่างรวดเร็ว"
- step6_3: "หาà¸à¸•้องà¸à¸²à¸£à¹à¸™à¸š \"ปà¸à¸´à¸à¸´à¸£à¸´à¸¢à¸²\" ให้à¸à¸”เครื่องหมาย \"+\" ในโน้ตของผู้ใช้รายอื่นà¹à¸¥à¹‰à¸§à¹€à¸¥à¸·à¸­à¸à¸­à¸µà¹‚มจิที่คุณต้องà¸à¸²à¸£à¹‚ต้ตอบด้วย"
- step7_1: "ยินดีด้วยนะ! คุณได้เสร็จสิ้นà¸à¸²à¸£à¸à¸§à¸”วิชาพื้นà¸à¸²à¸™à¸‚อง Misskey à¹à¸¥à¹‰à¸§"
- step7_2: "ถ้าหาà¸à¸„ุณต้องà¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸™à¸£à¸¹à¹‰à¹€à¸žà¸´à¹ˆà¸¡à¹€à¸•ิมเà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸š Misskey ให้ลองใช้ส่วน {help}"
- step7_3: "ตอนนี้ ถ้าอย่างนั้นà¸à¹‡à¸‚อให้โชคดีà¹à¸¥à¸°à¸ªà¸™à¸¸à¸à¸à¸±à¸š Misskey! 🚀"
- step8_1: "สุดท้ายนี้นายต้องà¸à¸²à¸£à¹€à¸›à¸´à¸”ใช้งานà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือนà¹à¸šà¸šà¸žà¸¸à¸Šà¸«à¸£à¸·à¸­à¸›à¹ˆà¸²à¸§?"
- step8_2: "à¸à¸²à¸£à¹€à¸›à¸´à¸”ใช้งานสิ่งเหล่านี้ จะช่วยให้คุณนั้นได้รับà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือนสำหรับà¸à¸²à¸£à¸à¸¥à¹ˆà¸²à¸§à¸–ึง à¸à¸²à¸£à¹à¸ªà¸”งรีà¹à¸­à¸„ชั่น à¸à¸²à¸£à¸•ิดตาม ฯลฯ เป็นต้น ถึงà¹à¸¡à¹‰à¸§à¹ˆà¸²à¸ˆà¸°à¹„ม่ได้เปิด Misskey à¸à¹‡à¸•าม"
- step8_3: "คุณสามารถเปลี่ยนà¸à¸²à¸£à¸•ั้งค่านี้ในภายหลังได้ตลอดเวลานะ"
_2fa:
alreadyRegistered: "คุณได้ลงทะเบียนอุปà¸à¸£à¸“์ยืนยันตัวตนà¹à¸šà¸š 2 ชั้นà¹à¸¥à¹‰à¸§"
registerTOTP: "ลงทะเบียนà¹à¸­à¸žà¸•ัวตรวจสอบสิทธิ์"
@@ -1887,6 +1916,7 @@ _deck:
channel: "à¹à¸Šà¸™à¹à¸™à¸¥"
mentions: "พูดถึง"
direct: "ไดเร็ค"
+ roleTimeline: "บทบาทไทม์ไลน์"
_dialog:
charactersExceeded: "คุณà¸à¸³à¸¥à¸±à¸‡à¸¡à¸µà¸•ัวอัà¸à¸‚ระเà¸à¸´à¸™à¸‚ีดจำà¸à¸±à¸”สูงสุดà¹à¸¥à¹‰à¸§à¸™à¸°! ปัจจุบันอยู่ที่ {current} จาภ{max}"
charactersBelow: "คุณà¸à¸³à¸¥à¸±à¸‡à¹ƒà¸Šà¹‰à¸­à¸±à¸à¸‚ระต่ำà¸à¸§à¹ˆà¸²à¸‚ีดจำà¸à¸±à¸”ขั้นต่ำเลยนะ! ปัจจุบันอยู่ที่ {current} จาภ{min}"
diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml
index 895d2e1f12..4f215bc980 100644
--- a/locales/uk-UA.yml
+++ b/locales/uk-UA.yml
@@ -258,12 +258,12 @@ startMessaging: "Розпочати діалог"
nUsersRead: "Прочитали {n}"
agreeTo: "Я погоджуюÑÑŒ з {0}"
agreeBelow: "Я погоджуюÑÑ Ð· наведеним нижче"
-tos: "Умови викориÑтаннÑ"
start: "Розпочати"
home: "Домівка"
remoteUserCaution: "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¼Ð¾Ð¶Ðµ бути неповною, оÑкільки це віддалений кориÑтувач."
activity: "ÐктивніÑть"
images: "ЗображеннÑ"
+image: "ЗображеннÑ"
birthday: "День народженнÑ"
yearsOld: "{age} років"
registeredDate: "ПриєднаннÑ"
@@ -899,6 +899,8 @@ achievements: "ДоÑÑгненнÑ"
joinThisServer: "ЗареєÑтруватиÑÑ Ð½Ð° цьому Ñервері"
exploreOtherServers: "Знайти інший Ñервер"
letsLookAtTimeline: "ПереглÑд Ñ–Ñторії"
+horizontal: "Збоку"
+youFollowing: "ПідпиÑки"
_achievements:
earnedAt: "Відкрито"
_types:
@@ -1330,31 +1332,6 @@ _time:
minute: "Ñ…"
hour: "г"
day: "д"
-_tutorial:
- title: "Як кориÑтуватиÑÑŒ Misskey"
- step1_1: "ЛаÑкаво проÑимо!"
- step1_2: "Ð¦Ñ Ñторінка має назву \"Ñтрічка подій\". Ðа ній з'ÑвлÑютьÑÑ Ð·Ð°Ð¿Ð¸Ñи кориÑтувачів на Ñких ви підпиÑані."
- step1_3: "Ðаразі ваша Ñтрічка порожнÑ, оÑкільки ви ще не напиÑали жодної нотатки Ñ– не підпиÑані на інших."
- step2_1: "Перш ніж зробити Ð·Ð°Ð¿Ð¸Ñ Ð°Ð±Ð¾ підпиÑатиÑÑŒ на когоÑÑŒ, заповніть Ñвій профіль."
- step2_2: "ÐÐ°Ð´Ð°Ð½Ð½Ñ Ð´ÐµÑкої інформації про Ñебе допоможе іншим кориÑтувачам вирішити підпиÑатиÑÑŒ на ваÑ."
- step3_1: "Ви уÑпішно налаштували Ñвій обліковий запиÑ?"
- step3_2: "ÐаÑтупним кроком Ñ” напиÑÐ°Ð½Ð½Ñ Ð½Ð¾Ñ‚Ð°Ñ‚ÐºÐ¸. Це можна зробити, натиÑнувши Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð¾Ð»Ñ–Ð²Ñ†Ñ Ð½Ð° екрані."
- step3_3: "ПіÑÐ»Ñ Ð½Ð°Ð¿Ð¸ÑÐ°Ð½Ð½Ñ Ð²Ð¼Ñ–Ñту ви можете опублікувати його, натиÑнувши кнопку у верхньому правому куті форми."
- step3_4: "Ðе знаєте що напиÑати? Спробуйте \"Привіт, Misskey!\""
- step4_1: "Ви розміÑтили Ñвій перший запиÑ?"
- step4_2: "Ура! Ваш перший Ð·Ð°Ð¿Ð¸Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶Ð°Ñ”Ñ‚ÑŒÑÑ Ð½Ð° вашій Ñтрічці подій."
- step5_1: "ÐаÑтав Ñ‡Ð°Ñ Ð¾Ð¶Ð¸Ð²Ð¸Ñ‚Ð¸ вашу Ñтрічку подій підпиÑавшиÑÑŒ на інших кориÑтувачів."
- step5_2: "{explore} допоможе вам знайти цікавих людей та підпиÑатиÑÑ Ð½Ð° них."
- step5_3: "Щоб підпиÑатиÑÑŒ на інших кориÑтувачів, нажміть на їхнє зображеннÑ, а потім на кнопку \"підпиÑатиÑÑŒ\"."
- step5_4: "Якщо кориÑтувач має замок при імені, то йому потрібно буде вручну підтвердити вашу заÑвку на підпиÑку."
- step6_1: "Тепер ви повинні бачити запиÑи інших кориÑтувачів на вашій Ñтрічці подій."
- step6_2: "Також ви можете швидко відповіÑти, або \"відреагувати\" на запиÑи інших кориÑтувачів."
- step6_3: "Щоб \"відреагувати\", нажміть на знак Ð¿Ð»ÑŽÑ \"+\" на запиÑÑ– Ñ– виберіть емоджі Ñким ви хочете \"відреагувати\"."
- step7_1: "Вітаю! Ви пройшли Ð¾Ð·Ð½Ð°Ð¹Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð· Misskey."
- step7_2: "Якщо ви хочете більше дізнатиÑÑŒ про Misskey, зайдіть в розділ {help}."
- step7_3: "ÐаÑолоджуйтеÑÑŒ Misskey! 🚀"
- step8_1: "ÐаоÑтанку, чи бажаєте ви ввімкнути push-ÑповіщеннÑ?"
- step8_3: "Ви завжди можете змінити цей параметр пізніше."
_2fa:
alreadyRegistered: "Двофакторна Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ Ð²Ð¶Ðµ налаштована."
step1: "Спершу вÑтановіть на Ñвій приÑтрій програму автентифікації (наприклад {a} або {b})."
diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml
index ebd046b6d4..d620d99d80 100644
--- a/locales/vi-VN.yml
+++ b/locales/vi-VN.yml
@@ -261,12 +261,12 @@ nUsersRead: "Ä‘á»c bởi {n}"
agreeTo: "Tôi đồng ý {0}"
agreeBelow: "Äồng ý vá»›i ná»™i dung dưới đây"
basicNotesBeforeCreateAccount: "Những Ä‘iá»u cÆ¡ bản cần chú ý "
-tos: "Äiá»u khoản dịch vụ"
start: "Bắt đầu"
home: "Trang chính"
remoteUserCaution: "Vì ngưá»i dùng này ở máy chá»§ khác, thông tin hiển thị có thể không đầy đủ."
activity: "Hoạt động"
images: "Hình ảnh"
+image: "Hình ảnh"
birthday: "Sinh nhật"
yearsOld: "{age} tuổi"
registeredDate: "Tham gia"
@@ -944,6 +944,8 @@ copyErrorInfo: "Sao chép thông tin lỗi"
joinThisServer: "Äăng ký trên chá»§ máy này"
exploreOtherServers: "Tìm chủ máy khác"
letsLookAtTimeline: "Thá»­ xem Timeline"
+horizontal: "Thanh bên"
+youFollowing: "Äang theo dõi"
_achievements:
earnedAt: "Ngày thu nhận"
_types:
@@ -1349,31 +1351,6 @@ _time:
minute: "phút"
hour: "giá»"
day: "ngày"
-_tutorial:
- title: "Cách dùng Misskey"
- step1_1: "Xin chào!"
- step1_2: "Trang này gá»i là \"bảng tin\". Nó hiện \"tút\" từ những ngưá»i mà bạn \"theo dõi\" theo thứ tá»± thá»i gian."
- step1_3: "Bảng tin của bạn đang trống, bởi vì bạn chưa đăng tút nào hoặc chưa theo dõi ai."
- step2_1: "Hãy hoàn thành việc thiết lập hồ sơ của bạn trước khi viết tút hoặc theo dõi bất kỳ ai."
- step2_2: "Cung cấp má»™t số thông tin giá»›i thiệu bạn là ai sẽ giúp ngưá»i khác dá»… dàng biết được há» muốn Ä‘á»c tút hay theo dõi bạn."
- step3_1: "Hoàn thành thiết lập hồ sơ của bạn?"
- step3_2: "Sau đó, hãy thử đăng một tút tiếp theo. Bạn có thể làm như vậy bằng cách nhấn vào nút có biểu tượng bút chì trên màn hình."
- step3_3: "Nhập nội dung vào khung soạn thảo và nhấn nút đăng ở góc trên."
- step3_4: "Chưa biết nói gì? Thử \"Tôi mới tham gia Misskey\"!"
- step4_1: "Äăng xong tút đầu tiên cá»§a bạn?"
- step4_2: "De! Tút đầu tiên của bạn đã hiện trên bảng tin."
- step5_1: "Bây giá», hãy thá»­ làm cho bảng tin cá»§a bạn sinh động hÆ¡n bằng cách theo dõi những ngưá»i khác."
- step5_2: "{feature} sẽ hiển thị cho bạn các tút nổi bật trên máy chá»§ này. {explore} sẽ cho phép bạn tìm thấy những ngưá»i dùng thú vị. Hãy thá»­ tìm những ngưá»i bạn muốn theo dõi ở đó!"
- step5_3: "Äể theo dõi những ngưá»i dùng khác, hãy nhấn vào ảnh đại diện cá»§a há» và nhấn nút \"Theo dõi\" trên hồ sÆ¡ cá»§a há»."
- step5_4: "Nếu ngưá»i dùng khác có biểu tượng ổ khóa bên cạnh tên cá»§a há», có thể mất má»™t khoảng thá»i gian để ngưá»i dùng đó phê duyệt yêu cầu theo dõi cá»§a bạn theo cách thá»§ công."
- step6_1: "Bạn sẽ có thể xem tút cá»§a những ngưá»i dùng khác trên bảng tin cá»§a mình ngay bây giá»."
- step6_2: "Bạn cÅ©ng có thể đặt \"biểu cảm\" trên tút cá»§a ngưá»i khác để phản hồi nhanh chúng."
- step6_3: "Äể đính kèm \"biểu cảm\", hãy nhấn vào dấu \"+\" trên tút cá»§a ngưá»i dùng khác rồi chá»n biểu tượng cảm xúc mà bạn muốn dùng."
- step7_1: "Xin chúc mừng! Bây giỠbạn đã hoàn thành phần hướng dẫn cơ bản của Misskey."
- step7_2: "Nếu bạn muốn tìm hiểu thêm vỠMisskey, hãy thử phần {help}."
- step7_3: "Bây giá», chúc may mắn và vui vẻ vá»›i Misskey! 🚀"
- step8_1: "Cuối cùng, bạn hãy bật thông báo đẩy nha!"
- step8_2: "Nhận thông báo đẩy bạn sẽ có thể thấy phản hồi, theo dõi, lượt nhắc được trong khi đóng Misskey"
_2fa:
alreadyRegistered: "Bạn đã đăng ký thiết bị xác minh 2 bước."
passwordToTOTP: "Nhắn mật mã"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index 62f6bc6c32..1fe82185be 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -114,7 +114,7 @@ sensitive: "æ•æ„Ÿå†…容"
add: "添加"
reaction: "回应"
reactions: "回应"
-reactionSetting: "在选择器中显示的回应"
+reactionSetting: "在选择器中显示回应"
reactionSettingDescription2: "æ‹–åŠ¨é‡æ–°æŽ’åºï¼Œå•击删除,点击 + 添加。"
rememberNoteVisibility: "ä¿å­˜ä¸Šæ¬¡è®¾ç½®çš„å¯è§æ€§"
attachCancel: "删除附件"
@@ -167,7 +167,7 @@ searchWith: "æœç´¢:{q}"
youHaveNoLists: "列表为空"
followConfirm: "你确定è¦å…³æ³¨{name}å—?"
proxyAccount: "代ç†è´¦æˆ·"
-proxyAccountDescription: "代ç†è´¦æˆ·æ˜¯åœ¨æŸäº›æƒ…况下充当用户的远程关注者的账户。 例如,当一个用户列出一个远程用户时,如果没有人跟éšè¯¥åˆ—出的用户,则该活动将ä¸ä¼šä¼ é€’到该实例,因此将代之以代ç†è´¦æˆ·ã€‚"
+proxyAccountDescription: "代ç†è´¦æˆ·æ˜¯åœ¨æŸäº›æƒ…况下充当用户的远程关注者的账户。 例如,当一个用户列出一个远程用户时,如果没有人跟éšè¯¥åˆ—出的用户,则该活动将ä¸ä¼šä¼ é€’到该æœåŠ¡å™¨ï¼Œå› æ­¤å°†ä»£ä¹‹ä»¥ä»£ç†è´¦æˆ·ã€‚"
host: "主机å"
selectUser: "选择用户"
recipient: "收件人"
@@ -263,14 +263,16 @@ noMoreHistory: "没有更多的历å²è®°å½•"
startMessaging: "添加èŠå¤©"
nUsersRead: "{n}人已读"
agreeTo: "å‹¾é€‰åˆ™è¡¨ç¤ºå·²é˜…è¯»å¹¶åŒæ„{0}"
-agreeBelow: "åŒæ„以下观点"
+agree: "åŒæ„"
+agreeBelow: "åŒæ„以下内容"
basicNotesBeforeCreateAccount: "基本注æ„事项"
-tos: "æœåŠ¡æ¡æ¬¾"
+termsOfService: "æœåŠ¡æ¡æ¬¾"
start: "开始"
home: "首页"
remoteUserCaution: "由于此用户æ¥è‡ªå…¶å®ƒæœåŠ¡å™¨ï¼Œæ˜¾ç¤ºçš„ä¿¡æ¯å¯èƒ½ä¸å®Œæ•´ã€‚"
activity: "活动"
images: "图片"
+image: "图片"
birthday: "生日"
yearsOld: "{age}å²"
registeredDate: "注册于"
@@ -403,7 +405,7 @@ totpDescription: "使用认è¯åº”用输入一次性密ç ã€‚"
moderator: "监察员"
moderation: "管ç†"
nUsersMentioned: "{n} 被æåˆ°"
-securityKeyAndPasskey: "安全密钥/密ç "
+securityKeyAndPasskey: "安全密钥或 Passkey"
securityKey: "安全密钥"
lastUsed: "最åŽä½¿ç”¨ï¼š"
lastUsedAt: "最åŽä½¿ç”¨: {t}"
@@ -474,6 +476,8 @@ createAccount: "注册账户"
existingAccount: "现有的账户"
regenerate: "釿–°ç”Ÿæˆ"
fontSize: "字体大å°"
+mediaListWithOneImageAppearance: "仅一张图片的媒体列表高度"
+limitTo: "上é™ä¸º{x}"
noFollowRequests: "没有关注申请"
openImageInNewTab: "在新标签页中打开图片"
dashboard: "管ç†é¢æ¿"
@@ -613,7 +617,7 @@ testEmail: "邮件å‘逿µ‹è¯•"
wordMute: "文字å±è”½"
regexpError: "正则表达å¼é”™è¯¯"
regexpErrorDescription: "{tab} å±è”½æ–‡å­—的第 {line} è¡Œçš„æ­£åˆ™è¡¨è¾¾å¼æœ‰é”™è¯¯ï¼š"
-instanceMute: "实例的å±è”½"
+instanceMute: "被å±è”½çš„æœåŠ¡å™¨"
userSaysSomething: "{name}说了什么,但是被å±è”½è¯è¿‡æ»¤äº†"
makeActive: "å¯ç”¨"
display: "显示"
@@ -699,6 +703,8 @@ contact: "è”系人"
useSystemFont: "使用系统默认字体"
clips: "便签"
experimentalFeatures: "实验性功能"
+experimental: "实验性的"
+thisIsExperimentalFeature: "这是一项实验性功能。规范å¯èƒ½ä¼šå˜æ›´ï¼Œæˆ–者å¯èƒ½æ— æ³•正常工作。"
developer: "å¼€å‘者"
makeExplorable: "使账å·å¯è§ã€‚"
makeExplorableDescription: "关闭时,账å·ä¸ä¼šæ˜¾ç¤ºåœ¨\"å‘现\"中。"
@@ -886,7 +892,7 @@ typeToConfirm: "输入 {x} 以确认æ“作。"
deleteAccount: "删除账户"
document: "文档"
numberOfPageCache: "缓存页数"
-numberOfPageCacheDescription: "设置较高的值会更方便用户,但设备的负载和内存使用é‡ä¼šå¢žåŠ ã€‚"
+numberOfPageCacheDescription: "设置较高的值会更方便用户,但æœåŠ¡å™¨è´Ÿè½½å’Œå†…å­˜ä½¿ç”¨é‡ä¼šå¢žåŠ ã€‚"
logoutConfirm: "是å¦ç¡®è®¤ç™»å‡ºï¼Ÿ"
lastActiveDate: "æœ€åŽæ´»è·ƒæ—¶é—´"
statusbar: "çŠ¶æ€æ "
@@ -905,6 +911,7 @@ remoteOnly: "仅远程"
failedToUpload: "上传失败"
cannotUploadBecauseInappropriate: "因为å¯èƒ½å«æœ‰ä¸é€‚宜的内容,无法上传。"
cannotUploadBecauseNoFreeSpace: "因为已无å¯ç”¨ç©ºé—´ï¼Œæ— æ³•上传。"
+cannotUploadBecauseExceedsFileSizeLimit: "无法上传文件,超过文件大å°é™åˆ¶ã€‚"
beta: "测试"
enableAutoSensitive: "自动 NSFW 识别"
enableAutoSensitiveDescription: "如果å¯ç”¨ï¼Œè¯·ä½¿ç”¨æœºå™¨å­¦ä¹ åœ¨åª’体上自动设置 NSFW 标志。å³ä½¿å…³é—­æ­¤åŠŸèƒ½ï¼Œä¹Ÿå¯èƒ½ä¼šæ ¹æ®æœåŠ¡å™¨è‡ªåŠ¨è®¾ç½®ã€‚"
@@ -938,6 +945,7 @@ didYouLikeMisskey: "您喜欢Misskeyå—?"
pleaseDonate: "Misskey是{host}所使用的å…费软件。为了今åŽä¹Ÿèƒ½å¤Ÿç»´æŒMisskey的开å‘,请在有余力的情况下进行æåŠ©ï¼"
roles: "角色"
role: "角色"
+noRole: "角色ä¸å­˜åœ¨"
normalUser: "普通用户"
undefined: "未定义"
assign: "分é…"
@@ -947,6 +955,10 @@ manageCustomEmojis: "管ç†è‡ªå®šä¹‰è¡¨æƒ…符å·"
youCannotCreateAnymore: "抱歉,您无法å†åˆ›å»ºæ›´å¤šäº†ã€‚"
cannotPerformTemporary: "暂时ä¸å¯ç”¨"
cannotPerformTemporaryDescription: "å› æ“作过于频ç¹ï¼Œæš‚æ—¶ä¸å¯ç”¨ï¼Œè¯·ç¨åŽå†è¯•。"
+invalidParamError: "傿•°é”™è¯¯"
+invalidParamErrorDescription: "è¯·æ±‚å‚æ•°å‡ºçŽ°é—®é¢˜ã€‚é€šå¸¸æ˜¯å› ä¸ºbug造æˆçš„,但也å¯èƒ½æ˜¯è¾“入文字数é‡è¿‡å¤šä¹‹ç±»çš„原因。"
+permissionDeniedError: "æ“作被拒ç»"
+permissionDeniedErrorDescription: "本账户没有执行该æ“作的æƒé™ã€‚"
preset: "é è¨­å€¼"
selectFromPresets: "從é è¨­å€¼ä¸­é¸æ“‡"
achievements: "æˆå°±"
@@ -990,17 +1002,45 @@ enableChartsForFederatedInstances: "生æˆè¿œç¨‹æœåŠ¡å™¨çš„å›¾è¡¨"
showClipButtonInNoteFooter: "在贴文下方显示便签按钮"
largeNoteReactions: "ä½¿ç”¨å¤§å›¾æ ‡æ¥æ˜¾ç¤ºå›žåº”"
noteIdOrUrl: "帖å­ID或URL"
+video: "视频"
+videos: "视频"
+dataSaver: "çœæµé‡æ¨¡å¼"
accountMigration: "账户è¿ç§»"
accountMoved: "此用户已è¿ç§»è´¦æˆ·"
+accountMovedShort: "æ­¤å¸æˆ·å·²è¿ç§»"
+operationForbidden: "ä¸å…许此æ“作"
forceShowAds: "总是显示广告"
+addMemo: "添加备注"
+editMemo: "编辑备注"
+reactionsList: "回应列表"
+renotesList: "转å‘列表"
+notificationDisplay: "显示通知"
+leftTop: "å±å¹•左上方"
+rightTop: "å±å¹•å³ä¸Šæ–¹"
+leftBottom: "å±å¹•左下方"
+rightBottom: "å±å¹•å³ä¸‹æ–¹"
+stackAxis: "堆堿–¹å‘"
+vertical: "纵å‘"
+horizontal: "横å‘"
+position: "ä½ç½®"
+serverRules: "æœåŠ¡å™¨è§„åˆ™"
+pleaseConfirmBelowBeforeSignup: "在这个æœåŠ¡å™¨ä¸Šæ³¨å†Œè´¦å·å‰ï¼Œè¯·ç¡®è®¤ä»¥ä¸‹ä¿¡æ¯ã€‚"
+pleaseAgreeAllToContinue: "å¿…é¡»å…¨éƒ¨å‹¾é€‰ã€ŒåŒæ„ã€æ‰èƒ½å¤Ÿç»§ç»­ã€‚"
+continue: "ç»§ç»­"
+preservedUsernames: "ä¿ç•™çš„用户å"
+createNoteFromTheFile: "从文件创建帖å­"
+youFollowing: "正在关注"
+_serverRules:
+ description: "åœ¨æ–°ç”¨æˆ·æ³¨å†Œå‰æ˜¾ç¤ºæœåŠ¡å™¨çš„ç®€å•è§„åˆ™ã€‚æŽ¨èæ˜¾ç¤ºæœåŠ¡æ¡æ¬¾çš„主è¦å†…容。"
_accountMigration:
- moveTo: "把这个账户è¿ç§»åˆ°æ–°çš„账户"
- moveToLabel: "è¿ç§»åŽçš„账户"
- moveAccountDescription: "æ­¤æ“ä½œæ— æ³•å–æ¶ˆã€‚请先确认您已在è¿ç§»åŽçš„账户上,为此账户创造了别å。创造别ååŽï¼Œè¯·å¦‚以下输入您的è¿ç§»åŽçš„账户:@person@instance.com"
moveFrom: "从别的账å·è¿ç§»åˆ°æ­¤è´¦æˆ·"
moveFromLabel: "è¿ç§»å‰çš„账户"
moveFromDescription: "如果è¿ç§»æ—¶éœ€è¦ç»§æ‰¿å…¶ä»–账户的关注者,请在此创造别å。此æ“作需è¦åœ¨å®žè¡Œè¿ç§»ä¹‹å‰å®Œæˆï¼è¯·å¦‚已下输入需è¦è¿ç§»çš„账户:@person@instance.com"
+ moveTo: "把这个账户è¿ç§»åˆ°æ–°çš„账户"
+ moveToLabel: "è¿ç§»åŽçš„账户"
+ moveAccountDescription: "æ­¤æ“ä½œæ— æ³•å–æ¶ˆã€‚请先确认您已在è¿ç§»åŽçš„账户上,为此账户创造了别å。创造别ååŽï¼Œè¯·å¦‚以下输入您的è¿ç§»åŽçš„账户:@person@instance.com"
migrationConfirm: "ç¡®å®šè¦æŠŠæ­¤è´¦æˆ·è¿ç§»åˆ°{account}å—?一旦确定åŽï¼Œæ­¤æ“ä½œæ— æ³•å–æ¶ˆï¼Œæ­¤è´¦æˆ·ä¹Ÿæ— æ³•以原æ¥çš„状æ€ä½¿ç”¨ã€‚\nåŒæ—¶ï¼Œè¯·ç¡®è®¤è¿ç§»åŽçš„账户,已创造别å。"
+ movedTo: "è¿ç§»åŽçš„账户"
_achievements:
earnedAt: "è¾¾æˆæ—¶é—´"
_types:
@@ -1172,6 +1212,9 @@ _achievements:
_client30min:
title: "休æ¯ä¸€ä¸‹ï¼"
description: "å¯åŠ¨å®¢æˆ·ç«¯è¶…è¿‡30分钟"
+ _client60min:
+ title: "Misskeyé‡åº¦ä¾èµ–"
+ description: "å¯åŠ¨å®¢æˆ·ç«¯è¶…è¿‡60分钟"
_noteDeletedWithin1min:
title: "æ¬²è¨€åˆæ­¢"
description: "å‘帖åŽä¸€åˆ†é’Ÿå†…就将其删除"
@@ -1260,6 +1303,8 @@ _role:
iconUrl: "图标URL"
asBadge: "作为徽章显示"
descriptionOfAsBadge: "å¼€å¯åŽï¼Œç”¨æˆ·åæ—边将会出现角色图标。"
+ isExplorable: "公开角色时间线"
+ descriptionOfIsExplorable: "打开åŽå°†å…¬å¼€è§’è‰²æ—¶é—´çº¿ã€‚å¦‚æžœè§’è‰²ä¸æ˜¯å…¬å¼€çš„,就无法公开时间线。"
displayOrder: "显示顺åº"
descriptionOfDisplayOrder: "数字越大,显示ä½ç½®è¶Šé å‰ã€‚"
canEditMembersByModerator: "å…许监察者编辑æˆå‘˜"
@@ -1527,32 +1572,6 @@ _time:
minute: "分"
hour: "å°æ—¶"
day: "æ—¥"
-_tutorial:
- title: "Misskey的使用方法"
- step1_1: "欢迎ï¼"
- step1_2: "这个页é¢å«åšã€Œæ—¶é—´çº¿ã€ï¼Œå®ƒä¼šæŒ‰ç…§æ—¶é—´é¡ºåºæ˜¾ç¤ºæ‰€æœ‰ä½ ã€Œå…³æ³¨ã€çš„人所å‘的「帖å­ã€ã€‚"
- step1_3: "如果你并没有å‘布任何帖å­ï¼Œä¹Ÿæ²¡æœ‰å…³æ³¨å…¶ä»–的人,你的时间线页é¢åº”当什么都没有显示。"
- step2_1: "在您想è¦å‘帖或关注其他人之å‰ï¼Œè¯·å…ˆè®¾ç½®ä¸€ä¸‹ä¸ªäººèµ„æ–™å§ã€‚"
- step2_2: "如果别人能够更加的了解你,关注你的概率也会得到æå‡ã€‚"
- step3_1: "å·²ç»è®¾ç½®å®Œä¸ªäººèµ„料了å—?"
- step3_2: "那么接下æ¥ï¼Œè¯•ç€å†™ä¸€äº›ä»€ä¹ˆä¸œè¥¿æ¥å‘布å§ã€‚ä½ å¯ä»¥é€šè¿‡ç‚¹å‡»å±å¹•ä¸Šçš„é“…ç¬”å›¾æ ‡æ¥æ‰“开投稿页é¢ã€‚"
- step3_3: "写完内容åŽï¼Œç‚¹å‡»çª—å£å³ä¸Šæ–¹çš„æŒ‰é’®å°±å¯ä»¥æŠ•稿。"
- step3_4: "ä¸çŸ¥é“说些什么好å—?那就写下「Misskey我æ¥å•¦ï¼ã€è¿™æ ·çš„è¯å§ã€‚"
- step4_1: "将你的è¯è¯­å‘布出去了å—?"
- step4_2: "太棒了ï¼çŽ°åœ¨ä½ å¯ä»¥åœ¨ä½ çš„æ—¶é—´çº¿ä¸­çœ‹åˆ°ä½ åˆšåˆšå‘布的帖å­äº†ã€‚"
- step5_1: "接下æ¥ï¼Œå…³æ³¨å…¶ä»–人æ¥ä½¿æ—¶é—´çº¿æ›´ç”ŸåЍå§ã€‚"
- step5_2: "{featured}将呿‚¨å±•示热门趋势的帖å­ã€‚ {explore}将让您找到热门用户。 å°è¯•关注您喜欢的人ï¼"
- step5_3: "è¦å…³æ³¨å…¶ä»–用户,请å•击他的头åƒï¼Œç„¶åŽåœ¨ä»–çš„ä¸ªäººèµ„æ–™ä¸ŠæŒ‰ä¸‹â€œå…³æ³¨â€æŒ‰é’®ã€‚"
- step5_4: "如果用户的åç§°æ—边有é”å®šå›¾æ ‡ï¼Œåˆ™è¯¥ç”¨æˆ·éœ€è¦æ‰‹åŠ¨æ‰¹å‡†æ‚¨çš„å…³æ³¨è¯·æ±‚ã€‚"
- step6_1: "现在,您将å¯ä»¥åœ¨æ—¶é—´çº¿ä¸Šçœ‹åˆ°å…¶ä»–用户的帖å­ã€‚"
- step6_2: "您还å¯ä»¥åœ¨å…¶ä»–人的帖å­ä¸Šè¿›è¡Œã€Œå›žåº”ã€ï¼Œä»¥å¿«é€Ÿåšå‡ºç®€å•回å¤ã€‚"
- step6_3: "在他人的贴å­ä¸ŠæŒ‰ä¸‹ã€Œ+ã€å›¾æ ‡ï¼Œå³å¯é€‰æ‹©æƒ³è¦çš„表情æ¥è¿›è¡Œã€Œå›žåº”ã€ã€‚"
- step7_1: "对Misskey基本æ“作的简å•介ç»ï¼Œå°±åˆ°æ­¤ç»“æŸäº†ã€‚ 辛苦了ï¼"
- step7_2: "如果你想了解更多有关Misskey的信æ¯ï¼Œè¯·å‚è§{help}。"
- step7_3: "接下æ¥ï¼Œäº«å—Misskey带æ¥çš„ä¹è¶£å§ðŸš€"
- step8_1: "最åŽï¼Œæ‚¨æƒ³è¦å¯ç”¨æŽ¨é€é€šçŸ¥æ¶ˆæ¯å—?"
- step8_2: "通过接收推é€é€šçŸ¥æ¶ˆæ¯ï¼Œå³ä½¿æ²¡å¼€ Misskey,您也å¯ä»¥æ”¶åˆ°å›žåº”ã€å…³æ³¨ã€æåŠç­‰çš„æ¶ˆæ¯ã€‚"
- step8_3: "您也å¯ä»¥ç¨åŽå†æ›´æ”¹é€šçŸ¥è®¾ç½®ã€‚"
_2fa:
alreadyRegistered: "此设备已被注册"
registerTOTP: "开始设置认è¯åº”用"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 09253db2d7..4073b9629d 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -2,7 +2,7 @@
_lang_: "ç¹é«”中文"
headlineMisskey: "貼文連繫網路"
introMisskey: "æ­¡è¿Žï¼ Misskey是一個開放原始碼且去中心化的社群網路。\né€éŽã€Œè²¼æ–‡ã€åˆ†äº«å‘¨é‚Šæ–°é®®äº‹ï¼Œä¸¦å‘Šè¨´å…¶ä»–人您的想法ï¼ðŸ“¡\né€éŽã€Œå應ã€åŠŸèƒ½ï¼Œå°å¤§å®¶çš„è²¼æ–‡è¡¨é”æƒ…感ï¼ðŸ‘\n一起來探索這個新的世界å§ï¼ðŸš€"
-poweredByMisskeyDescription: "{name}是使用開放原始碼平å°<b>Misskey</b>çš„æœå‹™ä¹‹ä¸€ï¼ˆç¨±ç‚º Misskey 實例)。\n"
+poweredByMisskeyDescription: "{name}是使用開放原始碼平å°<b>Misskey</b>çš„æœå‹™ä¹‹ä¸€ï¼ˆç¨±ç‚º Misskey 伺æœå™¨ï¼‰ã€‚\n"
monthAndDay: "{month}月 {day}日"
search: "æœå°‹"
notifications: "通知"
@@ -18,7 +18,7 @@ enterUsername: "輸入使用者å稱"
renotedBy: "{user} 轉發了"
noNotes: "無貼文。"
noNotifications: "沒有通知"
-instance: "實例"
+instance: "伺æœå™¨"
settings: "設定"
notificationSettings: "通知é¸é …"
basicSettings: "基本設定"
@@ -86,7 +86,7 @@ somethingHappened: "發生錯誤"
retry: "é‡è©¦"
pageLoadError: "載入é é¢å¤±æ•—"
pageLoadErrorDescription: "這通常是因為網路錯誤或是ç€è¦½å™¨å¿«å–殘留的原因。請先清除ç€è¦½å™¨å¿«å–,ç¨å¾Œå†é‡è©¦"
-serverIsDead: "伺æœå™¨æ²’有回應。請ç¨ç­‰ç‰‡åˆ»ï¼Œç„¶å¾Œé‡è©¦ã€‚"
+serverIsDead: "伺æœå™¨æ²’有回應。請ç¨ç­‰ç‰‡åˆ»å†è©¦ã€‚"
youShouldUpgradeClient: "è«‹é‡æ–°è¼‰å…¥ä»¥ä½¿ç”¨æ–°ç‰ˆæœ¬çš„客戶端顯示此é é¢"
enterListName: "輸入清單å稱"
privacy: "éš±ç§"
@@ -96,7 +96,7 @@ follow: "追隨"
followRequest: "追隨請求"
followRequests: "追隨請求"
unfollow: "å–æ¶ˆè¿½éš¨"
-followRequestPending: "è¿½éš¨è¨±å¯æ‰¹å‡†ä¸­"
+followRequestPending: "追隨許å¯å¾…批准"
enterEmoji: "輸入表情符號"
renote: "轉發"
unrenote: "å–æ¶ˆè½‰ç™¼"
@@ -150,11 +150,11 @@ cacheRemoteFiles: "å¿«å–é ç«¯æª”案"
cacheRemoteFilesDescription: "ç¦ç”¨æ­¤è¨­å®šæœƒåœæ­¢é ç«¯æª”案的緩存,從而節çœå„²å­˜ç©ºé–“,但資料會因直接連線從而產生é¡å¤–連接數據。"
flagAsBot: "此使用者是機器人"
flagAsBotDescription: "å¦‚æžœæœ¬å¸³æˆ¶æ˜¯ç”±ç¨‹å¼æŽ§åˆ¶ï¼Œè«‹å•Ÿç”¨æ­¤é¸é …。啟用後,會作為標示幫助其他開發者防止機器人之間產生無é™äº’動的行為,並會調整Misskey內部系統將本帳戶識別為機器人"
-flagAsCat: "此使用者是貓"
+flagAsCat: "喵~~~~~~~~~~~~~~ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼"
flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示"
flagShowTimelineReplies: "在時間軸上顯示貼文的回覆"
flagShowTimelineRepliesDescription: "啟用時,時間線除了顯示用戶的貼文以外,還會顯示用戶å°å…¶ä»–貼文的回覆。"
-autoAcceptFollowed: "自動追隨中使用者的追隨請求"
+autoAcceptFollowed: "自動å…許來自追隨中使用者的追隨請求"
addAccount: "添加帳戶"
reloadAccountsList: "更新帳戶清單的資訊"
loginFailed: "登入失敗"
@@ -167,7 +167,7 @@ searchWith: "æœå°‹: {q}"
youHaveNoLists: "你沒有任何清單"
followConfirm: "你真的è¦è¿½éš¨{name}嗎?"
proxyAccount: "代ç†å¸³æˆ¶"
-proxyAccountDescription: "代ç†å¸³æˆ¶æ˜¯åœ¨æŸäº›æƒ…æ³ä¸‹å……當其他伺æœå™¨ç”¨æˆ¶çš„帳戶。例如,當使用者將一個來自其他伺æœå™¨çš„å¸³æˆ¶æ”¾åœ¨åˆ—è¡¨ä¸­æ™‚ï¼Œç”±æ–¼æ²’æœ‰å…¶ä»–ä½¿ç”¨è€…è¿½è¹¤è©²å¸³æˆ¶ï¼Œè©²æŒ‡ä»¤ä¸æœƒå‚³é€åˆ°è©²ä¼ºæœå™¨ä¸Šï¼Œå› æ­¤æœƒç”±ä»£ç†å¸³æˆ¶è¿½è¹¤ã€‚"
+proxyAccountDescription: "代ç†å¸³æˆ¶æ˜¯åœ¨æŸäº›æƒ…æ³ä¸‹å……當其他伺æœå™¨ç”¨æˆ¶çš„帳戶。例如,當使用者將一個來自其他伺æœå™¨çš„å¸³æˆ¶æ”¾åœ¨åˆ—è¡¨ä¸­æ™‚ï¼Œç”±æ–¼æ²’æœ‰å…¶ä»–ä½¿ç”¨è€…è¿½éš¨è©²å¸³æˆ¶ï¼Œè©²æŒ‡ä»¤ä¸æœƒå‚³é€åˆ°è©²ä¼ºæœå™¨ä¸Šï¼Œå› æ­¤æœƒç”±ä»£ç†å¸³æˆ¶è¿½éš¨ã€‚"
host: "主機"
selectUser: "é¸å–使用者"
recipient: "收件人"
@@ -182,7 +182,7 @@ charts: "圖表"
perHour: "æ¯å°æ™‚"
perDay: "æ¯æ—¥"
stopActivityDelivery: "åœæ­¢ç™¼é€æ´»å‹•"
-blockThisInstance: "å°éŽ–æ­¤å¯¦ä¾‹"
+blockThisInstance: "å°éŽ–æ­¤ä¼ºæœå™¨"
operations: "æ“作"
software: "軟體"
version: "版本"
@@ -193,15 +193,15 @@ jobQueue: "佇列"
cpuAndMemory: "CPUåŠè¨˜æ†¶é«”用é‡"
network: "網路"
disk: "硬碟"
-instanceInfo: "實例資訊"
+instanceInfo: "伺æœå™¨è³‡è¨Š"
statistics: "統計"
clearQueue: "清除佇列"
clearQueueConfirmTitle: "ç¢ºå®šè¦æ¸…除佇列嗎?"
clearQueueConfirmText: "æœªç™¼ä½ˆçš„è²¼æ–‡å°‡ä¸æœƒç™¼ä½ˆã€‚您通常ä¸éœ€è¦ç¢ºèªã€‚"
clearCachedFiles: "清除快å–資料"
clearCachedFilesConfirm: "ç¢ºå®šè¦æ¸…除所有é ç«¯æš«å­˜è³‡æ–™å—Žï¼Ÿ"
-blockedInstances: "å·²å°éŽ–çš„å¯¦ä¾‹"
-blockedInstancesDescription: "è«‹é€è¡Œè¼¸å…¥éœ€è¦å°éŽ–çš„å¯¦ä¾‹ã€‚å·²å°éŽ–çš„å¯¦ä¾‹å°‡ç„¡æ³•èˆ‡æœ¬å¯¦ä¾‹é€²è¡Œé€šè¨Šã€‚"
+blockedInstances: "å·²å°éŽ–çš„ä¼ºæœå™¨"
+blockedInstancesDescription: "è«‹é€è¡Œè¼¸å…¥éœ€è¦å°éŽ–çš„ä¼ºæœå™¨ã€‚å·²å°éŽ–çš„ä¼ºæœå™¨å°‡ç„¡æ³•與本伺æœå™¨é€²è¡Œé€šè¨Šã€‚"
muteAndBlock: "éœéŸ³å’Œå°éŽ–"
mutedUsers: "å·²éœéŸ³ç”¨æˆ¶"
blockedUsers: "å·²å°éŽ–ç”¨æˆ¶"
@@ -224,8 +224,8 @@ all: "全部"
subscribing: "訂閱中"
publishing: "直播中"
notResponding: "沒有回應"
-instanceFollowing: "追蹤實例"
-instanceFollowers: "追蹤實例"
+instanceFollowing: "追隨的伺æœå™¨"
+instanceFollowers: "伺æœå™¨çš„追隨者"
instanceUsers: "用戶"
changePassword: "修改密碼"
security: "安全性"
@@ -263,14 +263,16 @@ noMoreHistory: "沒有更多歷å²ç´€éŒ„"
startMessaging: "é–‹å§‹èŠå¤©"
nUsersRead: "{n}人已讀"
agreeTo: "æˆ‘åŒæ„{0}"
+agree: "åŒæ„"
agreeBelow: "åŒæ„以下內容"
basicNotesBeforeCreateAccount: "基本注æ„事項"
-tos: "ä½¿ç”¨æ¢æ¬¾"
+termsOfService: "æœå‹™æ¢æ¬¾"
start: "é–‹å§‹"
home: "首é "
remoteUserCaution: "由於該使用者來自é ç«¯å¯¦ä¾‹ï¼Œå› æ­¤è³‡è¨Šå¯èƒ½éžå³æ™‚的。"
activity: "å‹•æ…‹"
images: "圖片"
+image: "圖片"
birthday: "生日"
yearsOld: "{age}æ­²"
registeredDate: "註冊日期"
@@ -318,8 +320,8 @@ unwatch: "å–æ¶ˆè¿½éš¨"
accept: "接å—"
reject: "拒絕"
normal: "正常"
-instanceName: "實例å稱"
-instanceDescription: "實例說明"
+instanceName: "伺æœå™¨å稱"
+instanceDescription: "伺æœå™¨ä»‹ç´¹"
maintainerName: "管ç†å“¡å稱"
maintainerEmail: "管ç†å“¡éƒµç®±"
tosUrl: "æœå‹™æ¢æ¬¾URL"
@@ -390,7 +392,7 @@ recentlyUpdatedUsers: "最近發文的使用者"
recentlyRegisteredUsers: "新加入使用者"
recentlyDiscoveredUsers: "最近發ç¾çš„使用者"
exploreUsersCount: "有{count}個使用者"
-exploreFediverse: "探索è¯é‚¦ä¸–界"
+exploreFediverse: "探索è¯é‚¦å®‡å®™"
popularTags: "熱門標籤"
userList: "使用者清單"
about: "關於"
@@ -474,7 +476,9 @@ createAccount: "建立帳戶"
existingAccount: "ç¾æœ‰å¸³æˆ¶"
regenerate: "å†ç”Ÿ"
fontSize: "字體大å°"
-noFollowRequests: "沒有跟隨您的請求"
+mediaListWithOneImageAppearance: "僅1枚圖片的媒體列表高度"
+limitTo: "上é™ç‚º{x}"
+noFollowRequests: "沒有追隨您的請求"
openImageInNewTab: "於新分é ä¸­é–‹å•Ÿåœ–片"
dashboard: "儀表æ¿"
local: "本地"
@@ -483,7 +487,7 @@ total: "åˆè¨ˆ"
weekOverWeekChanges: "與上週相比"
dayOverDayChanges: "與å‰ä¸€æ—¥ç›¸æ¯”"
appearance: "外觀"
-clientSettings: "用戶端設定"
+clientSettings: "客戶端設定"
accountSettings: "帳戶設定"
promotion: "推廣"
promote: "推廣"
@@ -543,8 +547,8 @@ disablePagesScript: "åœç”¨é é¢çš„AiScript腳本"
updateRemoteUser: "æ›´æ–°é ç«¯ä½¿ç”¨è€…資訊"
deleteAllFiles: "刪除所有檔案"
deleteAllFilesConfirm: "è¦åˆ é™¤æ‰€æœ‰æª”案嗎?"
-removeAllFollowing: "解除所有追蹤"
-removeAllFollowingDescription: "解除{host}所有的追蹤。在實例ä¸å†å­˜åœ¨æ™‚執行。"
+removeAllFollowing: "解除所有追隨"
+removeAllFollowingDescription: "解除{host}所有的追隨。在伺æœå™¨ä¸å†å­˜åœ¨æ™‚執行。"
userSuspended: "該使用者已被åœç”¨"
userSilenced: "該用戶已被ç¦è¨€ã€‚"
yourAccountSuspendedTitle: "帳戶已被å‡çµ"
@@ -556,6 +560,7 @@ accountDeletedDescription: "這個帳戶已被刪除。"
menu: "é¸å–®"
divider: "分割線"
addItem: "新增項目"
+rearrange: "æŽ’åºæ–¹å¼"
relays: "中繼"
addRelay: "新增中繼"
inboxUrl: "收件夾URL"
@@ -673,8 +678,8 @@ repliesCount: "回覆數é‡"
renotesCount: "轉發數é‡"
repliedCount: "回覆數é‡"
renotedCount: "轉發次數"
-followingCount: "正在跟隨的用戶數é‡"
-followersCount: "跟隨者數é‡"
+followingCount: "正在追隨的用戶數é‡"
+followersCount: "追隨者數é‡"
sentReactionsCount: "忇‰ç™¼é€æ¬¡æ•¸"
receivedReactionsCount: "æ”¶åˆ°åæ‡‰æ¬¡æ•¸"
pollVotesCount: "已統計的投票數"
@@ -699,6 +704,8 @@ contact: "è¯çµ¡äºº"
useSystemFont: "使用系統é è¨­çš„å­—åž‹"
clips: "摘錄"
experimentalFeatures: "實驗中的功能"
+experimental: "實驗性"
+thisIsExperimentalFeature: "這是實驗性的功能。å¯èƒ½æœƒæœ‰è®Šæ›´è¦æ ¼å’Œä¸èƒ½æ­£å¸¸å‹•作的å¯èƒ½æ€§ã€‚"
developer: "開發者"
makeExplorable: "使自己的帳戶能夠在「探索ã€é é¢ä¸­é¡¯ç¤º"
makeExplorableDescription: "å¦‚æžœé—œé–‰ï¼Œå¸³æˆ¶å°‡ä¸æœƒè¢«é¡¯ç¤ºåœ¨ã€ŒæŽ¢ç´¢ã€é é¢ä¸­ã€‚"
@@ -734,8 +741,8 @@ registry: "登錄表"
closeAccount: "åœç”¨å¸³æˆ¶"
currentVersion: "ç›®å‰ç‰ˆæœ¬"
latestVersion: "最新版本"
-youAreRunningUpToDateClient: "您所使用的用戶端已經是最新的。"
-newVersionOfClientAvailable: "新版本的用戶端å¯ç”¨ã€‚"
+youAreRunningUpToDateClient: "您所使用的客戶端已經是最新的。"
+newVersionOfClientAvailable: "新版本的客戶端å¯ç”¨ã€‚"
usageAmount: "使用é‡"
capacity: "容é‡"
inUse: "已使用"
@@ -821,7 +828,7 @@ pubSub: "Pub/Sub 帳戶"
lastCommunication: "最近的通信"
resolved: "已解決"
unresolved: "未解決"
-breakFollow: "移除追蹤者"
+breakFollow: "解除追隨者"
breakFollowConfirm: "確定è¦å–消被追隨嗎?"
itsOn: "已開啟"
itsOff: "已關閉"
@@ -905,6 +912,7 @@ remoteOnly: "僅é™é ç«¯"
failedToUpload: "上傳失敗"
cannotUploadBecauseInappropriate: "由於判定å¯èƒ½åŒ…å«ä¸é©ç•¶çš„內容,因此無法上傳。"
cannotUploadBecauseNoFreeSpace: "由於雲端硬碟沒有å¯ç”¨ç©ºé–“,因此無法上傳。"
+cannotUploadBecauseExceedsFileSizeLimit: "由於超éŽäº†æª”案大å°çš„é™åˆ¶ï¼Œç„¡æ³•上傳。"
beta: "Beta"
enableAutoSensitive: "自動NSFW判定"
enableAutoSensitiveDescription: "如果å¯ç”¨ï¼Œè«‹åˆ©ç”¨æ©Ÿå™¨å­¸ç¿’在媒體上自動設置 NSFW 旗標。 å³ä½¿é—œé–‰æ­¤åŠŸèƒ½ï¼Œä¾å¯¦ä¾‹è€Œå®šä¹Ÿå¯èƒ½æœƒè‡ªå‹•設置。"
@@ -938,6 +946,7 @@ didYouLikeMisskey: "您是å¦å–œæ„›Misskey呢?"
pleaseDonate: "Misskey是由{host}使用的å…費軟體。請贊助我們,讓開發能夠æŒçºŒï¼"
roles: "角色"
role: "角色"
+noRole: "沒有角色"
normalUser: "一般使用者"
undefined: "未定義"
assign: "指派"
@@ -947,6 +956,10 @@ manageCustomEmojis: "管ç†è‡ªè¨‚表情符號"
youCannotCreateAnymore: "您無法å†å»ºç«‹æ›´å¤šäº†ã€‚"
cannotPerformTemporary: "暫時無法進行"
cannotPerformTemporaryDescription: "ç”±æ–¼è¶…éŽæ“作次數é™åˆ¶ï¼Œæš«æ™‚無法進行。請éŽä¸€æ®µæ™‚間之後å†å˜—試。"
+invalidParamError: "åƒæ•¸éŒ¯èª¤"
+invalidParamErrorDescription: "è«‹æ±‚åƒæ•¸æœ‰å•題。通常是bug造æˆçš„,但也有輸入的字元數éŽå¤šä¹‹é¡žçš„å¯èƒ½æ€§ã€‚"
+permissionDeniedError: "æ“作被拒絕"
+permissionDeniedErrorDescription: "本帳號沒有執行這個æ“作的權é™ã€‚"
preset: "é è¨­å€¼"
selectFromPresets: "從é è¨­å€¼ä¸­é¸æ“‡"
achievements: "æˆå°±"
@@ -990,17 +1003,57 @@ enableChartsForFederatedInstances: "生æˆé ç«¯ä¼ºæœå™¨çš„圖表"
showClipButtonInNoteFooter: "將摘錄添加至貼文"
largeNoteReactions: "å°‡è²¼æ–‡çš„åæ‡‰æ”¾å¤§é¡¯ç¤º"
noteIdOrUrl: "貼文ID或URL"
+video: "影片"
+videos: "影片"
+dataSaver: "æ•¸æ“šç¯€çœæ¨¡å¼"
accountMigration: "é·ç§»å¸³æˆ¶"
accountMoved: "這個使用者已é·ç§»è‡³æ–°çš„帳戶:"
+accountMovedShort: "此帳戶已é·ç§»"
+operationForbidden: "ä¸å…許此æ“作"
forceShowAds: "總是顯示廣告"
+addMemo: "新增備註"
+editMemo: "編輯備註"
+reactionsList: "忇‰åˆ—表"
+renotesList: "轉發貼文列表"
+notificationDisplay: "通知的顯示"
+leftTop: "左上"
+rightTop: "å³ä¸Š"
+leftBottom: "左下"
+rightBottom: "å³ä¸‹"
+stackAxis: "堆疊方å‘"
+vertical: "縱å‘"
+horizontal: "å´å‘"
+position: "ä½ç½®"
+serverRules: "伺æœå™¨è¦å‰‡"
+pleaseConfirmBelowBeforeSignup: "在本伺æœå™¨è¨»å†Šä¹‹å‰ï¼Œè«‹ç¢ºèªä¸‹åˆ—事項。"
+pleaseAgreeAllToContinue: "必須全部勾é¸ã€ŒåŒæ„ã€æ‰èƒ½ç¹¼çºŒã€‚"
+continue: "繼續"
+preservedUsernames: "ä¿ç•™çš„使用者å稱"
+preservedUsernamesDescription: "æ›è¡Œåˆ—舉è¦ä¿ç•™çš„使用者å稱。此處指定的使用者å稱,在建立帳戶時無法使用,但由管ç†è€…所建立的帳戶ä¸å—æ­¤é™ã€‚此外,既有的帳戶也ä¸å—影響。"
+createNoteFromTheFile: "由此檔案建立貼文"
+archive: "å°å­˜"
+channelArchiveConfirmTitle: "è¦å°å­˜{name}嗎?"
+channelArchiveConfirmDescription: "å°å­˜ä»¥å¾Œï¼Œåœ¨é »é“列表與æœç´¢çµæžœä¸­ä¸æœƒé¡¯ç¤ºï¼Œä¹Ÿç„¡æ³•發布新的貼文。"
+thisChannelArchived: "這個頻é“已被å°å­˜ã€‚"
+displayOfNote: "顯示貼文"
+youFollowing: "關注中"
+_serverRules:
+ description: "設定伺æœå™¨çš„ç°¡è¦è¦å‰‡ï¼Œåœ¨æ–°çš„註冊之å‰é¡¯ç¤ºã€‚å»ºè­°çš„å…§å®¹æ˜¯ä½¿ç”¨æ¢æ¬¾çš„æ‘˜è¦ã€‚"
_accountMigration:
+ moveFrom: "從其他帳戶é·ç§»åˆ°é€™å€‹å¸³æˆ¶"
+ moveFromSub: "為å¦ä¸€å€‹å¸³æˆ¶å»ºç«‹åˆ¥å"
+ moveFromLabel: "è¦é·ç§»éŽä¾†çš„帳戶:"
+ moveFromDescription: "如果你想把追隨者從別的帳戶é·ç§»éŽä¾†ï¼Œå¿…須先在這裡建立別å。請務必在執行é·ç§»ä¹‹å‰å»ºç«‹åˆ¥åï¼è«‹åƒé€™æ¨£è¼¸å…¥è¦é·ç§»çš„帳戶:@person@instance.com"
moveTo: "將這個帳戶é·ç§»è‡³æ–°çš„帳戶"
moveToLabel: "è¦é·ç§»åˆ°çš„帳戶:"
+ moveCannotBeUndone: "一旦é·ç§»å¸³æˆ¶ï¼Œå°±ç„¡æ³•å–æ¶ˆã€‚"
moveAccountDescription: "這個æ“作ä¸å¯æ’¤éŠ·ã€‚é¦–å…ˆï¼Œè«‹ç¢ºèªå·²åœ¨è¦é·ç§»åˆ°çš„帳戶中為這個帳戶建立了一個別å。建立別å之後,åƒé€™æ¨£è¼¸å…¥ä½ è¦é·ç§»åˆ°çš„帳戶:@person@instance.com"
- moveFrom: "從其他帳戶é·ç§»åˆ°é€™å€‹å¸³æˆ¶"
- moveFromLabel: "è¦é·ç§»éŽä¾†çš„帳戶:"
- moveFromDescription: "如果你想把跟隨者從別的帳戶é·ç§»éŽä¾†ï¼Œå¿…須先在這裡建立別å。請務必在執行é·ç§»ä¹‹å‰å»ºç«‹åˆ¥åï¼è«‹åƒé€™æ¨£è¼¸å…¥è¦é·ç§»çš„帳戶:@person@instance.com"
+ moveAccountHowTo: "è¦é·ç§»å¸³æˆ¶ï¼Œé¦–å…ˆè¦åœ¨ç›®æ¨™å¸³æˆ¶ä¸­ç‚ºæ­¤å¸³æˆ¶å»ºç«‹ä¸€å€‹åˆ¥å。\n 建立別å後,åƒé€™æ¨£è¼¸å…¥ç›®æ¨™å¸³æˆ¶ï¼š@username@server.example.com"
+ startMigration: "é·ç§»"
migrationConfirm: "確定è¦å°‡é€™å€‹å¸³æˆ¶é·ç§»è‡³ {account} 嗎?一旦é·ç§»å°±ç„¡æ³•撤銷,也就無法以原來的狀態使用這個帳戶。\nå¦å¤–,請確èªåœ¨è¦é·ç§»åˆ°çš„帳戶已經建立了一個別å。"
+ movedAndCannotBeUndone: "帳戶已é·ç§»ã€‚\né·ç§»ç„¡æ³•撤消。"
+ postMigrationNote: "在é·ç§»æ“作後的24å°æ™‚之後解除此帳戶的追隨。此帳戶的追隨中ã€è¿½éš¨è€…數é‡è®Šç‚º0ã€‚ç”±æ–¼ä¸æœƒè§£é™¤è¿½éš¨è€…,你的追隨者ä»ç„¶å¯ä»¥ç¹¼çºŒæª¢è¦–這個帳戶發布給追隨者的貼文。"
+ movedTo: "è¦é·ç§»åˆ°çš„帳戶:"
_achievements:
earnedAt: "ç²å¾—日期"
_types:
@@ -1125,31 +1178,31 @@ _achievements:
description: "首次追隨了"
_following10:
title: "è·Ÿè‘—è·Ÿè‘—"
- description: "跟隨超éŽ10人了"
+ description: "追隨超éŽ10人了"
_following50:
title: "朋å‹å¾ˆå¤š"
- description: "跟隨超éŽ50人了"
+ description: "追隨超éŽ50人了"
_following100:
title: "100使œ‹å‹"
- description: "跟隨超éŽ100人了"
+ description: "追隨超éŽ100人了"
_following300:
title: "朋å‹éŽå¤š"
- description: "跟隨超éŽ300人了"
+ description: "追隨超éŽ300人了"
_followers1:
title: "第一個追隨者"
description: "第一次被追隨"
_followers10:
title: "Follow me!"
- description: "跟隨者超éŽ10人了"
+ description: "追隨者超éŽ10人了"
_followers50:
title: "æˆç¾¤çµéšŠ"
- description: "跟隨者超éŽ50人了"
+ description: "追隨者超éŽ50人了"
_followers100:
title: "熱門人物"
- description: "跟隨者超éŽ100人了"
+ description: "追隨者超éŽ100人了"
_followers300:
title: "請排æˆä¸€æŽ’"
- description: "跟隨者超éŽ300人了"
+ description: "追隨者超éŽ300人了"
_followers500:
title: "基地å°"
description: "è¶…éŽ500å追隨者了"
@@ -1171,7 +1224,10 @@ _achievements:
description: "發ç¾äº†éš±è—的寶è—"
_client30min:
title: "休æ¯ä¸€ä¸‹"
- description: "用戶端啟動已超éŽ30分é˜"
+ description: "客戶端啟動已超éŽ30分é˜"
+ _client60min:
+ title: "Misskey看太多"
+ description: "客戶端啟動已超éŽ60分é˜"
_noteDeletedWithin1min:
title: "ç¾åœ¨æ²’有了"
description: "發文後1分é˜å…§åˆªæ–‡"
@@ -1260,6 +1316,8 @@ _role:
iconUrl: "圖示的URL"
asBadge: "顯示為徽章"
descriptionOfAsBadge: "é–‹å•Ÿçš„è©±ï¼Œè§’è‰²åœ–ç¤ºæœƒé¡¯ç¤ºåœ¨ç”¨æˆ¶åæ—é‚Šã€‚"
+ isExplorable: "公開角色時間軸"
+ descriptionOfIsExplorable: "è‹¥é–‹å•Ÿå‰‡å…¬é–‹è§’è‰²æ™‚é–“è»¸ã€‚è‹¥è§’è‰²ä¸æ˜¯å…¬é–‹çš„,則無法公開時間軸。"
displayOrder: "顯示順åº"
descriptionOfDisplayOrder: "數字越大,顯示在UI上的越上é¢ã€‚"
canEditMembersByModerator: "å…許編輯審查員的æˆå“¡"
@@ -1276,6 +1334,7 @@ _role:
canInvite: "發行實例邀請碼"
canManageCustomEmojis: "管ç†è‡ªè¨‚表情符號"
driveCapacity: "雲端硬碟容é‡"
+ alwaysMarkNsfw: "總是將檔案標記為NSFW"
pinMax: "置頂貼文的最大數é‡"
antennaMax: "å¯å»ºç«‹çš„天線數é‡"
wordMuteMax: "éœéŸ³æ–‡å­—的最大字數"
@@ -1527,32 +1586,6 @@ _time:
minute: "分é˜"
hour: "å°æ™‚"
day: "æ—¥"
-_tutorial:
- title: "Misskey使用方法"
- step1_1: "歡迎ï¼"
- step1_2: "此為「時間軸ã€é é¢ï¼Œå®ƒæœƒæŒ‰ç…§æ™‚é–“é †åºé¡¯ç¤ºä½ ã€Œè¿½éš¨ã€çš„人發出的「貼文ã€ã€‚"
- step1_3: "ç”±æ–¼ä½ æ²’æœ‰ç™¼ä½ˆä»»ä½•è²¼æ–‡ï¼Œä¹Ÿæ²’æœ‰è¿½éš¨ä»»ä½•äººï¼Œæ‰€ä»¥ä½ çš„æ™‚é–“è»¸ç›®å‰æ˜¯ç©ºçš„。"
- step2_1: "在發文或追隨其他人之å‰å…ˆè®“我們設定一下個人資料å§ã€‚"
- step2_2: "æä¾›ä¸€äº›é—œæ–¼è‡ªå·±çš„資訊來讓其他人更有追隨你的æ„願。"
- step3_1: "個人資料都設定好了嗎?"
- step3_2: "接下來,讓我們來試試看發個文,按一下畫é¢ä¸Šçš„鉛筆圖示來開始"
- step3_3: "輸入完內容後,按視窗å³ä¸Šè§’的按鈕來發文"
- step3_4: "ä¸çŸ¥é“該寫什麼內容嗎?試試看「開始使用Misskey了ã€å¦‚何。"
- step4_1: "貼文發出去了嗎?"
- step4_2: "如果你的貼文出ç¾åœ¨æ™‚間軸上,就代表發文æˆåŠŸã€‚"
- step5_1: "ç¾åœ¨è©¦è©¦çœ‹è¿½éš¨å…¶ä»–人來讓你的時間軸變得更生動å§ã€‚"
- step5_2: "你會在{featured}ä¸Šçœ‹åˆ°å—æ­¡è¿Žçš„貼文,你也å¯ä»¥å¾žåˆ—表中追隨你喜歡的人,或者在{explore}上找到熱門使用者。"
- step5_3: "想è¦è¿½éš¨å…¶ä»–人,åªè¦é»žæ“Šä»–們的大頭貼並按「追隨ã€å³å¯ã€‚"
- step5_4: "如果使用者的åå­—æ—æœ‰éŽ–é ­çš„åœ–ç¤ºï¼Œä»£è¡¨ä»–å€‘éœ€è¦æ‰‹å‹•核准你的追隨請求。"
- step6_1: "ç¾åœ¨ä½ å¯ä»¥åœ¨æ™‚間軸上看到其他用戶的貼文。"
- step6_2: "你也å¯ä»¥å°åˆ¥äººçš„è²¼æ–‡ä½œå‡ºã€Œåæ‡‰ã€ï¼Œä½œå‡ºç°¡å–®çš„回覆。"
- step6_3: "在他人的貼文按下\"+\"圖標,å³å¯é¸æ“‡å–œå¥½çš„表情符號進行回應。"
- step7_1: "以上為Misskey的基本æ“作說明,教學在此告一段è½ã€‚辛苦了。"
- step7_2: "歡迎到{help}來瞭解更多Misskey相關介紹。"
- step7_3: "é‚£éº¼ï¼Œç¥æ‚¨åœ¨Misskey玩的開心~ 🚀"
- step8_1: "最後,è¦ä¸è¦è©¦è©¦çœ‹å•Ÿç”¨æŽ¨æ’­é€šçŸ¥å‘¢ï¼Ÿ"
- step8_2: "é€éŽæŽ¥æ”¶æŽ¨æ’­é€šçŸ¥ï¼Œå³ä½¿æ²’有打開Misskey,您也會知é“忇‰ã€è¿½éš¨èˆ‡æåŠçš„æƒ…æ³ã€‚"
- step8_3: "通知的設定å¯ä»¥åœ¨ä¹‹å¾Œè®Šæ›´ã€‚"
_2fa:
alreadyRegistered: "此設備已經被註冊éŽäº†"
registerTOTP: "開始設定驗證應用程å¼"
diff --git a/package.json b/package.json
index a22bb2873f..5b1d21b815 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,12 @@
{
"name": "misskey",
- "version": "13.11.3",
+ "version": "13.12.0",
"codename": "nasubi",
"repository": {
"type": "git",
"url": "https://github.com/misskey-dev/misskey.git"
},
- "packageManager": "pnpm@8.1.1",
+ "packageManager": "pnpm@8.3.1",
"workspaces": [
"packages/frontend",
"packages/backend",
@@ -51,19 +51,19 @@
"gulp-replace": "1.1.4",
"gulp-terser": "2.1.0",
"js-yaml": "4.1.0",
- "typescript": "5.0.3"
+ "typescript": "5.0.4"
},
"devDependencies": {
"@types/gulp": "4.0.10",
"@types/gulp-rename": "2.0.1",
- "@typescript-eslint/eslint-plugin": "5.57.1",
- "@typescript-eslint/parser": "5.57.1",
+ "@typescript-eslint/eslint-plugin": "5.59.2",
+ "@typescript-eslint/parser": "5.59.2",
"cross-env": "7.0.3",
- "cypress": "12.9.0",
- "eslint": "8.37.0",
+ "cypress": "12.11.0",
+ "eslint": "8.39.0",
"start-server-and-test": "2.0.0"
},
"optionalDependencies": {
- "@tensorflow/tfjs-core": "4.2.0"
+ "@tensorflow/tfjs-core": "4.4.0"
}
}
diff --git a/packages/backend/migration/1680702787050-UserMemo.js b/packages/backend/migration/1680702787050-UserMemo.js
new file mode 100644
index 0000000000..7446bf8da5
--- /dev/null
+++ b/packages/backend/migration/1680702787050-UserMemo.js
@@ -0,0 +1,18 @@
+export class UserMemo1680702787050 {
+ name = 'UserMemo1680702787050'
+
+ async up(queryRunner) {
+ await queryRunner.query(`CREATE TABLE "user_memo" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "targetUserId" character varying(32) NOT NULL, "memo" character varying(2048) NOT NULL, CONSTRAINT "PK_e9aaa58f7d3699a84d79078f4d9" PRIMARY KEY ("id")); COMMENT ON COLUMN "user_memo"."userId" IS 'The ID of author.'; COMMENT ON COLUMN "user_memo"."targetUserId" IS 'The ID of target user.'; COMMENT ON COLUMN "user_memo"."memo" IS 'Memo.'`);
+ await queryRunner.query(`CREATE INDEX "IDX_650b49c5639b5840ee6a2b8f83" ON "user_memo" ("userId") `);
+ await queryRunner.query(`CREATE INDEX "IDX_66ac4a82894297fd09ba61f3d3" ON "user_memo" ("targetUserId") `);
+ await queryRunner.query(`CREATE UNIQUE INDEX "IDX_faef300913c738265638ba3ebc" ON "user_memo" ("userId", "targetUserId") `);
+ await queryRunner.query(`ALTER TABLE "user_memo" ADD CONSTRAINT "FK_650b49c5639b5840ee6a2b8f83e" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
+ await queryRunner.query(`ALTER TABLE "user_memo" ADD CONSTRAINT "FK_66ac4a82894297fd09ba61f3d35" FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "user_memo" DROP CONSTRAINT "FK_66ac4a82894297fd09ba61f3d35"`);
+ await queryRunner.query(`ALTER TABLE "user_memo" DROP CONSTRAINT "FK_650b49c5639b5840ee6a2b8f83e"`);
+ await queryRunner.query(`DROP TABLE "user_memo"`);
+ }
+}
diff --git a/packages/backend/migration/1681400427971-serverRules.js b/packages/backend/migration/1681400427971-serverRules.js
new file mode 100644
index 0000000000..2364e8e1d2
--- /dev/null
+++ b/packages/backend/migration/1681400427971-serverRules.js
@@ -0,0 +1,11 @@
+export class ServerRules1681400427971 {
+ name = 'ServerRules1681400427971'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" ADD "serverRules" character varying(280) array NOT NULL DEFAULT '{}'`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "serverRules"`);
+ }
+}
diff --git a/packages/backend/migration/1681870960239-RoleTLSetting.js b/packages/backend/migration/1681870960239-RoleTLSetting.js
new file mode 100644
index 0000000000..2280f44eaa
--- /dev/null
+++ b/packages/backend/migration/1681870960239-RoleTLSetting.js
@@ -0,0 +1,12 @@
+export class RoleTLSetting1681870960239 {
+ name = 'RoleTLSetting1681870960239'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "role" ADD "isExplorable" boolean NOT NULL DEFAULT false`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "isExplorable"`);
+ }
+
+}
diff --git a/packages/backend/migration/1682190963894-movedAt.js b/packages/backend/migration/1682190963894-movedAt.js
new file mode 100644
index 0000000000..1f8f030a5c
--- /dev/null
+++ b/packages/backend/migration/1682190963894-movedAt.js
@@ -0,0 +1,13 @@
+export class MovedAt1682190963894 {
+ name = 'MovedAt1682190963894'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "user" ADD "movedAt" TIMESTAMP WITH TIME ZONE`);
+ await queryRunner.query(`COMMENT ON COLUMN "user"."movedAt" IS 'When the user moved to another account'`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`COMMENT ON COLUMN "user"."movedAt" IS 'When the user moved to another account'`);
+ await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "movedAt"`);
+ }
+}
diff --git a/packages/backend/migration/1682754135458-preservedUsernames.js b/packages/backend/migration/1682754135458-preservedUsernames.js
new file mode 100644
index 0000000000..46a0826f43
--- /dev/null
+++ b/packages/backend/migration/1682754135458-preservedUsernames.js
@@ -0,0 +1,11 @@
+export class PreservedUsernames1682754135458 {
+ name = 'PreservedUsernames1682754135458'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" ADD "preservedUsernames" character varying(1024) array NOT NULL DEFAULT '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey" }'`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "preservedUsernames"`);
+ }
+}
diff --git a/packages/backend/migration/1682985520254-channelColor.js b/packages/backend/migration/1682985520254-channelColor.js
new file mode 100644
index 0000000000..294b7372b2
--- /dev/null
+++ b/packages/backend/migration/1682985520254-channelColor.js
@@ -0,0 +1,11 @@
+export class ChannelColor1682985520254 {
+ name = 'ChannelColor1682985520254'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "channel" ADD "color" character varying(16) NOT NULL DEFAULT '#86b300'`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "channel" DROP COLUMN "color"`);
+ }
+}
diff --git a/packages/backend/migration/1683328299359-channelArchive.js b/packages/backend/migration/1683328299359-channelArchive.js
new file mode 100644
index 0000000000..83695ff537
--- /dev/null
+++ b/packages/backend/migration/1683328299359-channelArchive.js
@@ -0,0 +1,13 @@
+export class ChannelArchive1683328299359 {
+ name = 'ChannelArchive1683328299359'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "channel" ADD "isArchived" boolean NOT NULL DEFAULT false`);
+ await queryRunner.query(`CREATE INDEX "IDX_cc7c72974f1b2f385a8921f094" ON "channel" ("isArchived") `);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`DROP INDEX "public"."IDX_cc7c72974f1b2f385a8921f094"`);
+ await queryRunner.query(`ALTER TABLE "channel" DROP COLUMN "isArchived"`);
+ }
+}
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 875774bbd5..e0ece2bfe5 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -23,33 +23,44 @@
},
"optionalDependencies": {
"@swc/core-android-arm64": "1.3.11",
- "@swc/core-darwin-arm64": "1.3.46",
- "@swc/core-darwin-x64": "1.3.46",
- "@swc/core-linux-arm-gnueabihf": "1.3.46",
- "@swc/core-linux-arm64-gnu": "1.3.46",
- "@swc/core-linux-arm64-musl": "1.3.46",
- "@swc/core-linux-x64-gnu": "1.3.46",
- "@swc/core-linux-x64-musl": "1.3.46",
- "@swc/core-win32-arm64-msvc": "1.3.46",
- "@swc/core-win32-ia32-msvc": "1.3.46",
- "@swc/core-win32-x64-msvc": "1.3.46",
- "@tensorflow/tfjs": "4.2.0",
- "@tensorflow/tfjs-node": "4.2.0"
+ "@swc/core-darwin-arm64": "1.3.56",
+ "@swc/core-darwin-x64": "1.3.56",
+ "@swc/core-linux-arm-gnueabihf": "1.3.56",
+ "@swc/core-linux-arm64-gnu": "1.3.56",
+ "@swc/core-linux-arm64-musl": "1.3.56",
+ "@swc/core-linux-x64-gnu": "1.3.56",
+ "@swc/core-linux-x64-musl": "1.3.56",
+ "@swc/core-win32-arm64-msvc": "1.3.56",
+ "@swc/core-win32-ia32-msvc": "1.3.56",
+ "@swc/core-win32-x64-msvc": "1.3.56",
+ "@tensorflow/tfjs": "4.4.0",
+ "@tensorflow/tfjs-node": "4.4.0",
+ "slacc-android-arm-eabi": "0.0.7",
+ "slacc-android-arm64": "0.0.7",
+ "slacc-darwin-arm64": "0.0.7",
+ "slacc-darwin-universal": "0.0.7",
+ "slacc-darwin-x64": "0.0.7",
+ "slacc-linux-arm-gnueabihf": "0.0.7",
+ "slacc-linux-arm64-gnu": "0.0.7",
+ "slacc-linux-arm64-musl": "0.0.7",
+ "slacc-linux-x64-gnu": "0.0.7",
+ "slacc-win32-arm64-msvc": "0.0.7",
+ "slacc-win32-x64-msvc": "0.0.7"
},
"dependencies": {
- "@aws-sdk/client-s3": "3.306.0",
- "@aws-sdk/lib-storage": "3.306.0",
- "@aws-sdk/node-http-handler": "3.306.0",
- "@bull-board/api": "5.0.0",
- "@bull-board/fastify": "5.0.0",
- "@bull-board/ui": "5.0.0",
+ "@aws-sdk/client-s3": "3.321.1",
+ "@aws-sdk/lib-storage": "3.321.1",
+ "@aws-sdk/node-http-handler": "3.321.1",
+ "@bull-board/api": "5.1.2",
+ "@bull-board/fastify": "5.1.2",
+ "@bull-board/ui": "5.1.2",
"@discordapp/twemoji": "14.1.2",
"@fastify/accepts": "4.1.0",
"@fastify/cookie": "8.3.0",
"@fastify/cors": "8.2.1",
"@fastify/http-proxy": "9.0.0",
- "@fastify/multipart": "7.5.0",
- "@fastify/static": "6.10.0",
+ "@fastify/multipart": "7.6.0",
+ "@fastify/static": "6.10.1",
"@fastify/view": "7.4.1",
"@nestjs/common": "9.4.0",
"@nestjs/core": "9.4.0",
@@ -57,7 +68,7 @@
"@peertube/http-signature": "1.7.0",
"@sinonjs/fake-timers": "10.0.2",
"@swc/cli": "0.1.62",
- "@swc/core": "1.3.46",
+ "@swc/core": "1.3.56",
"accepts": "1.3.8",
"ajv": "8.12.0",
"archiver": "5.3.1",
@@ -73,25 +84,26 @@
"cli-highlight": "2.1.11",
"color-convert": "2.0.1",
"content-disposition": "0.5.4",
- "date-fns": "2.29.3",
+ "date-fns": "2.30.0",
"deep-email-validator": "0.1.21",
"escape-regexp": "0.0.1",
- "fastify": "4.15.0",
+ "fastify": "4.17.0",
"feed": "4.2.2",
- "file-type": "18.2.1",
+ "file-type": "18.3.0",
"fluent-ffmpeg": "2.1.2",
"form-data": "4.0.0",
"got": "12.6.0",
- "happy-dom": "8.9.0",
+ "happy-dom": "9.10.2",
"hpagent": "1.2.0",
- "ioredis": "4.28.5",
+ "ioredis": "5.3.2",
"ip-cidr": "3.1.0",
"is-svg": "4.3.2",
"js-yaml": "4.1.0",
"jsdom": "21.1.1",
"json5": "2.2.3",
"jsonld": "8.1.1",
- "jsrsasign": "10.7.0",
+ "meilisearch": "0.32.3",
+ "jsrsasign": "10.8.6",
"mfm-js": "0.23.3",
"mime-types": "2.1.35",
"misskey-js": "workspace:*",
@@ -111,7 +123,7 @@
"pug": "3.0.2",
"punycode": "2.3.0",
"pureimage": "0.3.17",
- "qrcode": "1.5.1",
+ "qrcode": "1.5.3",
"random-seed": "0.3.0",
"ratelimiter": "3.4.1",
"re2": "1.18.0",
@@ -119,37 +131,38 @@
"reflect-metadata": "0.1.13",
"rename": "1.0.4",
"rndstr": "1.0.0",
- "rss-parser": "3.12.0",
- "rxjs": "7.8.0",
+ "rss-parser": "3.13.0",
+ "rxjs": "7.8.1",
"s-age": "1.1.2",
"sanitize-html": "2.10.0",
"seedrandom": "3.0.5",
- "semver": "7.3.8",
- "sharp": "0.32.0",
+ "semver": "7.5.0",
+ "sharp": "0.32.1",
"sharp-read-bmp": "github:misskey-dev/sharp-read-bmp",
+ "slacc": "0.0.7",
"strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0",
"summaly": "github:misskey-dev/summaly",
"systeminformation": "5.17.12",
"tinycolor2": "1.6.0",
"tmp": "0.2.1",
- "tsc-alias": "1.8.5",
+ "tsc-alias": "1.8.6",
"tsconfig-paths": "4.2.0",
"twemoji-parser": "14.0.0",
- "typeorm": "0.3.13",
- "typescript": "5.0.3",
+ "typeorm": "0.3.15",
+ "typescript": "5.0.4",
"ulid": "2.3.0",
"unzipper": "0.10.11",
"uuid": "9.0.0",
"vary": "1.1.2",
- "web-push": "3.5.0",
+ "web-push": "3.6.1",
"websocket": "1.0.34",
"ws": "8.13.0",
"xev": "3.0.2"
},
"devDependencies": {
"@jest/globals": "29.5.0",
- "@swc/jest": "0.2.24",
+ "@swc/jest": "0.2.26",
"@types/accepts": "1.3.5",
"@types/archiver": "5.3.2",
"@types/bcryptjs": "2.4.2",
@@ -159,14 +172,13 @@
"@types/content-disposition": "0.5.5",
"@types/escape-regexp": "0.0.1",
"@types/fluent-ffmpeg": "2.1.21",
- "@types/ioredis": "4.28.10",
- "@types/jest": "29.5.0",
+ "@types/jest": "29.5.1",
"@types/js-yaml": "4.0.5",
"@types/jsdom": "21.1.1",
"@types/jsonld": "1.5.8",
"@types/jsrsasign": "10.5.8",
"@types/mime-types": "2.1.1",
- "@types/node": "18.15.11",
+ "@types/node": "18.16.3",
"@types/node-fetch": "3.0.3",
"@types/nodemailer": "6.4.7",
"@types/oauth": "0.9.1",
@@ -180,7 +192,7 @@
"@types/rename": "1.0.4",
"@types/sanitize-html": "2.9.0",
"@types/semver": "7.3.13",
- "@types/sharp": "0.31.1",
+ "@types/sharp": "0.32.0",
"@types/sinonjs__fake-timers": "8.1.2",
"@types/tinycolor2": "1.4.3",
"@types/tmp": "0.2.3",
@@ -190,11 +202,11 @@
"@types/web-push": "3.3.2",
"@types/websocket": "1.0.5",
"@types/ws": "8.5.4",
- "@typescript-eslint/eslint-plugin": "5.57.1",
- "@typescript-eslint/parser": "5.57.1",
+ "@typescript-eslint/eslint-plugin": "5.59.2",
+ "@typescript-eslint/parser": "5.59.2",
"aws-sdk-client-mock": "^2.1.1",
"cross-env": "7.0.3",
- "eslint": "8.37.0",
+ "eslint": "8.39.0",
"eslint-plugin-import": "2.27.5",
"execa": "6.1.0",
"jest": "29.5.0",
diff --git a/packages/backend/src/GlobalModule.ts b/packages/backend/src/GlobalModule.ts
index 174d0d8beb..5fb4e8ef3c 100644
--- a/packages/backend/src/GlobalModule.ts
+++ b/packages/backend/src/GlobalModule.ts
@@ -1,7 +1,8 @@
import { setTimeout } from 'node:timers/promises';
import { Global, Inject, Module } from '@nestjs/common';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import { DataSource } from 'typeorm';
+import { MeiliSearch } from 'meilisearch';
import { DI } from './di-symbols.js';
import { loadConfig } from './config.js';
import { createPostgresDataSource } from './postgres.js';
@@ -22,10 +23,25 @@ const $db: Provider = {
inject: [DI.config],
};
+const $meilisearch: Provider = {
+ provide: DI.meilisearch,
+ useFactory: (config) => {
+ if (config.meilisearch) {
+ return new MeiliSearch({
+ host: `${config.meilisearch.ssl ? 'https' : 'http' }://${config.meilisearch.host}:${config.meilisearch.port}`,
+ apiKey: config.meilisearch.apiKey,
+ });
+ } else {
+ return null;
+ }
+ },
+ inject: [DI.config],
+};
+
const $redis: Provider = {
provide: DI.redis,
useFactory: (config) => {
- return new Redis({
+ return new Redis.Redis({
port: config.redis.port,
host: config.redis.host,
family: config.redis.family == null ? 0 : config.redis.family,
@@ -40,7 +56,7 @@ const $redis: Provider = {
const $redisForPub: Provider = {
provide: DI.redisForPub,
useFactory: (config) => {
- const redis = new Redis({
+ const redis = new Redis.Redis({
port: config.redisForPubsub.port,
host: config.redisForPubsub.host,
family: config.redisForPubsub.family == null ? 0 : config.redisForPubsub.family,
@@ -56,7 +72,7 @@ const $redisForPub: Provider = {
const $redisForSub: Provider = {
provide: DI.redisForSub,
useFactory: (config) => {
- const redis = new Redis({
+ const redis = new Redis.Redis({
port: config.redisForPubsub.port,
host: config.redisForPubsub.host,
family: config.redisForPubsub.family == null ? 0 : config.redisForPubsub.family,
@@ -73,8 +89,8 @@ const $redisForSub: Provider = {
@Global()
@Module({
imports: [RepositoryModule],
- providers: [$config, $db, $redis, $redisForPub, $redisForSub],
- exports: [$config, $db, $redis, $redisForPub, $redisForSub, RepositoryModule],
+ providers: [$config, $db, $meilisearch, $redis, $redisForPub, $redisForSub],
+ exports: [$config, $db, $meilisearch, $redis, $redisForPub, $redisForSub, RepositoryModule],
})
export class GlobalModule implements OnApplicationShutdown {
constructor(
diff --git a/packages/backend/src/boot/common.ts b/packages/backend/src/boot/common.ts
index 279a1fe59d..45ded5495c 100644
--- a/packages/backend/src/boot/common.ts
+++ b/packages/backend/src/boot/common.ts
@@ -34,4 +34,6 @@ export async function jobQueue() {
jobQueue.get(QueueProcessorService).start();
jobQueue.get(ChartManagementService).start();
+
+ return jobQueue;
}
diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts
index e4f7601fa9..b41fb603bb 100644
--- a/packages/backend/src/config.ts
+++ b/packages/backend/src/config.ts
@@ -4,7 +4,7 @@
import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url';
-import { dirname } from 'node:path';
+import { dirname, resolve } from 'node:path';
import * as yaml from 'js-yaml';
/**
@@ -57,13 +57,11 @@ export type Source = {
db?: number;
prefix?: string;
};
- elasticsearch: {
+ meilisearch?: {
host: string;
- port: number;
+ port: string;
+ apiKey: string;
ssl?: boolean;
- user?: string;
- pass?: string;
- index?: string;
};
proxy?: string;
@@ -84,8 +82,10 @@ export type Source = {
deliverJobConcurrency?: number;
inboxJobConcurrency?: number;
+ relashionshipJobConcurrency?: number;
deliverJobPerSec?: number;
inboxJobPerSec?: number;
+ relashionshipJobPerSec?: number;
deliverJobMaxAttempts?: number;
inboxJobMaxAttempts?: number;
@@ -132,9 +132,11 @@ const dir = `${_dirname}/../../../.config`;
/**
* Path of configuration file
*/
-const path = process.env.NODE_ENV === 'test'
- ? `${dir}/test.yml`
- : `${dir}/default.yml`;
+const path = process.env.MISSKEY_CONFIG_YML
+ ? resolve(dir, process.env.MISSKEY_CONFIG_YML)
+ : process.env.NODE_ENV === 'test'
+ ? resolve(dir, 'test.yml')
+ : resolve(dir, 'default.yml');
export function loadConfig() {
const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../built/meta.json`, 'utf-8'));
diff --git a/packages/backend/src/const.ts b/packages/backend/src/const.ts
index 6c7f214214..ee1a9a3093 100644
--- a/packages/backend/src/const.ts
+++ b/packages/backend/src/const.ts
@@ -56,6 +56,11 @@ export const FILE_TYPE_BROWSERSAFE = [
'audio/webm',
'audio/aac',
+
+ // see https://github.com/misskey-dev/misskey/pull/10686
+ 'audio/flac',
+ 'audio/wav',
+ // backward compatibility
'audio/x-flac',
'audio/vnd.wave',
];
diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts
index 3f2a19b771..ab11785e28 100644
--- a/packages/backend/src/core/AccountMoveService.ts
+++ b/packages/backend/src/core/AccountMoveService.ts
@@ -1,55 +1,90 @@
import { Inject, Injectable } from '@nestjs/common';
-import { IsNull } from 'typeorm';
+import { IsNull, In, MoreThan, Not } from 'typeorm';
import { bindThis } from '@/decorators.js';
import { DI } from '@/di-symbols.js';
-import type { LocalUser } from '@/models/entities/User.js';
-import { User } from '@/models/entities/User.js';
-import type { FollowingsRepository, UsersRepository } from '@/models/index.js';
+import type { Config } from '@/config.js';
+import type { LocalUser, RemoteUser } from '@/models/entities/User.js';
+import type { BlockingsRepository, FollowingsRepository, InstancesRepository, Muting, MutingsRepository, UserListJoiningsRepository, UsersRepository } from '@/models/index.js';
+import type { RelationshipJobData, ThinUser } from '@/queue/types.js';
+import type { User } from '@/models/entities/User.js';
+import { IdService } from '@/core/IdService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
-import { UserFollowingService } from '@/core/UserFollowingService.js';
+import { QueueService } from '@/core/QueueService.js';
+import { RelayService } from '@/core/RelayService.js';
+import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
-import { AccountUpdateService } from '@/core/AccountUpdateService.js';
-import { RelayService } from '@/core/RelayService.js';
+import { CacheService } from '@/core/CacheService.js';
+import { ProxyAccountService } from '@/core/ProxyAccountService.js';
+import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
+import { MetaService } from '@/core/MetaService.js';
+import InstanceChart from '@/core/chart/charts/instance.js';
+import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js';
@Injectable()
export class AccountMoveService {
constructor(
+ @Inject(DI.config)
+ private config: Config,
+
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.followingsRepository)
private followingsRepository: FollowingsRepository,
+ @Inject(DI.blockingsRepository)
+ private blockingsRepository: BlockingsRepository,
+
+ @Inject(DI.mutingsRepository)
+ private mutingsRepository: MutingsRepository,
+
+ @Inject(DI.userListJoiningsRepository)
+ private userListJoiningsRepository: UserListJoiningsRepository,
+
+ @Inject(DI.instancesRepository)
+ private instancesRepository: InstancesRepository,
+
private userEntityService: UserEntityService,
+ private idService: IdService,
+ private apPersonService: ApPersonService,
private apRendererService: ApRendererService,
private apDeliverManagerService: ApDeliverManagerService,
private globalEventService: GlobalEventService,
- private userFollowingService: UserFollowingService,
- private accountUpdateService: AccountUpdateService,
+ private proxyAccountService: ProxyAccountService,
+ private perUserFollowingChart: PerUserFollowingChart,
+ private federatedInstanceService: FederatedInstanceService,
+ private instanceChart: InstanceChart,
+ private metaService: MetaService,
private relayService: RelayService,
+ private cacheService: CacheService,
+ private queueService: QueueService,
) {
}
/**
- * Move a local account to a remote account.
+ * Move a local account to a new account.
*
* After delivering Move activity, its local followers unfollow the old account and then follow the new one.
*/
@bindThis
- public async moveToRemote(src: LocalUser, dst: User): Promise<unknown> {
- // Make sure that the destination is a remote account.
- if (this.userEntityService.isLocalUser(dst)) throw new Error('move destiantion is not remote');
- if (!dst.uri) throw new Error('destination uri is empty');
+ public async moveFromLocal(src: LocalUser, dst: LocalUser | RemoteUser): Promise<unknown> {
+ const srcUri = this.userEntityService.getUserUri(src);
+ const dstUri = this.userEntityService.getUserUri(dst);
// add movedToUri to indicate that the user has moved
- const update = {} as Partial<User>;
- update.alsoKnownAs = src.alsoKnownAs?.concat([dst.uri]) ?? [dst.uri];
- update.movedToUri = dst.uri;
+ const update = {} as Partial<LocalUser>;
+ update.alsoKnownAs = src.alsoKnownAs?.includes(dstUri) ? src.alsoKnownAs : src.alsoKnownAs?.concat([dstUri]) ?? [dstUri];
+ update.movedToUri = dstUri;
+ update.movedAt = new Date();
await this.usersRepository.update(src.id, update);
+ Object.assign(src, update);
+
+ // Update cache
+ this.cacheService.uriPersonCache.set(srcUri, src);
const srcPerson = await this.apRendererService.renderPerson(src);
const updateAct = this.apRendererService.addContext(this.apRendererService.renderUpdate(srcPerson, src));
@@ -64,51 +99,249 @@ export class AccountMoveService {
const iObj = await this.userEntityService.pack<true, true>(src.id, src, { detail: true, includeSecrets: true });
this.globalEventService.publishMainStream(src.id, 'meUpdated', iObj);
- // follow the new account and unfollow the old one
- const followings = await this.followingsRepository.find({
- relations: {
- follower: true,
- },
+ // Unfollow after 24 hours
+ const followings = await this.followingsRepository.findBy({
+ followerId: src.id,
+ });
+ this.queueService.createDelayedUnfollowJob(followings.map(following => ({
+ from: { id: src.id },
+ to: { id: following.followeeId },
+ })), process.env.NODE_ENV === 'test' ? 10000 : 1000 * 60 * 60 * 24);
+
+ await this.postMoveProcess(src, dst);
+
+ return iObj;
+ }
+
+ @bindThis
+ public async postMoveProcess(src: User, dst: User): Promise<void> {
+ // Copy blockings and mutings, and update lists
+ try {
+ await Promise.all([
+ this.copyBlocking(src, dst),
+ this.copyMutings(src, dst),
+ this.updateLists(src, dst),
+ ]);
+ } catch {
+ /* skip if any error happens */
+ }
+
+ // follow the new account
+ const proxy = await this.proxyAccountService.fetch();
+ const followings = await this.followingsRepository.findBy({
+ followeeId: src.id,
+ followerHost: IsNull(), // follower is local
+ followerId: proxy ? Not(proxy.id) : undefined,
+ });
+ const followJobs = followings.map(following => ({
+ from: { id: following.followerId },
+ to: { id: dst.id },
+ })) as RelationshipJobData[];
+
+ // Decrease following count instead of unfollowing.
+ try {
+ await this.adjustFollowingCounts(followJobs.map(job => job.from.id), src);
+ } catch {
+ /* skip if any error happens */
+ }
+
+ // Should be queued because this can cause a number of follow per one move.
+ this.queueService.createFollowJob(followJobs);
+ }
+
+ @bindThis
+ public async copyBlocking(src: ThinUser, dst: ThinUser): Promise<void> {
+ // Followers shouldn't overlap with blockers, but the destination account, different from the blockee (i.e., old account), may have followed the local user before moving.
+ // So block the destination account here.
+ const srcBlockings = await this.blockingsRepository.findBy({ blockeeId: src.id });
+ const dstBlockings = await this.blockingsRepository.findBy({ blockeeId: dst.id });
+ const blockerIds = dstBlockings.map(blocking => blocking.blockerId);
+ // reblock the destination account
+ const blockJobs: RelationshipJobData[] = [];
+ for (const blocking of srcBlockings) {
+ if (blockerIds.includes(blocking.blockerId)) continue; // skip if already blocked
+ blockJobs.push({ from: { id: blocking.blockerId }, to: { id: dst.id } });
+ }
+ // no need to unblock the old account because it may be still functional
+ this.queueService.createBlockJob(blockJobs);
+ }
+
+ @bindThis
+ public async copyMutings(src: ThinUser, dst: ThinUser): Promise<void> {
+ // Insert new mutings with the same values except mutee
+ const oldMutings = await this.mutingsRepository.findBy([
+ { muteeId: src.id, expiresAt: IsNull() },
+ { muteeId: src.id, expiresAt: MoreThan(new Date()) },
+ ]);
+ if (oldMutings.length === 0) return;
+
+ // Check if the destination account is already indefinitely muted by the muter
+ const existingMutingsMuterUserIds = await this.mutingsRepository.findBy(
+ { muteeId: dst.id, expiresAt: IsNull() },
+ ).then(mutings => mutings.map(muting => muting.muterId));
+
+ const newMutings: Map<string, { muterId: string; muteeId: string; createdAt: Date; expiresAt: Date | null; }> = new Map();
+
+ // é‡è¤‡ã—ãªã„よã†ã«IDを生æˆ
+ const genId = (): string => {
+ let id: string;
+ do {
+ id = this.idService.genId();
+ } while (newMutings.has(id));
+ return id;
+ };
+ for (const muting of oldMutings) {
+ if (existingMutingsMuterUserIds.includes(muting.muterId)) continue; // skip if already muted indefinitely
+ newMutings.set(genId(), {
+ ...muting,
+ createdAt: new Date(),
+ muteeId: dst.id,
+ });
+ }
+
+ const arrayToInsert = Array.from(newMutings.entries()).map(entry => ({ ...entry[1], id: entry[0] }));
+ await this.mutingsRepository.insert(arrayToInsert);
+ }
+
+ /**
+ * Update lists while moving accounts.
+ * - No removal of the old account from the lists
+ * - Users number limit is not checked
+ *
+ * @param src ThinUser (old account)
+ * @param dst User (new account)
+ * @returns Promise<void>
+ */
+ @bindThis
+ public async updateLists(src: ThinUser, dst: User): Promise<void> {
+ // Return if there is no list to be updated.
+ const oldJoinings = await this.userListJoiningsRepository.find({
where: {
- followeeId: src.id,
- followerHost: IsNull(), // follower is local
+ userId: src.id,
},
});
- for (const following of followings) {
- if (!following.follower) continue;
- try {
- await this.userFollowingService.follow(following.follower, dst);
- await this.userFollowingService.unfollow(following.follower, src);
- } catch {
- /* empty */
+ if (oldJoinings.length === 0) return;
+
+ const existingUserListIds = await this.userListJoiningsRepository.find({
+ where: {
+ userId: dst.id,
+ },
+ }).then(joinings => joinings.map(joining => joining.userListId));
+
+ const newJoinings: Map<string, { createdAt: Date; userId: string; userListId: string; }> = new Map();
+
+ // é‡è¤‡ã—ãªã„よã†ã«IDを生æˆ
+ const genId = (): string => {
+ let id: string;
+ do {
+ id = this.idService.genId();
+ } while (newJoinings.has(id));
+ return id;
+ };
+ for (const joining of oldJoinings) {
+ if (existingUserListIds.includes(joining.userListId)) continue; // skip if dst exists in this user's list
+ newJoinings.set(genId(), {
+ createdAt: new Date(),
+ userId: dst.id,
+ userListId: joining.userListId,
+ });
+ }
+
+ const arrayToInsert = Array.from(newJoinings.entries()).map(entry => ({ ...entry[1], id: entry[0] }));
+ await this.userListJoiningsRepository.insert(arrayToInsert);
+
+ // Have the proxy account follow the new account in the same way as UserListService.push
+ if (this.userEntityService.isRemoteUser(dst)) {
+ const proxy = await this.proxyAccountService.fetch();
+ if (proxy) {
+ this.queueService.createFollowJob([{ from: { id: proxy.id }, to: { id: dst.id } }]);
}
}
+ }
- return iObj;
+ @bindThis
+ private async adjustFollowingCounts(localFollowerIds: string[], oldAccount: User): Promise<void> {
+ if (localFollowerIds.length === 0) return;
+
+ // Set the old account's following and followers counts to 0.
+ await this.usersRepository.update({ id: oldAccount.id }, { followersCount: 0, followingCount: 0 });
+
+ // Decrease following counts of local followers by 1.
+ await this.usersRepository.decrement({ id: In(localFollowerIds) }, 'followingCount', 1);
+
+ // Decrease follower counts of local followees by 1.
+ const oldFollowings = await this.followingsRepository.findBy({ followerId: oldAccount.id });
+ if (oldFollowings.length > 0) {
+ await this.usersRepository.decrement({ id: In(oldFollowings.map(following => following.followeeId)) }, 'followersCount', 1);
+ }
+
+ // Update instance stats by decreasing remote followers count by the number of local followers who were following the old account.
+ if (this.userEntityService.isRemoteUser(oldAccount)) {
+ this.federatedInstanceService.fetch(oldAccount.host).then(async i => {
+ this.instancesRepository.decrement({ id: i.id }, 'followersCount', localFollowerIds.length);
+ if ((await this.metaService.fetch()).enableChartsForFederatedInstances) {
+ this.instanceChart.updateFollowers(i.host, false);
+ }
+ });
+ }
+
+ // FIXME: expensive?
+ for (const followerId of localFollowerIds) {
+ this.perUserFollowingChart.update({ id: followerId, host: null }, oldAccount, false);
+ }
}
/**
- * Create an alias of an old remote account.
+ * dstユーザーã®alsoKnownAsã‚’fetchPersonã—ã¦ã„ãã€æœ¬å½“ã«movedToUrlã‚’dstã«æŒ‡å®šã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒå­˜åœ¨ã™ã‚‹ã®ã‹ã‚’調ã¹ã‚‹
*
- * The user's new profile will be published to the followers.
+ * @param dst movedToUrlを指定ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼
+ * @param check
+ * @param instant checkãŒtrueã§ã‚ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒæœ€åˆã«è¦‹ã¤ã‹ã£ãŸã‚‰å³åº§ã«returnã™ã‚‹ã‹ã©ã†ã‹
+ * @returns Promise<LocalUser | RemoteUser | null>
*/
@bindThis
- public async createAlias(me: LocalUser, updates: Partial<User>): Promise<unknown> {
- await this.usersRepository.update(me.id, updates);
+ public async validateAlsoKnownAs(
+ dst: LocalUser | RemoteUser,
+ check: (oldUser: LocalUser | RemoteUser | null, newUser: LocalUser | RemoteUser) => boolean | Promise<boolean> = () => true,
+ instant = false,
+ ): Promise<LocalUser | RemoteUser | null> {
+ let resultUser: LocalUser | RemoteUser | null = null;
- // Publish meUpdated event
- const iObj = await this.userEntityService.pack<true, true>(me.id, me, {
- detail: true,
- includeSecrets: true,
- });
- this.globalEventService.publishMainStream(me.id, 'meUpdated', iObj);
-
- if (me.isLocked === false) {
- await this.userFollowingService.acceptAllFollowRequests(me);
+ if (this.userEntityService.isRemoteUser(dst)) {
+ if ((new Date()).getTime() - (dst.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) {
+ await this.apPersonService.updatePerson(dst.uri);
+ }
+ dst = await this.apPersonService.fetchPerson(dst.uri) ?? dst;
}
- this.accountUpdateService.publishToFollowers(me.id);
+ if (!dst.alsoKnownAs || dst.alsoKnownAs.length === 0) return null;
- return iObj;
+ const dstUri = this.userEntityService.getUserUri(dst);
+
+ for (const srcUri of dst.alsoKnownAs) {
+ try {
+ let src = await this.apPersonService.fetchPerson(srcUri);
+ if (!src) continue; // oldAccountを探ã—ã¦ã‚‚ã“ã®ã‚µãƒ¼ãƒãƒ¼ã«å­˜åœ¨ã—ãªã„å ´åˆã¯ãƒ•ォロー関係もãªã„ã¨ã„ã†ã“ã¨ãªã®ã§ã‚¹ãƒ«ãƒ¼
+
+ if (this.userEntityService.isRemoteUser(dst)) {
+ if ((new Date()).getTime() - (src.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) {
+ await this.apPersonService.updatePerson(srcUri);
+ }
+
+ src = await this.apPersonService.fetchPerson(srcUri) ?? src;
+ }
+
+ if (src.movedToUri === dstUri) {
+ if (await check(resultUser, src)) {
+ resultUser = src;
+ }
+ if (instant && resultUser) return resultUser;
+ }
+ } catch {
+ /* skip if any error happens */
+ }
+ }
+
+ return resultUser;
}
}
diff --git a/packages/backend/src/core/AchievementService.ts b/packages/backend/src/core/AchievementService.ts
index 1ca38d8bb0..9e223f1492 100644
--- a/packages/backend/src/core/AchievementService.ts
+++ b/packages/backend/src/core/AchievementService.ts
@@ -64,6 +64,7 @@ export const ACHIEVEMENT_TYPES = [
'iLoveMisskey',
'foundTreasure',
'client30min',
+ 'client60min',
'noteDeletedWithin1min',
'postedAtLateNight',
'postedAt0min0sec',
diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts
index 166c78f479..2d4226a32d 100644
--- a/packages/backend/src/core/AntennaService.ts
+++ b/packages/backend/src/core/AntennaService.ts
@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import type { Antenna } from '@/models/entities/Antenna.js';
import type { Note } from '@/models/entities/Note.js';
import type { User } from '@/models/entities/User.js';
diff --git a/packages/backend/src/core/AppLockService.ts b/packages/backend/src/core/AppLockService.ts
index ee179b7f01..8dd805552b 100644
--- a/packages/backend/src/core/AppLockService.ts
+++ b/packages/backend/src/core/AppLockService.ts
@@ -1,7 +1,7 @@
import { promisify } from 'node:util';
import { Inject, Injectable } from '@nestjs/common';
import redisLock from 'redis-lock';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts
index 561face5c3..cf1e81ffc8 100644
--- a/packages/backend/src/core/CacheService.ts
+++ b/packages/backend/src/core/CacheService.ts
@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import type { BlockingsRepository, ChannelFollowingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, UserProfile, UserProfilesRepository, UsersRepository } from '@/models/index.js';
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
import type { LocalUser, User } from '@/models/entities/User.js';
diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts
index 8775536e4a..d3a1b1b024 100644
--- a/packages/backend/src/core/CoreModule.ts
+++ b/packages/backend/src/core/CoreModule.ts
@@ -50,6 +50,7 @@ import { WebhookService } from './WebhookService.js';
import { ProxyAccountService } from './ProxyAccountService.js';
import { UtilityService } from './UtilityService.js';
import { FileInfoService } from './FileInfoService.js';
+import { SearchService } from './SearchService.js';
import { ChartLoggerService } from './chart/ChartLoggerService.js';
import FederationChart from './chart/charts/federation.js';
import NotesChart from './chart/charts/notes.js';
@@ -171,6 +172,8 @@ const $VideoProcessingService: Provider = { provide: 'VideoProcessingService', u
const $WebhookService: Provider = { provide: 'WebhookService', useExisting: WebhookService };
const $UtilityService: Provider = { provide: 'UtilityService', useExisting: UtilityService };
const $FileInfoService: Provider = { provide: 'FileInfoService', useExisting: FileInfoService };
+const $SearchService: Provider = { provide: 'SearchService', useExisting: SearchService };
+
const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService };
const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart };
const $NotesChart: Provider = { provide: 'NotesChart', useExisting: NotesChart };
@@ -295,6 +298,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
WebhookService,
UtilityService,
FileInfoService,
+ SearchService,
ChartLoggerService,
FederationChart,
NotesChart,
@@ -413,6 +417,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$WebhookService,
$UtilityService,
$FileInfoService,
+ $SearchService,
$ChartLoggerService,
$FederationChart,
$NotesChart,
@@ -532,6 +537,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
WebhookService,
UtilityService,
FileInfoService,
+ SearchService,
FederationChart,
NotesChart,
UsersChart,
@@ -649,6 +655,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$WebhookService,
$UtilityService,
$FileInfoService,
+ $SearchService,
$FederationChart,
$NotesChart,
$UsersChart,
diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts
index 185171dee2..93557ce617 100644
--- a/packages/backend/src/core/CustomEmojiService.ts
+++ b/packages/backend/src/core/CustomEmojiService.ts
@@ -1,6 +1,6 @@
import { Inject, Injectable } from '@nestjs/common';
import { DataSource, In, IsNull } from 'typeorm';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import { IdService } from '@/core/IdService.js';
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
@@ -197,6 +197,22 @@ export class CustomEmojiService {
emojis: await this.emojiEntityService.packDetailedMany(ids),
});
}
+
+ @bindThis
+ public async setLicenseBulk(ids: Emoji['id'][], license: string | null) {
+ await this.emojisRepository.update({
+ id: In(ids),
+ }, {
+ updatedAt: new Date(),
+ license: license,
+ });
+
+ this.localEmojisCache.refresh();
+
+ this.globalEventService.publishBroadcastStream('emojiUpdated', {
+ emojis: await this.emojiEntityService.packDetailedMany(ids),
+ });
+ }
@bindThis
public async delete(id: Emoji['id']) {
diff --git a/packages/backend/src/core/DownloadService.ts b/packages/backend/src/core/DownloadService.ts
index bd999c67da..bd535c6032 100644
--- a/packages/backend/src/core/DownloadService.ts
+++ b/packages/backend/src/core/DownloadService.ts
@@ -86,9 +86,13 @@ export class DownloadService {
const contentDisposition = res.headers['content-disposition'];
if (contentDisposition != null) {
- const parsed = parse(contentDisposition);
- if (parsed.parameters.filename) {
- filename = parsed.parameters.filename;
+ try {
+ const parsed = parse(contentDisposition);
+ if (parsed.parameters.filename) {
+ filename = parsed.parameters.filename;
+ }
+ } catch (e) {
+ this.logger.warn(`Failed to parse content-disposition: ${contentDisposition}`, { stack: e });
}
}
}).on('downloadProgress', (progress: Got.Progress) => {
diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts
index c6258474ec..1483b55469 100644
--- a/packages/backend/src/core/DriveService.ts
+++ b/packages/backend/src/core/DriveService.ts
@@ -59,6 +59,8 @@ type AddFileArgs = {
uri?: string | null;
/** Mark file as sensitive */
sensitive?: boolean | null;
+ /** Extension to force */
+ ext?: string | null;
requestIp?: string | null;
requestHeaders?: Record<string, string> | null;
@@ -125,7 +127,7 @@ export class DriveService {
/***
* Save file
* @param path Path for original
- * @param name Name for original
+ * @param name Name for original (should be extention corrected)
* @param type Content-Type for original
* @param hash Hash for original
* @param size Size for original
@@ -151,7 +153,7 @@ export class DriveService {
}
// æ‹¡å¼µå­ã‹ã‚‰Content-Typeを設定ã—ã¦ãã†ãªæŒ™å‹•を示ã™ã‚ªãƒ–ジェクトストレージ (upcloud?) も存在ã™ã‚‹ã®ã§ã€
- // 許å¯ã•れã¦ã„るファイル形å¼ã§ã—ã‹æ‹¡å¼µå­ã‚’ã¤ã‘ãªã„
+ // 許å¯ã•れã¦ã„るファイル形å¼ã§ã—ã‹URLã«æ‹¡å¼µå­ã‚’ã¤ã‘ãªã„
if (!FILE_TYPE_BROWSERSAFE.includes(type)) {
ext = '';
}
@@ -173,7 +175,7 @@ export class DriveService {
//#region Uploads
this.registerLogger.info(`uploading original: ${key}`);
const uploads = [
- this.upload(key, fs.createReadStream(path), type, ext, name),
+ this.upload(key, fs.createReadStream(path), type, null, name),
];
if (alts.webpublic) {
@@ -189,7 +191,7 @@ export class DriveService {
thumbnailUrl = `${ baseUrl }/${ thumbnailKey }`;
this.registerLogger.info(`uploading thumbnail: ${thumbnailKey}`);
- uploads.push(this.upload(thumbnailKey, alts.thumbnail.data, alts.thumbnail.type, alts.thumbnail.ext));
+ uploads.push(this.upload(thumbnailKey, alts.thumbnail.data, alts.thumbnail.type, alts.thumbnail.ext, `${name}.thumbnail`));
}
await Promise.all(uploads);
@@ -396,8 +398,9 @@ export class DriveService {
);
}
+ // Expire oldest file (without avatar or banner) of remote user
@bindThis
- private async deleteOldFile(user: RemoteUser) {
+ private async expireOldFile(user: RemoteUser, driveCapacity: number) {
const q = this.driveFilesRepository.createQueryBuilder('file')
.where('file.userId = :userId', { userId: user.id })
.andWhere('file.isLink = FALSE');
@@ -410,12 +413,17 @@ export class DriveService {
q.andWhere('file.id != :bannerId', { bannerId: user.bannerId });
}
+ //This selete is hard coded, be careful if change database schema
+ q.addSelect('SUM("file"."size") OVER (ORDER BY "file"."id" DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)', 'acc_usage');
q.orderBy('file.id', 'ASC');
- const oldFile = await q.getOne();
+ const fileList = await q.getRawMany();
+ const exceedFileIds = fileList.filter((x: any) => x.acc_usage > driveCapacity).map((x: any) => x.file_id);
- if (oldFile) {
- this.deleteFile(oldFile, true);
+ for (const fileId of exceedFileIds) {
+ const file = await this.driveFilesRepository.findOneBy({ id: fileId });
+ if (file == null) continue;
+ this.deleteFile(file, true);
}
}
@@ -437,10 +445,16 @@ export class DriveService {
sensitive = null,
requestIp = null,
requestHeaders = null,
+ ext = null,
}: AddFileArgs): Promise<DriveFile> {
let skipNsfwCheck = false;
const instance = await this.metaService.fetch();
- if (user == null) skipNsfwCheck = true;
+ const userRoleNSFW = user && (await this.roleService.getUserPolicies(user.id)).alwaysMarkNsfw;
+ if (user == null) {
+ skipNsfwCheck = true;
+ } else if (userRoleNSFW) {
+ skipNsfwCheck = true;
+ }
if (instance.sensitiveMediaDetection === 'none') skipNsfwCheck = true;
if (user && instance.sensitiveMediaDetection === 'local' && this.userEntityService.isRemoteUser(user)) skipNsfwCheck = true;
if (user && instance.sensitiveMediaDetection === 'remote' && this.userEntityService.isLocalUser(user)) skipNsfwCheck = true;
@@ -468,7 +482,7 @@ export class DriveService {
// DriveFile.nameã¯256文字, validateFileNameã¯200文字制é™ã§ã‚ã‚‹ãŸã‚ã€
// extを付加ã—ã¦ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã®æ–‡å­—数制é™ã«å½“ãŸã‚‹ã“ã¨ã¯ã¾ãšãªã„
(name && this.driveFileEntityService.validateFileName(name)) ? name : 'untitled',
- info.type.ext,
+ ext ?? info.type.ext,
);
if (user && !force) {
@@ -489,22 +503,19 @@ export class DriveService {
//#region Check drive usage
if (user && !isLink) {
const usage = await this.driveFileEntityService.calcDriveUsageOf(user);
+ const isLocalUser = this.userEntityService.isLocalUser(user);
const policies = await this.roleService.getUserPolicies(user.id);
const driveCapacity = 1024 * 1024 * policies.driveCapacityMb;
this.registerLogger.debug('drive capacity override applied');
this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`);
- this.registerLogger.debug(`drive usage is ${usage} (max: ${driveCapacity})`);
-
// If usage limit exceeded
- if (usage + info.size > driveCapacity) {
- if (this.userEntityService.isLocalUser(user)) {
+ if (driveCapacity < usage + info.size) {
+ if (isLocalUser) {
throw new IdentifiableError('c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6', 'No free space.');
- } else {
- // (ã‚¢ãƒã‚¿ãƒ¼ã¾ãŸã¯ãƒãƒŠãƒ¼ã‚’å«ã¾ãš)最もå¤ã„ファイルを削除ã™ã‚‹
- this.deleteOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as RemoteUser);
}
+ await this.expireOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as RemoteUser, driveCapacity - info.size);
}
}
//#endregion
@@ -565,6 +576,7 @@ export class DriveService {
if (info.sensitive && profile!.autoSensitive) file.isSensitive = true;
if (info.sensitive && instance.setSensitiveFlagAutomatically) file.isSensitive = true;
+ if (userRoleNSFW) file.isSensitive = true;
if (url !== null) {
file.src = url;
diff --git a/packages/backend/src/core/FederatedInstanceService.ts b/packages/backend/src/core/FederatedInstanceService.ts
index 56660ae0d0..2049bd4c60 100644
--- a/packages/backend/src/core/FederatedInstanceService.ts
+++ b/packages/backend/src/core/FederatedInstanceService.ts
@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import type { InstancesRepository } from '@/models/index.js';
import type { Instance } from '@/models/entities/Instance.js';
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
@@ -23,8 +23,8 @@ export class FederatedInstanceService {
private idService: IdService,
) {
this.federatedInstanceCache = new RedisKVCache<Instance | null>(this.redisClient, 'federatedInstance', {
- lifetime: 1000 * 60 * 60 * 24, // 24h
- memoryCacheLifetime: 1000 * 60 * 30, // 30m
+ lifetime: 1000 * 60 * 30, // 30m
+ memoryCacheLifetime: 1000 * 60 * 3, // 3m
fetcher: (key) => this.instancesRepository.findOneBy({ host: key }),
toRedisConverter: (value) => JSON.stringify(value),
fromRedisConverter: (value) => {
@@ -65,15 +65,18 @@ export class FederatedInstanceService {
}
@bindThis
- public async updateCachePartial(host: string, data: Partial<Instance>): Promise<void> {
- host = this.utilityService.toPuny(host);
-
- const cached = await this.federatedInstanceCache.get(host);
- if (cached == null) return;
+ public async update(id: Instance['id'], data: Partial<Instance>): Promise<void> {
+ const result = await this.instancesRepository.createQueryBuilder().update()
+ .set(data)
+ .where('id = :id', { id })
+ .returning('*')
+ .execute()
+ .then((response) => {
+ return response.raw[0];
+ });
+
+ const updated = result.raw[0];
- this.federatedInstanceCache.set(host, {
- ...cached,
- ...data,
- });
+ this.federatedInstanceCache.set(updated.host, updated);
}
}
diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts
index bbc8b4332e..8103d5afe9 100644
--- a/packages/backend/src/core/FetchInstanceMetadataService.ts
+++ b/packages/backend/src/core/FetchInstanceMetadataService.ts
@@ -10,6 +10,7 @@ import { DI } from '@/di-symbols.js';
import { LoggerService } from '@/core/LoggerService.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';
import { bindThis } from '@/decorators.js';
+import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import type { DOMWindow } from 'jsdom';
type NodeInfo = {
@@ -42,6 +43,7 @@ export class FetchInstanceMetadataService {
private appLockService: AppLockService,
private httpRequestService: HttpRequestService,
private loggerService: LoggerService,
+ private federatedInstanceService: FederatedInstanceService,
) {
this.logger = this.loggerService.getLogger('metadata', 'cyan');
}
@@ -96,7 +98,7 @@ export class FetchInstanceMetadataService {
if (favicon) updates.faviconUrl = favicon;
if (themeColor) updates.themeColor = themeColor;
- await this.instancesRepository.update(instance.id, updates);
+ await this.federatedInstanceService.update(instance.id, updates);
this.logger.succ(`Successfuly updated metadata of ${instance.host}`);
} catch (e) {
diff --git a/packages/backend/src/core/FileInfoService.ts b/packages/backend/src/core/FileInfoService.ts
index e39b134b7e..b6cae5ea75 100644
--- a/packages/backend/src/core/FileInfoService.ts
+++ b/packages/backend/src/core/FileInfoService.ts
@@ -5,7 +5,7 @@ import * as stream from 'node:stream';
import * as util from 'node:util';
import { Injectable } from '@nestjs/common';
import { FSWatcher } from 'chokidar';
-import { fileTypeFromFile } from 'file-type';
+import * as fileType from 'file-type';
import FFmpeg from 'fluent-ffmpeg';
import isSvg from 'is-svg';
import probeImageSize from 'probe-image-size';
@@ -301,21 +301,34 @@ export class FileInfoService {
return fs.promises.access(path).then(() => true, () => false);
}
+ @bindThis
+ public fixMime(mime: string | fileType.MimeType): string {
+ // see https://github.com/misskey-dev/misskey/pull/10686
+ if (mime === "audio/x-flac") {
+ return "audio/flac";
+ }
+ if (mime === "audio/vnd.wave") {
+ return "audio/wav";
+ }
+
+ return mime;
+ }
+
/**
* Detect MIME Type and extension
*/
@bindThis
public async detectType(path: string): Promise<{
- mime: string;
- ext: string | null;
-}> {
+ mime: string;
+ ext: string | null;
+ }> {
// Check 0 byte
const fileSize = await this.getFileSize(path);
if (fileSize === 0) {
return TYPE_OCTET_STREAM;
}
- const type = await fileTypeFromFile(path);
+ const type = await fileType.fileTypeFromFile(path);
if (type) {
// XMLã¯SVGã‹ã‚‚ã—れãªã„
@@ -324,7 +337,7 @@ export class FileInfoService {
}
return {
- mime: type.mime,
+ mime: this.fixMime(type.mime),
ext: type.ext,
};
}
diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts
index 2c2687a90c..0ed5241148 100644
--- a/packages/backend/src/core/GlobalEventService.ts
+++ b/packages/backend/src/core/GlobalEventService.ts
@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import type { User } from '@/models/entities/User.js';
import type { Note } from '@/models/entities/Note.js';
import type { UserList } from '@/models/entities/UserList.js';
diff --git a/packages/backend/src/core/MetaService.ts b/packages/backend/src/core/MetaService.ts
index 1322927c2c..0b861be8d0 100644
--- a/packages/backend/src/core/MetaService.ts
+++ b/packages/backend/src/core/MetaService.ts
@@ -1,6 +1,6 @@
import { Inject, Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import { Meta } from '@/models/entities/Meta.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 79629cb2a8..364976e4a7 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -1,7 +1,7 @@
import { setImmediate } from 'node:timers/promises';
import * as mfm from 'mfm-js';
import { In, DataSource } from 'typeorm';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
import { extractMentions } from '@/misc/extract-mentions.js';
import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js';
@@ -46,6 +46,7 @@ import { bindThis } from '@/decorators.js';
import { DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js';
import { RoleService } from '@/core/RoleService.js';
import { MetaService } from '@/core/MetaService.js';
+import { SearchService } from '@/core/SearchService.js';
const mutedWordsCache = new MemorySingleCache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5);
@@ -198,6 +199,7 @@ export class NoteCreateService implements OnApplicationShutdown {
private apRendererService: ApRendererService,
private roleService: RoleService,
private metaService: MetaService,
+ private searchService: SearchService,
private notesChart: NotesChart,
private perUserNotesChart: PerUserNotesChart,
private activeUsersChart: ActiveUsersChart,
@@ -728,17 +730,9 @@ export class NoteCreateService implements OnApplicationShutdown {
@bindThis
private index(note: Note) {
- if (note.text == null || this.config.elasticsearch == null) return;
- /*
- es!.index({
- index: this.config.elasticsearch.index ?? 'misskey_note',
- id: note.id.toString(),
- body: {
- text: normalizeForSearch(note.text),
- userId: note.userId,
- userHost: note.userHost,
- },
- });*/
+ if (note.text == null && note.cw == null) return;
+
+ this.searchService.indexNote(note);
}
@bindThis
diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts
index 6691c42836..a245908c98 100644
--- a/packages/backend/src/core/NotificationService.ts
+++ b/packages/backend/src/core/NotificationService.ts
@@ -1,5 +1,5 @@
import { setTimeout } from 'node:timers/promises';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
import { In } from 'typeorm';
import { DI } from '@/di-symbols.js';
@@ -111,7 +111,7 @@ export class NotificationService implements OnApplicationShutdown {
// 2秒経ã£ã¦ã‚‚(今回作æˆã—ãŸ)é€šçŸ¥ãŒæ—¢èª­ã«ãªã‚‰ãªã‹ã£ãŸã‚‰ã€Œæœªèª­ã®é€šçŸ¥ãŒã‚りã¾ã™ã‚ˆã€ã‚¤ãƒ™ãƒ³ãƒˆã‚’発行ã™ã‚‹
setTimeout(2000, 'unread notification', { signal: this.#shutdownController.signal }).then(async () => {
const latestReadNotificationId = await this.redisClient.get(`latestReadNotification:${notifieeId}`);
- if (latestReadNotificationId && (latestReadNotificationId >= await redisIdPromise)) return;
+ if (latestReadNotificationId && (latestReadNotificationId >= (await redisIdPromise)!)) return;
this.globalEventService.publishMainStream(notifieeId, 'unreadNotification', packed);
this.pushNotificationService.pushNotification(notifieeId, 'notification', packed);
diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts
index 9b44cf6413..a4c569bdec 100644
--- a/packages/backend/src/core/PushNotificationService.ts
+++ b/packages/backend/src/core/PushNotificationService.ts
@@ -1,6 +1,6 @@
import { Inject, Injectable } from '@nestjs/common';
import push from 'web-push';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import type { Packed } from '@/misc/json-schema';
diff --git a/packages/backend/src/core/QueueModule.ts b/packages/backend/src/core/QueueModule.ts
index bac85d7a15..d4905a5f88 100644
--- a/packages/backend/src/core/QueueModule.ts
+++ b/packages/backend/src/core/QueueModule.ts
@@ -78,7 +78,7 @@ const $db: Provider = {
const $relationship: Provider = {
provide: 'queue:relationship',
- useFactory: (config: Config) => q(config, 'relationship'),
+ useFactory: (config: Config) => q(config, 'relationship', config.relashionshipJobPerSec ?? 64),
inject: [DI.config],
};
diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts
index 375ac49911..b4ffffecc0 100644
--- a/packages/backend/src/core/QueueService.ts
+++ b/packages/backend/src/core/QueueService.ts
@@ -1,15 +1,16 @@
import { Inject, Injectable } from '@nestjs/common';
import { v4 as uuid } from 'uuid';
+import Bull from 'bull';
import type { IActivity } from '@/core/activitypub/type.js';
import type { DriveFile } from '@/models/entities/DriveFile.js';
import type { Webhook, webhookEventTypes } from '@/models/entities/Webhook.js';
import type { Config } from '@/config.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
+import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js';
import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, RelationshipQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js';
import type { DbJobData, RelationshipJobData, ThinUser } from '../queue/types.js';
import type httpSignature from '@peertube/http-signature';
-import Bull from 'bull';
@Injectable()
export class QueueService {
@@ -153,6 +154,16 @@ export class QueueService {
}
@bindThis
+ public createExportAntennasJob(user: ThinUser) {
+ return this.dbQueue.add('exportAntennas', {
+ user: { id: user.id },
+ }, {
+ removeOnComplete: true,
+ removeOnFail: true,
+ });
+ }
+
+ @bindThis
public createImportFollowingJob(user: ThinUser, fileId: DriveFile['id']) {
return this.dbQueue.add('importFollowing', {
user: { id: user.id },
@@ -236,6 +247,17 @@ export class QueueService {
}
@bindThis
+ public createImportAntennasJob(user: ThinUser, antenna: Antenna) {
+ return this.dbQueue.add('importAntennas', {
+ user: { id: user.id },
+ antenna,
+ }, {
+ removeOnComplete: true,
+ removeOnFail: true,
+ });
+ }
+
+ @bindThis
public createDeleteAccountJob(user: ThinUser, opts: { soft?: boolean; } = {}) {
return this.dbQueue.add('deleteAccount', {
user: { id: user.id },
@@ -259,6 +281,12 @@ export class QueueService {
}
@bindThis
+ public createDelayedUnfollowJob(followings: { from: ThinUser, to: ThinUser, requestId?: string }[], delay: number) {
+ const jobs = followings.map(rel => this.generateRelationshipJobData('unfollow', rel, { delay }));
+ return this.relationshipQueue.addBulk(jobs);
+ }
+
+ @bindThis
public createBlockJob(blockings: { from: ThinUser, to: ThinUser, silent?: boolean }[]) {
const jobs = blockings.map(rel => this.generateRelationshipJobData('block', rel));
return this.relationshipQueue.addBulk(jobs);
@@ -271,7 +299,7 @@ export class QueueService {
}
@bindThis
- private generateRelationshipJobData(name: 'follow' | 'unfollow' | 'block' | 'unblock', data: RelationshipJobData): {
+ private generateRelationshipJobData(name: 'follow' | 'unfollow' | 'block' | 'unblock', data: RelationshipJobData, opts: Bull.JobOptions = {}): {
name: string,
data: RelationshipJobData,
opts: Bull.JobOptions,
@@ -287,6 +315,7 @@ export class QueueService {
opts: {
removeOnComplete: true,
removeOnFail: true,
+ ...opts,
},
};
}
diff --git a/packages/backend/src/core/RemoteUserResolveService.ts b/packages/backend/src/core/RemoteUserResolveService.ts
index b72dce5180..ff68c24219 100644
--- a/packages/backend/src/core/RemoteUserResolveService.ts
+++ b/packages/backend/src/core/RemoteUserResolveService.ts
@@ -4,7 +4,7 @@ import chalk from 'chalk';
import { IsNull } from 'typeorm';
import { DI } from '@/di-symbols.js';
import type { UsersRepository } from '@/models/index.js';
-import type { RemoteUser, User } from '@/models/entities/User.js';
+import type { LocalUser, RemoteUser } from '@/models/entities/User.js';
import type { Config } from '@/config.js';
import type Logger from '@/logger.js';
import { UtilityService } from '@/core/UtilityService.js';
@@ -33,7 +33,7 @@ export class RemoteUserResolveService {
}
@bindThis
- public async resolveUser(username: string, host: string | null): Promise<User> {
+ public async resolveUser(username: string, host: string | null): Promise<LocalUser | RemoteUser> {
const usernameLower = username.toLowerCase();
if (host == null) {
@@ -44,7 +44,7 @@ export class RemoteUserResolveService {
} else {
return u;
}
- });
+ }) as LocalUser;
}
host = this.utilityService.toPuny(host);
@@ -57,7 +57,7 @@ export class RemoteUserResolveService {
} else {
return u;
}
- });
+ }) as LocalUser;
}
const user = await this.usersRepository.findOneBy({ usernameLower, host }) as RemoteUser | null;
@@ -109,7 +109,7 @@ export class RemoteUserResolveService {
if (u == null) {
throw new Error('user not found');
} else {
- return u;
+ return u as LocalUser | RemoteUser;
}
});
}
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 2a4271aa98..68087ccc3b 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import { In } from 'typeorm';
import type { Role, RoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/index.js';
import { MemoryKVCache, MemorySingleCache } from '@/misc/cache.js';
@@ -25,6 +25,7 @@ export type RolePolicies = {
canSearchNotes: boolean;
canHideAds: boolean;
driveCapacityMb: number;
+ alwaysMarkNsfw: boolean;
pinLimit: number;
antennaLimit: number;
wordMuteLimit: number;
@@ -45,6 +46,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
canSearchNotes: false,
canHideAds: false,
driveCapacityMb: 100,
+ alwaysMarkNsfw: false,
pinLimit: 5,
antennaLimit: 5,
wordMuteLimit: 200,
@@ -279,6 +281,7 @@ export class RoleService implements OnApplicationShutdown {
canSearchNotes: calc('canSearchNotes', vs => vs.some(v => v === true)),
canHideAds: calc('canHideAds', vs => vs.some(v => v === true)),
driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)),
+ alwaysMarkNsfw: calc('alwaysMarkNsfw', vs => vs.some(v => v === true)),
pinLimit: calc('pinLimit', vs => Math.max(...vs)),
antennaLimit: calc('antennaLimit', vs => Math.max(...vs)),
wordMuteLimit: calc('wordMuteLimit', vs => Math.max(...vs)),
diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts
new file mode 100644
index 0000000000..e68fde088d
--- /dev/null
+++ b/packages/backend/src/core/SearchService.ts
@@ -0,0 +1,178 @@
+import { Inject, Injectable } from '@nestjs/common';
+import { In } from 'typeorm';
+import { DI } from '@/di-symbols.js';
+import type { Config } from '@/config.js';
+import { bindThis } from '@/decorators.js';
+import { Note } from '@/models/entities/Note.js';
+import { User } from '@/models/index.js';
+import type { NotesRepository } from '@/models/index.js';
+import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
+import { QueryService } from '@/core/QueryService.js';
+import { IdService } from '@/core/IdService.js';
+import type { Index, MeiliSearch } from 'meilisearch';
+
+type K = string;
+type V = string | number | boolean;
+type Q =
+ { op: '=', k: K, v: V } |
+ { op: '!=', k: K, v: V } |
+ { op: '>', k: K, v: number } |
+ { op: '<', k: K, v: number } |
+ { op: '>=', k: K, v: number } |
+ { op: '<=', k: K, v: number } |
+ { op: 'and', qs: Q[] } |
+ { op: 'or', qs: Q[] } |
+ { op: 'not', q: Q };
+
+function compileValue(value: V): string {
+ if (typeof value === 'string') {
+ return `'${value}'`; // TODO: escape
+ } else if (typeof value === 'number') {
+ return value.toString();
+ } else if (typeof value === 'boolean') {
+ return value.toString();
+ }
+ throw new Error('unrecognized value');
+}
+
+function compileQuery(q: Q): string {
+ switch (q.op) {
+ case '=': return `(${q.k} = ${compileValue(q.v)})`;
+ case '!=': return `(${q.k} != ${compileValue(q.v)})`;
+ case '>': return `(${q.k} > ${compileValue(q.v)})`;
+ case '<': return `(${q.k} < ${compileValue(q.v)})`;
+ case '>=': return `(${q.k} >= ${compileValue(q.v)})`;
+ case '<=': return `(${q.k} <= ${compileValue(q.v)})`;
+ case 'and': return q.qs.length === 0 ? '' : `(${ q.qs.map(_q => compileQuery(_q)).join(' AND ') })`;
+ case 'or': return q.qs.length === 0 ? '' : `(${ q.qs.map(_q => compileQuery(_q)).join(' OR ') })`;
+ case 'not': return `(NOT ${compileQuery(q.q)})`;
+ default: throw new Error('unrecognized query operator');
+ }
+}
+
+@Injectable()
+export class SearchService {
+ private meilisearchNoteIndex: Index | null = null;
+
+ constructor(
+ @Inject(DI.config)
+ private config: Config,
+
+ @Inject(DI.meilisearch)
+ private meilisearch: MeiliSearch | null,
+
+ @Inject(DI.notesRepository)
+ private notesRepository: NotesRepository,
+
+ private queryService: QueryService,
+ private idService: IdService,
+ ) {
+ if (meilisearch) {
+ this.meilisearchNoteIndex = meilisearch.index('notes');
+ this.meilisearchNoteIndex.updateSettings({
+ searchableAttributes: [
+ 'text',
+ 'cw',
+ ],
+ sortableAttributes: [
+ 'createdAt',
+ ],
+ filterableAttributes: [
+ 'createdAt',
+ 'userId',
+ 'userHost',
+ 'channelId',
+ ],
+ typoTolerance: {
+ enabled: false,
+ },
+ pagination: {
+ maxTotalHits: 10000,
+ },
+ });
+ }
+ }
+
+ @bindThis
+ public async indexNote(note: Note): Promise<void> {
+ if (note.text == null && note.cw == null) return;
+ if (!['home', 'public'].includes(note.visibility)) return;
+
+ if (this.meilisearch) {
+ this.meilisearchNoteIndex!.addDocuments([{
+ id: note.id,
+ createdAt: note.createdAt.getTime(),
+ userId: note.userId,
+ userHost: note.userHost,
+ channelId: note.channelId,
+ cw: note.cw,
+ text: note.text,
+ }], {
+ primaryKey: 'id',
+ });
+ }
+ }
+
+ @bindThis
+ public async searchNote(q: string, me: User | null, opts: {
+ userId?: Note['userId'] | null;
+ channelId?: Note['channelId'] | null;
+ host?: string | null;
+ }, pagination: {
+ untilId?: Note['id'];
+ sinceId?: Note['id'];
+ limit?: number;
+ }): Promise<Note[]> {
+ if (this.meilisearch) {
+ const filter: Q = {
+ op: 'and',
+ qs: [],
+ };
+ if (pagination.untilId) filter.qs.push({ op: '<', k: 'createdAt', v: this.idService.parse(pagination.untilId).date.getTime() });
+ if (pagination.sinceId) filter.qs.push({ op: '>', k: 'createdAt', v: this.idService.parse(pagination.sinceId).date.getTime() });
+ if (opts.userId) filter.qs.push({ op: '=', k: 'userId', v: opts.userId });
+ if (opts.channelId) filter.qs.push({ op: '=', k: 'channelId', v: opts.channelId });
+ if (opts.host) {
+ if (opts.host === '.') {
+ // TODO: MeilisearchãŒ2023/05/07ç¾åœ¨å€¤ãŒNULLã‹ã©ã†ã‹ã®ã‚¯ã‚¨ãƒªãŒæ›¸ã‘ãªã„
+ } else {
+ filter.qs.push({ op: '=', k: 'userHost', v: opts.host });
+ }
+ }
+ const res = await this.meilisearchNoteIndex!.search(q, {
+ sort: ['createdAt:desc'],
+ matchingStrategy: 'all',
+ attributesToRetrieve: ['id', 'createdAt'],
+ filter: compileQuery(filter),
+ limit: pagination.limit,
+ });
+ if (res.hits.length === 0) return [];
+ const notes = await this.notesRepository.findBy({
+ id: In(res.hits.map(x => x.id)),
+ });
+ return notes.sort((a, b) => a.id > b.id ? -1 : 1);
+ } else {
+ const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), pagination.sinceId, pagination.untilId);
+
+ if (opts.userId) {
+ query.andWhere('note.userId = :userId', { userId: opts.userId });
+ } else if (opts.channelId) {
+ query.andWhere('note.channelId = :channelId', { channelId: opts.channelId });
+ }
+
+ query
+ .andWhere('note.text ILIKE :q', { q: `%${ sqlLikeEscape(q) }%` })
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser');
+
+ this.queryService.generateVisibilityQuery(query, me);
+ if (me) this.queryService.generateMutedUserQuery(query, me);
+ if (me) this.queryService.generateBlockedUserQuery(query, me);
+
+ return await query.take(pagination.limit).getMany();
+ }
+ }
+}
diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts
index d7bc05b8bd..29eb65fda4 100644
--- a/packages/backend/src/core/SignupService.ts
+++ b/packages/backend/src/core/SignupService.ts
@@ -13,8 +13,9 @@ import { UsedUsername } from '@/models/entities/UsedUsername.js';
import generateUserToken from '@/misc/generate-native-user-token.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { bindThis } from '@/decorators.js';
-import UsersChart from './chart/charts/users.js';
-import { UtilityService } from './UtilityService.js';
+import UsersChart from '@/core/chart/charts/users.js';
+import { UtilityService } from '@/core/UtilityService.js';
+import { MetaService } from '@/core/MetaService.js';
@Injectable()
export class SignupService {
@@ -34,6 +35,7 @@ export class SignupService {
private utilityService: UtilityService,
private userEntityService: UserEntityService,
private idService: IdService,
+ private metaService: MetaService,
private usersChart: UsersChart,
) {
}
@@ -44,6 +46,7 @@ export class SignupService {
password?: string | null;
passwordHash?: UserProfile['password'] | null;
host?: string | null;
+ ignorePreservedUsernames?: boolean;
}) {
const { username, password, passwordHash, host } = opts;
let hash = passwordHash;
@@ -76,7 +79,17 @@ export class SignupService {
if (await this.usedUsernamesRepository.findOneBy({ username: username.toLowerCase() })) {
throw new Error('USED_USERNAME');
}
-
+
+ const isTheFirstUser = (await this.usersRepository.countBy({ host: IsNull() })) === 0;
+
+ if (!opts.ignorePreservedUsernames && !isTheFirstUser) {
+ const instance = await this.metaService.fetch(true);
+ const isPreserved = instance.preservedUsernames.map(x => x.toLowerCase()).includes(username.toLowerCase());
+ if (isPreserved) {
+ throw new Error('USED_USERNAME');
+ }
+ }
+
const keyPair = await new Promise<string[]>((res, rej) =>
generateKeyPair('rsa', {
modulusLength: 4096,
@@ -112,9 +125,7 @@ export class SignupService {
usernameLower: username.toLowerCase(),
host: this.utilityService.toPunyNullable(host),
token: secret,
- isRoot: (await this.usersRepository.countBy({
- host: IsNull(),
- })) === 0,
+ isRoot: isTheFirstUser,
}));
await transactionalEntityManager.save(new UserKeypair({
diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts
index a8eded6733..7d90bc2c08 100644
--- a/packages/backend/src/core/UserFollowingService.ts
+++ b/packages/backend/src/core/UserFollowingService.ts
@@ -1,6 +1,6 @@
import { Inject, Injectable, OnModuleInit, forwardRef } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
-import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js';
+import type { LocalUser, PartialLocalUser, PartialRemoteUser, RemoteUser, User } from '@/models/entities/User.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { QueueService } from '@/core/QueueService.js';
import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js';
@@ -22,6 +22,8 @@ import { MetaService } from '@/core/MetaService.js';
import { CacheService } from '@/core/CacheService.js';
import type { Config } from '@/config.js';
import Logger from '../logger.js';
+import { IsNull } from 'typeorm';
+import { AccountMoveService } from '@/core/AccountMoveService.js';
const logger = new Logger('following/create');
@@ -73,6 +75,7 @@ export class UserFollowingService implements OnModuleInit {
private federatedInstanceService: FederatedInstanceService,
private webhookService: WebhookService,
private apRendererService: ApRendererService,
+ private accountMoveService: AccountMoveService,
private perUserFollowingChart: PerUserFollowingChart,
private instanceChart: InstanceChart,
) {
@@ -87,7 +90,7 @@ export class UserFollowingService implements OnModuleInit {
const [follower, followee] = await Promise.all([
this.usersRepository.findOneByOrFail({ id: _follower.id }),
this.usersRepository.findOneByOrFail({ id: _followee.id }),
- ]);
+ ]) as [LocalUser | RemoteUser, LocalUser | RemoteUser];
// check blocking
const [blocking, blocked] = await Promise.all([
@@ -137,6 +140,20 @@ export class UserFollowingService implements OnModuleInit {
if (followed) autoAccept = true;
}
+ // Automatically accept if the follower is an account who has moved and the locked followee had accepted the old account.
+ if (followee.isLocked && !autoAccept) {
+ autoAccept = !!(await this.accountMoveService.validateAlsoKnownAs(
+ follower,
+ (oldSrc, newSrc) => this.followingsRepository.exist({
+ where: {
+ followeeId: followee.id,
+ followerId: newSrc.id,
+ },
+ }),
+ true,
+ ));
+ }
+
if (!autoAccept) {
await this.createFollowRequest(follower, followee, requestId);
return;
@@ -210,32 +227,40 @@ export class UserFollowingService implements OnModuleInit {
this.globalEventService.publishInternalEvent('follow', { followerId: follower.id, followeeId: followee.id });
- //#region Increment counts
- await Promise.all([
- this.usersRepository.increment({ id: follower.id }, 'followingCount', 1),
- this.usersRepository.increment({ id: followee.id }, 'followersCount', 1),
+ const [followeeUser, followerUser] = await Promise.all([
+ this.usersRepository.findOneByOrFail({ id: followee.id }),
+ this.usersRepository.findOneByOrFail({ id: follower.id }),
]);
- //#endregion
- //#region Update instance stats
- if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
- this.federatedInstanceService.fetch(follower.host).then(async i => {
- this.instancesRepository.increment({ id: i.id }, 'followingCount', 1);
- if ((await this.metaService.fetch()).enableChartsForFederatedInstances) {
- this.instanceChart.updateFollowing(i.host, true);
- }
- });
- } else if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
- this.federatedInstanceService.fetch(followee.host).then(async i => {
- this.instancesRepository.increment({ id: i.id }, 'followersCount', 1);
- if ((await this.metaService.fetch()).enableChartsForFederatedInstances) {
- this.instanceChart.updateFollowers(i.host, true);
- }
- });
- }
- //#endregion
+ // Neither followee nor follower has moved.
+ if (!followeeUser.movedToUri && !followerUser.movedToUri) {
+ //#region Increment counts
+ await Promise.all([
+ this.usersRepository.increment({ id: follower.id }, 'followingCount', 1),
+ this.usersRepository.increment({ id: followee.id }, 'followersCount', 1),
+ ]);
+ //#endregion
+
+ //#region Update instance stats
+ if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
+ this.federatedInstanceService.fetch(follower.host).then(async i => {
+ this.instancesRepository.increment({ id: i.id }, 'followingCount', 1);
+ if ((await this.metaService.fetch()).enableChartsForFederatedInstances) {
+ this.instanceChart.updateFollowing(i.host, true);
+ }
+ });
+ } else if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
+ this.federatedInstanceService.fetch(followee.host).then(async i => {
+ this.instancesRepository.increment({ id: i.id }, 'followersCount', 1);
+ if ((await this.metaService.fetch()).enableChartsForFederatedInstances) {
+ this.instanceChart.updateFollowers(i.host, true);
+ }
+ });
+ }
+ //#endregion
- this.perUserFollowingChart.update(follower, followee, true);
+ this.perUserFollowingChart.update(follower, followee, true);
+ }
// Publish follow event
if (this.userEntityService.isLocalUser(follower) && !silent) {
@@ -283,12 +308,18 @@ export class UserFollowingService implements OnModuleInit {
},
silent = false,
): Promise<void> {
- const following = await this.followingsRepository.findOneBy({
- followerId: follower.id,
- followeeId: followee.id,
+ const following = await this.followingsRepository.findOne({
+ relations: {
+ follower: true,
+ followee: true,
+ },
+ where: {
+ followerId: follower.id,
+ followeeId: followee.id,
+ }
});
- if (following == null) {
+ if (following === null || !following.follower || !following.followee) {
logger.warn('フォロー解除ãŒãƒªã‚¯ã‚¨ã‚¹ãƒˆã•れã¾ã—ãŸãŒãƒ•ォローã—ã¦ã„ã¾ã›ã‚“ã§ã—ãŸ');
return;
}
@@ -297,7 +328,7 @@ export class UserFollowingService implements OnModuleInit {
this.cacheService.userFollowingsCache.refresh(follower.id);
- this.decrementFollowing(follower, followee);
+ this.decrementFollowing(following.follower, following.followee);
// Publish unfollow event
if (!silent && this.userEntityService.isLocalUser(follower)) {
@@ -316,50 +347,87 @@ export class UserFollowingService implements OnModuleInit {
}
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
- const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower));
+ const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower as PartialLocalUser, followee as PartialRemoteUser), follower));
this.queueService.deliver(follower, content, followee.inbox, false);
}
if (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower)) {
// local user has null host
- const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee), followee));
+ const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower as PartialRemoteUser, followee as PartialLocalUser), followee));
this.queueService.deliver(followee, content, follower.inbox, false);
}
}
@bindThis
private async decrementFollowing(
- follower: { id: User['id']; host: User['host']; },
- followee: { id: User['id']; host: User['host']; },
+ follower: User,
+ followee: User,
): Promise<void> {
this.globalEventService.publishInternalEvent('unfollow', { followerId: follower.id, followeeId: followee.id });
- //#region Decrement following / followers counts
- await Promise.all([
- this.usersRepository.decrement({ id: follower.id }, 'followingCount', 1),
- this.usersRepository.decrement({ id: followee.id }, 'followersCount', 1),
- ]);
- //#endregion
+ // Neither followee nor follower has moved.
+ if (!follower.movedToUri && !followee.movedToUri) {
+ //#region Decrement following / followers counts
+ await Promise.all([
+ this.usersRepository.decrement({ id: follower.id }, 'followingCount', 1),
+ this.usersRepository.decrement({ id: followee.id }, 'followersCount', 1),
+ ]);
+ //#endregion
- //#region Update instance stats
- if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
- this.federatedInstanceService.fetch(follower.host).then(async i => {
- this.instancesRepository.decrement({ id: i.id }, 'followingCount', 1);
- if ((await this.metaService.fetch()).enableChartsForFederatedInstances) {
- this.instanceChart.updateFollowing(i.host, false);
- }
- });
- } else if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
- this.federatedInstanceService.fetch(followee.host).then(async i => {
- this.instancesRepository.decrement({ id: i.id }, 'followersCount', 1);
- if ((await this.metaService.fetch()).enableChartsForFederatedInstances) {
- this.instanceChart.updateFollowers(i.host, false);
- }
- });
- }
- //#endregion
+ //#region Update instance stats
+ if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
+ this.federatedInstanceService.fetch(follower.host).then(async i => {
+ this.instancesRepository.decrement({ id: i.id }, 'followingCount', 1);
+ if ((await this.metaService.fetch()).enableChartsForFederatedInstances) {
+ this.instanceChart.updateFollowing(i.host, false);
+ }
+ });
+ } else if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
+ this.federatedInstanceService.fetch(followee.host).then(async i => {
+ this.instancesRepository.decrement({ id: i.id }, 'followersCount', 1);
+ if ((await this.metaService.fetch()).enableChartsForFederatedInstances) {
+ this.instanceChart.updateFollowers(i.host, false);
+ }
+ });
+ }
+ //#endregion
- this.perUserFollowingChart.update(follower, followee, false);
+ this.perUserFollowingChart.update(follower, followee, false);
+ } else {
+ // Adjust following/followers counts
+ for (const user of [follower, followee]) {
+ if (user.movedToUri) continue; // No need to update if the user has already moved.
+
+ const nonMovedFollowees = await this.followingsRepository.count({
+ relations: {
+ followee: true,
+ },
+ where: {
+ followerId: user.id,
+ followee: {
+ movedToUri: IsNull(),
+ }
+ }
+ });
+ const nonMovedFollowers = await this.followingsRepository.count({
+ relations: {
+ follower: true,
+ },
+ where: {
+ followeeId: user.id,
+ follower: {
+ movedToUri: IsNull(),
+ }
+ }
+ });
+ await this.usersRepository.update(
+ { id: user.id },
+ { followingCount: nonMovedFollowees, followersCount: nonMovedFollowers },
+ );
+ }
+
+ // TODO: adjust charts
+ }
}
@bindThis
@@ -415,7 +483,7 @@ export class UserFollowingService implements OnModuleInit {
}
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
- const content = this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee, requestId ?? `${this.config.url}/follows/${followRequest.id}`));
+ const content = this.apRendererService.addContext(this.apRendererService.renderFollow(follower as PartialLocalUser, followee as PartialRemoteUser, requestId ?? `${this.config.url}/follows/${followRequest.id}`));
this.queueService.deliver(follower, content, followee.inbox, false);
}
}
@@ -430,7 +498,7 @@ export class UserFollowingService implements OnModuleInit {
},
): Promise<void> {
if (this.userEntityService.isRemoteUser(followee)) {
- const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower));
+ const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower as PartialLocalUser | PartialRemoteUser, followee as PartialRemoteUser), follower));
if (this.userEntityService.isLocalUser(follower)) { // 本æ¥ã“ã®ãƒã‚§ãƒƒã‚¯ã¯ä¸è¦ã ã‘ã©TSã«æ€’られるã®ã§
this.queueService.deliver(follower, content, followee.inbox, false);
@@ -475,7 +543,7 @@ export class UserFollowingService implements OnModuleInit {
await this.insertFollowingDoc(followee, follower);
if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
- const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, request.requestId!), followee));
+ const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee as PartialLocalUser, request.requestId!), followee));
this.queueService.deliver(followee, content, follower.inbox, false);
}
@@ -562,15 +630,22 @@ export class UserFollowingService implements OnModuleInit {
*/
@bindThis
private async removeFollow(followee: Both, follower: Both): Promise<void> {
- const following = await this.followingsRepository.findOneBy({
- followeeId: followee.id,
- followerId: follower.id,
+ const following = await this.followingsRepository.findOne({
+ relations: {
+ followee: true,
+ follower: true,
+ },
+ where: {
+ followeeId: followee.id,
+ followerId: follower.id,
+ }
});
- if (!following) return;
+ if (!following || !following.followee || !following.follower) return;
await this.followingsRepository.delete(following.id);
- this.decrementFollowing(follower, followee);
+
+ this.decrementFollowing(following.follower, following.followee);
}
/**
diff --git a/packages/backend/src/core/UserKeypairService.ts b/packages/backend/src/core/UserKeypairService.ts
index 22a9fb2b8e..72c35c529c 100644
--- a/packages/backend/src/core/UserKeypairService.ts
+++ b/packages/backend/src/core/UserKeypairService.ts
@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import type { User } from '@/models/entities/User.js';
import type { UserKeypairsRepository } from '@/models/index.js';
import { RedisKVCache } from '@/misc/cache.js';
diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts
index d00bb89c76..b197d335d8 100644
--- a/packages/backend/src/core/UserSuspendService.ts
+++ b/packages/backend/src/core/UserSuspendService.ts
@@ -35,7 +35,7 @@ export class UserSuspendService {
if (this.userEntityService.isLocalUser(user)) {
// 知り得る全SharedInboxã«Deleteé…ä¿¡
- const content = this.apRendererService.addContext(this.apRendererService.renderDelete(`${this.config.url}/users/${user.id}`, user));
+ const content = this.apRendererService.addContext(this.apRendererService.renderDelete(this.userEntityService.genLocalUserUri(user.id), user));
const queue: string[] = [];
@@ -65,7 +65,7 @@ export class UserSuspendService {
if (this.userEntityService.isLocalUser(user)) {
// 知り得る全SharedInboxã«Undo Deleteé…ä¿¡
- const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderDelete(`${this.config.url}/users/${user.id}`, user), user));
+ const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderDelete(this.userEntityService.genLocalUserUri(user.id), user), user));
const queue: string[] = [];
diff --git a/packages/backend/src/core/WebfingerService.ts b/packages/backend/src/core/WebfingerService.ts
index 69df2d0c1b..3ee7990643 100644
--- a/packages/backend/src/core/WebfingerService.ts
+++ b/packages/backend/src/core/WebfingerService.ts
@@ -43,7 +43,8 @@ export class WebfingerService {
const m = query.match(/^([^@]+)@(.*)/);
if (m) {
const hostname = m[2];
- return `https://${hostname}/.well-known/webfinger?` + urlQuery({ resource: `acct:${query}` });
+ const useHttp = process.env.MISSKEY_WEBFINGER_USE_HTTP && process.env.MISSKEY_WEBFINGER_USE_HTTP.toLowerCase() === 'true';
+ return `http${useHttp ? '' : 's'}://${hostname}/.well-known/webfinger?${urlQuery({ resource: `acct:${query}` })}`;
}
throw new Error(`Invalid query (${query})`);
diff --git a/packages/backend/src/core/WebhookService.ts b/packages/backend/src/core/WebhookService.ts
index 926115613b..57baade777 100644
--- a/packages/backend/src/core/WebhookService.ts
+++ b/packages/backend/src/core/WebhookService.ts
@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import type { WebhooksRepository } from '@/models/index.js';
import type { Webhook } from '@/models/entities/Webhook.js';
import { DI } from '@/di-symbols.js';
diff --git a/packages/backend/src/core/activitypub/ApDbResolverService.ts b/packages/backend/src/core/activitypub/ApDbResolverService.ts
index 4b032be89a..2b404ebeca 100644
--- a/packages/backend/src/core/activitypub/ApDbResolverService.ts
+++ b/packages/backend/src/core/activitypub/ApDbResolverService.ts
@@ -8,7 +8,7 @@ import type { UserPublickey } from '@/models/entities/UserPublickey.js';
import { CacheService } from '@/core/CacheService.js';
import type { Note } from '@/models/entities/Note.js';
import { bindThis } from '@/decorators.js';
-import { RemoteUser, User } from '@/models/entities/User.js';
+import { LocalUser, RemoteUser } from '@/models/entities/User.js';
import { getApId } from './type.js';
import { ApPersonService } from './models/ApPersonService.js';
import type { IObject } from './type.js';
@@ -101,7 +101,7 @@ export class ApDbResolverService {
* AP Person => Misskey User in DB
*/
@bindThis
- public async getUserFromApId(value: string | IObject): Promise<User | null> {
+ public async getUserFromApId(value: string | IObject): Promise<LocalUser | RemoteUser | null> {
const parsed = this.parseUri(value);
if (parsed.local) {
@@ -109,11 +109,11 @@ export class ApDbResolverService {
return await this.cacheService.userByIdCache.fetchMaybe(parsed.id, () => this.usersRepository.findOneBy({
id: parsed.id,
- }).then(x => x ?? undefined)) ?? null;
+ }).then(x => x ?? undefined)) as LocalUser | undefined ?? null;
} else {
return await this.cacheService.uriPersonCache.fetch(parsed.uri, () => this.usersRepository.findOneBy({
uri: parsed.uri,
- }));
+ })) as RemoteUser | null;
}
}
diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index 3fca0bb1fd..efef777fb0 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
-import { In, IsNull } from 'typeorm';
+import { In } from 'typeorm';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import { UserFollowingService } from '@/core/UserFollowingService.js';
@@ -13,13 +13,15 @@ import { concat, toArray, toSingle, unique } from '@/misc/prelude/array.js';
import { AppLockService } from '@/core/AppLockService.js';
import type Logger from '@/logger.js';
import { MetaService } from '@/core/MetaService.js';
+import { AccountMoveService } from '@/core/AccountMoveService.js';
import { IdService } from '@/core/IdService.js';
import { StatusError } from '@/misc/status-error.js';
import { UtilityService } from '@/core/UtilityService.js';
+import { CacheService } from '@/core/CacheService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { QueueService } from '@/core/QueueService.js';
-import type { UsersRepository, NotesRepository, FollowingsRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/index.js';
+import type { UsersRepository, NotesRepository, FollowingsRepository, AbuseUserReportsRepository, FollowRequestsRepository, } from '@/models/index.js';
import { bindThis } from '@/decorators.js';
import type { RemoteUser } from '@/models/entities/User.js';
import { getApHrefNullable, getApId, getApIds, getApType, getOneApHrefNullable, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js';
@@ -76,6 +78,8 @@ export class ApInboxService {
private apNoteService: ApNoteService,
private apPersonService: ApPersonService,
private apQuestionService: ApQuestionService,
+ private accountMoveService: AccountMoveService,
+ private cacheService: CacheService,
private queueService: QueueService,
) {
this.logger = this.apLoggerService.logger;
@@ -140,7 +144,7 @@ export class ApInboxService {
} else if (isFlag(activity)) {
await this.flag(actor, activity);
} else if (isMove(activity)) {
- //await this.move(actor, activity);
+ await this.move(actor, activity);
} else {
this.logger.warn(`unrecognized activity type: ${activity.type}`);
}
@@ -158,6 +162,7 @@ export class ApInboxService {
return 'skip: フォローã—よã†ã¨ã—ã¦ã„るユーザーã¯ãƒ­ãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã§ã¯ã‚りã¾ã›ã‚“';
}
+ // don't queue because the sender may attempt again when timeout
await this.userFollowingService.follow(actor, followee, activity.id);
return 'ok';
}
@@ -596,6 +601,7 @@ export class ApInboxService {
throw e;
});
+ // don't queue because the sender may attempt again when timeout
if (isFollow(object)) return await this.undoFollow(actor, object);
if (isBlock(object)) return await this.undoBlock(actor, object);
if (isLike(object)) return await this.undoLike(actor, object);
@@ -736,53 +742,7 @@ export class ApInboxService {
// fetch the new and old accounts
const targetUri = getApHrefNullable(activity.target);
if (!targetUri) return 'skip: invalid activity target';
- let new_acc = await this.apPersonService.resolvePerson(targetUri);
- let old_acc = await this.apPersonService.resolvePerson(actor.uri);
- // update them if they're remote
- if (new_acc.uri) await this.apPersonService.updatePerson(new_acc.uri);
- if (old_acc.uri) await this.apPersonService.updatePerson(old_acc.uri);
-
- // retrieve updated users
- new_acc = await this.apPersonService.resolvePerson(targetUri);
- old_acc = await this.apPersonService.resolvePerson(actor.uri);
-
- // check if alsoKnownAs of the new account is valid
- let isValidMove = true;
- if (old_acc.uri) {
- if (!new_acc.alsoKnownAs?.includes(old_acc.uri)) {
- isValidMove = false;
- }
- } else if (!new_acc.alsoKnownAs?.includes(old_acc.id)) {
- isValidMove = false;
- }
- if (!isValidMove) {
- return 'skip: accounts invalid';
- }
-
- // add target uri to movedToUri in order to indicate that the user has moved
- await this.usersRepository.update(old_acc.id, { movedToUri: targetUri });
-
- // follow the new account and unfollow the old one
- const followings = await this.followingsRepository.find({
- relations: {
- follower: true,
- },
- where: {
- followeeId: old_acc.id,
- followerHost: IsNull(), // follower is local
- },
- });
- for (const following of followings) {
- if (!following.follower) continue;
- try {
- await this.userFollowingService.follow(following.follower, new_acc);
- await this.userFollowingService.unfollow(following.follower, old_acc);
- } catch {
- /* empty */
- }
- }
-
- return 'ok';
+ return await this.apPersonService.updatePerson(actor.uri) ?? 'skip: nothing to do';
}
}
diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts
index 0b22aa9bcf..60e19bfca5 100644
--- a/packages/backend/src/core/activitypub/ApRendererService.ts
+++ b/packages/backend/src/core/activitypub/ApRendererService.ts
@@ -5,7 +5,7 @@ import { v4 as uuid } from 'uuid';
import * as mfm from 'mfm-js';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
-import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js';
+import type { PartialLocalUser, LocalUser, PartialRemoteUser, RemoteUser, User } from '@/models/entities/User.js';
import type { IMentionedRemoteUsers, Note } from '@/models/entities/Note.js';
import type { Blocking } from '@/models/entities/Blocking.js';
import type { Relay } from '@/models/entities/Relay.js';
@@ -66,7 +66,7 @@ export class ApRendererService {
public renderAccept(object: any, user: { id: User['id']; host: null }): IAccept {
return {
type: 'Accept',
- actor: `${this.config.url}/users/${user.id}`,
+ actor: this.userEntityService.genLocalUserUri(user.id),
object,
};
}
@@ -75,7 +75,7 @@ export class ApRendererService {
public renderAdd(user: LocalUser, target: any, object: any): IAdd {
return {
type: 'Add',
- actor: `${this.config.url}/users/${user.id}`,
+ actor: this.userEntityService.genLocalUserUri(user.id),
target,
object,
};
@@ -83,7 +83,7 @@ export class ApRendererService {
@bindThis
public renderAnnounce(object: any, note: Note): IAnnounce {
- const attributedTo = `${this.config.url}/users/${note.userId}`;
+ const attributedTo = this.userEntityService.genLocalUserUri(note.userId);
let to: string[] = [];
let cc: string[] = [];
@@ -103,7 +103,7 @@ export class ApRendererService {
return {
id: `${this.config.url}/notes/${note.id}/activity`,
- actor: `${this.config.url}/users/${note.userId}`,
+ actor: this.userEntityService.genLocalUserUri(note.userId),
type: 'Announce',
published: note.createdAt.toISOString(),
to,
@@ -126,7 +126,7 @@ export class ApRendererService {
return {
type: 'Block',
id: `${this.config.url}/blocks/${block.id}`,
- actor: `${this.config.url}/users/${block.blockerId}`,
+ actor: this.userEntityService.genLocalUserUri(block.blockerId),
object: block.blockee.uri,
};
}
@@ -135,7 +135,7 @@ export class ApRendererService {
public renderCreate(object: IObject, note: Note): ICreate {
const activity = {
id: `${this.config.url}/notes/${note.id}/activity`,
- actor: `${this.config.url}/users/${note.userId}`,
+ actor: this.userEntityService.genLocalUserUri(note.userId),
type: 'Create',
published: note.createdAt.toISOString(),
object,
@@ -151,7 +151,7 @@ export class ApRendererService {
public renderDelete(object: IObject | string, user: { id: User['id']; host: null }): IDelete {
return {
type: 'Delete',
- actor: `${this.config.url}/users/${user.id}`,
+ actor: this.userEntityService.genLocalUserUri(user.id),
object,
published: new Date().toISOString(),
};
@@ -188,7 +188,7 @@ export class ApRendererService {
public renderFlag(user: LocalUser, object: IObject | string, content: string): IFlag {
return {
type: 'Flag',
- actor: `${this.config.url}/users/${user.id}`,
+ actor: this.userEntityService.genLocalUserUri(user.id),
content,
object,
};
@@ -199,7 +199,7 @@ export class ApRendererService {
return {
id: `${this.config.url}/activities/follow-relay/${relay.id}`,
type: 'Follow',
- actor: `${this.config.url}/users/${relayActor.id}`,
+ actor: this.userEntityService.genLocalUserUri(relayActor.id),
object: 'https://www.w3.org/ns/activitystreams#Public',
};
}
@@ -210,21 +210,21 @@ export class ApRendererService {
*/
@bindThis
public async renderFollowUser(id: User['id']) {
- const user = await this.usersRepository.findOneByOrFail({ id: id });
- return this.userEntityService.isLocalUser(user) ? `${this.config.url}/users/${user.id}` : user.uri;
+ const user = await this.usersRepository.findOneByOrFail({ id: id }) as PartialLocalUser | PartialRemoteUser;
+ return this.userEntityService.getUserUri(user);
}
@bindThis
public renderFollow(
- follower: { id: User['id']; host: User['host']; uri: User['host'] },
- followee: { id: User['id']; host: User['host']; uri: User['host'] },
+ follower: PartialLocalUser | PartialRemoteUser,
+ followee: PartialLocalUser | PartialRemoteUser,
requestId?: string,
): IFollow {
return {
id: requestId ?? `${this.config.url}/follows/${follower.id}/${followee.id}`,
type: 'Follow',
- actor: this.userEntityService.isLocalUser(follower) ? `${this.config.url}/users/${follower.id}` : follower.uri!,
- object: this.userEntityService.isLocalUser(followee) ? `${this.config.url}/users/${followee.id}` : followee.uri!,
+ actor: this.userEntityService.getUserUri(follower)!,
+ object: this.userEntityService.getUserUri(followee)!,
};
}
@@ -252,7 +252,7 @@ export class ApRendererService {
return {
id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`,
type: 'Key',
- owner: `${this.config.url}/users/${user.id}`,
+ owner: this.userEntityService.genLocalUserUri(user.id),
publicKeyPem: createPublicKey(key.publicKey).export({
type: 'spki',
format: 'pem',
@@ -284,21 +284,21 @@ export class ApRendererService {
}
@bindThis
- public renderMention(mention: User): IApMention {
+ public renderMention(mention: PartialLocalUser | PartialRemoteUser): IApMention {
return {
type: 'Mention',
- href: this.userEntityService.isRemoteUser(mention) ? mention.uri! : `${this.config.url}/users/${(mention as LocalUser).id}`,
+ href: this.userEntityService.getUserUri(mention)!,
name: this.userEntityService.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as LocalUser).username}`,
};
}
@bindThis
public renderMove(
- src: { id: User['id']; host: User['host']; uri: User['host'] },
- dst: { id: User['id']; host: User['host']; uri: User['host'] },
+ src: PartialLocalUser | PartialRemoteUser,
+ dst: PartialLocalUser | PartialRemoteUser,
): IMove {
- const actor = this.userEntityService.isLocalUser(src) ? `${this.config.url}/users/${src.id}` : src.uri!;
- const target = this.userEntityService.isLocalUser(dst) ? `${this.config.url}/users/${dst.id}` : dst.uri!;
+ const actor = this.userEntityService.getUserUri(src)!;
+ const target = this.userEntityService.getUserUri(dst)!;
return {
id: `${this.config.url}/moves/${src.id}/${dst.id}`,
actor,
@@ -351,7 +351,7 @@ export class ApRendererService {
}
}
- const attributedTo = `${this.config.url}/users/${note.userId}`;
+ const attributedTo = this.userEntityService.genLocalUserUri(note.userId);
const mentions = (JSON.parse(note.mentionedRemoteUsers) as IMentionedRemoteUsers).map(x => x.uri);
@@ -376,7 +376,7 @@ export class ApRendererService {
}) : [];
const hashtagTags = (note.tags ?? []).map(tag => this.renderHashtag(tag));
- const mentionTags = mentionedUsers.map(u => this.renderMention(u));
+ const mentionTags = mentionedUsers.map(u => this.renderMention(u as LocalUser | RemoteUser));
const files = await getPromisedFiles(note.fileIds);
@@ -450,7 +450,7 @@ export class ApRendererService {
@bindThis
public async renderPerson(user: LocalUser) {
- const id = `${this.config.url}/users/${user.id}`;
+ const id = this.userEntityService.genLocalUserUri(user.id);
const isSystem = !!user.username.match(/\./);
const [avatar, banner, profile] = await Promise.all([
@@ -538,7 +538,7 @@ export class ApRendererService {
return {
type: 'Question',
id: `${this.config.url}/questions/${note.id}`,
- actor: `${this.config.url}/users/${user.id}`,
+ actor: this.userEntityService.genLocalUserUri(user.id),
content: note.text ?? '',
[poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({
name: text,
@@ -555,7 +555,7 @@ export class ApRendererService {
public renderReject(object: any, user: { id: User['id'] }): IReject {
return {
type: 'Reject',
- actor: `${this.config.url}/users/${user.id}`,
+ actor: this.userEntityService.genLocalUserUri(user.id),
object,
};
}
@@ -564,7 +564,7 @@ export class ApRendererService {
public renderRemove(user: { id: User['id'] }, target: any, object: any): IRemove {
return {
type: 'Remove',
- actor: `${this.config.url}/users/${user.id}`,
+ actor: this.userEntityService.genLocalUserUri(user.id),
target,
object,
};
@@ -585,7 +585,7 @@ export class ApRendererService {
return {
type: 'Undo',
...(id ? { id } : {}),
- actor: `${this.config.url}/users/${user.id}`,
+ actor: this.userEntityService.genLocalUserUri(user.id),
object,
published: new Date().toISOString(),
};
@@ -595,7 +595,7 @@ export class ApRendererService {
public renderUpdate(object: any, user: { id: User['id'] }): IUpdate {
return {
id: `${this.config.url}/users/${user.id}#updates/${new Date().getTime()}`,
- actor: `${this.config.url}/users/${user.id}`,
+ actor: this.userEntityService.genLocalUserUri(user.id),
type: 'Update',
to: ['https://www.w3.org/ns/activitystreams#Public'],
object,
@@ -607,14 +607,14 @@ export class ApRendererService {
public renderVote(user: { id: User['id'] }, vote: PollVote, note: Note, poll: Poll, pollOwner: RemoteUser): ICreate {
return {
id: `${this.config.url}/users/${user.id}#votes/${vote.id}/activity`,
- actor: `${this.config.url}/users/${user.id}`,
+ actor: this.userEntityService.genLocalUserUri(user.id),
type: 'Create',
to: [pollOwner.uri],
published: new Date().toISOString(),
object: {
id: `${this.config.url}/users/${user.id}#votes/${vote.id}`,
type: 'Note',
- attributedTo: `${this.config.url}/users/${user.id}`,
+ attributedTo: this.userEntityService.genLocalUserUri(user.id),
to: [pollOwner.uri],
inReplyTo: note.uri,
name: poll.choices[vote.choice],
diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts
index df7bb46405..d3e0345c9c 100644
--- a/packages/backend/src/core/activitypub/ApResolverService.ts
+++ b/packages/backend/src/core/activitypub/ApResolverService.ts
@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
-import type { LocalUser } from '@/models/entities/User.js';
+import type { LocalUser, RemoteUser } from '@/models/entities/User.js';
import { InstanceActorService } from '@/core/InstanceActorService.js';
import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository } from '@/models/index.js';
import type { Config } from '@/config.js';
@@ -151,7 +151,7 @@ export class Resolver {
return Promise.all(
[parsed.id, parsed.rest].map(id => this.usersRepository.findOneByOrFail({ id })),
)
- .then(([follower, followee]) => this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee, url)));
+ .then(([follower, followee]) => this.apRendererService.addContext(this.apRendererService.renderFollow(follower as LocalUser | RemoteUser, followee as LocalUser | RemoteUser, url)));
default:
throw new Error(`resolveLocal: type ${parsed.type} unhandled`);
}
diff --git a/packages/backend/src/core/activitypub/models/ApImageService.ts b/packages/backend/src/core/activitypub/models/ApImageService.ts
index 3b671af127..0043907c21 100644
--- a/packages/backend/src/core/activitypub/models/ApImageService.ts
+++ b/packages/backend/src/core/activitypub/models/ApImageService.ts
@@ -12,6 +12,7 @@ import type Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
import { ApResolverService } from '../ApResolverService.js';
import { ApLoggerService } from '../ApLoggerService.js';
+import { checkHttps } from '@/misc/check-https.js';
@Injectable()
export class ApImageService {
@@ -48,8 +49,8 @@ export class ApImageService {
throw new Error('invalid image: url not privided');
}
- if (!image.url.startsWith('https://')) {
- throw new Error('invalid image: unexpected shcema of url: ' + image.url);
+ if (!checkHttps(image.url)) {
+ throw new Error('invalid image: unexpected schema of url: ' + image.url);
}
this.logger.info(`Creating the Image: ${image.url}`);
diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts
index 5ca5f6e843..87a9db405f 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -32,6 +32,7 @@ import { ApQuestionService } from './ApQuestionService.js';
import { ApImageService } from './ApImageService.js';
import type { Resolver } from '../ApResolverService.js';
import type { IObject, IPost } from '../type.js';
+import { checkHttps } from '@/misc/check-https.js';
@Injectable()
export class ApNoteService {
@@ -71,7 +72,7 @@ export class ApNoteService {
}
@bindThis
- public validateNote(object: any, uri: string) {
+ public validateNote(object: IObject, uri: string) {
const expectHost = this.utilityService.extractDbHost(uri);
if (object == null) {
@@ -85,9 +86,10 @@ export class ApNoteService {
if (object.id && this.utilityService.extractDbHost(object.id) !== expectHost) {
return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${this.utilityService.extractDbHost(object.id)}`);
}
-
- if (object.attributedTo && this.utilityService.extractDbHost(getOneApId(object.attributedTo)) !== expectHost) {
- return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${this.utilityService.extractDbHost(object.attributedTo)}`);
+
+ const actualHost = object.attributedTo && this.utilityService.extractDbHost(getOneApId(object.attributedTo));
+ if (object.attributedTo && actualHost !== expectHost) {
+ return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${actualHost}`);
}
return null;
@@ -129,13 +131,13 @@ export class ApNoteService {
this.logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`);
- if (note.id && !note.id.startsWith('https://')) {
+ if (note.id && !checkHttps(note.id)) {
throw new Error('unexpected shcema of note.id: ' + note.id);
}
const url = getOneApHrefNullable(note.url);
- if (url && !url.startsWith('https://')) {
+ if (url && !checkHttps(url)) {
throw new Error('unexpected shcema of note url: ' + url);
}
@@ -148,7 +150,7 @@ export class ApNoteService {
if (actor.isSuspended) {
throw new Error('actor has been suspended');
}
-
+
const noteAudience = await this.apAudienceService.parseAudience(actor, note.to, note.cc, resolver);
let visibility = noteAudience.visibility;
const visibleUsers = noteAudience.visibleUsers;
diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts
index 21797cfcb7..eea1d1b848 100644
--- a/packages/backend/src/core/activitypub/models/ApPersonService.ts
+++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts
@@ -3,9 +3,9 @@ import promiseLimit from 'promise-limit';
import { DataSource } from 'typeorm';
import { ModuleRef } from '@nestjs/core';
import { DI } from '@/di-symbols.js';
-import type { FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js';
+import type { BlockingsRepository, MutingsRepository, FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js';
import type { Config } from '@/config.js';
-import type { RemoteUser } from '@/models/entities/User.js';
+import type { LocalUser, RemoteUser } from '@/models/entities/User.js';
import { User } from '@/models/entities/User.js';
import { truncate } from '@/misc/truncate.js';
import type { CacheService } from '@/core/CacheService.js';
@@ -42,6 +42,8 @@ import type { ApLoggerService } from '../ApLoggerService.js';
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
import type { ApImageService } from './ApImageService.js';
import type { IActor, IObject } from '../type.js';
+import type { AccountMoveService } from '@/core/AccountMoveService.js';
+import { checkHttps } from '@/misc/check-https.js';
const nameLength = 128;
const summaryLength = 2048;
@@ -66,6 +68,7 @@ export class ApPersonService implements OnModuleInit {
private usersChart: UsersChart;
private instanceChart: InstanceChart;
private apLoggerService: ApLoggerService;
+ private accountMoveService: AccountMoveService;
private logger: Logger;
constructor(
@@ -131,9 +134,16 @@ export class ApPersonService implements OnModuleInit {
this.usersChart = this.moduleRef.get('UsersChart');
this.instanceChart = this.moduleRef.get('InstanceChart');
this.apLoggerService = this.moduleRef.get('ApLoggerService');
+ this.accountMoveService = this.moduleRef.get('AccountMoveService');
this.logger = this.apLoggerService.logger;
}
+ private punyHost(url: string): string {
+ const urlObj = new URL(url);
+ const host = `${this.utilityService.toPuny(urlObj.hostname)}${urlObj.port.length > 0 ? ':' + urlObj.port : ''}`;
+ return host;
+ }
+
/**
* Validate and convert to actor object
* @param x Fetched object
@@ -141,7 +151,7 @@ export class ApPersonService implements OnModuleInit {
*/
@bindThis
private validateActor(x: IObject, uri: string): IActor {
- const expectHost = this.utilityService.toPuny(new URL(uri).hostname);
+ const expectHost = this.punyHost(uri);
if (x == null) {
throw new Error('invalid Actor: object is null');
@@ -182,7 +192,7 @@ export class ApPersonService implements OnModuleInit {
x.summary = truncate(x.summary, summaryLength);
}
- const idHost = this.utilityService.toPuny(new URL(x.id!).hostname);
+ const idHost = this.punyHost(x.id);
if (idHost !== expectHost) {
throw new Error('invalid Actor: id has different host');
}
@@ -192,7 +202,7 @@ export class ApPersonService implements OnModuleInit {
throw new Error('invalid Actor: publicKey.id is not a string');
}
- const publicKeyIdHost = this.utilityService.toPuny(new URL(x.publicKey.id).hostname);
+ const publicKeyIdHost = this.punyHost(x.publicKey.id);
if (publicKeyIdHost !== expectHost) {
throw new Error('invalid Actor: publicKey.id has different host');
}
@@ -202,27 +212,27 @@ export class ApPersonService implements OnModuleInit {
}
/**
- * Personをフェッãƒã—ã¾ã™ã€‚
+ * uriã‹ã‚‰User(Person)をフェッãƒã—ã¾ã™ã€‚
*
- * Misskeyã«å¯¾è±¡ã®PersonãŒç™»éŒ²ã•れã¦ã„れã°ãれを返ã—ã¾ã™ã€‚
+ * Misskeyã«å¯¾è±¡ã®PersonãŒç™»éŒ²ã•れã¦ã„れã°ãれを返ã—ã€ç™»éŒ²ãŒãªã‘れã°nullã‚’è¿”ã—ã¾ã™ã€‚
*/
@bindThis
- public async fetchPerson(uri: string, resolver?: Resolver): Promise<User | null> {
+ public async fetchPerson(uri: string): Promise<LocalUser | RemoteUser | null> {
if (typeof uri !== 'string') throw new Error('uri is not string');
- const cached = this.cacheService.uriPersonCache.get(uri);
+ const cached = this.cacheService.uriPersonCache.get(uri) as LocalUser | RemoteUser | null;
if (cached) return cached;
// URIãŒã“ã®ã‚µãƒ¼ãƒãƒ¼ã‚’指ã—ã¦ã„ã‚‹ãªã‚‰ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã‹ã‚‰ãƒ•ェッãƒ
- if (uri.startsWith(this.config.url + '/')) {
+ if (uri.startsWith(`${this.config.url}/`)) {
const id = uri.split('/').pop();
- const u = await this.usersRepository.findOneBy({ id });
+ const u = await this.usersRepository.findOneBy({ id }) as LocalUser;
if (u) this.cacheService.uriPersonCache.set(uri, u);
return u;
}
//#region ã“ã®ã‚µãƒ¼ãƒãƒ¼ã«æ—¢ã«ç™»éŒ²ã•れã¦ã„ãŸã‚‰ãれを返ã™
- const exist = await this.usersRepository.findOneBy({ uri });
+ const exist = await this.usersRepository.findOneBy({ uri }) as LocalUser | RemoteUser;
if (exist) {
this.cacheService.uriPersonCache.set(uri, exist);
@@ -237,7 +247,7 @@ export class ApPersonService implements OnModuleInit {
* Personを作æˆã—ã¾ã™ã€‚
*/
@bindThis
- public async createPerson(uri: string, resolver?: Resolver): Promise<User> {
+ public async createPerson(uri: string, resolver?: Resolver): Promise<RemoteUser> {
if (typeof uri !== 'string') throw new Error('uri is not string');
if (uri.startsWith(this.config.url)) {
@@ -252,7 +262,7 @@ export class ApPersonService implements OnModuleInit {
this.logger.info(`Creating the Person: ${person.id}`);
- const host = this.utilityService.toPuny(new URL(object.id).hostname);
+ const host = this.punyHost(object.id);
const { fields } = this.analyzeAttachments(person.attachment ?? []);
@@ -264,8 +274,8 @@ export class ApPersonService implements OnModuleInit {
const url = getOneApHrefNullable(person.url);
- if (url && !url.startsWith('https://')) {
- throw new Error('unexpected shcema of person url: ' + url);
+ if (url && !checkHttps(url)) {
+ throw new Error('unexpected schema of person url: ' + url);
}
// Create user
@@ -282,6 +292,7 @@ export class ApPersonService implements OnModuleInit {
name: truncate(person.name, nameLength),
isLocked: !!person.manuallyApprovesFollowers,
movedToUri: person.movedTo,
+ movedAt: person.movedTo ? new Date() : null,
alsoKnownAs: person.alsoKnownAs,
isExplorable: !!person.discoverable,
username: person.preferredUsername,
@@ -404,23 +415,26 @@ export class ApPersonService implements OnModuleInit {
/**
* Personã®æƒ…報を更新ã—ã¾ã™ã€‚
* Misskeyã«å¯¾è±¡ã®PersonãŒç™»éŒ²ã•れã¦ã„ãªã‘れã°ç„¡è¦–ã—ã¾ã™ã€‚
+ * ã‚‚ã—アカウントã®ç§»è¡ŒãŒç¢ºèªã•れãŸå ´åˆã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆç§»è¡Œå‡¦ç†ã‚’行ã„ã¾ã™ã€‚
+ *
* @param uri URI of Person
* @param resolver Resolver
* @param hint Hint of Person object (ã“ã®å€¤ãŒæ­£å½“ãªPersonã®å ´åˆã€Remote resolveã‚’ã›ãšã«æ›´æ–°ã«åˆ©ç”¨ã—ã¾ã™)
+ * @param movePreventUris ã“ã“ã«æŒ‡å®šã•れãŸURIãŒPersonã®movedToã«æŒ‡å®šã•れã¦ã„ãŸã‚Š10回より多ã回ã£ã¦ã„ã‚‹å ´åˆã“れ以上アカウント移行を行ã‚ãªã„(無é™ãƒ«ãƒ¼ãƒ—防止)
*/
@bindThis
- public async updatePerson(uri: string, resolver?: Resolver | null, hint?: IObject): Promise<void> {
+ public async updatePerson(uri: string, resolver?: Resolver | null, hint?: IObject, movePreventUris: string[] = []): Promise<string | void> {
if (typeof uri !== 'string') throw new Error('uri is not string');
// URIãŒã“ã®ã‚µãƒ¼ãƒãƒ¼ã‚’指ã—ã¦ã„ã‚‹ãªã‚‰ã‚¹ã‚­ãƒƒãƒ—
- if (uri.startsWith(this.config.url + '/')) {
+ if (uri.startsWith(`${this.config.url}/`)) {
return;
}
//#region ã“ã®ã‚µãƒ¼ãƒãƒ¼ã«æ—¢ã«ç™»éŒ²ã•れã¦ã„ã‚‹ã‹
- const exist = await this.usersRepository.findOneBy({ uri }) as RemoteUser;
+ const exist = await this.usersRepository.findOneBy({ uri }) as RemoteUser | null;
- if (exist == null) {
+ if (exist === null) {
return;
}
//#endregion
@@ -459,8 +473,8 @@ export class ApPersonService implements OnModuleInit {
const url = getOneApHrefNullable(person.url);
- if (url && !url.startsWith('https://')) {
- throw new Error('unexpected shcema of person url: ' + url);
+ if (url && !checkHttps(url)) {
+ throw new Error('unexpected schema of person url: ' + url);
}
const updates = {
@@ -478,7 +492,16 @@ export class ApPersonService implements OnModuleInit {
movedToUri: person.movedTo ?? null,
alsoKnownAs: person.alsoKnownAs ?? null,
isExplorable: !!person.discoverable,
- } as Partial<User>;
+ } as Partial<RemoteUser> & Pick<RemoteUser, 'isBot' | 'isCat' | 'isLocked' | 'movedToUri' | 'alsoKnownAs' | 'isExplorable'>;
+
+ const moving =
+ // 移行先ãŒãªã„→ã‚ã‚‹
+ (!exist.movedToUri && updates.movedToUri) ||
+ // 移行先ãŒã‚る→別ã®ã‚‚ã®
+ (exist.movedToUri !== updates.movedToUri && exist.movedToUri && updates.movedToUri);
+ // 移行先ãŒã‚る→ãªã„ã€ãªã„→ãªã„ã¯ç„¡è¦–
+
+ if (moving) updates.movedAt = new Date();
if (avatar) {
updates.avatarId = avatar.id;
@@ -523,6 +546,31 @@ export class ApPersonService implements OnModuleInit {
});
await this.updateFeatured(exist.id, resolver).catch(err => this.logger.error(err));
+
+ const updated = { ...exist, ...updates };
+
+ this.cacheService.uriPersonCache.set(uri, updated);
+
+ // 移行処ç†ã‚’行ã†
+ if (updated.movedAt && (
+ // åˆã‚ã¦ç§»è¡Œã™ã‚‹å ´åˆã¯movedAtãŒnullãªã®ã§ç§»è¡Œå‡¦ç†ã‚’許å¯
+ exist.movedAt == null ||
+ // 以å‰ã®movingã‹ã‚‰14日以上経éŽã—ãŸå ´åˆã®ã¿ç§»è¡Œå‡¦ç†ã‚’許å¯
+ // (Mastodonã®ã‚¯ãƒ¼ãƒ«ãƒ€ã‚¦ãƒ³æœŸé–“ã¯30æ—¥ã ãŒè‹¥å¹²ç·©ã‚ã«è¨­å®šã—ã¦ãŠã)
+ exist.movedAt.getTime() + 1000 * 60 * 60 * 24 * 14 < updated.movedAt.getTime()
+ )) {
+ this.logger.info(`Start to process Move of @${updated.username}@${updated.host} (${uri})`);
+ return this.processRemoteMove(updated, movePreventUris)
+ .then(result => {
+ this.logger.info(`Processing Move Finished [${result}] @${updated.username}@${updated.host} (${uri})`);
+ return result;
+ })
+ .catch(e => {
+ this.logger.info(`Processing Move Failed @${updated.username}@${updated.host} (${uri})`, { stack: e });
+ });
+ }
+
+ return 'skip';
}
/**
@@ -532,7 +580,7 @@ export class ApPersonService implements OnModuleInit {
* リモートサーãƒãƒ¼ã‹ã‚‰ãƒ•ェッãƒã—ã¦Misskeyã«ç™»éŒ²ã—ãれを返ã—ã¾ã™ã€‚
*/
@bindThis
- public async resolvePerson(uri: string, resolver?: Resolver): Promise<User> {
+ public async resolvePerson(uri: string, resolver?: Resolver): Promise<LocalUser | RemoteUser> {
if (typeof uri !== 'string') throw new Error('uri is not string');
//#region ã“ã®ã‚µãƒ¼ãƒãƒ¼ã«æ—¢ã«ç™»éŒ²ã•れã¦ã„ãŸã‚‰ãれを返ã™
@@ -607,4 +655,53 @@ export class ApPersonService implements OnModuleInit {
}
});
}
+
+ /**
+ * リモート由æ¥ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆç§»è¡Œå‡¦ç†ã‚’行ã„ã¾ã™
+ * @param src 移行元アカウント(リモートã‹ã¤updatePerson後ã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ã€ã¨ã„ã†ã‹ã“れ自体ãŒupdatePersonã§å‘¼ã°ã‚Œã‚‹å‰æï¼‰
+ * @param movePreventUris ã“ã“ã«åˆ—挙ã•れãŸURIã«src.movedToUriãŒå«ã¾ã‚Œã‚‹å ´åˆã€ç§»è¡Œå‡¦ç†ã¯ã—ãªã„(無é™ãƒ«ãƒ¼ãƒ—防止)
+ */
+ @bindThis
+ private async processRemoteMove(src: RemoteUser, movePreventUris: string[] = []): Promise<string> {
+ if (!src.movedToUri) return 'skip: no movedToUri';
+ if (src.uri === src.movedToUri) return 'skip: movedTo itself (src)'; // ???
+ if (movePreventUris.length > 10) return 'skip: too many moves';
+
+ // ã¾ãšã‚µãƒ¼ãƒãƒ¼å†…ã§æ¤œç´¢ã—ã¦æ§˜å­è¦‹
+ let dst = await this.fetchPerson(src.movedToUri);
+
+ if (dst && this.userEntityService.isLocalUser(dst)) {
+ // targetãŒãƒ­ãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã ã£ãŸå ´åˆãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã‹ã‚‰å¼•ã£å¼µã£ã¦ãã‚‹
+ dst = await this.usersRepository.findOneByOrFail({ uri: src.movedToUri }) as LocalUser;
+ } else if (dst) {
+ if (movePreventUris.includes(src.movedToUri)) return 'skip: circular move';
+
+ // targetを見ã¤ã‘ãŸã“ã¨ãŒã‚ã‚‹ãªã‚‰targetã‚’updatePersonã™ã‚‹
+ await this.updatePerson(src.movedToUri, undefined, undefined, [...movePreventUris, src.uri]);
+ dst = await this.fetchPerson(src.movedToUri) ?? dst;
+ } else {
+ if (src.movedToUri.startsWith(`${this.config.url}/`)) {
+ // ローカルユーザーã£ã½ã„ã®ã«fetchPersonã§è¦‹ã¤ã‹ã‚‰ãªã„ã¨ã„ã†ã“ã¨ã¯movedToUriãŒé–“é•ã£ã¦ã„ã‚‹
+ return 'failed: movedTo is local but not found';
+ }
+
+ // targetãŒçŸ¥ã‚‰ãªã„人ã ã£ãŸã‚‰resolvePerson
+ // (uriãŒå­˜åœ¨ã—ãªã‹ã£ãŸã‚Šå¿œç­”ãŒãªã‹ã£ãŸã‚Šã™ã‚‹å ´åˆresolvePersonã¯throw Errorã™ã‚‹)
+ dst = await this.resolvePerson(src.movedToUri);
+ }
+
+ if (dst.movedToUri === dst.uri) return 'skip: movedTo itself (dst)'; // ???
+ if (src.movedToUri !== dst.uri) return 'skip: missmatch uri'; // ???
+ if (dst.movedToUri === src.uri) return 'skip: dst.movedToUri === src.uri';
+ if (!dst.alsoKnownAs || dst.alsoKnownAs.length === 0) {
+ return 'skip: dst.alsoKnownAs is empty';
+ }
+ if (!dst.alsoKnownAs?.includes(src.uri)) {
+ return 'skip: alsoKnownAs does not include from.uri';
+ }
+
+ await this.accountMoveService.postMoveProcess(src, dst);
+
+ return 'ok';
+ }
}
diff --git a/packages/backend/src/core/entities/ChannelEntityService.ts b/packages/backend/src/core/entities/ChannelEntityService.ts
index 987002606f..15ffd44861 100644
--- a/packages/backend/src/core/entities/ChannelEntityService.ts
+++ b/packages/backend/src/core/entities/ChannelEntityService.ts
@@ -74,6 +74,8 @@ export class ChannelEntityService {
userId: channel.userId,
bannerUrl: banner ? this.driveFileEntityService.getPublicUrl(banner) : null,
pinnedNoteIds: channel.pinnedNoteIds,
+ color: channel.color,
+ isArchived: channel.isArchived,
usersCount: channel.usersCount,
notesCount: channel.notesCount,
diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts
index 26debd6adc..32269a4101 100644
--- a/packages/backend/src/core/entities/NoteEntityService.ts
+++ b/packages/backend/src/core/entities/NoteEntityService.ts
@@ -335,6 +335,7 @@ export class NoteEntityService implements OnModuleInit {
channel: channel ? {
id: channel.id,
name: channel.name,
+ color: channel.color,
} : undefined,
mentions: note.mentions.length > 0 ? note.mentions : undefined,
uri: note.uri ?? undefined,
diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts
index 0dc63d969f..d76b863957 100644
--- a/packages/backend/src/core/entities/NotificationEntityService.ts
+++ b/packages/backend/src/core/entities/NotificationEntityService.ts
@@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { In } from 'typeorm';
import { DI } from '@/di-symbols.js';
-import type { AccessTokensRepository, NoteReactionsRepository, NotesRepository, User, UsersRepository } from '@/models/index.js';
+import type { AccessTokensRepository, FollowRequestsRepository, NoteReactionsRepository, NotesRepository, User, UsersRepository } from '@/models/index.js';
import { awaitAll } from '@/misc/prelude/await-all.js';
import type { Notification } from '@/models/entities/Notification.js';
import type { Note } from '@/models/entities/Note.js';
@@ -35,6 +35,9 @@ export class NotificationEntityService implements OnModuleInit {
@Inject(DI.noteReactionsRepository)
private noteReactionsRepository: NoteReactionsRepository,
+ @Inject(DI.followRequestsRepository)
+ private followRequestsRepository: FollowRequestsRepository,
+
@Inject(DI.accessTokensRepository)
private accessTokensRepository: AccessTokensRepository,
@@ -131,6 +134,15 @@ export class NotificationEntityService implements OnModuleInit {
});
const packedUsers = new Map(packedUsersArray.map(p => [p.id, p]));
+ // æ—¢ã«è§£æ±ºã•れãŸãƒ•ォローリクエストã®é€šçŸ¥ã‚’除外
+ const followRequestNotifications = validNotifications.filter(x => x.type === 'receiveFollowRequest');
+ if (followRequestNotifications.length > 0) {
+ const reqs = await this.followRequestsRepository.find({
+ where: { followerId: In(followRequestNotifications.map(x => x.notifierId!)) },
+ });
+ validNotifications = validNotifications.filter(x => (x.type !== 'receiveFollowRequest') || reqs.some(r => r.followerId === x.notifierId));
+ }
+
return await Promise.all(validNotifications.map(x => this.pack(x, meId, {}, {
packedNotes,
packedUsers,
diff --git a/packages/backend/src/core/entities/RoleEntityService.ts b/packages/backend/src/core/entities/RoleEntityService.ts
index e111a10b77..54818782dd 100644
--- a/packages/backend/src/core/entities/RoleEntityService.ts
+++ b/packages/backend/src/core/entities/RoleEntityService.ts
@@ -59,6 +59,7 @@ export class RoleEntityService {
isPublic: role.isPublic,
isAdministrator: role.isAdministrator,
isModerator: role.isModerator,
+ isExplorable: role.isExplorable,
asBadge: role.asBadge,
canEditMembersByModerator: role.canEditMembersByModerator,
displayOrder: role.displayOrder,
diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index e02f7535d4..a7f62c05f7 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -1,6 +1,6 @@
import { Inject, Injectable } from '@nestjs/common';
import { In, Not } from 'typeorm';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import Ajv from 'ajv';
import { ModuleRef } from '@nestjs/core';
import { DI } from '@/di-symbols.js';
@@ -9,10 +9,9 @@ import type { Packed } from '@/misc/json-schema.js';
import type { Promiseable } from '@/misc/prelude/await-all.js';
import { awaitAll } from '@/misc/prelude/await-all.js';
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js';
-import type { Instance } from '@/models/entities/Instance.js';
-import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js';
+import type { LocalUser, PartialLocalUser, PartialRemoteUser, RemoteUser, User } from '@/models/entities/User.js';
import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js';
-import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, ChannelFollowingsRepository, UserNotePiningsRepository, UserProfilesRepository, InstancesRepository, AnnouncementReadsRepository, AnnouncementsRepository, PagesRepository, UserProfile, RenoteMutingsRepository } from '@/models/index.js';
+import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, ChannelFollowingsRepository, UserNotePiningsRepository, UserProfilesRepository, InstancesRepository, AnnouncementReadsRepository, AnnouncementsRepository, PagesRepository, UserProfile, RenoteMutingsRepository, UserMemoRepository } from '@/models/index.js';
import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
@@ -35,13 +34,13 @@ type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends bo
const ajv = new Ajv();
function isLocalUser(user: User): user is LocalUser;
-function isLocalUser<T extends { host: User['host'] }>(user: T): user is T & { host: null; };
+function isLocalUser<T extends { host: User['host'] }>(user: T): user is (T & { host: null; });
function isLocalUser(user: User | { host: User['host'] }): boolean {
return user.host == null;
}
function isRemoteUser(user: User): user is RemoteUser;
-function isRemoteUser<T extends { host: User['host'] }>(user: T): user is T & { host: string; };
+function isRemoteUser<T extends { host: User['host'] }>(user: T): user is (T & { host: string; });
function isRemoteUser(user: User | { host: User['host'] }): boolean {
return !isLocalUser(user);
}
@@ -113,6 +112,9 @@ export class UserEntityService implements OnModuleInit {
@Inject(DI.pagesRepository)
private pagesRepository: PagesRepository,
+
+ @Inject(DI.userMemosRepository)
+ private userMemosRepository: UserMemoRepository,
//private noteEntityService: NoteEntityService,
//private driveFileEntityService: DriveFileEntityService,
@@ -277,9 +279,20 @@ export class UserEntityService implements OnModuleInit {
return `${this.config.url}/identicon/${user.username.toLowerCase()}@${user.host ?? this.config.host}`;
}
+ @bindThis
+ public getUserUri(user: LocalUser | PartialLocalUser | RemoteUser | PartialRemoteUser): string {
+ return this.isRemoteUser(user)
+ ? user.uri : this.genLocalUserUri(user.id);
+ }
+
+ @bindThis
+ public genLocalUserUri(userId: string): string {
+ return `${this.config.url}/users/${userId}`;
+ }
+
public async pack<ExpectsMe extends boolean | null = null, D extends boolean = false>(
src: User['id'] | User,
- me?: { id: User['id'] } | null | undefined,
+ me?: { id: User['id']; } | null | undefined,
options?: {
detail?: D,
includeSecrets?: boolean,
@@ -293,26 +306,9 @@ export class UserEntityService implements OnModuleInit {
const user = typeof src === 'object' ? src : await this.usersRepository.findOneByOrFail({ id: src });
- // migration
- if (user.avatarId != null && user.avatarUrl === null) {
- const avatar = await this.driveFilesRepository.findOneByOrFail({ id: user.avatarId });
- user.avatarUrl = this.driveFileEntityService.getPublicUrl(avatar, 'avatar');
- this.usersRepository.update(user.id, {
- avatarUrl: user.avatarUrl,
- avatarBlurhash: avatar.blurhash,
- });
- }
- if (user.bannerId != null && user.bannerUrl === null) {
- const banner = await this.driveFilesRepository.findOneByOrFail({ id: user.bannerId });
- user.bannerUrl = this.driveFileEntityService.getPublicUrl(banner);
- this.usersRepository.update(user.id, {
- bannerUrl: user.bannerUrl,
- bannerBlurhash: banner.blurhash,
- });
- }
-
const meId = me ? me.id : null;
const isMe = meId === user.id;
+ const iAmModerator = me ? await this.roleService.isModerator(me as User) : false;
const relation = meId && !isMe && opts.detail ? await this.getRelation(meId, user.id) : null;
const pins = opts.detail ? await this.userNotePiningsRepository.createQueryBuilder('pin')
@@ -366,8 +362,11 @@ export class UserEntityService implements OnModuleInit {
...(opts.detail ? {
url: profile!.url,
uri: user.uri,
- movedToUri: user.movedToUri ? await this.apPersonService.resolvePerson(user.movedToUri) : null,
- alsoKnownAs: user.alsoKnownAs,
+ movedTo: user.movedToUri ? this.apPersonService.resolvePerson(user.movedToUri).then(user => user.id).catch(() => null) : null,
+ alsoKnownAs: user.alsoKnownAs
+ ? Promise.all(user.alsoKnownAs.map(uri => this.apPersonService.fetchPerson(uri).then(user => user?.id).catch(() => null)))
+ .then(xs => xs.length === 0 ? null : xs.filter(x => x != null) as string[])
+ : null,
createdAt: user.createdAt.toISOString(),
updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null,
lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null,
@@ -409,6 +408,11 @@ export class UserEntityService implements OnModuleInit {
isAdministrator: role.isAdministrator,
displayOrder: role.displayOrder,
}))),
+ memo: meId == null ? null : await this.userMemosRepository.findOneBy({
+ userId: meId,
+ targetUserId: user.id,
+ }).then(row => row?.memo ?? null),
+ moderationNote: iAmModerator ? (profile!.moderationNote ?? '') : undefined,
} : {}),
...(opts.detail && isMe ? {
diff --git a/packages/backend/src/daemons/ServerStatsService.ts b/packages/backend/src/daemons/ServerStatsService.ts
index 1688bb2ba7..bb190cf60f 100644
--- a/packages/backend/src/daemons/ServerStatsService.ts
+++ b/packages/backend/src/daemons/ServerStatsService.ts
@@ -40,7 +40,7 @@ export class ServerStatsService implements OnApplicationShutdown {
const stats = {
cpu: roundCpu(cpu),
mem: {
- used: round(memStats.used - memStats.buffers - memStats.cached),
+ used: round(memStats.total - memStats.available),
active: round(memStats.active),
},
net: {
diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts
index d4b1fb31b1..c06c7a7159 100644
--- a/packages/backend/src/di-symbols.ts
+++ b/packages/backend/src/di-symbols.ts
@@ -1,6 +1,7 @@
export const DI = {
config: Symbol('config'),
db: Symbol('db'),
+ meilisearch: Symbol('meilisearch'),
redis: Symbol('redis'),
redisForPub: Symbol('redisForPub'),
redisForSub: Symbol('redisForSub'),
@@ -70,5 +71,6 @@ export const DI = {
roleAssignmentsRepository: Symbol('roleAssignmentsRepository'),
flashsRepository: Symbol('flashsRepository'),
flashLikesRepository: Symbol('flashLikesRepository'),
+ userMemosRepository: Symbol('userMemosRepository'),
//#endregion
};
diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts
index f413246a1f..5610929648 100644
--- a/packages/backend/src/misc/cache.ts
+++ b/packages/backend/src/misc/cache.ts
@@ -1,4 +1,4 @@
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import { bindThis } from '@/decorators.js';
export class RedisKVCache<T> {
@@ -38,7 +38,7 @@ export class RedisKVCache<T> {
await this.redisClient.set(
`kvcache:${this.name}:${key}`,
this.toRedisConverter(value),
- 'ex', Math.round(this.lifetime / 1000),
+ 'EX', Math.round(this.lifetime / 1000),
);
}
}
@@ -122,7 +122,7 @@ export class RedisSingleCache<T> {
await this.redisClient.set(
`singlecache:${this.name}`,
this.toRedisConverter(value),
- 'ex', Math.round(this.lifetime / 1000),
+ 'EX', Math.round(this.lifetime / 1000),
);
}
}
diff --git a/packages/backend/src/misc/check-https.ts b/packages/backend/src/misc/check-https.ts
new file mode 100644
index 0000000000..b33f019973
--- /dev/null
+++ b/packages/backend/src/misc/check-https.ts
@@ -0,0 +1,4 @@
+export function checkHttps(url: string) {
+ return url.startsWith('https://') ||
+ (url.startsWith('http://') && process.env.NODE_ENV !== 'production');
+}
diff --git a/packages/backend/src/misc/check-word-mute.ts b/packages/backend/src/misc/check-word-mute.ts
index e8c66683cc..910bebfcfe 100644
--- a/packages/backend/src/misc/check-word-mute.ts
+++ b/packages/backend/src/misc/check-word-mute.ts
@@ -1,3 +1,4 @@
+import { AhoCorasick } from 'slacc';
import RE2 from 're2';
import type { Note } from '@/models/entities/Note.js';
import type { User } from '@/models/entities/User.js';
@@ -12,6 +13,8 @@ type UserLike = {
id: User['id'];
};
+const acCache = new Map<string, AhoCorasick>();
+
export async function checkWordMute(note: NoteLike, me: UserLike | null | undefined, mutedWords: Array<string | string[]>): Promise<boolean> {
// 自分自身
if (me && (note.userId === me.id)) return false;
@@ -21,7 +24,22 @@ export async function checkWordMute(note: NoteLike, me: UserLike | null | undefi
if (text === '') return false;
- const matched = mutedWords.some(filter => {
+ const acable = mutedWords.filter(filter => Array.isArray(filter) && filter.length === 1).map(filter => filter[0]).sort();
+ const unacable = mutedWords.filter(filter => !Array.isArray(filter) || filter.length !== 1);
+ const acCacheKey = acable.join('\n');
+ const ac = acCache.get(acCacheKey) ?? AhoCorasick.withPatterns(acable);
+ acCache.delete(acCacheKey);
+ for (const obsoleteKeys of acCache.keys()) {
+ if (acCache.size > 1000) {
+ acCache.delete(obsoleteKeys);
+ }
+ }
+ acCache.set(acCacheKey, ac);
+ if (ac.isMatch(text)) {
+ return true;
+ }
+
+ const matched = unacable.some(filter => {
if (Array.isArray(filter)) {
return filter.every(keyword => text.includes(keyword));
} else {
diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts
index 7be7b81904..588c98b58d 100644
--- a/packages/backend/src/models/RepositoryModule.ts
+++ b/packages/backend/src/models/RepositoryModule.ts
@@ -1,6 +1,6 @@
import { Module } from '@nestjs/common';
import { DI } from '@/di-symbols.js';
-import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, RenoteMuting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelFavorite, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation, FlashLike, Flash, Role, RoleAssignment, ClipFavorite } from './index.js';
+import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, RenoteMuting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelFavorite, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation, FlashLike, Flash, Role, RoleAssignment, ClipFavorite, UserMemo } from './index.js';
import type { DataSource } from 'typeorm';
import type { Provider } from '@nestjs/common';
@@ -388,6 +388,12 @@ const $roleAssignmentsRepository: Provider = {
inject: [DI.db],
};
+const $userMemosRepository: Provider = {
+ provide: DI.userMemosRepository,
+ useFactory: (db: DataSource) => db.getRepository(UserMemo),
+ inject: [DI.db],
+};
+
@Module({
imports: [
],
@@ -456,6 +462,7 @@ const $roleAssignmentsRepository: Provider = {
$roleAssignmentsRepository,
$flashsRepository,
$flashLikesRepository,
+ $userMemosRepository,
],
exports: [
$usersRepository,
@@ -522,6 +529,7 @@ const $roleAssignmentsRepository: Provider = {
$roleAssignmentsRepository,
$flashsRepository,
$flashLikesRepository,
+ $userMemosRepository,
],
})
export class RepositoryModule {}
diff --git a/packages/backend/src/models/entities/Channel.ts b/packages/backend/src/models/entities/Channel.ts
index 2d346fdf9d..d7c4583da3 100644
--- a/packages/backend/src/models/entities/Channel.ts
+++ b/packages/backend/src/models/entities/Channel.ts
@@ -64,6 +64,18 @@ export class Channel {
})
public pinnedNoteIds: string[];
+ @Column('varchar', {
+ length: 16,
+ default: '#86b300',
+ })
+ public color: string;
+
+ @Index()
+ @Column('boolean', {
+ default: false,
+ })
+ public isArchived: boolean;
+
@Index()
@Column('integer', {
default: 0,
diff --git a/packages/backend/src/models/entities/Meta.ts b/packages/backend/src/models/entities/Meta.ts
index 2e4f90b57f..6d44e4edc7 100644
--- a/packages/backend/src/models/entities/Meta.ts
+++ b/packages/backend/src/models/entities/Meta.ts
@@ -405,4 +405,16 @@ export class Meta {
default: { },
})
public policies: Record<string, any>;
+
+ @Column('varchar', {
+ length: 280,
+ array: true,
+ default: '{}',
+ })
+ public serverRules: string[];
+
+ @Column('varchar', {
+ length: 1024, array: true, default: '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey" }',
+ })
+ public preservedUsernames: string[];
}
diff --git a/packages/backend/src/models/entities/Role.ts b/packages/backend/src/models/entities/Role.ts
index eca9bcf270..61f40d59da 100644
--- a/packages/backend/src/models/entities/Role.ts
+++ b/packages/backend/src/models/entities/Role.ts
@@ -154,6 +154,11 @@ export class Role {
@Column('boolean', {
default: false,
})
+ public isExplorable: boolean;
+
+ @Column('boolean', {
+ default: false,
+ })
public canEditMembersByModerator: boolean;
// UIã«è¡¨ç¤ºã™ã‚‹éš›ã®ä¸¦ã³é †ç”¨(大ãã„ã»ã©å…ˆé ­)
diff --git a/packages/backend/src/models/entities/User.ts b/packages/backend/src/models/entities/User.ts
index 04dfa21107..8e10f999b6 100644
--- a/packages/backend/src/models/entities/User.ts
+++ b/packages/backend/src/models/entities/User.ts
@@ -75,6 +75,12 @@ export class User {
})
public movedToUri: string | null;
+ @Column('timestamp with time zone', {
+ nullable: true,
+ comment: 'When the user moved to another account',
+ })
+ public movedAt: Date | null;
+
@Column('simple-array', {
nullable: true,
comment: 'URIs the user is known as too',
@@ -253,11 +259,23 @@ export type LocalUser = User & {
uri: null;
}
+export type PartialLocalUser = Partial<User> & {
+ id: User['id'];
+ host: null;
+ uri: null;
+}
+
export type RemoteUser = User & {
host: string;
uri: string;
}
+export type PartialRemoteUser = Partial<User> & {
+ id: User['id'];
+ host: string;
+ uri: string;
+}
+
export const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const;
export const passwordSchema = { type: 'string', minLength: 1 } as const;
export const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
diff --git a/packages/backend/src/models/entities/UserMemo.ts b/packages/backend/src/models/entities/UserMemo.ts
new file mode 100644
index 0000000000..7dc34b4346
--- /dev/null
+++ b/packages/backend/src/models/entities/UserMemo.ts
@@ -0,0 +1,42 @@
+import { Column, Entity, Index, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm';
+import { id } from '../id.js';
+import { User } from './User.js';
+
+@Entity()
+@Index(['userId', 'targetUserId'], { unique: true })
+export class UserMemo {
+ @PrimaryColumn(id())
+ public id: string;
+
+ @Index()
+ @Column({
+ ...id(),
+ comment: 'The ID of author.',
+ })
+ public userId: User['id'];
+
+ @ManyToOne(type => User, {
+ onDelete: 'CASCADE',
+ })
+ @JoinColumn()
+ public user: User | null;
+
+ @Index()
+ @Column({
+ ...id(),
+ comment: 'The ID of target user.',
+ })
+ public targetUserId: User['id'];
+
+ @ManyToOne(type => User, {
+ onDelete: 'CASCADE',
+ })
+ @JoinColumn()
+ public targetUser: User | null;
+
+ @Column('varchar', {
+ length: 2048,
+ comment: 'Memo.',
+ })
+ public memo: string;
+}
diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts
index 48d6e15f2a..b8ba28db9b 100644
--- a/packages/backend/src/models/index.ts
+++ b/packages/backend/src/models/index.ts
@@ -55,6 +55,7 @@ import { UserPending } from '@/models/entities/UserPending.js';
import { UserProfile } from '@/models/entities/UserProfile.js';
import { UserPublickey } from '@/models/entities/UserPublickey.js';
import { UserSecurityKey } from '@/models/entities/UserSecurityKey.js';
+import { UserMemo } from '@/models/entities/UserMemo.js';
import { Webhook } from '@/models/entities/Webhook.js';
import { Channel } from '@/models/entities/Channel.js';
import { RetentionAggregation } from '@/models/entities/RetentionAggregation.js';
@@ -129,6 +130,7 @@ export {
RoleAssignment,
Flash,
FlashLike,
+ UserMemo,
};
export type AbuseUserReportsRepository = Repository<AbuseUserReport>;
@@ -195,3 +197,4 @@ export type RolesRepository = Repository<Role>;
export type RoleAssignmentsRepository = Repository<RoleAssignment>;
export type FlashsRepository = Repository<Flash>;
export type FlashLikesRepository = Repository<FlashLike>;
+export type UserMemoRepository = Repository<UserMemo>;
diff --git a/packages/backend/src/models/json-schema/channel.ts b/packages/backend/src/models/json-schema/channel.ts
index 745b39a6ba..fd61a70c0e 100644
--- a/packages/backend/src/models/json-schema/channel.ts
+++ b/packages/backend/src/models/json-schema/channel.ts
@@ -30,6 +30,10 @@ export const packedChannelSchema = {
format: 'url',
nullable: true, optional: false,
},
+ isArchived: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
notesCount: {
type: 'number',
nullable: false, optional: false,
@@ -59,5 +63,9 @@ export const packedChannelSchema = {
format: 'id',
},
},
+ color: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
},
} as const;
diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index 7d40979e3d..529c1303d1 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -80,9 +80,14 @@ export const packedUserDetailedNotMeOnlySchema = {
},
alsoKnownAs: {
type: 'array',
- format: 'uri',
nullable: true,
optional: false,
+ items: {
+ type: 'string',
+ format: 'id',
+ nullable: false,
+ optional: false,
+ },
},
createdAt: {
type: 'string',
@@ -143,6 +148,7 @@ export const packedUserDetailedNotMeOnlySchema = {
fields: {
type: 'array',
nullable: false, optional: false,
+ maxItems: 16,
items: {
type: 'object',
nullable: false, optional: false,
@@ -156,7 +162,6 @@ export const packedUserDetailedNotMeOnlySchema = {
nullable: false, optional: false,
},
},
- maxLength: 4,
},
},
followersCount: {
@@ -250,6 +255,10 @@ export const packedUserDetailedNotMeOnlySchema = {
type: 'boolean',
nullable: false, optional: true,
},
+ memo: {
+ type: 'string',
+ nullable: false, optional: true,
+ },
//#endregion
},
} as const;
diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts
index bb21ed827e..f3d404e6c9 100644
--- a/packages/backend/src/postgres.ts
+++ b/packages/backend/src/postgres.ts
@@ -70,6 +70,7 @@ import { Role } from '@/models/entities/Role.js';
import { RoleAssignment } from '@/models/entities/RoleAssignment.js';
import { Flash } from '@/models/entities/Flash.js';
import { FlashLike } from '@/models/entities/FlashLike.js';
+import { UserMemo } from '@/models/entities/UserMemo.js';
import { Config } from '@/config.js';
import MisskeyLogger from '@/logger.js';
@@ -183,6 +184,7 @@ export const entities = [
RoleAssignment,
Flash,
FlashLike,
+ UserMemo,
...charts,
];
diff --git a/packages/backend/src/queue/DbQueueProcessorsService.ts b/packages/backend/src/queue/DbQueueProcessorsService.ts
index 233a36dd04..df8ac3a301 100644
--- a/packages/backend/src/queue/DbQueueProcessorsService.ts
+++ b/packages/backend/src/queue/DbQueueProcessorsService.ts
@@ -9,11 +9,13 @@ import { ExportFollowingProcessorService } from './processors/ExportFollowingPro
import { ExportMutingProcessorService } from './processors/ExportMutingProcessorService.js';
import { ExportBlockingProcessorService } from './processors/ExportBlockingProcessorService.js';
import { ExportUserListsProcessorService } from './processors/ExportUserListsProcessorService.js';
+import { ExportAntennasProcessorService } from './processors/ExportAntennasProcessorService.js';
import { ImportFollowingProcessorService } from './processors/ImportFollowingProcessorService.js';
import { ImportMutingProcessorService } from './processors/ImportMutingProcessorService.js';
import { ImportBlockingProcessorService } from './processors/ImportBlockingProcessorService.js';
import { ImportUserListsProcessorService } from './processors/ImportUserListsProcessorService.js';
import { ImportCustomEmojisProcessorService } from './processors/ImportCustomEmojisProcessorService.js';
+import { ImportAntennasProcessorService } from './processors/ImportAntennasProcessorService.js';
import { DeleteAccountProcessorService } from './processors/DeleteAccountProcessorService.js';
import { ExportFavoritesProcessorService } from './processors/ExportFavoritesProcessorService.js';
import type Bull from 'bull';
@@ -32,11 +34,13 @@ export class DbQueueProcessorsService {
private exportMutingProcessorService: ExportMutingProcessorService,
private exportBlockingProcessorService: ExportBlockingProcessorService,
private exportUserListsProcessorService: ExportUserListsProcessorService,
+ private exportAntennasProcessorService: ExportAntennasProcessorService,
private importFollowingProcessorService: ImportFollowingProcessorService,
private importMutingProcessorService: ImportMutingProcessorService,
private importBlockingProcessorService: ImportBlockingProcessorService,
private importUserListsProcessorService: ImportUserListsProcessorService,
private importCustomEmojisProcessorService: ImportCustomEmojisProcessorService,
+ private importAntennasProcessorService: ImportAntennasProcessorService,
private deleteAccountProcessorService: DeleteAccountProcessorService,
) {
}
@@ -51,6 +55,7 @@ export class DbQueueProcessorsService {
q.process('exportMuting', (job, done) => this.exportMutingProcessorService.process(job, done));
q.process('exportBlocking', (job, done) => this.exportBlockingProcessorService.process(job, done));
q.process('exportUserLists', (job, done) => this.exportUserListsProcessorService.process(job, done));
+ q.process('exportAntennas', (job, done) => this.exportAntennasProcessorService.process(job, done));
q.process('importFollowing', (job, done) => this.importFollowingProcessorService.process(job, done));
q.process('importFollowingToDb', (job) => this.importFollowingProcessorService.processDb(job));
q.process('importMuting', (job, done) => this.importMutingProcessorService.process(job, done));
@@ -58,6 +63,7 @@ export class DbQueueProcessorsService {
q.process('importBlockingToDb', (job) => this.importBlockingProcessorService.processDb(job));
q.process('importUserLists', (job, done) => this.importUserListsProcessorService.process(job, done));
q.process('importCustomEmojis', (job, done) => this.importCustomEmojisProcessorService.process(job, done));
+ q.process('importAntennas', (job, done) => this.importAntennasProcessorService.process(job, done));
q.process('deleteAccount', (job) => this.deleteAccountProcessorService.process(job));
}
}
diff --git a/packages/backend/src/queue/QueueProcessorModule.ts b/packages/backend/src/queue/QueueProcessorModule.ts
index 4db9b38547..3d4cc77321 100644
--- a/packages/backend/src/queue/QueueProcessorModule.ts
+++ b/packages/backend/src/queue/QueueProcessorModule.ts
@@ -24,11 +24,13 @@ import { ExportFollowingProcessorService } from './processors/ExportFollowingPro
import { ExportMutingProcessorService } from './processors/ExportMutingProcessorService.js';
import { ExportNotesProcessorService } from './processors/ExportNotesProcessorService.js';
import { ExportUserListsProcessorService } from './processors/ExportUserListsProcessorService.js';
+import { ExportAntennasProcessorService } from './processors/ExportAntennasProcessorService.js';
import { ImportBlockingProcessorService } from './processors/ImportBlockingProcessorService.js';
import { ImportCustomEmojisProcessorService } from './processors/ImportCustomEmojisProcessorService.js';
import { ImportFollowingProcessorService } from './processors/ImportFollowingProcessorService.js';
import { ImportMutingProcessorService } from './processors/ImportMutingProcessorService.js';
import { ImportUserListsProcessorService } from './processors/ImportUserListsProcessorService.js';
+import { ImportAntennasProcessorService } from './processors/ImportAntennasProcessorService.js';
import { ResyncChartsProcessorService } from './processors/ResyncChartsProcessorService.js';
import { TickChartsProcessorService } from './processors/TickChartsProcessorService.js';
import { AggregateRetentionProcessorService } from './processors/AggregateRetentionProcessorService.js';
@@ -55,11 +57,13 @@ import { RelationshipProcessorService } from './processors/RelationshipProcessor
ExportMutingProcessorService,
ExportBlockingProcessorService,
ExportUserListsProcessorService,
+ ExportAntennasProcessorService,
ImportFollowingProcessorService,
ImportMutingProcessorService,
ImportBlockingProcessorService,
ImportUserListsProcessorService,
ImportCustomEmojisProcessorService,
+ ImportAntennasProcessorService,
DeleteAccountProcessorService,
DeleteFileProcessorService,
CleanRemoteFilesProcessorService,
diff --git a/packages/backend/src/queue/RelationshipQueueProcessorsService.ts b/packages/backend/src/queue/RelationshipQueueProcessorsService.ts
index af086fa4e7..736b4fa80d 100644
--- a/packages/backend/src/queue/RelationshipQueueProcessorsService.ts
+++ b/packages/backend/src/queue/RelationshipQueueProcessorsService.ts
@@ -17,7 +17,7 @@ export class RelationshipQueueProcessorsService {
@bindThis
public start(q: Bull.Queue): void {
- const maxJobs = (this.config.deliverJobConcurrency ?? 128) / 4; // conservative?
+ const maxJobs = this.config.relashionshipJobConcurrency ?? 16;
q.process('follow', maxJobs, (job) => this.relationshipProcessorService.processFollow(job));
q.process('unfollow', maxJobs, (job) => this.relationshipProcessorService.processUnfollow(job));
q.process('block', maxJobs, (job) => this.relationshipProcessorService.processBlock(job));
diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts
index 0e99b7bcd2..f293bd4d7e 100644
--- a/packages/backend/src/queue/processors/DeliverProcessorService.ts
+++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts
@@ -79,10 +79,7 @@ export class DeliverProcessorService {
// Update stats
this.federatedInstanceService.fetch(host).then(i => {
if (i.isNotResponding) {
- this.instancesRepository.update(i.id, {
- isNotResponding: false,
- });
- this.federatedInstanceService.updateCachePartial(host, {
+ this.federatedInstanceService.update(i.id, {
isNotResponding: false,
});
}
@@ -101,10 +98,7 @@ export class DeliverProcessorService {
// Update stats
this.federatedInstanceService.fetch(host).then(i => {
if (!i.isNotResponding) {
- this.instancesRepository.update(i.id, {
- isNotResponding: true,
- });
- this.federatedInstanceService.updateCachePartial(host, {
+ this.federatedInstanceService.update(i.id, {
isNotResponding: true,
});
}
@@ -123,10 +117,7 @@ export class DeliverProcessorService {
// 相手ãŒé–‰éŽ–ã—ã¦ã„ã‚‹ã“ã¨ã‚’明示ã—ã¦ã„ã‚‹ãŸã‚ã€é…é€åœæ­¢ã™ã‚‹
if (job.data.isSharedInbox && res.statusCode === 410) {
this.federatedInstanceService.fetch(host).then(i => {
- this.instancesRepository.update(i.id, {
- isSuspended: true,
- });
- this.federatedInstanceService.updateCachePartial(host, {
+ this.federatedInstanceService.update(i.id, {
isSuspended: true,
});
});
diff --git a/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts
new file mode 100644
index 0000000000..894903e79b
--- /dev/null
+++ b/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts
@@ -0,0 +1,103 @@
+import fs from 'node:fs';
+import { Inject, Injectable } from '@nestjs/common';
+import { format as DateFormat } from 'date-fns';
+import { In } from 'typeorm';
+import { DI } from '@/di-symbols.js';
+import type { AntennasRepository, UsersRepository, UserListJoiningsRepository, User } from '@/models/index.js';
+import type { Config } from '@/config.js';
+import Logger from '@/logger.js';
+import { DriveService } from '@/core/DriveService.js';
+import { bindThis } from '@/decorators.js';
+import { createTemp } from '@/misc/create-temp.js';
+import { UtilityService } from '@/core/UtilityService.js';
+import { QueueLoggerService } from '../QueueLoggerService.js';
+import type { DBExportAntennasData } from '../types.js';
+import type Bull from 'bull';
+
+@Injectable()
+export class ExportAntennasProcessorService {
+ private logger: Logger;
+
+ constructor (
+ @Inject(DI.config)
+ private config: Config,
+
+ @Inject(DI.usersRepository)
+ private usersRepository: UsersRepository,
+
+ @Inject(DI.antennasRepository)
+ private antennsRepository: AntennasRepository,
+
+ @Inject(DI.userListJoiningsRepository)
+ private userListJoiningsRepository: UserListJoiningsRepository,
+
+ private driveService: DriveService,
+ private utilityService: UtilityService,
+ private queueLoggerService: QueueLoggerService,
+ ) {
+ this.logger = this.queueLoggerService.logger.createSubLogger('export-antennas');
+ }
+
+ @bindThis
+ public async process(job: Bull.Job<DBExportAntennasData>, done: () => void): Promise<void> {
+ const user = await this.usersRepository.findOneBy({ id: job.data.user.id });
+ if (user == null) {
+ done();
+ return;
+ }
+ const [path, cleanup] = await createTemp();
+ const stream = fs.createWriteStream(path, { flags: 'a' });
+ const write = (input: string): Promise<void> => {
+ return new Promise((resolve, reject) => {
+ stream.write(input, err => {
+ if (err) {
+ this.logger.error(err);
+ reject();
+ } else {
+ resolve();
+ }
+ });
+ });
+ };
+ try {
+ const antennas = await this.antennsRepository.findBy({ userId: job.data.user.id });
+ write('[');
+ for (const [index, antenna] of antennas.entries()) {
+ let users: User[] | undefined;
+ if (antenna.userListId !== null) {
+ const joinings = await this.userListJoiningsRepository.findBy({ userListId: antenna.userListId });
+ users = await this.usersRepository.findBy({
+ id: In(joinings.map(j => j.userId)),
+ });
+ }
+ write(JSON.stringify({
+ name: antenna.name,
+ src: antenna.src,
+ keywords: antenna.keywords,
+ excludeKeywords: antenna.excludeKeywords,
+ users: antenna.users,
+ userListAccts: typeof users !== 'undefined' ? users.map((u) => {
+ return this.utilityService.getFullApAccount(u.username, u.host); // acct
+ }) : null,
+ caseSensitive: antenna.caseSensitive,
+ withReplies: antenna.withReplies,
+ withFile: antenna.withFile,
+ notify: antenna.notify,
+ }));
+ if (antennas.length - 1 !== index) {
+ write(', ');
+ }
+ }
+ write(']');
+ stream.end();
+
+ const fileName = 'antennas-' + DateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json';
+ const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'json' });
+ this.logger.succ('Exported to: ' + driveFile.id);
+ } finally {
+ cleanup();
+ done();
+ }
+ }
+}
+
diff --git a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts
index a020006732..c7b54070d6 100644
--- a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts
+++ b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts
@@ -106,7 +106,7 @@ export class ExportBlockingProcessorService {
this.logger.succ(`Exported to: ${path}`);
const fileName = 'blocking-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
- const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true });
+ const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'csv' });
this.logger.succ(`Exported to: ${driveFile.id}`);
} finally {
diff --git a/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts b/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts
index daefcdf2f5..f2f2383a88 100644
--- a/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts
+++ b/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts
@@ -121,7 +121,7 @@ export class ExportFavoritesProcessorService {
this.logger.succ(`Exported to: ${path}`);
const fileName = 'favorites-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json';
- const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true });
+ const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'json' });
this.logger.succ(`Exported to: ${driveFile.id}`);
} finally {
diff --git a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts
index 59443de57f..fa9c1ac1ea 100644
--- a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts
+++ b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts
@@ -110,7 +110,7 @@ export class ExportFollowingProcessorService {
this.logger.succ(`Exported to: ${path}`);
const fileName = 'following-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
- const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true });
+ const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'csv' });
this.logger.succ(`Exported to: ${driveFile.id}`);
} finally {
diff --git a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts
index a2a718b892..b14bf5f5b1 100644
--- a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts
+++ b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts
@@ -110,7 +110,7 @@ export class ExportMutingProcessorService {
this.logger.succ(`Exported to: ${path}`);
const fileName = 'mute-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
- const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true });
+ const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'csv' });
this.logger.succ(`Exported to: ${driveFile.id}`);
} finally {
diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts
index 1aa20d6f1d..e4f12ad101 100644
--- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts
+++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts
@@ -117,7 +117,7 @@ export class ExportNotesProcessorService {
this.logger.succ(`Exported to: ${path}`);
const fileName = 'notes-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json';
- const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true });
+ const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'json' });
this.logger.succ(`Exported to: ${driveFile.id}`);
} finally {
diff --git a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts
index ce8ed2f5e8..54bde44044 100644
--- a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts
+++ b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts
@@ -86,7 +86,7 @@ export class ExportUserListsProcessorService {
this.logger.succ(`Exported to: ${path}`);
const fileName = 'user-lists-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
- const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true });
+ const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'csv' });
this.logger.succ(`Exported to: ${driveFile.id}`);
} finally {
diff --git a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts
new file mode 100644
index 0000000000..d06131b8c8
--- /dev/null
+++ b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts
@@ -0,0 +1,96 @@
+import { Injectable, Inject } from '@nestjs/common';
+import Ajv from 'ajv';
+import { IdService } from '@/core/IdService.js';
+import { GlobalEventService } from '@/core/GlobalEventService.js';
+import Logger from '@/logger.js';
+import type { AntennasRepository } from '@/models/index.js';
+import { DI } from '@/di-symbols.js';
+import { bindThis } from '@/decorators.js';
+import { QueueLoggerService } from '../QueueLoggerService.js';
+import { DBAntennaImportJobData } from '../types.js';
+import type Bull from 'bull';
+
+const validate = new Ajv().compile({
+ type: 'object',
+ properties: {
+ name: { type: 'string', minLength: 1, maxLength: 100 },
+ src: { type: 'string', enum: ['home', 'all', 'users', 'list'] },
+ userListAccts: {
+ type: 'array',
+ items: {
+ type: 'string',
+ },
+ nullable: true,
+ },
+ keywords: { type: 'array', items: {
+ type: 'array', items: {
+ type: 'string',
+ },
+ } },
+ excludeKeywords: { type: 'array', items: {
+ type: 'array', items: {
+ type: 'string',
+ },
+ } },
+ users: { type: 'array', items: {
+ type: 'string',
+ } },
+ caseSensitive: { type: 'boolean' },
+ withReplies: { type: 'boolean' },
+ withFile: { type: 'boolean' },
+ notify: { type: 'boolean' },
+ },
+ required: ['name', 'src', 'keywords', 'excludeKeywords', 'users', 'caseSensitive', 'withReplies', 'withFile', 'notify'],
+});
+
+@Injectable()
+export class ImportAntennasProcessorService {
+ private logger: Logger;
+
+ constructor (
+ @Inject(DI.antennasRepository)
+ private antennasRepository: AntennasRepository,
+
+ private queueLoggerService: QueueLoggerService,
+ private idService: IdService,
+ private globalEventService: GlobalEventService,
+ ) {
+ this.logger = this.queueLoggerService.logger.createSubLogger('import-antennas');
+ }
+
+ @bindThis
+ public async process(job: Bull.Job<DBAntennaImportJobData>, done: () => void): Promise<void> {
+ const now = new Date();
+ try {
+ for (const antenna of job.data.antenna) {
+ if (antenna.keywords.length === 0 || antenna.keywords[0].every(x => x === '')) continue;
+ if (!validate(antenna)) {
+ this.logger.warn('Validation Failed');
+ continue;
+ }
+ const result = await this.antennasRepository.insert({
+ id: this.idService.genId(),
+ createdAt: now,
+ lastUsedAt: now,
+ userId: job.data.user.id,
+ name: antenna.name,
+ src: antenna.src === 'list' && antenna.userListAccts ? 'users' : antenna.src,
+ userListId: null,
+ keywords: antenna.keywords,
+ excludeKeywords: antenna.excludeKeywords,
+ users: (antenna.src === 'list' && antenna.userListAccts !== null ? antenna.userListAccts : antenna.users).filter(Boolean),
+ caseSensitive: antenna.caseSensitive,
+ withReplies: antenna.withReplies,
+ withFile: antenna.withFile,
+ notify: antenna.notify,
+ }).then(x => this.antennasRepository.findOneByOrFail(x.identifiers[0]));
+ this.logger.succ('Antenna created: ' + result.id);
+ this.globalEventService.publishInternalEvent('antennaCreated', result);
+ }
+ } catch (err: any) {
+ this.logger.error(err);
+ } finally {
+ done();
+ }
+ }
+}
diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts
index ed7f38d013..ab8b1e9e22 100644
--- a/packages/backend/src/queue/processors/InboxProcessorService.ts
+++ b/packages/backend/src/queue/processors/InboxProcessorService.ts
@@ -84,9 +84,9 @@ export class InboxProcessorService {
// HTTP-Signature keyIdã‚’å…ƒã«DBã‹ã‚‰å–å¾—
let authUser: {
- user: RemoteUser;
- key: UserPublickey | null;
- } | null = await this.apDbResolverService.getAuthUserFromKeyId(signature.keyId);
+ user: RemoteUser;
+ key: UserPublickey | null;
+ } | null = await this.apDbResolverService.getAuthUserFromKeyId(signature.keyId);
// keyIdã§ã‚ã‹ã‚‰ãªã‘れã°ã€activity.actorã‚’å…ƒã«DBã‹ã‚‰å–å¾— || activity.actorã‚’å…ƒã«ãƒªãƒ¢ãƒ¼ãƒˆã‹ã‚‰å–å¾—
if (authUser == null) {
@@ -174,13 +174,10 @@ export class InboxProcessorService {
// Update stats
this.federatedInstanceService.fetch(authUser.user.host).then(i => {
- this.instancesRepository.update(i.id, {
+ this.federatedInstanceService.update(i.id, {
latestRequestReceivedAt: new Date(),
isNotResponding: false,
});
- this.federatedInstanceService.updateCachePartial(host, {
- isNotResponding: false,
- });
this.fetchInstanceMetadataService.fetchInstanceMetadata(i);
diff --git a/packages/backend/src/queue/processors/RelationshipProcessorService.ts b/packages/backend/src/queue/processors/RelationshipProcessorService.ts
index a5006dcf03..ff454df455 100644
--- a/packages/backend/src/queue/processors/RelationshipProcessorService.ts
+++ b/packages/backend/src/queue/processors/RelationshipProcessorService.ts
@@ -10,6 +10,7 @@ import { QueueLoggerService } from '../QueueLoggerService.js';
import { RelationshipJobData } from '../types.js';
import type { UsersRepository } from '@/models/index.js';
import { DI } from '@/di-symbols.js';
+import { LocalUser, RemoteUser } from '@/models/entities/User.js';
@Injectable()
export class RelationshipProcessorService {
@@ -39,7 +40,7 @@ export class RelationshipProcessorService {
const [follower, followee] = await Promise.all([
this.usersRepository.findOneByOrFail({ id: job.data.from.id }),
this.usersRepository.findOneByOrFail({ id: job.data.to.id }),
- ]);
+ ]) as [LocalUser | RemoteUser, LocalUser | RemoteUser];
await this.userFollowingService.unfollow(follower, followee, job.data.silent);
return 'ok';
}
diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts
index 23c973d449..776dd3aa12 100644
--- a/packages/backend/src/queue/types.ts
+++ b/packages/backend/src/queue/types.ts
@@ -1,3 +1,4 @@
+import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js';
import type { DriveFile } from '@/models/entities/DriveFile.js';
import type { Note } from '@/models/entities/Note.js';
import type { User } from '@/models/entities/User.js';
@@ -33,12 +34,14 @@ export type DbJobData<T extends keyof DbJobMap> = DbJobMap[T];
export type DbJobMap = {
deleteDriveFiles: DbJobDataWithUser;
exportCustomEmojis: DbJobDataWithUser;
+ exportAntennas: DBExportAntennasData;
exportNotes: DbJobDataWithUser;
exportFavorites: DbJobDataWithUser;
exportFollowing: DbExportFollowingData;
exportMuting: DbJobDataWithUser;
exportBlocking: DbJobDataWithUser;
exportUserLists: DbJobDataWithUser;
+ importAntennas: DBAntennaImportJobData;
importFollowing: DbUserImportJobData;
importFollowingToDb: DbUserImportToDbJobData;
importMuting: DbUserImportJobData;
@@ -59,6 +62,10 @@ export type DbExportFollowingData = {
excludeInactive: boolean;
};
+export type DBExportAntennasData = {
+ user: ThinUser
+}
+
export type DbUserDeleteJobData = {
user: ThinUser;
soft?: boolean;
@@ -69,6 +76,11 @@ export type DbUserImportJobData = {
fileId: DriveFile['id'];
};
+export type DBAntennaImportJobData = {
+ user: ThinUser,
+ antenna: Antenna
+}
+
export type DbUserImportToDbJobData = {
user: ThinUser;
target: string;
diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index e13e9265ab..e675d9cf1b 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -11,7 +11,7 @@ import * as url from '@/misc/prelude/url.js';
import type { Config } from '@/config.js';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { QueueService } from '@/core/QueueService.js';
-import type { LocalUser, User } from '@/models/entities/User.js';
+import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js';
import { UserKeypairService } from '@/core/UserKeypairService.js';
import type { Following } from '@/models/entities/Following.js';
import { countIf } from '@/misc/prelude/array.js';
@@ -630,7 +630,7 @@ export class ActivityPubServerService {
id: request.params.followee,
host: Not(IsNull()),
}),
- ]);
+ ]) as [LocalUser | RemoteUser | null, LocalUser | RemoteUser | null];
if (follower == null || followee == null) {
reply.code(404);
@@ -665,7 +665,7 @@ export class ActivityPubServerService {
id: followRequest.followeeId,
host: Not(IsNull()),
}),
- ]);
+ ]) as [LocalUser | RemoteUser | null, LocalUser | RemoteUser | null];
if (follower == null || followee == null) {
reply.code(404);
diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts
index 794fa76d9e..98329ddffa 100644
--- a/packages/backend/src/server/FileServerService.ts
+++ b/packages/backend/src/server/FileServerService.ts
@@ -297,7 +297,8 @@ export class FileServerService {
} else if ('badge' in request.query) {
const mask = (await sharpBmp(file.path, file.mime))
.resize(96, 96, {
- fit: 'inside',
+ fit: 'contain',
+ position: 'centre',
withoutEnlargement: false,
})
.greyscale()
@@ -453,7 +454,8 @@ export class FileServerService {
fileRole: 'original',
file,
filename: file.name,
- mime: file.type,
+ // å¤ã„ファイルã¯ä¿®æ­£å‰ã®mimeã‚’æŒã£ã¦ã„ã‚‹ã®ã§ã§ãã‚‹ã ã‘修正ã—ã¦ã‚ã’ã‚‹
+ mime: this.fileInfoService.fixMime(file.type),
ext: null,
path,
};
diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts
index e722563036..9bf8deb221 100644
--- a/packages/backend/src/server/WellKnownServerService.ts
+++ b/packages/backend/src/server/WellKnownServerService.ts
@@ -8,6 +8,7 @@ import { escapeAttribute, escapeValue } from '@/misc/prelude/xml.js';
import type { User } from '@/models/entities/User.js';
import * as Acct from '@/misc/acct.js';
import { NodeinfoServerService } from './NodeinfoServerService.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
import type { FindOptionsWhere } from 'typeorm';
import { bindThis } from '@/decorators.js';
import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
@@ -23,6 +24,7 @@ export class WellKnownServerService {
private usersRepository: UsersRepository,
private nodeinfoServerService: NodeinfoServerService,
+ private userEntityService: UserEntityService,
) {
//this.createServer = this.createServer.bind(this);
}
@@ -130,7 +132,7 @@ fastify.get('/.well-known/change-password', async (request, reply) => {
const self = {
rel: 'self',
type: 'application/activity+json',
- href: `${this.config.url}/users/${user.id}`,
+ href: this.userEntityService.genLocalUserUri(user.id),
};
const profilePage = {
rel: 'http://webfinger.net/rel/profile-page',
diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts
index bf5cb20918..e3483c82c6 100644
--- a/packages/backend/src/server/api/ApiCallService.ts
+++ b/packages/backend/src/server/api/ApiCallService.ts
@@ -261,6 +261,17 @@ export class ApiCallService implements OnApplicationShutdown {
}
}
+ if (ep.meta.prohibitMoved) {
+ if (user?.movedToUri) {
+ throw new ApiError({
+ message: 'You have moved your account.',
+ code: 'YOUR_ACCOUNT_MOVED',
+ id: '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31',
+ httpStatusCode: 403,
+ });
+ }
+ }
+
if ((ep.meta.requireModerator || ep.meta.requireAdmin) && !user!.isRoot) {
const myRoles = await this.roleService.getUserRoles(user!.id);
if (ep.meta.requireModerator && !myRoles.some(r => r.isModerator || r.isAdministrator)) {
diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts
index 689f90287e..ee1aae5b6c 100644
--- a/packages/backend/src/server/api/EndpointsModule.ts
+++ b/packages/backend/src/server/api/EndpointsModule.ts
@@ -29,6 +29,7 @@ import * as ep___admin_emoji_list from './endpoints/admin/emoji/list.js';
import * as ep___admin_emoji_removeAliasesBulk from './endpoints/admin/emoji/remove-aliases-bulk.js';
import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-aliases-bulk.js';
import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js';
+import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js';
import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js';
import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js';
import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js';
@@ -193,6 +194,7 @@ import * as ep___i_exportMute from './endpoints/i/export-mute.js';
import * as ep___i_exportNotes from './endpoints/i/export-notes.js';
import * as ep___i_exportFavorites from './endpoints/i/export-favorites.js';
import * as ep___i_exportUserLists from './endpoints/i/export-user-lists.js';
+import * as ep___i_exportAntennas from './endpoints/i/export-antennas.js';
import * as ep___i_favorites from './endpoints/i/favorites.js';
import * as ep___i_gallery_likes from './endpoints/i/gallery/likes.js';
import * as ep___i_gallery_posts from './endpoints/i/gallery/posts.js';
@@ -201,6 +203,7 @@ import * as ep___i_importBlocking from './endpoints/i/import-blocking.js';
import * as ep___i_importFollowing from './endpoints/i/import-following.js';
import * as ep___i_importMuting from './endpoints/i/import-muting.js';
import * as ep___i_importUserLists from './endpoints/i/import-user-lists.js';
+import * as ep___i_importAntennas from './endpoints/i/import-antennas.js';
import * as ep___i_notifications from './endpoints/i/notifications.js';
import * as ep___i_pageLikes from './endpoints/i/page-likes.js';
import * as ep___i_pages from './endpoints/i/pages.js';
@@ -222,7 +225,6 @@ import * as ep___i_unpin from './endpoints/i/unpin.js';
import * as ep___i_updateEmail from './endpoints/i/update-email.js';
import * as ep___i_update from './endpoints/i/update.js';
import * as ep___i_move from './endpoints/i/move.js';
-import * as ep___i_knownAs from './endpoints/i/known-as.js';
import * as ep___i_webhooks_create from './endpoints/i/webhooks/create.js';
import * as ep___i_webhooks_show from './endpoints/i/webhooks/show.js';
import * as ep___i_webhooks_list from './endpoints/i/webhooks/list.js';
@@ -330,6 +332,7 @@ import * as ep___users_search from './endpoints/users/search.js';
import * as ep___users_show from './endpoints/users/show.js';
import * as ep___users_stats from './endpoints/users/stats.js';
import * as ep___users_achievements from './endpoints/users/achievements.js';
+import * as ep___users_updateMemo from './endpoints/users/update-memo.js';
import * as ep___fetchRss from './endpoints/fetch-rss.js';
import * as ep___retention from './endpoints/retention.js';
import { GetterService } from './GetterService.js';
@@ -364,6 +367,7 @@ const $admin_emoji_list: Provider = { provide: 'ep:admin/emoji/list', useClass:
const $admin_emoji_removeAliasesBulk: Provider = { provide: 'ep:admin/emoji/remove-aliases-bulk', useClass: ep___admin_emoji_removeAliasesBulk.default };
const $admin_emoji_setAliasesBulk: Provider = { provide: 'ep:admin/emoji/set-aliases-bulk', useClass: ep___admin_emoji_setAliasesBulk.default };
const $admin_emoji_setCategoryBulk: Provider = { provide: 'ep:admin/emoji/set-category-bulk', useClass: ep___admin_emoji_setCategoryBulk.default };
+const $admin_emoji_setLicenseBulk: Provider = { provide: 'ep:admin/emoji/set-license-bulk', useClass: ep___admin_emoji_setLicenseBulk.default };
const $admin_emoji_update: Provider = { provide: 'ep:admin/emoji/update', useClass: ep___admin_emoji_update.default };
const $admin_federation_deleteAllFiles: Provider = { provide: 'ep:admin/federation/delete-all-files', useClass: ep___admin_federation_deleteAllFiles.default };
const $admin_federation_refreshRemoteInstanceMetadata: Provider = { provide: 'ep:admin/federation/refresh-remote-instance-metadata', useClass: ep___admin_federation_refreshRemoteInstanceMetadata.default };
@@ -528,6 +532,7 @@ const $i_exportMute: Provider = { provide: 'ep:i/export-mute', useClass: ep___i_
const $i_exportNotes: Provider = { provide: 'ep:i/export-notes', useClass: ep___i_exportNotes.default };
const $i_exportFavorites: Provider = { provide: 'ep:i/export-favorites', useClass: ep___i_exportFavorites.default };
const $i_exportUserLists: Provider = { provide: 'ep:i/export-user-lists', useClass: ep___i_exportUserLists.default };
+const $i_exportAntennas: Provider = { provide: 'ep:i/export-antennas', useClass: ep___i_exportAntennas.default };
const $i_favorites: Provider = { provide: 'ep:i/favorites', useClass: ep___i_favorites.default };
const $i_gallery_likes: Provider = { provide: 'ep:i/gallery/likes', useClass: ep___i_gallery_likes.default };
const $i_gallery_posts: Provider = { provide: 'ep:i/gallery/posts', useClass: ep___i_gallery_posts.default };
@@ -536,6 +541,7 @@ const $i_importBlocking: Provider = { provide: 'ep:i/import-blocking', useClass:
const $i_importFollowing: Provider = { provide: 'ep:i/import-following', useClass: ep___i_importFollowing.default };
const $i_importMuting: Provider = { provide: 'ep:i/import-muting', useClass: ep___i_importMuting.default };
const $i_importUserLists: Provider = { provide: 'ep:i/import-user-lists', useClass: ep___i_importUserLists.default };
+const $i_importAntennas: Provider = { provide: 'ep:i/import-antennas', useClass: ep___i_importAntennas.default };
const $i_notifications: Provider = { provide: 'ep:i/notifications', useClass: ep___i_notifications.default };
const $i_pageLikes: Provider = { provide: 'ep:i/page-likes', useClass: ep___i_pageLikes.default };
const $i_pages: Provider = { provide: 'ep:i/pages', useClass: ep___i_pages.default };
@@ -557,7 +563,6 @@ const $i_unpin: Provider = { provide: 'ep:i/unpin', useClass: ep___i_unpin.defau
const $i_updateEmail: Provider = { provide: 'ep:i/update-email', useClass: ep___i_updateEmail.default };
const $i_update: Provider = { provide: 'ep:i/update', useClass: ep___i_update.default };
const $i_move: Provider = { provide: 'ep:i/move', useClass: ep___i_move.default };
-const $i_knownAs: Provider = { provide: 'ep:i/known-as', useClass: ep___i_knownAs.default };
const $i_webhooks_create: Provider = { provide: 'ep:i/webhooks/create', useClass: ep___i_webhooks_create.default };
const $i_webhooks_list: Provider = { provide: 'ep:i/webhooks/list', useClass: ep___i_webhooks_list.default };
const $i_webhooks_show: Provider = { provide: 'ep:i/webhooks/show', useClass: ep___i_webhooks_show.default };
@@ -665,6 +670,7 @@ const $users_search: Provider = { provide: 'ep:users/search', useClass: ep___use
const $users_show: Provider = { provide: 'ep:users/show', useClass: ep___users_show.default };
const $users_stats: Provider = { provide: 'ep:users/stats', useClass: ep___users_stats.default };
const $users_achievements: Provider = { provide: 'ep:users/achievements', useClass: ep___users_achievements.default };
+const $users_updateMemo: Provider = { provide: 'ep:users/update-memo', useClass: ep___users_updateMemo.default };
const $fetchRss: Provider = { provide: 'ep:fetch-rss', useClass: ep___fetchRss.default };
const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention.default };
@@ -703,6 +709,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
$admin_emoji_removeAliasesBulk,
$admin_emoji_setAliasesBulk,
$admin_emoji_setCategoryBulk,
+ $admin_emoji_setLicenseBulk,
$admin_emoji_update,
$admin_federation_deleteAllFiles,
$admin_federation_refreshRemoteInstanceMetadata,
@@ -867,6 +874,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
$i_exportNotes,
$i_exportFavorites,
$i_exportUserLists,
+ $i_exportAntennas,
$i_favorites,
$i_gallery_likes,
$i_gallery_posts,
@@ -875,6 +883,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
$i_importFollowing,
$i_importMuting,
$i_importUserLists,
+ $i_importAntennas,
$i_notifications,
$i_pageLikes,
$i_pages,
@@ -896,7 +905,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
$i_updateEmail,
$i_update,
$i_move,
- $i_knownAs,
$i_webhooks_create,
$i_webhooks_list,
$i_webhooks_show,
@@ -1004,6 +1012,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
$users_show,
$users_stats,
$users_achievements,
+ $users_updateMemo,
$fetchRss,
$retention,
],
@@ -1036,6 +1045,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
$admin_emoji_removeAliasesBulk,
$admin_emoji_setAliasesBulk,
$admin_emoji_setCategoryBulk,
+ $admin_emoji_setLicenseBulk,
$admin_emoji_update,
$admin_federation_deleteAllFiles,
$admin_federation_refreshRemoteInstanceMetadata,
@@ -1200,6 +1210,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
$i_exportNotes,
$i_exportFavorites,
$i_exportUserLists,
+ $i_exportAntennas,
$i_favorites,
$i_gallery_likes,
$i_gallery_posts,
@@ -1208,6 +1219,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
$i_importFollowing,
$i_importMuting,
$i_importUserLists,
+ $i_importAntennas,
$i_notifications,
$i_pageLikes,
$i_pages,
@@ -1229,7 +1241,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
$i_updateEmail,
$i_update,
$i_move,
- $i_knownAs,
$i_webhooks_create,
$i_webhooks_list,
$i_webhooks_show,
@@ -1335,6 +1346,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
$users_show,
$users_stats,
$users_achievements,
+ $users_updateMemo,
$fetchRss,
$retention,
],
diff --git a/packages/backend/src/server/api/RateLimiterService.ts b/packages/backend/src/server/api/RateLimiterService.ts
index 1f8915ecca..fe2db1d66a 100644
--- a/packages/backend/src/server/api/RateLimiterService.ts
+++ b/packages/backend/src/server/api/RateLimiterService.ts
@@ -1,6 +1,6 @@
import { Inject, Injectable } from '@nestjs/common';
import Limiter from 'ratelimiter';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import type Logger from '@/logger.js';
import { LoggerService } from '@/core/LoggerService.js';
diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts
index fbabf47aff..b2bd7d82e7 100644
--- a/packages/backend/src/server/api/SignupApiService.ts
+++ b/packages/backend/src/server/api/SignupApiService.ts
@@ -1,6 +1,7 @@
import { Inject, Injectable } from '@nestjs/common';
import rndstr from 'rndstr';
import bcrypt from 'bcryptjs';
+import { IsNull } from 'typeorm';
import { DI } from '@/di-symbols.js';
import type { RegistrationTicketsRepository, UsedUsernamesRepository, UserPendingsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js';
import type { Config } from '@/config.js';
@@ -15,7 +16,6 @@ import { FastifyReplyError } from '@/misc/fastify-reply-error.js';
import { bindThis } from '@/decorators.js';
import { SigninService } from './SigninService.js';
import type { FastifyRequest, FastifyReply } from 'fastify';
-import { IsNull } from 'typeorm';
@Injectable()
export class SignupApiService {
@@ -137,6 +137,11 @@ export class SignupApiService {
throw new FastifyReplyError(400, 'USED_USERNAME');
}
+ const isPreserved = instance.preservedUsernames.map(x => x.toLowerCase()).includes(username.toLowerCase());
+ if (isPreserved) {
+ throw new FastifyReplyError(400, 'DENIED_USERNAME');
+ }
+
const code = rndstr('a-z0-9', 16);
// Generate hash of password
diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts
index 769a4490d6..258e8de034 100644
--- a/packages/backend/src/server/api/StreamingApiServerService.ts
+++ b/packages/backend/src/server/api/StreamingApiServerService.ts
@@ -1,6 +1,6 @@
import { EventEmitter } from 'events';
import { Inject, Injectable } from '@nestjs/common';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import * as websocket from 'websocket';
import { DI } from '@/di-symbols.js';
import type { UsersRepository, BlockingsRepository, ChannelFollowingsRepository, FollowingsRepository, MutingsRepository, UserProfilesRepository, RenoteMutingsRepository } from '@/models/index.js';
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index d0fe6a57c1..09bd7cbff4 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -29,6 +29,7 @@ import * as ep___admin_emoji_list from './endpoints/admin/emoji/list.js';
import * as ep___admin_emoji_removeAliasesBulk from './endpoints/admin/emoji/remove-aliases-bulk.js';
import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-aliases-bulk.js';
import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js';
+import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js';
import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js';
import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js';
import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js';
@@ -193,6 +194,7 @@ import * as ep___i_exportMute from './endpoints/i/export-mute.js';
import * as ep___i_exportNotes from './endpoints/i/export-notes.js';
import * as ep___i_exportFavorites from './endpoints/i/export-favorites.js';
import * as ep___i_exportUserLists from './endpoints/i/export-user-lists.js';
+import * as ep___i_exportAntennas from './endpoints/i/export-antennas.js';
import * as ep___i_favorites from './endpoints/i/favorites.js';
import * as ep___i_gallery_likes from './endpoints/i/gallery/likes.js';
import * as ep___i_gallery_posts from './endpoints/i/gallery/posts.js';
@@ -201,6 +203,7 @@ import * as ep___i_importBlocking from './endpoints/i/import-blocking.js';
import * as ep___i_importFollowing from './endpoints/i/import-following.js';
import * as ep___i_importMuting from './endpoints/i/import-muting.js';
import * as ep___i_importUserLists from './endpoints/i/import-user-lists.js';
+import * as ep___i_importAntennas from './endpoints/i/import-antennas.js';
import * as ep___i_notifications from './endpoints/i/notifications.js';
import * as ep___i_pageLikes from './endpoints/i/page-likes.js';
import * as ep___i_pages from './endpoints/i/pages.js';
@@ -222,7 +225,6 @@ import * as ep___i_unpin from './endpoints/i/unpin.js';
import * as ep___i_updateEmail from './endpoints/i/update-email.js';
import * as ep___i_update from './endpoints/i/update.js';
import * as ep___i_move from './endpoints/i/move.js';
-import * as ep___i_knownAs from './endpoints/i/known-as.js';
import * as ep___i_webhooks_create from './endpoints/i/webhooks/create.js';
import * as ep___i_webhooks_show from './endpoints/i/webhooks/show.js';
import * as ep___i_webhooks_list from './endpoints/i/webhooks/list.js';
@@ -330,6 +332,7 @@ import * as ep___users_search from './endpoints/users/search.js';
import * as ep___users_show from './endpoints/users/show.js';
import * as ep___users_stats from './endpoints/users/stats.js';
import * as ep___users_achievements from './endpoints/users/achievements.js';
+import * as ep___users_updateMemo from './endpoints/users/update-memo.js';
import * as ep___fetchRss from './endpoints/fetch-rss.js';
import * as ep___retention from './endpoints/retention.js';
@@ -362,6 +365,7 @@ const eps = [
['admin/emoji/remove-aliases-bulk', ep___admin_emoji_removeAliasesBulk],
['admin/emoji/set-aliases-bulk', ep___admin_emoji_setAliasesBulk],
['admin/emoji/set-category-bulk', ep___admin_emoji_setCategoryBulk],
+ ['admin/emoji/set-license-bulk', ep___admin_emoji_setLicenseBulk],
['admin/emoji/update', ep___admin_emoji_update],
['admin/federation/delete-all-files', ep___admin_federation_deleteAllFiles],
['admin/federation/refresh-remote-instance-metadata', ep___admin_federation_refreshRemoteInstanceMetadata],
@@ -526,6 +530,7 @@ const eps = [
['i/export-notes', ep___i_exportNotes],
['i/export-favorites', ep___i_exportFavorites],
['i/export-user-lists', ep___i_exportUserLists],
+ ['i/export-antennas', ep___i_exportAntennas],
['i/favorites', ep___i_favorites],
['i/gallery/likes', ep___i_gallery_likes],
['i/gallery/posts', ep___i_gallery_posts],
@@ -534,6 +539,7 @@ const eps = [
['i/import-following', ep___i_importFollowing],
['i/import-muting', ep___i_importMuting],
['i/import-user-lists', ep___i_importUserLists],
+ ['i/import-antennas', ep___i_importAntennas],
['i/notifications', ep___i_notifications],
['i/page-likes', ep___i_pageLikes],
['i/pages', ep___i_pages],
@@ -554,8 +560,7 @@ const eps = [
['i/unpin', ep___i_unpin],
['i/update-email', ep___i_updateEmail],
['i/update', ep___i_update],
- //['i/move', ep___i_move],
- //['i/known-as', ep___i_knownAs],
+ ['i/move', ep___i_move],
['i/webhooks/create', ep___i_webhooks_create],
['i/webhooks/list', ep___i_webhooks_list],
['i/webhooks/show', ep___i_webhooks_show],
@@ -663,6 +668,7 @@ const eps = [
['users/show', ep___users_show],
['users/stats', ep___users_stats],
['users/achievements', ep___users_achievements],
+ ['users/update-memo', ep___users_updateMemo],
['fetch-rss', ep___fetchRss],
['retention', ep___retention],
];
@@ -701,6 +707,12 @@ export interface IEndpointMeta {
readonly requireRolePolicy?: keyof RolePolicies;
/**
+ * 引ã£è¶Šã—済ã¿ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã‚ˆã‚‹ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’ç¦æ­¢ã™ã‚‹ã‹
+ * çœç•¥ã—ãŸå ´åˆã¯ false ã¨ã—ã¦è§£é‡ˆã•れã¾ã™ã€‚
+ */
+ readonly prohibitMoved?: boolean;
+
+ /**
* エンドãƒã‚¤ãƒ³ãƒˆã®ãƒªãƒŸãƒ†ãƒ¼ã‚·ãƒ§ãƒ³ã«é–¢ã™ã‚‹ã‚„ã¤
* çœç•¥ã—ãŸå ´åˆã¯ãƒªãƒŸãƒ†ãƒ¼ã‚·ãƒ§ãƒ³ã¯ç„¡ã„ã‚‚ã®ã¨ã—ã¦è§£é‡ˆã•れã¾ã™ã€‚
*/
diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts
index bac8ae16e5..8a3541dffe 100644
--- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts
@@ -52,6 +52,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
const { account, secret } = await this.signupService.signup({
username: ps.username,
password: ps.password,
+ ignorePreservedUsernames: true,
});
const res = await this.userEntityService.pack(account, account, {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
index 814668294f..4aa4ad82b4 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
@@ -87,12 +87,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
//const emojis = await q.take(ps.limit).getMany();
emojis = await q.getMany();
+ const queryarry = ps.query.match(/\:([a-z0-9_]*)\:/g);
- emojis = emojis.filter(emoji =>
- emoji.name.includes(ps.query!) ||
- emoji.aliases.some(a => a.includes(ps.query!)) ||
- emoji.category?.includes(ps.query!));
-
+ if (queryarry) {
+ emojis = emojis.filter(emoji =>
+ queryarry.includes(`:${emoji.name}:`)
+ );
+ } else {
+ emojis = emojis.filter(emoji =>
+ emoji.name.includes(ps.query!) ||
+ emoji.aliases.some(a => a.includes(ps.query!)) ||
+ emoji.category?.includes(ps.query!));
+ }
emojis.splice(ps.limit + 1);
} else {
emojis = await q.take(ps.limit).getMany();
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts
new file mode 100644
index 0000000000..b90b9757be
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts
@@ -0,0 +1,37 @@
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { CustomEmojiService } from '@/core/CustomEmojiService.js';
+
+export const meta = {
+ tags: ['admin'],
+
+ requireCredential: true,
+ requireRolePolicy: 'canManageCustomEmojis',
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ ids: { type: 'array', items: {
+ type: 'string', format: 'misskey:id',
+ } },
+ license: {
+ type: 'string',
+ nullable: true,
+ description: 'Use `null` to reset the license.',
+ },
+ },
+ required: ['ids'],
+} as const;
+
+// eslint-disable-next-line import/no-default-export
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+ constructor(
+ private customEmojiService: CustomEmojiService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ await this.customEmojiService.setLicenseBulk(ps.ids, ps.license ?? null);
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts
index 54ce095488..83f729953a 100644
--- a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts
+++ b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts
@@ -39,9 +39,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
const pairs = await Promise.all(followings.map(f => Promise.all([
this.usersRepository.findOneByOrFail({ id: f.followerId }),
this.usersRepository.findOneByOrFail({ id: f.followeeId }),
- ])));
+ ]).then(([from, to]) => [{ id: from.id }, { id: to.id }])));
- this.queueService.createUnfollowJob(pairs.map(p => ({ to: p[0], from: p[1], silent: true })));
+ this.queueService.createUnfollowJob(pairs.map(p => ({ from: p[0], to: p[1], silent: true })));
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
index 0a529ecb08..4fd74e591d 100644
--- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
+++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
@@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import type { InstancesRepository } from '@/models/index.js';
import { UtilityService } from '@/core/UtilityService.js';
import { DI } from '@/di-symbols.js';
+import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
export const meta = {
tags: ['admin'],
@@ -28,6 +29,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private instancesRepository: InstancesRepository,
private utilityService: UtilityService,
+ private federatedInstanceService: FederatedInstanceService,
) {
super(meta, paramDef, async (ps, me) => {
const instance = await this.instancesRepository.findOneBy({ host: this.utilityService.toPuny(ps.host) });
@@ -36,7 +38,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
throw new Error('instance not found');
}
- this.instancesRepository.update({ host: this.utilityService.toPuny(ps.host) }, {
+ this.federatedInstanceService.update(instance.id, {
isSuspended: ps.isSuspended,
});
});
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index fc318a621a..87a2d22ac2 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -118,6 +118,14 @@ export const meta = {
optional: false, nullable: false,
},
},
+ preservedUsernames: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ },
hcaptchaSecretKey: {
type: 'string',
optional: true, nullable: true,
@@ -311,6 +319,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
hiddenTags: instance.hiddenTags,
blockedHosts: instance.blockedHosts,
sensitiveWords: instance.sensitiveWords,
+ preservedUsernames: instance.preservedUsernames,
hcaptchaSecretKey: instance.hcaptchaSecretKey,
recaptchaSecretKey: instance.recaptchaSecretKey,
turnstileSecretKey: instance.turnstileSecretKey,
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/create.ts b/packages/backend/src/server/api/endpoints/admin/roles/create.ts
index 1359894634..916172f54a 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/create.ts
@@ -25,6 +25,7 @@ export const paramDef = {
isPublic: { type: 'boolean' },
isModerator: { type: 'boolean' },
isAdministrator: { type: 'boolean' },
+ isExplorable: { type: 'boolean', default: false }, // optional for backward compatibility
asBadge: { type: 'boolean' },
canEditMembersByModerator: { type: 'boolean' },
displayOrder: { type: 'number' },
@@ -76,12 +77,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
isPublic: ps.isPublic,
isAdministrator: ps.isAdministrator,
isModerator: ps.isModerator,
+ isExplorable: ps.isExplorable,
asBadge: ps.asBadge,
canEditMembersByModerator: ps.canEditMembersByModerator,
displayOrder: ps.displayOrder,
policies: ps.policies,
}).then(x => this.rolesRepository.findOneByOrFail(x.identifiers[0]));
-
+
this.globalEventService.publishInternalEvent('roleCreated', created);
return await this.roleEntityService.pack(created, me);
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update.ts b/packages/backend/src/server/api/endpoints/admin/roles/update.ts
index 37b68c4c41..467f157a61 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts
@@ -33,6 +33,7 @@ export const paramDef = {
isPublic: { type: 'boolean' },
isModerator: { type: 'boolean' },
isAdministrator: { type: 'boolean' },
+ isExplorable: { type: 'boolean' },
asBadge: { type: 'boolean' },
canEditMembersByModerator: { type: 'boolean' },
displayOrder: { type: 'number' },
@@ -85,6 +86,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
isPublic: ps.isPublic,
isModerator: ps.isModerator,
isAdministrator: ps.isAdministrator,
+ isExplorable: ps.isExplorable,
asBadge: ps.asBadge,
canEditMembersByModerator: ps.canEditMembersByModerator,
displayOrder: ps.displayOrder,
diff --git a/packages/backend/src/server/api/endpoints/admin/server-info.ts b/packages/backend/src/server/api/endpoints/admin/server-info.ts
index 9c576dffe9..4ef4fdc665 100644
--- a/packages/backend/src/server/api/endpoints/admin/server-info.ts
+++ b/packages/backend/src/server/api/endpoints/admin/server-info.ts
@@ -2,7 +2,7 @@ import * as os from 'node:os';
import si from 'systeminformation';
import { Inject, Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { DI } from '@/di-symbols.js';
diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts
index 9d19efbbcf..42229c8f23 100644
--- a/packages/backend/src/server/api/endpoints/admin/show-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts
@@ -80,7 +80,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
isSilenced: isSilenced,
isSuspended: user.isSuspended,
lastActiveDate: user.lastActiveDate,
- moderationNote: profile.moderationNote,
+ moderationNote: profile.moderationNote ?? '',
signins,
policies: await this.roleService.getUserPolicies(user.id),
roles: await this.roleEntityService.packMany(roles, me),
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 11de29bf83..0e94f56cfd 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -94,6 +94,8 @@ export const paramDef = {
enableActiveEmailValidation: { type: 'boolean' },
enableChartsForRemoteUser: { type: 'boolean' },
enableChartsForFederatedInstances: { type: 'boolean' },
+ serverRules: { type: 'array', items: { type: 'string' } },
+ preservedUsernames: { type: 'array', items: { type: 'string' } },
},
required: [],
} as const;
@@ -387,6 +389,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
set.enableChartsForFederatedInstances = ps.enableChartsForFederatedInstances;
}
+ if (ps.serverRules !== undefined) {
+ set.serverRules = ps.serverRules;
+ }
+
+ if (ps.preservedUsernames !== undefined) {
+ set.preservedUsernames = ps.preservedUsernames;
+ }
+
await this.metaService.update(set);
this.moderationLogService.insertModerationLog(me, 'updateMeta');
});
diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts
index b7ce3363a9..5754a9f12a 100644
--- a/packages/backend/src/server/api/endpoints/antennas/create.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/create.ts
@@ -13,6 +13,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:account',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts
index 88623ce26a..dca0f443b7 100644
--- a/packages/backend/src/server/api/endpoints/antennas/notes.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts
@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { NotesRepository, AntennasRepository } from '@/models/index.js';
import { QueryService } from '@/core/QueryService.js';
diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts
index 3f85442131..5f980bdbeb 100644
--- a/packages/backend/src/server/api/endpoints/antennas/update.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/update.ts
@@ -11,6 +11,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:account',
errors: {
@@ -71,7 +73,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
@Inject(DI.userListsRepository)
private userListsRepository: UserListsRepository,
-
+
private antennaEntityService: AntennaEntityService,
private globalEventService: GlobalEventService,
) {
diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts
index dff8a9d10d..69e2f2504c 100644
--- a/packages/backend/src/server/api/endpoints/channels/create.ts
+++ b/packages/backend/src/server/api/endpoints/channels/create.ts
@@ -13,6 +13,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:channels',
limit: {
@@ -41,6 +43,7 @@ export const paramDef = {
name: { type: 'string', minLength: 1, maxLength: 128 },
description: { type: 'string', nullable: true, minLength: 1, maxLength: 2048 },
bannerId: { type: 'string', format: 'misskey:id', nullable: true },
+ color: { type: 'string', minLength: 1, maxLength: 16 },
},
required: ['name'],
} as const;
@@ -78,6 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
name: ps.name,
description: ps.description ?? null,
bannerId: banner ? banner.id : null,
+ ...(ps.color !== undefined ? { color: ps.color } : {}),
} as Channel).then(x => this.channelsRepository.findOneByOrFail(x.identifiers[0]));
return await this.channelEntityService.pack(channel, me);
diff --git a/packages/backend/src/server/api/endpoints/channels/favorite.ts b/packages/backend/src/server/api/endpoints/channels/favorite.ts
index f52b45ccf3..c8544273a1 100644
--- a/packages/backend/src/server/api/endpoints/channels/favorite.ts
+++ b/packages/backend/src/server/api/endpoints/channels/favorite.ts
@@ -10,6 +10,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:channels',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/channels/featured.ts b/packages/backend/src/server/api/endpoints/channels/featured.ts
index d25faae38d..1a8d1164c7 100644
--- a/packages/backend/src/server/api/endpoints/channels/featured.ts
+++ b/packages/backend/src/server/api/endpoints/channels/featured.ts
@@ -38,6 +38,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
super(meta, paramDef, async (ps, me) => {
const query = this.channelsRepository.createQueryBuilder('channel')
.where('channel.lastNotedAt IS NOT NULL')
+ .andWhere('channel.isArchived = FALSE')
.orderBy('channel.lastNotedAt', 'DESC');
const channels = await query.take(10).getMany();
diff --git a/packages/backend/src/server/api/endpoints/channels/follow.ts b/packages/backend/src/server/api/endpoints/channels/follow.ts
index 8ab59991c7..f3ca66cfd2 100644
--- a/packages/backend/src/server/api/endpoints/channels/follow.ts
+++ b/packages/backend/src/server/api/endpoints/channels/follow.ts
@@ -11,6 +11,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:channels',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/channels/owned.ts b/packages/backend/src/server/api/endpoints/channels/owned.ts
index 59df0616be..8fae972cb1 100644
--- a/packages/backend/src/server/api/endpoints/channels/owned.ts
+++ b/packages/backend/src/server/api/endpoints/channels/owned.ts
@@ -44,7 +44,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private queryService: QueryService,
) {
super(meta, paramDef, async (ps, me) => {
- const query = this.queryService.makePaginationQuery(this.channelsRepository.createQueryBuilder(), ps.sinceId, ps.untilId)
+ const query = this.queryService.makePaginationQuery(this.channelsRepository.createQueryBuilder('channel'), ps.sinceId, ps.untilId)
+ .andWhere('channel.isArchived = FALSE')
.andWhere({ userId: me.id });
const channels = await query
diff --git a/packages/backend/src/server/api/endpoints/channels/search.ts b/packages/backend/src/server/api/endpoints/channels/search.ts
index a954ba224c..a3b40b0bbd 100644
--- a/packages/backend/src/server/api/endpoints/channels/search.ts
+++ b/packages/backend/src/server/api/endpoints/channels/search.ts
@@ -46,15 +46,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private queryService: QueryService,
) {
super(meta, paramDef, async (ps, me) => {
- const query = this.queryService.makePaginationQuery(this.channelsRepository.createQueryBuilder('channel'), ps.sinceId, ps.untilId);
+ const query = this.queryService.makePaginationQuery(this.channelsRepository.createQueryBuilder('channel'), ps.sinceId, ps.untilId)
+ .andWhere('channel.isArchived = FALSE');
- if (ps.type === 'nameAndDescription') {
- query.andWhere(new Brackets(qb => { qb
- .where('channel.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` })
- .orWhere('channel.description ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` });
- }));
- } else {
- query.andWhere('channel.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` });
+ if (ps.query !== '') {
+ if (ps.type === 'nameAndDescription') {
+ query.andWhere(new Brackets(qb => { qb
+ .where('channel.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` })
+ .orWhere('channel.description ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` });
+ }));
+ } else {
+ query.andWhere('channel.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` });
+ }
}
const channels = await query
diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts
index 2491d14235..c881074bab 100644
--- a/packages/backend/src/server/api/endpoints/channels/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts
@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { ChannelsRepository, Note, NotesRepository } from '@/models/index.js';
import { QueryService } from '@/core/QueryService.js';
diff --git a/packages/backend/src/server/api/endpoints/channels/unfavorite.ts b/packages/backend/src/server/api/endpoints/channels/unfavorite.ts
index 0c3f6c4855..67fb1ea03e 100644
--- a/packages/backend/src/server/api/endpoints/channels/unfavorite.ts
+++ b/packages/backend/src/server/api/endpoints/channels/unfavorite.ts
@@ -9,6 +9,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:channels',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/channels/unfollow.ts b/packages/backend/src/server/api/endpoints/channels/unfollow.ts
index 855ba47f8c..f46ff9f286 100644
--- a/packages/backend/src/server/api/endpoints/channels/unfollow.ts
+++ b/packages/backend/src/server/api/endpoints/channels/unfollow.ts
@@ -10,6 +10,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:channels',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/channels/update.ts b/packages/backend/src/server/api/endpoints/channels/update.ts
index 084b3f919e..30d7f8b244 100644
--- a/packages/backend/src/server/api/endpoints/channels/update.ts
+++ b/packages/backend/src/server/api/endpoints/channels/update.ts
@@ -47,12 +47,14 @@ export const paramDef = {
name: { type: 'string', minLength: 1, maxLength: 128 },
description: { type: 'string', nullable: true, minLength: 1, maxLength: 2048 },
bannerId: { type: 'string', format: 'misskey:id', nullable: true },
+ isArchived: { type: 'boolean', nullable: true },
pinnedNoteIds: {
type: 'array',
items: {
type: 'string', format: 'misskey:id',
},
},
+ color: { type: 'string', minLength: 1, maxLength: 16 },
},
required: ['channelId'],
} as const;
@@ -104,6 +106,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
...(ps.name !== undefined ? { name: ps.name } : {}),
...(ps.description !== undefined ? { description: ps.description } : {}),
...(ps.pinnedNoteIds !== undefined ? { pinnedNoteIds: ps.pinnedNoteIds } : {}),
+ ...(ps.color !== undefined ? { color: ps.color } : {}),
+ ...(typeof ps.isArchived === 'boolean' ? { isArchived: ps.isArchived } : {}),
...(banner ? { bannerId: banner.id } : {}),
});
diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts
index b9d8dce47a..c3561e2a71 100644
--- a/packages/backend/src/server/api/endpoints/clips/add-note.ts
+++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts
@@ -13,6 +13,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:account',
limit: {
diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts
index a770dc986d..5395a5c373 100644
--- a/packages/backend/src/server/api/endpoints/clips/create.ts
+++ b/packages/backend/src/server/api/endpoints/clips/create.ts
@@ -12,6 +12,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:account',
res: {
@@ -57,7 +59,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
if (currentCount > (await this.roleService.getUserPolicies(me.id)).clipLimit) {
throw new ApiError(meta.errors.tooManyClips);
}
-
+
const clip = await this.clipsRepository.insert({
id: this.idService.genId(),
createdAt: new Date(),
diff --git a/packages/backend/src/server/api/endpoints/clips/favorite.ts b/packages/backend/src/server/api/endpoints/clips/favorite.ts
index 6addf743a2..f08caaf8d7 100644
--- a/packages/backend/src/server/api/endpoints/clips/favorite.ts
+++ b/packages/backend/src/server/api/endpoints/clips/favorite.ts
@@ -10,6 +10,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:clip-favorite',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/clips/remove-note.ts b/packages/backend/src/server/api/endpoints/clips/remove-note.ts
index 5d88870ed2..50c5d758bd 100644
--- a/packages/backend/src/server/api/endpoints/clips/remove-note.ts
+++ b/packages/backend/src/server/api/endpoints/clips/remove-note.ts
@@ -10,6 +10,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:account',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/clips/unfavorite.ts b/packages/backend/src/server/api/endpoints/clips/unfavorite.ts
index 244843d50f..3da252a226 100644
--- a/packages/backend/src/server/api/endpoints/clips/unfavorite.ts
+++ b/packages/backend/src/server/api/endpoints/clips/unfavorite.ts
@@ -9,6 +9,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:clip-favorite',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/clips/update.ts b/packages/backend/src/server/api/endpoints/clips/update.ts
index a103c3f7d3..70f1959353 100644
--- a/packages/backend/src/server/api/endpoints/clips/update.ts
+++ b/packages/backend/src/server/api/endpoints/clips/update.ts
@@ -10,6 +10,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:account',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/drive/files/create.ts b/packages/backend/src/server/api/endpoints/drive/files/create.ts
index b3bdef41d3..a1c1f9325e 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/create.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/create.ts
@@ -15,6 +15,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
limit: {
duration: ms('1hour'),
max: 120,
diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts
index 3141e0fc01..3ecbba22b5 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/update.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts
@@ -40,8 +40,13 @@ export const meta = {
code: 'NO_SUCH_FOLDER',
id: 'ea8fb7a5-af77-4a08-b608-c0218176cd73',
},
+
+ restrictedByRole: {
+ message: 'This feature is restricted by your role.',
+ code: 'RESTRICTED_BY_ROLE',
+ id: '7f59dccb-f465-75ab-5cf4-3ce44e3282f7',
+ },
},
-
res: {
type: 'object',
optional: false, nullable: false,
@@ -77,7 +82,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
) {
super(meta, paramDef, async (ps, me) => {
const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
-
+ const alwaysMarkNsfw = (await this.roleService.getUserPolicies(me.id)).alwaysMarkNsfw;
if (file == null) {
throw new ApiError(meta.errors.noSuchFile);
}
@@ -93,6 +98,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
if (ps.comment !== undefined) file.comment = ps.comment;
+ if (ps.isSensitive !== undefined && ps.isSensitive !== file.isSensitive && alwaysMarkNsfw && !ps.isSensitive) {
+ throw new ApiError(meta.errors.restrictedByRole);
+ }
+
if (ps.isSensitive !== undefined) file.isSensitive = ps.isSensitive;
if (ps.folderId !== undefined) {
diff --git a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts
index cfef793831..c835587c4a 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts
@@ -19,6 +19,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:drive',
} as const;
diff --git a/packages/backend/src/server/api/endpoints/flash/create.ts b/packages/backend/src/server/api/endpoints/flash/create.ts
index f21d9d5c33..3172bdbfda 100644
--- a/packages/backend/src/server/api/endpoints/flash/create.ts
+++ b/packages/backend/src/server/api/endpoints/flash/create.ts
@@ -11,6 +11,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:flash',
limit: {
diff --git a/packages/backend/src/server/api/endpoints/flash/like.ts b/packages/backend/src/server/api/endpoints/flash/like.ts
index 5581b8ec60..23de2f3970 100644
--- a/packages/backend/src/server/api/endpoints/flash/like.ts
+++ b/packages/backend/src/server/api/endpoints/flash/like.ts
@@ -10,6 +10,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:flash-likes',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/flash/unlike.ts b/packages/backend/src/server/api/endpoints/flash/unlike.ts
index b994f5d347..696512b06c 100644
--- a/packages/backend/src/server/api/endpoints/flash/unlike.ts
+++ b/packages/backend/src/server/api/endpoints/flash/unlike.ts
@@ -9,6 +9,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:flash-likes',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/flash/update.ts b/packages/backend/src/server/api/endpoints/flash/update.ts
index cd4e413a40..78dfd4a06a 100644
--- a/packages/backend/src/server/api/endpoints/flash/update.ts
+++ b/packages/backend/src/server/api/endpoints/flash/update.ts
@@ -10,6 +10,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:flash',
limit: {
diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts
index 411c39110a..4ad16de911 100644
--- a/packages/backend/src/server/api/endpoints/following/create.ts
+++ b/packages/backend/src/server/api/endpoints/following/create.ts
@@ -19,6 +19,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:following',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts
index cb8b6a2e3e..ca6bfa7e0f 100644
--- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts
@@ -13,6 +13,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:gallery',
limit: {
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts
index 519e56ed6a..6ac5fa8606 100644
--- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts
@@ -10,6 +10,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:gallery-likes',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts
index cfbedcc4d9..513089217d 100644
--- a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts
@@ -9,6 +9,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:gallery-likes',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts
index f14d644a3a..a2a10d8400 100644
--- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts
@@ -11,6 +11,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:gallery',
limit: {
diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts
index c3f2ea9ea7..dd3549020e 100644
--- a/packages/backend/src/server/api/endpoints/hashtags/users.ts
+++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts
@@ -44,7 +44,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
) {
super(meta, paramDef, async (ps, me) => {
const query = this.usersRepository.createQueryBuilder('user')
- .where(':tag = ANY(user.tags)', { tag: normalizeForSearch(ps.tag) });
+ .where(':tag = ANY(user.tags)', { tag: normalizeForSearch(ps.tag) })
+ .andWhere('user.isSuspended = FALSE');
const recent = new Date(Date.now() - (1000 * 60 * 60 * 24 * 5));
diff --git a/packages/backend/src/server/api/endpoints/i/claim-achievement.ts b/packages/backend/src/server/api/endpoints/i/claim-achievement.ts
index 102dae4fb7..4eef496385 100644
--- a/packages/backend/src/server/api/endpoints/i/claim-achievement.ts
+++ b/packages/backend/src/server/api/endpoints/i/claim-achievement.ts
@@ -4,6 +4,7 @@ import { AchievementService, ACHIEVEMENT_TYPES } from '@/core/AchievementService
export const meta = {
requireCredential: true,
+ prohibitMoved: true,
} as const;
export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/i/export-antennas.ts b/packages/backend/src/server/api/endpoints/i/export-antennas.ts
new file mode 100644
index 0000000000..4182c1b247
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/i/export-antennas.ts
@@ -0,0 +1,30 @@
+import { Injectable } from '@nestjs/common';
+import ms from 'ms';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { QueueService } from '@/core/QueueService.js';
+
+export const meta = {
+ secure: true,
+ requireCredential: true,
+ limit: {
+ duration: ms('1hour'),
+ max: 1,
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {},
+ required: [],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+ constructor (
+ private queueService: QueueService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ this.queueService.createExportAntennasJob(me);
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/i/import-antennas.ts b/packages/backend/src/server/api/endpoints/i/import-antennas.ts
new file mode 100644
index 0000000000..efb5ce4223
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/i/import-antennas.ts
@@ -0,0 +1,84 @@
+import { Inject, Injectable } from '@nestjs/common';
+import ms from 'ms';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { QueueService } from '@/core/QueueService.js';
+import type { AntennasRepository, DriveFilesRepository, UsersRepository, Antenna as _Antenna } from '@/models/index.js';
+import { DI } from '@/di-symbols.js';
+import { RoleService } from '@/core/RoleService.js';
+import { DownloadService } from '@/core/DownloadService.js';
+import { ApiError } from '../../error.js';
+
+export const meta = {
+ secure: true,
+ requireCredential: true,
+ prohibitMoved: true,
+
+ limit: {
+ duration: ms('1hour'),
+ max: 1,
+ },
+ errors: {
+ noSuchFile: {
+ message: 'No such file.',
+ code: 'NO_SUCH_FILE',
+ id: '3b71d086-c3fa-431c-b01d-ded65a777172',
+ },
+ noSuchUser: {
+ message: 'No such user.',
+ code: 'NO_SUCH_USER',
+ id: 'e842c379-8ac7-4cf7-b07a-4d4de7e4671c',
+ },
+ emptyFile: {
+ message: 'That file is empty.',
+ code: 'EMPTY_FILE',
+ id: '7f60115d-8d93-4b0f-bd0e-3815dcbb389f',
+ },
+ tooManyAntennas: {
+ message: 'You cannot create antenna any more.',
+ code: 'TOO_MANY_ANTENNAS',
+ id: '600917d4-a4cb-4cc5-8ba8-7ac8ea3c7779',
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ fileId: { type: 'string', format: 'misskey:id' },
+ },
+ required: ['fileId'],
+} as const;
+
+@Injectable() // eslint-disable-next-line import/no-default-export
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+ constructor (
+ @Inject(DI.driveFilesRepository)
+ private driveFilesRepository: DriveFilesRepository,
+
+ @Inject(DI.antennasRepository)
+ private antennasRepository: AntennasRepository,
+
+ @Inject(DI.usersRepository)
+ private usersRepository: UsersRepository,
+
+ private roleService: RoleService,
+ private queueService: QueueService,
+ private downloadService: DownloadService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const users = await this.usersRepository.findOneBy({ id: me.id });
+ if (users === null) throw new ApiError(meta.errors.noSuchUser);
+ const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
+ if (file === null) throw new ApiError(meta.errors.noSuchFile);
+ if (file.size === 0) throw new ApiError(meta.errors.emptyFile);
+ const antennas: (_Antenna & { userListAccts: string[] | null })[] = JSON.parse(await this.downloadService.downloadTextFile(file.url));
+ const currentAntennasCount = await this.antennasRepository.countBy({ userId: me.id });
+ if (currentAntennasCount + antennas.length > (await this.roleService.getUserPolicies(me.id)).antennaLimit) {
+ throw new ApiError(meta.errors.tooManyAntennas);
+ }
+ this.queueService.createImportAntennasJob(me, antennas);
+ });
+ }
+}
+
+export type Antenna = (_Antenna & { userListAccts: string[] | null })[];
diff --git a/packages/backend/src/server/api/endpoints/i/import-blocking.ts b/packages/backend/src/server/api/endpoints/i/import-blocking.ts
index 8c1c158ab1..811971591a 100644
--- a/packages/backend/src/server/api/endpoints/i/import-blocking.ts
+++ b/packages/backend/src/server/api/endpoints/i/import-blocking.ts
@@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
import ms from 'ms';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueueService } from '@/core/QueueService.js';
+import { AccountMoveService } from '@/core/AccountMoveService.js';
import type { DriveFilesRepository } from '@/models/index.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../../error.js';
@@ -9,6 +10,7 @@ import { ApiError } from '../../error.js';
export const meta = {
secure: true,
requireCredential: true,
+ prohibitMoved: true,
limit: {
duration: ms('1hour'),
@@ -58,15 +60,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private driveFilesRepository: DriveFilesRepository,
private queueService: QueueService,
+ private accountMoveService: AccountMoveService,
) {
super(meta, paramDef, async (ps, me) => {
const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
if (file == null) throw new ApiError(meta.errors.noSuchFile);
//if (!file.type.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType);
- if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile);
if (file.size === 0) throw new ApiError(meta.errors.emptyFile);
+ const checkMoving = await this.accountMoveService.validateAlsoKnownAs(
+ me,
+ (old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > (new Date()).getTime(),
+ true
+ );
+ if (checkMoving ? file.size > 32 * 1024 * 1024 : file.size > 64 * 1024) throw new ApiError(meta.errors.tooBigFile);
+
this.queueService.createImportBlockingJob(me, file.id);
});
}
diff --git a/packages/backend/src/server/api/endpoints/i/import-following.ts b/packages/backend/src/server/api/endpoints/i/import-following.ts
index 383bdc02b5..8af278c883 100644
--- a/packages/backend/src/server/api/endpoints/i/import-following.ts
+++ b/packages/backend/src/server/api/endpoints/i/import-following.ts
@@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
import ms from 'ms';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueueService } from '@/core/QueueService.js';
+import { AccountMoveService } from '@/core/AccountMoveService.js';
import type { DriveFilesRepository } from '@/models/index.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../../error.js';
@@ -9,6 +10,7 @@ import { ApiError } from '../../error.js';
export const meta = {
secure: true,
requireCredential: true,
+ prohibitMoved: true,
limit: {
duration: ms('1hour'),
max: 1,
@@ -57,15 +59,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private driveFilesRepository: DriveFilesRepository,
private queueService: QueueService,
+ private accountMoveService: AccountMoveService,
) {
super(meta, paramDef, async (ps, me) => {
const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
if (file == null) throw new ApiError(meta.errors.noSuchFile);
//if (!file.type.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType);
- if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile);
if (file.size === 0) throw new ApiError(meta.errors.emptyFile);
+ const checkMoving = await this.accountMoveService.validateAlsoKnownAs(
+ me,
+ (old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > (new Date()).getTime(),
+ true
+ );
+ if (checkMoving ? file.size > 32 * 1024 * 1024 : file.size > 64 * 1024) throw new ApiError(meta.errors.tooBigFile);
+
this.queueService.createImportFollowingJob(me, file.id);
});
}
diff --git a/packages/backend/src/server/api/endpoints/i/import-muting.ts b/packages/backend/src/server/api/endpoints/i/import-muting.ts
index 345ad916cb..eb0f9ba474 100644
--- a/packages/backend/src/server/api/endpoints/i/import-muting.ts
+++ b/packages/backend/src/server/api/endpoints/i/import-muting.ts
@@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
import ms from 'ms';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueueService } from '@/core/QueueService.js';
+import { AccountMoveService } from '@/core/AccountMoveService.js';
import type { DriveFilesRepository } from '@/models/index.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../../error.js';
@@ -9,6 +10,7 @@ import { ApiError } from '../../error.js';
export const meta = {
secure: true,
requireCredential: true,
+ prohibitMoved: true,
limit: {
duration: ms('1hour'),
@@ -58,15 +60,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private driveFilesRepository: DriveFilesRepository,
private queueService: QueueService,
+ private accountMoveService: AccountMoveService,
) {
super(meta, paramDef, async (ps, me) => {
const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
if (file == null) throw new ApiError(meta.errors.noSuchFile);
//if (!file.type.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType);
- if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile);
if (file.size === 0) throw new ApiError(meta.errors.emptyFile);
+ const checkMoving = await this.accountMoveService.validateAlsoKnownAs(
+ me,
+ (old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > (new Date()).getTime(),
+ true
+ );
+ if (checkMoving ? file.size > 32 * 1024 * 1024 : file.size > 64 * 1024) throw new ApiError(meta.errors.tooBigFile);
+
this.queueService.createImportMutingJob(me, file.id);
});
}
diff --git a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts
index 875af7ec23..4568e93901 100644
--- a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts
+++ b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts
@@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
import ms from 'ms';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueueService } from '@/core/QueueService.js';
+import { AccountMoveService } from '@/core/AccountMoveService.js';
import type { DriveFilesRepository } from '@/models/index.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../../error.js';
@@ -9,6 +10,7 @@ import { ApiError } from '../../error.js';
export const meta = {
secure: true,
requireCredential: true,
+ prohibitMoved: true,
limit: {
duration: ms('1hour'),
max: 1,
@@ -57,15 +59,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private driveFilesRepository: DriveFilesRepository,
private queueService: QueueService,
+ private accountMoveService: AccountMoveService,
) {
super(meta, paramDef, async (ps, me) => {
const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
if (file == null) throw new ApiError(meta.errors.noSuchFile);
//if (!file.type.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType);
- if (file.size > 30000) throw new ApiError(meta.errors.tooBigFile);
if (file.size === 0) throw new ApiError(meta.errors.emptyFile);
+ const checkMoving = await this.accountMoveService.validateAlsoKnownAs(
+ me,
+ (old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > (new Date()).getTime(),
+ true
+ );
+ if (checkMoving ? file.size > 32 * 1024 * 1024 : file.size > 64 * 1024) throw new ApiError(meta.errors.tooBigFile);
+
this.queueService.createImportUserListsJob(me, file.id);
});
}
diff --git a/packages/backend/src/server/api/endpoints/i/known-as.ts b/packages/backend/src/server/api/endpoints/i/known-as.ts
deleted file mode 100644
index 964704d82b..0000000000
--- a/packages/backend/src/server/api/endpoints/i/known-as.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import { Injectable } from '@nestjs/common';
-import ms from 'ms';
-
-import { User } from '@/models/entities/User.js';
-import { Endpoint } from '@/server/api/endpoint-base.js';
-import { ApiError } from '@/server/api/error.js';
-
-import { AccountMoveService } from '@/core/AccountMoveService.js';
-import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js';
-import { UserEntityService } from '@/core/entities/UserEntityService.js';
-import { ApiLoggerService } from '@/server/api/ApiLoggerService.js';
-
-export const meta = {
- tags: ['users'],
-
- secure: true,
- requireCredential: true,
-
- limit: {
- duration: ms('1day'),
- max: 30,
- },
-
- errors: {
- noSuchUser: {
- message: 'No such user.',
- code: 'NO_SUCH_USER',
- id: 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5',
- },
- notRemote: {
- message: 'User is not remote. You can only migrate from other instances.',
- code: 'NOT_REMOTE',
- id: '4362f8dc-731f-4ad8-a694-be2a88922a24',
- },
- uriNull: {
- message: 'User ActivityPup URI is null.',
- code: 'URI_NULL',
- id: 'bf326f31-d430-4f97-9933-5d61e4d48a23',
- },
- },
-} as const;
-
-export const paramDef = {
- type: 'object',
- properties: {
- alsoKnownAs: { type: 'string' },
- },
- required: ['alsoKnownAs'],
-} as const;
-
-@Injectable()
-export default class extends Endpoint<typeof meta, typeof paramDef> {
- constructor(
- private userEntityService: UserEntityService,
- private remoteUserResolveService: RemoteUserResolveService,
- private apiLoggerService: ApiLoggerService,
- private accountMoveService: AccountMoveService,
- ) {
- super(meta, paramDef, async (ps, me) => {
- // Check parameter
- if (!ps.alsoKnownAs) throw new ApiError(meta.errors.noSuchUser);
-
- let unfiltered = ps.alsoKnownAs;
- const updates = {} as Partial<User>;
-
- if (!unfiltered) {
- updates.alsoKnownAs = null;
- } else {
- // Parse user's input into the old account
- if (unfiltered.startsWith('acct:')) unfiltered = unfiltered.substring(5);
- if (unfiltered.startsWith('@')) unfiltered = unfiltered.substring(1);
- if (!unfiltered.includes('@')) throw new ApiError(meta.errors.notRemote);
-
- const userAddress = unfiltered.split('@');
- // Retrieve the old account
- const knownAs = await this.remoteUserResolveService.resolveUser(userAddress[0], userAddress[1]).catch((e) => {
- this.apiLoggerService.logger.warn(`failed to resolve remote user: ${e}`);
- throw new ApiError(meta.errors.noSuchUser);
- });
-
- const toUrl: string | null = knownAs.uri;
- if (!toUrl) throw new ApiError(meta.errors.uriNull);
- // Only allow moving from a remote account
- if (this.userEntityService.isLocalUser(knownAs)) throw new ApiError(meta.errors.notRemote);
-
- updates.alsoKnownAs = updates.alsoKnownAs?.concat([toUrl]) ?? [toUrl];
- }
-
- return await this.accountMoveService.createAlias(me, updates);
- });
- }
-}
diff --git a/packages/backend/src/server/api/endpoints/i/move.ts b/packages/backend/src/server/api/endpoints/i/move.ts
index ac76e1f620..261dd527c0 100644
--- a/packages/backend/src/server/api/endpoints/i/move.ts
+++ b/packages/backend/src/server/api/endpoints/i/move.ts
@@ -7,40 +7,35 @@ import { DI } from '@/di-symbols.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { ApiError } from '@/server/api/error.js';
+import { LocalUser, RemoteUser } from '@/models/entities/User.js';
+
import { AccountMoveService } from '@/core/AccountMoveService.js';
import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js';
-import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { ApiLoggerService } from '@/server/api/ApiLoggerService.js';
import { GetterService } from '@/server/api/GetterService.js';
import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
+
+import * as Acct from '@/misc/acct.js';
export const meta = {
tags: ['users'],
secure: true,
requireCredential: true,
+ prohibitMoved: true,
limit: {
duration: ms('1day'),
max: 5,
},
errors: {
- noSuchMoveTarget: {
- message: 'No such move target.',
- code: 'NO_SUCH_MOVE_TARGET',
- id: 'b5c90186-4ab0-49c8-9bba-a1f76c202ba4',
- },
- remoteAccountForbids: {
+ destinationAccountForbids: {
message:
- 'Remote account doesn\'t have proper \'Known As\' alias. Did you remember to set it?',
- code: 'REMOTE_ACCOUNT_FORBIDS',
+ 'Destination account doesn\'t have proper \'Known As\' alias, or has already moved.',
+ code: 'DESTINATION_ACCOUNT_FORBIDS',
id: 'b5c90186-4ab0-49c8-9bba-a1f766282ba4',
},
- notRemote: {
- message: 'User is not remote. You can only migrate to other instances.',
- code: 'NOT_REMOTE',
- id: '4362f8dc-731f-4ad8-a694-be2a88922a24',
- },
rootForbidden: {
message: 'The root can\'t migrate.',
code: 'NOT_ROOT_FORBIDDEN',
@@ -84,57 +79,52 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
@Inject(DI.config)
private config: Config,
- private userEntityService: UserEntityService,
private remoteUserResolveService: RemoteUserResolveService,
private apiLoggerService: ApiLoggerService,
private accountMoveService: AccountMoveService,
private getterService: GetterService,
private apPersonService: ApPersonService,
+ private userEntityService: UserEntityService,
) {
super(meta, paramDef, async (ps, me) => {
// check parameter
- if (!ps.moveToAccount) throw new ApiError(meta.errors.noSuchMoveTarget);
+ if (!ps.moveToAccount) throw new ApiError(meta.errors.noSuchUser);
// abort if user is the root
if (me.isRoot) throw new ApiError(meta.errors.rootForbidden);
// abort if user has already moved
if (me.movedToUri) throw new ApiError(meta.errors.alreadyMoved);
- let unfiltered = ps.moveToAccount;
- if (!unfiltered) throw new ApiError(meta.errors.noSuchMoveTarget);
-
// parse user's input into the destination account
- if (unfiltered.startsWith('acct:')) unfiltered = unfiltered.substring(5);
- if (unfiltered.startsWith('@')) unfiltered = unfiltered.substring(1);
- if (!unfiltered.includes('@')) throw new ApiError(meta.errors.notRemote);
-
- const userAddress = unfiltered.split('@');
+ const { username, host } = Acct.parse(ps.moveToAccount);
// retrieve the destination account
- let moveTo = await this.remoteUserResolveService.resolveUser(userAddress[0], userAddress[1]).catch((e) => {
+ let moveTo = await this.remoteUserResolveService.resolveUser(username, host).catch((e) => {
this.apiLoggerService.logger.warn(`failed to resolve remote user: ${e}`);
- throw new ApiError(meta.errors.noSuchMoveTarget);
+ throw new ApiError(meta.errors.noSuchUser);
});
- const remoteMoveTo = await this.getterService.getRemoteUser(moveTo.id);
- if (!remoteMoveTo.uri) throw new ApiError(meta.errors.uriNull);
+ const destination = await this.getterService.getUser(moveTo.id) as LocalUser | RemoteUser;
+ const newUri = this.userEntityService.getUserUri(destination);
// update local db
- await this.apPersonService.updatePerson(remoteMoveTo.uri);
+ await this.apPersonService.updatePerson(newUri);
// retrieve updated user
- moveTo = await this.apPersonService.resolvePerson(remoteMoveTo.uri);
- // only allow moving to a remote account
- if (this.userEntityService.isLocalUser(moveTo)) throw new ApiError(meta.errors.notRemote);
+ moveTo = await this.apPersonService.resolvePerson(newUri);
- let allowed = false;
-
- const fromUrl = `${this.config.url}/users/${me.id}`;
// make sure that the user has indicated the old account as an alias
- moveTo.alsoKnownAs?.forEach((elem) => {
- if (fromUrl.includes(elem)) allowed = true;
- });
+ const fromUrl = this.userEntityService.genLocalUserUri(me.id);
+ let allowed = false;
+ if (moveTo.alsoKnownAs) {
+ for (const knownAs of moveTo.alsoKnownAs) {
+ if (knownAs.includes(fromUrl)) {
+ allowed = true;
+ break;
+ }
+ }
+ }
// abort if unintended
- if (!(allowed && moveTo.uri && fromUrl)) throw new ApiError(meta.errors.remoteAccountForbids);
+ if (!allowed || moveTo.movedToUri) throw new ApiError(meta.errors.destinationAccountForbids);
- return await this.accountMoveService.moveToRemote(me, moveTo);
+ return await this.accountMoveService.moveFromLocal(me, moveTo);
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts
index ba0487f223..e141be764a 100644
--- a/packages/backend/src/server/api/endpoints/i/notifications.ts
+++ b/packages/backend/src/server/api/endpoints/i/notifications.ts
@@ -1,5 +1,5 @@
import { Brackets, In } from 'typeorm';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import { Inject, Injectable } from '@nestjs/common';
import type { UsersRepository, FollowingsRepository, MutingsRepository, UserProfilesRepository, NotesRepository } from '@/models/index.js';
import { obsoleteNotificationTypes, notificationTypes } from '@/types.js';
diff --git a/packages/backend/src/server/api/endpoints/i/pin.ts b/packages/backend/src/server/api/endpoints/i/pin.ts
index d4af00027e..2293500945 100644
--- a/packages/backend/src/server/api/endpoints/i/pin.ts
+++ b/packages/backend/src/server/api/endpoints/i/pin.ts
@@ -8,6 +8,7 @@ export const meta = {
tags: ['account', 'notes'],
requireCredential: true,
+ prohibitMoved: true,
kind: 'write:account',
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index be1c72b207..6c66300bb7 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -3,6 +3,7 @@ import * as mfm from 'mfm-js';
import { Inject, Injectable } from '@nestjs/common';
import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js';
import { extractHashtags } from '@/misc/extract-hashtags.js';
+import * as Acct from '@/misc/acct.js';
import type { UsersRepository, DriveFilesRepository, UserProfilesRepository, PagesRepository } from '@/models/index.js';
import type { User } from '@/models/entities/User.js';
import { birthdaySchema, descriptionSchema, locationSchema, nameSchema } from '@/models/entities/User.js';
@@ -19,7 +20,10 @@ import { HashtagService } from '@/core/HashtagService.js';
import { DI } from '@/di-symbols.js';
import { RoleService } from '@/core/RoleService.js';
import { CacheService } from '@/core/CacheService.js';
+import { AccountMoveService } from '@/core/AccountMoveService.js';
+import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js';
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
+import { ApiLoggerService } from '../../ApiLoggerService.js';
import { ApiError } from '../../error.js';
export const meta = {
@@ -71,6 +75,30 @@ export const meta = {
code: 'TOO_MANY_MUTED_WORDS',
id: '010665b1-a211-42d2-bc64-8f6609d79785',
},
+
+ noSuchUser: {
+ message: 'No such user.',
+ code: 'NO_SUCH_USER',
+ id: 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5',
+ },
+
+ uriNull: {
+ message: 'User ActivityPup URI is null.',
+ code: 'URI_NULL',
+ id: 'bf326f31-d430-4f97-9933-5d61e4d48a23',
+ },
+
+ forbiddenToSetYourself: {
+ message: 'You can\'t set yourself as your own alias.',
+ code: 'FORBIDDEN_TO_SET_YOURSELF',
+ id: '25c90186-4ab0-49c8-9bba-a1fa6c202ba4',
+ },
+
+ restrictedByRole: {
+ message: 'This feature is restricted by your role.',
+ code: 'RESTRICTED_BY_ROLE',
+ id: '8feff0ba-5ab5-585b-31f4-4df816663fad',
+ }
},
res: {
@@ -129,6 +157,12 @@ export const paramDef = {
emailNotificationTypes: { type: 'array', items: {
type: 'string',
} },
+ alsoKnownAs: {
+ type: 'array',
+ maxItems: 10,
+ uniqueItems: true,
+ items: { type: 'string' },
+ },
},
} as const;
@@ -153,6 +187,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private globalEventService: GlobalEventService,
private userFollowingService: UserFollowingService,
private accountUpdateService: AccountUpdateService,
+ private accountMoveService: AccountMoveService,
+ private remoteUserResolveService: RemoteUserResolveService,
+ private apiLoggerService: ApiLoggerService,
private hashtagService: HashtagService,
private roleService: RoleService,
private cacheService: CacheService,
@@ -208,7 +245,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
if (typeof ps.isCat === 'boolean') updates.isCat = ps.isCat;
if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote;
if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail;
- if (typeof ps.alwaysMarkNsfw === 'boolean') profileUpdates.alwaysMarkNsfw = ps.alwaysMarkNsfw;
+ if (typeof ps.alwaysMarkNsfw === 'boolean') {
+ if ((await roleService.getUserPolicies(user.id)).alwaysMarkNsfw) throw new ApiError(meta.errors.restrictedByRole);
+ profileUpdates.alwaysMarkNsfw = ps.alwaysMarkNsfw;
+ }
if (typeof ps.autoSensitive === 'boolean') profileUpdates.autoSensitive = ps.autoSensitive;
if (ps.emailNotificationTypes !== undefined) profileUpdates.emailNotificationTypes = ps.emailNotificationTypes;
@@ -221,6 +261,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
updates.avatarId = avatar.id;
updates.avatarUrl = this.driveFileEntityService.getPublicUrl(avatar, 'avatar');
updates.avatarBlurhash = avatar.blurhash;
+ } else if (ps.avatarId === null) {
+ updates.avatarId = null;
+ updates.avatarUrl = null;
+ updates.avatarBlurhash = null;
}
if (ps.bannerId) {
@@ -232,6 +276,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
updates.bannerId = banner.id;
updates.bannerUrl = this.driveFileEntityService.getPublicUrl(banner);
updates.bannerBlurhash = banner.blurhash;
+ } else if (ps.bannerId === null) {
+ updates.bannerId = null;
+ updates.bannerUrl = null;
+ updates.bannerBlurhash = null;
}
if (ps.pinnedPageId) {
@@ -252,6 +300,38 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
});
}
+ if (ps.alsoKnownAs) {
+ if (_user.movedToUri) {
+ throw new ApiError({
+ message: 'You have moved your account.',
+ code: 'YOUR_ACCOUNT_MOVED',
+ id: '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31',
+ httpStatusCode: 403,
+ });
+ }
+
+ // Parse user's input into the old account
+ const newAlsoKnownAs = new Set<string>();
+ for (const line of ps.alsoKnownAs) {
+ if (!line) throw new ApiError(meta.errors.noSuchUser);
+ const { username, host } = Acct.parse(line);
+
+ // Retrieve the old account
+ const knownAs = await this.remoteUserResolveService.resolveUser(username, host).catch((e) => {
+ this.apiLoggerService.logger.warn(`failed to resolve dstination user: ${e}`);
+ throw new ApiError(meta.errors.noSuchUser);
+ });
+ if (knownAs.id === _user.id) throw new ApiError(meta.errors.forbiddenToSetYourself);
+
+ const toUrl = this.userEntityService.getUserUri(knownAs);
+ if (!toUrl) throw new ApiError(meta.errors.uriNull);
+
+ newAlsoKnownAs.add(toUrl);
+ }
+
+ updates.alsoKnownAs = newAlsoKnownAs.size > 0 ? Array.from(newAlsoKnownAs) : null;
+ }
+
//#region emojis/tags
let emojis = [] as string[];
@@ -279,6 +359,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
//#endregion
if (Object.keys(updates).length > 0) await this.usersRepository.update(user.id, updates);
+ if (Object.keys(updates).includes('alsoKnownAs')) {
+ this.cacheService.uriPersonCache.set(this.userEntityService.genLocalUserUri(user.id), { ...user, ...updates });
+ }
if (Object.keys(profileUpdates).length > 0) await this.userProfilesRepository.update(user.id, profileUpdates);
const iObj = await this.userEntityService.pack<true, true>(user.id, user, {
diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts
index 37974ce2a3..584ea07c3b 100644
--- a/packages/backend/src/server/api/endpoints/meta.ts
+++ b/packages/backend/src/server/api/endpoints/meta.ts
@@ -201,10 +201,6 @@ export const meta = {
type: 'boolean',
optional: false, nullable: false,
},
- elasticsearch: {
- type: 'boolean',
- optional: false, nullable: false,
- },
hcaptcha: {
type: 'boolean',
optional: false, nullable: false,
@@ -310,6 +306,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
translatorAvailable: instance.deeplAuthKey != null,
+ serverRules: instance.serverRules,
+
policies: { ...DEFAULT_POLICIES, ...instance.policies },
mediaProxy: this.config.mediaProxy,
@@ -329,7 +327,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
response.features = {
registration: !instance.disableRegistration,
emailRequiredForSignup: instance.emailRequiredForSignup,
- elasticsearch: this.config.elasticsearch ? true : false,
hcaptcha: instance.enableHcaptcha,
recaptcha: instance.enableRecaptcha,
turnstile: instance.enableTurnstile,
diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts
index 6e24e1024d..ee358d5c6c 100644
--- a/packages/backend/src/server/api/endpoints/mute/create.ts
+++ b/packages/backend/src/server/api/endpoints/mute/create.ts
@@ -11,6 +11,7 @@ export const meta = {
tags: ['account'],
requireCredential: true,
+ prohibitMoved: true,
kind: 'write:mutes',
diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts
index 69fafcb9c7..3f7f2cdece 100644
--- a/packages/backend/src/server/api/endpoints/notes/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/create.ts
@@ -18,6 +18,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
limit: {
duration: ms('1hour'),
max: 300,
@@ -260,7 +262,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
let channel: Channel | null = null;
if (ps.channelId != null) {
- channel = await this.channelsRepository.findOneBy({ id: ps.channelId });
+ channel = await this.channelsRepository.findOneBy({ id: ps.channelId, isArchived: false });
if (channel == null) {
throw new ApiError(meta.errors.noSuchChannel);
diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts
index 0ce80a1a63..611ea19560 100644
--- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts
@@ -12,6 +12,7 @@ export const meta = {
tags: ['notes', 'favorites'],
requireCredential: true,
+ prohibitMoved: true,
kind: 'write:favorites',
diff --git a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts
index 2a44dc537e..3a33b037f8 100644
--- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts
+++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts
@@ -17,6 +17,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:votes',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts
index 04e374d1ae..97cb026779 100644
--- a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts
@@ -9,6 +9,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:reactions',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts
index fb5abd917f..f6385400c3 100644
--- a/packages/backend/src/server/api/endpoints/notes/search.ts
+++ b/packages/backend/src/server/api/endpoints/notes/search.ts
@@ -1,11 +1,10 @@
import { Inject, Injectable } from '@nestjs/common';
import type { NotesRepository } from '@/models/index.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
-import { QueryService } from '@/core/QueryService.js';
+import { SearchService } from '@/core/SearchService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import type { Config } from '@/config.js';
import { DI } from '@/di-symbols.js';
-import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
import { RoleService } from '@/core/RoleService.js';
import { ApiError } from '../../error.js';
@@ -43,8 +42,7 @@ export const paramDef = {
offset: { type: 'integer', default: 0 },
host: {
type: 'string',
- nullable: true,
- description: 'The local host is represented with `null`.',
+ description: 'The local host is represented with `.`.',
},
userId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
channelId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
@@ -61,11 +59,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
@Inject(DI.config)
private config: Config,
- @Inject(DI.notesRepository)
- private notesRepository: NotesRepository,
-
private noteEntityService: NoteEntityService,
- private queryService: QueryService,
+ private searchService: SearchService,
private roleService: RoleService,
) {
super(meta, paramDef, async (ps, me) => {
@@ -74,27 +69,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
throw new ApiError(meta.errors.unavailable);
}
- const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId);
-
- if (ps.userId) {
- query.andWhere('note.userId = :userId', { userId: ps.userId });
- } else if (ps.channelId) {
- query.andWhere('note.channelId = :channelId', { channelId: ps.channelId });
- }
-
- query
- .andWhere('note.text ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` })
- .innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('note.reply', 'reply')
- .leftJoinAndSelect('note.renote', 'renote')
- .leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('renote.user', 'renoteUser');
-
- this.queryService.generateVisibilityQuery(query, me);
- if (me) this.queryService.generateMutedUserQuery(query, me);
- if (me) this.queryService.generateBlockedUserQuery(query, me);
-
- const notes = await query.take(ps.limit).getMany();
+ const notes = await this.searchService.searchNote(ps.query, me, {
+ userId: ps.userId,
+ channelId: ps.channelId,
+ host: ps.host,
+ }, {
+ untilId: ps.untilId,
+ sinceId: ps.sinceId,
+ limit: ps.limit,
+ });
return await this.noteEntityService.packMany(notes, me);
});
diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts
index 4015bf1f29..e08ab399f8 100644
--- a/packages/backend/src/server/api/endpoints/pages/create.ts
+++ b/packages/backend/src/server/api/endpoints/pages/create.ts
@@ -13,6 +13,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:pages',
limit: {
diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts
index d27990f7e1..543c126d9c 100644
--- a/packages/backend/src/server/api/endpoints/pages/like.ts
+++ b/packages/backend/src/server/api/endpoints/pages/like.ts
@@ -10,6 +10,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:page-likes',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/pages/unlike.ts b/packages/backend/src/server/api/endpoints/pages/unlike.ts
index e397e2a23b..f0c0198460 100644
--- a/packages/backend/src/server/api/endpoints/pages/unlike.ts
+++ b/packages/backend/src/server/api/endpoints/pages/unlike.ts
@@ -9,6 +9,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:page-likes',
errors: {
diff --git a/packages/backend/src/server/api/endpoints/pages/update.ts b/packages/backend/src/server/api/endpoints/pages/update.ts
index 35b402ec56..751274067e 100644
--- a/packages/backend/src/server/api/endpoints/pages/update.ts
+++ b/packages/backend/src/server/api/endpoints/pages/update.ts
@@ -11,6 +11,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:pages',
limit: {
diff --git a/packages/backend/src/server/api/endpoints/renote-mute/create.ts b/packages/backend/src/server/api/endpoints/renote-mute/create.ts
index b285269617..beb5850d78 100644
--- a/packages/backend/src/server/api/endpoints/renote-mute/create.ts
+++ b/packages/backend/src/server/api/endpoints/renote-mute/create.ts
@@ -13,6 +13,7 @@ export const meta = {
tags: ['account'],
requireCredential: true,
+ prohibitMoved: true,
kind: 'write:mutes',
diff --git a/packages/backend/src/server/api/endpoints/reset-db.ts b/packages/backend/src/server/api/endpoints/reset-db.ts
index 655dd7cd83..4ced6d3ff1 100644
--- a/packages/backend/src/server/api/endpoints/reset-db.ts
+++ b/packages/backend/src/server/api/endpoints/reset-db.ts
@@ -1,6 +1,6 @@
import { Inject, Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { DI } from '@/di-symbols.js';
import { resetDb } from '@/misc/reset-db.js';
diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts
index b45d4af1fe..6202c740f1 100644
--- a/packages/backend/src/server/api/endpoints/roles/notes.ts
+++ b/packages/backend/src/server/api/endpoints/roles/notes.ts
@@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
-import Redis from 'ioredis';
+import * as Redis from 'ioredis';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { NotesRepository, RolesRepository } from '@/models/index.js';
import { QueryService } from '@/core/QueryService.js';
@@ -65,12 +65,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
super(meta, paramDef, async (ps, me) => {
const role = await this.rolesRepository.findOneBy({
id: ps.roleId,
+ isPublic: true,
});
if (role == null) {
throw new ApiError(meta.errors.noSuchRole);
}
-
+ if (!role.isExplorable) {
+ return [];
+ }
const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdã«æŒ‡å®šã—ãŸã‚‚ã®ã‚‚å«ã¾ã‚Œã‚‹ãŸã‚+1
const noteIdsRes = await this.redisClient.xrevrange(
`roleTimeline:${role.id}`,
diff --git a/packages/backend/src/server/api/endpoints/username/available.ts b/packages/backend/src/server/api/endpoints/username/available.ts
index c80b6efdcd..6293c5cb50 100644
--- a/packages/backend/src/server/api/endpoints/username/available.ts
+++ b/packages/backend/src/server/api/endpoints/username/available.ts
@@ -4,6 +4,7 @@ import type { UsedUsernamesRepository, UsersRepository } from '@/models/index.js
import { Endpoint } from '@/server/api/endpoint-base.js';
import { localUsernameSchema } from '@/models/entities/User.js';
import { DI } from '@/di-symbols.js';
+import { MetaService } from '@/core/MetaService.js';
export const meta = {
tags: ['users'],
@@ -39,9 +40,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
@Inject(DI.usedUsernamesRepository)
private usedUsernamesRepository: UsedUsernamesRepository,
+
+ private metaService: MetaService,
) {
super(meta, paramDef, async (ps, me) => {
- // Get exist
const exist = await this.usersRepository.countBy({
host: IsNull(),
usernameLower: ps.username.toLowerCase(),
@@ -49,8 +51,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
const exist2 = await this.usedUsernamesRepository.countBy({ username: ps.username.toLowerCase() });
+ const meta = await this.metaService.fetch();
+ const isPreserved = meta.preservedUsernames.map(x => x.toLowerCase()).includes(ps.username.toLowerCase());
+
return {
- available: exist === 0 && exist2 === 0,
+ available: exist === 0 && exist2 === 0 && !isPreserved,
};
});
}
diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts
index 8becb68a34..28cd9f6ce5 100644
--- a/packages/backend/src/server/api/endpoints/users.ts
+++ b/packages/backend/src/server/api/endpoints/users.ts
@@ -50,8 +50,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private queryService: QueryService,
) {
super(meta, paramDef, async (ps, me) => {
- const query = this.usersRepository.createQueryBuilder('user');
- query.where('user.isExplorable = TRUE');
+ const query = this.usersRepository.createQueryBuilder('user')
+ .where('user.isExplorable = TRUE')
+ .andWhere('user.isSuspended = FALSE');
switch (ps.state) {
case 'alive': query.andWhere('user.updatedAt > :date', { date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5) }); break;
diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts
index a840c1a04e..7510889526 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/create.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts
@@ -13,6 +13,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:account',
description: 'Create a new list of users.',
@@ -58,7 +60,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
if (currentCount > (await this.roleService.getUserPolicies(me.id)).userListLimit) {
throw new ApiError(meta.errors.tooManyUserLists);
}
-
+
const userList = await this.userListsRepository.insert({
id: this.idService.genId(),
createdAt: new Date(),
diff --git a/packages/backend/src/server/api/endpoints/users/lists/pull.ts b/packages/backend/src/server/api/endpoints/users/lists/pull.ts
index d2dd5731ee..d50b70efc2 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts
@@ -12,6 +12,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:account',
description: 'Remove a user from a list.',
diff --git a/packages/backend/src/server/api/endpoints/users/lists/push.ts b/packages/backend/src/server/api/endpoints/users/lists/push.ts
index 1c1fdc23f1..925037e484 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/push.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts
@@ -12,6 +12,8 @@ export const meta = {
requireCredential: true,
+ prohibitMoved: true,
+
kind: 'write:account',
description: 'Add a user to an existing list.',
diff --git a/packages/backend/src/server/api/endpoints/users/update-memo.ts b/packages/backend/src/server/api/endpoints/users/update-memo.ts
new file mode 100644
index 0000000000..ca7756ef75
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/users/update-memo.ts
@@ -0,0 +1,85 @@
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { IdService } from '@/core/IdService.js';
+import type { UserMemoRepository } from '@/models/index.js';
+import { DI } from '@/di-symbols.js';
+import { GetterService } from '@/server/api/GetterService.js';
+import { ApiError } from '../../error.js';
+
+export const meta = {
+ tags: ['account'],
+
+ requireCredential: true,
+
+ kind: 'write:account',
+
+ errors: {
+ noSuchUser: {
+ message: 'No such user.',
+ code: 'NO_SUCH_USER',
+ id: '6fef56f3-e765-4957-88e5-c6f65329b8a5',
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ userId: { type: 'string', format: 'misskey:id' },
+ memo: {
+ type: 'string',
+ nullable: true,
+ description: 'A personal memo for the target user. If null or empty, delete the memo.',
+ },
+ },
+ required: ['userId', 'memo'],
+} as const;
+
+// eslint-disable-next-line import/no-default-export
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+ constructor(
+ @Inject(DI.userMemosRepository)
+ private userMemosRepository: UserMemoRepository,
+ private getterService: GetterService,
+ private idService: IdService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ // Get target
+ const target = await this.getterService.getUser(ps.userId).catch(err => {
+ if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
+ throw err;
+ });
+
+ // 引数ãŒnullã‹ç©ºæ–‡å­—ã§ã‚れã°ã€ãƒ‘ーソナルメモを削除ã™ã‚‹
+ if (ps.memo === '' || ps.memo == null) {
+ await this.userMemosRepository.delete({
+ userId: me.id,
+ targetUserId: target.id,
+ });
+ return;
+ }
+
+ // 以å‰ã«ä½œæˆã•れãŸãƒ‘ーソナルメモãŒã‚ã‚‹ã‹ã©ã†ã‹ç¢ºèª
+ const previousMemo = await this.userMemosRepository.findOneBy({
+ userId: me.id,
+ targetUserId: target.id,
+ });
+
+ if (!previousMemo) {
+ await this.userMemosRepository.insert({
+ id: this.idService.genId(),
+ userId: me.id,
+ targetUserId: target.id,
+ memo: ps.memo,
+ });
+ } else {
+ await this.userMemosRepository.update(previousMemo.id, {
+ userId: me.id,
+ targetUserId: target.id,
+ memo: ps.memo,
+ });
+ }
+ });
+ }
+}
diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts
index b3e193cd34..e61e92c623 100644
--- a/packages/backend/src/server/web/UrlPreviewService.ts
+++ b/packages/backend/src/server/web/UrlPreviewService.ts
@@ -70,10 +70,10 @@ export class UrlPreviewService {
await summaly(url, {
followRedirects: false,
lang: lang ?? 'ja-JP',
- agent: {
+ agent: this.config.proxy ? {
http: this.httpRequestService.httpAgent,
https: this.httpRequestService.httpsAgent,
- },
+ } : undefined,
});
this.logger.succ(`Got preview of ${url}: ${summary.title}`);
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index d772ac3184..cb5d05a403 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -36,7 +36,7 @@ html
link(rel='prefetch' href='https://xn--931a.moe/assets/not-found.jpg')
link(rel='prefetch' href='https://xn--931a.moe/assets/error.jpg')
//- https://github.com/misskey-dev/misskey/issues/9842
- link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v2.12.0')
+ link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v2.17.0')
link(rel='modulepreload' href=`/vite/${clientEntry.file}`)
if !config.clientManifestExists
diff --git a/packages/backend/test/e2e/endpoints.ts b/packages/backend/test/e2e/endpoints.ts
index afb72c84d4..f885209b7f 100644
--- a/packages/backend/test/e2e/endpoints.ts
+++ b/packages/backend/test/e2e/endpoints.ts
@@ -4,8 +4,9 @@ import * as assert from 'assert';
// node-fetch only supports it's own Blob yet
// https://github.com/node-fetch/node-fetch/pull/1664
import { Blob } from 'node-fetch';
-import { startServer, signup, post, api, uploadFile, simpleGet } from '../utils.js';
+import { startServer, signup, post, api, uploadFile, simpleGet, initTestDb } from '../utils.js';
import type { INestApplicationContext } from '@nestjs/common';
+import { User } from '@/models/index.js';
describe('Endpoints', () => {
let app: INestApplicationContext;
@@ -289,6 +290,16 @@ describe('Endpoints', () => {
}, bob);
assert.strictEqual(res.status, 200);
+
+ const connection = await initTestDb(true);
+ const Users = connection.getRepository(User);
+ const newBob = await Users.findOneByOrFail({ id: bob.id });
+ assert.strictEqual(newBob.followersCount, 0);
+ assert.strictEqual(newBob.followingCount, 1);
+ const newAlice = await Users.findOneByOrFail({ id: alice.id });
+ assert.strictEqual(newAlice.followersCount, 1);
+ assert.strictEqual(newAlice.followingCount, 0);
+ connection.destroy();
});
test('æ—¢ã«ãƒ•ォローã—ã¦ã„ã‚‹å ´åˆã¯æ€’ã‚‹', async () => {
@@ -341,6 +352,16 @@ describe('Endpoints', () => {
}, bob);
assert.strictEqual(res.status, 200);
+
+ const connection = await initTestDb(true);
+ const Users = connection.getRepository(User);
+ const newBob = await Users.findOneByOrFail({ id: bob.id });
+ assert.strictEqual(newBob.followersCount, 0);
+ assert.strictEqual(newBob.followingCount, 0);
+ const newAlice = await Users.findOneByOrFail({ id: alice.id });
+ assert.strictEqual(newAlice.followersCount, 0);
+ assert.strictEqual(newAlice.followingCount, 0);
+ connection.destroy();
});
test('フォローã—ã¦ã„ãªã„å ´åˆã¯æ€’ã‚‹', async () => {
@@ -382,6 +403,100 @@ describe('Endpoints', () => {
});
});
+ describe('channels/search', () => {
+ test('空白検索ã§ä¸€è¦§ã‚’å–å¾—ã§ãã‚‹', async () => {
+ await api('/channels/create', {
+ name: 'aaa',
+ description: 'bbb',
+ }, bob);
+ await api('/channels/create', {
+ name: 'ccc1',
+ description: 'ddd1',
+ }, bob);
+ await api('/channels/create', {
+ name: 'ccc2',
+ description: 'ddd2',
+ }, bob);
+
+ const res = await api('/channels/search', {
+ query: '',
+ }, bob);
+
+ assert.strictEqual(res.status, 200);
+ assert.strictEqual(typeof res.body === 'object' && Array.isArray(res.body), true);
+ assert.strictEqual(res.body.length, 3);
+ });
+ test('åå‰ã®ã¿ã®æ¤œç´¢ã§åå‰ã‚’検索ã§ãã‚‹', async () => {
+ const res = await api('/channels/search', {
+ query: 'aaa',
+ type: 'nameOnly',
+ }, bob);
+
+ assert.strictEqual(res.status, 200);
+ assert.strictEqual(typeof res.body === 'object' && Array.isArray(res.body), true);
+ assert.strictEqual(res.body.length, 1);
+ assert.strictEqual(res.body[0].name, 'aaa');
+ });
+ test('åå‰ã®ã¿ã®æ¤œç´¢ã§åå‰ã‚’複数検索ã§ãã‚‹', async () => {
+ const res = await api('/channels/search', {
+ query: 'ccc',
+ type: 'nameOnly',
+ }, bob);
+
+ assert.strictEqual(res.status, 200);
+ assert.strictEqual(typeof res.body === 'object' && Array.isArray(res.body), true);
+ assert.strictEqual(res.body.length, 2);
+ });
+ test('åå‰ã®ã¿ã®æ¤œç´¢ã§èª¬æ˜Žã¯æ¤œç´¢ã§ããªã„', async () => {
+ const res = await api('/channels/search', {
+ query: 'bbb',
+ type: 'nameOnly',
+ }, bob);
+
+ assert.strictEqual(res.status, 200);
+ assert.strictEqual(typeof res.body === 'object' && Array.isArray(res.body), true);
+ assert.strictEqual(res.body.length, 0);
+ });
+ test('åå‰ã¨èª¬æ˜Žã®æ¤œç´¢ã§åå‰ã‚’検索ã§ãã‚‹', async () => {
+ const res = await api('/channels/search', {
+ query: 'ccc1',
+ }, bob);
+
+ assert.strictEqual(res.status, 200);
+ assert.strictEqual(typeof res.body === 'object' && Array.isArray(res.body), true);
+ assert.strictEqual(res.body.length, 1);
+ assert.strictEqual(res.body[0].name, 'ccc1');
+ });
+ test('åå‰ã¨èª¬æ˜Žã§ã®æ¤œç´¢ã§èª¬æ˜Žã‚’検索ã§ãã‚‹', async () => {
+ const res = await api('/channels/search', {
+ query: 'ddd1',
+ }, bob);
+
+ assert.strictEqual(res.status, 200);
+ assert.strictEqual(typeof res.body === 'object' && Array.isArray(res.body), true);
+ assert.strictEqual(res.body.length, 1);
+ assert.strictEqual(res.body[0].name, 'ccc1');
+ });
+ test('åå‰ã¨èª¬æ˜Žã®æ¤œç´¢ã§åå‰ã‚’複数検索ã§ãã‚‹', async () => {
+ const res = await api('/channels/search', {
+ query: 'ccc',
+ }, bob);
+
+ assert.strictEqual(res.status, 200);
+ assert.strictEqual(typeof res.body === 'object' && Array.isArray(res.body), true);
+ assert.strictEqual(res.body.length, 2);
+ });
+ test('åå‰ã¨èª¬æ˜Žã§ã®æ¤œç´¢ã§èª¬æ˜Žã‚’複数検索ã§ãã‚‹', async () => {
+ const res = await api('/channels/search', {
+ query: 'ddd',
+ }, bob);
+
+ assert.strictEqual(res.status, 200);
+ assert.strictEqual(typeof res.body === 'object' && Array.isArray(res.body), true);
+ assert.strictEqual(res.body.length, 2);
+ });
+ });
+
describe('drive', () => {
test('ドライブ情報をå–å¾—ã§ãã‚‹', async () => {
await uploadFile(alice, {
@@ -849,4 +964,85 @@ describe('Endpoints', () => {
assert.strictEqual(res.body.error.code, 'URL_PREVIEW_FAILED');
});
});
+
+ describe('パーソナルメモ機能ã®ãƒ†ã‚¹ãƒˆ', () => {
+ test('他者ã«é–¢ã™ã‚‹ãƒ¡ãƒ¢ã‚’æ›´æ–°ã§ãã‚‹', async () => {
+ const memo = '10月ã¾ã§ä½Žæµ®ä¸Šã¨ã®ã“ã¨ã€‚';
+
+ const res1 = await api('/users/update-memo', {
+ memo,
+ userId: bob.id,
+ }, alice);
+
+ const res2 = await api('/users/show', {
+ userId: bob.id,
+ }, alice);
+ assert.strictEqual(res1.status, 204);
+ assert.strictEqual(res2.body?.memo, memo);
+ });
+
+ test('自分ã«é–¢ã™ã‚‹ãƒ¡ãƒ¢ã‚’æ›´æ–°ã§ãã‚‹', async () => {
+ const memo = 'ãƒã‚±ãƒƒãƒˆã‚’月末ã¾ã§ã«è²·ã†ã€‚';
+
+ const res1 = await api('/users/update-memo', {
+ memo,
+ userId: alice.id,
+ }, alice);
+
+ const res2 = await api('/users/show', {
+ userId: alice.id,
+ }, alice);
+ assert.strictEqual(res1.status, 204);
+ assert.strictEqual(res2.body?.memo, memo);
+ });
+
+ test('メモを削除ã§ãã‚‹', async () => {
+ const memo = '10月ã¾ã§ä½Žæµ®ä¸Šã¨ã®ã“ã¨ã€‚';
+
+ await api('/users/update-memo', {
+ memo,
+ userId: bob.id,
+ }, alice);
+
+ await api('/users/update-memo', {
+ memo: '',
+ userId: bob.id,
+ }, alice);
+
+ const res = await api('/users/show', {
+ userId: bob.id,
+ }, alice);
+
+ // memoã«ã¯å¸¸ã«æ–‡å­—列ã‹nullãŒå…¥ã£ã¦ã„ã‚‹(5cac151)
+ assert.strictEqual(res.body.memo, null);
+ });
+
+ test('メモã¯å€‹äººã”ã¨ã«ç‹¬ç«‹ã—ã¦ä¿å­˜ã•れる', async () => {
+ const memoAliceToBob = '10月ã¾ã§ä½Žæµ®ä¸Šã¨ã®ã“ã¨ã€‚';
+ const memoCarolToBob = '例ã®ä»¶ã«ã¤ã„ã¦ä»Šåº¦å•ã„ãŸã ã™ã€‚';
+
+ await Promise.all([
+ api('/users/update-memo', {
+ memo: memoAliceToBob,
+ userId: bob.id,
+ }, alice),
+ api('/users/update-memo', {
+ memo: memoCarolToBob,
+ userId: bob.id,
+ }, carol),
+ ]);
+
+ const [resAlice, resCarol] = await Promise.all([
+ api('/users/show', {
+ userId: bob.id,
+ }, alice),
+ api('/users/show', {
+ userId: bob.id,
+ }, carol),
+ ]);
+
+ assert.strictEqual(resAlice.body.memo, memoAliceToBob);
+ assert.strictEqual(resCarol.body.memo, memoCarolToBob);
+ });
+ });
});
diff --git a/packages/backend/test/e2e/move.ts b/packages/backend/test/e2e/move.ts
new file mode 100644
index 0000000000..7d6c646090
--- /dev/null
+++ b/packages/backend/test/e2e/move.ts
@@ -0,0 +1,456 @@
+process.env.NODE_ENV = 'test';
+
+import * as assert from 'assert';
+import rndstr from 'rndstr';
+import { loadConfig } from '@/config.js';
+import { User, UsersRepository } from '@/models/index.js';
+import { jobQueue } from '@/boot/common.js';
+import { uploadFile, signup, startServer, initTestDb, api, sleep, successfulApiCall } from '../utils.js';
+import type { INestApplicationContext } from '@nestjs/common';
+
+describe('Account Move', () => {
+ let app: INestApplicationContext;
+ let jq: INestApplicationContext;
+ let url: URL;
+
+ let root: any;
+ let alice: any;
+ let bob: any;
+ let carol: any;
+ let dave: any;
+ let eve: any;
+ let frank: any;
+
+ let Users: UsersRepository;
+
+ beforeAll(async () => {
+ app = await startServer();
+ jq = await jobQueue();
+ const config = loadConfig();
+ url = new URL(config.url);
+ const connection = await initTestDb(false);
+ root = await signup({ username: 'root' });
+ alice = await signup({ username: 'alice' });
+ bob = await signup({ username: 'bob' });
+ carol = await signup({ username: 'carol' });
+ dave = await signup({ username: 'dave' });
+ eve = await signup({ username: 'eve' });
+ frank = await signup({ username: 'frank' });
+ Users = connection.getRepository(User);
+ }, 1000 * 60 * 2);
+
+ afterAll(async () => {
+ await Promise.all([app.close(), jq.close()]);
+ });
+
+ describe('Create Alias', () => {
+ afterEach(async () => {
+ await Users.update(bob.id, { alsoKnownAs: null });
+ }, 1000 * 10);
+
+ test('Able to create an alias', async () => {
+ const res = await api('/i/update', {
+ alsoKnownAs: [`@alice@${url.hostname}`],
+ }, bob);
+
+ const newBob = await Users.findOneByOrFail({ id: bob.id });
+ assert.strictEqual(newBob.alsoKnownAs?.length, 1);
+ assert.strictEqual(newBob.alsoKnownAs[0], `${url.origin}/users/${alice.id}`);
+ assert.strictEqual(res.body.alsoKnownAs?.length, 1);
+ assert.strictEqual(res.body.alsoKnownAs[0], alice.id);
+ });
+
+ test('Able to create a local alias without hostname', async () => {
+ await api('/i/update', {
+ alsoKnownAs: ['@alice'],
+ }, bob);
+
+ const newBob = await Users.findOneByOrFail({ id: bob.id });
+ assert.strictEqual(newBob.alsoKnownAs?.length, 1);
+ assert.strictEqual(newBob.alsoKnownAs[0], `${url.origin}/users/${alice.id}`);
+ });
+
+ test('Able to create a local alias without @', async () => {
+ await api('/i/update', {
+ alsoKnownAs: ['alice'],
+ }, bob);
+
+ const newBob = await Users.findOneByOrFail({ id: bob.id });
+ assert.strictEqual(newBob.alsoKnownAs?.length, 1);
+ assert.strictEqual(newBob.alsoKnownAs[0], `${url.origin}/users/${alice.id}`);
+ });
+
+ test('Able to set remote user (but may fail)', async () => {
+ const res = await api('/i/update', {
+ alsoKnownAs: ['@syuilo@example.com'],
+ }, bob);
+
+ assert.strictEqual(res.status, 400);
+ assert.strictEqual(res.body.error.code, 'NO_SUCH_USER');
+ assert.strictEqual(res.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
+ });
+
+ test('Unable to add duplicated aliases to alsoKnownAs', async () => {
+ const res = await api('/i/update', {
+ alsoKnownAs: [`@alice@${url.hostname}`, `@alice@${url.hostname}`],
+ }, bob);
+
+ assert.strictEqual(res.status, 400);
+ assert.strictEqual(res.body.error.code, 'INVALID_PARAM');
+ assert.strictEqual(res.body.error.id, '3d81ceae-475f-4600-b2a8-2bc116157532');
+ });
+
+ test('Unable to add itself', async () => {
+ const res = await api('/i/update', {
+ alsoKnownAs: [`@bob@${url.hostname}`],
+ }, bob);
+
+ assert.strictEqual(res.status, 400);
+ assert.strictEqual(res.body.error.code, 'FORBIDDEN_TO_SET_YOURSELF');
+ assert.strictEqual(res.body.error.id, '25c90186-4ab0-49c8-9bba-a1fa6c202ba4');
+ });
+
+ test('Unable to add a nonexisting local account to alsoKnownAs', async () => {
+ const res1 = await api('/i/update', {
+ alsoKnownAs: [`@nonexist@${url.hostname}`],
+ }, bob);
+
+ assert.strictEqual(res1.status, 400);
+ assert.strictEqual(res1.body.error.code, 'NO_SUCH_USER');
+ assert.strictEqual(res1.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
+
+ const res2 = await api('/i/update', {
+ alsoKnownAs: ['@alice', 'nonexist'],
+ }, bob);
+
+ assert.strictEqual(res2.status, 400);
+ assert.strictEqual(res2.body.error.code, 'NO_SUCH_USER');
+ assert.strictEqual(res2.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
+ });
+
+ test('Able to add two existing local account to alsoKnownAs', async () => {
+ await api('/i/update', {
+ alsoKnownAs: [`@alice@${url.hostname}`, `@carol@${url.hostname}`],
+ }, bob);
+
+ const newBob = await Users.findOneByOrFail({ id: bob.id });
+ assert.strictEqual(newBob.alsoKnownAs?.length, 2);
+ assert.strictEqual(newBob.alsoKnownAs[0], `${url.origin}/users/${alice.id}`);
+ assert.strictEqual(newBob.alsoKnownAs[1], `${url.origin}/users/${carol.id}`);
+ });
+
+ test('Able to properly overwrite alsoKnownAs', async () => {
+ await api('/i/update', {
+ alsoKnownAs: [`@alice@${url.hostname}`],
+ }, bob);
+ await api('/i/update', {
+ alsoKnownAs: [`@carol@${url.hostname}`, `@dave@${url.hostname}`],
+ }, bob);
+
+ const newBob = await Users.findOneByOrFail({ id: bob.id });
+ assert.strictEqual(newBob.alsoKnownAs?.length, 2);
+ assert.strictEqual(newBob.alsoKnownAs[0], `${url.origin}/users/${carol.id}`);
+ assert.strictEqual(newBob.alsoKnownAs[1], `${url.origin}/users/${dave.id}`);
+ });
+ });
+
+ describe('Local to Local', () => {
+ let antennaId = '';
+
+ beforeAll(async () => {
+ await api('/i/update', {
+ alsoKnownAs: [`@alice@${url.hostname}`],
+ }, root);
+ const listRoot = await api('/users/lists/create', {
+ name: rndstr('0-9a-z', 8),
+ }, root);
+ await api('/users/lists/push', {
+ listId: listRoot.body.id,
+ userId: alice.id,
+ }, root);
+
+ await api('/following/create', {
+ userId: root.id,
+ }, alice);
+ await api('/following/create', {
+ userId: eve.id,
+ }, alice);
+ const antenna = await api('/antennas/create', {
+ name: rndstr('0-9a-z', 8),
+ src: 'home',
+ keywords: [rndstr('0-9a-z', 8)],
+ excludeKeywords: [],
+ users: [],
+ caseSensitive: false,
+ withReplies: false,
+ withFile: false,
+ notify: false,
+ }, alice);
+ antennaId = antenna.body.id;
+
+ await api('/i/update', {
+ alsoKnownAs: [`@alice@${url.hostname}`],
+ }, bob);
+
+ await api('/following/create', {
+ userId: alice.id,
+ }, carol);
+
+ await api('/mute/create', {
+ userId: alice.id,
+ }, dave);
+ await api('/blocking/create', {
+ userId: alice.id,
+ }, dave);
+ await api('/following/create', {
+ userId: eve.id,
+ }, dave);
+
+ await api('/following/create', {
+ userId: dave.id,
+ }, eve);
+ const listEve = await api('/users/lists/create', {
+ name: rndstr('0-9a-z', 8),
+ }, eve);
+ await api('/users/lists/push', {
+ listId: listEve.body.id,
+ userId: bob.id,
+ }, eve);
+
+ await api('/i/update', {
+ isLocked: true,
+ }, frank);
+ await api('/following/create', {
+ userId: frank.id,
+ }, alice);
+ await api('/following/requests/accept', {
+ userId: alice.id,
+ }, frank);
+ }, 1000 * 10);
+
+ test('Prohibit the root account from moving', async () => {
+ const res = await api('/i/move', {
+ moveToAccount: `@bob@${url.hostname}`,
+ }, root);
+
+ assert.strictEqual(res.status, 400);
+ assert.strictEqual(res.body.error.code, 'NOT_ROOT_FORBIDDEN');
+ assert.strictEqual(res.body.error.id, '4362e8dc-731f-4ad8-a694-be2a88922a24');
+ });
+
+ test('Unable to move to a nonexisting local account', async () => {
+ const res = await api('/i/move', {
+ moveToAccount: `@nonexist@${url.hostname}`,
+ }, alice);
+
+ assert.strictEqual(res.status, 400);
+ assert.strictEqual(res.body.error.code, 'NO_SUCH_USER');
+ assert.strictEqual(res.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
+ });
+
+ test('Unable to move if alsoKnownAs is invalid', async () => {
+ const res = await api('/i/move', {
+ moveToAccount: `@carol@${url.hostname}`,
+ }, alice);
+
+ assert.strictEqual(res.status, 400);
+ assert.strictEqual(res.body.error.code, 'DESTINATION_ACCOUNT_FORBIDS');
+ assert.strictEqual(res.body.error.id, 'b5c90186-4ab0-49c8-9bba-a1f766282ba4');
+ });
+
+ test('Relationships have been properly migrated', async () => {
+ const move = await api('/i/move', {
+ moveToAccount: `@bob@${url.hostname}`,
+ }, alice);
+
+ assert.strictEqual(move.status, 200);
+
+ await sleep(1000 * 3); // wait for jobs to finish
+
+ // Unfollow delayed?
+ const aliceFollowings = await api('/users/following', {
+ userId: alice.id,
+ }, alice);
+ assert.strictEqual(aliceFollowings.status, 200);
+ assert.strictEqual(aliceFollowings.body.length, 3);
+
+ const carolFollowings = await api('/users/following', {
+ userId: carol.id,
+ }, carol);
+ assert.strictEqual(carolFollowings.status, 200);
+ assert.strictEqual(carolFollowings.body.length, 2);
+ assert.strictEqual(carolFollowings.body[0].followeeId, bob.id);
+ assert.strictEqual(carolFollowings.body[1].followeeId, alice.id);
+
+ const blockings = await api('/blocking/list', {}, dave);
+ assert.strictEqual(blockings.status, 200);
+ assert.strictEqual(blockings.body.length, 2);
+ assert.strictEqual(blockings.body[0].blockeeId, bob.id);
+ assert.strictEqual(blockings.body[1].blockeeId, alice.id);
+
+ const mutings = await api('/mute/list', {}, dave);
+ assert.strictEqual(mutings.status, 200);
+ assert.strictEqual(mutings.body.length, 2);
+ assert.strictEqual(mutings.body[0].muteeId, bob.id);
+ assert.strictEqual(mutings.body[1].muteeId, alice.id);
+
+ const rootLists = await api('/users/lists/list', {}, root);
+ assert.strictEqual(rootLists.status, 200);
+ assert.strictEqual(rootLists.body[0].userIds.length, 2);
+ assert.ok(rootLists.body[0].userIds.find((id: string) => id === bob.id));
+ assert.ok(rootLists.body[0].userIds.find((id: string) => id === alice.id));
+
+ const eveLists = await api('/users/lists/list', {}, eve);
+ assert.strictEqual(eveLists.status, 200);
+ assert.strictEqual(eveLists.body[0].userIds.length, 1);
+ assert.ok(eveLists.body[0].userIds.find((id: string) => id === bob.id));
+ });
+
+ test('A locked account automatically accept the follow request if it had already accepted the old account.', async () => {
+ await successfulApiCall({
+ endpoint: '/following/create',
+ parameters: {
+ userId: frank.id,
+ },
+ user: bob,
+ });
+ const followers = await api('/users/followers', {
+ userId: frank.id,
+ }, frank);
+
+ assert.strictEqual(followers.status, 200);
+ assert.strictEqual(followers.body.length, 2);
+ assert.strictEqual(followers.body[0].followerId, bob.id);
+ });
+
+ test('Unfollowed after 10 sec (24 hours in production).', async () => {
+ await sleep(1000 * 8);
+
+ const following = await api('/users/following', {
+ userId: alice.id,
+ }, alice);
+
+ assert.strictEqual(following.status, 200);
+ assert.strictEqual(following.body.length, 0);
+ });
+
+ test('Unable to move if the destination account has already moved.', async () => {
+ const res = await api('/i/move', {
+ moveToAccount: `@alice@${url.hostname}`,
+ }, bob);
+
+ assert.strictEqual(res.status, 400);
+ assert.strictEqual(res.body.error.code, 'DESTINATION_ACCOUNT_FORBIDS');
+ assert.strictEqual(res.body.error.id, 'b5c90186-4ab0-49c8-9bba-a1f766282ba4');
+ });
+
+ test('Follow and follower counts are properly adjusted', async () => {
+ await api('/following/create', {
+ userId: alice.id,
+ }, eve);
+ const newAlice = await Users.findOneByOrFail({ id: alice.id });
+ const newCarol = await Users.findOneByOrFail({ id: carol.id });
+ let newEve = await Users.findOneByOrFail({ id: eve.id });
+ assert.strictEqual(newAlice.movedToUri, `${url.origin}/users/${bob.id}`);
+ assert.strictEqual(newAlice.followingCount, 0);
+ assert.strictEqual(newAlice.followersCount, 0);
+ assert.strictEqual(newCarol.followingCount, 1);
+ assert.strictEqual(newEve.followingCount, 1);
+ assert.strictEqual(newEve.followersCount, 1);
+
+ await api('/following/delete', {
+ userId: alice.id,
+ }, eve);
+ newEve = await Users.findOneByOrFail({ id: eve.id });
+ assert.strictEqual(newEve.followingCount, 1);
+ assert.strictEqual(newEve.followersCount, 1);
+ });
+
+ test.each([
+ '/antennas/create',
+ '/channels/create',
+ '/channels/favorite',
+ '/channels/follow',
+ '/channels/unfavorite',
+ '/channels/unfollow',
+ '/clips/add-note',
+ '/clips/create',
+ '/clips/favorite',
+ '/clips/remove-note',
+ '/clips/unfavorite',
+ '/clips/update',
+ '/drive/files/upload-from-url',
+ '/flash/create',
+ '/flash/like',
+ '/flash/unlike',
+ '/flash/update',
+ '/following/create',
+ '/gallery/posts/create',
+ '/gallery/posts/like',
+ '/gallery/posts/unlike',
+ '/gallery/posts/update',
+ '/i/claim-achievement',
+ '/i/move',
+ '/i/import-blocking',
+ '/i/import-following',
+ '/i/import-muting',
+ '/i/import-user-lists',
+ '/i/pin',
+ '/mute/create',
+ '/notes/create',
+ '/notes/favorites/create',
+ '/notes/polls/vote',
+ '/notes/reactions/create',
+ '/pages/create',
+ '/pages/like',
+ '/pages/unlike',
+ '/pages/update',
+ '/renote-mute/create',
+ '/users/lists/create',
+ '/users/lists/pull',
+ '/users/lists/push',
+ ])('Prohibit access after moving: %s', async (endpoint) => {
+ const res = await api(endpoint, {}, alice);
+ assert.strictEqual(res.status, 403);
+ assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED');
+ assert.strictEqual(res.body.error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
+ });
+
+ test('Prohibit access after moving: /antennas/update', async () => {
+ const res = await api('/antennas/update', {
+ antennaId,
+ name: rndstr('0-9a-z', 8),
+ src: 'users',
+ keywords: [rndstr('0-9a-z', 8)],
+ excludeKeywords: [],
+ users: [eve.id],
+ caseSensitive: false,
+ withReplies: false,
+ withFile: false,
+ notify: false,
+ }, alice);
+
+ assert.strictEqual(res.status, 403);
+ assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED');
+ assert.strictEqual(res.body.error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
+ });
+
+ test('Prohibit access after moving: /drive/files/create', async () => {
+ const res = await uploadFile(alice);
+
+ assert.strictEqual(res.status, 403);
+ assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED');
+ assert.strictEqual(res.body.error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
+ });
+
+ test('Prohibit updating alsoKnownAs after moving', async () => {
+ const res = await api('/i/update', {
+ alsoKnownAs: [`@eve@${url.hostname}`],
+ }, alice);
+
+ assert.strictEqual(res.status, 403);
+ assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED');
+ assert.strictEqual(res.body.error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
+ });
+ });
+});
diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts
index e87045a8cf..9c851a5dd6 100644
--- a/packages/backend/test/e2e/note.ts
+++ b/packages/backend/test/e2e/note.ts
@@ -352,6 +352,72 @@ describe('Note', () => {
assert.strictEqual(myNote.renote.reply.files.length, 1);
assert.strictEqual(myNote.renote.reply.files[0].id, file.body.id);
});
+
+ test('NSFWãŒå¼·åˆ¶ã•れã¦ã„ã‚‹å ´åˆå¤‰æ›´ã§ããªã„', async () => {
+ const file = await uploadFile(alice);
+
+ const res = await api('admin/roles/create', {
+ name: 'test',
+ description: '',
+ color: null,
+ iconUrl: null,
+ displayOrder: 0,
+ target: 'manual',
+ condFormula: {},
+ isAdministrator: false,
+ isModerator: false,
+ isPublic: false,
+ isExplorable: false,
+ asBadge: false,
+ canEditMembersByModerator: false,
+ policies: {
+ alwaysMarkNsfw: {
+ useDefault: false,
+ priority: 0,
+ value: true,
+ },
+ },
+ }, alice);
+
+ assert.strictEqual(res.status, 200);
+
+ const assign = await api('admin/roles/assign', {
+ userId: alice.id,
+ roleId: res.body.id,
+ }, alice);
+
+ assert.strictEqual(assign.status, 204);
+ assert.strictEqual(file.body.isSensitive, false);
+
+ const nsfwfile = await uploadFile(alice);
+
+ assert.strictEqual(nsfwfile.status, 200);
+ assert.strictEqual(nsfwfile.body.isSensitive, true);
+
+ const liftnsfw = await api('drive/files/update', {
+ fileId: nsfwfile.body.id,
+ isSensitive: false,
+ }, alice);
+
+ assert.strictEqual(liftnsfw.status, 400);
+ assert.strictEqual(liftnsfw.body.error.code, 'RESTRICTED_BY_ROLE');
+
+ const oldaddnsfw = await api('drive/files/update', {
+ fileId: file.body.id,
+ isSensitive: true,
+ }, alice);
+
+ assert.strictEqual(oldaddnsfw.status, 200);
+
+ await api('admin/roles/unassign', {
+ userId: alice.id,
+ roleId: res.body.id,
+ });
+
+ await api('admin/roles/delete', {
+ roleId: res.body.id,
+ }, alice);
+ });
});
describe('notes/create', () => {
diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts
index bc3455e346..51537dda16 100644
--- a/packages/backend/test/e2e/users.ts
+++ b/packages/backend/test/e2e/users.ts
@@ -31,7 +31,7 @@ describe('ユーザー', () => {
}, {});
};
- // FIXME: 足りãªã„キーãŒãŸãã•ã‚“ã‚ã‚‹
+ // BUG misskey-jsã¨json-schemaã¨å®Ÿéš›ã«è¿”ã£ã¦ãるデータãŒå…¨éƒ¨é•ã†
type UserLite = misskey.entities.UserLite & {
badgeRoles: any[],
};
@@ -51,10 +51,11 @@ describe('ユーザー', () => {
type User = MeDetailed & { token: string };
- const show = async (id: string, me = alice): Promise<MeDetailed | UserDetailedNotMe> => {
+ const show = async (id: string, me = root): Promise<MeDetailed | UserDetailedNotMe> => {
return successfulApiCall({ endpoint: 'users/show', parameters: { userId: id }, user: me }) as any;
};
+ // UserLiteã®ã‚­ãƒ¼ãŒéŽä¸è¶³ãªãå…¥ã£ã¦ã„る?
const userLite = (user: User): Partial<UserLite> => {
return stripUndefined({
id: user.id,
@@ -76,12 +77,13 @@ describe('ユーザー', () => {
});
};
+ // UserDetailedNotMeã®ã‚­ãƒ¼ãŒéŽä¸è¶³ãªãå…¥ã£ã¦ã„る?
const userDetailedNotMe = (user: User): Partial<UserDetailedNotMe> => {
return stripUndefined({
...userLite(user),
url: user.url,
uri: user.uri,
- movedToUri: user.movedToUri,
+ movedTo: user.movedTo,
alsoKnownAs: user.alsoKnownAs,
createdAt: user.createdAt,
updatedAt: user.updatedAt,
@@ -109,9 +111,11 @@ describe('ユーザー', () => {
usePasswordLessLogin: user.usePasswordLessLogin,
securityKeys: user.securityKeys,
roles: user.roles,
+ memo: user.memo,
});
};
+ // Relations関連ã®ã‚­ãƒ¼ãŒéŽä¸è¶³ãªãå…¥ã£ã¦ã„る?
const userDetailedNotMeWithRelations = (user: User): Partial<UserDetailedNotMe> => {
return stripUndefined({
...userDetailedNotMe(user),
@@ -126,6 +130,7 @@ describe('ユーザー', () => {
});
};
+ // MeDetailedã®ã‚­ãƒ¼ãŒéŽä¸è¶³ãªãå…¥ã£ã¦ã„る?
const meDetailed = (user: User, security = false): Partial<MeDetailed> => {
return stripUndefined({
...userDetailedNotMe(user),
@@ -216,8 +221,8 @@ describe('ユーザー', () => {
}, 1000 * 60 * 2);
beforeAll(async () => {
- root = await signup({ username: 'alice' });
- alice = root;
+ root = await signup({ username: 'root' });
+ alice = await signup({ username: 'alice' });
aliceNote = await post(alice, { text: 'test' }) as any;
alicePage = await page(alice);
aliceList = (await api('users/list/create', { name: 'aliceList' }, alice)).body;
@@ -343,7 +348,7 @@ describe('ユーザー', () => {
// UserDetailedNotMeOnly
assert.strictEqual(response.url, null);
assert.strictEqual(response.uri, null);
- assert.strictEqual(response.movedToUri, null);
+ assert.strictEqual(response.movedTo, null);
assert.strictEqual(response.alsoKnownAs, null);
assert.strictEqual(response.createdAt, new Date(response.createdAt).toISOString());
assert.strictEqual(response.updatedAt, null);
@@ -371,6 +376,7 @@ describe('ユーザー', () => {
assert.strictEqual(response.usePasswordLessLogin, false);
assert.strictEqual(response.securityKeys, false);
assert.deepStrictEqual(response.roles, []);
+ assert.strictEqual(response.memo, null);
// MeDetailedOnly
assert.strictEqual(response.avatarId, null);
@@ -410,7 +416,7 @@ describe('ユーザー', () => {
//#endregion
//#region è‡ªåˆ†ã®æƒ…å ±(i)
- test('を読ã¿å–ã‚‹ã“ã¨ãŒã§ãる。(自分)', async () => {
+ test('を読ã¿å–ã‚‹ã“ã¨ãŒã§ãã‚‹ã“ã¨ï¼ˆè‡ªåˆ†ï¼‰ã€ã‚­ãƒ¼ãŒéŽä¸è¶³ãªãå…¥ã£ã¦ã„ã‚‹ã“ã¨ã€‚', async () => {
const response = await successfulApiCall({
endpoint: 'i',
parameters: {},
@@ -502,7 +508,6 @@ describe('ユーザー', () => {
};
assert.deepStrictEqual(response, expected, inspect(parameters));
- if (1) return; // BUG 521eb95 以é™ã‚¢ãƒã‚¿ãƒ¼ã®ãƒªã‚»ãƒƒãƒˆãŒã§ããªã„。
const parameters2 = { avatarId: null };
const response2 = await successfulApiCall({ endpoint: 'i/update', parameters: parameters2, user: alice });
const expected2 = {
@@ -528,7 +533,6 @@ describe('ユーザー', () => {
};
assert.deepStrictEqual(response, expected, inspect(parameters));
- if (1) return; // BUG 521eb95 以é™ãƒãƒŠãƒ¼ã®ãƒªã‚»ãƒƒãƒˆãŒã§ããªã„。
const parameters2 = { bannerId: null };
const response2 = await successfulApiCall({ endpoint: 'i/update', parameters: parameters2, user: alice });
const expected2 = {
@@ -555,6 +559,21 @@ describe('ユーザー', () => {
});
//#endregion
+ //#region ãƒ¡ãƒ¢ã®æ›´æ–°(users/update-memo)
+
+ test.each([
+ { label: '最大長', memo: 'x'.repeat(2048) },
+ { label: '空文字', memo: '', expects: null },
+ { label: 'null', memo: null },
+ ])('ã‚’æ›¸ãæ›ãˆã‚‹ã“ã¨ãŒã§ãã‚‹(メモを$labelã«)', async ({ memo, expects }) => {
+ const expected = { ...await show(bob.id, alice), memo: expects === undefined ? memo : expects };
+ const parameters = { userId: bob.id, memo };
+ await successfulApiCall({ endpoint: 'users/update-memo', parameters, user: alice });
+ const response = await show(bob.id, alice);
+ assert.deepStrictEqual(response, expected);
+ });
+
+ //#endregion
//#region ユーザー(users)
test.each([
@@ -569,7 +588,7 @@ describe('ユーザー', () => {
const response = await successfulApiCall({ endpoint: 'users', parameters, user: alice });
// çµæžœã®ä¸¦ã³ã‚’事å‰ã«ã‚¢ã‚µãƒ¼ãƒˆã™ã‚‹ã®ã¯å›°é›£ãªã®ã§è¿”ã£ã¦ããŸidã«å¯¾å¿œã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒè¿”ã£ã¦ãŠã‚Šã€ã‚½ãƒ¼ãƒˆé †ãŒæ­£ã—ã„ã“ã¨ã ã‘を検証ã™ã‚‹
- const users = await Promise.all(response.map(u => show(u.id)));
+ const users = await Promise.all(response.map(u => show(u.id, alice)));
const expected = users.sort((x, y) => {
const index = (selector(x) < selector(y)) ? -1 : (selector(x) > selector(y)) ? 1 : 0;
return index * (parameters.sort?.startsWith('+') ? -1 : 1);
@@ -583,13 +602,13 @@ describe('ユーザー', () => {
{ label: 'ブロックã—ã¦ãã¦ã„るユーザーãŒå«ã¾ã‚Œã‚‹', user: (): User => userBlockingAlice, excluded: true },
{ label: '承èªåˆ¶ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒå«ã¾ã‚Œã‚‹', user: (): User => userLocking },
{ label: 'サイレンスユーザーãŒå«ã¾ã‚Œã‚‹', user: (): User => userSilenced },
- { label: 'サスペンドユーザーãŒå«ã¾ã‚Œã‚‹', user: (): User => userSuspended },
+ { label: 'サスペンドユーザーãŒå«ã¾ã‚Œãªã„', user: (): User => userSuspended, excluded: true },
{ label: '削除済ユーザーãŒå«ã¾ã‚Œã‚‹', user: (): User => userDeletedBySelf },
{ label: '削除済(byAdmin)ユーザーãŒå«ã¾ã‚Œã‚‹', user: (): User => userDeletedByAdmin },
] as const)('をリスト形å¼ã§å–å¾—ã™ã‚‹ã“ã¨ãŒã§ãã€çµæžœã«$label', async ({ user, excluded }) => {
const parameters = { limit: 100 };
const response = await successfulApiCall({ endpoint: 'users', parameters, user: alice });
- const expected = (excluded ?? false) ? [] : [await show(user().id)];
+ const expected = (excluded ?? false) ? [] : [await show(user().id, alice)];
assert.deepStrictEqual(response.filter((u) => u.id === user().id), expected);
});
test.todo('をリスト形å¼ã§å–å¾—ã™ã‚‹ã“ã¨ãŒã§ãる(リモート, hostname指定)');
@@ -616,7 +635,7 @@ describe('ユーザー', () => {
{ label: 'Moderatorã«ãªã£ã¦ã„ã‚‹', user: (): User => userModerator, me: (): User => userModerator, selector: (user: User): unknown => user.isModerator },
{ label: '自分以外ã‹ã‚‰è¦‹ãŸã¨ãã¯Moderatorã‹åˆ¤å®šã§ããªã„', user: (): User => userModerator, selector: (user: User): unknown => user.isModerator, expected: (): undefined => undefined },
{ label: 'サイレンスã«ãªã£ã¦ã„ã‚‹', user: (): User => userSilenced, selector: (user: User): unknown => user.isSilenced },
- { label: 'サスペンドã«ãªã£ã¦ã„ã‚‹', user: (): User => userSuspended, selector: (user: User): unknown => user.isSuspended },
+ //{ label: 'サスペンドã«ãªã£ã¦ã„ã‚‹', user: (): User => userSuspended, selector: (user: User): unknown => user.isSuspended },
{ label: '削除済ã¿ã«ãªã£ã¦ã„ã‚‹', user: (): User => userDeletedBySelf, me: (): User => userDeletedBySelf, selector: (user: User): unknown => user.isDeleted },
{ label: '自分以外ã‹ã‚‰è¦‹ãŸã¨ãã¯å‰Šé™¤æ¸ˆã¿ã‹åˆ¤å®šã§ããªã„', user: (): User => userDeletedBySelf, selector: (user: User): unknown => user.isDeleted, expected: (): undefined => undefined },
{ label: '削除済ã¿(byAdmin)ã«ãªã£ã¦ã„ã‚‹', user: (): User => userDeletedByAdmin, me: (): User => userDeletedByAdmin, selector: (user: User): unknown => user.isDeleted },
@@ -698,13 +717,13 @@ describe('ユーザー', () => {
test('を検索ã™ã‚‹ã“ã¨ãŒã§ãã‚‹', async () => {
const parameters = { query: 'carol', limit: 10 };
const response = await successfulApiCall({ endpoint: 'users/search', parameters, user: alice });
- const expected = [await show(carol.id)];
+ const expected = [await show(carol.id, alice)];
assert.deepStrictEqual(response, expected);
});
test('を検索ã™ã‚‹ã“ã¨ãŒã§ãã‚‹(UserLite)', async () => {
const parameters = { query: 'carol', detail: false, limit: 10 };
const response = await successfulApiCall({ endpoint: 'users/search', parameters, user: alice });
- const expected = [userLite(await show(carol.id))];
+ const expected = [userLite(await show(carol.id, alice))];
assert.deepStrictEqual(response, expected);
});
test.each([
@@ -720,7 +739,7 @@ describe('ユーザー', () => {
] as const)('を検索ã™ã‚‹ã“ã¨ãŒã§ãã€çµæžœã«$labelãŒå«ã¾ã‚Œã‚‹', async ({ user, excluded }) => {
const parameters = { query: user().username, limit: 1 };
const response = await successfulApiCall({ endpoint: 'users/search', parameters, user: alice });
- const expected = (excluded ?? false) ? [] : [await show(user().id)];
+ const expected = (excluded ?? false) ? [] : [await show(user().id, alice)];
assert.deepStrictEqual(response, expected);
});
test.todo('を検索ã™ã‚‹ã“ã¨ãŒã§ãã‚‹(リモート)');
@@ -741,7 +760,7 @@ describe('ユーザー', () => {
{ label: 'ローカル', parameters: { host: '.', limit: 1 }, user: (): User[] => [userFollowedByAlice] },
])('ã‚’ID&ãƒ›ã‚¹ãƒˆæŒ‡å®šã§æ¤œç´¢ã§ãã‚‹($label)', async ({ parameters, user }) => {
const response = await successfulApiCall({ endpoint: 'users/search-by-username-and-host', parameters, user: alice });
- const expected = await Promise.all(user().map(u => show(u.id)));
+ const expected = await Promise.all(user().map(u => show(u.id, alice)));
assert.deepStrictEqual(response, expected);
});
test.each([
@@ -757,7 +776,7 @@ describe('ユーザー', () => {
] as const)('ã‚’ID&ãƒ›ã‚¹ãƒˆæŒ‡å®šã§æ¤œç´¢ã§ãã€çµæžœã«$label', async ({ user, excluded }) => {
const parameters = { username: user().username };
const response = await successfulApiCall({ endpoint: 'users/search-by-username-and-host', parameters, user: alice });
- const expected = (excluded ?? false) ? [] : [await show(user().id)];
+ const expected = (excluded ?? false) ? [] : [await show(user().id, alice)];
assert.deepStrictEqual(response, expected);
});
test.todo('ã‚’ID&ãƒ›ã‚¹ãƒˆæŒ‡å®šã§æ¤œç´¢ã§ãã‚‹(リモート)');
@@ -769,7 +788,7 @@ describe('ユーザー', () => {
const parameters = { userId: alice.id, limit: 5 };
const response = await successfulApiCall({ endpoint: 'users/get-frequently-replied-users', parameters, user: alice });
const expected = await Promise.all(usersReplying.slice(0, parameters.limit).map(async (s, i) => ({
- user: await show(s.id),
+ user: await show(s.id, alice),
weight: (usersReplying.length - i) / usersReplying.length,
})));
assert.deepStrictEqual(response, expected);
@@ -781,7 +800,7 @@ describe('ユーザー', () => {
{ label: 'ブロックã—ã¦ãã¦ã„るユーザーãŒå«ã¾ã‚Œãªã„', user: (): User => userBlockingAlice, excluded: true },
{ label: '承èªåˆ¶ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒå«ã¾ã‚Œã‚‹', user: (): User => userLocking },
{ label: 'サイレンスユーザーãŒå«ã¾ã‚Œã‚‹', user: (): User => userSilenced },
- { label: 'サスペンドユーザーãŒå«ã¾ã‚Œãªã„', user: (): User => userSuspended },
+ //{ label: 'サスペンドユーザーãŒå«ã¾ã‚Œãªã„', user: (): User => userSuspended, excluded: true },
{ label: '削除済ユーザーãŒå«ã¾ã‚Œã‚‹', user: (): User => userDeletedBySelf },
{ label: '削除済(byAdmin)ユーザーãŒå«ã¾ã‚Œã‚‹', user: (): User => userDeletedByAdmin },
] as const)('ãŒã‚ˆãリプライをã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒªã‚¹ãƒˆã‚’å–å¾—ã§ãã€çµæžœã«$label', async ({ user, excluded }) => {
@@ -789,7 +808,7 @@ describe('ユーザー', () => {
await post(alice, { text: `@${user().username} test`, replyId: replyTo.id });
const parameters = { userId: alice.id, limit: 100 };
const response = await successfulApiCall({ endpoint: 'users/get-frequently-replied-users', parameters, user: alice });
- const expected = (excluded ?? false) ? [] : [await show(user().id)];
+ const expected = (excluded ?? false) ? [] : [await show(user().id, alice)];
assert.deepStrictEqual(response.map(s => s.user).filter((u) => u.id === user().id), expected);
});
@@ -808,7 +827,7 @@ describe('ユーザー', () => {
await successfulApiCall({ endpoint: 'i/update', parameters: { description: `#${hashtag}` }, user: alice });
const parameters = { tag: hashtag, limit: 5, ...sort };
const response = await successfulApiCall({ endpoint: 'hashtags/users', parameters, user: alice });
- const users = await Promise.all(response.map(u => show(u.id)));
+ const users = await Promise.all(response.map(u => show(u.id, alice)));
const expected = users.sort((x, y) => {
const index = (selector(x) < selector(y)) ? -1 : (selector(x) > selector(y)) ? 1 : 0;
return index * (parameters.sort.startsWith('+') ? -1 : 1);
@@ -822,10 +841,10 @@ describe('ユーザー', () => {
{ label: 'ブロックã—ã¦ãã¦ã„るユーザーãŒå«ã¾ã‚Œã‚‹', user: (): User => userBlockingAlice },
{ label: '承èªåˆ¶ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒå«ã¾ã‚Œã‚‹', user: (): User => userLocking },
{ label: 'サイレンスユーザーãŒå«ã¾ã‚Œã‚‹', user: (): User => userSilenced },
- { label: 'サスペンドユーザーãŒå«ã¾ã‚Œã‚‹', user: (): User => userSuspended },
+ { label: 'サスペンドユーザーãŒå«ã¾ã‚Œãªã„', user: (): User => userSuspended, excluded: true },
{ label: '削除済ユーザーãŒå«ã¾ã‚Œã‚‹', user: (): User => userDeletedBySelf },
{ label: '削除済(byAdmin)ユーザーãŒå«ã¾ã‚Œã‚‹', user: (): User => userDeletedByAdmin },
- ] as const)('ã‚’ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°æŒ‡å®šã§å–å¾—ã™ã‚‹ã“ã¨ãŒã§ãã€çµæžœã«$label', async ({ user }) => {
+ ] as const)('ã‚’ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°æŒ‡å®šã§å–å¾—ã™ã‚‹ã“ã¨ãŒã§ãã€çµæžœã«$label', async ({ user, excluded }) => {
const hashtag = `user_test${user().username}`;
if (user() !== userSuspended) {
// サスペンドユーザーã¯updateã§ããªã„。
@@ -833,7 +852,7 @@ describe('ユーザー', () => {
}
const parameters = { tag: hashtag, limit: 100, sort: '-follower' } as const;
const response = await successfulApiCall({ endpoint: 'hashtags/users', parameters, user: alice });
- const expected = [await show(user().id)];
+ const expected = (excluded ?? false) ? [] : [await show(user().id, alice)];
assert.deepStrictEqual(response, expected);
});
test.todo('ã‚’ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°æŒ‡å®šã§å–å¾—ã™ã‚‹ã“ã¨ãŒã§ãã‚‹(リモート)');
@@ -856,7 +875,7 @@ describe('ユーザー', () => {
await successfulApiCall({ endpoint: 'admin/update-meta', parameters: { pinnedUsers: [bob.username, `@${carol.username}`] }, user: root });
const parameters = {} as const;
const response = await successfulApiCall({ endpoint: 'pinned-users', parameters, user: alice });
- const expected = await Promise.all([bob, carol].map(u => show(u.id)));
+ const expected = await Promise.all([bob, carol].map(u => show(u.id, alice)));
assert.deepStrictEqual(response, expected);
});
diff --git a/packages/backend/test/resources/kick_gaba7.aac b/packages/backend/test/resources/kick_gaba7.aac
new file mode 100644
index 0000000000..4644542f96
--- /dev/null
+++ b/packages/backend/test/resources/kick_gaba7.aac
Binary files differ
diff --git a/packages/backend/test/resources/kick_gaba7.flac b/packages/backend/test/resources/kick_gaba7.flac
new file mode 100644
index 0000000000..7512812018
--- /dev/null
+++ b/packages/backend/test/resources/kick_gaba7.flac
Binary files differ
diff --git a/packages/backend/test/resources/kick_gaba7.mp3 b/packages/backend/test/resources/kick_gaba7.mp3
new file mode 100644
index 0000000000..6ba317deb1
--- /dev/null
+++ b/packages/backend/test/resources/kick_gaba7.mp3
Binary files differ
diff --git a/packages/backend/test/resources/kick_gaba7.wav b/packages/backend/test/resources/kick_gaba7.wav
new file mode 100644
index 0000000000..2cd280148e
--- /dev/null
+++ b/packages/backend/test/resources/kick_gaba7.wav
Binary files differ
diff --git a/packages/backend/test/resources/kick_gaba7.webm b/packages/backend/test/resources/kick_gaba7.webm
new file mode 100644
index 0000000000..82c5349cd4
--- /dev/null
+++ b/packages/backend/test/resources/kick_gaba7.webm
Binary files differ
diff --git a/packages/backend/test/unit/FileInfoService.ts b/packages/backend/test/unit/FileInfoService.ts
index d05833560d..f378184c74 100644
--- a/packages/backend/test/unit/FileInfoService.ts
+++ b/packages/backend/test/unit/FileInfoService.ts
@@ -7,10 +7,10 @@ import { ModuleMocker } from 'jest-mock';
import { Test } from '@nestjs/testing';
import { GlobalModule } from '@/GlobalModule.js';
import { FileInfoService } from '@/core/FileInfoService.js';
-import { DI } from '@/di-symbols.js';
+//import { DI } from '@/di-symbols.js';
import { AiService } from '@/core/AiService.js';
import type { TestingModule } from '@nestjs/testing';
-import type { jest } from '@jest/globals';
+import { describe, beforeAll, afterAll, test } from '@jest/globals';
import type { MockFunctionMetadata } from 'jest-mock';
const _filename = fileURLToPath(import.meta.url);
@@ -74,164 +74,271 @@ describe('FileInfoService', () => {
});
});
- test('Generic JPEG', async () => {
- const path = `${resources}/Lenna.jpg`;
- const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
- delete info.warnings;
- delete info.blurhash;
- delete info.sensitive;
- delete info.porn;
- assert.deepStrictEqual(info, {
- size: 25360,
- md5: '091b3f259662aa31e2ffef4519951168',
- type: {
- mime: 'image/jpeg',
- ext: 'jpg',
- },
- width: 512,
- height: 512,
- orientation: undefined,
+ describe('IMAGE', () => {
+ test('Generic JPEG', async () => {
+ const path = `${resources}/Lenna.jpg`;
+ const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
+ delete info.warnings;
+ delete info.blurhash;
+ delete info.sensitive;
+ delete info.porn;
+ assert.deepStrictEqual(info, {
+ size: 25360,
+ md5: '091b3f259662aa31e2ffef4519951168',
+ type: {
+ mime: 'image/jpeg',
+ ext: 'jpg',
+ },
+ width: 512,
+ height: 512,
+ orientation: undefined,
+ });
});
- });
-
- test('Generic APNG', async () => {
- const path = `${resources}/anime.png`;
- const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
- delete info.warnings;
- delete info.blurhash;
- delete info.sensitive;
- delete info.porn;
- assert.deepStrictEqual(info, {
- size: 1868,
- md5: '08189c607bea3b952704676bb3c979e0',
- type: {
- mime: 'image/apng',
- ext: 'apng',
- },
- width: 256,
- height: 256,
- orientation: undefined,
+
+ test('Generic APNG', async () => {
+ const path = `${resources}/anime.png`;
+ const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
+ delete info.warnings;
+ delete info.blurhash;
+ delete info.sensitive;
+ delete info.porn;
+ assert.deepStrictEqual(info, {
+ size: 1868,
+ md5: '08189c607bea3b952704676bb3c979e0',
+ type: {
+ mime: 'image/apng',
+ ext: 'apng',
+ },
+ width: 256,
+ height: 256,
+ orientation: undefined,
+ });
});
- });
-
- test('Generic AGIF', async () => {
- const path = `${resources}/anime.gif`;
- const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
- delete info.warnings;
- delete info.blurhash;
- delete info.sensitive;
- delete info.porn;
- assert.deepStrictEqual(info, {
- size: 2248,
- md5: '32c47a11555675d9267aee1a86571e7e',
- type: {
- mime: 'image/gif',
- ext: 'gif',
- },
- width: 256,
- height: 256,
- orientation: undefined,
+
+ test('Generic AGIF', async () => {
+ const path = `${resources}/anime.gif`;
+ const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
+ delete info.warnings;
+ delete info.blurhash;
+ delete info.sensitive;
+ delete info.porn;
+ assert.deepStrictEqual(info, {
+ size: 2248,
+ md5: '32c47a11555675d9267aee1a86571e7e',
+ type: {
+ mime: 'image/gif',
+ ext: 'gif',
+ },
+ width: 256,
+ height: 256,
+ orientation: undefined,
+ });
});
- });
-
- test('PNG with alpha', async () => {
- const path = `${resources}/with-alpha.png`;
- const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
- delete info.warnings;
- delete info.blurhash;
- delete info.sensitive;
- delete info.porn;
- assert.deepStrictEqual(info, {
- size: 3772,
- md5: 'f73535c3e1e27508885b69b10cf6e991',
- type: {
- mime: 'image/png',
- ext: 'png',
- },
- width: 256,
- height: 256,
- orientation: undefined,
+
+ test('PNG with alpha', async () => {
+ const path = `${resources}/with-alpha.png`;
+ const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
+ delete info.warnings;
+ delete info.blurhash;
+ delete info.sensitive;
+ delete info.porn;
+ assert.deepStrictEqual(info, {
+ size: 3772,
+ md5: 'f73535c3e1e27508885b69b10cf6e991',
+ type: {
+ mime: 'image/png',
+ ext: 'png',
+ },
+ width: 256,
+ height: 256,
+ orientation: undefined,
+ });
});
- });
-
- test('Generic SVG', async () => {
- const path = `${resources}/image.svg`;
- const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
- delete info.warnings;
- delete info.blurhash;
- delete info.sensitive;
- delete info.porn;
- assert.deepStrictEqual(info, {
- size: 505,
- md5: 'b6f52b4b021e7b92cdd04509c7267965',
- type: {
- mime: 'image/svg+xml',
- ext: 'svg',
- },
- width: 256,
- height: 256,
- orientation: undefined,
+
+ test('Generic SVG', async () => {
+ const path = `${resources}/image.svg`;
+ const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
+ delete info.warnings;
+ delete info.blurhash;
+ delete info.sensitive;
+ delete info.porn;
+ assert.deepStrictEqual(info, {
+ size: 505,
+ md5: 'b6f52b4b021e7b92cdd04509c7267965',
+ type: {
+ mime: 'image/svg+xml',
+ ext: 'svg',
+ },
+ width: 256,
+ height: 256,
+ orientation: undefined,
+ });
});
- });
-
- test('SVG with XML definition', async () => {
- // https://github.com/misskey-dev/misskey/issues/4413
- const path = `${resources}/with-xml-def.svg`;
- const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
- delete info.warnings;
- delete info.blurhash;
- delete info.sensitive;
- delete info.porn;
- assert.deepStrictEqual(info, {
- size: 544,
- md5: '4b7a346cde9ccbeb267e812567e33397',
- type: {
- mime: 'image/svg+xml',
- ext: 'svg',
- },
- width: 256,
- height: 256,
- orientation: undefined,
+
+ test('SVG with XML definition', async () => {
+ // https://github.com/misskey-dev/misskey/issues/4413
+ const path = `${resources}/with-xml-def.svg`;
+ const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
+ delete info.warnings;
+ delete info.blurhash;
+ delete info.sensitive;
+ delete info.porn;
+ assert.deepStrictEqual(info, {
+ size: 544,
+ md5: '4b7a346cde9ccbeb267e812567e33397',
+ type: {
+ mime: 'image/svg+xml',
+ ext: 'svg',
+ },
+ width: 256,
+ height: 256,
+ orientation: undefined,
+ });
});
- });
-
- test('Dimension limit', async () => {
- const path = `${resources}/25000x25000.png`;
- const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
- delete info.warnings;
- delete info.blurhash;
- delete info.sensitive;
- delete info.porn;
- assert.deepStrictEqual(info, {
- size: 75933,
- md5: '268c5dde99e17cf8fe09f1ab3f97df56',
- type: {
- mime: 'application/octet-stream', // do not treat as image
- ext: null,
- },
- width: 25000,
- height: 25000,
- orientation: undefined,
+
+ test('Dimension limit', async () => {
+ const path = `${resources}/25000x25000.png`;
+ const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
+ delete info.warnings;
+ delete info.blurhash;
+ delete info.sensitive;
+ delete info.porn;
+ assert.deepStrictEqual(info, {
+ size: 75933,
+ md5: '268c5dde99e17cf8fe09f1ab3f97df56',
+ type: {
+ mime: 'application/octet-stream', // do not treat as image
+ ext: null,
+ },
+ width: 25000,
+ height: 25000,
+ orientation: undefined,
+ });
+ });
+
+ test('Rotate JPEG', async () => {
+ const path = `${resources}/rotate.jpg`;
+ const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
+ delete info.warnings;
+ delete info.blurhash;
+ delete info.sensitive;
+ delete info.porn;
+ assert.deepStrictEqual(info, {
+ size: 12624,
+ md5: '68d5b2d8d1d1acbbce99203e3ec3857e',
+ type: {
+ mime: 'image/jpeg',
+ ext: 'jpg',
+ },
+ width: 512,
+ height: 256,
+ orientation: 8,
+ });
});
});
- test('Rotate JPEG', async () => {
- const path = `${resources}/rotate.jpg`;
- const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
- delete info.warnings;
- delete info.blurhash;
- delete info.sensitive;
- delete info.porn;
- assert.deepStrictEqual(info, {
- size: 12624,
- md5: '68d5b2d8d1d1acbbce99203e3ec3857e',
- type: {
- mime: 'image/jpeg',
- ext: 'jpg',
- },
- width: 512,
- height: 256,
- orientation: 8,
+ describe('AUDIO', () => {
+ test('MP3', async () => {
+ const path = `${resources}/kick_gaba7.mp3`;
+ const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
+ delete info.warnings;
+ delete info.blurhash;
+ delete info.sensitive;
+ delete info.porn;
+ delete info.width;
+ delete info.height;
+ delete info.orientation;
+ assert.deepStrictEqual(info, {
+ size: 19853,
+ md5: '4f557df8548bc3cecc794c652f690446',
+ type: {
+ mime: 'audio/mpeg',
+ ext: 'mp3',
+ },
+ });
+ });
+
+ test('WAV', async () => {
+ const path = `${resources}/kick_gaba7.wav`;
+ const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
+ delete info.warnings;
+ delete info.blurhash;
+ delete info.sensitive;
+ delete info.porn;
+ delete info.width;
+ delete info.height;
+ delete info.orientation;
+ assert.deepStrictEqual(info, {
+ size: 87630,
+ md5: '8bc9bb4fe5e77bb1871448209be635c1',
+ type: {
+ mime: 'audio/wav',
+ ext: 'wav',
+ },
+ });
+ });
+
+ test('AAC', async () => {
+ const path = `${resources}/kick_gaba7.aac`;
+ const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
+ delete info.warnings;
+ delete info.blurhash;
+ delete info.sensitive;
+ delete info.porn;
+ delete info.width;
+ delete info.height;
+ delete info.orientation;
+ assert.deepStrictEqual(info, {
+ size: 7291,
+ md5: '2789323f05e3392b648066f50be6a2a6',
+ type: {
+ mime: 'audio/aac',
+ ext: 'aac',
+ },
+ });
+ });
+
+ test('FLAC', async () => {
+ const path = `${resources}/kick_gaba7.flac`;
+ const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
+ delete info.warnings;
+ delete info.blurhash;
+ delete info.sensitive;
+ delete info.porn;
+ delete info.width;
+ delete info.height;
+ delete info.orientation;
+ assert.deepStrictEqual(info, {
+ size: 108793,
+ md5: 'bc0f3adfe0e1ca99ae6c7528c46b3173',
+ type: {
+ mime: 'audio/flac',
+ ext: 'flac',
+ },
+ });
+ });
+
+ /*
+ * video/webmã¨ã—ã¦æ¤œå‡ºã•れã¦ã—ã¾ã†
+ test('WEBM AUDIO', async () => {
+ const path = `${resources}/kick_gaba7.webm`;
+ const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
+ delete info.warnings;
+ delete info.blurhash;
+ delete info.sensitive;
+ delete info.porn;
+ delete info.width;
+ delete info.height;
+ delete info.orientation;
+ assert.deepStrictEqual(info, {
+ size: 8879,
+ md5: '3350083dec312419cfdc06c16413aca7',
+ type: {
+ mime: 'audio/webm',
+ ext: 'webm',
+ },
+ });
});
+ */
});
});
diff --git a/packages/backend/test/unit/RelayService.ts b/packages/backend/test/unit/RelayService.ts
index 529e923b2c..c2280142a6 100644
--- a/packages/backend/test/unit/RelayService.ts
+++ b/packages/backend/test/unit/RelayService.ts
@@ -7,6 +7,7 @@ import { GlobalModule } from '@/GlobalModule.js';
import { RelayService } from '@/core/RelayService.js';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { CreateSystemUserService } from '@/core/CreateSystemUserService.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { QueueService } from '@/core/QueueService.js';
import { IdService } from '@/core/IdService.js';
import type { RelaysRepository } from '@/models/index.js';
@@ -21,6 +22,7 @@ describe('RelayService', () => {
let relayService: RelayService;
let queueService: jest.Mocked<QueueService>;
let relaysRepository: RelaysRepository;
+ let userEntityService: UserEntityService;
beforeAll(async () => {
app = await Test.createTestingModule({
@@ -32,6 +34,7 @@ describe('RelayService', () => {
CreateSystemUserService,
ApRendererService,
RelayService,
+ UserEntityService,
],
})
.useMocker((token) => {
@@ -51,6 +54,7 @@ describe('RelayService', () => {
relayService = app.get<RelayService>(RelayService);
queueService = app.get<QueueService>(QueueService) as jest.Mocked<QueueService>;
relaysRepository = app.get<RelaysRepository>(DI.relaysRepository);
+ userEntityService = app.get<UserEntityService>(UserEntityService);
});
afterAll(async () => {
@@ -63,7 +67,7 @@ describe('RelayService', () => {
expect(result.inbox).toBe('https://example.com');
expect(result.status).toBe('requesting');
expect(queueService.deliver).toHaveBeenCalled();
- expect(queueService.deliver.mock.lastCall![1].type).toBe('Follow');
+ expect(queueService.deliver.mock.lastCall![1]?.type).toBe('Follow');
expect(queueService.deliver.mock.lastCall![2]).toBe('https://example.com');
//expect(queueService.deliver.mock.lastCall![0].username).toBe('relay.actor');
});
@@ -80,8 +84,8 @@ describe('RelayService', () => {
await relayService.removeRelay('https://example.com');
expect(queueService.deliver).toHaveBeenCalled();
- expect(queueService.deliver.mock.lastCall![1].type).toBe('Undo');
- expect(queueService.deliver.mock.lastCall![1].object.type).toBe('Follow');
+ expect(queueService.deliver.mock.lastCall![1]?.type).toBe('Undo');
+ expect(queueService.deliver.mock.lastCall![1]?.object.type).toBe('Follow');
expect(queueService.deliver.mock.lastCall![2]).toBe('https://example.com');
//expect(queueService.deliver.mock.lastCall![0].username).toBe('relay.actor');
diff --git a/packages/backend/test/unit/misc/check-word-mute.ts b/packages/backend/test/unit/misc/check-word-mute.ts
new file mode 100644
index 0000000000..7ab838bdee
--- /dev/null
+++ b/packages/backend/test/unit/misc/check-word-mute.ts
@@ -0,0 +1,49 @@
+import { checkWordMute } from '@/misc/check-word-mute.js';
+
+describe(checkWordMute, () => {
+ describe('Slacc boost mode', () => {
+ it('should return false if mutedWords is empty', async () => {
+ expect(await checkWordMute({ userId: '1', text: 'foo' }, null, [])).toBe(false);
+ });
+ it('should return true if mutedWords is not empty and text contains muted word', async () => {
+ expect(await checkWordMute({ userId: '1', text: 'foo' }, null, [['foo']])).toBe(true);
+ });
+ it('should return false if mutedWords is not empty and text does not contain muted word', async () => {
+ expect(await checkWordMute({ userId: '1', text: 'foo' }, null, [['bar']])).toBe(false);
+ });
+ it('should return false when the note is written by me even if mutedWords is not empty and text contains muted word', async () => {
+ expect(await checkWordMute({ userId: '1', text: 'foo' }, { id: '1' }, [['foo']])).toBe(false);
+ });
+ it('should return true if mutedWords is not empty and text contains muted word in CW', async () => {
+ expect(await checkWordMute({ userId: '1', text: 'foo', cw: 'bar' }, null, [['bar']])).toBe(true);
+ });
+ it('should return true if mutedWords is not empty and text contains muted word in both CW and text', async () => {
+ expect(await checkWordMute({ userId: '1', text: 'foo', cw: 'bar' }, null, [['foo'], ['bar']])).toBe(true);
+ });
+ it('should return true if mutedWords is not empty and text does not contain muted word in both CW and text', async () => {
+ expect(await checkWordMute({ userId: '1', text: 'foo', cw: 'bar' }, null, [['foo'], ['baz']])).toBe(true);
+ });
+ });
+ describe('normal mode', () => {
+ it('should return false if text does not contain muted words', async () => {
+ expect(await checkWordMute({ userId: '1', text: 'foo' }, null, [['foo', 'bar']])).toBe(false);
+ });
+ it('should return true if text contains muted words', async () => {
+ expect(await checkWordMute({ userId: '1', text: 'foobar' }, null, [['foo', 'bar']])).toBe(true);
+ });
+ it('should return false when the note is written by me even if text contains muted words', async () => {
+ expect(await checkWordMute({ userId: '1', text: 'foo bar' }, { id: '1' }, [['foo', 'bar']])).toBe(false);
+ });
+ });
+ describe('RegExp mode', () => {
+ it('should return false if text does not contain muted words', async () => {
+ expect(await checkWordMute({ userId: '1', text: 'foo' }, null, ['/bar/'])).toBe(false);
+ });
+ it('should return true if text contains muted words', async () => {
+ expect(await checkWordMute({ userId: '1', text: 'foobar' }, null, ['/bar/'])).toBe(true);
+ });
+ it('should return false when the note is written by me even if text contains muted words', async () => {
+ expect(await checkWordMute({ userId: '1', text: 'foo bar' }, { id: '1' }, ['/bar/'])).toBe(false);
+ });
+ });
+});
diff --git a/packages/frontend/.storybook/changes.ts b/packages/frontend/.storybook/changes.ts
index f0827331f7..755bec6869 100644
--- a/packages/frontend/.storybook/changes.ts
+++ b/packages/frontend/.storybook/changes.ts
@@ -38,6 +38,7 @@ fs.readFile(
path.resolve(__dirname, '../../..', arg)
)
)
+ .map((path) => path.replace(/(?:(?<=\.stories)\.(?:impl|meta)|\.msw)(?=\.ts$)/g, ''))
.map((path) => (path.startsWith('.') ? path : `./${path}`))
);
if (
diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx
index dd40bac2cc..7c51d4c00c 100644
--- a/packages/frontend/.storybook/generate.tsx
+++ b/packages/frontend/.storybook/generate.tsx
@@ -118,7 +118,7 @@ function toStories(component: string): string {
.replace(/[-.]|^(?=\d)/g, '_')
.replace(/(?<=^[^A-Z_]*$)/, '_')}
/> as estree.Identifier;
- const parameters = (
+ const parameters =
<object-expression
properties={[
<property
@@ -137,9 +137,8 @@ function toStories(component: string): string {
]
: []),
]}
- />
- ) as estree.ObjectExpression;
- const program = (
+ /> as estree.ObjectExpression;
+ const program =
<program
body={[
<import-declaration
@@ -379,11 +378,11 @@ function toStories(component: string): string {
declaration={(<identifier name='meta' />) as estree.Identifier}
/> as estree.ExportDefaultDeclaration,
]}
- />
- ) as estree.Program;
+ /> as estree.Program;
return format(
'/* eslint-disable @typescript-eslint/explicit-function-return-type */\n' +
'/* eslint-disable import/no-default-export */\n' +
+ '/* eslint-disable import/no-duplicates */\n' +
generate(program, { generator }) +
(hasImplStories ? readFileSync(`${implStories}.ts`, 'utf-8') : ''),
{
@@ -397,7 +396,11 @@ function toStories(component: string): string {
// glob('src/{components,pages,ui,widgets}/**/*.vue')
Promise.all([
glob('src/components/global/*.vue'),
+ glob('src/components/Mk{A,B}*.vue'),
glob('src/components/MkGalleryPostPreview.vue'),
+ glob('src/components/MkSignupServerRules.vue'),
+ glob('src/components/MkUserSetupDialog.vue'),
+ glob('src/components/MkUserSetupDialog.*.vue'),
glob('src/pages/user/home.vue'),
])
.then((globs) => globs.flat())
diff --git a/packages/frontend/.storybook/main.ts b/packages/frontend/.storybook/main.ts
index 45db48fa1d..1d0ce5ab63 100644
--- a/packages/frontend/.storybook/main.ts
+++ b/packages/frontend/.storybook/main.ts
@@ -1,6 +1,6 @@
import { resolve } from 'node:path';
import type { StorybookConfig } from '@storybook/vue3-vite';
-import { mergeConfig } from 'vite';
+import { type Plugin, mergeConfig } from 'vite';
import turbosnap from 'vite-plugin-turbosnap';
const config = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
@@ -22,6 +22,10 @@ const config = {
disableTelemetry: true,
},
async viteFinal(config) {
+ const replacePluginForIsChromatic = config.plugins?.findIndex((plugin) => plugin && (plugin as Partial<Plugin>)?.name === 'replace') ?? -1;
+ if (~replacePluginForIsChromatic) {
+ config.plugins?.splice(replacePluginForIsChromatic, 1);
+ }
return mergeConfig(config, {
plugins: [
turbosnap({
diff --git a/packages/frontend/.storybook/mocks.ts b/packages/frontend/.storybook/mocks.ts
index 41c3c5c4d9..4091e39686 100644
--- a/packages/frontend/.storybook/mocks.ts
+++ b/packages/frontend/.storybook/mocks.ts
@@ -8,6 +8,16 @@ export const onUnhandledRequest = ((req, print) => {
}) satisfies SharedOptions['onUnhandledRequest'];
export const commonHandlers = [
+ rest.get('/fluent-emoji/:codepoints.png', async (req, res, ctx) => {
+ const { codepoints } = req.params;
+ const value = await fetch(`https://raw.githubusercontent.com/misskey-dev/emojis/main/dist/${codepoints}.png`).then((response) => response.blob());
+ return res(ctx.set('Content-Type', 'image/png'), ctx.body(value));
+ }),
+ rest.get('/fluent-emojis/:codepoints.png', async (req, res, ctx) => {
+ const { codepoints } = req.params;
+ const value = await fetch(`https://raw.githubusercontent.com/misskey-dev/emojis/main/dist/${codepoints}.png`).then((response) => response.blob());
+ return res(ctx.set('Content-Type', 'image/png'), ctx.body(value));
+ }),
rest.get('/twemoji/:codepoints.svg', async (req, res, ctx) => {
const { codepoints } = req.params;
const value = await fetch(`https://unpkg.com/@discordapp/twemoji@14.1.2/dist/svg/${codepoints}.svg`).then((response) => response.blob());
diff --git a/packages/frontend/.storybook/preview-head.html b/packages/frontend/.storybook/preview-head.html
index 64e537b931..ab694f64fb 100644
--- a/packages/frontend/.storybook/preview-head.html
+++ b/packages/frontend/.storybook/preview-head.html
@@ -1,3 +1,5 @@
+<link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true" as="image" type="image/png" crossorigin="anonymous">
+<link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true" as="image" type="image/jpeg" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@2.12.0/tabler-icons.min.css">
<link rel="stylesheet" href="https://unpkg.com/@fontsource/m-plus-rounded-1c/index.css">
<style>
diff --git a/packages/frontend/.storybook/preview.ts b/packages/frontend/.storybook/preview.ts
index b2974276ab..e887acaa2e 100644
--- a/packages/frontend/.storybook/preview.ts
+++ b/packages/frontend/.storybook/preview.ts
@@ -3,6 +3,7 @@ import { FORCE_REMOUNT } from '@storybook/core-events';
import { type Preview, setup } from '@storybook/vue3';
import isChromatic from 'chromatic/isChromatic';
import { initialize, mswDecorator } from 'msw-storybook-addon';
+import { userDetailed } from './fakes';
import locale from './locale';
import { commonHandlers, onUnhandledRequest } from './mocks';
import themes from './themes';
@@ -10,6 +11,7 @@ import '../src/style.scss';
const appInitialized = Symbol();
+let lastStory = null;
let moduleInitialized = false;
let unobserve = () => {};
let misskeyOS = null;
@@ -19,7 +21,7 @@ function loadTheme(applyTheme: typeof import('../src/scripts/theme')['applyTheme
const theme = themes[document.documentElement.dataset.misskeyTheme];
if (theme) {
applyTheme(themes[document.documentElement.dataset.misskeyTheme]);
- } else if (isChromatic()) {
+ } else {
applyTheme(themes['l-light']);
}
const observer = new MutationObserver((entries) => {
@@ -42,10 +44,19 @@ function loadTheme(applyTheme: typeof import('../src/scripts/theme')['applyTheme
unobserve = () => observer.disconnect();
}
+function initLocalStorage() {
+ localStorage.clear();
+ localStorage.setItem('account', JSON.stringify({
+ ...userDetailed(),
+ policies: {},
+ }));
+ localStorage.setItem('locale', JSON.stringify(locale));
+}
+
initialize({
onUnhandledRequest,
});
-localStorage.setItem("locale", JSON.stringify(locale));
+initLocalStorage();
queueMicrotask(() => {
Promise.all([
import('../src/components'),
@@ -76,6 +87,27 @@ queueMicrotask(() => {
const preview = {
decorators: [
(Story, context) => {
+ if (lastStory === context.id) {
+ lastStory = null;
+ } else {
+ lastStory = context.id;
+ const channel = addons.getChannel();
+ const resetIndexedDBPromise = globalThis.indexedDB?.databases
+ ? indexedDB.databases().then((r) => {
+ for (var i = 0; i < r.length; i++) {
+ indexedDB.deleteDatabase(r[i].name!);
+ }
+ }).catch(() => {})
+ : Promise.resolve();
+ const resetDefaultStorePromise = import('../src/store').then(({ defaultStore }) => {
+ // @ts-expect-error
+ defaultStore.init();
+ }).catch(() => {});
+ Promise.all([resetIndexedDBPromise, resetDefaultStorePromise]).then(() => {
+ initLocalStorage();
+ channel.emit(FORCE_REMOUNT, { storyId: context.id });
+ });
+ }
const story = Story();
if (!moduleInitialized) {
const channel = addons.getChannel();
diff --git a/packages/frontend/.vscode/storybook.code-snippets b/packages/frontend/.vscode/storybook.code-snippets
new file mode 100644
index 0000000000..785d0a1608
--- /dev/null
+++ b/packages/frontend/.vscode/storybook.code-snippets
@@ -0,0 +1,84 @@
+{
+ "Storybook Story Impl File": {
+ "scope": "typescript",
+ "prefix": "storyimpl",
+ "body": [
+ "/* eslint-disable @typescript-eslint/explicit-function-return-type */",
+ "import { StoryObj } from '@storybook/vue3';",
+ "import $1 from './$1.vue';",
+ "export const Default = {",
+ "\trender(args) {",
+ "\t\treturn {",
+ "\t\t\tcomponents: {",
+ "\t\t\t\t$1,",
+ "\t\t\t},",
+ "\t\t\tsetup() {",
+ "\t\t\t\treturn {",
+ "\t\t\t\t\targs,",
+ "\t\t\t\t};",
+ "\t\t\t},",
+ "\t\t\tcomputed: {",
+ "\t\t\t\tprops() {",
+ "\t\t\t\t\treturn {",
+ "\t\t\t\t\t\t...this.args,",
+ "\t\t\t\t\t};",
+ "\t\t\t\t},",
+ "\t\t\t},",
+ "\t\t\ttemplate: '<$1 v-bind=\"props\" />',",
+ "\t\t};",
+ "\t},",
+ "\targs: {",
+ "\t\t$2",
+ "\t},",
+ "\tparameters: {",
+ "\t\tlayout: 'centered',",
+ "\t},",
+ "} satisfies StoryObj<typeof $1>;",
+ ""
+ ]
+ },
+ "Storybook Story Impl File (w/ events)": {
+ "scope": "typescript",
+ "prefix": "storyimplevent",
+ "body": [
+ "/* eslint-disable @typescript-eslint/explicit-function-return-type */",
+ "import { action } from '@storybook/addon-actions';",
+ "import { StoryObj } from '@storybook/vue3';",
+ "import $1 from './$1.vue';",
+ "export const Default = {",
+ "\trender(args) {",
+ "\t\treturn {",
+ "\t\t\tcomponents: {",
+ "\t\t\t\t$1,",
+ "\t\t\t},",
+ "\t\t\tsetup() {",
+ "\t\t\t\treturn {",
+ "\t\t\t\t\targs,",
+ "\t\t\t\t};",
+ "\t\t\t},",
+ "\t\t\tcomputed: {",
+ "\t\t\t\tprops() {",
+ "\t\t\t\t\treturn {",
+ "\t\t\t\t\t\t...this.args,",
+ "\t\t\t\t\t};",
+ "\t\t\t\t},",
+ "\t\t\t\tevents() {",
+ "\t\t\t\t\treturn {",
+ "\t\t\t\t\t\t$3",
+ "\t\t\t\t\t};",
+ "\t\t\t\t},",
+ "\t\t\t},",
+ "\t\t\ttemplate: '<$1 v-bind=\"props\" v-on=\"events\" />',",
+ "\t\t};",
+ "\t},",
+ "\targs: {",
+ "\t\t$2",
+ "\t},",
+ "\tparameters: {",
+ "\t\tlayout: 'centered',",
+ "\t},",
+ "} satisfies StoryObj<typeof $1>;",
+ ""
+ ]
+ }
+}
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 79fb626a9a..7646e152f9 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -15,28 +15,31 @@
},
"dependencies": {
"@discordapp/twemoji": "14.1.2",
- "@rollup/plugin-alias": "4.0.3",
+ "@rollup/plugin-alias": "5.0.0",
"@rollup/plugin-json": "6.0.0",
+ "@rollup/plugin-replace": "^5.0.2",
"@rollup/pluginutils": "5.0.2",
- "@syuilo/aiscript": "0.13.1",
- "@tabler/icons-webfont": "2.12.0",
- "@vitejs/plugin-vue": "4.1.0",
+ "@syuilo/aiscript": "0.13.2",
+ "@tabler/icons-webfont": "2.17.0",
+ "@vitejs/plugin-vue": "4.2.1",
+ "@vue-macros/reactivity-transform": "^0.3.5",
"@vue/compiler-sfc": "3.2.47",
"autosize": "5.0.2",
"blurhash": "2.0.5",
"broadcast-channel": "4.20.2",
"browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3",
"canvas-confetti": "1.6.0",
- "chart.js": "4.2.1",
+ "chart.js": "4.3.0",
"chartjs-adapter-date-fns": "3.0.0",
"chartjs-chart-matrix": "2.0.1",
"chartjs-plugin-gradient": "0.6.1",
"chartjs-plugin-zoom": "2.0.1",
+ "chromatic": "6.17.3",
"compare-versions": "5.0.1",
"cropperjs": "2.0.0-beta.2",
- "date-fns": "2.29.3",
+ "date-fns": "2.30.0",
"escape-regexp": "0.0.1",
- "eventemitter3": "5.0.0",
+ "eventemitter3": "5.0.1",
"gsap": "3.11.5",
"idb-keyval": "6.2.0",
"insert-text-at-cursor": "0.3.0",
@@ -50,10 +53,10 @@
"punycode": "2.3.0",
"querystring": "0.2.1",
"rndstr": "1.0.0",
- "rollup": "3.20.2",
+ "rollup": "3.21.3",
"s-age": "1.1.2",
"sanitize-html": "2.10.0",
- "sass": "1.60.0",
+ "sass": "1.62.1",
"seedrandom": "3.0.5",
"strict-event-emitter-types": "2.0.0",
"syuilo-password-strength": "0.0.1",
@@ -61,45 +64,46 @@
"three": "0.151.3",
"throttle-debounce": "5.0.0",
"tinycolor2": "1.6.0",
- "tsc-alias": "1.8.5",
+ "tsc-alias": "1.8.6",
"tsconfig-paths": "4.2.0",
"twemoji-parser": "14.0.0",
- "typescript": "5.0.3",
+ "typescript": "5.0.4",
"uuid": "9.0.0",
"vanilla-tilt": "1.8.0",
- "vite": "4.2.1",
+ "vite": "4.3.4",
"vue": "3.2.47",
"vue-plyr": "7.0.0",
"vue-prism-editor": "2.0.0-alpha.2",
"vuedraggable": "next"
},
"devDependencies": {
- "@storybook/addon-essentials": "7.0.2",
- "@storybook/addon-interactions": "7.0.2",
- "@storybook/addon-links": "7.0.2",
- "@storybook/addon-storysource": "7.0.2",
- "@storybook/addons": "7.0.2",
- "@storybook/blocks": "7.0.2",
- "@storybook/core-events": "7.0.2",
+ "@storybook/addon-actions": "7.0.7",
+ "@storybook/addon-essentials": "7.0.7",
+ "@storybook/addon-interactions": "7.0.7",
+ "@storybook/addon-links": "7.0.7",
+ "@storybook/addon-storysource": "7.0.7",
+ "@storybook/addons": "7.0.7",
+ "@storybook/blocks": "7.0.7",
+ "@storybook/core-events": "7.0.7",
"@storybook/jest": "0.1.0",
- "@storybook/manager-api": "7.0.2",
- "@storybook/preview-api": "7.0.2",
- "@storybook/react": "7.0.2",
- "@storybook/react-vite": "7.0.2",
- "@storybook/testing-library": "0.0.14-next.1",
- "@storybook/theming": "7.0.2",
- "@storybook/types": "7.0.2",
- "@storybook/vue3": "7.0.2",
- "@storybook/vue3-vite": "7.0.2",
+ "@storybook/manager-api": "7.0.7",
+ "@storybook/preview-api": "7.0.7",
+ "@storybook/react": "7.0.7",
+ "@storybook/react-vite": "7.0.7",
+ "@storybook/testing-library": "0.1.0",
+ "@storybook/theming": "7.0.7",
+ "@storybook/types": "7.0.7",
+ "@storybook/vue3": "7.0.7",
+ "@storybook/vue3-vite": "7.0.7",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/vue": "7.0.0",
"@types/escape-regexp": "0.0.1",
- "@types/estree": "1.0.0",
+ "@types/estree": "1.0.1",
"@types/gulp": "4.0.10",
"@types/gulp-rename": "2.0.1",
"@types/matter-js": "0.18.2",
- "@types/micromatch": "3.1.1",
- "@types/node": "18.15.11",
+ "@types/micromatch": "4.0.2",
+ "@types/node": "18.16.3",
"@types/punycode": "2.1.0",
"@types/sanitize-html": "2.9.0",
"@types/seedrandom": "3.0.5",
@@ -109,34 +113,33 @@
"@types/uuid": "9.0.1",
"@types/websocket": "1.0.5",
"@types/ws": "8.5.4",
- "@typescript-eslint/eslint-plugin": "5.57.1",
- "@typescript-eslint/parser": "5.57.1",
- "@vitest/coverage-c8": "^0.29.8",
+ "@typescript-eslint/eslint-plugin": "5.59.2",
+ "@typescript-eslint/parser": "5.59.2",
+ "@vitest/coverage-c8": "0.30.1",
"@vue/runtime-core": "3.2.47",
"astring": "1.8.4",
"chokidar-cli": "3.0.0",
- "chromatic": "6.17.3",
"cross-env": "7.0.3",
- "cypress": "12.9.0",
- "eslint": "8.37.0",
+ "cypress": "12.11.0",
+ "eslint": "8.39.0",
"eslint-plugin-import": "2.27.5",
- "eslint-plugin-vue": "9.10.0",
+ "eslint-plugin-vue": "9.11.0",
"fast-glob": "3.2.12",
- "happy-dom": "8.9.0",
+ "happy-dom": "9.10.2",
"micromatch": "3.1.10",
"msw": "1.2.1",
"msw-storybook-addon": "1.8.0",
- "prettier": "2.8.7",
+ "prettier": "2.8.8",
"react": "18.2.0",
"react-dom": "18.2.0",
"start-server-and-test": "2.0.0",
- "storybook": "7.0.2",
+ "storybook": "7.0.7",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"summaly": "github:misskey-dev/summaly",
- "vite-plugin-turbosnap": "^1.0.1",
- "vitest": "0.29.8",
+ "vite-plugin-turbosnap": "1.0.2",
+ "vitest": "0.30.1",
"vitest-fetch-mock": "0.2.2",
"vue-eslint-parser": "9.1.1",
- "vue-tsc": "1.2.0"
+ "vue-tsc": "1.6.3"
}
}
diff --git a/packages/frontend/src/components/MkAbuseReport.stories.impl.ts b/packages/frontend/src/components/MkAbuseReport.stories.impl.ts
new file mode 100644
index 0000000000..7d27adeb04
--- /dev/null
+++ b/packages/frontend/src/components/MkAbuseReport.stories.impl.ts
@@ -0,0 +1,49 @@
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { action } from '@storybook/addon-actions';
+import { StoryObj } from '@storybook/vue3';
+import { rest } from 'msw';
+import { abuseUserReport } from '../../.storybook/fakes';
+import { commonHandlers } from '../../.storybook/mocks';
+import MkAbuseReport from './MkAbuseReport.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkAbuseReport,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ events() {
+ return {
+ resolved: action('resolved'),
+ };
+ },
+ },
+ template: '<MkAbuseReport v-bind="props" v-on="events" />',
+ };
+ },
+ args: {
+ report: abuseUserReport(),
+ },
+ parameters: {
+ layout: 'fullscreen',
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ rest.post('/api/admin/resolve-abuse-user-report', async (req, res, ctx) => {
+ action('POST /api/admin/resolve-abuse-user-report')(await req.json());
+ return res(ctx.json({}));
+ }),
+ ],
+ },
+ },
+} satisfies StoryObj<typeof MkAbuseReport>;
diff --git a/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts b/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts
new file mode 100644
index 0000000000..d0877ffd3b
--- /dev/null
+++ b/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts
@@ -0,0 +1,49 @@
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { action } from '@storybook/addon-actions';
+import { StoryObj } from '@storybook/vue3';
+import { rest } from 'msw';
+import { userDetailed } from '../../.storybook/fakes';
+import { commonHandlers } from '../../.storybook/mocks';
+import MkAbuseReportWindow from './MkAbuseReportWindow.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkAbuseReportWindow,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ events() {
+ return {
+ 'closed': action('closed'),
+ };
+ },
+ },
+ template: '<MkAbuseReportWindow v-bind="props" v-on="events" />',
+ };
+ },
+ args: {
+ user: userDetailed(),
+ },
+ parameters: {
+ layout: 'centered',
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ rest.post('/api/users/report-abuse', async (req, res, ctx) => {
+ action('POST /api/users/report-abuse')(await req.json());
+ return res(ctx.json({}));
+ }),
+ ],
+ },
+ },
+} satisfies StoryObj<typeof MkAbuseReportWindow>;
diff --git a/packages/frontend/src/components/MkAccountMoved.stories.impl.ts b/packages/frontend/src/components/MkAccountMoved.stories.impl.ts
new file mode 100644
index 0000000000..bed9d94311
--- /dev/null
+++ b/packages/frontend/src/components/MkAccountMoved.stories.impl.ts
@@ -0,0 +1,33 @@
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { StoryObj } from '@storybook/vue3';
+import { userDetailed } from '../../.storybook/fakes';
+import MkAccountMoved from './MkAccountMoved.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkAccountMoved,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkAccountMoved v-bind="props" />',
+ };
+ },
+ args: {
+ username: userDetailed().username,
+ host: userDetailed().host,
+ },
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkAccountMoved>;
diff --git a/packages/frontend/src/components/MkAccountMoved.vue b/packages/frontend/src/components/MkAccountMoved.vue
index fd472de6c1..b02bfdc2b8 100644
--- a/packages/frontend/src/components/MkAccountMoved.vue
+++ b/packages/frontend/src/components/MkAccountMoved.vue
@@ -1,8 +1,8 @@
<template>
-<div :class="$style.root">
+<div v-if="user" :class="$style.root">
<i class="ti ti-plane-departure" style="margin-right: 8px;"></i>
{{ i18n.ts.accountMoved }}
- <MkMention :class="$style.link" :username="acct" :host="host ?? localHost"/>
+ <MkMention :class="$style.link" :username="user.username" :host="user.host ?? localHost"/>
</div>
</template>
@@ -10,11 +10,17 @@
import MkMention from './MkMention.vue';
import { i18n } from '@/i18n';
import { host as localHost } from '@/config';
+import { ref } from 'vue';
+import { UserLite } from 'misskey-js/built/entities';
+import { api } from '@/os';
-defineProps<{
- acct: string;
- host: string;
+const user = ref<UserLite>();
+
+const props = defineProps<{
+ movedTo: string; // user id
}>();
+
+api('users/show', { userId: props.movedTo }).then(u => user.value = u);
</script>
<style lang="scss" module>
diff --git a/packages/frontend/src/components/MkAchievements.stories.impl.ts b/packages/frontend/src/components/MkAchievements.stories.impl.ts
new file mode 100644
index 0000000000..477152a47b
--- /dev/null
+++ b/packages/frontend/src/components/MkAchievements.stories.impl.ts
@@ -0,0 +1,56 @@
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { StoryObj } from '@storybook/vue3';
+import { rest } from 'msw';
+import { userDetailed } from '../../.storybook/fakes';
+import { commonHandlers } from '../../.storybook/mocks';
+import MkAchievements from './MkAchievements.vue';
+import { ACHIEVEMENT_TYPES } from '@/scripts/achievements';
+export const Empty = {
+ render(args) {
+ return {
+ components: {
+ MkAchievements,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkAchievements v-bind="props" />',
+ };
+ },
+ args: {
+ user: userDetailed(),
+ },
+ parameters: {
+ layout: 'fullscreen',
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ rest.post('/api/users/achievements', (req, res, ctx) => {
+ return res(ctx.json([]));
+ }),
+ ],
+ },
+ },
+} satisfies StoryObj<typeof MkAchievements>;
+export const All = {
+ ...Empty,
+ parameters: {
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ rest.post('/api/users/achievements', (req, res, ctx) => {
+ return res(ctx.json(ACHIEVEMENT_TYPES.map((name) => ({ name, unlockedAt: 0 }))));
+ }),
+ ],
+ },
+ },
+} satisfies StoryObj<typeof MkAchievements>;
diff --git a/packages/frontend/src/components/MkAnalogClock.stories.impl.ts b/packages/frontend/src/components/MkAnalogClock.stories.impl.ts
index 05190aa268..e7fbb47284 100644
--- a/packages/frontend/src/components/MkAnalogClock.stories.impl.ts
+++ b/packages/frontend/src/components/MkAnalogClock.stories.impl.ts
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
import MkAnalogClock from './MkAnalogClock.vue';
+import isChromatic from 'chromatic';
export const Default = {
render(args) {
return {
@@ -22,6 +23,14 @@ export const Default = {
template: '<MkAnalogClock v-bind="props" />',
};
},
+ args: {
+ now: isChromatic() ? () => new Date('2023-01-01T10:10:30') : undefined,
+ },
+ decorators: [
+ () => ({
+ template: '<div style="container-type:inline-size;height:100%"><div style="height:100cqmin;margin:auto;width:100cqmin"><story/></div></div>',
+ }),
+ ],
parameters: {
layout: 'fullscreen',
},
diff --git a/packages/frontend/src/components/MkAnalogClock.vue b/packages/frontend/src/components/MkAnalogClock.vue
index 1218202616..f12020f810 100644
--- a/packages/frontend/src/components/MkAnalogClock.vue
+++ b/packages/frontend/src/components/MkAnalogClock.vue
@@ -99,6 +99,7 @@ const props = withDefaults(defineProps<{
graduations?: 'none' | 'dots' | 'numbers';
fadeGraduations?: boolean;
sAnimation?: 'none' | 'elastic' | 'easeOut';
+ now?: () => Date;
}>(), {
numbers: false,
thickness: 0.1,
@@ -107,6 +108,7 @@ const props = withDefaults(defineProps<{
graduations: 'dots',
fadeGraduations: true,
sAnimation: 'elastic',
+ now: () => new Date(),
});
const graduationsMajor = computed(() => {
@@ -145,11 +147,17 @@ let disableSAnimate = $ref(false);
let sOneRound = false;
function tick() {
- const now = new Date();
- now.setMinutes(now.getMinutes() + (new Date().getTimezoneOffset() + props.offset));
+ const now = props.now();
+ now.setMinutes(now.getMinutes() + now.getTimezoneOffset() + props.offset);
+ const previousS = s;
+ const previousM = m;
+ const previousH = h;
s = now.getSeconds();
m = now.getMinutes();
h = now.getHours();
+ if (previousS === s && previousM === m && previousH === h) {
+ return;
+ }
hAngle = Math.PI * (h % (props.twentyfour ? 24 : 12) + (m + s / 60) / 60) / (props.twentyfour ? 12 : 6);
mAngle = Math.PI * (m + s / 60) / 30;
if (sOneRound) { // ç§’é‡ãŒä¸€å‘¨ã—ãŸéš›ã®ã‚¢ãƒ‹ãƒ¡ãƒ¼ã‚·ãƒ§ãƒ³ã‚’よã—ãªã«å‡¦ç†ã™ã‚‹(ã“れãŒç„¡ã„ã¨ç§’ãŒ59->0ã«ãªã£ãŸã¨ãã«æœŸå¾…ã—ãŸã‚¢ãƒ‹ãƒ¡ãƒ¼ã‚·ãƒ§ãƒ³ã«ãªã‚‰ãªã„)
diff --git a/packages/frontend/src/components/MkAsUi.stories.impl.ts b/packages/frontend/src/components/MkAsUi.stories.impl.ts
new file mode 100644
index 0000000000..b67c0e679d
--- /dev/null
+++ b/packages/frontend/src/components/MkAsUi.stories.impl.ts
@@ -0,0 +1,2 @@
+import MkAsUi from './MkAsUi.vue';
+void MkAsUi;
diff --git a/packages/frontend/src/components/MkAutocomplete.stories.impl.ts b/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
new file mode 100644
index 0000000000..075904d6a3
--- /dev/null
+++ b/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
@@ -0,0 +1,176 @@
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { action } from '@storybook/addon-actions';
+import { expect } from '@storybook/jest';
+import { userEvent, waitFor, within } from '@storybook/testing-library';
+import { StoryObj } from '@storybook/vue3';
+import { rest } from 'msw';
+import { userDetailed } from '../../.storybook/fakes';
+import { commonHandlers } from '../../.storybook/mocks';
+import MkAutocomplete from './MkAutocomplete.vue';
+import MkInput from './MkInput.vue';
+import { tick } from '@/scripts/test-utils';
+const common = {
+ render(args) {
+ return {
+ components: {
+ MkAutocomplete,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ events() {
+ return {
+ open: action('open'),
+ closed: action('closed'),
+ };
+ },
+ },
+ template: '<MkAutocomplete v-bind="props" v-on="events" :textarea="textarea" />',
+ };
+ },
+ args: {
+ close: action('close'),
+ x: 0,
+ y: 0,
+ },
+ decorators: [
+ (_, context) => ({
+ components: {
+ MkInput,
+ },
+ data() {
+ return {
+ q: context.args.q,
+ textarea: null,
+ };
+ },
+ methods: {
+ inputMounted() {
+ this.textarea = this.$refs.input.$refs.inputEl;
+ },
+ },
+ template: '<MkInput v-model="q" ref="input" @vue:mounted="inputMounted"/><story v-if="textarea" :q="q" :textarea="textarea"/>',
+ }),
+ ],
+ parameters: {
+ controls: {
+ exclude: ['textarea'],
+ },
+ layout: 'centered',
+ chromatic: {
+ // FIXME: flaky
+ disableSnapshot: true,
+ },
+ },
+} satisfies StoryObj<typeof MkAutocomplete>;
+export const User = {
+ ...common,
+ args: {
+ ...common.args,
+ type: 'user',
+ },
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+ const input = canvas.getByRole('combobox');
+ await waitFor(() => userEvent.hover(input));
+ await waitFor(() => userEvent.click(input));
+ await waitFor(() => userEvent.type(input, 'm'));
+ await waitFor(async () => {
+ await userEvent.type(input, ' ', { delay: 256 });
+ await tick();
+ return await expect(canvas.getByRole('list')).toBeInTheDocument();
+ }, { timeout: 16384 });
+ },
+ parameters: {
+ ...common.parameters,
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ rest.post('/api/users/search-by-username-and-host', (req, res, ctx) => {
+ return res(ctx.json([
+ userDetailed('44', 'mizuki', 'misskey-hub.net', 'Mizuki'),
+ userDetailed('49', 'momoko', 'misskey-hub.net', 'Momoko'),
+ ]));
+ }),
+ ],
+ },
+ },
+};
+export const Hashtag = {
+ ...common,
+ args: {
+ ...common.args,
+ type: 'hashtag',
+ },
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+ const input = canvas.getByRole('combobox');
+ await waitFor(() => userEvent.hover(input));
+ await waitFor(() => userEvent.click(input));
+ await waitFor(() => userEvent.type(input, '気象'));
+ await waitFor(async () => {
+ await userEvent.type(input, ' ', { delay: 256 });
+ await tick();
+ return await expect(canvas.getByRole('list')).toBeInTheDocument();
+ }, { interval: 256, timeout: 16384 });
+ },
+ parameters: {
+ ...common.parameters,
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ rest.post('/api/hashtags/search', (req, res, ctx) => {
+ return res(ctx.json([
+ '気象警報注æ„å ±',
+ '気象警報',
+ '気象情報',
+ ]));
+ }),
+ ],
+ },
+ },
+};
+export const Emoji = {
+ ...common,
+ args: {
+ ...common.args,
+ type: 'emoji',
+ },
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+ const input = canvas.getByRole('combobox');
+ await waitFor(() => userEvent.hover(input));
+ await waitFor(() => userEvent.click(input));
+ await waitFor(() => userEvent.type(input, 'smile'));
+ await waitFor(async () => {
+ await userEvent.type(input, ' ', { delay: 256 });
+ await tick();
+ return await expect(canvas.getByRole('list')).toBeInTheDocument();
+ }, { interval: 256, timeout: 16384 });
+ },
+} satisfies StoryObj<typeof MkAutocomplete>;
+export const MfmTag = {
+ ...common,
+ args: {
+ ...common.args,
+ type: 'mfmTag',
+ },
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+ const input = canvas.getByRole('combobox');
+ await waitFor(() => userEvent.hover(input));
+ await waitFor(() => userEvent.click(input));
+ await waitFor(async () => {
+ await tick();
+ return await expect(canvas.getByRole('list')).toBeInTheDocument();
+ }, { interval: 256, timeout: 16384 });
+ },
+} satisfies StoryObj<typeof MkAutocomplete>;
diff --git a/packages/frontend/src/components/MkAvatars.stories.impl.ts b/packages/frontend/src/components/MkAvatars.stories.impl.ts
new file mode 100644
index 0000000000..14052c7343
--- /dev/null
+++ b/packages/frontend/src/components/MkAvatars.stories.impl.ts
@@ -0,0 +1,46 @@
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { StoryObj } from '@storybook/vue3';
+import { rest } from 'msw';
+import { userDetailed } from '../../.storybook/fakes';
+import { commonHandlers } from '../../.storybook/mocks';
+import MkAvatars from './MkAvatars.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkAvatars,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkAvatars v-bind="props" />',
+ };
+ },
+ args: {
+ userIds: ['17', '20', '18'],
+ },
+ parameters: {
+ layout: 'centered',
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ rest.post('/api/users/show', (req, res, ctx) => {
+ return res(ctx.json([
+ userDetailed('17'),
+ userDetailed('20'),
+ userDetailed('18'),
+ ]));
+ }),
+ ],
+ },
+ },
+} satisfies StoryObj<typeof MkAvatars>;
diff --git a/packages/frontend/src/components/MkButton.stories.impl.ts b/packages/frontend/src/components/MkButton.stories.impl.ts
index e1c1c54d10..982a8b3be1 100644
--- a/packages/frontend/src/components/MkButton.stories.impl.ts
+++ b/packages/frontend/src/components/MkButton.stories.impl.ts
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable import/no-default-export */
-/* eslint-disable import/no-duplicates */
+import { action } from '@storybook/addon-actions';
import { StoryObj } from '@storybook/vue3';
import MkButton from './MkButton.vue';
export const Default = {
@@ -20,11 +20,60 @@ export const Default = {
...this.args,
};
},
+ events() {
+ return {
+ click: action('click'),
+ };
+ },
},
- template: '<MkButton v-bind="props">Text</MkButton>',
+ template: '<MkButton v-bind="props" v-on="events">Text</MkButton>',
};
},
+ args: {
+ },
parameters: {
layout: 'centered',
},
} satisfies StoryObj<typeof MkButton>;
+export const Primary = {
+ ...Default,
+ args: {
+ ...Default.args,
+ primary: true,
+ },
+} satisfies StoryObj<typeof MkButton>;
+export const Gradate = {
+ ...Default,
+ args: {
+ ...Default.args,
+ gradate: true,
+ },
+} satisfies StoryObj<typeof MkButton>;
+export const Rounded = {
+ ...Default,
+ args: {
+ ...Default.args,
+ rounded: true,
+ },
+} satisfies StoryObj<typeof MkButton>;
+export const Danger = {
+ ...Default,
+ args: {
+ ...Default.args,
+ danger: true,
+ },
+} satisfies StoryObj<typeof MkButton>;
+export const Small = {
+ ...Default,
+ args: {
+ ...Default.args,
+ small: true,
+ },
+} satisfies StoryObj<typeof MkButton>;
+export const Large = {
+ ...Default,
+ args: {
+ ...Default.args,
+ large: true,
+ },
+} satisfies StoryObj<typeof MkButton>;
diff --git a/packages/frontend/src/components/MkColorInput.vue b/packages/frontend/src/components/MkColorInput.vue
new file mode 100644
index 0000000000..2471aa958d
--- /dev/null
+++ b/packages/frontend/src/components/MkColorInput.vue
@@ -0,0 +1,110 @@
+<template>
+<div>
+ <div :class="$style.label"><slot name="label"></slot></div>
+ <div :class="[$style.input, { disabled }]">
+ <input
+ ref="inputEl"
+ v-model="v"
+ v-adaptive-border
+ :class="$style.inputCore"
+ type="color"
+ :disabled="disabled"
+ :required="required"
+ :readonly="readonly"
+ @input="onInput"
+ >
+ </div>
+ <div :class="$style.caption"><slot name="caption"></slot></div>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { onMounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue';
+import { i18n } from '@/i18n';
+
+const props = defineProps<{
+ modelValue: string | null;
+ required?: boolean;
+ readonly?: boolean;
+ disabled?: boolean;
+}>();
+
+const emit = defineEmits<{
+ (ev: 'update:modelValue', value: string): void;
+}>();
+
+const { modelValue } = toRefs(props);
+const v = ref(modelValue.value);
+const inputEl = shallowRef<HTMLElement>();
+
+const onInput = (ev: KeyboardEvent) => {
+ emit('update:modelValue', v.value);
+};
+</script>
+
+<style lang="scss" module>
+.label {
+ font-size: 0.85em;
+ padding: 0 0 8px 0;
+ user-select: none;
+
+ &:empty {
+ display: none;
+ }
+}
+
+.caption {
+ font-size: 0.85em;
+ padding: 8px 0 0 0;
+ color: var(--fgTransparentWeak);
+
+ &:empty {
+ display: none;
+ }
+}
+
+.input {
+ position: relative;
+
+ &.focused {
+ > .inputCore {
+ border-color: var(--accent) !important;
+ //box-shadow: 0 0 0 4px var(--focus);
+ }
+ }
+
+ &.disabled {
+ opacity: 0.7;
+
+ &,
+ > .inputCore {
+ cursor: not-allowed !important;
+ }
+ }
+}
+
+.inputCore {
+ appearance: none;
+ -webkit-appearance: none;
+ display: block;
+ height: 42px;
+ width: 100%;
+ margin: 0;
+ padding: 0 12px;
+ font: inherit;
+ font-weight: normal;
+ font-size: 1em;
+ color: var(--fg);
+ background: var(--panel);
+ border: solid 1px var(--panel);
+ border-radius: 6px;
+ outline: none;
+ box-shadow: none;
+ box-sizing: border-box;
+ transition: border-color 0.1s ease-out;
+
+ &:hover {
+ border-color: var(--inputBorderHover) !important;
+ }
+}
+</style>
diff --git a/packages/frontend/src/components/MkContainer.vue b/packages/frontend/src/components/MkContainer.vue
index a6372b7b6f..d03331a6eb 100644
--- a/packages/frontend/src/components/MkContainer.vue
+++ b/packages/frontend/src/components/MkContainer.vue
@@ -1,6 +1,6 @@
<template>
-<div class="_panel" :class="[$style.root, { [$style.naked]: naked, [$style.thin]: thin, [$style.hideHeader]: !showHeader, [$style.scrollable]: scrollable, [$style.closed]: !showBody }]">
- <header v-if="showHeader" ref="header" :class="$style.header">
+<div ref="rootEl" class="_panel" :class="[$style.root, { [$style.naked]: naked, [$style.thin]: thin, [$style.hideHeader]: !showHeader, [$style.scrollable]: scrollable, [$style.closed]: !showBody }]">
+ <header v-if="showHeader" ref="headerEl" :class="$style.header">
<div :class="$style.title">
<span :class="$style.titleIcon"><slot name="icon"></slot></span>
<slot name="header"></slot>
@@ -23,7 +23,7 @@
@leave="leave"
@after-leave="afterLeave"
>
- <div v-show="showBody" ref="content" :class="[$style.content, { [$style.omitted]: omitted }]">
+ <div v-show="showBody" ref="contentEl" :class="[$style.content, { [$style.omitted]: omitted }]">
<slot></slot>
<button v-if="omitted" :class="$style.fade" class="_button" @click="() => { ignoreOmit = true; omitted = false; }">
<span :class="$style.fadeLabel">{{ i18n.ts.showMore }}</span>
@@ -33,109 +33,80 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { onMounted, ref, shallowRef, watch } from 'vue';
import { defaultStore } from '@/store';
import { i18n } from '@/i18n';
-export default defineComponent({
- props: {
- showHeader: {
- type: Boolean,
- required: false,
- default: true,
- },
- thin: {
- type: Boolean,
- required: false,
- default: false,
- },
- naked: {
- type: Boolean,
- required: false,
- default: false,
- },
- foldable: {
- type: Boolean,
- required: false,
- default: false,
- },
- expanded: {
- type: Boolean,
- required: false,
- default: true,
- },
- scrollable: {
- type: Boolean,
- required: false,
- default: false,
- },
- maxHeight: {
- type: Number,
- required: false,
- default: null,
- },
- },
- data() {
- return {
- showBody: this.expanded,
- omitted: null,
- ignoreOmit: false,
- defaultStore,
- i18n,
- };
- },
- mounted() {
- this.$watch('showBody', showBody => {
- const headerHeight = this.showHeader ? this.$refs.header.offsetHeight : 0;
- this.$el.style.minHeight = `${headerHeight}px`;
- if (showBody) {
- this.$el.style.flexBasis = 'auto';
- } else {
- this.$el.style.flexBasis = `${headerHeight}px`;
- }
- }, {
- immediate: true,
- });
+const props = withDefaults(defineProps<{
+ showHeader?: boolean;
+ thin?: boolean;
+ naked?: boolean;
+ foldable?: boolean;
+ scrollable?: boolean;
+ expanded?: boolean;
+ maxHeight?: number | null;
+}>(), {
+ expanded: true,
+ showHeader: true,
+ maxHeight: null,
+});
- this.$el.style.setProperty('--maxHeight', this.maxHeight + 'px');
+const rootEl = shallowRef<HTMLElement>();
+const contentEl = shallowRef<HTMLElement>();
+const headerEl = shallowRef<HTMLElement>();
+const showBody = ref(props.expanded);
+const ignoreOmit = ref(false);
+const omitted = ref(false);
- const calcOmit = () => {
- if (this.omitted || this.ignoreOmit || this.maxHeight == null) return;
- const height = this.$refs.content.offsetHeight;
- this.omitted = height > this.maxHeight;
- };
+function enter(el) {
+ const elementHeight = el.getBoundingClientRect().height;
+ el.style.height = 0;
+ el.offsetHeight; // reflow
+ el.style.height = Math.min(elementHeight, props.maxHeight ?? Infinity) + 'px';
+}
- calcOmit();
- new ResizeObserver((entries, observer) => {
- calcOmit();
- }).observe(this.$refs.content);
- },
- methods: {
- toggleContent(show: boolean) {
- if (!this.foldable) return;
- this.showBody = show;
- },
+function afterEnter(el) {
+ el.style.height = null;
+}
+
+function leave(el) {
+ const elementHeight = el.getBoundingClientRect().height;
+ el.style.height = elementHeight + 'px';
+ el.offsetHeight; // reflow
+ el.style.height = 0;
+}
- enter(el) {
- const elementHeight = el.getBoundingClientRect().height;
- el.style.height = 0;
- el.offsetHeight; // reflow
- el.style.height = elementHeight + 'px';
- },
- afterEnter(el) {
- el.style.height = null;
- },
- leave(el) {
- const elementHeight = el.getBoundingClientRect().height;
- el.style.height = elementHeight + 'px';
- el.offsetHeight; // reflow
- el.style.height = 0;
- },
- afterLeave(el) {
- el.style.height = null;
- },
- },
+function afterLeave(el) {
+ el.style.height = null;
+}
+
+const calcOmit = () => {
+ if (omitted.value || ignoreOmit.value || props.maxHeight == null) return;
+ const height = contentEl.value.offsetHeight;
+ omitted.value = height > props.maxHeight;
+};
+
+onMounted(() => {
+ watch(showBody, v => {
+ const headerHeight = props.showHeader ? headerEl.value.offsetHeight : 0;
+ rootEl.value.style.minHeight = `${headerHeight}px`;
+ if (v) {
+ rootEl.value.style.flexBasis = 'auto';
+ } else {
+ rootEl.value.style.flexBasis = `${headerHeight}px`;
+ }
+ }, {
+ immediate: true,
+ });
+
+ rootEl.value.style.setProperty('--maxHeight', props.maxHeight + 'px');
+
+ calcOmit();
+
+ new ResizeObserver((entries, observer) => {
+ calcOmit();
+ }).observe(contentEl.value);
});
</script>
diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue
index 93c1f89199..9f5404ce15 100644
--- a/packages/frontend/src/components/MkDialog.vue
+++ b/packages/frontend/src/components/MkDialog.vue
@@ -9,7 +9,7 @@
<i v-else-if="type === 'error'" :class="$style.iconInner" class="ti ti-circle-x"></i>
<i v-else-if="type === 'warning'" :class="$style.iconInner" class="ti ti-alert-triangle"></i>
<i v-else-if="type === 'info'" :class="$style.iconInner" class="ti ti-info-circle"></i>
- <i v-else-if="type === 'question'" :class="$style.iconInner" class="ti ti-question-circle"></i>
+ <i v-else-if="type === 'question'" :class="$style.iconInner" class="ti ti-help-circle"></i>
<MkLoading v-else-if="type === 'waiting'" :class="$style.iconInner" :em="true"/>
</div>
<header v-if="title" :class="$style.title"><Mfm :text="title"/></header>
@@ -32,8 +32,8 @@
</template>
</MkSelect>
<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons">
- <MkButton v-if="showOkButton" inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabled" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
- <MkButton v-if="showCancelButton || input || select" inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton>
+ <MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabled" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
+ <MkButton v-if="showCancelButton || input || select" data-cy-modal-dialog-cancel inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton>
</div>
<div v-if="actions" :class="$style.buttons">
<MkButton v-for="action in actions" :key="action.text" inline rounded :primary="action.primary" :danger="action.danger" @click="() => { action.callback(); modal?.close(); }">{{ action.text }}</MkButton>
@@ -183,7 +183,7 @@ onBeforeUnmount(() => {
box-sizing: border-box;
text-align: center;
background: var(--panel);
- border-radius: var(--radius);
+ border-radius: 16px;
}
.icon {
diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue
index 58cc0de5c8..10eee6aab1 100644
--- a/packages/frontend/src/components/MkFolder.vue
+++ b/packages/frontend/src/components/MkFolder.vue
@@ -1,8 +1,8 @@
<template>
-<div ref="rootEl" :class="$style.root">
+<div ref="rootEl" :class="$style.root" role="group" :aria-expanded="opened">
<MkStickyContainer>
<template #header>
- <div :class="[$style.header, { [$style.opened]: opened }]" class="_button" @click="toggle">
+ <div :class="[$style.header, { [$style.opened]: opened }]" class="_button" role="button" data-cy-folder-header @click="toggle">
<div :class="$style.headerIcon"><slot name="icon"></slot></div>
<div :class="$style.headerText">
<div :class="$style.headerTextMain">
@@ -20,7 +20,7 @@
</div>
</template>
- <div v-if="openedAtLeastOnce" :class="[$style.body, { [$style.bgSame]: bgSame }]" :style="{ maxHeight: maxHeight ? `${maxHeight}px` : null, overflow: maxHeight ? `auto` : null }">
+ <div v-if="openedAtLeastOnce" :class="[$style.body, { [$style.bgSame]: bgSame }]" :style="{ maxHeight: maxHeight ? `${maxHeight}px` : null, overflow: maxHeight ? `auto` : null }" :aria-hidden="!opened">
<Transition
:enter-active-class="defaultStore.state.animation ? $style.transition_toggle_enterActive : ''"
:leave-active-class="defaultStore.state.animation ? $style.transition_toggle_leaveActive : ''"
@@ -65,7 +65,7 @@ const getBgColor = (el: HTMLElement) => {
}
};
-let rootEl = $ref<HTMLElement>();
+let rootEl = $shallowRef<HTMLElement>();
let bgSame = $ref(false);
let opened = $ref(props.defaultOpen);
let openedAtLeastOnce = $ref(props.defaultOpen);
@@ -196,7 +196,7 @@ onMounted(() => {
.headerRight {
margin-left: auto;
- opacity: 0.7;
+ color: var(--fgTransparentWeak);
white-space: nowrap;
}
diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue
index de8db54bfa..beee21c647 100644
--- a/packages/frontend/src/components/MkFollowButton.vue
+++ b/packages/frontend/src/components/MkFollowButton.vue
@@ -178,7 +178,7 @@ onBeforeUnmount(() => {
}
&.active {
- color: #fff;
+ color: var(--fgOnAccent);
background: var(--accent);
&:hover {
diff --git a/packages/frontend/src/components/MkGalleryPostPreview.stories.impl.ts b/packages/frontend/src/components/MkGalleryPostPreview.stories.impl.ts
index e46a708192..57b3e75513 100644
--- a/packages/frontend/src/components/MkGalleryPostPreview.stories.impl.ts
+++ b/packages/frontend/src/components/MkGalleryPostPreview.stories.impl.ts
@@ -28,9 +28,11 @@ export const Default = {
async play({ canvasElement }) {
const canvas = within(canvasElement);
const links = canvas.getAllByRole('link');
- await expect(links).toHaveLength(2);
- await expect(links[0]).toHaveAttribute('href', `/gallery/${galleryPost().id}`);
- await expect(links[1]).toHaveAttribute('href', `/@${galleryPost().user.username}@${galleryPost().user.host}`);
+ expect(links).toHaveLength(2);
+ expect(links[0]).toHaveAttribute('href', `/gallery/${galleryPost().id}`);
+ expect(links[1]).toHaveAttribute('href', `/@${galleryPost().user.username}@${galleryPost().user.host}`);
+ const images = canvas.getAllByRole<HTMLImageElement>('img');
+ await waitFor(() => expect(Promise.all(images.map((image) => image.decode()))).resolves.toBeDefined());
},
args: {
post: galleryPost(),
diff --git a/packages/frontend/src/components/MkGalleryPostPreview.vue b/packages/frontend/src/components/MkGalleryPostPreview.vue
index 944f5ad97b..4f8f7b945a 100644
--- a/packages/frontend/src/components/MkGalleryPostPreview.vue
+++ b/packages/frontend/src/components/MkGalleryPostPreview.vue
@@ -1,9 +1,21 @@
<template>
<MkA :to="`/gallery/${post.id}`" class="ttasepnz _panel" tabindex="-1" @pointerenter="enterHover" @pointerleave="leaveHover">
<div class="thumbnail">
- <ImgWithBlurhash class="img" :hash="post.files[0].blurhash"/>
<Transition>
- <ImgWithBlurhash v-if="show" class="img layered" :src="post.files[0].thumbnailUrl" :hash="post.files[0].blurhash"/>
+ <ImgWithBlurhash
+ class="img layered"
+ :transition="safe ? null : {
+ enterActiveClass: $style.transition_toggle_enterActive,
+ leaveActiveClass: $style.transition_toggle_leaveActive,
+ enterFromClass: $style.transition_toggle_enterFrom,
+ leaveToClass: $style.transition_toggle_leaveTo,
+ enterToClass: $style.transition_toggle_enterTo,
+ leaveFromClass: $style.transition_toggle_leaveFrom,
+ }"
+ :src="post.files[0].thumbnailUrl"
+ :hash="post.files[0].blurhash"
+ :force-blurhash="!show"
+ />
</Transition>
</div>
<article>
@@ -28,7 +40,8 @@ const props = defineProps<{
}>();
const hover = ref(false);
-const show = computed(() => defaultStore.state.nsfw === 'ignore' || defaultStore.state.nsfw === 'respect' && !props.post.isSensitive || hover.value);
+const safe = computed(() => defaultStore.state.nsfw === 'ignore' || defaultStore.state.nsfw === 'respect' && !props.post.isSensitive);
+const show = computed(() => safe.value || hover.value);
function enterHover(): void {
hover.value = true;
@@ -39,6 +52,27 @@ function leaveHover(): void {
}
</script>
+<style lang="scss" module>
+.transition_toggle_enterActive,
+.transition_toggle_leaveActive {
+ transition: opacity 0.5s;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.transition_toggle_enterFrom,
+.transition_toggle_leaveTo {
+ opacity: 0;
+}
+
+.transition_toggle_enterTo,
+.transition_toggle_leaveFrom {
+ transition: none;
+ opacity: 1;
+}
+</style>
+
<style lang="scss" scoped>
.ttasepnz {
display: block;
@@ -66,7 +100,7 @@ function leaveHover(): void {
width: 100%;
height: 100%;
position: absolute;
- transition: all 0.5s ease;
+ transition: transform 0.5s ease;
> .img {
width: 100%;
@@ -76,16 +110,6 @@ function leaveHover(): void {
&.layered {
position: absolute;
top: 0;
-
- &.v-enter-active,
- &.v-leave-active {
- transition: opacity 0.5s ease;
- }
-
- &.v-enter-from,
- &.v-leave-to {
- opacity: 0;
- }
}
}
}
diff --git a/packages/frontend/src/components/MkImgWithBlurhash.vue b/packages/frontend/src/components/MkImgWithBlurhash.vue
index 944c76d7dc..6406a35060 100644
--- a/packages/frontend/src/components/MkImgWithBlurhash.vue
+++ b/packages/frontend/src/components/MkImgWithBlurhash.vue
@@ -1,44 +1,90 @@
<template>
-<div :class="[$style.root, { [$style.cover]: cover }]" :title="title">
- <canvas v-if="!loaded" ref="canvas" :class="$style.canvas" :width="size" :height="size" :title="title"/>
- <img v-if="src" :class="$style.img" :src="src" :title="title" :alt="alt" @load="onLoad"/>
+<div :class="[$style.root, { [$style.cover]: cover }]" :title="title ?? ''">
+ <img v-if="!loaded && src && !forceBlurhash" :class="$style.loader" :src="src" @load="onLoad"/>
+ <Transition
+ mode="in-out"
+ :enter-active-class="defaultStore.state.animation && (props.transition?.enterActiveClass ?? $style['transition_toggle_enterActive']) || undefined"
+ :leave-active-class="defaultStore.state.animation && (props.transition?.leaveActiveClass ?? $style['transition_toggle_leaveActive']) || undefined"
+ :enter-from-class="defaultStore.state.animation && props.transition?.enterFromClass || undefined"
+ :leave-to-class="defaultStore.state.animation && props.transition?.leaveToClass || undefined"
+ :enter-to-class="defaultStore.state.animation && (props.transition?.enterToClass ?? $style['transition_toggle_enterTo']) || undefined"
+ :leave-from-class="defaultStore.state.animation && (props.transition?.leaveFromClass ?? $style['transition_toggle_leaveFrom']) || undefined"
+ >
+ <canvas v-if="!loaded || forceBlurhash" ref="canvas" :class="$style.canvas" :width="width" :height="height" :title="title ?? undefined"/>
+ <img v-else :class="$style.img" :src="src ?? undefined" :title="title ?? undefined" :alt="alt ?? undefined"/>
+ </Transition>
</div>
</template>
<script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, shallowRef, useCssModule, watch } from 'vue';
import { decode } from 'blurhash';
+import { defaultStore } from '@/store';
+
+const $style = useCssModule();
const props = withDefaults(defineProps<{
+ transition?: {
+ enterActiveClass?: string;
+ leaveActiveClass?: string;
+ enterFromClass?: string;
+ leaveToClass?: string;
+ enterToClass?: string;
+ leaveFromClass?: string;
+ } | null;
src?: string | null;
hash?: string;
- alt?: string;
+ alt?: string | null;
title?: string | null;
- size?: number;
+ height?: number;
+ width?: number;
cover?: boolean;
+ forceBlurhash?: boolean;
}>(), {
+ transition: null,
src: null,
alt: '',
title: null,
- size: 64,
+ height: 64,
+ width: 64,
cover: true,
+ forceBlurhash: false,
});
-const canvas = $shallowRef<HTMLCanvasElement>();
+const canvas = shallowRef<HTMLCanvasElement>();
let loaded = $ref(false);
+let width = $ref(props.width);
+let height = $ref(props.height);
+
+function onLoad() {
+ loaded = true;
+}
+
+watch([() => props.width, () => props.height], () => {
+ const ratio = props.width / props.height;
+ if (ratio > 1) {
+ width = Math.round(64 * ratio);
+ height = 64;
+ } else {
+ width = 64;
+ height = Math.round(64 / ratio);
+ }
+}, {
+ immediate: true,
+});
function draw() {
- if (props.hash == null) return;
- const pixels = decode(props.hash, props.size, props.size);
- const ctx = canvas.getContext('2d');
- const imageData = ctx!.createImageData(props.size, props.size);
+ if (props.hash == null || !canvas.value) return;
+ const pixels = decode(props.hash, width, height);
+ const ctx = canvas.value.getContext('2d');
+ const imageData = ctx!.createImageData(width, height);
imageData.data.set(pixels);
ctx!.putImageData(imageData, 0, 0);
}
-function onLoad() {
- loaded = true;
-}
+watch([() => props.hash, canvas], () => {
+ draw();
+});
onMounted(() => {
draw();
@@ -46,12 +92,33 @@ onMounted(() => {
</script>
<style lang="scss" module>
+.transition_toggle_enterActive,
+.transition_toggle_leaveActive {
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.transition_toggle_enterTo,
+.transition_toggle_leaveFrom {
+ opacity: 0;
+}
+
+.loader {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 0;
+ height: 0;
+}
+
.root {
position: relative;
width: 100%;
height: 100%;
&.cover {
+ > .canvas,
> .img {
object-fit: cover;
}
@@ -66,8 +133,7 @@ onMounted(() => {
}
.canvas {
- position: absolute;
- object-fit: cover;
+ object-fit: contain;
}
.img {
diff --git a/packages/frontend/src/components/MkInfo.vue b/packages/frontend/src/components/MkInfo.vue
index dc7344d707..cda428a77c 100644
--- a/packages/frontend/src/components/MkInfo.vue
+++ b/packages/frontend/src/components/MkInfo.vue
@@ -21,6 +21,7 @@ const props = defineProps<{
background: var(--infoBg);
color: var(--infoFg);
border-radius: var(--radius);
+ white-space: pre-wrap;
&.warn {
background: var(--infoWarnBg);
diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue
index 3e3d7354c1..e48032d599 100644
--- a/packages/frontend/src/components/MkInput.vue
+++ b/packages/frontend/src/components/MkInput.vue
@@ -1,12 +1,13 @@
<template>
-<div class="matxzzsk">
- <div class="label" @click="focus"><slot name="label"></slot></div>
- <div class="input" :class="{ inline, disabled, focused }">
- <div ref="prefixEl" class="prefix"><slot name="prefix"></slot></div>
+<div>
+ <div :class="$style.label" @click="focus"><slot name="label"></slot></div>
+ <div :class="[$style.input, { [$style.inline]: inline, [$style.disabled]: disabled, [$style.focused]: focused }]">
+ <div ref="prefixEl" :class="$style.prefix"><slot name="prefix"></slot></div>
<input
ref="inputEl"
v-model="v"
v-adaptive-border
+ :class="$style.inputCore"
:type="type"
:disabled="disabled"
:required="required"
@@ -25,11 +26,11 @@
<datalist v-if="datalist" :id="id">
<option v-for="data in datalist" :key="data" :value="data"/>
</datalist>
- <div ref="suffixEl" class="suffix"><slot name="suffix"></slot></div>
+ <div ref="suffixEl" :class="$style.suffix"><slot name="suffix"></slot></div>
</div>
- <div class="caption"><slot name="caption"></slot></div>
+ <div :class="$style.caption"><slot name="caption"></slot></div>
- <MkButton v-if="manualSave && changed" primary class="save" @click="updated"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
+ <MkButton v-if="manualSave && changed" primary :class="$style.save" @click="updated"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
</div>
</template>
@@ -151,115 +152,110 @@ onMounted(() => {
});
</script>
-<style lang="scss" scoped>
-.matxzzsk {
- > .label {
- font-size: 0.85em;
- padding: 0 0 8px 0;
- user-select: none;
+<style lang="scss" module>
+.label {
+ font-size: 0.85em;
+ padding: 0 0 8px 0;
+ user-select: none;
- &:empty {
- display: none;
- }
+ &:empty {
+ display: none;
}
+}
- > .caption {
- font-size: 0.85em;
- padding: 8px 0 0 0;
- color: var(--fgTransparentWeak);
+.caption {
+ font-size: 0.85em;
+ padding: 8px 0 0 0;
+ color: var(--fgTransparentWeak);
- &:empty {
- display: none;
- }
+ &:empty {
+ display: none;
}
+}
- > .input {
- position: relative;
-
- > input {
- appearance: none;
- -webkit-appearance: none;
- display: block;
- height: v-bind("height + 'px'");
- width: 100%;
- margin: 0;
- padding: 0 12px;
- font: inherit;
- font-weight: normal;
- font-size: 1em;
- color: var(--fg);
- background: var(--panel);
- border: solid 1px var(--panel);
- border-radius: 6px;
- outline: none;
- box-shadow: none;
- box-sizing: border-box;
- transition: border-color 0.1s ease-out;
-
- &:hover {
- border-color: var(--inputBorderHover) !important;
- }
- }
-
- > .prefix,
- > .suffix {
- display: flex;
- align-items: center;
- position: absolute;
- z-index: 1;
- top: 0;
- padding: 0 12px;
- font-size: 1em;
- height: v-bind("height + 'px'");
- pointer-events: none;
+.input {
+ position: relative;
- &:empty {
- display: none;
- }
+ &.inline {
+ display: inline-block;
+ margin: 0;
+ }
- > * {
- display: inline-block;
- min-width: 16px;
- max-width: 150px;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
+ &.focused {
+ > .inputCore {
+ border-color: var(--accent) !important;
+ //box-shadow: 0 0 0 4px var(--focus);
}
+ }
- > .prefix {
- left: 0;
- padding-right: 6px;
- }
+ &.disabled {
+ opacity: 0.7;
- > .suffix {
- right: 0;
- padding-left: 6px;
+ &,
+ > .inputCore {
+ cursor: not-allowed !important;
}
+ }
+}
- &.inline {
- display: inline-block;
- margin: 0;
- }
+.inputCore {
+ appearance: none;
+ -webkit-appearance: none;
+ display: block;
+ height: v-bind("height + 'px'");
+ width: 100%;
+ margin: 0;
+ padding: 0 12px;
+ font: inherit;
+ font-weight: normal;
+ font-size: 1em;
+ color: var(--fg);
+ background: var(--panel);
+ border: solid 1px var(--panel);
+ border-radius: 6px;
+ outline: none;
+ box-shadow: none;
+ box-sizing: border-box;
+ transition: border-color 0.1s ease-out;
- &.focused {
- > input {
- border-color: var(--accent) !important;
- //box-shadow: 0 0 0 4px var(--focus);
- }
- }
+ &:hover {
+ border-color: var(--inputBorderHover) !important;
+ }
+}
- &.disabled {
- opacity: 0.7;
+.prefix,
+.suffix {
+ display: flex;
+ align-items: center;
+ position: absolute;
+ z-index: 1;
+ top: 0;
+ padding: 0 12px;
+ font-size: 1em;
+ height: v-bind("height + 'px'");
+ min-width: 16px;
+ max-width: 150px;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ box-sizing: border-box;
+ pointer-events: none;
- &, * {
- cursor: not-allowed !important;
- }
- }
+ &:empty {
+ display: none;
}
+}
- > .save {
- margin: 8px 0 0 0;
- }
+.prefix {
+ left: 0;
+ padding-right: 6px;
+}
+
+.suffix {
+ right: 0;
+ padding-left: 6px;
+}
+.save {
+ margin: 8px 0 0 0;
}
</style>
diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue
index a4065dcd07..42dc9e79ff 100644
--- a/packages/frontend/src/components/MkMediaImage.vue
+++ b/packages/frontend/src/components/MkMediaImage.vue
@@ -1,9 +1,10 @@
<template>
<div v-if="hide" :class="$style.hidden" @click="hide = false">
- <ImgWithBlurhash style="filter: brightness(0.5);" :hash="image.blurhash" :title="image.comment" :alt="image.comment"/>
+ <ImgWithBlurhash style="filter: brightness(0.5);" :hash="image.blurhash" :title="image.comment" :alt="image.comment" :width="image.properties.width" :height="image.properties.height" :force-blurhash="defaultStore.state.enableDataSaverMode"/>
<div :class="$style.hiddenText">
<div :class="$style.hiddenTextWrapper">
- <b style="display: block;"><i class="ti ti-alert-triangle"></i> {{ i18n.ts.sensitive }}</b>
+ <b v-if="image.isSensitive" style="display: block;"><i class="ti ti-alert-triangle"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.enableDataSaverMode ? ` (${i18n.ts.image}${image.size ? ' ' + bytes(image.size) : ''})` : '' }}</b>
+ <b v-else style="display: block;"><i class="ti ti-photo"></i> {{ defaultStore.state.enableDataSaverMode && image.size ? bytes(image.size) : i18n.ts.image }}</b>
<span style="display: block;">{{ i18n.ts.clickToShow }}</span>
</div>
</div>
@@ -14,13 +15,15 @@
:href="image.url"
:title="image.name"
>
- <ImgWithBlurhash :hash="image.blurhash" :src="url" :alt="image.comment || image.name" :title="image.comment || image.name" :cover="false"/>
+ <ImgWithBlurhash :hash="image.blurhash" :src="url" :alt="image.comment || image.name" :title="image.comment || image.name" :width="image.properties.width" :height="image.properties.height" :cover="false"/>
</a>
<div :class="$style.indicators">
<div v-if="['image/gif', 'image/apng'].includes(image.type)" :class="$style.indicator">GIF</div>
<div v-if="image.comment" :class="$style.indicator">ALT</div>
+ <div v-if="image.isSensitive" :class="$style.indicator" style="color: var(--warn);">NSFW</div>
</div>
<button v-tooltip="i18n.ts.hide" :class="$style.hide" class="_button" @click="hide = true"><i class="ti ti-eye-off"></i></button>
+ <button :class="$style.menu" class="_button" @click.stop="showMenu"><i class="ti ti-dots"></i></button>
</div>
</template>
@@ -28,9 +31,12 @@
import { watch } from 'vue';
import * as misskey from 'misskey-js';
import { getStaticImageUrl } from '@/scripts/media-proxy';
+import bytes from '@/filters/bytes';
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
import { defaultStore } from '@/store';
import { i18n } from '@/i18n';
+import * as os from '@/os';
+import { iAmModerator } from '@/account';
const props = defineProps<{
image: misskey.entities.DriveFile;
@@ -38,21 +44,33 @@ const props = defineProps<{
}>();
let hide = $ref(true);
-let darkMode = $ref(defaultStore.state.darkMode);
+let darkMode: boolean = $ref(defaultStore.state.darkMode);
-const url = (props.raw || defaultStore.state.loadRawImages)
+const url = $computed(() => (props.raw || defaultStore.state.loadRawImages)
? props.image.url
: defaultStore.state.disableShowingAnimatedImages
? getStaticImageUrl(props.image.url)
- : props.image.thumbnailUrl;
+ : props.image.thumbnailUrl,
+);
// Plugin:register_note_view_interruptor を使ã£ã¦æ›¸ãæ›ãˆã‚‰ã‚Œã‚‹å¯èƒ½æ€§ãŒã‚ã‚‹ãŸã‚watchã™ã‚‹
watch(() => props.image, () => {
- hide = (defaultStore.state.nsfw === 'force') ? true : props.image.isSensitive && (defaultStore.state.nsfw !== 'ignore');
+ hide = (defaultStore.state.nsfw === 'force' || defaultStore.state.enableDataSaverMode) ? true : (props.image.isSensitive && defaultStore.state.nsfw !== 'ignore');
}, {
deep: true,
immediate: true,
});
+
+function showMenu(ev: MouseEvent) {
+ os.popupMenu([...(iAmModerator ? [{
+ text: i18n.ts.markAsSensitive,
+ icon: 'ti ti-eye-off',
+ action: () => {
+ os.apiWithDialog('drive/files/update', { fileId: props.image.id, isSensitive: true });
+ },
+ }] : [])], ev.currentTarget ?? ev.target);
+}
+
</script>
<style lang="scss" module>
@@ -102,6 +120,21 @@ watch(() => props.image, () => {
right: 12px;
}
+.menu {
+ display: block;
+ position: absolute;
+ border-radius: 6px;
+ background-color: rgba(0, 0, 0, 0.3);
+ -webkit-backdrop-filter: var(--blur, blur(15px));
+ backdrop-filter: var(--blur, blur(15px));
+ color: #fff;
+ font-size: 0.8em;
+ padding: 6px 8px;
+ text-align: center;
+ bottom: 12px;
+ right: 12px;
+}
+
.imageContainer {
display: block;
cursor: zoom-in;
@@ -132,6 +165,7 @@ watch(() => props.image, () => {
color: var(--accentLighten);
display: inline-block;
font-weight: bold;
- padding: 0 6px;
+ font-size: 12px;
+ padding: 2px 6px;
}
</style>
diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue
index d36cc2d26b..e456ff3eec 100644
--- a/packages/frontend/src/components/MkMediaList.vue
+++ b/packages/frontend/src/components/MkMediaList.vue
@@ -2,10 +2,16 @@
<div>
<XBanner v-for="media in mediaList.filter(media => !previewable(media))" :key="media.id" :media="media"/>
<div v-if="mediaList.filter(media => previewable(media)).length > 0" :class="$style.container">
- <div ref="gallery" :class="[$style.medias, count <= 4 ? $style['n' + count] : $style.nMany]">
+ <div
+ ref="gallery"
+ :class="[
+ $style.medias,
+ count <= 4 ? $style['n' + count] : $style.nMany,
+ ]"
+ >
<template v-for="media in mediaList.filter(media => previewable(media))">
- <XVideo v-if="media.type.startsWith('video')" :key="media.id" :class="$style.media" :video="media"/>
- <XImage v-else-if="media.type.startsWith('image')" :key="media.id" :class="$style.media" class="image" :data-id="media.id" :image="media" :raw="raw"/>
+ <XVideo v-if="media.type.startsWith('video')" :key="`video:${media.id}`" :class="$style.media" :video="media"/>
+ <XImage v-else-if="media.type.startsWith('image')" :key="`image:${media.id}`" :class="$style.media" class="image" :data-id="media.id" :image="media" :raw="raw"/>
</template>
</div>
</div>
@@ -13,7 +19,7 @@
</template>
<script lang="ts" setup>
-import { onMounted, ref, useCssModule } from 'vue';
+import { onMounted, ref, useCssModule, watch } from 'vue';
import * as misskey from 'misskey-js';
import PhotoSwipeLightbox from 'photoswipe/lightbox';
import PhotoSwipe from 'photoswipe';
@@ -23,6 +29,7 @@ import XImage from '@/components/MkMediaImage.vue';
import XVideo from '@/components/MkMediaVideo.vue';
import * as os from '@/os';
import { FILE_TYPE_BROWSERSAFE } from '@/const';
+import { defaultStore } from '@/store';
const props = defineProps<{
mediaList: misskey.entities.DriveFile[];
@@ -31,7 +38,7 @@ const props = defineProps<{
const $style = useCssModule();
-const gallery = ref(null);
+const gallery = ref<HTMLDivElement>();
const pswpZIndex = os.claimZIndex('middle');
document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString());
const count = $computed(() => props.mediaList.filter(media => previewable(media)).length);
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index e02a7af09e..a4b76300e6 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -1,7 +1,9 @@
<template>
<div v-if="hide" class="icozogqfvdetwohsdglrbswgrejoxbdj" @click="hide = false">
+ <!-- ã€æ³¨æ„】dataSaverMode ãŒæœ‰åйã«ãªã£ã¦ã„ã‚‹éš›ã«ã¯ã€hide ㌠false ã«ãªã‚‹ã¾ã§ã‚µãƒ ãƒã‚¤ãƒ«ã‚„動画を読ã¿è¾¼ã¾ãªã„よã†ã«ã™ã‚‹ã“㨠-->
<div>
- <b><i class="ti ti-alert-triangle"></i> {{ i18n.ts.sensitive }}</b>
+ <b v-if="video.isSensitive"><i class="ti ti-alert-triangle"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.enableDataSaverMode ? ` (${i18n.ts.video}${video.size ? ' ' + bytes(video.size) : ''})` : '' }}</b>
+ <b v-else><i class="ti ti-movie"></i> {{ defaultStore.state.enableDataSaverMode && video.size ? bytes(video.size) : i18n.ts.video }}</b>
<span>{{ i18n.ts.clickToShow }}</span>
</div>
</div>
@@ -25,6 +27,7 @@
<script lang="ts" setup>
import { ref } from 'vue';
import * as misskey from 'misskey-js';
+import bytes from '@/filters/bytes';
import VuePlyr from 'vue-plyr';
import { defaultStore } from '@/store';
import 'vue-plyr/dist/vue-plyr.css';
@@ -34,7 +37,7 @@ const props = defineProps<{
video: misskey.entities.DriveFile;
}>();
-const hide = ref((defaultStore.state.nsfw === 'force') ? true : props.video.isSensitive && (defaultStore.state.nsfw !== 'ignore'));
+const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.enableDataSaverMode) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore'));
</script>
<style lang="scss" scoped>
diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue
index 852c72f6ff..99df9e8150 100644
--- a/packages/frontend/src/components/MkModal.vue
+++ b/packages/frontend/src/components/MkModal.vue
@@ -404,16 +404,10 @@ defineExpose({
right: 0;
margin: auto;
padding: 32px;
- // TODO: mask-imageã¯iOSã ã¨ã‚„ãŸã‚‰é‡ã„。ãªã‚“ã¨ã‹ã—ãŸã„
- -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 32px, rgba(0,0,0,1) calc(100% - 32px), rgba(0,0,0,0) 100%);
- mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 32px, rgba(0,0,0,1) calc(100% - 32px), rgba(0,0,0,0) 100%);
- overflow: auto;
display: flex;
@media (max-width: 500px) {
padding: 16px;
- -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 16px, rgba(0,0,0,1) calc(100% - 16px), rgba(0,0,0,0) 100%);
- mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 16px, rgba(0,0,0,1) calc(100% - 16px), rgba(0,0,0,0) 100%);
}
}
}
diff --git a/packages/frontend/src/components/MkModalWindow.vue b/packages/frontend/src/components/MkModalWindow.vue
index dd115246ff..1c942cfd0d 100644
--- a/packages/frontend/src/components/MkModalWindow.vue
+++ b/packages/frontend/src/components/MkModalWindow.vue
@@ -1,12 +1,12 @@
<template>
<MkModal ref="modal" :prefer-type="'dialog'" @click="onBgClick" @closed="$emit('closed')">
- <div ref="rootEl" class="ebkgoccj" :style="{ width: `${width}px`, height: scroll ? (height ? `${height}px` : null) : (height ? `min(${height}px, 100%)` : '100%') }" @keydown="onKeydown">
+ <div ref="rootEl" class="ebkgoccj" :style="{ width: `${width}px`, height: height ? `${height}px` : null }" @keydown="onKeydown">
<div ref="headerEl" class="header">
<button v-if="withOkButton" class="_button" @click="$emit('close')"><i class="ti ti-x"></i></button>
<span class="title">
<slot name="header"></slot>
</span>
- <button v-if="!withOkButton" class="_button" @click="$emit('close')"><i class="ti ti-x"></i></button>
+ <button v-if="!withOkButton" class="_button" data-cy-modal-window-close @click="$emit('close')"><i class="ti ti-x"></i></button>
<button v-if="withOkButton" class="_button" :disabled="okButtonDisabled" @click="$emit('ok')"><i class="ti ti-check"></i></button>
</div>
<div class="body">
@@ -25,13 +25,11 @@ const props = withDefaults(defineProps<{
okButtonDisabled: boolean;
width: number;
height: number | null;
- scroll: boolean;
}>(), {
withOkButton: false,
okButtonDisabled: false,
width: 400,
height: null,
- scroll: true,
});
const emit = defineEmits<{
@@ -86,11 +84,11 @@ defineExpose({
<style lang="scss" scoped>
.ebkgoccj {
margin: auto;
+ max-height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
contain: content;
- container-type: inline-size;
border-radius: var(--radius);
--root-margin: 24px;
@@ -143,6 +141,7 @@ defineExpose({
flex: 1;
overflow: auto;
background: var(--panel);
+ container-type: size;
}
}
</style>
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 36ec778a14..d95f8de311 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -12,6 +12,7 @@
<!--<div v-if="appearNote._prId_" class="tip"><i class="ti ti-speakerphone"></i> {{ i18n.ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ i18n.ts.hideThisNote }} <i class="ti ti-x"></i></button></div>-->
<!--<div v-if="appearNote._featuredId_" class="tip"><i class="ti ti-bolt"></i> {{ i18n.ts.featured }}</div>-->
<div v-if="isRenote" :class="$style.renote">
+ <div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
<MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/>
<i class="ti ti-repeat" style="margin-right: 4px;"></i>
<I18n :src="i18n.ts.renotedBy" tag="span" :class="$style.renoteText">
@@ -40,6 +41,7 @@
<Mfm :text="getNoteSummary(appearNote)" :plain="true" :nowrap="true" :author="appearNote.user" :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/>
</div>
<article v-else :class="$style.article" @contextmenu.stop="onContextmenu">
+ <div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div>
<MkAvatar :class="$style.avatar" :user="appearNote.user" link preview/>
<div :class="$style.main">
<MkNoteHeader :class="$style.header" :note="appearNote" :mini="true"/>
@@ -162,6 +164,7 @@ import { claimAchievement } from '@/scripts/achievements';
import { getNoteSummary } from '@/scripts/get-note-summary';
import { MenuItem } from '@/types/menu';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
+import { showMovedDialog } from '@/scripts/show-moved-dialog';
const props = defineProps<{
note: misskey.entities.Note;
@@ -255,6 +258,7 @@ useTooltip(renoteButton, async (showing) => {
function renote(viaKeyboard = false) {
pleaseLogin();
+ showMovedDialog();
let items = [] as MenuItem[];
@@ -335,6 +339,7 @@ function reply(viaKeyboard = false): void {
function react(viaKeyboard = false): void {
pleaseLogin();
+ showMovedDialog();
if (appearNote.reactionAcceptance === 'likeOnly') {
os.api('notes/reactions/create', {
noteId: appearNote.id,
@@ -401,6 +406,7 @@ async function clip() {
function showRenoteMenu(viaKeyboard = false): void {
if (!isMyRenote) return;
+ pleaseLogin();
os.popupMenu([{
text: i18n.ts.unrenote,
icon: 'ti ti-trash',
@@ -484,6 +490,11 @@ function showReactions(): void {
}
}
+ .footer {
+ position: relative;
+ z-index: 1;
+ }
+
&:hover > .article > .main > .footer > .footerButton {
opacity: 1;
}
@@ -537,6 +548,7 @@ function showReactions(): void {
}
.renote {
+ position: relative;
display: flex;
align-items: center;
padding: 16px 32px 8px 32px;
@@ -547,6 +559,10 @@ function showReactions(): void {
& + .article {
padding-top: 8px;
}
+
+ > .colorBar {
+ height: calc(100% - 6px);
+ }
}
.renoteAvatar {
@@ -618,6 +634,16 @@ function showReactions(): void {
padding: 28px 32px;
}
+.colorBar {
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ width: 5px;
+ height: calc(100% - 16px);
+ border-radius: 999px;
+ pointer-events: none;
+}
+
.avatar {
flex-shrink: 0;
display: block !important;
@@ -669,6 +695,7 @@ function showReactions(): void {
position: absolute;
bottom: 0;
left: 0;
+ z-index: 2;
width: 100%;
height: 64px;
background: linear-gradient(0deg, var(--panel), var(--X15));
@@ -833,6 +860,13 @@ function showReactions(): void {
}
}
}
+
+ .colorBar {
+ top: 6px;
+ left: 6px;
+ width: 4px;
+ height: calc(100% - 12px);
+ }
}
@container (max-width: 300px) {
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index b9ab366850..0d6d329d98 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -166,6 +166,7 @@ import { useTooltip } from '@/scripts/use-tooltip';
import { claimAchievement } from '@/scripts/achievements';
import { MenuItem } from '@/types/menu';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
+import { showMovedDialog } from '@/scripts/show-moved-dialog';
const props = defineProps<{
note: misskey.entities.Note;
@@ -248,6 +249,7 @@ useTooltip(renoteButton, async (showing) => {
function renote(viaKeyboard = false) {
pleaseLogin();
+ showMovedDialog();
let items = [] as MenuItem[];
@@ -318,6 +320,7 @@ function renote(viaKeyboard = false) {
function reply(viaKeyboard = false): void {
pleaseLogin();
+ showMovedDialog();
os.post({
reply: appearNote,
animation: !viaKeyboard,
@@ -328,6 +331,7 @@ function reply(viaKeyboard = false): void {
function react(viaKeyboard = false): void {
pleaseLogin();
+ showMovedDialog();
if (appearNote.reactionAcceptance === 'likeOnly') {
os.api('notes/reactions/create', {
noteId: appearNote.id,
@@ -394,6 +398,7 @@ async function clip() {
function showRenoteMenu(viaKeyboard = false): void {
if (!isMyRenote) return;
+ pleaseLogin();
os.popupMenu([{
text: i18n.ts.unrenote,
icon: 'ti ti-trash',
diff --git a/packages/frontend/src/components/MkNumberDiff.vue b/packages/frontend/src/components/MkNumberDiff.vue
index e7d4a5472a..303417dae8 100644
--- a/packages/frontend/src/components/MkNumberDiff.vue
+++ b/packages/frontend/src/components/MkNumberDiff.vue
@@ -1,47 +1,32 @@
<template>
-<span class="ceaaebcd" :class="{ isPlus, isMinus, isZero }">
+<span class="ceaaebcd" :class="{ [$style.isPlus]: isPlus, [$style.isMinus]: isMinus, [$style.isZero]: isZero }">
<slot name="before"></slot>{{ isPlus ? '+' : '' }}{{ number(value) }}<slot name="after"></slot>
</span>
</template>
-<script lang="ts">
-import { computed, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { computed } from 'vue';
import number from '@/filters/number';
-export default defineComponent({
- props: {
- value: {
- type: Number,
- required: true,
- },
- },
+const props = defineProps<{
+ value: number;
+}>();
- setup(props) {
- const isPlus = computed(() => props.value > 0);
- const isMinus = computed(() => props.value < 0);
- const isZero = computed(() => props.value === 0);
- return {
- isPlus,
- isMinus,
- isZero,
- number,
- };
- },
-});
+const isPlus = computed(() => props.value > 0);
+const isMinus = computed(() => props.value < 0);
+const isZero = computed(() => props.value === 0);
</script>
-<style lang="scss" scoped>
-.ceaaebcd {
- &.isPlus {
- color: var(--success);
- }
+<style lang="scss" module>
+.isPlus {
+ color: var(--success);
+}
- &.isMinus {
- color: var(--error);
- }
+.isMinus {
+ color: var(--error);
+}
- &.isZero {
- opacity: 0.5;
- }
+.isZero {
+ opacity: 0.5;
}
</style>
diff --git a/packages/frontend/src/components/MkOmit.vue b/packages/frontend/src/components/MkOmit.vue
index 0f148022bf..e2d68d12c3 100644
--- a/packages/frontend/src/components/MkOmit.vue
+++ b/packages/frontend/src/components/MkOmit.vue
@@ -17,7 +17,7 @@ const props = withDefaults(defineProps<{
maxHeight: 200,
});
-let content = $ref<HTMLElement>();
+let content = $shallowRef<HTMLElement>();
let omitted = $ref(false);
let ignoreOmit = $ref(false);
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 42a3748d9a..c65cb7d6e5 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -247,6 +247,10 @@ watch($$(text), () => {
checkMissingMention();
}, { immediate: true });
+watch($$(visibility), () => {
+ checkMissingMention();
+}, { immediate: true });
+
watch($$(visibleUsers), () => {
checkMissingMention();
}, {
@@ -900,27 +904,28 @@ defineExpose({
}
.headerLeft {
- display: grid;
- grid-template-columns: repeat(2, minmax(36px, 50px));
- grid-template-rows: minmax(40px, 100%);
+ display: flex;
+ flex: 0 1 100px;
}
.cancel {
padding: 0;
font-size: 1em;
height: 100%;
+ flex: 0 1 50px;
}
.account {
height: 100%;
display: inline-flex;
vertical-align: bottom;
+ flex: 0 1 50px;
}
.avatar {
width: 28px;
height: 28px;
- margin: auto 0;
+ margin: auto;
}
.headerRight {
diff --git a/packages/frontend/src/components/MkRadio.vue b/packages/frontend/src/components/MkRadio.vue
index fcf454c77a..5db2f5ee6d 100644
--- a/packages/frontend/src/components/MkRadio.vue
+++ b/packages/frontend/src/components/MkRadio.vue
@@ -24,7 +24,7 @@ import { } from 'vue';
const props = defineProps<{
modelValue: any;
value: any;
- disabled: boolean;
+ disabled?: boolean;
}>();
const emit = defineEmits<{
diff --git a/packages/frontend/src/components/MkRadios.vue b/packages/frontend/src/components/MkRadios.vue
index 8590ccf9ae..e2240fb4e1 100644
--- a/packages/frontend/src/components/MkRadios.vue
+++ b/packages/frontend/src/components/MkRadios.vue
@@ -1,5 +1,5 @@
<script lang="ts">
-import { defineComponent, h } from 'vue';
+import { VNode, defineComponent, h } from 'vue';
import MkRadio from './MkRadio.vue';
export default defineComponent({
@@ -22,31 +22,33 @@ export default defineComponent({
},
},
render() {
+ console.log(this.$slots, this.$slots.label && this.$slots.label());
+ if (!this.$slots.default) return null;
let options = this.$slots.default();
const label = this.$slots.label && this.$slots.label();
const caption = this.$slots.caption && this.$slots.caption();
// ãªãœã‹Fragmentã«ãªã‚‹ã“ã¨ãŒã‚ã‚‹ãŸã‚
- if (options.length === 1 && options[0].props == null) options = options[0].children;
+ if (options.length === 1 && options[0].props == null) options = options[0].children as VNode[];
return h('div', {
class: 'novjtcto',
}, [
...(label ? [h('div', {
class: 'label',
- }, [label])] : []),
+ }, label)] : []),
h('div', {
class: 'body',
}, options.map(option => h(MkRadio, {
key: option.key,
- value: option.props.value,
+ value: option.props?.value,
modelValue: this.value,
'onUpdate:modelValue': value => this.value = value,
- }, option.children)),
+ }, () => option.children)),
),
...(caption ? [h('div', {
class: 'caption',
- }, [caption])] : []),
+ }, caption)] : []),
]);
},
});
diff --git a/packages/frontend/src/components/MkRange.vue b/packages/frontend/src/components/MkRange.vue
index a1ee6367a0..eaa134df25 100644
--- a/packages/frontend/src/components/MkRange.vue
+++ b/packages/frontend/src/components/MkRange.vue
@@ -17,7 +17,7 @@
</template>
<script lang="ts" setup>
-import { computed, defineAsyncComponent, onMounted, onUnmounted, ref, watch } from 'vue';
+import { computed, defineAsyncComponent, onMounted, onUnmounted, ref, watch, shallowRef } from 'vue';
import * as os from '@/os';
const props = withDefaults(defineProps<{
@@ -39,8 +39,8 @@ const emit = defineEmits<{
(ev: 'update:modelValue', value: number): void;
}>();
-const containerEl = ref<HTMLElement>();
-const thumbEl = ref<HTMLElement>();
+const containerEl = shallowRef<HTMLElement>();
+const thumbEl = shallowRef<HTMLElement>();
const rawValue = ref((props.modelValue - props.min) / (props.max - props.min));
const steppedRawValue = computed(() => {
diff --git a/packages/frontend/src/components/MkReactedUsersDialog.vue b/packages/frontend/src/components/MkReactedUsersDialog.vue
index 1506e24ce8..0c0cc36692 100644
--- a/packages/frontend/src/components/MkReactedUsersDialog.vue
+++ b/packages/frontend/src/components/MkReactedUsersDialog.vue
@@ -6,7 +6,7 @@
@close="dialog.close()"
@closed="emit('closed')"
>
- <template #header>{{ i18n.ts.reactions }}</template>
+ <template #header>{{ i18n.ts.reactionsList }}</template>
<MkSpacer :margin-min="20" :margin-max="28">
<div v-if="note" class="_gaps">
@@ -21,7 +21,7 @@
<span style="margin-left: 4px;">{{ note.reactions[reaction] }}</span>
</button>
</div>
- <MkA v-for="user in users" :key="user.id" :to="userPage(user)">
+ <MkA v-for="user in users" :key="user.id" :to="userPage(user)" @click="dialog.close()">
<MkUserCardMini :user="user" :with-chart="false"/>
</MkA>
</template>
diff --git a/packages/frontend/src/components/MkReactionsViewer.details.vue b/packages/frontend/src/components/MkReactionsViewer.details.vue
index b4210be911..f5e611c62a 100644
--- a/packages/frontend/src/components/MkReactionsViewer.details.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.details.vue
@@ -10,7 +10,7 @@
<MkAvatar :class="$style.avatar" :user="u"/>
<MkUserName :user="u" :nowrap="true"/>
</div>
- <div v-if="users.length > 10">+{{ count - 10 }}</div>
+ <div v-if="users.length > 10" :class="$style.more">+{{ count - 10 }}</div>
</div>
</div>
</MkTooltip>
@@ -50,7 +50,9 @@ function getReactionName(reaction: string): string {
.reaction {
max-width: 100px;
+ padding-right: 10px;
text-align: center;
+ border-right: solid 0.5px var(--divider);
}
.reactionIcon {
@@ -66,25 +68,20 @@ function getReactionName(reaction: string): string {
}
.users {
+ contain: content;
flex: 1;
min-width: 0;
+ margin: -4px 14px 0 10px;
font-size: 0.95em;
- border-left: solid 0.5px var(--divider);
- padding-left: 10px;
- margin-left: 10px;
- margin-right: 14px;
text-align: left;
}
.user {
line-height: 24px;
+ padding-top: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
-
- &:not(:last-child) {
- margin-bottom: 3px;
- }
}
.avatar {
@@ -92,4 +89,8 @@ function getReactionName(reaction: string): string {
height: 24px;
margin-right: 3px;
}
+
+.more {
+ padding-top: 4px;
+}
</style>
diff --git a/packages/frontend/src/components/MkRenotedUsersDialog.vue b/packages/frontend/src/components/MkRenotedUsersDialog.vue
new file mode 100644
index 0000000000..56025535f1
--- /dev/null
+++ b/packages/frontend/src/components/MkRenotedUsersDialog.vue
@@ -0,0 +1,65 @@
+<template>
+<MkModalWindow
+ ref="dialog"
+ :width="400"
+ :height="450"
+ @close="dialog.close()"
+ @closed="emit('closed')"
+>
+ <template #header>{{ i18n.ts.renotesList }}</template>
+
+ <MkSpacer :margin-min="20" :margin-max="28">
+ <div v-if="renotes" class="_gaps">
+ <div v-if="renotes.length === 0" class="_fullinfo">
+ <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <div>{{ i18n.ts.nothing }}</div>
+ </div>
+ <template v-else>
+ <MkA v-for="user in users" :key="user.id" :to="userPage(user)" @click="dialog.close()">
+ <MkUserCardMini :user="user" :with-chart="false"/>
+ </MkA>
+ </template>
+ </div>
+ <div v-else>
+ <MkLoading/>
+ </div>
+ </MkSpacer>
+</MkModalWindow>
+</template>
+
+<script lang="ts" setup>
+import { onMounted } from 'vue';
+import * as misskey from 'misskey-js';
+import MkModalWindow from '@/components/MkModalWindow.vue';
+import MkUserCardMini from '@/components/MkUserCardMini.vue';
+import { userPage } from '@/filters/user';
+import { i18n } from '@/i18n';
+import * as os from '@/os';
+
+const emit = defineEmits<{
+ (ev: 'closed'): void,
+}>();
+
+const props = defineProps<{
+ noteId: misskey.entities.Note['id'];
+}>();
+
+const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
+
+let note = $ref<misskey.entities.Note>();
+let renotes = $ref();
+let users = $ref();
+
+onMounted(async () => {
+ const res = await os.api('notes/renotes', {
+ noteId: props.noteId,
+ limit: 30,
+ });
+
+ renotes = res;
+ users = res.map(x => x.user);
+});
+</script>
+
+<style lang="scss" module>
+</style>
diff --git a/packages/frontend/src/components/MkRetentionHeatmap.vue b/packages/frontend/src/components/MkRetentionHeatmap.vue
index 85c009f746..f33f68cab7 100644
--- a/packages/frontend/src/components/MkRetentionHeatmap.vue
+++ b/packages/frontend/src/components/MkRetentionHeatmap.vue
@@ -44,7 +44,13 @@ async function renderChart() {
const data = [];
for (const record of raw) {
- let i = 0;
+ data.push({
+ x: 0,
+ y: record.createdAt,
+ v: record.users,
+ });
+
+ let i = 1;
for (const date of Object.keys(record.data).sort((a, b) => new Date(a).getTime() - new Date(b).getTime())) {
data.push({
x: i,
@@ -61,8 +67,14 @@ async function renderChart() {
const color = defaultStore.state.darkMode ? '#b4e900' : '#86b300';
- // 視覚上ã®åˆ†ã‹ã‚Šã‚„ã™ã•ã®ãŸã‚上ã‹ã‚‰æœ€ã‚‚大ãã„3ã¤ã®å€¤ã®å¹³å‡ã‚’最大値ã¨ã™ã‚‹
- const max = raw.map(x => x.users).slice().sort((a, b) => b - a).slice(0, 3).reduce((a, b) => a + b, 0) / 3;
+ const getYYYYMMDD = (date: Date) => {
+ const y = date.getFullYear().toString().padStart(2, '0');
+ const m = (date.getMonth() + 1).toString().padStart(2, '0');
+ const d = date.getDate().toString().padStart(2, '0');
+ return `${y}/${m}/${d}`;
+ };
+
+ const max = (createdAt: string) => raw.find(x => x.createdAt === createdAt)!.users;
const marginEachCell = 12;
@@ -78,7 +90,7 @@ async function renderChart() {
borderRadius: 3,
backgroundColor(c) {
const value = c.dataset.data[c.dataIndex].v;
- const a = value / max;
+ const a = value / max(c.dataset.data[c.dataIndex].y);
return alpha(color, a);
},
fill: true,
@@ -115,7 +127,7 @@ async function renderChart() {
maxRotation: 0,
autoSkipPadding: 0,
autoSkip: false,
- callback: (value, index, values) => value + 1,
+ callback: (value, index, values) => value,
},
},
y: {
@@ -150,11 +162,11 @@ async function renderChart() {
callbacks: {
title(context) {
const v = context[0].dataset.data[context[0].dataIndex];
- return v.d;
+ return getYYYYMMDD(new Date(new Date(v.y).getTime() + (v.x * 86400000)));
},
label(context) {
const v = context.dataset.data[context.dataIndex];
- return ['Active: ' + v.v];
+ return [`Active: ${v.v} (${Math.round((v.v / max(v.y)) * 100)}%)`];
},
},
//mode: 'index',
diff --git a/packages/frontend/src/components/MkSample.vue b/packages/frontend/src/components/MkSample.vue
index 7a3bc20888..922b862b47 100644
--- a/packages/frontend/src/components/MkSample.vue
+++ b/packages/frontend/src/components/MkSample.vue
@@ -87,7 +87,7 @@ export default defineComponent({
},
async openDrive() {
- os.selectDriveFile();
+ os.selectDriveFile(false);
},
async selectUser() {
diff --git a/packages/frontend/src/components/MkSignup.vue b/packages/frontend/src/components/MkSignup.vue
deleted file mode 100644
index 30279148f8..0000000000
--- a/packages/frontend/src/components/MkSignup.vue
+++ /dev/null
@@ -1,263 +0,0 @@
-<template>
-<form class="qlvuhzng _gaps_m" autocomplete="new-password" @submit.prevent="onSubmit">
- <MkInput v-if="instance.disableRegistration" v-model="invitationCode" type="text" :spellcheck="false" required>
- <template #label>{{ i18n.ts.invitationCode }}</template>
- <template #prefix><i class="ti ti-key"></i></template>
- </MkInput>
- <MkInput v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" required data-cy-signup-username @update:model-value="onChangeUsername">
- <template #label>{{ i18n.ts.username }} <div v-tooltip:dialog="i18n.ts.usernameInfo" class="_button _help"><i class="ti ti-question-circle"></i></div></template>
- <template #prefix>@</template>
- <template #suffix>@{{ host }}</template>
- <template #caption>
- <div><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.cannotBeChangedLater }}</div>
- <span v-if="usernameState === 'wait'" style="color:#999"><MkLoading :em="true"/> {{ i18n.ts.checking }}</span>
- <span v-else-if="usernameState === 'ok'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.available }}</span>
- <span v-else-if="usernameState === 'unavailable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.unavailable }}</span>
- <span v-else-if="usernameState === 'error'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.error }}</span>
- <span v-else-if="usernameState === 'invalid-format'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.usernameInvalidFormat }}</span>
- <span v-else-if="usernameState === 'min-range'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.tooShort }}</span>
- <span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.tooLong }}</span>
- </template>
- </MkInput>
- <MkInput v-if="instance.emailRequiredForSignup" v-model="email" :debounce="true" type="email" :spellcheck="false" required data-cy-signup-email @update:model-value="onChangeEmail">
- <template #label>{{ i18n.ts.emailAddress }} <div v-tooltip:dialog="i18n.ts._signup.emailAddressInfo" class="_button _help"><i class="ti ti-question-circle"></i></div></template>
- <template #prefix><i class="ti ti-mail"></i></template>
- <template #caption>
- <span v-if="emailState === 'wait'" style="color:#999"><MkLoading :em="true"/> {{ i18n.ts.checking }}</span>
- <span v-else-if="emailState === 'ok'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.available }}</span>
- <span v-else-if="emailState === 'unavailable:used'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.used }}</span>
- <span v-else-if="emailState === 'unavailable:format'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.format }}</span>
- <span v-else-if="emailState === 'unavailable:disposable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.disposable }}</span>
- <span v-else-if="emailState === 'unavailable:mx'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.mx }}</span>
- <span v-else-if="emailState === 'unavailable:smtp'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.smtp }}</span>
- <span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.unavailable }}</span>
- <span v-else-if="emailState === 'error'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.error }}</span>
- </template>
- </MkInput>
- <MkInput v-model="password" type="password" autocomplete="new-password" required data-cy-signup-password @update:model-value="onChangePassword">
- <template #label>{{ i18n.ts.password }}</template>
- <template #prefix><i class="ti ti-lock"></i></template>
- <template #caption>
- <span v-if="passwordStrength == 'low'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.weakPassword }}</span>
- <span v-if="passwordStrength == 'medium'" style="color: var(--warn)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.normalPassword }}</span>
- <span v-if="passwordStrength == 'high'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.strongPassword }}</span>
- </template>
- </MkInput>
- <MkInput v-model="retypedPassword" type="password" autocomplete="new-password" required data-cy-signup-password-retype @update:model-value="onChangePasswordRetype">
- <template #label>{{ i18n.ts.password }} ({{ i18n.ts.retype }})</template>
- <template #prefix><i class="ti ti-lock"></i></template>
- <template #caption>
- <span v-if="passwordRetypeState == 'match'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.passwordMatched }}</span>
- <span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.passwordNotMatched }}</span>
- </template>
- </MkInput>
- <MkSwitch v-model="ToSAgreement" class="tou">
- <template #label>{{ i18n.ts.agreeBelow }}</template>
- </MkSwitch>
- <ul style="margin: 0; padding-left: 2em;">
- <li v-if="instance.tosUrl"><a :href="instance.tosUrl" class="_link" target="_blank">{{ i18n.ts.tos }}</a></li>
- <li><a href="https://misskey-hub.net/docs/notes.html" class="_link" target="_blank">{{ i18n.ts.basicNotesBeforeCreateAccount }}</a></li>
- </ul>
- <MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/>
- <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/>
- <MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" class="captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/>
- <MkButton type="submit" :disabled="shouldDisableSubmitting" gradate data-cy-signup-submit>{{ i18n.ts.start }}</MkButton>
-</form>
-</template>
-
-<script lang="ts" setup>
-import { } from 'vue';
-import getPasswordStrength from 'syuilo-password-strength';
-import { toUnicode } from 'punycode/';
-import MkButton from './MkButton.vue';
-import MkInput from './MkInput.vue';
-import MkSwitch from './MkSwitch.vue';
-import MkCaptcha, { type Captcha } from '@/components/MkCaptcha.vue';
-import * as config from '@/config';
-import * as os from '@/os';
-import { login } from '@/account';
-import { instance } from '@/instance';
-import { i18n } from '@/i18n';
-
-const props = withDefaults(defineProps<{
- autoSet?: boolean;
-}>(), {
- autoSet: false,
-});
-
-const emit = defineEmits<{
- (ev: 'signup', user: Record<string, any>): void;
- (ev: 'signupEmailPending'): void;
-}>();
-
-const host = toUnicode(config.host);
-
-let hcaptcha = $ref<Captcha | undefined>();
-let recaptcha = $ref<Captcha | undefined>();
-let turnstile = $ref<Captcha | undefined>();
-
-let username: string = $ref('');
-let password: string = $ref('');
-let retypedPassword: string = $ref('');
-let invitationCode: string = $ref('');
-let email = $ref('');
-let usernameState: null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range' = $ref(null);
-let emailState: null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error' = $ref(null);
-let passwordStrength: '' | 'low' | 'medium' | 'high' = $ref('');
-let passwordRetypeState: null | 'match' | 'not-match' = $ref(null);
-let submitting: boolean = $ref(false);
-let ToSAgreement: boolean = $ref(false);
-let hCaptchaResponse = $ref(null);
-let reCaptchaResponse = $ref(null);
-let turnstileResponse = $ref(null);
-let usernameAbortController: null | AbortController = $ref(null);
-let emailAbortController: null | AbortController = $ref(null);
-
-const shouldDisableSubmitting = $computed((): boolean => {
- return submitting ||
- instance.tosUrl && !ToSAgreement ||
- instance.enableHcaptcha && !hCaptchaResponse ||
- instance.enableRecaptcha && !reCaptchaResponse ||
- instance.enableTurnstile && !turnstileResponse ||
- instance.emailRequiredForSignup && emailState !== 'ok' ||
- usernameState !== 'ok' ||
- passwordRetypeState !== 'match';
-});
-
-function onChangeUsername(): void {
- if (username === '') {
- usernameState = null;
- return;
- }
-
- {
- const err =
- !username.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' :
- username.length < 1 ? 'min-range' :
- username.length > 20 ? 'max-range' :
- null;
-
- if (err) {
- usernameState = err;
- return;
- }
- }
-
- if (usernameAbortController != null) {
- usernameAbortController.abort();
- }
- usernameState = 'wait';
- usernameAbortController = new AbortController();
-
- os.api('username/available', {
- username,
- }, undefined, usernameAbortController.signal).then(result => {
- usernameState = result.available ? 'ok' : 'unavailable';
- }).catch((err) => {
- if (err.name !== 'AbortError') {
- usernameState = 'error';
- }
- });
-}
-
-function onChangeEmail(): void {
- if (email === '') {
- emailState = null;
- return;
- }
-
- if (emailAbortController != null) {
- emailAbortController.abort();
- }
- emailState = 'wait';
- emailAbortController = new AbortController();
-
- os.api('email-address/available', {
- emailAddress: email,
- }, undefined, emailAbortController.signal).then(result => {
- emailState = result.available ? 'ok' :
- result.reason === 'used' ? 'unavailable:used' :
- result.reason === 'format' ? 'unavailable:format' :
- result.reason === 'disposable' ? 'unavailable:disposable' :
- result.reason === 'mx' ? 'unavailable:mx' :
- result.reason === 'smtp' ? 'unavailable:smtp' :
- 'unavailable';
- }).catch((err) => {
- if (err.name !== 'AbortError') {
- emailState = 'error';
- }
- });
-}
-
-function onChangePassword(): void {
- if (password === '') {
- passwordStrength = '';
- return;
- }
-
- const strength = getPasswordStrength(password);
- passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
-}
-
-function onChangePasswordRetype(): void {
- if (retypedPassword === '') {
- passwordRetypeState = null;
- return;
- }
-
- passwordRetypeState = password === retypedPassword ? 'match' : 'not-match';
-}
-
-async function onSubmit(): Promise<void> {
- if (submitting) return;
- submitting = true;
-
- try {
- await os.api('signup', {
- username,
- password,
- emailAddress: email,
- invitationCode,
- 'hcaptcha-response': hCaptchaResponse,
- 'g-recaptcha-response': reCaptchaResponse,
- 'turnstile-response': turnstileResponse,
- });
- if (instance.emailRequiredForSignup) {
- os.alert({
- type: 'success',
- title: i18n.ts._signup.almostThere,
- text: i18n.t('_signup.emailSent', { email }),
- });
- emit('signupEmailPending');
- } else {
- const res = await os.api('signin', {
- username,
- password,
- });
- emit('signup', res);
-
- if (props.autoSet) {
- return login(res.i);
- }
- }
- } catch {
- submitting = false;
- hcaptcha?.reset?.();
- recaptcha?.reset?.();
- turnstile?.reset?.();
-
- os.alert({
- type: 'error',
- text: i18n.ts.somethingHappened,
- });
- }
-}
-</script>
-
-<style lang="scss" scoped>
-.qlvuhzng {
- .captcha {
- margin: 16px 0;
- }
-}
-</style>
diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue
new file mode 100644
index 0000000000..0e8bdb321e
--- /dev/null
+++ b/packages/frontend/src/components/MkSignupDialog.form.vue
@@ -0,0 +1,272 @@
+<template>
+<div>
+ <div :class="$style.banner">
+ <i class="ti ti-user-edit"></i>
+ </div>
+ <MkSpacer :margin-min="20" :margin-max="32">
+ <form class="_gaps_m" autocomplete="new-password" @submit.prevent="onSubmit">
+ <MkInput v-if="instance.disableRegistration" v-model="invitationCode" type="text" :spellcheck="false" required>
+ <template #label>{{ i18n.ts.invitationCode }}</template>
+ <template #prefix><i class="ti ti-key"></i></template>
+ </MkInput>
+ <MkInput v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" required data-cy-signup-username @update:model-value="onChangeUsername">
+ <template #label>{{ i18n.ts.username }} <div v-tooltip:dialog="i18n.ts.usernameInfo" class="_button _help"><i class="ti ti-help-circle"></i></div></template>
+ <template #prefix>@</template>
+ <template #suffix>@{{ host }}</template>
+ <template #caption>
+ <div><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.cannotBeChangedLater }}</div>
+ <span v-if="usernameState === 'wait'" style="color:#999"><MkLoading :em="true"/> {{ i18n.ts.checking }}</span>
+ <span v-else-if="usernameState === 'ok'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.available }}</span>
+ <span v-else-if="usernameState === 'unavailable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.unavailable }}</span>
+ <span v-else-if="usernameState === 'error'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.error }}</span>
+ <span v-else-if="usernameState === 'invalid-format'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.usernameInvalidFormat }}</span>
+ <span v-else-if="usernameState === 'min-range'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.tooShort }}</span>
+ <span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.tooLong }}</span>
+ </template>
+ </MkInput>
+ <MkInput v-if="instance.emailRequiredForSignup" v-model="email" :debounce="true" type="email" :spellcheck="false" required data-cy-signup-email @update:model-value="onChangeEmail">
+ <template #label>{{ i18n.ts.emailAddress }} <div v-tooltip:dialog="i18n.ts._signup.emailAddressInfo" class="_button _help"><i class="ti ti-help-circle"></i></div></template>
+ <template #prefix><i class="ti ti-mail"></i></template>
+ <template #caption>
+ <span v-if="emailState === 'wait'" style="color:#999"><MkLoading :em="true"/> {{ i18n.ts.checking }}</span>
+ <span v-else-if="emailState === 'ok'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.available }}</span>
+ <span v-else-if="emailState === 'unavailable:used'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.used }}</span>
+ <span v-else-if="emailState === 'unavailable:format'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.format }}</span>
+ <span v-else-if="emailState === 'unavailable:disposable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.disposable }}</span>
+ <span v-else-if="emailState === 'unavailable:mx'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.mx }}</span>
+ <span v-else-if="emailState === 'unavailable:smtp'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.smtp }}</span>
+ <span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.unavailable }}</span>
+ <span v-else-if="emailState === 'error'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.error }}</span>
+ </template>
+ </MkInput>
+ <MkInput v-model="password" type="password" autocomplete="new-password" required data-cy-signup-password @update:model-value="onChangePassword">
+ <template #label>{{ i18n.ts.password }}</template>
+ <template #prefix><i class="ti ti-lock"></i></template>
+ <template #caption>
+ <span v-if="passwordStrength == 'low'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.weakPassword }}</span>
+ <span v-if="passwordStrength == 'medium'" style="color: var(--warn)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.normalPassword }}</span>
+ <span v-if="passwordStrength == 'high'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.strongPassword }}</span>
+ </template>
+ </MkInput>
+ <MkInput v-model="retypedPassword" type="password" autocomplete="new-password" required data-cy-signup-password-retype @update:model-value="onChangePasswordRetype">
+ <template #label>{{ i18n.ts.password }} ({{ i18n.ts.retype }})</template>
+ <template #prefix><i class="ti ti-lock"></i></template>
+ <template #caption>
+ <span v-if="passwordRetypeState == 'match'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.passwordMatched }}</span>
+ <span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.passwordNotMatched }}</span>
+ </template>
+ </MkInput>
+ <MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :class="$style.captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/>
+ <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" :class="$style.captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/>
+ <MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" :class="$style.captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/>
+ <MkButton type="submit" :disabled="shouldDisableSubmitting" large gradate rounded data-cy-signup-submit style="margin: 0 auto;">
+ <template v-if="submitting">
+ <MkLoading :em="true" :colored="false"/>
+ </template>
+ <template v-else>{{ i18n.ts.start }}</template>
+ </MkButton>
+ </form>
+ </MkSpacer>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { } from 'vue';
+import getPasswordStrength from 'syuilo-password-strength';
+import { toUnicode } from 'punycode/';
+import MkButton from './MkButton.vue';
+import MkInput from './MkInput.vue';
+import MkSwitch from './MkSwitch.vue';
+import MkCaptcha, { type Captcha } from '@/components/MkCaptcha.vue';
+import * as config from '@/config';
+import * as os from '@/os';
+import { login } from '@/account';
+import { instance } from '@/instance';
+import { i18n } from '@/i18n';
+
+const props = withDefaults(defineProps<{
+ autoSet?: boolean;
+}>(), {
+ autoSet: false,
+});
+
+const emit = defineEmits<{
+ (ev: 'signup', user: Record<string, any>): void;
+ (ev: 'signupEmailPending'): void;
+}>();
+
+const host = toUnicode(config.host);
+
+let hcaptcha = $ref<Captcha | undefined>();
+let recaptcha = $ref<Captcha | undefined>();
+let turnstile = $ref<Captcha | undefined>();
+
+let username: string = $ref('');
+let password: string = $ref('');
+let retypedPassword: string = $ref('');
+let invitationCode: string = $ref('');
+let email = $ref('');
+let usernameState: null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range' = $ref(null);
+let emailState: null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error' = $ref(null);
+let passwordStrength: '' | 'low' | 'medium' | 'high' = $ref('');
+let passwordRetypeState: null | 'match' | 'not-match' = $ref(null);
+let submitting: boolean = $ref(false);
+let hCaptchaResponse = $ref(null);
+let reCaptchaResponse = $ref(null);
+let turnstileResponse = $ref(null);
+let usernameAbortController: null | AbortController = $ref(null);
+let emailAbortController: null | AbortController = $ref(null);
+
+const shouldDisableSubmitting = $computed((): boolean => {
+ return submitting ||
+ instance.enableHcaptcha && !hCaptchaResponse ||
+ instance.enableRecaptcha && !reCaptchaResponse ||
+ instance.enableTurnstile && !turnstileResponse ||
+ instance.emailRequiredForSignup && emailState !== 'ok' ||
+ usernameState !== 'ok' ||
+ passwordRetypeState !== 'match';
+});
+
+function onChangeUsername(): void {
+ if (username === '') {
+ usernameState = null;
+ return;
+ }
+
+ {
+ const err =
+ !username.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' :
+ username.length < 1 ? 'min-range' :
+ username.length > 20 ? 'max-range' :
+ null;
+
+ if (err) {
+ usernameState = err;
+ return;
+ }
+ }
+
+ if (usernameAbortController != null) {
+ usernameAbortController.abort();
+ }
+ usernameState = 'wait';
+ usernameAbortController = new AbortController();
+
+ os.api('username/available', {
+ username,
+ }, undefined, usernameAbortController.signal).then(result => {
+ usernameState = result.available ? 'ok' : 'unavailable';
+ }).catch((err) => {
+ if (err.name !== 'AbortError') {
+ usernameState = 'error';
+ }
+ });
+}
+
+function onChangeEmail(): void {
+ if (email === '') {
+ emailState = null;
+ return;
+ }
+
+ if (emailAbortController != null) {
+ emailAbortController.abort();
+ }
+ emailState = 'wait';
+ emailAbortController = new AbortController();
+
+ os.api('email-address/available', {
+ emailAddress: email,
+ }, undefined, emailAbortController.signal).then(result => {
+ emailState = result.available ? 'ok' :
+ result.reason === 'used' ? 'unavailable:used' :
+ result.reason === 'format' ? 'unavailable:format' :
+ result.reason === 'disposable' ? 'unavailable:disposable' :
+ result.reason === 'mx' ? 'unavailable:mx' :
+ result.reason === 'smtp' ? 'unavailable:smtp' :
+ 'unavailable';
+ }).catch((err) => {
+ if (err.name !== 'AbortError') {
+ emailState = 'error';
+ }
+ });
+}
+
+function onChangePassword(): void {
+ if (password === '') {
+ passwordStrength = '';
+ return;
+ }
+
+ const strength = getPasswordStrength(password);
+ passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
+}
+
+function onChangePasswordRetype(): void {
+ if (retypedPassword === '') {
+ passwordRetypeState = null;
+ return;
+ }
+
+ passwordRetypeState = password === retypedPassword ? 'match' : 'not-match';
+}
+
+async function onSubmit(): Promise<void> {
+ if (submitting) return;
+ submitting = true;
+
+ try {
+ await os.api('signup', {
+ username,
+ password,
+ emailAddress: email,
+ invitationCode,
+ 'hcaptcha-response': hCaptchaResponse,
+ 'g-recaptcha-response': reCaptchaResponse,
+ 'turnstile-response': turnstileResponse,
+ });
+ if (instance.emailRequiredForSignup) {
+ os.alert({
+ type: 'success',
+ title: i18n.ts._signup.almostThere,
+ text: i18n.t('_signup.emailSent', { email }),
+ });
+ emit('signupEmailPending');
+ } else {
+ const res = await os.api('signin', {
+ username,
+ password,
+ });
+ emit('signup', res);
+
+ if (props.autoSet) {
+ return login(res.i);
+ }
+ }
+ } catch {
+ submitting = false;
+ hcaptcha?.reset?.();
+ recaptcha?.reset?.();
+ turnstile?.reset?.();
+
+ os.alert({
+ type: 'error',
+ text: i18n.ts.somethingHappened,
+ });
+ }
+}
+</script>
+
+<style lang="scss" module>
+.banner {
+ padding: 16px;
+ text-align: center;
+ font-size: 26px;
+ background-color: var(--accentedBg);
+ color: var(--accent);
+}
+
+.captcha {
+ margin: 16px 0;
+}
+</style>
diff --git a/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts b/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts
new file mode 100644
index 0000000000..2d95455730
--- /dev/null
+++ b/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts
@@ -0,0 +1,94 @@
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { expect } from '@storybook/jest';
+import { userEvent, waitFor, within } from '@storybook/testing-library';
+import { StoryObj } from '@storybook/vue3';
+import { onBeforeUnmount } from 'vue';
+import MkSignupServerRules from './MkSignupDialog.rules.vue';
+import { i18n } from '@/i18n';
+import { instance } from '@/instance';
+export const Empty = {
+ render(args) {
+ return {
+ components: {
+ MkSignupServerRules,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkSignupServerRules v-bind="props" />',
+ };
+ },
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+ const groups = await canvas.findAllByRole('group');
+ const buttons = await canvas.findAllByRole('button');
+ for (const group of groups) {
+ if (group.ariaExpanded === 'true') {
+ continue;
+ }
+ const button = await within(group).findByRole('button');
+ userEvent.click(button);
+ await waitFor(() => expect(group).toHaveAttribute('aria-expanded', 'true'));
+ }
+ const labels = await canvas.findAllByText(i18n.ts.agree);
+ for (const label of labels) {
+ expect(buttons.at(-1)).toBeDisabled();
+ await waitFor(() => userEvent.click(label));
+ }
+ expect(buttons.at(-1)).toBeEnabled();
+ },
+ args: {
+ serverRules: [],
+ tosUrl: null,
+ },
+ decorators: [
+ (_, context) => ({
+ setup() {
+ instance.serverRules = context.args.serverRules;
+ instance.tosUrl = context.args.tosUrl;
+ onBeforeUnmount(() => {
+ // FIXME: 呼ã³å‡ºã•れãªã„
+ instance.serverRules = [];
+ instance.tosUrl = null;
+ });
+ },
+ template: '<story/>',
+ }),
+ ],
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkSignupServerRules>;
+export const ServerRulesOnly = {
+ ...Empty,
+ args: {
+ ...Empty.args,
+ serverRules: [
+ 'ルール',
+ ],
+ },
+} satisfies StoryObj<typeof MkSignupServerRules>;
+export const TOSOnly = {
+ ...Empty,
+ args: {
+ ...Empty.args,
+ tosUrl: 'https://example.com/tos',
+ },
+} satisfies StoryObj<typeof MkSignupServerRules>;
+export const ServerRulesAndTOS = {
+ ...Empty,
+ args: {
+ ...Empty.args,
+ serverRules: ServerRulesOnly.args.serverRules,
+ tosUrl: TOSOnly.args.tosUrl,
+ },
+} satisfies StoryObj<typeof MkSignupServerRules>;
diff --git a/packages/frontend/src/components/MkSignupDialog.rules.vue b/packages/frontend/src/components/MkSignupDialog.rules.vue
new file mode 100644
index 0000000000..6da81c3bcb
--- /dev/null
+++ b/packages/frontend/src/components/MkSignupDialog.rules.vue
@@ -0,0 +1,124 @@
+<template>
+<div>
+ <div :class="$style.banner">
+ <i class="ti ti-checklist"></i>
+ </div>
+ <MkSpacer :margin-min="20" :margin-max="28">
+ <div class="_gaps_m">
+ <div v-if="instance.disableRegistration">
+ <MkInfo warn>{{ i18n.ts.invitationRequiredToRegister }}</MkInfo>
+ </div>
+
+ <div style="text-align: center;">{{ i18n.ts.pleaseConfirmBelowBeforeSignup }}</div>
+
+ <MkFolder v-if="availableServerRules" :default-open="true">
+ <template #label>{{ i18n.ts.serverRules }}</template>
+ <template #suffix><i v-if="agreeServerRules" class="ti ti-check" style="color: var(--success)"></i></template>
+
+ <ol class="_gaps_s" :class="$style.rules">
+ <li v-for="item in instance.serverRules" :class="$style.rule"><div :class="$style.ruleText" v-html="item"></div></li>
+ </ol>
+
+ <MkSwitch v-model="agreeServerRules" style="margin-top: 16px;">{{ i18n.ts.agree }}</MkSwitch>
+ </MkFolder>
+
+ <MkFolder v-if="availableTos" :default-open="true">
+ <template #label>{{ i18n.ts.termsOfService }}</template>
+ <template #suffix><i v-if="agreeTos" class="ti ti-check" style="color: var(--success)"></i></template>
+
+ <a :href="instance.tosUrl" class="_link" target="_blank">{{ i18n.ts.termsOfService }} <i class="ti ti-external-link"></i></a>
+
+ <MkSwitch v-model="agreeTos" style="margin-top: 16px;">{{ i18n.ts.agree }}</MkSwitch>
+ </MkFolder>
+
+ <MkFolder :default-open="true">
+ <template #label>{{ i18n.ts.basicNotesBeforeCreateAccount }}</template>
+ <template #suffix><i v-if="agreeNote" class="ti ti-check" style="color: var(--success)"></i></template>
+
+ <a href="https://misskey-hub.net/docs/notes.html" class="_link" target="_blank">{{ i18n.ts.basicNotesBeforeCreateAccount }} <i class="ti ti-external-link"></i></a>
+
+ <MkSwitch v-model="agreeNote" style="margin-top: 16px;" data-cy-signup-rules-notes-agree>{{ i18n.ts.agree }}</MkSwitch>
+ </MkFolder>
+
+ <div v-if="!agreed" style="text-align: center;">{{ i18n.ts.pleaseAgreeAllToContinue }}</div>
+
+ <div class="_buttonsCenter">
+ <MkButton inline rounded @click="emit('cancel')">{{ i18n.ts.cancel }}</MkButton>
+ <MkButton inline primary rounded gradate :disabled="!agreed" data-cy-signup-rules-continue @click="emit('done')">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
+ </div>
+ </div>
+ </MkSpacer>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref } from 'vue';
+import { instance } from '@/instance';
+import { i18n } from '@/i18n';
+import MkButton from '@/components/MkButton.vue';
+import MkFolder from '@/components/MkFolder.vue';
+import MkSwitch from '@/components/MkSwitch.vue';
+import MkInfo from '@/components/MkInfo.vue';
+
+const availableServerRules = instance.serverRules.length > 0;
+const availableTos = instance.tosUrl != null;
+
+const agreeServerRules = ref(false);
+const agreeTos = ref(false);
+const agreeNote = ref(false);
+
+const agreed = computed(() => {
+ return (!availableServerRules || agreeServerRules.value) && (!availableTos || agreeTos.value) && agreeNote.value;
+});
+
+const emit = defineEmits<{
+ (ev: 'cancel'): void;
+ (ev: 'done'): void;
+}>();
+</script>
+
+<style lang="scss" module>
+.banner {
+ padding: 16px;
+ text-align: center;
+ font-size: 26px;
+ background-color: var(--accentedBg);
+ color: var(--accent);
+}
+
+.rules {
+ counter-reset: item;
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.rule {
+ display: flex;
+ gap: 8px;
+ word-break: break-word;
+
+ &::before {
+ flex-shrink: 0;
+ display: flex;
+ position: sticky;
+ top: calc(var(--stickyTop, 0px) + 8px);
+ counter-increment: item;
+ content: counter(item);
+ width: 32px;
+ height: 32px;
+ line-height: 32px;
+ background-color: var(--accentedBg);
+ color: var(--accent);
+ font-size: 13px;
+ font-weight: bold;
+ align-items: center;
+ justify-content: center;
+ border-radius: 999px;
+ }
+}
+
+.ruleText {
+ padding-top: 6px;
+}
+</style>
diff --git a/packages/frontend/src/components/MkSignupDialog.vue b/packages/frontend/src/components/MkSignupDialog.vue
index 790c1e94df..17f8b86425 100644
--- a/packages/frontend/src/components/MkSignupDialog.vue
+++ b/packages/frontend/src/components/MkSignupDialog.vue
@@ -1,24 +1,40 @@
<template>
<MkModalWindow
ref="dialog"
- :width="366"
- :height="500"
+ :width="500"
+ :height="600"
@close="dialog.close()"
@closed="$emit('closed')"
>
<template #header>{{ i18n.ts.signup }}</template>
- <MkSpacer :margin-min="20" :margin-max="28">
- <XSignup :auto-set="autoSet" @signup="onSignup" @signup-email-pending="onSignupEmailPending"/>
- </MkSpacer>
+ <div style="overflow-x: clip;">
+ <Transition
+ mode="out-in"
+ :enter-active-class="$style.transition_x_enterActive"
+ :leave-active-class="$style.transition_x_leaveActive"
+ :enter-from-class="$style.transition_x_enterFrom"
+ :leave-to-class="$style.transition_x_leaveTo"
+ >
+ <template v-if="!isAcceptedServerRule">
+ <XServerRules @done="isAcceptedServerRule = true" @cancel="dialog.close()"/>
+ </template>
+ <template v-else>
+ <XSignup :auto-set="autoSet" @signup="onSignup" @signup-email-pending="onSignupEmailPending"/>
+ </template>
+ </Transition>
+ </div>
</MkModalWindow>
</template>
<script lang="ts" setup>
import { } from 'vue';
-import XSignup from '@/components/MkSignup.vue';
+import { $ref } from 'vue/macros';
+import XSignup from '@/components/MkSignupDialog.form.vue';
+import XServerRules from '@/components/MkSignupDialog.rules.vue';
import MkModalWindow from '@/components/MkModalWindow.vue';
import { i18n } from '@/i18n';
+import { instance } from '@/instance';
const props = withDefaults(defineProps<{
autoSet?: boolean;
@@ -33,6 +49,8 @@ const emit = defineEmits<{
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
+const isAcceptedServerRule = $ref(false);
+
function onSignup(res) {
emit('done', res);
dialog.close();
@@ -42,3 +60,18 @@ function onSignupEmailPending() {
dialog.close();
}
</script>
+
+<style lang="scss" module>
+.transition_x_enterActive,
+.transition_x_leaveActive {
+ transition: opacity 0.3s cubic-bezier(0,0,.35,1), transform 0.3s cubic-bezier(0,0,.35,1);
+}
+.transition_x_enterFrom {
+ opacity: 0;
+ transform: translateX(50px);
+}
+.transition_x_leaveTo {
+ opacity: 0;
+ transform: translateX(-50px);
+}
+</style>
diff --git a/packages/frontend/src/components/MkSwitch.vue b/packages/frontend/src/components/MkSwitch.vue
index 8bb8637dda..d9f6716f92 100644
--- a/packages/frontend/src/components/MkSwitch.vue
+++ b/packages/frontend/src/components/MkSwitch.vue
@@ -9,7 +9,7 @@
:disabled="disabled"
@keydown.enter="toggle"
>
- <span ref="button" v-tooltip="checked ? i18n.ts.itsOn : i18n.ts.itsOff" class="button" @click.prevent="toggle">
+ <span ref="button" v-tooltip="checked ? i18n.ts.itsOn : i18n.ts.itsOff" class="button" data-cy-switch-toggle @click.prevent="toggle">
<div class="knob"></div>
</span>
<span class="label">
diff --git a/packages/frontend/src/components/MkUserInfo.vue b/packages/frontend/src/components/MkUserInfo.vue
index 5086c1b319..6349ada65a 100644
--- a/packages/frontend/src/components/MkUserInfo.vue
+++ b/packages/frontend/src/components/MkUserInfo.vue
@@ -1,30 +1,30 @@
<template>
-<div class="_panel vjnjpkug">
- <div class="banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''"></div>
- <MkAvatar class="avatar" :user="user" indicator/>
- <div class="title">
- <MkA class="name" :to="userPage(user)"><MkUserName :user="user" :nowrap="false"/></MkA>
- <p class="username"><MkAcct :user="user"/></p>
+<div class="_panel" :class="$style.root">
+ <div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''"></div>
+ <MkAvatar :class="$style.avatar" :user="user" indicator/>
+ <div :class="$style.title">
+ <MkA :class="$style.name" :to="userPage(user)"><MkUserName :user="user" :nowrap="false"/></MkA>
+ <p :class="$style.username"><MkAcct :user="user"/></p>
</div>
- <span v-if="$i && $i.id !== user.id && user.isFollowed" class="followed">{{ i18n.ts.followsYou }}</span>
- <div class="description">
+ <span v-if="$i && $i.id !== user.id && user.isFollowed" :class="$style.followed">{{ i18n.ts.followsYou }}</span>
+ <div :class="$style.description">
<div v-if="user.description" class="mfm">
<Mfm :text="user.description" :author="user" :i="$i"/>
</div>
<span v-else style="opacity: 0.7;">{{ i18n.ts.noAccountDescription }}</span>
</div>
- <div class="status">
- <div>
- <p>{{ i18n.ts.notes }}</p><span>{{ user.notesCount }}</span>
+ <div :class="$style.status">
+ <div :class="$style.statusItem">
+ <p :class="$style.statusItemLabel">{{ i18n.ts.notes }}</p><span :class="$style.statusItemValue">{{ user.notesCount }}</span>
</div>
- <div>
- <p>{{ i18n.ts.following }}</p><span>{{ user.followingCount }}</span>
+ <div :class="$style.statusItem">
+ <p :class="$style.statusItemLabel">{{ i18n.ts.following }}</p><span :class="$style.statusItemValue">{{ user.followingCount }}</span>
</div>
- <div>
- <p>{{ i18n.ts.followers }}</p><span>{{ user.followersCount }}</span>
+ <div :class="$style.statusItem">
+ <p :class="$style.statusItemLabel">{{ i18n.ts.followers }}</p><span :class="$style.statusItemValue">{{ user.followersCount }}</span>
</div>
</div>
- <MkFollowButton v-if="$i && user.id != $i.id" class="koudoku-button" :user="user" mini/>
+ <MkFollowButton v-if="$i && user.id != $i.id" :class="$style.follow" :user="user" mini/>
</div>
</template>
@@ -40,99 +40,99 @@ defineProps<{
}>();
</script>
-<style lang="scss" scoped>
-.vjnjpkug {
+<style lang="scss" module>
+.root {
position: relative;
+}
- > .banner {
- height: 84px;
- background-color: rgba(0, 0, 0, 0.1);
- background-size: cover;
- background-position: center;
- }
+.banner {
+ height: 84px;
+ background-color: rgba(0, 0, 0, 0.1);
+ background-size: cover;
+ background-position: center;
+}
- > .avatar {
- display: block;
- position: absolute;
- top: 62px;
- left: 13px;
- z-index: 2;
- width: 58px;
- height: 58px;
- border: solid 4px var(--panel);
- }
+.avatar {
+ display: block;
+ position: absolute;
+ top: 62px;
+ left: 13px;
+ z-index: 2;
+ width: 58px;
+ height: 58px;
+ border: solid 4px var(--panel);
+}
- > .title {
- display: block;
- padding: 10px 0 10px 88px;
+.title {
+ display: block;
+ padding: 10px 0 10px 88px;
+}
- > .name {
- display: inline-block;
- margin: 0;
- font-weight: bold;
- line-height: 16px;
- word-break: break-all;
- }
+.name {
+ display: inline-block;
+ margin: 0;
+ font-weight: bold;
+ line-height: 16px;
+ word-break: break-all;
+}
- > .username {
- display: block;
- margin: 0;
- line-height: 16px;
- font-size: 0.8em;
- color: var(--fg);
- opacity: 0.7;
- }
- }
-
- > .followed {
- position: absolute;
- top: 12px;
- left: 12px;
- padding: 4px 8px;
- color: #fff;
- background: rgba(0, 0, 0, 0.7);
- font-size: 0.7em;
- border-radius: 6px;
- }
-
- > .description {
- padding: 16px;
- font-size: 0.8em;
- border-top: solid 0.5px var(--divider);
+.username {
+ display: block;
+ margin: 0;
+ line-height: 16px;
+ font-size: 0.8em;
+ color: var(--fg);
+ opacity: 0.7;
+}
- > .mfm {
- display: -webkit-box;
- -webkit-line-clamp: 3;
- -webkit-box-orient: vertical;
- overflow: hidden;
- }
- }
+.followed {
+ position: absolute;
+ top: 12px;
+ left: 12px;
+ padding: 4px 8px;
+ color: #fff;
+ background: rgba(0, 0, 0, 0.7);
+ font-size: 0.7em;
+ border-radius: 6px;
+}
- > .status {
- padding: 10px 16px;
- border-top: solid 0.5px var(--divider);
+.description {
+ padding: 16px;
+ font-size: 0.8em;
+ border-top: solid 0.5px var(--divider);
+}
- > div {
- display: inline-block;
- width: 33%;
+.mfm {
+ display: -webkit-box;
+ -webkit-line-clamp: 3;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+}
+
+.status {
+ padding: 10px 16px;
+ border-top: solid 0.5px var(--divider);
+}
- > p {
- margin: 0;
- font-size: 0.7em;
- color: var(--fg);
- }
+.statusItem {
+ display: inline-block;
+ width: 33%;
+}
- > span {
- font-size: 1em;
- color: var(--accent);
- }
- }
- }
+.statusItemLabel {
+ margin: 0;
+ font-size: 0.7em;
+ color: var(--fg);
+}
+
+.statusItemValue {
+ font-size: 1em;
+ color: var(--accent);
+}
- > .koudoku-button {
- position: absolute;
- top: 8px;
- right: 8px;
- }
+.follow {
+ position: absolute;
+ top: 8px;
+ right: 8px;
}
</style>
diff --git a/packages/frontend/src/components/MkUserList.vue b/packages/frontend/src/components/MkUserList.vue
index 51eb426e97..3571ca84d9 100644
--- a/packages/frontend/src/components/MkUserList.vue
+++ b/packages/frontend/src/components/MkUserList.vue
@@ -8,7 +8,7 @@
</template>
<template #default="{ items }">
- <div class="efvhhmdq">
+ <div :class="$style.root">
<MkUserInfo v-for="item in items" :key="item.id" class="user" :user="extractor(item)"/>
</div>
</template>
@@ -29,8 +29,8 @@ const props = withDefaults(defineProps<{
});
</script>
-<style lang="scss" scoped>
-.efvhhmdq {
+<style lang="scss" module>
+.root {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
grid-gap: var(--margin);
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts
new file mode 100644
index 0000000000..7d5a65f41a
--- /dev/null
+++ b/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts
@@ -0,0 +1,51 @@
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { StoryObj } from '@storybook/vue3';
+import { rest } from 'msw';
+import { commonHandlers } from '../../.storybook/mocks';
+import { userDetailed } from '../../.storybook/fakes';
+import MkUserSetupDialog_Follow from './MkUserSetupDialog.Follow.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkUserSetupDialog_Follow,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkUserSetupDialog_Follow v-bind="props" />',
+ };
+ },
+ args: {
+
+ },
+ parameters: {
+ layout: 'centered',
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ rest.post('/api/users', (req, res, ctx) => {
+ return res(ctx.json([
+ userDetailed('44'),
+ userDetailed('49'),
+ ]));
+ }),
+ rest.post('/api/pinned-users', (req, res, ctx) => {
+ return res(ctx.json([
+ userDetailed('44'),
+ userDetailed('49'),
+ ]));
+ }),
+ ],
+ },
+ },
+} satisfies StoryObj<typeof MkUserSetupDialog_Follow>;
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Follow.vue b/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
new file mode 100644
index 0000000000..b89e3e4c9d
--- /dev/null
+++ b/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
@@ -0,0 +1,63 @@
+<template>
+<div class="_gaps">
+ <div style="text-align: center;">{{ i18n.ts._initialAccountSetting.followUsers }}</div>
+
+ <MkFolder :default-open="true">
+ <template #label>{{ i18n.ts.recommended }}</template>
+
+ <MkPagination :pagination="pinnedUsers">
+ <template #default="{ items }">
+ <div :class="$style.users">
+ <XUser v-for="item in items" :key="item.id" :user="item"/>
+ </div>
+ </template>
+ </MkPagination>
+ </MkFolder>
+
+ <MkFolder :default-open="true">
+ <template #label>{{ i18n.ts.popularUsers }}</template>
+
+ <MkPagination :pagination="popularUsers">
+ <template #default="{ items }">
+ <div :class="$style.users">
+ <XUser v-for="item in items" :key="item.id" :user="item"/>
+ </div>
+ </template>
+ </MkPagination>
+ </MkFolder>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref, watch } from 'vue';
+import { instance } from '@/instance';
+import { i18n } from '@/i18n';
+import MkButton from '@/components/MkButton.vue';
+import MkFolder from '@/components/MkFolder.vue';
+import XUser from '@/components/MkUserSetupDialog.User.vue';
+import MkInfo from '@/components/MkInfo.vue';
+import * as os from '@/os';
+import { $i } from '@/account';
+import MkPagination from '@/components/MkPagination.vue';
+
+const emit = defineEmits<{
+ (ev: 'done'): void;
+}>();
+
+const pinnedUsers = { endpoint: 'pinned-users', noPaging: true };
+
+const popularUsers = { endpoint: 'users', limit: 10, noPaging: true, params: {
+ state: 'alive',
+ origin: 'local',
+ sort: '+follower',
+} };
+</script>
+
+<style lang="scss" module>
+.users {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
+ grid-gap: var(--margin);
+ justify-content: center;
+}
+</style>
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Profile.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.Profile.stories.impl.ts
new file mode 100644
index 0000000000..f4930aa26b
--- /dev/null
+++ b/packages/frontend/src/components/MkUserSetupDialog.Profile.stories.impl.ts
@@ -0,0 +1,31 @@
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { StoryObj } from '@storybook/vue3';
+import MkUserSetupDialog_Profile from './MkUserSetupDialog.Profile.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkUserSetupDialog_Profile,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkUserSetupDialog_Profile v-bind="props" />',
+ };
+ },
+ args: {
+
+ },
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkUserSetupDialog_Profile>;
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
new file mode 100644
index 0000000000..adb8d43349
--- /dev/null
+++ b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
@@ -0,0 +1,101 @@
+<template>
+<div class="_gaps">
+ <MkInfo>{{ i18n.ts._initialAccountSetting.theseSettingsCanEditLater }}</MkInfo>
+
+ <FormSlot>
+ <template #label>{{ i18n.ts.avatar }}</template>
+ <div v-adaptive-bg :class="$style.avatarSection" class="_panel">
+ <MkAvatar :class="$style.avatar" :user="$i" @click="setAvatar"/>
+ <div style="margin-top: 16px;">
+ <MkButton primary rounded inline @click="setAvatar">{{ i18n.ts._profile.changeAvatar }}</MkButton>
+ </div>
+ </div>
+ </FormSlot>
+
+ <MkInput v-model="name" :max="30" manual-save data-cy-user-setup-user-name>
+ <template #label>{{ i18n.ts._profile.name }}</template>
+ </MkInput>
+
+ <MkTextarea v-model="description" :max="500" tall manual-save data-cy-user-setup-user-description>
+ <template #label>{{ i18n.ts._profile.description }}</template>
+ </MkTextarea>
+
+ <MkInfo>{{ i18n.ts._initialAccountSetting.youCanEditMoreSettingsInSettingsPageLater }}</MkInfo>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref, watch } from 'vue';
+import { instance } from '@/instance';
+import { i18n } from '@/i18n';
+import MkButton from '@/components/MkButton.vue';
+import MkInput from '@/components/MkInput.vue';
+import MkTextarea from '@/components/MkTextarea.vue';
+import FormSlot from '@/components/form/slot.vue';
+import MkInfo from '@/components/MkInfo.vue';
+import { chooseFileFromPc } from '@/scripts/select-file';
+import * as os from '@/os';
+import { $i } from '@/account';
+
+const emit = defineEmits<{
+ (ev: 'done'): void;
+}>();
+
+const name = ref('');
+const description = ref('');
+
+watch(name, () => {
+ os.apiWithDialog('i/update', {
+ // 空文字列をnullã«ã—ãŸã„ã®ã§??ã¯ä½¿ã†ãª
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ name: name.value || null,
+ });
+});
+
+watch(description, () => {
+ os.apiWithDialog('i/update', {
+ // 空文字列をnullã«ã—ãŸã„ã®ã§??ã¯ä½¿ã†ãª
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ description: description.value || null,
+ });
+});
+
+function setAvatar(ev) {
+ chooseFileFromPc(false).then(async (files) => {
+ const file = files[0];
+
+ let originalOrCropped = file;
+
+ const { canceled } = await os.confirm({
+ type: 'question',
+ text: i18n.t('cropImageAsk'),
+ okText: i18n.ts.cropYes,
+ cancelText: i18n.ts.cropNo,
+ });
+
+ if (!canceled) {
+ originalOrCropped = await os.cropImage(file, {
+ aspectRatio: 1,
+ });
+ }
+
+ const i = await os.apiWithDialog('i/update', {
+ avatarId: originalOrCropped.id,
+ });
+ $i.avatarId = i.avatarId;
+ $i.avatarUrl = i.avatarUrl;
+ });
+}
+</script>
+
+<style lang="scss" module>
+.avatarSection {
+ text-align: center;
+ padding: 20px;
+}
+
+.avatar {
+ width: 100px;
+ height: 100px;
+}
+</style>
diff --git a/packages/frontend/src/components/MkUserSetupDialog.User.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.User.stories.impl.ts
new file mode 100644
index 0000000000..7413f4884b
--- /dev/null
+++ b/packages/frontend/src/components/MkUserSetupDialog.User.stories.impl.ts
@@ -0,0 +1,32 @@
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { StoryObj } from '@storybook/vue3';
+import { userDetailed } from '../../.storybook/fakes';
+import MkUserSetupDialog_User from './MkUserSetupDialog.User.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkUserSetupDialog_User,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkUserSetupDialog_User v-bind="props" />',
+ };
+ },
+ args: {
+ user: userDetailed(),
+ },
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkUserSetupDialog_User>;
diff --git a/packages/frontend/src/components/MkUserSetupDialog.User.vue b/packages/frontend/src/components/MkUserSetupDialog.User.vue
new file mode 100644
index 0000000000..d66f34f165
--- /dev/null
+++ b/packages/frontend/src/components/MkUserSetupDialog.User.vue
@@ -0,0 +1,101 @@
+<template>
+<div v-adaptive-bg class="_panel" style="position: relative;">
+ <div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''"></div>
+ <MkAvatar :class="$style.avatar" :user="user" indicator/>
+ <div :class="$style.title">
+ <div :class="$style.name"><MkUserName :user="user" :nowrap="false"/></div>
+ <p :class="$style.username"><MkAcct :user="user"/></p>
+ </div>
+ <div :class="$style.description">
+ <div v-if="user.description" :class="$style.mfm">
+ <Mfm :text="user.description" :author="user" :i="$i"/>
+ </div>
+ <span v-else style="opacity: 0.7;">{{ i18n.ts.noAccountDescription }}</span>
+ </div>
+ <div :class="$style.footer">
+ <MkButton v-if="!isFollowing" primary gradate rounded full @click="follow"><i class="ti ti-plus"></i> {{ i18n.ts.follow }}</MkButton>
+ <div v-else style="opacity: 0.7; text-align: center;">{{ i18n.ts.youFollowing }} <i class="ti ti-check"></i></div>
+ </div>
+</div>
+</template>
+
+<script lang="ts" setup>
+import * as misskey from 'misskey-js';
+import { ref } from 'vue';
+import MkButton from '@/components/MkButton.vue';
+import { i18n } from '@/i18n';
+import { $i } from '@/account';
+import * as os from '@/os';
+
+const props = defineProps<{
+ user: misskey.entities.UserDetailed;
+}>();
+
+const isFollowing = ref(false);
+
+async function follow() {
+ isFollowing.value = true;
+ os.api('following/create', {
+ userId: props.user.id,
+ });
+}
+</script>
+
+<style lang="scss" module>
+.banner {
+ height: 60px;
+ background-color: rgba(0, 0, 0, 0.1);
+ background-size: cover;
+ background-position: center;
+}
+
+.avatar {
+ display: block;
+ position: absolute;
+ top: 30px;
+ left: 13px;
+ z-index: 2;
+ width: 58px;
+ height: 58px;
+ border: solid 4px var(--panel);
+}
+
+.title {
+ display: block;
+ padding: 10px 0 10px 88px;
+}
+
+.name {
+ display: inline-block;
+ margin: 0;
+ font-weight: bold;
+ line-height: 16px;
+ word-break: break-all;
+}
+
+.username {
+ display: block;
+ margin: 0;
+ line-height: 16px;
+ font-size: 0.8em;
+ color: var(--fg);
+ opacity: 0.7;
+}
+
+.description {
+ padding: 0 16px 16px 88px;
+ font-size: 0.9em;
+}
+
+.mfm {
+ display: -webkit-box;
+ -webkit-line-clamp: 5;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+}
+
+.footer {
+ border-top: solid 0.5px var(--divider);
+ padding: 16px;
+}
+</style>
diff --git a/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts
new file mode 100644
index 0000000000..55790602d5
--- /dev/null
+++ b/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts
@@ -0,0 +1,51 @@
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { StoryObj } from '@storybook/vue3';
+import { rest } from 'msw';
+import { commonHandlers } from '../../.storybook/mocks';
+import { userDetailed } from '../../.storybook/fakes';
+import MkUserSetupDialog from './MkUserSetupDialog.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkUserSetupDialog,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkUserSetupDialog v-bind="props" />',
+ };
+ },
+ args: {
+
+ },
+ parameters: {
+ layout: 'centered',
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ rest.post('/api/users', (req, res, ctx) => {
+ return res(ctx.json([
+ userDetailed('44'),
+ userDetailed('49'),
+ ]));
+ }),
+ rest.post('/api/pinned-users', (req, res, ctx) => {
+ return res(ctx.json([
+ userDetailed('44'),
+ userDetailed('49'),
+ ]));
+ }),
+ ],
+ },
+ },
+} satisfies StoryObj<typeof MkUserSetupDialog>;
diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue
new file mode 100644
index 0000000000..096b88c309
--- /dev/null
+++ b/packages/frontend/src/components/MkUserSetupDialog.vue
@@ -0,0 +1,145 @@
+<template>
+<MkModalWindow
+ ref="dialog"
+ :width="500"
+ :height="550"
+ data-cy-user-setup
+ @close="close(true)"
+ @closed="emit('closed')"
+>
+ <template #header>{{ i18n.ts.initialAccountSetting }}</template>
+
+ <div style="overflow-x: clip;">
+ <Transition
+ mode="out-in"
+ :enter-active-class="$style.transition_x_enterActive"
+ :leave-active-class="$style.transition_x_leaveActive"
+ :enter-from-class="$style.transition_x_enterFrom"
+ :leave-to-class="$style.transition_x_leaveTo"
+ >
+ <template v-if="page === 0">
+ <div :class="$style.centerPage">
+ <MkSpacer :margin-min="20" :margin-max="28">
+ <div class="_gaps" style="text-align: center;">
+ <i class="ti ti-confetti" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i>
+ <div style="font-size: 120%;">{{ i18n.ts._initialAccountSetting.accountCreated }}</div>
+ <div>{{ i18n.ts._initialAccountSetting.letsStartAccountSetup }}</div>
+ <MkButton primary rounded gradate style="margin: 16px auto 0 auto;" data-cy-user-setup-continue @click="page++">{{ i18n.ts._initialAccountSetting.profileSetting }} <i class="ti ti-arrow-right"></i></MkButton>
+ </div>
+ </MkSpacer>
+ </div>
+ </template>
+ <template v-else-if="page === 1">
+ <div style="height: 100cqh; overflow: auto;">
+ <MkSpacer :margin-min="20" :margin-max="28">
+ <XProfile/>
+ <MkButton primary rounded gradate style="margin: 16px auto 0 auto;" data-cy-user-setup-continue @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
+ </MkSpacer>
+ </div>
+ </template>
+ <template v-else-if="page === 2">
+ <div style="height: 100cqh; overflow: auto;">
+ <MkSpacer :margin-min="20" :margin-max="28">
+ <XFollow/>
+ <MkButton primary rounded gradate style="margin: 16px auto 0 auto;" data-cy-user-setup-continue @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
+ </MkSpacer>
+ </div>
+ </template>
+ <template v-else-if="page === 3">
+ <div :class="$style.centerPage">
+ <MkSpacer :margin-min="20" :margin-max="28">
+ <div class="_gaps" style="text-align: center;">
+ <i class="ti ti-bell-ringing-2" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i>
+ <div style="font-size: 120%;">{{ i18n.ts.pushNotification }}</div>
+ <div style="padding: 0 16px;">{{ i18n.t('_initialAccountSetting.pushNotificationDescription', { name: instance.name ?? host }) }}</div>
+ <MkPushNotificationAllowButton primary show-only-to-register style="margin: 0 auto;"/>
+ <MkButton primary rounded gradate style="margin: 16px auto 0 auto;" data-cy-user-setup-continue @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
+ </div>
+ </MkSpacer>
+ </div>
+ </template>
+ <template v-else-if="page === 4">
+ <div :class="$style.centerPage">
+ <MkSpacer :margin-min="20" :margin-max="28">
+ <div class="_gaps" style="text-align: center;">
+ <i class="ti ti-check" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i>
+ <div style="font-size: 120%;">{{ i18n.ts._initialAccountSetting.initialAccountSettingCompleted }}</div>
+ <I18n :src="i18n.ts._initialAccountSetting.ifYouNeedLearnMore" tag="div" style="padding: 0 16px;">
+ <template #name>{{ instance.name ?? host }}</template>
+ <template #link>
+ <a href="https://misskey-hub.net/help.html" target="_blank" class="_link">{{ i18n.ts.help }}</a>
+ </template>
+ </I18n>
+ <div>{{ i18n.t('_initialAccountSetting.haveFun', { name: instance.name ?? host }) }}</div>
+ <MkButton primary rounded gradate style="margin: 16px auto 0 auto;" data-cy-user-setup-continue @click="close(false)">{{ i18n.ts.close }}</MkButton>
+ </div>
+ </MkSpacer>
+ </div>
+ </template>
+ </Transition>
+ </div>
+</MkModalWindow>
+</template>
+
+<script lang="ts" setup>
+import { ref, shallowRef, watch } from 'vue';
+import MkModalWindow from '@/components/MkModalWindow.vue';
+import MkButton from '@/components/MkButton.vue';
+import XProfile from '@/components/MkUserSetupDialog.Profile.vue';
+import XFollow from '@/components/MkUserSetupDialog.Follow.vue';
+import { i18n } from '@/i18n';
+import { instance } from '@/instance';
+import { host } from '@/config';
+import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
+import { defaultStore } from '@/store';
+import * as os from '@/os';
+
+const emit = defineEmits<{
+ (ev: 'closed'): void;
+}>();
+
+const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
+
+const page = ref(defaultStore.state.accountSetupWizard);
+
+watch(page, () => {
+ defaultStore.set('accountSetupWizard', page.value);
+});
+
+async function close(skip: boolean) {
+ if (skip) {
+ const { canceled } = await os.confirm({
+ type: 'warning',
+ text: i18n.ts._initialAccountSetting.skipAreYouSure,
+ });
+ if (canceled) return;
+ }
+
+ dialog.value.close();
+ defaultStore.set('accountSetupWizard', -1);
+}
+</script>
+
+<style lang="scss" module>
+.transition_x_enterActive,
+.transition_x_leaveActive {
+ transition: opacity 0.3s cubic-bezier(0,0,.35,1), transform 0.3s cubic-bezier(0,0,.35,1);
+}
+.transition_x_enterFrom {
+ opacity: 0;
+ transform: translateX(50px);
+}
+.transition_x_leaveTo {
+ opacity: 0;
+ transform: translateX(-50px);
+}
+
+.centerPage {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100cqh;
+ padding-bottom: 30px;
+ box-sizing: border-box;
+}
+</style>
diff --git a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
new file mode 100644
index 0000000000..fb705786cf
--- /dev/null
+++ b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
@@ -0,0 +1,157 @@
+<template>
+<div>
+ <MkLoading v-if="fetching"/>
+ <div v-show="!fetching" :class="$style.root">
+ <canvas ref="chartEl"></canvas>
+ </div>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { onMounted } from 'vue';
+import { Chart } from 'chart.js';
+import gradient from 'chartjs-plugin-gradient';
+import tinycolor from 'tinycolor2';
+import * as os from '@/os';
+import { defaultStore } from '@/store';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip';
+import { chartVLine } from '@/scripts/chart-vline';
+import { initChart } from '@/scripts/init-chart';
+
+initChart();
+
+const chartEl = $shallowRef<HTMLCanvasElement>(null);
+const now = new Date();
+let chartInstance: Chart = null;
+const chartLimit = 30;
+let fetching = $ref(true);
+
+const { handler: externalTooltipHandler } = useChartTooltip();
+
+async function renderChart() {
+ if (chartInstance) {
+ chartInstance.destroy();
+ }
+
+ const getDate = (ago: number) => {
+ const y = now.getFullYear();
+ const m = now.getMonth();
+ const d = now.getDate();
+
+ return new Date(y, m, d - ago);
+ };
+
+ const format = (arr) => {
+ return arr.map((v, i) => ({
+ x: getDate(i).getTime(),
+ y: v,
+ }));
+ };
+
+ const raw = await os.api('charts/active-users', { limit: chartLimit, span: 'day' });
+
+ const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
+
+ const computedStyle = getComputedStyle(document.documentElement);
+ const accent = tinycolor(computedStyle.getPropertyValue('--accent')).toHexString();
+
+ const colorRead = accent;
+ const colorWrite = '#2ecc71';
+
+ const max = Math.max(...raw.read);
+
+ chartInstance = new Chart(chartEl, {
+ type: 'bar',
+ data: {
+ datasets: [{
+ parsing: false,
+ label: 'Read',
+ data: format(raw.read).slice().reverse(),
+ pointRadius: 0,
+ borderWidth: 0,
+ borderJoinStyle: 'round',
+ borderRadius: 4,
+ backgroundColor: colorRead,
+ barPercentage: 0.5,
+ categoryPercentage: 1,
+ fill: true,
+ }],
+ },
+ options: {
+ aspectRatio: 2.5,
+ layout: {
+ padding: {
+ left: 0,
+ right: 8,
+ top: 0,
+ bottom: 0,
+ },
+ },
+ scales: {
+ x: {
+ type: 'time',
+ offset: true,
+ time: {
+ stepSize: 1,
+ unit: 'day',
+ displayFormats: {
+ day: 'M/d',
+ month: 'Y/M',
+ },
+ },
+ grid: {
+ display: false,
+ },
+ ticks: {
+ display: true,
+ maxRotation: 0,
+ autoSkipPadding: 8,
+ },
+ },
+ y: {
+ position: 'left',
+ suggestedMax: 10,
+ grid: {
+ display: true,
+ },
+ ticks: {
+ display: true,
+ //mirror: true,
+ },
+ },
+ },
+ interaction: {
+ intersect: false,
+ mode: 'index',
+ },
+ plugins: {
+ legend: {
+ display: false,
+ },
+ tooltip: {
+ enabled: false,
+ mode: 'index',
+ animation: {
+ duration: 0,
+ },
+ external: externalTooltipHandler,
+ },
+ gradient,
+ },
+ },
+ plugins: [chartVLine(vLineColor)],
+ });
+
+ fetching = false;
+}
+
+onMounted(async () => {
+ renderChart();
+});
+</script>
+
+<style lang="scss" module>
+.root {
+ padding: 20px;
+}
+</style>
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
new file mode 100644
index 0000000000..6226768127
--- /dev/null
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -0,0 +1,227 @@
+<template>
+<div v-if="meta" :class="$style.root">
+ <div :class="[$style.main, $style.panel]">
+ <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.mainIcon"/>
+ <button class="_button _acrylic" :class="$style.mainMenu" @click="showMenu"><i class="ti ti-dots"></i></button>
+ <div :class="$style.mainFg">
+ <h1 :class="$style.mainTitle">
+ <!-- 背景色ã«ã‚ˆã£ã¦ã¯ãƒ­ã‚´ãŒè¦‹ãˆãªããªã‚‹ã®ã§ã¨ã‚Šã‚ãˆãšç„¡åŠ¹ã« -->
+ <!-- <img class="logo" v-if="meta.logoImageUrl" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span> -->
+ <span>{{ instanceName }}</span>
+ </h1>
+ <div :class="$style.mainAbout">
+ <!-- eslint-disable-next-line vue/no-v-html -->
+ <div v-html="meta.description || i18n.ts.headlineMisskey"></div>
+ </div>
+ <div v-if="instance.disableRegistration" :class="$style.mainWarn">
+ <MkInfo warn>{{ i18n.ts.invitationRequiredToRegister }}</MkInfo>
+ </div>
+ <div class="_gaps_s" :class="$style.mainActions">
+ <MkButton :class="$style.mainAction" full rounded gradate data-cy-signup style="margin-right: 12px;" @click="signup()">{{ i18n.ts.joinThisServer }}</MkButton>
+ <MkButton :class="$style.mainAction" full rounded @click="exploreOtherServers()">{{ i18n.ts.exploreOtherServers }}</MkButton>
+ <MkButton :class="$style.mainAction" full rounded data-cy-signin @click="signin()">{{ i18n.ts.login }}</MkButton>
+ </div>
+ </div>
+ </div>
+ <div v-if="stats" :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>
+ </div>
+ <div :class="[$style.statsItem, $style.panel]">
+ <div :class="$style.statsItemLabel">{{ i18n.ts.notes }}</div>
+ <div :class="$style.statsItemCount"><MkNumber :value="stats.originalNotesCount"/></div>
+ </div>
+ </div>
+ <div v-if="instance.policies.ltlAvailable" :class="[$style.tl, $style.panel]">
+ <div :class="$style.tlHeader">{{ i18n.ts.letsLookAtTimeline }}</div>
+ <div :class="$style.tlBody">
+ <MkTimeline src="local"/>
+ </div>
+ </div>
+ <div :class="[$style.activeUsersChart, $style.panel]">
+ <XActiveUsersChart/>
+ </div>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { } from 'vue';
+import { Instance } from 'misskey-js/built/entities';
+import XTimeline from './welcome.timeline.vue';
+import XSigninDialog from '@/components/MkSigninDialog.vue';
+import XSignupDialog from '@/components/MkSignupDialog.vue';
+import MkButton from '@/components/MkButton.vue';
+import MkTimeline from '@/components/MkTimeline.vue';
+import MkInfo from '@/components/MkInfo.vue';
+import { instanceName } from '@/config';
+import * as os from '@/os';
+import { i18n } from '@/i18n';
+import { instance } from '@/instance';
+import number from '@/filters/number';
+import MkNumber from '@/components/MkNumber.vue';
+import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue';
+
+let meta = $ref<Instance>();
+let stats = $ref(null);
+
+os.api('meta', { detail: true }).then(_meta => {
+ meta = _meta;
+});
+
+os.api('stats', {
+}).then((res) => {
+ stats = res;
+});
+
+function signin() {
+ os.popup(XSigninDialog, {
+ autoSet: true,
+ }, {}, 'closed');
+}
+
+function signup() {
+ os.popup(XSignupDialog, {
+ autoSet: true,
+ }, {}, 'closed');
+}
+
+function showMenu(ev) {
+ os.popupMenu([{
+ text: i18n.ts.instanceInfo,
+ icon: 'ti ti-info-circle',
+ action: () => {
+ os.pageWindow('/about');
+ },
+ }, {
+ text: i18n.ts.aboutMisskey,
+ icon: 'ti ti-info-circle',
+ action: () => {
+ os.pageWindow('/about-misskey');
+ },
+ }, null, {
+ text: i18n.ts.help,
+ icon: 'ti ti-help-circle',
+ action: () => {
+ window.open('https://misskey-hub.net/help.md', '_blank');
+ },
+ }], ev.currentTarget ?? ev.target);
+}
+
+function exploreOtherServers() {
+ // TODO: 言語をよã—ãªã«
+ window.open('https://join.misskey.page/ja-JP/instances', '_blank');
+}
+</script>
+
+<style lang="scss" module>
+.root {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ padding: 32px 0 0 0;
+}
+
+.panel {
+ position: relative;
+ background: var(--panel);
+ border-radius: var(--radius);
+ box-shadow: 0 12px 32px rgb(0 0 0 / 25%);
+}
+
+.main {
+ text-align: center;
+}
+
+.mainIcon {
+ width: 85px;
+ margin-top: -47px;
+ vertical-align: bottom;
+ filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.5));
+}
+
+.mainMenu {
+ position: absolute;
+ top: 16px;
+ right: 16px;
+ width: 32px;
+ height: 32px;
+ border-radius: 8px;
+ font-size: 18px;
+}
+
+.mainFg {
+ position: relative;
+ z-index: 1;
+}
+
+.mainTitle {
+ display: block;
+ margin: 0;
+ padding: 16px 32px 24px 32px;
+ font-size: 1.4em;
+}
+
+.mainLogo {
+ vertical-align: bottom;
+ max-height: 120px;
+ max-width: min(100%, 300px);
+}
+
+.mainAbout {
+ padding: 0 32px;
+}
+
+.mainWarn {
+ padding: 32px 32px 0 32px;
+}
+
+.mainActions {
+ padding: 32px;
+}
+
+.mainAction {
+ line-height: 28px;
+}
+
+.stats {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-gap: 16px;
+}
+
+.statsItem {
+ overflow: clip;
+ padding: 16px 20px;
+}
+
+.statsItemLabel {
+ color: var(--fgTransparentWeak);
+ font-size: 0.9em;
+}
+
+.statsItemCount {
+ font-weight: bold;
+ font-size: 1.2em;
+ color: var(--accent);
+}
+
+.tl {
+ overflow: clip;
+}
+
+.tlHeader {
+ padding: 12px 16px;
+ border-bottom: solid 1px var(--divider);
+}
+
+.tlBody {
+ height: 350px;
+ overflow: auto;
+}
+
+.activeUsersChart {
+
+}
+</style>
diff --git a/packages/frontend/src/components/MkWidgets.vue b/packages/frontend/src/components/MkWidgets.vue
index d074fdd150..33e594acd8 100644
--- a/packages/frontend/src/components/MkWidgets.vue
+++ b/packages/frontend/src/components/MkWidgets.vue
@@ -2,11 +2,11 @@
<div :class="$style.root">
<template v-if="edit">
<header :class="$style['edit-header']">
- <MkSelect v-model="widgetAdderSelected" style="margin-bottom: var(--margin)" class="mk-widget-select">
+ <MkSelect v-model="widgetAdderSelected" style="margin-bottom: var(--margin)" data-cy-widget-select>
<template #label>{{ i18n.ts.selectWidget }}</template>
<option v-for="widget in widgetDefs" :key="widget" :value="widget">{{ i18n.t(`_widgets.${widget}`) }}</option>
</MkSelect>
- <MkButton inline primary class="mk-widget-add" @click="addWidget"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
+ <MkButton inline primary data-cy-widget-add @click="addWidget"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
<MkButton inline @click="$emit('exit')">{{ i18n.ts.close }}</MkButton>
</header>
<Sortable
diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue
index 687abed632..b662479b2a 100644
--- a/packages/frontend/src/components/MkWindow.vue
+++ b/packages/frontend/src/components/MkWindow.vue
@@ -29,7 +29,7 @@
<button v-if="closeButton" v-tooltip="i18n.ts.close" class="_button" :class="$style.headerButton" @click="close()"><i class="ti ti-x"></i></button>
</span>
</div>
- <div v-container :class="$style.content">
+ <div :class="$style.content">
<slot></slot>
</div>
</div>
@@ -541,7 +541,7 @@ defineExpose({
flex: 1;
overflow: auto;
background: var(--panel);
- container-type: inline-size;
+ container-type: size;
}
$handleSize: 8px;
diff --git a/packages/frontend/src/components/global/MkAcct.stories.impl.ts b/packages/frontend/src/components/global/MkAcct.stories.impl.ts
index d5e3fc3568..9d5fd3947d 100644
--- a/packages/frontend/src/components/global/MkAcct.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkAcct.stories.impl.ts
@@ -41,3 +41,35 @@ export const Detail = {
detail: true,
},
} satisfies StoryObj<typeof MkAcct>;
+export const Long = {
+ ...Default,
+ args: {
+ ...Default.args,
+ user: {
+ ...userDetailed(),
+ username: 'the_quick_brown_fox_jumped_over_the_lazy_dog',
+ host: 'misskey.example',
+ },
+ },
+ decorators: [
+ () => ({
+ template: '<div style="width: 360px;"><story/></div>',
+ }),
+ ],
+} satisfies StoryObj<typeof MkAcct>;
+export const VeryLong = {
+ ...Default,
+ args: {
+ ...Default.args,
+ user: {
+ ...userDetailed(),
+ username: '2c7cc62a697ea3a7826521f3fd34f0cb273693cbe5e9310f35449f43622a5cdc',
+ host: 'the.quick.brown.fox.jumped.over.the.lazy.dog.very.long.hostname.nostr.example',
+ },
+ },
+ decorators: [
+ () => ({
+ template: '<div style="width: 360px;"><story/></div>',
+ }),
+ ],
+} satisfies StoryObj<typeof MkAcct>;
diff --git a/packages/frontend/src/components/global/MkAcct.vue b/packages/frontend/src/components/global/MkAcct.vue
index 2b9f892fc6..59358aef70 100644
--- a/packages/frontend/src/components/global/MkAcct.vue
+++ b/packages/frontend/src/components/global/MkAcct.vue
@@ -1,5 +1,9 @@
<template>
-<span>
+<MkCondensedLine v-if="defaultStore.state.enableCondensedLineForAcct" :min-scale="2 / 3">
+ <span>@{{ user.username }}</span>
+ <span v-if="user.host || detail || defaultStore.state.showFullAcct" style="opacity: 0.5;">@{{ user.host || host }}</span>
+</MkCondensedLine>
+<span v-else>
<span>@{{ user.username }}</span>
<span v-if="user.host || detail || defaultStore.state.showFullAcct" style="opacity: 0.5;">@{{ user.host || host }}</span>
</span>
@@ -8,6 +12,7 @@
<script lang="ts" setup>
import * as misskey from 'misskey-js';
import { toUnicode } from 'punycode/';
+import MkCondensedLine from './MkCondensedLine.vue';
import { host as hostRaw } from '@/config';
import { defaultStore } from '@/store';
diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue
index 8497b8443b..ad36dcabe4 100644
--- a/packages/frontend/src/components/global/MkAvatar.vue
+++ b/packages/frontend/src/components/global/MkAvatar.vue
@@ -222,7 +222,7 @@ watch(() => props.user.avatarBlurhash, () => {
transform: rotate(37.5deg) skew(30deg);
&, &::after {
- border-radius: 0 75% 75%;
+ border-radius: 25% 75% 75%;
}
> .layer {
@@ -251,7 +251,7 @@ watch(() => props.user.avatarBlurhash, () => {
transform: rotate(-37.5deg) skew(-30deg);
&, &::after {
- border-radius: 75% 0 75% 75%;
+ border-radius: 75% 25% 75% 75%;
}
> .layer {
diff --git a/packages/frontend/src/components/global/MkCondensedLine.stories.impl.ts b/packages/frontend/src/components/global/MkCondensedLine.stories.impl.ts
new file mode 100644
index 0000000000..ce985bc59f
--- /dev/null
+++ b/packages/frontend/src/components/global/MkCondensedLine.stories.impl.ts
@@ -0,0 +1,39 @@
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { StoryObj } from '@storybook/vue3';
+import MkCondensedLine from './MkCondensedLine.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkCondensedLine,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkCondensedLine>{{ props.text }}</MkCondensedLine>',
+ };
+ },
+ args: {
+ text: 'This is a condensed line.',
+ },
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkCondensedLine>;
+export const ContainerIs100px = {
+ ...Default,
+ decorators: [
+ () => ({
+ template: '<div style="width: 100px;"><story/></div>',
+ }),
+ ],
+} satisfies StoryObj<typeof MkCondensedLine>;
diff --git a/packages/frontend/src/components/global/MkCondensedLine.vue b/packages/frontend/src/components/global/MkCondensedLine.vue
new file mode 100644
index 0000000000..1d46ff1ec9
--- /dev/null
+++ b/packages/frontend/src/components/global/MkCondensedLine.vue
@@ -0,0 +1,65 @@
+<template>
+<span :class="$style.container">
+ <span ref="content" :class="$style.content">
+ <slot/>
+ </span>
+</span>
+</template>
+
+<script lang="ts">
+interface Props {
+ readonly minScale?: number;
+}
+
+const contentSymbol = Symbol();
+const observer = new ResizeObserver((entries) => {
+ for (const entry of entries) {
+ const content = (entry.target[contentSymbol] ? entry.target : entry.target.firstElementChild) as HTMLSpanElement;
+ const props: Required<Props> = content[contentSymbol];
+ const container = content.parentElement as HTMLSpanElement;
+ const contentWidth = content.getBoundingClientRect().width;
+ const containerWidth = container.getBoundingClientRect().width;
+ container.style.transform = `scaleX(${Math.max(props.minScale, Math.min(1, containerWidth / contentWidth))})`;
+ }
+});
+</script>
+
+<script setup lang="ts">
+import { ref, watch } from 'vue';
+
+const props = withDefaults(defineProps<Props>(), {
+ minScale: 0,
+});
+
+const content = ref<HTMLSpanElement>();
+
+watch(content, (value, oldValue) => {
+ if (oldValue) {
+ delete oldValue[contentSymbol];
+ observer.unobserve(oldValue);
+ if (oldValue.parentElement) {
+ observer.unobserve(oldValue.parentElement);
+ }
+ }
+ if (value) {
+ value[contentSymbol] = props;
+ observer.observe(value);
+ if (value.parentElement) {
+ observer.observe(value.parentElement);
+ }
+ }
+});
+</script>
+
+<style module lang="scss">
+.container {
+ display: inline-block;
+ max-width: 100%;
+ transform-origin: 0;
+}
+
+.content {
+ display: inline-block;
+ white-space: nowrap;
+}
+</style>
diff --git a/packages/frontend/src/components/global/MkError.stories.impl.ts b/packages/frontend/src/components/global/MkError.stories.impl.ts
index 60ac5c91ad..8252a4d76e 100644
--- a/packages/frontend/src/components/global/MkError.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkError.stories.impl.ts
@@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { action } from '@storybook/addon-actions';
import { expect } from '@storybook/jest';
import { waitFor } from '@storybook/testing-library';
import { StoryObj } from '@storybook/vue3';
@@ -20,14 +21,21 @@ export const Default = {
...this.args,
};
},
+ events() {
+ return {
+ retry: action('retry'),
+ };
+ },
},
- template: '<MkError v-bind="props" />',
+ template: '<MkError v-bind="props" v-on="events" />',
};
},
async play({ canvasElement }) {
await expect(canvasElement.firstElementChild).not.toBeNull();
await waitFor(async () => expect(canvasElement.firstElementChild?.classList).not.toContain('_transition_zoom-enter-active'));
},
+ args: {
+ },
parameters: {
layout: 'centered',
},
diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index 710edd797a..b91d378b17 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -156,7 +156,7 @@ onUnmounted(() => {
}
&.thin {
- --height: 42px;
+ --height: 40px;
> .buttons {
> .button {
diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue
index 99169512db..261cc0ee18 100644
--- a/packages/frontend/src/components/global/MkTime.vue
+++ b/packages/frontend/src/components/global/MkTime.vue
@@ -8,6 +8,7 @@
</template>
<script lang="ts" setup>
+import isChromatic from 'chromatic/isChromatic';
import { onUnmounted } from 'vue';
import { i18n } from '@/i18n';
import { dateTimeFormat } from '@/scripts/intl-const';
@@ -17,7 +18,7 @@ const props = withDefaults(defineProps<{
origin?: Date | null;
mode?: 'relative' | 'absolute' | 'detail';
}>(), {
- origin: null,
+ origin: isChromatic() ? new Date('2023-04-01T00:00:00Z') : null,
mode: 'relative',
});
diff --git a/packages/frontend/src/components/index.ts b/packages/frontend/src/components/index.ts
index 63e8fc225c..4ef8111da9 100644
--- a/packages/frontend/src/components/index.ts
+++ b/packages/frontend/src/components/index.ts
@@ -5,6 +5,7 @@ import MkA from './global/MkA.vue';
import MkAcct from './global/MkAcct.vue';
import MkAvatar from './global/MkAvatar.vue';
import MkEmoji from './global/MkEmoji.vue';
+import MkCondensedLine from './global/MkCondensedLine.vue';
import MkCustomEmoji from './global/MkCustomEmoji.vue';
import MkUserName from './global/MkUserName.vue';
import MkEllipsis from './global/MkEllipsis.vue';
@@ -33,6 +34,7 @@ export const components = {
MkAcct: MkAcct,
MkAvatar: MkAvatar,
MkEmoji: MkEmoji,
+ MkCondensedLine: MkCondensedLine,
MkCustomEmoji: MkCustomEmoji,
MkUserName: MkUserName,
MkEllipsis: MkEllipsis,
@@ -55,6 +57,7 @@ declare module '@vue/runtime-core' {
MkAcct: typeof MkAcct;
MkAvatar: typeof MkAvatar;
MkEmoji: typeof MkEmoji;
+ MkCondensedLine: typeof MkCondensedLine;
MkCustomEmoji: typeof MkCustomEmoji;
MkUserName: typeof MkUserName;
MkEllipsis: typeof MkEllipsis;
diff --git a/packages/frontend/src/config.ts b/packages/frontend/src/config.ts
index 073b21a0ae..24f60910d1 100644
--- a/packages/frontend/src/config.ts
+++ b/packages/frontend/src/config.ts
@@ -1,21 +1,22 @@
-import { miLocalStorage } from "./local-storage";
+import { miLocalStorage } from './local-storage';
const address = new URL(location.href);
-const siteName = (document.querySelector('meta[property="og:site_name"]') as HTMLMetaElement)?.content;
+const siteName = document.querySelector<HTMLMetaElement>('meta[property="og:site_name"]')?.content;
export const host = address.host;
export const hostname = address.hostname;
export const url = address.origin;
export const apiUrl = url + '/api';
export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming';
-export const lang = miLocalStorage.getItem('lang');
+export const lang = miLocalStorage.getItem('lang') ?? 'en-US';
export const langs = _LANGS_;
-export let locale = JSON.parse(miLocalStorage.getItem('locale'));
+const preParseLocale = miLocalStorage.getItem('locale');
+export let locale = preParseLocale ? JSON.parse(preParseLocale) : null;
export const version = _VERSION_;
export const instanceName = siteName === 'Misskey' ? host : siteName;
export const ui = miLocalStorage.getItem('ui');
export const debug = miLocalStorage.getItem('debug') === 'true';
-export function updateLocale(newLocale) {
+export function updateLocale(newLocale): void {
locale = newLocale;
}
diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts
index 1d1b8fcea4..aaa3d10302 100644
--- a/packages/frontend/src/const.ts
+++ b/packages/frontend/src/const.ts
@@ -35,6 +35,11 @@ export const FILE_TYPE_BROWSERSAFE = [
'audio/webm',
'audio/aac',
+
+ // see https://github.com/misskey-dev/misskey/pull/10686
+ 'audio/flac',
+ 'audio/wav',
+ // backward compatibility
'audio/x-flac',
'audio/vnd.wave',
];
@@ -56,6 +61,7 @@ export const ROLE_POLICIES = [
'canSearchNotes',
'canHideAds',
'driveCapacityMb',
+ 'alwaysMarkNsfw',
'pinLimit',
'antennaLimit',
'wordMuteLimit',
diff --git a/packages/frontend/src/directives/container.ts b/packages/frontend/src/directives/container.ts
deleted file mode 100644
index a8a93eb9be..0000000000
--- a/packages/frontend/src/directives/container.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Directive } from 'vue';
-
-const map = new WeakMap<HTMLElement, ResizeObserver>();
-
-export default {
- mounted(el: HTMLElement, binding, vn) {
- const ro = new ResizeObserver((entries, observer) => {
- el.style.setProperty('--containerHeight', el.offsetHeight + 'px');
- });
- ro.observe(el);
- map.set(el, ro);
- },
-
- unmounted(el, binding, vn) {
- const ro = map.get(el);
- if (ro) {
- ro.disconnect();
- map.delete(el);
- }
- },
-} as Directive;
diff --git a/packages/frontend/src/directives/index.ts b/packages/frontend/src/directives/index.ts
index 064ee4f64b..7847d661d4 100644
--- a/packages/frontend/src/directives/index.ts
+++ b/packages/frontend/src/directives/index.ts
@@ -11,7 +11,6 @@ import clickAnime from './click-anime';
import panel from './panel';
import adaptiveBorder from './adaptive-border';
import adaptiveBg from './adaptive-bg';
-import container from './container';
export default function(app: App) {
for (const [key, value] of Object.entries(directives)) {
@@ -32,5 +31,4 @@ export const directives = {
'panel': panel,
'adaptive-border': adaptiveBorder,
'adaptive-bg': adaptiveBg,
- 'container': container,
};
diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts
index 5b3e7ec932..49e7bb4008 100644
--- a/packages/frontend/src/init.ts
+++ b/packages/frontend/src/init.ts
@@ -6,18 +6,6 @@ import 'vite/modulepreload-polyfill';
import '@/style.scss';
-//#region account indexedDB migration
-import { set } from '@/scripts/idb-proxy';
-
-{
- const accounts = miLocalStorage.getItem('accounts');
- if (accounts) {
- set('accounts', JSON.parse(accounts));
- miLocalStorage.removeItem('accounts');
- }
-}
-//#endregion
-
import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent } from 'vue';
import { compareVersions } from 'compare-versions';
import JSON5 from 'json5';
@@ -42,11 +30,11 @@ import { reloadChannel } from '@/scripts/unison-reload';
import { reactionPicker } from '@/scripts/reaction-picker';
import { getUrlWithoutLoginId } from '@/scripts/login-id';
import { getAccountFromId } from '@/scripts/get-account-from-id';
-import { deckStore } from './ui/deck/deck-store';
-import { miLocalStorage } from './local-storage';
-import { claimAchievement, claimedAchievements } from './scripts/achievements';
-import { fetchCustomEmojis } from './custom-emojis';
-import { mainRouter } from './router';
+import { deckStore } from '@/ui/deck/deck-store';
+import { miLocalStorage } from '@/local-storage';
+import { claimAchievement, claimedAchievements } from '@/scripts/achievements';
+import { fetchCustomEmojis } from '@/custom-emojis';
+import { mainRouter } from '@/router';
console.info(`Misskey v${version}`);
@@ -55,7 +43,9 @@ if (_DEV_) {
console.info(`vue ${vueVersion}`);
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any).$i = $i;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any).$store = defaultStore;
window.addEventListener('error', event => {
@@ -184,7 +174,7 @@ fetchInstanceMetaPromise.then(() => {
try {
await fetchCustomEmojis();
-} catch (err) {}
+} catch (err) { /* empty */ }
const app = createApp(
new URLSearchParams(window.location.search).has('zen') ? defineAsyncComponent(() => import('@/ui/zen.vue')) :
@@ -212,20 +202,20 @@ await deckStore.ready;
// https://github.com/misskey-dev/misskey/pull/8575#issuecomment-1114239210
// ãªãœã‹init.tsã®å†…容ãŒ2回実行ã•れるã“ã¨ãŒã‚ã‚‹ãŸã‚ã€mountã™ã‚‹divã‚’1ã¤ã«åˆ¶é™ã™ã‚‹
-const rootEl = (() => {
+const rootEl = ((): HTMLElement => {
const MISSKEY_MOUNT_DIV_ID = 'misskey_app';
- const currentEl = document.getElementById(MISSKEY_MOUNT_DIV_ID);
+ const currentRoot = document.getElementById(MISSKEY_MOUNT_DIV_ID);
- if (currentEl) {
+ if (currentRoot) {
console.warn('multiple import detected');
- return currentEl;
+ return currentRoot;
}
- const rootEl = document.createElement('div');
- rootEl.id = MISSKEY_MOUNT_DIV_ID;
- document.body.appendChild(rootEl);
- return rootEl;
+ const root = document.createElement('div');
+ root.id = MISSKEY_MOUNT_DIV_ID;
+ document.body.appendChild(root);
+ return root;
})();
app.mount(rootEl);
@@ -256,8 +246,7 @@ if (lastVersion !== version) {
popup(defineAsyncComponent(() => import('@/components/MkUpdated.vue')), {}, {}, 'closed');
}
}
- } catch (err) {
- }
+ } catch (err) { /* empty */ }
}
await defaultStore.ready;
@@ -354,6 +343,16 @@ if ($i) {
// only add post shortcuts if logged in
hotkeys['p|n'] = post;
+ if (defaultStore.state.accountSetupWizard !== -1) {
+ // ã“ã®ã‚¦ã‚£ã‚¶ãƒ¼ãƒ‰ãŒå®Ÿè£…ã•れるå‰ã«ç™»éŒ²ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã¯è¡¨ç¤ºã•ã›ãªã„ãŸã‚
+ // TODO: ãã®ã†ã¡æ¶ˆã™
+ if (Date.now() - new Date($i.createdAt).getTime() < 1000 * 60 * 60 * 24) {
+ popup(defineAsyncComponent(() => import('@/components/MkUserSetupDialog.vue')), {}, {}, 'closed');
+ } else {
+ defaultStore.set('accountSetupWizard', -1);
+ }
+ }
+
if ($i.isDeleted) {
alert({
type: 'warning',
@@ -442,6 +441,10 @@ if ($i) {
claimAchievement('client30min');
}, 1000 * 60 * 30);
+ window.setTimeout(() => {
+ claimAchievement('client60min');
+ }, 1000 * 60 * 60);
+
const lastUsed = miLocalStorage.getItem('lastUsed');
if (lastUsed) {
const lastUsedDate = parseInt(lastUsed, 10);
@@ -456,7 +459,7 @@ if ($i) {
const latestDonationInfoShownAt = miLocalStorage.getItem('latestDonationInfoShownAt');
const neverShowDonationInfo = miLocalStorage.getItem('neverShowDonationInfo');
- if (neverShowDonationInfo !== 'true' && (new Date($i.createdAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 3)))) {
+ if (neverShowDonationInfo !== 'true' && (new Date($i.createdAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 3))) && !location.pathname.startsWith('/miauth')) {
if (latestDonationInfoShownAt == null || (new Date(latestDonationInfoShownAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 30)))) {
popup(defineAsyncComponent(() => import('@/components/MkDonation.vue')), {}, {}, 'closed');
}
diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts
index 9a288f264c..441a35747a 100644
--- a/packages/frontend/src/local-storage.ts
+++ b/packages/frontend/src/local-storage.ts
@@ -24,6 +24,7 @@ type Keys =
'customCss' |
'message_drafts' |
'scratchpad' |
+ 'debug' |
`miux:${string}` |
`ui:folder:${string}` |
`themes:${string}` |
@@ -32,7 +33,7 @@ type Keys =
'emojis' // DEPRECATED, stored in indexeddb (13.9.0~);
export const miLocalStorage = {
- getItem: (key: Keys) => window.localStorage.getItem(key),
- setItem: (key: Keys, value: string) => window.localStorage.setItem(key, value),
- removeItem: (key: Keys) => window.localStorage.removeItem(key),
+ getItem: (key: Keys): string | null => window.localStorage.getItem(key),
+ setItem: (key: Keys, value: string): void => window.localStorage.setItem(key, value),
+ removeItem: (key: Keys): void => window.localStorage.removeItem(key),
};
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index 962f9cdd98..c4f9d47d7d 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -18,6 +18,8 @@ import MkPopupMenu from '@/components/MkPopupMenu.vue';
import MkContextMenu from '@/components/MkContextMenu.vue';
import { MenuItem } from '@/types/menu';
import copyToClipboard from './scripts/copy-to-clipboard';
+import { showMovedDialog } from './scripts/show-moved-dialog';
+import { DriveFile } from 'misskey-js/built/entities';
export const openingWindowsCount = ref(0);
@@ -55,6 +57,12 @@ export const apiWithDialog = ((
} else if (err.code === 'RATE_LIMIT_EXCEEDED') {
title = i18n.ts.cannotPerformTemporary;
text = i18n.ts.cannotPerformTemporaryDescription;
+ } else if (err.code === 'INVALID_PARAM') {
+ title = i18n.ts.invalidParamError;
+ text = i18n.ts.invalidParamErrorDescription;
+ } else if (err.code === 'ROLE_PERMISSION_DENIED') {
+ title = i18n.ts.permissionDeniedError;
+ text = i18n.ts.permissionDeniedErrorDescription;
} else if (err.code.startsWith('TOO_MANY')) {
title = i18n.ts.youCannotCreateAnymore;
text = `${i18n.ts.error}: ${err.id}`;
@@ -413,7 +421,7 @@ export async function selectUser(opts: { includeSelf?: boolean } = {}) {
});
}
-export async function selectDriveFile(multiple: boolean) {
+export async function selectDriveFile(multiple: boolean): Promise<DriveFile[]> {
return new Promise((resolve, reject) => {
popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), {
type: 'file',
@@ -421,7 +429,7 @@ export async function selectDriveFile(multiple: boolean) {
}, {
done: files => {
if (files) {
- resolve(multiple ? files : files[0]);
+ resolve(files);
}
},
}, 'closed');
@@ -572,6 +580,8 @@ export function contextMenu(items: MenuItem[] | Ref<MenuItem[]>, ev: MouseEvent)
}
export function post(props: Record<string, any> = {}): Promise<void> {
+ showMovedDialog();
+
return new Promise((resolve, reject) => {
// NOTE: MkPostFormDialogã‚’dynamic importã™ã‚‹ã¨iOSã§ãƒ†ã‚­ã‚¹ãƒˆã‚¨ãƒªã‚¢ã«è‡ªå‹•フォーカスã§ããªã„
// NOTE: ãŸã ã€dynamic importã—ãªã„å ´åˆã€MkPostFormDialogインスタンスãŒä½¿ã„ã¾ã‚ã•れã€
diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index bca4d17784..e592c629ce 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -132,6 +132,18 @@ const patronsWithIcon = [{
}, {
name: 'mollinaca',
icon: 'https://misskey-hub.net/patrons/ceb36b8f66e549bdadb3b90d5da62314.jpg',
+}, {
+ name: '傿œ¬é¾',
+ icon: 'https://misskey-hub.net/patrons/a631cf8b490145cf8dbbe4e7508cfbc2.jpg',
+}, {
+ name: 'takke',
+ icon: 'https://misskey-hub.net/patrons/6c3327e626c046f2914fbcd9f7557935.jpg',
+}, {
+ name: 'ãºã‚“ãŽã‚“',
+ icon: 'https://misskey-hub.net/patrons/6a652e0534ff4cb1836e7ce4968d76a7.jpg',
+}, {
+ name: 'ã‹ã¿ã‚‰ãˆã£ã¨',
+ icon: 'https://misskey-hub.net/patrons/be1326bda7d940a482f3758ffd9ffaf6.jpg',
}];
const patrons = [
@@ -219,6 +231,13 @@ const patrons = [
'巣黒るã„@リスケモ男ã®å¨˜VTuber!',
'ãµã‡ã„ã½ã‚€',
'ä¾å¤ç”°ã‚¤ã‚³',
+ '戸塚ã“ã ã¾',
+ 'ã™ãƒ¼ã€‚',
+ '秋雨/Slime-hatena.jp',
+ 'ã‘ã',
+ 'ãšã‚‚',
+ 'binvinyl',
+ '渡志郎',
];
let 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 d461430234..2d82fcf277 100644
--- a/packages/frontend/src/pages/about.emojis.vue
+++ b/packages/frontend/src/pages/about.emojis.vue
@@ -53,7 +53,15 @@ function search() {
}
if (selectedTags.size === 0) {
- searchEmojis = customEmojis.value.filter(emoji => emoji.name.includes(q) || emoji.aliases.includes(q));
+ const queryarry = q.match(/\:([a-z0-9_]*)\:/g);
+
+ if (queryarry) {
+ searchEmojis = customEmojis.value.filter(emoji =>
+ queryarry.includes(`:${emoji.name}:`)
+ );
+ } else {
+ searchEmojis = customEmojis.value.filter(emoji => emoji.name.includes(q) || emoji.aliases.includes(q));
+ }
} else {
searchEmojis = customEmojis.value.filter(emoji => (emoji.name.includes(q) || emoji.aliases.includes(q)) && [...selectedTags].every(t => emoji.aliases.includes(t)));
}
diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue
index d54d93eaee..8e29990426 100644
--- a/packages/frontend/src/pages/about.vue
+++ b/packages/frontend/src/pages/about.vue
@@ -3,10 +3,10 @@
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer v-if="tab === 'overview'" :content-max="600" :margin-min="20">
<div class="_gaps_m">
- <div class="fwhjspax" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }">
- <div class="content">
- <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" class="icon"/>
- <div class="name">
+ <div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }">
+ <div style="overflow: clip;">
+ <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.bannerIcon"/>
+ <div :class="$style.bannerName">
<b>{{ instance.name ?? host }}</b>
</div>
</div>
@@ -41,7 +41,14 @@
<template #value>{{ instance.maintainerEmail }}</template>
</MkKeyValue>
</FormSplit>
- <FormLink v-if="instance.tosUrl" :to="instance.tosUrl" external>{{ i18n.ts.tos }}</FormLink>
+ <MkFolder v-if="instance.serverRules.length > 0">
+ <template #label>{{ i18n.ts.serverRules }}</template>
+
+ <ol class="_gaps_s" :class="$style.rules">
+ <li v-for="item in instance.serverRules" :class="$style.rule"><div :class="$style.ruleText" v-html="item"></div></li>
+ </ol>
+ </MkFolder>
+ <FormLink v-if="instance.tosUrl" :to="instance.tosUrl" external>{{ i18n.ts.termsOfService }}</FormLink>
</div>
</FormSection>
@@ -94,6 +101,7 @@ import FormLink from '@/components/form/link.vue';
import FormSection from '@/components/form/section.vue';
import FormSuspense from '@/components/form/suspense.vue';
import FormSplit from '@/components/form/split.vue';
+import MkFolder from '@/components/MkFolder.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
import MkInstanceStats from '@/components/MkInstanceStats.vue';
import * as os from '@/os';
@@ -148,31 +156,63 @@ definePageMetadata(computed(() => ({
})));
</script>
-<style lang="scss" scoped>
-.fwhjspax {
+<style lang="scss" module>
+.banner {
text-align: center;
border-radius: 10px;
overflow: clip;
background-size: cover;
background-position: center center;
+}
- > .content {
- overflow: hidden;
+.bannerIcon {
+ display: block;
+ margin: 16px auto 0 auto;
+ height: 64px;
+ border-radius: 8px;
+}
- > .icon {
- display: block;
- margin: 16px auto 0 auto;
- height: 64px;
- border-radius: 8px;
- }
+.bannerName {
+ display: block;
+ padding: 16px;
+ color: #fff;
+ text-shadow: 0 0 8px #000;
+ background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
+}
- > .name {
- display: block;
- padding: 16px;
- color: #fff;
- text-shadow: 0 0 8px #000;
- background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
- }
+.rules {
+ counter-reset: item;
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.rule {
+ display: flex;
+ gap: 8px;
+ word-break: break-word;
+
+ &::before {
+ flex-shrink: 0;
+ display: flex;
+ position: sticky;
+ top: calc(var(--stickyTop, 0px) + 8px);
+ counter-increment: item;
+ content: counter(item);
+ width: 32px;
+ height: 32px;
+ line-height: 32px;
+ background-color: var(--accentedBg);
+ color: var(--accent);
+ font-size: 13px;
+ font-weight: bold;
+ align-items: center;
+ justify-content: center;
+ border-radius: 999px;
}
}
+
+.ruleText {
+ padding-top: 6px;
+}
</style>
diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue
index b742132af6..d51bf6230a 100644
--- a/packages/frontend/src/pages/admin/email-settings.vue
+++ b/packages/frontend/src/pages/admin/email-settings.vue
@@ -96,7 +96,9 @@ async function testEmail() {
const { canceled, result: destination } = await os.inputText({
title: i18n.ts.destination,
type: 'email',
- placeholder: instance.maintainerEmail,
+ default: instance.maintainerEmail ?? '',
+ placeholder: 'test@example.com',
+ minLength: 1,
});
if (canceled) return;
os.apiWithDialog('admin/send-email', {
diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue
index ebe1a8ade0..ffd3b6e233 100644
--- a/packages/frontend/src/pages/admin/moderation.vue
+++ b/packages/frontend/src/pages/admin/moderation.vue
@@ -5,14 +5,30 @@
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
<FormSuspense :p="init">
<div class="_gaps_m">
- <FormSection first>
- <div class="_gaps_m">
- <MkTextarea v-model="sensitiveWords">
- <template #label>{{ i18n.ts.sensitiveWords }}</template>
- <template #caption>{{ i18n.ts.sensitiveWordsDescription }}</template>
- </MkTextarea>
- </div>
- </FormSection>
+ <MkSwitch v-model="enableRegistration">
+ <template #label>{{ i18n.ts.enableRegistration }}</template>
+ </MkSwitch>
+
+ <MkSwitch v-model="emailRequiredForSignup">
+ <template #label>{{ i18n.ts.emailRequiredForSignup }}</template>
+ </MkSwitch>
+
+ <FormLink to="/admin/server-rules">{{ i18n.ts.serverRules }}</FormLink>
+
+ <MkInput v-model="tosUrl">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label>{{ i18n.ts.tosUrl }}</template>
+ </MkInput>
+
+ <MkTextarea v-model="preservedUsernames">
+ <template #label>{{ i18n.ts.preservedUsernames }}</template>
+ <template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template>
+ </MkTextarea>
+
+ <MkTextarea v-model="sensitiveWords">
+ <template #label>{{ i18n.ts.sensitiveWords }}</template>
+ <template #caption>{{ i18n.ts.sensitiveWordsDescription }}</template>
+ </MkTextarea>
</div>
</FormSuspense>
</MkSpacer>
@@ -41,17 +57,30 @@ import { fetchInstance } from '@/instance';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
import MkButton from '@/components/MkButton.vue';
+import FormLink from '@/components/form/link.vue';
+let enableRegistration: boolean = $ref(false);
+let emailRequiredForSignup: boolean = $ref(false);
let sensitiveWords: string = $ref('');
+let preservedUsernames: string = $ref('');
+let tosUrl: string | null = $ref(null);
async function init() {
const meta = await os.api('admin/meta');
+ enableRegistration = !meta.disableRegistration;
+ emailRequiredForSignup = meta.emailRequiredForSignup;
sensitiveWords = meta.sensitiveWords.join('\n');
+ preservedUsernames = meta.preservedUsernames.join('\n');
+ tosUrl = meta.tosUrl;
}
function save() {
os.apiWithDialog('admin/update-meta', {
+ disableRegistration: !enableRegistration,
+ emailRequiredForSignup,
+ tosUrl,
sensitiveWords: sensitiveWords.split('\n'),
+ preservedUsernames: preservedUsernames.split('\n'),
}).then(() => {
fetchInstance();
});
diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue
index b1aa03f1f7..c211ef2f05 100644
--- a/packages/frontend/src/pages/admin/roles.edit.vue
+++ b/packages/frontend/src/pages/admin/roles.edit.vue
@@ -54,6 +54,7 @@ if (props.id) {
target: 'manual',
condFormula: { id: uuid(), type: 'isRemote' },
isPublic: false,
+ isExplorable: false,
asBadge: false,
canEditMembersByModerator: false,
displayOrder: 0,
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index 873ff02feb..49942c87ce 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -8,10 +8,9 @@
<template #label>{{ i18n.ts._role.description }}</template>
</MkTextarea>
- <MkInput v-model="role.color">
+ <MkColorInput v-model="role.color">
<template #label>{{ i18n.ts.color }}</template>
- <template #caption>#RRGGBB</template>
- </MkInput>
+ </MkColorInput>
<MkInput v-model="role.iconUrl">
<template #label>{{ i18n.ts._role.iconUrl }}</template>
@@ -59,6 +58,11 @@
<template #caption>{{ i18n.ts._role.descriptionOfAsBadge }}</template>
</MkSwitch>
+ <MkSwitch v-model="role.isExplorable" :readonly="readonly">
+ <template #label>{{ i18n.ts._role.isExplorable }}</template>
+ <template #caption>{{ i18n.ts._role.descriptionOfIsExplorable }}</template>
+ </MkSwitch>
+
<FormSlot>
<template #label><i class="ti ti-license"></i> {{ i18n.ts._role.policies }}</template>
<div class="_gaps_s">
@@ -206,7 +210,7 @@
</MkRange>
</div>
</MkFolder>
-
+
<MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])">
<template #label>{{ i18n.ts._role._options.driveCapacity }}</template>
<template #suffix>
@@ -227,6 +231,26 @@
</div>
</MkFolder>
+ <MkFolder v-if="matchQuery([i18n.ts._role._options.alwaysMarkNsfw, 'alwaysMarkNsfw'])">
+ <template #label>{{ i18n.ts._role._options.alwaysMarkNsfw }}</template>
+ <template #suffix>
+ <span v-if="role.policies.alwaysMarkNsfw.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
+ <span v-else>{{ role.policies.alwaysMarkNsfw.value ? i18n.ts.yes : i18n.ts.no }}</span>
+ <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.alwaysMarkNsfw)"></i></span>
+ </template>
+ <div class="_gaps">
+ <MkSwitch v-model="role.policies.alwaysMarkNsfw.useDefault" :readonly="readonly">
+ <template #label>{{ i18n.ts._role.useBaseValue }}</template>
+ </MkSwitch>
+ <MkSwitch v-model="role.policies.alwaysMarkNsfw.value" :disabled="role.policies.alwaysMarkNsfw.useDefault" :readonly="readonly">
+ <template #label>{{ i18n.ts.enable }}</template>
+ </MkSwitch>
+ <MkRange v-model="role.policies.alwaysMarkNsfw.priority" :min="0" :max="2" :step="1" easing :text-converter="(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.pinMax, 'pinLimit'])">
<template #label>{{ i18n.ts._role._options.pinMax }}</template>
<template #suffix>
@@ -409,6 +433,7 @@ import { watch } from 'vue';
import { throttle } from 'throttle-debounce';
import RolesEditorFormula from './RolesEditorFormula.vue';
import MkInput from '@/components/MkInput.vue';
+import MkColorInput from '@/components/MkColorInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkFolder from '@/components/MkFolder.vue';
@@ -475,6 +500,7 @@ const save = throttle(100, () => {
isAdministrator: role.isAdministrator,
isModerator: role.isModerator,
isPublic: role.isPublic,
+ isExplorable: role.isExplorable,
asBadge: role.asBadge,
canEditMembersByModerator: role.canEditMembersByModerator,
policies: role.policies,
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index a1e467edbd..e8dbe1c5f0 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -75,6 +75,14 @@
</MkInput>
</MkFolder>
+ <MkFolder v-if="matchQuery([i18n.ts._role._options.alwaysMarkNsfw, 'alwaysMarkNsfw'])">
+ <template #label>{{ i18n.ts._role._options.alwaysMarkNsfw }}</template>
+ <template #suffix>{{ policies.alwaysMarkNsfw ? i18n.ts.yes : i18n.ts.no }}</template>
+ <MkSwitch v-model="policies.alwaysMarkNsfw">
+ <template #label>{{ i18n.ts.enable }}</template>
+ </MkSwitch>
+ </MkFolder>
+
<MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])">
<template #label>{{ i18n.ts._role._options.pinMax }}</template>
<template #suffix>{{ policies.pinLimit }}</template>
diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue
new file mode 100644
index 0000000000..85781c0bd0
--- /dev/null
+++ b/packages/frontend/src/pages/admin/server-rules.vue
@@ -0,0 +1,128 @@
+<template>
+<div>
+ <MkStickyContainer>
+ <template #header><XHeader :tabs="headerTabs"/></template>
+ <MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
+ <div class="_gaps_m">
+ <div>{{ i18n.ts._serverRules.description }}</div>
+ <Sortable
+ v-model="serverRules"
+ class="_gaps_m"
+ :item-key="(_, i) => i"
+ :animation="150"
+ :handle="'.' + $style.itemHandle"
+ @start="e => e.item.classList.add('active')"
+ @end="e => e.item.classList.remove('active')"
+ >
+ <template #item="{element,index}">
+ <div :class="$style.item">
+ <div :class="$style.itemHeader">
+ <div :class="$style.itemNumber" v-text="String(index + 1)"/>
+ <span :class="$style.itemHandle"><i class="ti ti-menu"/></span>
+ <button class="_button" :class="$style.itemRemove" @click="remove(index)"><i class="ti ti-x"></i></button>
+ </div>
+ <MkInput v-model="serverRules[index]"/>
+ </div>
+ </template>
+ </Sortable>
+ <div :class="$style.commands">
+ <MkButton rounded @click="serverRules.push('')"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
+ <MkButton primary rounded :class="$style.buttonSave" @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
+ </div>
+ </div>
+ </MkSpacer>
+ </MkStickyContainer>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { defineAsyncComponent } from 'vue';
+import XHeader from './_header_.vue';
+import * as os from '@/os';
+import { fetchInstance, instance } from '@/instance';
+import { i18n } from '@/i18n';
+import { definePageMetadata } from '@/scripts/page-metadata';
+import MkButton from '@/components/MkButton.vue';
+import MkInput from '@/components/MkInput.vue';
+
+const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
+
+let serverRules: string[] = $ref(instance.serverRules);
+
+const save = async () => {
+ await os.apiWithDialog('admin/update-meta', {
+ serverRules,
+ });
+ fetchInstance();
+};
+
+const remove = (index: number): void => {
+ serverRules.splice(index, 1);
+};
+
+const headerTabs = $computed(() => []);
+
+definePageMetadata({
+ title: i18n.ts.serverRules,
+ icon: 'ti ti-checkbox',
+});
+</script>
+
+<style lang="scss" module>
+.item {
+ display: block;
+ color: var(--navFg);
+}
+
+.itemHeader {
+ display: flex;
+ margin-bottom: 8px;
+ align-items: center;
+}
+
+.itemHandle {
+ display: flex;
+ width: 40px;
+ height: 40px;
+ align-items: center;
+ justify-content: center;
+ cursor: move;
+}
+
+.itemNumber {
+ display: flex;
+ background-color: var(--accentedBg);
+ color: var(--accent);
+ font-size: 14px;
+ font-weight: bold;
+ width: 28px;
+ height: 28px;
+ align-items: center;
+ justify-content: center;
+ border-radius: 999px;
+ margin-right: 8px;
+}
+
+.itemEdit {
+ width: 100%;
+ max-width: 100%;
+ min-width: 100%;
+}
+
+.itemRemove {
+ width: 40px;
+ height: 40px;
+ color: var(--error);
+ margin-left: auto;
+ border-radius: 6px;
+
+ &:hover {
+ background: var(--X5);
+ }
+}
+
+.commands {
+ display: flex;
+ gap: 16px;
+}
+</style>
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index 65e64930d5..7ec3c381f3 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -13,11 +13,6 @@
<template #label>{{ i18n.ts.instanceDescription }}</template>
</MkTextarea>
- <MkInput v-model="tosUrl">
- <template #prefix><i class="ti ti-link"></i></template>
- <template #label>{{ i18n.ts.tosUrl }}</template>
- </MkInput>
-
<FormSplit :min-width="300">
<MkInput v-model="maintainerName">
<template #label>{{ i18n.ts.maintainerName }}</template>
@@ -36,14 +31,6 @@
<FormSection>
<div class="_gaps_s">
- <MkSwitch v-model="enableRegistration">
- <template #label>{{ i18n.ts.enableRegistration }}</template>
- </MkSwitch>
-
- <MkSwitch v-model="emailRequiredForSignup">
- <template #label>{{ i18n.ts.emailRequiredForSignup }}</template>
- </MkSwitch>
-
<MkSwitch v-model="enableChartsForRemoteUser">
<template #label>{{ i18n.ts.enableChartsForRemoteUser }}</template>
</MkSwitch>
@@ -73,11 +60,9 @@
<template #label>{{ i18n.ts.backgroundImageUrl }}</template>
</MkInput>
- <MkInput v-model="themeColor">
- <template #prefix><i class="ti ti-palette"></i></template>
+ <MkColorInput v-model="themeColor">
<template #label>{{ i18n.ts.themeColor }}</template>
- <template #caption>#RRGGBB</template>
- </MkInput>
+ </MkColorInput>
<MkTextarea v-model="defaultLightTheme">
<template #label>{{ i18n.ts.instanceDefaultLightTheme }}</template>
@@ -166,10 +151,10 @@ import { fetchInstance } from '@/instance';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
import MkButton from '@/components/MkButton.vue';
+import MkColorInput from '@/components/MkColorInput.vue';
let name: string | null = $ref(null);
let description: string | null = $ref(null);
-let tosUrl: string | null = $ref(null);
let maintainerName: string | null = $ref(null);
let maintainerEmail: string | null = $ref(null);
let iconUrl: string | null = $ref(null);
@@ -180,8 +165,6 @@ let defaultLightTheme: any = $ref(null);
let defaultDarkTheme: any = $ref(null);
let pinnedUsers: string = $ref('');
let cacheRemoteFiles: boolean = $ref(false);
-let enableRegistration: boolean = $ref(false);
-let emailRequiredForSignup: boolean = $ref(false);
let enableServiceWorker: boolean = $ref(false);
let enableChartsForRemoteUser: boolean = $ref(false);
let enableChartsForFederatedInstances: boolean = $ref(false);
@@ -194,7 +177,6 @@ async function init() {
const meta = await os.api('admin/meta');
name = meta.name;
description = meta.description;
- tosUrl = meta.tosUrl;
iconUrl = meta.iconUrl;
bannerUrl = meta.bannerUrl;
backgroundImageUrl = meta.backgroundImageUrl;
@@ -205,8 +187,6 @@ async function init() {
maintainerEmail = meta.maintainerEmail;
pinnedUsers = meta.pinnedUsers.join('\n');
cacheRemoteFiles = meta.cacheRemoteFiles;
- enableRegistration = !meta.disableRegistration;
- emailRequiredForSignup = meta.emailRequiredForSignup;
enableServiceWorker = meta.enableServiceWorker;
enableChartsForRemoteUser = meta.enableChartsForRemoteUser;
enableChartsForFederatedInstances = meta.enableChartsForFederatedInstances;
@@ -220,7 +200,6 @@ function save() {
os.apiWithDialog('admin/update-meta', {
name,
description,
- tosUrl,
iconUrl,
bannerUrl,
backgroundImageUrl,
@@ -231,8 +210,6 @@ function save() {
maintainerEmail,
pinnedUsers: pinnedUsers.split('\n'),
cacheRemoteFiles,
- disableRegistration: !enableRegistration,
- emailRequiredForSignup,
enableServiceWorker,
enableChartsForRemoteUser,
enableChartsForFederatedInstances,
diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue
index 9cb440d2bb..a74ab40473 100644
--- a/packages/frontend/src/pages/channel-editor.vue
+++ b/packages/frontend/src/pages/channel-editor.vue
@@ -11,6 +11,10 @@
<template #label>{{ i18n.ts.description }}</template>
</MkTextarea>
+ <MkColorInput v-model="color">
+ <template #label>{{ i18n.ts.color }}</template>
+ </MkColorInput>
+
<div>
<MkButton v-if="bannerId == null" @click="setBannerImage"><i class="ti ti-plus"></i> {{ i18n.ts._channel.setBanner }}</MkButton>
<div v-else-if="bannerUrl">
@@ -42,8 +46,9 @@
</div>
</MkFolder>
- <div>
+ <div class="_buttons">
<MkButton primary @click="save()"><i class="ti ti-device-floppy"></i> {{ channelId ? i18n.ts.save : i18n.ts.create }}</MkButton>
+ <MkButton v-if="channelId" danger @click="archive()"><i class="ti ti-trash"></i> {{ i18n.ts.archive }}</MkButton>
</div>
</div>
</MkSpacer>
@@ -55,6 +60,7 @@ import { computed, ref, watch, defineAsyncComponent } from 'vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
+import MkColorInput from '@/components/MkColorInput.vue';
import { selectFile } from '@/scripts/select-file';
import * as os from '@/os';
import { useRouter } from '@/router';
@@ -75,6 +81,7 @@ let name = $ref(null);
let description = $ref(null);
let bannerUrl = $ref<string | null>(null);
let bannerId = $ref<string | null>(null);
+let color = $ref('#000');
const pinnedNotes = ref([]);
watch(() => bannerId, async () => {
@@ -101,6 +108,7 @@ async function fetchChannel() {
pinnedNotes.value = channel.pinnedNoteIds.map(id => ({
id,
}));
+ color = channel.color;
}
fetchChannel();
@@ -128,6 +136,7 @@ function save() {
description: description,
bannerId: bannerId,
pinnedNoteIds: pinnedNotes.value.map(x => x.id),
+ color: color,
};
if (props.channelId) {
@@ -143,6 +152,23 @@ function save() {
}
}
+async function archive() {
+ const { canceled } = await os.confirm({
+ type: 'warning',
+ title: i18n.t('channelArchiveConfirmTitle', { name: name }),
+ text: i18n.ts.channelArchiveConfirmDescription,
+ });
+
+ if (canceled) return;
+
+ os.api('channels/update', {
+ channelId: props.channelId,
+ isArchived: true,
+ }).then(() => {
+ os.success();
+ });
+}
+
function setBannerImage(evt) {
selectFile(evt.currentTarget ?? evt.target, null).then(file => {
bannerId = file.id;
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue
index 437c1fae31..af1b4d2056 100644
--- a/packages/frontend/src/pages/channel.vue
+++ b/packages/frontend/src/pages/channel.vue
@@ -28,6 +28,8 @@
</MkFoldableSection>
</div>
<div v-if="channel && tab === 'timeline'" class="_gaps">
+ <MkInfo v-if="channel.isArchived" warn>{{ i18n.ts.thisChannelArchived }}</MkInfo>
+
<!-- スマホ・タブレットã®å ´åˆã€ã‚­ãƒ¼ãƒœãƒ¼ãƒ‰ãŒè¡¨ç¤ºã•ã‚Œã‚‹ã¨æŠ•ç¨¿ãŒè¦‹ã¥ã‚‰ããªã‚‹ã®ã§ã€ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—å ´åˆã®ã¿è‡ªå‹•ã§ãƒ•ォーカスを当ã¦ã‚‹ -->
<MkPostForm v-if="$i && defaultStore.reactiveState.showFixedPostFormInChannel.value" :channel="channel" class="post-form _panel" fixed :autofocus="deviceKind === 'desktop'"/>
@@ -36,6 +38,17 @@
<div v-else-if="tab === 'featured'">
<MkNotes :pagination="featuredPagination"/>
</div>
+ <div v-else-if="tab === 'search'">
+ <div class="_gaps">
+ <div>
+ <MkInput v-model="searchQuery">
+ <template #prefix><i class="ti ti-search"></i></template>
+ </MkInput>
+ <MkButton primary rounded style="margin-top: 8px;" @click="search()">{{ i18n.ts.search }}</MkButton>
+ </div>
+ <MkNotes v-if="searchPagination" :key="searchQuery" :pagination="searchPagination"/>
+ </div>
+ </div>
</MkSpacer>
<template #footer>
<div :class="$style.footer">
@@ -63,8 +76,10 @@ import { deviceKind } from '@/scripts/device-kind';
import MkNotes from '@/components/MkNotes.vue';
import { url } from '@/config';
import MkButton from '@/components/MkButton.vue';
+import MkInput from '@/components/MkInput.vue';
import { defaultStore } from '@/store';
import MkNote from '@/components/MkNote.vue';
+import MkInfo from '@/components/MkInfo.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
const router = useRouter();
@@ -76,6 +91,8 @@ const props = defineProps<{
let tab = $ref('timeline');
let channel = $ref(null);
let favorited = $ref(false);
+let searchQuery = $ref('');
+let searchPagination = $ref();
const featuredPagination = $computed(() => ({
endpoint: 'notes/featured' as const,
limit: 10,
@@ -123,6 +140,21 @@ async function unfavorite() {
});
}
+async function search() {
+ const query = searchQuery.toString().trim();
+
+ if (query == null) return;
+
+ searchPagination = {
+ endpoint: 'notes/search',
+ limit: 10,
+ params: {
+ query: searchQuery,
+ channelId: channel.id,
+ },
+ };
+}
+
const headerActions = $computed(() => {
if (channel && channel.userId) {
const share = {
@@ -160,6 +192,10 @@ const headerTabs = $computed(() => [{
key: 'featured',
title: i18n.ts.featured,
icon: 'ti ti-bolt',
+}, {
+ key: 'search',
+ title: i18n.ts.search,
+ icon: 'ti ti-search',
}]);
definePageMetadata(computed(() => channel ? {
@@ -170,7 +206,7 @@ definePageMetadata(computed(() => channel ? {
<style lang="scss" module>
.main {
- min-height: calc(var(--containerHeight) - (var(--stickyTop, 0px) + var(--stickyBottom, 0px)));
+ min-height: calc(100cqh - (var(--stickyTop, 0px) + var(--stickyBottom, 0px)));
}
.footer {
diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue
index 70e7705d1d..e670cdd864 100644
--- a/packages/frontend/src/pages/channels.vue
+++ b/packages/frontend/src/pages/channels.vue
@@ -96,7 +96,7 @@ const ownedPagination = {
async function search() {
const query = searchQuery.toString().trim();
- if (query == null || query === '') return;
+ if (query == null) return;
const type = searchType.toString().trim();
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index 59cb3262b7..3f13f0787d 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -15,9 +15,10 @@
<div v-if="selectMode" class="_buttons">
<MkButton inline @click="selectAll">Select all</MkButton>
<MkButton inline @click="setCategoryBulk">Set category</MkButton>
+ <MkButton inline @click="setTagBulk">Set tag</MkButton>
<MkButton inline @click="addTagBulk">Add tag</MkButton>
<MkButton inline @click="removeTagBulk">Remove tag</MkButton>
- <MkButton inline @click="setTagBulk">Set tag</MkButton>
+ <MkButton inline @click="setLisenceBulk">Set Lisence</MkButton>
<MkButton inline danger @click="delBulk">Delete</MkButton>
</div>
<MkPagination ref="emojisPaginationComponent" :pagination="pagination">
@@ -221,6 +222,18 @@ const setCategoryBulk = async () => {
emojisPaginationComponent.value.reload();
};
+const setLisenceBulk = async () => {
+ const { canceled, result } = await os.inputText({
+ title: 'License',
+ });
+ if (canceled) return;
+ await os.apiWithDialog('admin/emoji/set-license-bulk', {
+ ids: selectedEmojis.value,
+ license: result,
+ });
+ emojisPaginationComponent.value.reload();
+};
+
const addTagBulk = async () => {
const { canceled, result } = await os.inputText({
title: 'Tag',
diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index 35edcc7cda..816825e5b6 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -33,7 +33,7 @@ import MkTextarea from '@/components/MkTextarea.vue';
import MkInput from '@/components/MkInput.vue';
import { useRouter } from '@/router';
-const PRESET_DEFAULT = `/// @ 0.13.1
+const PRESET_DEFAULT = `/// @ 0.13.2
var name = ""
@@ -51,7 +51,7 @@ Ui:render([
])
`;
-const PRESET_OMIKUJI = `/// @ 0.13.1
+const PRESET_OMIKUJI = `/// @ 0.13.2
// ユーザーã”ã¨ã«æ—¥æ›¿ã‚りã®ãŠã¿ãã˜ã®ãƒ—リセット
// é¸æŠžè‚¢
@@ -94,7 +94,7 @@ Ui:render([
])
`;
-const PRESET_SHUFFLE = `/// @ 0.13.1
+const PRESET_SHUFFLE = `/// @ 0.13.2
// å·»ãæˆ»ã—å¯èƒ½ãªæ–‡å­—シャッフルã®ãƒ—リセット
let string = "ペペロンãƒãƒ¼ãƒŽ"
@@ -173,7 +173,7 @@ var cursor = 0
do()
`;
-const PRESET_QUIZ = `/// @ 0.13.1
+const PRESET_QUIZ = `/// @ 0.13.2
let title = '地ç†ã‚¯ã‚¤ã‚º'
let qas = [{
@@ -286,7 +286,7 @@ qaEls.push(Ui:C:container({
Ui:render(qaEls)
`;
-const PRESET_TIMELINE = `/// @ 0.13.1
+const PRESET_TIMELINE = `/// @ 0.13.2
// APIリクエストを行ã„ローカルタイムラインを表示ã™ã‚‹ãƒ—リセット
@fetch() {
@@ -305,6 +305,11 @@ const PRESET_TIMELINE = `/// @ 0.13.1
// ãれãžã‚Œã®ãƒŽãƒ¼ãƒˆã”ã¨ã«UIè¦ç´ ä½œæˆ
let noteEls = []
each (let note, notes) {
+ // 表示åを設定ã—ã¦ã„ãªã„アカウントã¯idを表示
+ let userName = if Core:type(note.user.name) == "str" note.user.name else note.user.username
+ // リノートもã—ãã¯ãƒ¡ãƒ‡ã‚£ã‚¢ãƒ»æŠ•票ã®ã¿ã§æœ¬æ–‡ãŒç„¡ã„ノートã«ä»£æ›¿è¡¨ç¤ºæ–‡ã‚’設定
+ let noteText = if Core:type(note.text) == "str" note.text else "(リノートもã—ãã¯ãƒ¡ãƒ‡ã‚£ã‚¢ãƒ»æŠ•票ã®ã¿ã®ãƒŽãƒ¼ãƒˆï¼‰"
+
let el = Ui:C:container({
bgColor: "#444"
fgColor: "#fff"
@@ -312,11 +317,11 @@ const PRESET_TIMELINE = `/// @ 0.13.1
rounded: true
children: [
Ui:C:mfm({
- text: note.user.name
+ text: userName
bold: true
})
Ui:C:mfm({
- text: note.text
+ text: noteText
})
]
})
diff --git a/packages/frontend/src/pages/gallery/edit.vue b/packages/frontend/src/pages/gallery/edit.vue
index 1fae7686e5..cafcee0c33 100644
--- a/packages/frontend/src/pages/gallery/edit.vue
+++ b/packages/frontend/src/pages/gallery/edit.vue
@@ -2,7 +2,7 @@
<MkStickyContainer>
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :content-max="800" :margin-min="16" :margin-max="32">
- <FormSuspense :p="init">
+ <FormSuspense :p="init" class="_gaps">
<MkInput v-model="title">
<template #label>{{ i18n.ts.title }}</template>
</MkInput>
@@ -11,7 +11,7 @@
<template #label>{{ i18n.ts.description }}</template>
</MkTextarea>
- <div class="">
+ <div class="_gaps_s">
<div v-for="file in files" :key="file.id" class="wqugxsfx" :style="{ backgroundImage: file ? `url(${ file.thumbnailUrl })` : null }">
<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>
@@ -21,10 +21,12 @@
<MkSwitch v-model="isSensitive">{{ i18n.ts.markAsSensitive }}</MkSwitch>
- <MkButton v-if="postId" primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
- <MkButton v-else primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.publish }}</MkButton>
+ <div class="_buttons">
+ <MkButton v-if="postId" primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
+ <MkButton v-else primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.publish }}</MkButton>
- <MkButton v-if="postId" danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
+ <MkButton v-if="postId" danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
+ </div>
</FormSuspense>
</MkSpacer>
</MkStickyContainer>
diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue
index 768a48746c..86201e8e0c 100644
--- a/packages/frontend/src/pages/my-lists/list.vue
+++ b/packages/frontend/src/pages/my-lists/list.vue
@@ -131,7 +131,7 @@ definePageMetadata(computed(() => list ? {
<style lang="scss" module>
.main {
- min-height: calc(var(--containerHeight) - (var(--stickyTop, 0px) + var(--stickyBottom, 0px)));
+ min-height: calc(100cqh - (var(--stickyTop, 0px) + var(--stickyBottom, 0px)));
}
.userItem {
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
index ffeb8ba285..e97a4b07f1 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
@@ -33,8 +33,8 @@ const emit = defineEmits<{
let file: any = $ref(null);
async function choose() {
- os.selectDriveFile(false).then((fileResponse: any) => {
- file = fileResponse;
+ os.selectDriveFile(false).then((fileResponse) => {
+ file = fileResponse[0];
emit('update:modelValue', {
...props.modelValue,
fileId: fileResponse.id,
diff --git a/packages/frontend/src/pages/role.vue b/packages/frontend/src/pages/role.vue
index f2645394a2..fe39c594ba 100644
--- a/packages/frontend/src/pages/role.vue
+++ b/packages/frontend/src/pages/role.vue
@@ -1,8 +1,16 @@
<template>
<MkStickyContainer>
<template #header><MkPageHeader v-model:tab="tab" :tabs="headerTabs"/></template>
-
- <MkSpacer v-if="tab === 'users'" :content-max="1200">
+ <MKSpacer v-if="!(typeof error === 'undefined')" :content-max="1200">
+ <div :class="$style.root">
+ <img :class="$style.img" src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/>
+ <p :class="$style.text">
+ <i class="ti ti-alert-triangle"></i>
+ {{ error }}
+ </p>
+ </div>
+ </MKSpacer>
+ <MkSpacer v-else-if="tab === 'users'" :content-max="1200">
<div class="_gaps_s">
<div v-if="role">{{ role.description }}</div>
<MkUserList :pagination="users" :extractor="(item) => item.user"/>
@@ -13,7 +21,6 @@
</MkSpacer>
</MkStickyContainer>
</template>
-
<script lang="ts" setup>
import { computed, watch } from 'vue';
import * as os from '@/os';
@@ -21,6 +28,7 @@ import MkUserList from '@/components/MkUserList.vue';
import { definePageMetadata } from '@/scripts/page-metadata';
import { i18n } from '@/i18n';
import MkTimeline from '@/components/MkTimeline.vue';
+import { instanceName } from '@/config';
const props = withDefaults(defineProps<{
role: string;
@@ -31,12 +39,21 @@ const props = withDefaults(defineProps<{
let tab = $ref(props.initialTab);
let role = $ref();
+let error = $ref();
watch(() => props.role, () => {
os.api('roles/show', {
roleId: props.role,
}).then(res => {
role = res;
+ document.title = `${role?.name} | ${instanceName}`;
+ }).catch((err) => {
+ if (err.code === 'NO_SUCH_ROLE') {
+ error = i18n.ts.noRole;
+ } else {
+ error = i18n.ts.somethingHappened;
+ }
+ document.title = `${error} | ${instanceName}`;
});
}, { immediate: true });
@@ -63,4 +80,23 @@ definePageMetadata(computed(() => ({
icon: 'ti ti-badge',
})));
</script>
+<style lang="scss" module>
+.root {
+ padding: 32px;
+ text-align: center;
+ align-items: center;
+}
+
+.text {
+ margin: 0 0 8px 0;
+}
+
+.img {
+ vertical-align: bottom;
+ width: 128px;
+ height: 128px;
+ margin-bottom: 16px;
+ border-radius: 16px;
+}
+</style>
diff --git a/packages/frontend/src/pages/settings/account-info.vue b/packages/frontend/src/pages/settings/account-stats.vue
index 584808b0b4..a0f1541b40 100644
--- a/packages/frontend/src/pages/settings/account-info.vue
+++ b/packages/frontend/src/pages/settings/account-stats.vue
@@ -1,18 +1,6 @@
<template>
<div class="_gaps_m">
- <MkKeyValue>
- <template #key>ID</template>
- <template #value><span class="_monospace">{{ $i.id }}</span></template>
- </MkKeyValue>
-
- <FormSection>
- <MkKeyValue>
- <template #key>{{ i18n.ts.registeredDate }}</template>
- <template #value><MkTime :time="$i.createdAt" mode="detail"/></template>
- </MkKeyValue>
- </FormSection>
-
- <FormSection v-if="stats">
+ <FormSection v-if="stats" first>
<template #label>{{ i18n.ts.statistics }}</template>
<MkKeyValue oneline style="margin: 1em 0;">
<template #key>{{ i18n.ts.notesCount }}</template>
diff --git a/packages/frontend/src/pages/settings/delete-account.vue b/packages/frontend/src/pages/settings/delete-account.vue
deleted file mode 100644
index c6e79165c5..0000000000
--- a/packages/frontend/src/pages/settings/delete-account.vue
+++ /dev/null
@@ -1,52 +0,0 @@
-<template>
-<div class="_gaps_m">
- <FormInfo warn>{{ i18n.ts._accountDelete.mayTakeTime }}</FormInfo>
- <FormInfo>{{ i18n.ts._accountDelete.sendEmail }}</FormInfo>
- <MkButton v-if="!$i.isDeleted" danger @click="deleteAccount">{{ i18n.ts._accountDelete.requestAccountDelete }}</MkButton>
- <MkButton v-else disabled>{{ i18n.ts._accountDelete.inProgress }}</MkButton>
-</div>
-</template>
-
-<script lang="ts" setup>
-import FormInfo from '@/components/MkInfo.vue';
-import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { signout, $i } from '@/account';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-
-async function deleteAccount() {
- {
- const { canceled } = await os.confirm({
- type: 'warning',
- text: i18n.ts.deleteAccountConfirm,
- });
- if (canceled) return;
- }
-
- const { canceled, result: password } = await os.inputText({
- title: i18n.ts.password,
- type: 'password',
- });
- if (canceled) return;
-
- await os.apiWithDialog('i/delete-account', {
- password: password,
- });
-
- await os.alert({
- title: i18n.ts._accountDelete.started,
- });
-
- await signout();
-}
-
-const headerActions = $computed(() => []);
-
-const headerTabs = $computed(() => []);
-
-definePageMetadata({
- title: i18n.ts._accountDelete.accountDelete,
- icon: 'ti ti-alert-triangle',
-});
-</script>
diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue
index d3fb422e01..73c2b2e604 100644
--- a/packages/frontend/src/pages/settings/drive.vue
+++ b/packages/frontend/src/pages/settings/drive.vue
@@ -119,6 +119,13 @@ function saveProfile() {
os.api('i/update', {
alwaysMarkNsfw: !!alwaysMarkNsfw,
autoSensitive: !!autoSensitive,
+ }).catch(err => {
+ os.alert({
+ type: 'error',
+ title: i18n.ts.error,
+ text: err.message,
+ });
+ alwaysMarkNsfw = true;
});
}
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 904fd3f952..ba0f3274fc 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -20,24 +20,71 @@
<option value="desktop"><i class="ti ti-device-desktop"/> {{ i18n.ts.desktop }}</option>
</MkRadios>
- <MkSwitch v-model="showFixedPostForm">{{ i18n.ts.showFixedPostForm }}</MkSwitch>
- <MkSwitch v-model="showFixedPostFormInChannel">{{ i18n.ts.showFixedPostFormInChannel }}</MkSwitch>
+ <FormSection>
+ <div class="_gaps_s">
+ <MkSwitch v-model="showFixedPostForm">{{ i18n.ts.showFixedPostForm }}</MkSwitch>
+ <MkSwitch v-model="showFixedPostFormInChannel">{{ i18n.ts.showFixedPostFormInChannel }}</MkSwitch>
+ </div>
+ </FormSection>
<FormSection>
- <template #label>{{ i18n.ts.behavior }}</template>
+ <template #label>{{ i18n.ts.displayOfNote }}</template>
<div class="_gaps_m">
<div class="_gaps_s">
- <MkSwitch v-model="imageNewTab">{{ i18n.ts.openImageInNewTab }}</MkSwitch>
- <MkSwitch v-model="enableInfiniteScroll">{{ i18n.ts.enableInfiniteScroll }}</MkSwitch>
+ <MkSwitch v-model="showNoteActionsOnlyHover">{{ i18n.ts.showNoteActionsOnlyHover }}</MkSwitch>
+ <MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch>
+ <MkSwitch v-model="largeNoteReactions">{{ i18n.ts.largeNoteReactions }}</MkSwitch>
+ <MkSwitch v-model="collapseRenotes">{{ i18n.ts.collapseRenotes }}</MkSwitch>
+ <MkSwitch v-model="advancedMfm">{{ i18n.ts.enableAdvancedMfm }}</MkSwitch>
+ <MkSwitch v-if="advancedMfm" v-model="animatedMfm">{{ i18n.ts.enableAnimatedMfm }}</MkSwitch>
+ <MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch>
+ <MkSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</MkSwitch>
<MkSwitch v-model="useReactionPickerForContextMenu">{{ i18n.ts.useReactionPickerForContextMenu }}</MkSwitch>
</div>
- <MkSelect v-model="serverDisconnectedBehavior">
- <template #label>{{ i18n.ts.whenServerDisconnected }}</template>
- <option value="reload">{{ i18n.ts._serverDisconnectedBehavior.reload }}</option>
- <option value="dialog">{{ i18n.ts._serverDisconnectedBehavior.dialog }}</option>
- <option value="quiet">{{ i18n.ts._serverDisconnectedBehavior.quiet }}</option>
+
+ <MkSelect v-model="instanceTicker">
+ <template #label>{{ i18n.ts.instanceTicker }}</template>
+ <option value="none">{{ i18n.ts._instanceTicker.none }}</option>
+ <option value="remote">{{ i18n.ts._instanceTicker.remote }}</option>
+ <option value="always">{{ i18n.ts._instanceTicker.always }}</option>
</MkSelect>
+
+ <MkSelect v-model="nsfw">
+ <template #label>{{ i18n.ts.nsfw }}</template>
+ <option value="respect">{{ i18n.ts._nsfw.respect }}</option>
+ <option value="ignore">{{ i18n.ts._nsfw.ignore }}</option>
+ <option value="force">{{ i18n.ts._nsfw.force }}</option>
+ </MkSelect>
+ <!--
+ <MkRadios v-model="mediaListWithOneImageAppearance">
+ <template #label>{{ i18n.ts.mediaListWithOneImageAppearance }}</template>
+ <option value="expand">{{ i18n.ts.default }}</option>
+ <option value="16_9">{{ i18n.t('limitTo', { x: '16:9' }) }}</option>
+ <option value="1_1">{{ i18n.t('limitTo', { x: '1:1' }) }}</option>
+ <option value="2_3">{{ i18n.t('limitTo', { x: '2:3' }) }}</option>
+ </MkRadios>
+ -->
+ </div>
+ </FormSection>
+
+ <FormSection>
+ <template #label>{{ i18n.ts.notificationDisplay }}</template>
+
+ <div class="_gaps_m">
+ <MkRadios v-model="notificationPosition">
+ <template #label>{{ i18n.ts.position }}</template>
+ <option value="leftTop"><i class="ti ti-align-box-left-top"></i> {{ i18n.ts.leftTop }}</option>
+ <option value="rightTop"><i class="ti ti-align-box-right-top"></i> {{ i18n.ts.rightTop }}</option>
+ <option value="leftBottom"><i class="ti ti-align-box-left-bottom"></i> {{ i18n.ts.leftBottom }}</option>
+ <option value="rightBottom"><i class="ti ti-align-box-right-bottom"></i> {{ i18n.ts.rightBottom }}</option>
+ </MkRadios>
+
+ <MkRadios v-model="notificationStackAxis">
+ <template #label>{{ i18n.ts.stackAxis }}</template>
+ <option value="vertical"><i class="ti ti-carousel-vertical"></i> {{ i18n.ts.vertical }}</option>
+ <option value="horizontal"><i class="ti ti-carousel-horizontal"></i> {{ i18n.ts.horizontal }}</option>
+ </MkRadios>
</div>
</FormSection>
@@ -46,22 +93,15 @@
<div class="_gaps_m">
<div class="_gaps_s">
- <MkSwitch v-model="showNoteActionsOnlyHover">{{ i18n.ts.showNoteActionsOnlyHover }}</MkSwitch>
- <MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch>
- <MkSwitch v-model="largeNoteReactions">{{ i18n.ts.largeNoteReactions }}</MkSwitch>
- <MkSwitch v-model="collapseRenotes">{{ i18n.ts.collapseRenotes }}</MkSwitch>
- <MkSwitch v-model="advancedMfm">{{ i18n.ts.enableAdvancedMfm }}</MkSwitch>
- <MkSwitch v-if="advancedMfm" v-model="animatedMfm">{{ i18n.ts.enableAnimatedMfm }}</MkSwitch>
<MkSwitch v-model="reduceAnimation">{{ i18n.ts.reduceUiAnimation }}</MkSwitch>
<MkSwitch v-model="useBlurEffect">{{ i18n.ts.useBlurEffect }}</MkSwitch>
<MkSwitch v-model="useBlurEffectForModal">{{ i18n.ts.useBlurEffectForModal }}</MkSwitch>
- <MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch>
- <MkSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</MkSwitch>
<MkSwitch v-model="disableShowingAnimatedImages">{{ i18n.ts.disableShowingAnimatedImages }}</MkSwitch>
<MkSwitch v-model="squareAvatars">{{ i18n.ts.squareAvatars }}</MkSwitch>
<MkSwitch v-model="useSystemFont">{{ i18n.ts.useSystemFont }}</MkSwitch>
<MkSwitch v-model="disableDrawer">{{ i18n.ts.disableDrawer }}</MkSwitch>
<MkSwitch v-model="forceShowAds">{{ i18n.ts.forceShowAds }}</MkSwitch>
+ <MkSwitch v-model="enableDataSaverMode">{{ i18n.ts.dataSaver }}</MkSwitch>
</div>
<div>
<MkRadios v-model="emojiStyle">
@@ -84,27 +124,29 @@
</FormSection>
<FormSection>
- <MkSwitch v-model="aiChanMode">{{ i18n.ts.aiChanMode }}</MkSwitch>
- </FormSection>
-
- <MkSelect v-model="instanceTicker">
- <template #label>{{ i18n.ts.instanceTicker }}</template>
- <option value="none">{{ i18n.ts._instanceTicker.none }}</option>
- <option value="remote">{{ i18n.ts._instanceTicker.remote }}</option>
- <option value="always">{{ i18n.ts._instanceTicker.always }}</option>
- </MkSelect>
+ <template #label>{{ i18n.ts.behavior }}</template>
- <MkSelect v-model="nsfw">
- <template #label>{{ i18n.ts.nsfw }}</template>
- <option value="respect">{{ i18n.ts._nsfw.respect }}</option>
- <option value="ignore">{{ i18n.ts._nsfw.ignore }}</option>
- <option value="force">{{ i18n.ts._nsfw.force }}</option>
- </MkSelect>
+ <div class="_gaps_m">
+ <div class="_gaps_s">
+ <MkSwitch v-model="imageNewTab">{{ i18n.ts.openImageInNewTab }}</MkSwitch>
+ <MkSwitch v-model="enableInfiniteScroll">{{ i18n.ts.enableInfiniteScroll }}</MkSwitch>
+ </div>
+ <MkSelect v-model="serverDisconnectedBehavior">
+ <template #label>{{ i18n.ts.whenServerDisconnected }}</template>
+ <option value="reload">{{ i18n.ts._serverDisconnectedBehavior.reload }}</option>
+ <option value="dialog">{{ i18n.ts._serverDisconnectedBehavior.dialog }}</option>
+ <option value="quiet">{{ i18n.ts._serverDisconnectedBehavior.quiet }}</option>
+ </MkSelect>
+ <MkRange v-model="numberOfPageCache" :min="1" :max="10" :step="1" easing>
+ <template #label>{{ i18n.ts.numberOfPageCache }}</template>
+ <template #caption>{{ i18n.ts.numberOfPageCacheDescription }}</template>
+ </MkRange>
+ </div>
+ </FormSection>
- <MkRange v-model="numberOfPageCache" :min="1" :max="10" :step="1" easing>
- <template #label>{{ i18n.ts.numberOfPageCache }}</template>
- <template #caption>{{ i18n.ts.numberOfPageCacheDescription }}</template>
- </MkRange>
+ <FormSection>
+ <MkSwitch v-model="aiChanMode">{{ i18n.ts.aiChanMode }}</MkSwitch>
+ </FormSection>
<FormLink to="/settings/deck">{{ i18n.ts.deck }}</FormLink>
@@ -160,6 +202,7 @@ const disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer'));
const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages'));
const forceShowAds = computed(defaultStore.makeGetterSetter('forceShowAds'));
const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages'));
+const enableDataSaverMode = computed(defaultStore.makeGetterSetter('enableDataSaverMode'));
const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab'));
const nsfw = computed(defaultStore.makeGetterSetter('nsfw'));
const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm'));
@@ -170,6 +213,9 @@ const enableInfiniteScroll = computed(defaultStore.makeGetterSetter('enableInfin
const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('useReactionPickerForContextMenu'));
const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars'));
const aiChanMode = computed(defaultStore.makeGetterSetter('aiChanMode'));
+const mediaListWithOneImageAppearance = computed(defaultStore.makeGetterSetter('mediaListWithOneImageAppearance'));
+const notificationPosition = computed(defaultStore.makeGetterSetter('notificationPosition'));
+const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis'));
watch(lang, () => {
miLocalStorage.setItem('lang', lang.value as string);
diff --git a/packages/frontend/src/pages/settings/import-export.vue b/packages/frontend/src/pages/settings/import-export.vue
index a8274f5601..89b4104020 100644
--- a/packages/frontend/src/pages/settings/import-export.vue
+++ b/packages/frontend/src/pages/settings/import-export.vue
@@ -32,7 +32,7 @@
<MkButton primary :class="$style.button" inline @click="exportFollowing()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
</div>
</MkFolder>
- <MkFolder>
+ <MkFolder v-if="$i && !$i.movedTo">
<template #label>{{ i18n.ts.import }}</template>
<template #icon><i class="ti ti-upload"></i></template>
<MkButton primary :class="$style.button" inline @click="importFollowing($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
@@ -47,7 +47,7 @@
<template #icon><i class="ti ti-download"></i></template>
<MkButton primary :class="$style.button" inline @click="exportUserLists()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
</MkFolder>
- <MkFolder>
+ <MkFolder v-if="$i && !$i.movedTo">
<template #label>{{ i18n.ts.import }}</template>
<template #icon><i class="ti ti-upload"></i></template>
<MkButton primary :class="$style.button" inline @click="importUserLists($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
@@ -62,7 +62,7 @@
<template #icon><i class="ti ti-download"></i></template>
<MkButton primary :class="$style.button" inline @click="exportMuting()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
</MkFolder>
- <MkFolder>
+ <MkFolder v-if="$i && !$i.movedTo">
<template #label>{{ i18n.ts.import }}</template>
<template #icon><i class="ti ti-upload"></i></template>
<MkButton primary :class="$style.button" inline @click="importMuting($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
@@ -77,13 +77,28 @@
<template #icon><i class="ti ti-download"></i></template>
<MkButton primary :class="$style.button" inline @click="exportBlocking()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
</MkFolder>
- <MkFolder>
+ <MkFolder v-if="$i && !$i.movedTo">
<template #label>{{ i18n.ts.import }}</template>
<template #icon><i class="ti ti-upload"></i></template>
<MkButton primary :class="$style.button" inline @click="importBlocking($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
</MkFolder>
</div>
</FormSection>
+ <FormSection>
+ <template #label><i class="ti ti-antenna"></i> {{ i18n.ts.antennas }}</template>
+ <div class="_gaps_s">
+ <MkFolder>
+ <template #label>{{ i18n.ts.export }}</template>
+ <template #icon><i class="ti ti-download"></i></template>
+ <MkButton primary :class="$style.button" inline @click="exportAntennas()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
+ </MkFolder>
+ <MkFolder v-if="$i && !$i.movedTo">
+ <template #label>{{ i18n.ts.import }}</template>
+ <template #icon><i class="ti ti-upload"></i></template>
+ <MkButton primary :class="$style.button" inline @click="importAntennas($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
+ </MkFolder>
+ </div>
+ </FormSection>
</div>
</template>
@@ -97,6 +112,7 @@ import * as os from '@/os';
import { selectFile } from '@/scripts/select-file';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
+import { $i } from '@/account';
const excludeMutingUsers = ref(false);
const excludeInactiveUsers = ref(false);
@@ -150,6 +166,10 @@ const exportMuting = () => {
os.api('i/export-mute', {}).then(onExportSuccess).catch(onError);
};
+const exportAntennas = () => {
+ os.api('i/export-antennas', {}).then(onExportSuccess).catch(onError);
+};
+
const importFollowing = async (ev) => {
const file = await selectFile(ev.currentTarget ?? ev.target);
os.api('i/import-following', { fileId: file.id }).then(onImportSuccess).catch(onError);
@@ -170,6 +190,11 @@ const importBlocking = async (ev) => {
os.api('i/import-blocking', { fileId: file.id }).then(onImportSuccess).catch(onError);
};
+const importAntennas = async (ev) => {
+ const file = await selectFile(ev.currentTarget ?? ev.target);
+ os.api('i/import-antennas', { fileId: file.id }).then(onImportSuccess).catch(onError);
+};
+
const headerActions = $computed(() => []);
const headerTabs = $computed(() => []);
diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue
index 17af7417fd..34a962ef4c 100644
--- a/packages/frontend/src/pages/settings/index.vue
+++ b/packages/frontend/src/pages/settings/index.vue
@@ -164,12 +164,12 @@ const menuDef = computed(() => [{
text: i18n.ts.importAndExport,
to: '/settings/import-export',
active: currentPage?.route.name === 'import-export',
- }, /*{
+ }, {
icon: 'ti ti-plane',
- text: i18n.ts.accountMigration,
+ text: `${i18n.ts.accountMigration} (${i18n.ts.experimental})`,
to: '/settings/migration',
active: currentPage?.route.name === 'migration',
- },*/ {
+ }, {
icon: 'ti ti-dots',
text: i18n.ts.other,
to: '/settings/other',
diff --git a/packages/frontend/src/pages/settings/migration.vue b/packages/frontend/src/pages/settings/migration.vue
index 2ef8af7481..541992875e 100644
--- a/packages/frontend/src/pages/settings/migration.vue
+++ b/packages/frontend/src/pages/settings/migration.vue
@@ -1,63 +1,121 @@
<template>
<div class="_gaps_m">
- <FormSection first>
+ <FormInfo warn>
+ {{ i18n.ts.thisIsExperimentalFeature }}
+ </FormInfo>
+ <MkFolder :default-open="true">
+ <template #icon><i class="ti ti-plane-arrival"></i></template>
+ <template #label>{{ i18n.ts._accountMigration.moveFrom }}</template>
+ <template #caption>{{ i18n.ts._accountMigration.moveFromSub }}</template>
+
+ <div class="_gaps_m">
+ <FormInfo>
+ {{ i18n.ts._accountMigration.moveFromDescription }}
+ </FormInfo>
+ <div>
+ <MkButton :disabled="accountAliases.length >= 10" inline style="margin-right: 8px;" @click="add"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
+ <MkButton inline primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
+ </div>
+ <div class="_gaps">
+ <MkInput v-for="(_, i) in accountAliases" v-model="accountAliases[i]">
+ <template #prefix><i class="ti ti-plane-arrival"></i></template>
+ <template #label>{{ i18n.t('_accountMigration.moveFromLabel', { n: i + 1 }) }}</template>
+ </MkInput>
+ </div>
+ </div>
+ </MkFolder>
+
+ <MkFolder :default-open="!!$i?.movedTo">
+ <template #icon><i class="ti ti-plane-departure"></i></template>
<template #label>{{ i18n.ts._accountMigration.moveTo }}</template>
- <MkInput v-model="moveToAccount" manual-save>
- <template #prefix><i class="ti ti-plane-departure"></i></template>
- <template #label>{{ i18n.ts._accountMigration.moveToLabel }}</template>
- </MkInput>
- </FormSection>
- <FormInfo warn>{{ i18n.ts._accountMigration.moveAccountDescription }}</FormInfo>
- <FormSection>
- <template #label>{{ i18n.ts._accountMigration.moveFrom }}</template>
- <MkInput v-model="accountAlias" manual-save>
- <template #prefix><i class="ti ti-plane-arrival"></i></template>
- <template #label>{{ i18n.ts._accountMigration.moveFromLabel }}</template>
- </MkInput>
- </FormSection>
- <FormInfo warn>{{ i18n.ts._accountMigration.moveFromDescription }}</FormInfo>
+ <div class="_gaps_m">
+ <FormInfo>{{ i18n.ts._accountMigration.moveAccountDescription }}</FormInfo>
+
+ <template v-if="$i && !$i.movedTo">
+ <FormInfo>{{ i18n.ts._accountMigration.moveAccountHowTo }}</FormInfo>
+ <FormInfo warn>{{ i18n.ts._accountMigration.moveCannotBeUndone }}</FormInfo>
+
+ <MkInput v-model="moveToAccount">
+ <template #prefix><i class="ti ti-plane-departure"></i></template>
+ <template #label>{{ i18n.ts._accountMigration.moveToLabel }}</template>
+ </MkInput>
+ <MkButton inline danger :disabled="!moveToAccount" @click="move">
+ <i class="ti ti-check"></i> {{ i18n.ts._accountMigration.startMigration }}
+ </MkButton>
+ </template>
+ <template v-else-if="$i">
+ <FormInfo>{{ i18n.ts._accountMigration.postMigrationNote }}</FormInfo>
+ <FormInfo warn>{{ i18n.ts._accountMigration.movedAndCannotBeUndone }}</FormInfo>
+ <div>{{ i18n.ts._accountMigration.movedTo }}</div>
+ <MkUserInfo v-if="movedTo" :user="movedTo" class="_panel _shadow" />
+ </template>
+ </div>
+ </MkFolder>
</div>
</template>
<script lang="ts" setup>
-import { ref, watch } from 'vue';
-import FormSection from '@/components/form/section.vue';
+import { ref } from 'vue';
import FormInfo from '@/components/MkInfo.vue';
import MkInput from '@/components/MkInput.vue';
+import MkButton from '@/components/MkButton.vue';
+import MkFolder from '@/components/MkFolder.vue';
+import MkUserInfo from '@/components/MkUserInfo.vue';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
+import { $i } from '@/account';
+import { toString } from 'misskey-js/built/acct';
+import { UserDetailed } from 'misskey-js/built/entities';
+import { unisonReload } from '@/scripts/unison-reload';
const moveToAccount = ref('');
-const accountAlias = ref('');
+const movedTo = ref<UserDetailed>();
+const accountAliases = ref(['']);
+
+async function init() {
+ if ($i?.movedTo) {
+ movedTo.value = await os.api('users/show', { userId: $i.movedTo });
+ } else {
+ moveToAccount.value = '';
+ }
+
+ if ($i?.alsoKnownAs && $i.alsoKnownAs.length > 0) {
+ const alsoKnownAs = await os.api('users/show', { userIds: $i.alsoKnownAs });
+ accountAliases.value = (alsoKnownAs && alsoKnownAs.length > 0) ? alsoKnownAs.map(user => `@${toString(user)}`) : [''];
+ } else {
+ accountAliases.value = [''];
+ }
+}
async function move(): Promise<void> {
const account = moveToAccount.value;
const confirm = await os.confirm({
type: 'warning',
- text: i18n.t('migrationConfirm', { account: account.toString() }),
+ text: i18n.t('_accountMigration.migrationConfirm', { account }),
});
if (confirm.canceled) return;
- os.apiWithDialog('i/move', {
+ await os.apiWithDialog('i/move', {
moveToAccount: account,
});
+ unisonReload();
+}
+
+function add(): void {
+ accountAliases.value.push('');
}
async function save(): Promise<void> {
- const account = accountAlias.value;
- os.apiWithDialog('i/known-as', {
- alsoKnownAs: account,
+ const alsoKnownAs = accountAliases.value.map(alias => alias.trim()).filter(alias => alias !== '');
+ const i = await os.apiWithDialog('i/update', {
+ alsoKnownAs,
});
+ $i.alsoKnownAs = i.alsoKnownAs;
+ init();
}
-watch(accountAlias, async () => {
- await save();
-});
-
-watch(moveToAccount, async () => {
- await move();
-});
+init();
definePageMetadata({
title: i18n.ts.accountMigration,
diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue
index 892ea61e75..776305d723 100644
--- a/packages/frontend/src/pages/settings/other.vue
+++ b/packages/frontend/src/pages/settings/other.vue
@@ -1,32 +1,85 @@
<template>
<div class="_gaps_m">
+ <!--
<MkSwitch v-model="$i.injectFeaturedNote" @update:model-value="onChangeInjectFeaturedNote">
- {{ i18n.ts.showFeaturedNotesInTimeline }}
+ <template #label>{{ i18n.ts.showFeaturedNotesInTimeline }}</template>
</MkSwitch>
+ -->
<!--
<MkSwitch v-model="reportError">{{ i18n.ts.sendErrorReports }}<template #caption>{{ i18n.ts.sendErrorReportsDescription }}</template></MkSwitch>
-->
- <FormLink to="/settings/account-info">{{ i18n.ts.accountInfo }}</FormLink>
+ <FormSection first>
+ <div class="_gaps_s">
+ <MkFolder>
+ <template #icon><i class="ti ti-info-circle"></i></template>
+ <template #label>{{ i18n.ts.accountInfo }}</template>
+
+ <div class="_gaps_m">
+ <MkKeyValue>
+ <template #key>ID</template>
+ <template #value><span class="_monospace">{{ $i.id }}</span></template>
+ </MkKeyValue>
+
+ <MkKeyValue>
+ <template #key>{{ i18n.ts.registeredDate }}</template>
+ <template #value><MkTime :time="$i.createdAt" mode="detail"/></template>
+ </MkKeyValue>
+
+ <FormLink to="/settings/account-stats"><template #icon><i class="ti ti-info-circle"></i></template>{{ i18n.ts.statistics }}</FormLink>
+ </div>
+ </MkFolder>
+
+ <MkFolder>
+ <template #icon><i class="ti ti-alert-triangle"></i></template>
+ <template #label>{{ i18n.ts.closeAccount }}</template>
- <FormLink to="/registry"><template #icon><i class="ti ti-adjustments"></i></template>{{ i18n.ts.registry }}</FormLink>
+ <div class="_gaps_m">
+ <FormInfo warn>{{ i18n.ts._accountDelete.mayTakeTime }}</FormInfo>
+ <FormInfo>{{ i18n.ts._accountDelete.sendEmail }}</FormInfo>
+ <MkButton v-if="!$i.isDeleted" danger @click="deleteAccount">{{ i18n.ts._accountDelete.requestAccountDelete }}</MkButton>
+ <MkButton v-else disabled>{{ i18n.ts._accountDelete.inProgress }}</MkButton>
+ </div>
+ </MkFolder>
- <FormLink to="/settings/delete-account"><template #icon><i class="ti ti-alert-triangle"></i></template>{{ i18n.ts.closeAccount }}</FormLink>
+ <MkFolder>
+ <template #icon><i class="ti ti-flask"></i></template>
+ <template #label>{{ i18n.ts.experimentalFeatures }}</template>
+
+ <div class="_gaps_m">
+ <MkSwitch v-model="enableCondensedLineForAcct">
+ <template #label>Enable condensed line for acct</template>
+ </MkSwitch>
+ </div>
+ </MkFolder>
+ </div>
+ </FormSection>
+
+ <FormSection>
+ <FormLink to="/registry"><template #icon><i class="ti ti-adjustments"></i></template>{{ i18n.ts.registry }}</FormLink>
+ </FormSection>
</div>
</template>
<script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, watch } from 'vue';
import MkSwitch from '@/components/MkSwitch.vue';
import FormLink from '@/components/form/link.vue';
+import MkFolder from '@/components/MkFolder.vue';
+import FormInfo from '@/components/MkInfo.vue';
+import MkKeyValue from '@/components/MkKeyValue.vue';
+import MkButton from '@/components/MkButton.vue';
import * as os from '@/os';
import { defaultStore } from '@/store';
-import { $i } from '@/account';
+import { signout, $i } from '@/account';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
+import { unisonReload } from '@/scripts/unison-reload';
+import FormSection from '@/components/form/section.vue';
const reportError = computed(defaultStore.makeGetterSetter('reportError'));
+const enableCondensedLineForAcct = computed(defaultStore.makeGetterSetter('enableCondensedLineForAcct'));
function onChangeInjectFeaturedNote(v) {
os.api('i/update', {
@@ -36,6 +89,48 @@ function onChangeInjectFeaturedNote(v) {
});
}
+async function deleteAccount() {
+ {
+ const { canceled } = await os.confirm({
+ type: 'warning',
+ text: i18n.ts.deleteAccountConfirm,
+ });
+ if (canceled) return;
+ }
+
+ const { canceled, result: password } = await os.inputText({
+ title: i18n.ts.password,
+ type: 'password',
+ });
+ if (canceled) return;
+
+ await os.apiWithDialog('i/delete-account', {
+ password: password,
+ });
+
+ await os.alert({
+ title: i18n.ts._accountDelete.started,
+ });
+
+ await signout();
+}
+
+async function reloadAsk() {
+ const { canceled } = await os.confirm({
+ type: 'info',
+ text: i18n.ts.reloadToApplySetting,
+ });
+ if (canceled) return;
+
+ unisonReload();
+}
+
+watch([
+ enableCondensedLineForAcct,
+], async () => {
+ await reloadAsk();
+});
+
const headerActions = $computed(() => []);
const headerTabs = $computed(() => []);
diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue
index 092b9a9cc8..6613ce4c1d 100644
--- a/packages/frontend/src/pages/settings/preferences-backups.vue
+++ b/packages/frontend/src/pages/settings/preferences-backups.vue
@@ -88,6 +88,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
'squareAvatars',
'numberOfPageCache',
'aiChanMode',
+ 'mediaListWithOneImageAppearance',
];
const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [
'lightTheme',
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index a5f6c11f89..6ffd682610 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -37,19 +37,40 @@
<template #icon><i class="ti ti-list"></i></template>
<template #label>{{ i18n.ts._profile.metadataEdit }}</template>
- <div class="_gaps_m">
- <FormSplit v-for="(record, i) in fields" :min-width="250">
- <MkInput v-model="record.name" small>
- <template #label>{{ i18n.ts._profile.metadataLabel }} #{{ i + 1 }}</template>
- </MkInput>
- <MkInput v-model="record.value" small>
- <template #label>{{ i18n.ts._profile.metadataContent }} #{{ i + 1 }}</template>
- </MkInput>
- </FormSplit>
- <div>
+ <div :class="$style.metadataRoot">
+ <div :class="$style.metadataMargin">
<MkButton :disabled="fields.length >= 16" inline style="margin-right: 8px;" @click="addField"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
+ <MkButton v-if="!fieldEditMode" :disabled="fields.length <= 1" inline danger style="margin-right: 8px;" @click="fieldEditMode = !fieldEditMode"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
+ <MkButton v-else inline style="margin-right: 8px;" @click="fieldEditMode = !fieldEditMode"><i class="ti ti-arrows-sort"></i> {{ i18n.ts.rearrange }}</MkButton>
<MkButton inline primary @click="saveFields"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
</div>
+
+ <Sortable
+ v-model="fields"
+ class="_gaps_s"
+ item-key="id"
+ :animation="150"
+ :handle="'.' + $style.dragItemHandle"
+ @start="e => e.item.classList.add('active')"
+ @end="e => e.item.classList.remove('active')"
+ >
+ <template #item="{element, index}">
+ <div :class="$style.fieldDragItem">
+ <button v-if="!fieldEditMode" class="_button" :class="$style.dragItemHandle" tabindex="-1"><i class="ti ti-menu"></i></button>
+ <button v-if="fieldEditMode" :disabled="fields.length <= 1" class="_button" :class="$style.dragItemRemove" @click="deleteField(index)"><i class="ti ti-x"></i></button>
+ <div :class="$style.dragItemForm">
+ <FormSplit :min-width="200">
+ <MkInput v-model="element.name" small>
+ <template #label>{{ i18n.ts._profile.metadataLabel }}</template>
+ </MkInput>
+ <MkInput v-model="element.value" small>
+ <template #label>{{ i18n.ts._profile.metadataContent }}</template>
+ </MkInput>
+ </FormSplit>
+ </div>
+ </div>
+ </template>
+ </Sortable>
</div>
</MkFolder>
<template #caption>{{ i18n.ts._profile.metadataDescription }}</template>
@@ -76,7 +97,7 @@
</template>
<script lang="ts" setup>
-import { computed, reactive, watch } from 'vue';
+import { computed, reactive, ref, watch, defineAsyncComponent, onMounted, onUnmounted } from 'vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
@@ -94,6 +115,8 @@ import { definePageMetadata } from '@/scripts/page-metadata';
import { claimAchievement } from '@/scripts/achievements';
import { defaultStore } from '@/store';
+const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
+
const reactionAcceptance = computed(defaultStore.makeGetterSetter('reactionAcceptance'));
const profile = reactive({
@@ -113,22 +136,28 @@ watch(() => profile, () => {
deep: true,
});
-const fields = reactive($i.fields.map(field => ({ name: field.name, value: field.value })));
+const fields = ref($i?.fields.map(field => ({ id: Math.random().toString(), name: field.name, value: field.value })) ?? []);
+const fieldEditMode = ref(false);
function addField() {
- fields.push({
+ fields.value.push({
+ id: Math.random().toString(),
name: '',
value: '',
});
}
-while (fields.length < 4) {
+while (fields.value.length < 4) {
addField();
}
+function deleteField(index: number) {
+ fields.value.splice(index, 1);
+}
+
function saveFields() {
os.apiWithDialog('i/update', {
- fields: fields.filter(field => field.name !== '' && field.value !== ''),
+ fields: fields.value.filter(field => field.name !== '' && field.value !== '').map(field => ({ name: field.name, value: field.value })),
});
}
@@ -248,3 +277,60 @@ definePageMetadata({
}
}
</style>
+<style lang="scss" module>
+.metadataRoot {
+ container-type: inline-size;
+}
+
+.metadataMargin {
+ margin-bottom: 1.5em;
+}
+
+.fieldDragItem {
+ display: flex;
+ padding-bottom: .75em;
+ align-items: flex-end;
+ border-bottom: solid 0.5px var(--divider);
+
+ &:last-child {
+ border-bottom: 0;
+ }
+
+ /* (drag button) 32px + (drag button margin) 8px + (input width) 200px * 2 + (input gap) 12px = 452px */
+ @container (max-width: 452px) {
+ align-items: center;
+ }
+}
+
+.dragItemHandle {
+ cursor: grab;
+ width: 32px;
+ height: 32px;
+ margin: 0 8px 0 0;
+ opacity: 0.5;
+ flex-shrink: 0;
+
+ &:active {
+ cursor: grabbing;
+ }
+}
+
+.dragItemRemove {
+ @extend .dragItemHandle;
+
+ color: #ff2a2a;
+ opacity: 1;
+ cursor: pointer;
+
+ &:hover, &:focus {
+ opacity: .7;
+ }
+ &:active {
+ cursor: pointer;
+ }
+}
+
+.dragItemForm {
+ flex-grow: 1;
+}
+</style>
diff --git a/packages/frontend/src/pages/timeline.tutorial.vue b/packages/frontend/src/pages/timeline.tutorial.vue
index 0d0c932a5c..32228d28f4 100644
--- a/packages/frontend/src/pages/timeline.tutorial.vue
+++ b/packages/frontend/src/pages/timeline.tutorial.vue
@@ -1,7 +1,7 @@
<template>
<div :class="$style.container">
<div :class="$style.title">
- <div :class="$style.titleText"><i class="ti ti-info-circle"></i> {{ i18n.ts._tutorial.title }}</div>
+ <div :class="$style.titleText"><i class="ti ti-info-circle"></i> {{ i18n.ts._timelineTutorial.title }}</div>
<div :class="$style.step">
<button class="_button" :class="$style.stepArrow" :disabled="tutorial === 0" @click="tutorial--">
<i class="ti ti-chevron-left"></i>
@@ -12,66 +12,30 @@
</button>
</div>
</div>
+
<div v-if="tutorial === 0" :class="$style.body">
- <div>{{ i18n.ts._tutorial.step1_1 }}</div>
- <div>{{ i18n.ts._tutorial.step1_2 }}</div>
- <div>{{ i18n.ts._tutorial.step1_3 }}</div>
+ <div>{{ i18n.t('_timelineTutorial.step1_1', { name: instance.name ?? host }) }}</div>
+ <div>{{ i18n.t('_timelineTutorial.step1_2', { name: instance.name ?? host }) }}</div>
</div>
<div v-else-if="tutorial === 1" :class="$style.body">
- <div>{{ i18n.ts._tutorial.step2_1 }}</div>
- <div>{{ i18n.ts._tutorial.step2_2 }}</div>
- <MkA class="_link" to="/settings/profile">{{ i18n.ts.editProfile }}</MkA>
+ <div>{{ i18n.ts._timelineTutorial.step2_1 }}</div>
+ <div>{{ i18n.t('_timelineTutorial.step2_2', { name: instance.name ?? host }) }}</div>
</div>
<div v-else-if="tutorial === 2" :class="$style.body">
- <div>{{ i18n.ts._tutorial.step3_1 }}</div>
- <div>{{ i18n.ts._tutorial.step3_2 }}</div>
- <div>{{ i18n.ts._tutorial.step3_3 }}</div>
- <small :class="$style.small">{{ i18n.ts._tutorial.step3_4 }}</small>
+ <div>{{ i18n.ts._timelineTutorial.step3_1 }}</div>
+ <div>{{ i18n.ts._timelineTutorial.step3_2 }}</div>
</div>
<div v-else-if="tutorial === 3" :class="$style.body">
- <div>{{ i18n.ts._tutorial.step4_1 }}</div>
- <div>{{ i18n.ts._tutorial.step4_2 }}</div>
- </div>
- <div v-else-if="tutorial === 4" :class="$style.body">
- <div>{{ i18n.ts._tutorial.step5_1 }}</div>
- <I18n :src="i18n.ts._tutorial.step5_2" tag="div">
- <template #featured>
- <MkA class="_link" to="/explore">{{ i18n.ts.featured }}</MkA>
- </template>
- <template #explore>
- <MkA class="_link" to="/explore#users">{{ i18n.ts.explore }}</MkA>
- </template>
- </I18n>
- <div>{{ i18n.ts._tutorial.step5_3 }}</div>
- <small :class="$style.small">{{ i18n.ts._tutorial.step5_4 }}</small>
- </div>
- <div v-else-if="tutorial === 5" :class="$style.body">
- <div>{{ i18n.ts._tutorial.step6_1 }}</div>
- <div>{{ i18n.ts._tutorial.step6_2 }}</div>
- <div>{{ i18n.ts._tutorial.step6_3 }}</div>
- </div>
- <div v-else-if="tutorial === 6" :class="$style.body">
- <div>{{ i18n.ts._tutorial.step7_1 }}</div>
- <I18n :src="i18n.ts._tutorial.step7_2" tag="div">
- <template #help>
- <a href="https://misskey-hub.net/help.html" target="_blank" class="_link">{{ i18n.ts.help }}</a>
- </template>
- </I18n>
- <div>{{ i18n.ts._tutorial.step7_3 }}</div>
- </div>
- <div v-else-if="tutorial === 7" :class="$style.body">
- <div>{{ i18n.ts._tutorial.step8_1 }}</div>
- <div>{{ i18n.ts._tutorial.step8_2 }}</div>
- <small :class="$style.small">{{ i18n.ts._tutorial.step8_3 }}</small>
+ <div>{{ i18n.ts._timelineTutorial.step4_1 }}</div>
+ <div>{{ i18n.ts._timelineTutorial.step4_2 }}</div>
</div>
<div :class="$style.footer">
<template v-if="tutorial === tutorialsNumber - 1">
- <MkPushNotificationAllowButton :class="$style.footerItem" primary show-only-to-register @click="tutorial = -1"/>
- <MkButton :class="$style.footerItem" :primary="false" @click="tutorial = -1">{{ i18n.ts.noThankYou }}</MkButton>
+ <MkButton :class="$style.footerItem" primary rounded gradate @click="tutorial = -1">{{ i18n.ts.done }} <i class="ti ti-check"></i></MkButton>
</template>
<template v-else>
- <MkButton :class="$style.footerItem" primary @click="tutorial++"><i class="ti ti-check"></i> {{ i18n.ts.next }}</MkButton>
+ <MkButton :class="$style.footerItem" primary rounded gradate @click="tutorial++">{{ i18n.ts.next }} <i class="ti ti-arrow-right"></i></MkButton>
</template>
</div>
</div>
@@ -80,15 +44,16 @@
<script lang="ts" setup>
import { computed } from 'vue';
import MkButton from '@/components/MkButton.vue';
-import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
import { defaultStore } from '@/store';
import { i18n } from '@/i18n';
+import { instance } from '@/instance';
+import { host } from '@/config';
-const tutorialsNumber = 8;
+const tutorialsNumber = 4;
const tutorial = computed({
- get() { return defaultStore.reactiveState.tutorial.value || 0; },
- set(value) { defaultStore.set('tutorial', value); },
+ get() { return defaultStore.reactiveState.timelineTutorial.value || 0; },
+ set(value) { defaultStore.set('timelineTutorial', value); },
});
</script>
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index 9f13f7a1dd..1bf4cdc99a 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -3,7 +3,7 @@
<template #header><MkPageHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :display-my-avatar="true"/></template>
<MkSpacer :content-max="800">
<div ref="rootEl" v-hotkey.global="keymap">
- <XTutorial v-if="$i && defaultStore.reactiveState.tutorial.value != -1" class="_panel" style="margin-bottom: var(--margin);"/>
+ <XTutorial v-if="$i && defaultStore.reactiveState.timelineTutorial.value != -1" class="_panel" style="margin-bottom: var(--margin);"/>
<MkPostForm v-if="defaultStore.reactiveState.showFixedPostForm.value" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/>
<div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index 8c3478d8f2..5bc1578268 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -7,7 +7,7 @@
<!-- <div class="punished" v-if="user.isSilenced"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i> {{ i18n.ts.userSilenced }}</div> -->
<div class="profile _gaps">
- <MkAccountMoved v-if="user.movedToUri" :host="user.movedToUri.host" :acct="user.movedToUri.username"/>
+ <MkAccountMoved v-if="user.movedTo" :moved-to="user.movedTo"/>
<MkRemoteCaution v-if="user.host != null" :href="user.url ?? user.uri!" class="warn"/>
<div :key="user.id" class="main _panel">
@@ -21,6 +21,9 @@
<span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--badge);"><i class="ti ti-shield"></i></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>
+ <button v-if="!isEditingMemo && !memoDraft" class="_button add-note-button" @click="showMemoTextarea">
+ <i class="ti ti-edit"/> {{ i18n.ts.addMemo }}
+ </button>
</div>
</div>
<span v-if="$i && $i.id != user.id && user.isFollowed" class="followed">{{ i18n.ts.followsYou }}</span>
@@ -45,6 +48,22 @@
{{ role.name }}
</span>
</div>
+ <div v-if="iAmModerator" class="moderationNote">
+ <MkTextarea v-model="moderationNote" manual-save>
+ <template #label>Moderation note</template>
+ </MkTextarea>
+ </div>
+ <div v-if="isEditingMemo || memoDraft" class="memo" :class="{'no-memo': !memoDraft}">
+ <div class="heading" v-text="i18n.ts.memo"/>
+ <textarea
+ ref="memoTextareaEl"
+ v-model="memoDraft"
+ rows="1"
+ @focus="isEditingMemo = true"
+ @blur="updateMemo"
+ @input="adjustMemoTextarea"
+ />
+ </div>
<div class="description">
<MkOmit>
<Mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$i"/>
@@ -113,13 +132,14 @@
</template>
<script lang="ts" setup>
-import { defineAsyncComponent, computed, onMounted, onUnmounted } from 'vue';
+import { defineAsyncComponent, computed, onMounted, onUnmounted, nextTick, watch } from 'vue';
import calcAge from 's-age';
import * as misskey from 'misskey-js';
import MkNote from '@/components/MkNote.vue';
import MkFollowButton from '@/components/MkFollowButton.vue';
import MkAccountMoved from '@/components/MkAccountMoved.vue';
import MkRemoteCaution from '@/components/MkRemoteCaution.vue';
+import MkTextarea from '@/components/MkTextarea.vue';
import MkOmit from '@/components/MkOmit.vue';
import MkInfo from '@/components/MkInfo.vue';
import { getScrollPosition } from '@/scripts/scroll';
@@ -129,10 +149,11 @@ import { userPage } from '@/filters/user';
import * as os from '@/os';
import { useRouter } from '@/router';
import { i18n } from '@/i18n';
-import { $i } from '@/account';
+import { $i, iAmModerator } from '@/account';
import { dateString } from '@/filters/date';
import { confetti } from '@/scripts/confetti';
import MkNotes from '@/components/MkNotes.vue';
+import { api } from '@/os';
const XPhotos = defineAsyncComponent(() => import('./index.photos.vue'));
const XActivity = defineAsyncComponent(() => import('./index.activity.vue'));
@@ -151,6 +172,14 @@ let parallaxAnimationId = $ref<null | number>(null);
let narrow = $ref<null | boolean>(null);
let rootEl = $ref<null | HTMLElement>(null);
let bannerEl = $ref<null | HTMLElement>(null);
+let memoTextareaEl = $ref<null | HTMLElement>(null);
+let memoDraft = $ref(props.user.memo);
+let isEditingMemo = $ref(false);
+let moderationNote = $ref(props.user.moderationNote);
+
+watch($$(moderationNote), async () => {
+ await os.api('admin/update-user-note', { userId: props.user.id, text: moderationNote });
+});
const pagination = {
endpoint: 'users/notes' as const,
@@ -193,6 +222,31 @@ function parallax() {
banner.style.backgroundPosition = `center calc(50% - ${pos}px)`;
}
+function showMemoTextarea() {
+ isEditingMemo = true;
+ nextTick(() => {
+ memoTextareaEl?.focus();
+ });
+}
+
+function adjustMemoTextarea() {
+ if (!memoTextareaEl) return;
+ memoTextareaEl.style.height = '0px';
+ memoTextareaEl.style.height = `${memoTextareaEl.scrollHeight}px`;
+}
+
+async function updateMemo() {
+ await api('users/update-memo', {
+ memo: memoDraft,
+ userId: props.user.id,
+ });
+ isEditingMemo = false;
+}
+
+watch([props.user], () => {
+ memoDraft = props.user.memo;
+});
+
onMounted(() => {
window.requestAnimationFrame(parallaxLoop);
narrow = rootEl!.clientWidth < 1000;
@@ -208,6 +262,9 @@ onMounted(() => {
});
}
}
+ nextTick(() => {
+ adjustMemoTextarea();
+ });
});
onUnmounted(() => {
@@ -323,6 +380,16 @@ onUnmounted(() => {
font-weight: bold;
}
}
+
+ > .add-note-button {
+ background: rgba(0, 0, 0, 0.2);
+ color: #fff;
+ -webkit-backdrop-filter: var(--blur, blur(8px));
+ backdrop-filter: var(--blur, blur(8px));
+ border-radius: 24px;
+ padding: 4px 8px;
+ font-size: 80%;
+ }
}
}
}
@@ -369,6 +436,43 @@ onUnmounted(() => {
}
}
+ > .moderationNote {
+ margin: 12px 24px 0 154px;
+ }
+
+ > .memo {
+ margin: 12px 24px 0 154px;
+ background: transparent;
+ color: var(--fg);
+ border: 1px solid var(--divider);
+ border-radius: 8px;
+ padding: 8px;
+ line-height: 0;
+
+ > .heading {
+ text-align: left;
+ color: var(--fgTransparent);
+ line-height: 1.5;
+ font-size: 85%;
+ }
+
+ textarea {
+ margin: 0;
+ padding: 0;
+ resize: none;
+ border: none;
+ outline: none;
+ width: 100%;
+ height: auto;
+ min-height: 0;
+ line-height: 1.5;
+ color: var(--fg);
+ overflow: hidden;
+ background: transparent;
+ font-family: inherit;
+ }
+ }
+
> .description {
padding: 24px 24px 24px 154px;
font-size: 0.95em;
@@ -504,6 +608,14 @@ onUnmounted(() => {
justify-content: center;
}
+ > .moderationNote {
+ margin: 16px 16px 0 16px;
+ }
+
+ > .memo {
+ margin: 16px 16px 0 16px;
+ }
+
> .description {
padding: 16px;
text-align: center;
diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue
index 4d8d76db18..929152bd5a 100644
--- a/packages/frontend/src/pages/welcome.entrance.a.vue
+++ b/packages/frontend/src/pages/welcome.entrance.a.vue
@@ -13,35 +13,7 @@
<MkEmoji :normal="true" :no-style="true" emoji="ðŸ®"/>
</div>
<div class="contents">
- <div class="main">
- <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" class="icon"/>
- <button class="_button _acrylic menu" @click="showMenu"><i class="ti ti-dots"></i></button>
- <div class="fg">
- <h1>
- <!-- 背景色ã«ã‚ˆã£ã¦ã¯ãƒ­ã‚´ãŒè¦‹ãˆãªããªã‚‹ã®ã§ã¨ã‚Šã‚ãˆãšç„¡åŠ¹ã« -->
- <!-- <img class="logo" v-if="meta.logoImageUrl" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span> -->
- <span class="text">{{ instanceName }}</span>
- </h1>
- <div class="about">
- <!-- eslint-disable-next-line vue/no-v-html -->
- <div class="desc" v-html="meta.description || i18n.ts.headlineMisskey"></div>
- </div>
- <div v-if="instance.disableRegistration" class="warn">
- <MkInfo warn>{{ i18n.ts.invitationRequiredToRegister }}</MkInfo>
- </div>
- <div class="action _gaps_s">
- <MkButton full rounded gradate data-cy-signup style="margin-right: 12px;" @click="signup()">{{ i18n.ts.joinThisServer }}</MkButton>
- <MkButton full rounded @click="exploreOtherServers()">{{ i18n.ts.exploreOtherServers }}</MkButton>
- <MkButton full rounded data-cy-signin @click="signin()">{{ i18n.ts.login }}</MkButton>
- </div>
- </div>
- </div>
- <div v-if="instance.policies.ltlAvailable" class="tl">
- <div class="title">{{ i18n.ts.letsLookAtTimeline }}</div>
- <div class="body">
- <MkTimeline src="local"/>
- </div>
- </div>
+ <MkVisitorDashboard/>
</div>
<div v-if="instances && instances.length > 0" class="federation">
<MarqueeText :duration="40">
@@ -60,16 +32,15 @@ import { } from 'vue';
import { Instance } from 'misskey-js/built/entities';
import XTimeline from './welcome.timeline.vue';
import MarqueeText from '@/components/MkMarquee.vue';
-import XSigninDialog from '@/components/MkSigninDialog.vue';
-import XSignupDialog from '@/components/MkSignupDialog.vue';
-import MkButton from '@/components/MkButton.vue';
import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue';
-import MkTimeline from '@/components/MkTimeline.vue';
import MkInfo from '@/components/MkInfo.vue';
import { instanceName } from '@/config';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { instance } from '@/instance';
+import number from '@/filters/number';
+import MkNumber from '@/components/MkNumber.vue';
+import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue';
let meta = $ref<Instance>();
let instances = $ref<any[]>();
@@ -84,45 +55,6 @@ os.apiGet('federation/instances', {
}).then(_instances => {
instances = _instances;
});
-
-function signin() {
- os.popup(XSigninDialog, {
- autoSet: true,
- }, {}, 'closed');
-}
-
-function signup() {
- os.popup(XSignupDialog, {
- autoSet: true,
- }, {}, 'closed');
-}
-
-function showMenu(ev) {
- os.popupMenu([{
- text: i18n.ts.instanceInfo,
- icon: 'ti ti-info-circle',
- action: () => {
- os.pageWindow('/about');
- },
- }, {
- text: i18n.ts.aboutMisskey,
- icon: 'ti ti-info-circle',
- action: () => {
- os.pageWindow('/about-misskey');
- },
- }, null, {
- text: i18n.ts.help,
- icon: 'ti ti-question-circle',
- action: () => {
- window.open('https://misskey-hub.net/help.md', '_blank');
- },
- }], ev.currentTarget ?? ev.target);
-}
-
-function exploreOtherServers() {
- // TODO: 言語をよã—ãªã«
- window.open('https://join.misskey.page/ja-JP/instances', '_blank');
-}
</script>
<style lang="scss" scoped>
@@ -202,89 +134,11 @@ function exploreOtherServers() {
position: relative;
width: min(430px, calc(100% - 32px));
margin-left: 128px;
- padding: 150px 0 100px 0;
+ padding: 100px 0 100px 0;
@media (max-width: 1200px) {
margin: auto;
}
-
- > .main {
- position: relative;
- background: var(--panel);
- border-radius: var(--radius);
- box-shadow: 0 12px 32px rgb(0 0 0 / 25%);
- text-align: center;
-
- > .icon {
- width: 85px;
- margin-top: -47px;
- border-radius: 100%;
- vertical-align: bottom;
- }
-
- > .menu {
- position: absolute;
- top: 16px;
- right: 16px;
- width: 32px;
- height: 32px;
- border-radius: 8px;
- font-size: 18px;
- }
-
- > .fg {
- position: relative;
- z-index: 1;
-
- > h1 {
- display: block;
- margin: 0;
- padding: 16px 32px 24px 32px;
- font-size: 1.4em;
-
- > .logo {
- vertical-align: bottom;
- max-height: 120px;
- max-width: min(100%, 300px);
- }
- }
-
- > .about {
- padding: 0 32px;
- }
-
- > .warn {
- padding: 32px 32px 0 32px;
- }
-
- > .action {
- padding: 32px;
-
- > * {
- line-height: 28px;
- }
- }
- }
- }
-
- > .tl {
- position: relative;
- background: var(--panel);
- border-radius: var(--radius);
- overflow: clip;
- box-shadow: 0 12px 32px rgb(0 0 0 / 25%);
- margin-top: 16px;
-
- > .title {
- padding: 12px 16px;
- border-bottom: solid 1px var(--divider);
- }
-
- > .body {
- height: 350px;
- overflow: auto;
- }
- }
}
> .federation {
diff --git a/packages/frontend/src/pages/welcome.entrance.b.vue b/packages/frontend/src/pages/welcome.entrance.b.vue
deleted file mode 100644
index 03bf174710..0000000000
--- a/packages/frontend/src/pages/welcome.entrance.b.vue
+++ /dev/null
@@ -1,239 +0,0 @@
-<template>
-<div v-if="meta" class="rsqzvsbo">
- <div class="top">
- <MkFeaturedPhotos class="bg"/>
- <XTimeline class="tl"/>
- <div class="shape"></div>
- <div class="main">
- <h1>
- <img v-if="meta.logoImageUrl" class="logo" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span>
- </h1>
- <div class="about">
- <!-- eslint-disable-next-line vue/no-v-html -->
- <div class="desc" v-html="meta.description || i18n.ts.headlineMisskey"></div>
- </div>
- <div class="action">
- <MkButton class="signup" inline gradate @click="signup()">{{ i18n.ts.signup }}</MkButton>
- <MkButton class="signin" inline @click="signin()">{{ i18n.ts.login }}</MkButton>
- </div>
- <div v-if="onlineUsersCount && stats" class="status">
- <div>
- <I18n :src="i18n.ts.nUsers" text-tag="span" class="users">
- <template #n><b>{{ number(stats.originalUsersCount) }}</b></template>
- </I18n>
- <I18n :src="i18n.ts.nNotes" text-tag="span" class="notes">
- <template #n><b>{{ number(stats.originalNotesCount) }}</b></template>
- </I18n>
- </div>
- <I18n :src="i18n.ts.onlineUsersCount" text-tag="span" class="online">
- <template #n><b>{{ onlineUsersCount }}</b></template>
- </I18n>
- </div>
- </div>
- <img src="/client-assets/misskey.svg" class="misskey"/>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-import { toUnicode } from 'punycode/';
-import XTimeline from './welcome.timeline.vue';
-import XSigninDialog from '@/components/MkSigninDialog.vue';
-import XSignupDialog from '@/components/MkSignupDialog.vue';
-import MkButton from '@/components/MkButton.vue';
-import MkNote from '@/components/MkNote.vue';
-import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue';
-import { host, instanceName } from '@/config';
-import * as os from '@/os';
-import number from '@/filters/number';
-import { i18n } from '@/i18n';
-
-export default defineComponent({
- components: {
- MkButton,
- MkNote,
- XTimeline,
- MkFeaturedPhotos,
- },
-
- data() {
- return {
- host: toUnicode(host),
- instanceName,
- meta: null,
- stats: null,
- tags: [],
- onlineUsersCount: null,
- i18n,
- };
- },
-
- created() {
- os.api('meta', { detail: true }).then(meta => {
- this.meta = meta;
- });
-
- os.api('stats').then(stats => {
- this.stats = stats;
- });
-
- os.api('get-online-users-count').then(res => {
- this.onlineUsersCount = res.count;
- });
-
- os.api('hashtags/list', {
- sort: '+mentionedLocalUsers',
- limit: 8,
- }).then(tags => {
- this.tags = tags;
- });
- },
-
- methods: {
- signin() {
- os.popup(XSigninDialog, {
- autoSet: true,
- }, {}, 'closed');
- },
-
- signup() {
- os.popup(XSignupDialog, {
- autoSet: true,
- }, {}, 'closed');
- },
-
- showMenu(ev) {
- os.popupMenu([{
- text: i18n.t('aboutX', { x: instanceName }),
- icon: 'ti ti-info-circle',
- action: () => {
- os.pageWindow('/about');
- },
- }, {
- text: i18n.ts.aboutMisskey,
- icon: 'ti ti-info-circle',
- action: () => {
- os.pageWindow('/about-misskey');
- },
- }, null, {
- text: i18n.ts.help,
- icon: 'ti ti-question-circle',
- action: () => {
- window.open('https://misskey-hub.net/help.md', '_blank');
- },
- }], ev.currentTarget ?? ev.target);
- },
-
- number,
- },
-});
-</script>
-
-<style lang="scss" scoped>
-.rsqzvsbo {
- > .top {
- min-height: 100vh;
- box-sizing: border-box;
-
- > .bg {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- }
-
- > .tl {
- position: absolute;
- top: 0;
- bottom: 0;
- right: 64px;
- margin: auto;
- width: 500px;
- height: calc(100% - 128px);
- overflow: hidden;
- -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
- mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
- }
-
- > .shape {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: var(--accent);
- clip-path: polygon(0% 0%, 40% 0%, 22% 100%, 0% 100%);
- }
-
- > .misskey {
- position: absolute;
- bottom: 64px;
- left: 64px;
- width: 160px;
- }
-
- > .main {
- position: relative;
- width: min(450px, 100%);
- padding: 64px;
- color: #fff;
- font-size: 1.1em;
-
- @media (max-width: 1200px) {
- margin: auto;
- }
-
- > h1 {
- display: block;
- margin: 0 0 32px 0;
- padding: 0;
-
- > .logo {
- vertical-align: bottom;
- max-height: 100px;
- }
- }
-
- > .about {
- padding: 0;
- }
-
- > .action {
- margin: 32px 0;
-
- > * {
- line-height: 32px;
- }
-
- > .signup {
- background: var(--panel);
- color: var(--fg);
- }
-
- > .signin {
- background: var(--accent);
- color: inherit;
- }
- }
-
- > .status {
- margin: 32px 0;
- border-top: solid 1px rgba(255, 255, 255, 0.5);
- font-size: 90%;
-
- > div {
- padding: 16px 0;
-
- > span:not(:last-child) {
- padding-right: 1em;
- margin-right: 1em;
- border-right: solid 1px rgba(255, 255, 255, 0.5);
- }
- }
- }
- }
- }
-}
-</style>
diff --git a/packages/frontend/src/pages/welcome.entrance.c.vue b/packages/frontend/src/pages/welcome.entrance.c.vue
deleted file mode 100644
index eca4e5764d..0000000000
--- a/packages/frontend/src/pages/welcome.entrance.c.vue
+++ /dev/null
@@ -1,308 +0,0 @@
-<template>
-<div v-if="meta" class="rsqzvsbo">
- <div class="top">
- <MkFeaturedPhotos class="bg"/>
- <div class="fade"></div>
- <div class="emojis">
- <MkEmoji :normal="true" :no-style="true" emoji="ðŸ‘"/>
- <MkEmoji :normal="true" :no-style="true" emoji="â¤"/>
- <MkEmoji :normal="true" :no-style="true" emoji="😆"/>
- <MkEmoji :normal="true" :no-style="true" emoji="🎉"/>
- <MkEmoji :normal="true" :no-style="true" emoji="ðŸ®"/>
- </div>
- <div class="main">
- <img src="/client-assets/misskey.svg" class="misskey"/>
- <div class="form _panel">
- <div class="bg">
- <div class="fade"></div>
- </div>
- <div class="fg">
- <h1>
- <img v-if="meta.logoImageUrl" class="logo" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span>
- </h1>
- <div class="about">
- <!-- eslint-disable-next-line vue/no-v-html -->
- <div class="desc" v-html="meta.description || i18n.ts.headlineMisskey"></div>
- </div>
- <div class="action">
- <MkButton inline gradate @click="signup()">{{ i18n.ts.signup }}</MkButton>
- <MkButton inline @click="signin()">{{ i18n.ts.login }}</MkButton>
- </div>
- <div v-if="onlineUsersCount && stats" class="status">
- <div>
- <I18n :src="i18n.ts.nUsers" text-tag="span" class="users">
- <template #n><b>{{ number(stats.originalUsersCount) }}</b></template>
- </I18n>
- <I18n :src="i18n.ts.nNotes" text-tag="span" class="notes">
- <template #n><b>{{ number(stats.originalNotesCount) }}</b></template>
- </I18n>
- </div>
- <I18n :src="i18n.ts.onlineUsersCount" text-tag="span" class="online">
- <template #n><b>{{ onlineUsersCount }}</b></template>
- </I18n>
- </div>
- <button class="_button _acrylic menu" @click="showMenu"><i class="ti ti-dots"></i></button>
- </div>
- </div>
- <nav class="nav">
- <MkA to="/announcements">{{ i18n.ts.announcements }}</MkA>
- <MkA to="/explore">{{ i18n.ts.explore }}</MkA>
- <MkA to="/channels">{{ i18n.ts.channel }}</MkA>
- <MkA to="/featured">{{ i18n.ts.featured }}</MkA>
- </nav>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-import { toUnicode } from 'punycode/';
-import XTimeline from './welcome.timeline.vue';
-import XSigninDialog from '@/components/MkSigninDialog.vue';
-import XSignupDialog from '@/components/MkSignupDialog.vue';
-import MkButton from '@/components/MkButton.vue';
-import MkNote from '@/components/MkNote.vue';
-import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue';
-import { host, instanceName } from '@/config';
-import * as os from '@/os';
-import number from '@/filters/number';
-import { i18n } from '@/i18n';
-
-export default defineComponent({
- components: {
- MkButton,
- MkNote,
- MkFeaturedPhotos,
- XTimeline,
- },
-
- data() {
- return {
- host: toUnicode(host),
- instanceName,
- meta: null,
- stats: null,
- tags: [],
- onlineUsersCount: null,
- i18n,
- };
- },
-
- created() {
- os.api('meta', { detail: true }).then(meta => {
- this.meta = meta;
- });
-
- os.api('stats').then(stats => {
- this.stats = stats;
- });
-
- os.api('get-online-users-count').then(res => {
- this.onlineUsersCount = res.count;
- });
-
- os.api('hashtags/list', {
- sort: '+mentionedLocalUsers',
- limit: 8,
- }).then(tags => {
- this.tags = tags;
- });
- },
-
- methods: {
- signin() {
- os.popup(XSigninDialog, {
- autoSet: true,
- }, {}, 'closed');
- },
-
- signup() {
- os.popup(XSignupDialog, {
- autoSet: true,
- }, {}, 'closed');
- },
-
- showMenu(ev) {
- os.popupMenu([{
- text: i18n.t('aboutX', { x: instanceName }),
- icon: 'ti ti-info-circle',
- action: () => {
- os.pageWindow('/about');
- },
- }, {
- text: i18n.ts.aboutMisskey,
- icon: 'ti ti-info-circle',
- action: () => {
- os.pageWindow('/about-misskey');
- },
- }, null, {
- text: i18n.ts.help,
- icon: 'ti ti-question-circle',
- action: () => {
- window.open('https://misskey-hub.net/help.md', '_blank');
- },
- }], ev.currentTarget ?? ev.target);
- },
-
- number,
- },
-});
-</script>
-
-<style lang="scss" scoped>
-.rsqzvsbo {
- > .top {
- display: flex;
- text-align: center;
- min-height: 100vh;
- box-sizing: border-box;
- padding: 16px;
-
- > .bg {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- }
-
- > .fade {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: rgba(0, 0, 0, 0.25);
- }
-
- > .emojis {
- position: absolute;
- bottom: 32px;
- left: 35px;
-
- > * {
- margin-right: 8px;
- }
-
- @media (max-width: 1200px) {
- display: none;
- }
- }
-
- > .main {
- position: relative;
- width: min(460px, 100%);
- margin: auto;
-
- > .misskey {
- width: 150px;
- margin-bottom: 16px;
-
- @media (max-width: 450px) {
- width: 130px;
- }
- }
-
- > .form {
- position: relative;
- box-shadow: 0 12px 32px rgb(0 0 0 / 25%);
-
- > .bg {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 128px;
- background-position: center;
- background-size: cover;
- opacity: 0.75;
-
- > .fade {
- position: absolute;
- bottom: 0;
- left: 0;
- width: 100%;
- height: 128px;
- background: linear-gradient(0deg, var(--panel), var(--X15));
- }
- }
-
- > .fg {
- position: relative;
- z-index: 1;
-
- > h1 {
- display: block;
- margin: 0;
- padding: 32px 32px 24px 32px;
-
- > .logo {
- vertical-align: bottom;
- max-height: 120px;
- }
- }
-
- > .about {
- padding: 0 32px;
- }
-
- > .action {
- padding: 32px;
-
- > * {
- line-height: 28px;
- }
- }
-
- > .status {
- border-top: solid 0.5px var(--divider);
- padding: 32px;
- font-size: 90%;
-
- > div {
- > span:not(:last-child) {
- padding-right: 1em;
- margin-right: 1em;
- border-right: solid 0.5px var(--divider);
- }
- }
-
- > .online {
- ::v-deep(b) {
- color: #41b781;
- }
-
- ::v-deep(span) {
- opacity: 0.7;
- }
- }
- }
-
- > .menu {
- position: absolute;
- top: 16px;
- right: 16px;
- width: 32px;
- height: 32px;
- border-radius: 8px;
- }
- }
- }
-
- > .nav {
- position: relative;
- z-index: 2;
- margin-top: 20px;
- color: #fff;
- text-shadow: 0 0 8px black;
- font-size: 0.9em;
-
- > *:not(:last-child) {
- margin-right: 1.5em;
- }
- }
- }
- }
-}
-</style>
diff --git a/packages/frontend/src/pages/welcome.setup.vue b/packages/frontend/src/pages/welcome.setup.vue
index 212d156a83..7728d97a65 100644
--- a/packages/frontend/src/pages/welcome.setup.vue
+++ b/packages/frontend/src/pages/welcome.setup.vue
@@ -1,8 +1,11 @@
<template>
-<form class="mk-setup" @submit.prevent="submit()">
- <h1>Welcome to Misskey!</h1>
- <div class="_gaps_m">
- <p>{{ i18n.ts.intro }}</p>
+<form :class="$style.root" class="_panel" @submit.prevent="submit()">
+ <div :class="$style.title">
+ <div>Welcome to Misskey!</div>
+ <div :class="$style.version">v{{ version }}</div>
+ </div>
+ <div class="_gaps_m" style="padding: 32px;">
+ <div>{{ i18n.ts.intro }}</div>
<MkInput v-model="username" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" required data-cy-admin-username>
<template #label>{{ i18n.ts.username }}</template>
<template #prefix>@</template>
@@ -12,8 +15,8 @@
<template #label>{{ i18n.ts.password }}</template>
<template #prefix><i class="ti ti-lock"></i></template>
</MkInput>
- <div class="bottom">
- <MkButton gradate type="submit" :disabled="submitting" data-cy-admin-ok>
+ <div>
+ <MkButton gradate large rounded type="submit" :disabled="submitting" data-cy-admin-ok style="margin: 0 auto;">
{{ submitting ? i18n.ts.processing : i18n.ts.done }}<MkEllipsis v-if="submitting"/>
</MkButton>
</div>
@@ -25,7 +28,7 @@
import { } from 'vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
-import { host } from '@/config';
+import { host, version } from '@/config';
import * as os from '@/os';
import { login } from '@/account';
import { i18n } from '@/i18n';
@@ -54,36 +57,28 @@ function submit() {
}
</script>
-<style lang="scss" scoped>
-.mk-setup {
+<style lang="scss" module>
+.root {
border-radius: var(--radius);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
overflow: hidden;
max-width: 500px;
margin: 32px auto;
+}
- > h1 {
- margin: 0;
- font-size: 1.5em;
- text-align: center;
- padding: 32px;
- background: var(--accent);
- color: #fff;
- }
-
- > div {
- padding: 32px;
- background: var(--panel);
-
- > p {
- margin-top: 0;
- }
+.title {
+ margin: 0;
+ font-size: 1.5em;
+ text-align: center;
+ padding: 32px;
+ background: var(--accentedBg);
+ color: var(--accent);
+ font-weight: bold;
+}
- > .bottom {
- > * {
- margin: 0 auto;
- }
- }
- }
+.version {
+ font-size: 70%;
+ font-weight: normal;
+ opacity: 0.7;
}
</style>
diff --git a/packages/frontend/src/pages/welcome.timeline.vue b/packages/frontend/src/pages/welcome.timeline.vue
index 6a507ee1ed..6ec6e3f863 100644
--- a/packages/frontend/src/pages/welcome.timeline.vue
+++ b/packages/frontend/src/pages/welcome.timeline.vue
@@ -33,7 +33,7 @@ import { $i } from '@/account';
let notes = $ref<Note[]>([]);
let isScrolling = $ref(false);
-let scrollEl = $ref<HTMLElement>();
+let scrollEl = $shallowRef<HTMLElement>();
os.apiGet('notes/featured').then(_notes => {
notes = _notes;
diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts
index 0769ec2614..e46c1eeb77 100644
--- a/packages/frontend/src/router.ts
+++ b/packages/frontend/src/router.ts
@@ -164,7 +164,7 @@ export const routes = [{
}, {
path: '/migration',
name: 'migration',
- component: page(() => import('./pages/settings/migration.vue'))
+ component: page(() => import('./pages/settings/migration.vue')),
}, {
path: '/custom-css',
name: 'general',
@@ -174,13 +174,9 @@ export const routes = [{
name: 'profile',
component: page(() => import('./pages/settings/accounts.vue')),
}, {
- path: '/account-info',
+ path: '/account-stats',
name: 'other',
- component: page(() => import('./pages/settings/account-info.vue')),
- }, {
- path: '/delete-account',
- name: 'other',
- component: page(() => import('./pages/settings/delete-account.vue')),
+ component: page(() => import('./pages/settings/account-stats.vue')),
}, {
path: '/other',
name: 'other',
@@ -428,6 +424,10 @@ export const routes = [{
name: 'other-settings',
component: page(() => import('./pages/admin/other-settings.vue')),
}, {
+ path: '/server-rules',
+ name: 'server-rules',
+ component: page(() => import('./pages/admin/server-rules.vue')),
+ }, {
path: '/',
component: page(() => import('./pages/_empty_.vue')),
}],
diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts
index 25e8b71a12..fbca005769 100644
--- a/packages/frontend/src/scripts/achievements.ts
+++ b/packages/frontend/src/scripts/achievements.ts
@@ -60,6 +60,7 @@ export const ACHIEVEMENT_TYPES = [
'iLoveMisskey',
'foundTreasure',
'client30min',
+ 'client60min',
'noteDeletedWithin1min',
'postedAtLateNight',
'postedAt0min0sec',
@@ -343,6 +344,11 @@ export const ACHIEVEMENT_BADGES = {
bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))',
frame: 'bronze',
},
+ 'client60min': {
+ img: '/fluent-emoji/1f552.png',
+ bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))',
+ frame: 'silver',
+ },
'noteDeletedWithin1min': {
img: '/fluent-emoji/1f5d1.png',
bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))',
@@ -458,6 +464,7 @@ const claimingQueue = new Set<string>();
export async function claimAchievement(type: typeof ACHIEVEMENT_TYPES[number]) {
if ($i == null) return;
+ if ($i.movedTo) return;
if (claimedAchievements.includes(type)) return;
claimingQueue.add(type);
claimedAchievements.push(type);
diff --git a/packages/frontend/src/scripts/aiscript/api.ts b/packages/frontend/src/scripts/aiscript/api.ts
index 1b47eaa420..b6b7445b67 100644
--- a/packages/frontend/src/scripts/aiscript/api.ts
+++ b/packages/frontend/src/scripts/aiscript/api.ts
@@ -17,6 +17,7 @@ export function createAiScriptEnv(opts) {
title: title.value,
text: text.value,
});
+ return values.NULL;
}),
'Mk:confirm': values.FN_NATIVE(async ([title, text, type]) => {
const confirm = await os.confirm({
diff --git a/packages/frontend/src/scripts/get-drive-file-menu.ts b/packages/frontend/src/scripts/get-drive-file-menu.ts
index 52e610e437..ed01b49054 100644
--- a/packages/frontend/src/scripts/get-drive-file-menu.ts
+++ b/packages/frontend/src/scripts/get-drive-file-menu.ts
@@ -36,6 +36,12 @@ function toggleSensitive(file: Misskey.entities.DriveFile) {
os.api('drive/files/update', {
fileId: file.id,
isSensitive: !file.isSensitive,
+ }).catch(err => {
+ os.alert({
+ type: 'error',
+ title: i18n.ts.error,
+ text: err.message,
+ });
});
}
@@ -74,6 +80,12 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile) {
icon: 'ti ti-text-caption',
action: () => describe(file),
}, null, {
+ text: i18n.ts.createNoteFromTheFile,
+ icon: 'ti ti-pencil',
+ action: () => os.post({
+ initialFiles: [file],
+ }),
+ }, {
text: i18n.ts.copyUrl,
icon: 'ti ti-link',
action: () => copyUrl(file),
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index d91f0b0eb6..c8a6100253 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -211,6 +211,12 @@ export function getNoteMenu(props: {
}, {}, 'closed');
}
+ function showRenotes(): void {
+ os.popup(defineAsyncComponent(() => import('@/components/MkRenotedUsersDialog.vue')), {
+ noteId: appearNote.id,
+ }, {}, 'closed');
+ }
+
async function translate(): Promise<void> {
if (props.translation.value != null) return;
props.translating.value = true;
@@ -241,8 +247,12 @@ export function getNoteMenu(props: {
text: i18n.ts.details,
action: openDetail,
}, {
- icon: 'ti ti-users',
- text: i18n.ts.reactions,
+ icon: 'ti ti-repeat',
+ text: i18n.ts.renotesList,
+ action: showRenotes,
+ }, {
+ icon: 'ti ti-icons',
+ text: i18n.ts.reactionsList,
action: showReactions,
}, {
icon: 'ti ti-copy',
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index fe941c77b2..6ff9fb63f1 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -98,6 +98,27 @@ export function getUserMenu(user: misskey.entities.UserDetailed, router: Router
});
}
+ async function editMemo(): Promise<void> {
+ const userDetailed = await os.api('users/show', {
+ userId: user.id,
+ });
+ const { canceled, result } = await os.form(i18n.ts.editMemo, {
+ memo: {
+ type: 'string',
+ required: true,
+ multiline: true,
+ label: i18n.ts.memo,
+ default: userDetailed.memo,
+ },
+ });
+ if (canceled) return;
+
+ os.apiWithDialog('users/update-memo', {
+ memo: result.memo,
+ userId: user.id,
+ });
+ }
+
let menu = [{
icon: 'ti ti-at',
text: i18n.ts.copyUsername,
@@ -123,6 +144,12 @@ export function getUserMenu(user: misskey.entities.UserDetailed, router: Router
os.post({ specified: user, initialText: `@${user.username} ` });
},
}, null, {
+ icon: 'ti ti-pencil',
+ text: i18n.ts.editMemo,
+ action: () => {
+ editMemo();
+ },
+ }, {
type: 'parent',
icon: 'ti ti-list',
text: i18n.ts.addToList,
diff --git a/packages/frontend/src/scripts/please-login.ts b/packages/frontend/src/scripts/please-login.ts
index b8fb853cc1..c101a127f3 100644
--- a/packages/frontend/src/scripts/please-login.ts
+++ b/packages/frontend/src/scripts/please-login.ts
@@ -17,5 +17,5 @@ export function pleaseLogin(path?: string) {
},
}, 'closed');
- if (!path) throw new Error('signin required');
+ throw new Error('signin required');
}
diff --git a/packages/frontend/src/scripts/select-file.ts b/packages/frontend/src/scripts/select-file.ts
index ec5f8f65e9..fe9f0a2447 100644
--- a/packages/frontend/src/scripts/select-file.ts
+++ b/packages/frontend/src/scripts/select-file.ts
@@ -6,70 +6,76 @@ import { i18n } from '@/i18n';
import { defaultStore } from '@/store';
import { uploadFile } from '@/scripts/upload';
-function select(src: any, label: string | null, multiple: boolean): Promise<DriveFile | DriveFile[]> {
+export function chooseFileFromPc(multiple: boolean, keepOriginal = false): Promise<DriveFile[]> {
return new Promise((res, rej) => {
- const keepOriginal = ref(defaultStore.state.keepOriginalUploading);
-
- const chooseFileFromPc = () => {
- const input = document.createElement('input');
- input.type = 'file';
- input.multiple = multiple;
- input.onchange = () => {
- const promises = Array.from(input.files).map(file => uploadFile(file, defaultStore.state.uploadFolder, undefined, keepOriginal.value));
+ const input = document.createElement('input');
+ input.type = 'file';
+ input.multiple = multiple;
+ input.onchange = () => {
+ const promises = Array.from(input.files).map(file => uploadFile(file, defaultStore.state.uploadFolder, undefined, keepOriginal));
- Promise.all(promises).then(driveFiles => {
- res(multiple ? driveFiles : driveFiles[0]);
- }).catch(err => {
- // アップロードã®ã‚¨ãƒ©ãƒ¼ã¯ uploadFile 内ã§ãƒãƒ³ãƒ‰ãƒªãƒ³ã‚°ã•れã¦ã„ã‚‹ãŸã‚アラートダイアログを出ã—ãŸã‚Šã¯ã—ã¦ã¯ã„ã‘ãªã„
- });
+ Promise.all(promises).then(driveFiles => {
+ res(driveFiles);
+ }).catch(err => {
+ // アップロードã®ã‚¨ãƒ©ãƒ¼ã¯ uploadFile 内ã§ãƒãƒ³ãƒ‰ãƒªãƒ³ã‚°ã•れã¦ã„ã‚‹ãŸã‚アラートダイアログを出ã—ãŸã‚Šã¯ã—ã¦ã¯ã„ã‘ãªã„
+ });
- // 一応廃棄
- (window as any).__misskey_input_ref__ = null;
- };
+ // 一応廃棄
+ (window as any).__misskey_input_ref__ = null;
+ };
- // https://qiita.com/fukasawah/items/b9dc732d95d99551013d
- // iOS Safari ã§æ­£å¸¸ã«å‹•ã‹ã™ç‚ºã®ãŠã¾ã˜ãªã„
- (window as any).__misskey_input_ref__ = input;
+ // https://qiita.com/fukasawah/items/b9dc732d95d99551013d
+ // iOS Safari ã§æ­£å¸¸ã«å‹•ã‹ã™ç‚ºã®ãŠã¾ã˜ãªã„
+ (window as any).__misskey_input_ref__ = input;
- input.click();
- };
+ input.click();
+ });
+}
- const chooseFileFromDrive = () => {
- os.selectDriveFile(multiple).then(files => {
- res(files);
- });
- };
+export function chooseFileFromDrive(multiple: boolean): Promise<DriveFile[]> {
+ return new Promise((res, rej) => {
+ os.selectDriveFile(multiple).then(files => {
+ res(files);
+ });
+ });
+}
- const chooseFileFromUrl = () => {
- os.inputText({
- title: i18n.ts.uploadFromUrl,
- type: 'url',
- placeholder: i18n.ts.uploadFromUrlDescription,
- }).then(({ canceled, result: url }) => {
- if (canceled) return;
+export function chooseFileFromUrl(): Promise<DriveFile> {
+ return new Promise((res, rej) => {
+ os.inputText({
+ title: i18n.ts.uploadFromUrl,
+ type: 'url',
+ placeholder: i18n.ts.uploadFromUrlDescription,
+ }).then(({ canceled, result: url }) => {
+ if (canceled) return;
- const marker = Math.random().toString(); // TODO: UUIDã¨ã‹ä½¿ã†
+ const marker = Math.random().toString(); // TODO: UUIDã¨ã‹ä½¿ã†
- const connection = stream.useChannel('main');
- connection.on('urlUploadFinished', urlResponse => {
- if (urlResponse.marker === marker) {
- res(multiple ? [urlResponse.file] : urlResponse.file);
- connection.dispose();
- }
- });
+ const connection = stream.useChannel('main');
+ connection.on('urlUploadFinished', urlResponse => {
+ if (urlResponse.marker === marker) {
+ res(urlResponse.file);
+ connection.dispose();
+ }
+ });
- os.api('drive/files/upload-from-url', {
- url: url,
- folderId: defaultStore.state.uploadFolder,
- marker,
- });
+ os.api('drive/files/upload-from-url', {
+ url: url,
+ folderId: defaultStore.state.uploadFolder,
+ marker,
+ });
- os.alert({
- title: i18n.ts.uploadFromUrlRequested,
- text: i18n.ts.uploadFromUrlMayTakeTime,
- });
+ os.alert({
+ title: i18n.ts.uploadFromUrlRequested,
+ text: i18n.ts.uploadFromUrlMayTakeTime,
});
- };
+ });
+ });
+}
+
+function select(src: any, label: string | null, multiple: boolean): Promise<DriveFile[]> {
+ return new Promise((res, rej) => {
+ const keepOriginal = ref(defaultStore.state.keepOriginalUploading);
os.popupMenu([label ? {
text: label,
@@ -81,23 +87,23 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Driv
}, {
text: i18n.ts.upload,
icon: 'ti ti-upload',
- action: chooseFileFromPc,
+ action: () => chooseFileFromPc(multiple, keepOriginal.value).then(files => res(files)),
}, {
text: i18n.ts.fromDrive,
icon: 'ti ti-cloud',
- action: chooseFileFromDrive,
+ action: () => chooseFileFromDrive(multiple).then(files => res(files)),
}, {
text: i18n.ts.fromUrl,
icon: 'ti ti-link',
- action: chooseFileFromUrl,
+ action: () => chooseFileFromUrl().then(file => res([file])),
}], src);
});
}
export function selectFile(src: any, label: string | null = null): Promise<DriveFile> {
- return select(src, label, false) as Promise<DriveFile>;
+ return select(src, label, false).then(files => files[0]);
}
export function selectFiles(src: any, label: string | null = null): Promise<DriveFile[]> {
- return select(src, label, true) as Promise<DriveFile[]>;
+ return select(src, label, true);
}
diff --git a/packages/frontend/src/scripts/show-moved-dialog.ts b/packages/frontend/src/scripts/show-moved-dialog.ts
new file mode 100644
index 0000000000..acb26c36e2
--- /dev/null
+++ b/packages/frontend/src/scripts/show-moved-dialog.ts
@@ -0,0 +1,16 @@
+import * as os from '@/os';
+import { $i } from '@/account';
+import { i18n } from '@/i18n';
+
+export function showMovedDialog() {
+ if (!$i) return;
+ if (!$i.movedTo) return;
+
+ os.alert({
+ type: 'error',
+ title: i18n.ts.accountMovedShort,
+ text: i18n.ts.operationForbidden,
+ });
+
+ throw new Error('account moved');
+}
diff --git a/packages/frontend/src/scripts/upload.ts b/packages/frontend/src/scripts/upload.ts
index 9a39652ef5..2dd11c9fa2 100644
--- a/packages/frontend/src/scripts/upload.ts
+++ b/packages/frontend/src/scripts/upload.ts
@@ -83,7 +83,13 @@ export function uploadFile(
// TODO: 消ã™ã®ã§ã¯ãªãã¦(ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯çš„ãªã‚¨ãƒ©ãƒ¼ãªã‚‰)å†é€ã§ãるよã†ã«ã—ãŸã„
uploads.value = uploads.value.filter(x => x.id !== id);
- if (ev.target?.response) {
+ if (xhr.status === 413) {
+ alert({
+ type: 'error',
+ title: i18n.ts.failedToUpload,
+ text: i18n.ts.cannotUploadBecauseExceedsFileSizeLimit,
+ });
+ } else if (ev.target?.response) {
const res = JSON.parse(ev.target.response);
if (res.error?.id === 'bec5bd69-fba3-43c9-b4fb-2894b66ad5d2') {
alert({
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index a935093240..245bcbefe1 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -38,7 +38,11 @@ export const pageViewInterruptors: PageViewInterruptor[] = [];
// TODO: ãれãžã‚Œã„ã¡ã„ã¡whereã¨ã‹defaultã¨ã„ã†ã‚­ãƒ¼ã‚’付ã‘ãªãゃã„ã‘ãªã„ã®å†—é•·ãªã®ã§ãªã‚“ã¨ã‹ã™ã‚‹(ãŸã åž‹å®šç¾©ãŒé¢å€’ã«ãªã‚Šãã†)
// ã‚ã¨ã€ç¾è¡Œã®å®šç¾©ã®ä»•æ–¹ãªã‚‰ã€ŒwhereãŒä½•ã§ã‚ã‚‹ã‹ã«é–¢ã‚らãšã‚­ãƒ¼åã®é‡è¤‡ä¸å¯ã€ã¨ã„ã†åˆ¶ç´„を付ã‘られるメリットもã‚ã‚‹ã‹ã‚‰ãã®ãƒ¡ãƒªãƒƒãƒˆã‚’引ãç¶™ãæ–¹æ³•も考ãˆãªã„ã¨ã„ã‘ãªã„
export const defaultStore = markRaw(new Storage('base', {
- tutorial: {
+ accountSetupWizard: {
+ where: 'account',
+ default: 0,
+ },
+ timelineTutorial: {
where: 'account',
default: 0,
},
@@ -164,7 +168,7 @@ export const defaultStore = markRaw(new Storage('base', {
},
animation: {
where: 'device',
- default: !matchMedia('(prefers-reduced-motion)').matches,
+ default: !window.matchMedia('(prefers-reduced-motion)').matches,
},
animatedMfm: {
where: 'device',
@@ -182,9 +186,13 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device',
default: false,
},
+ enableDataSaverMode: {
+ where: 'device',
+ default: false,
+ },
disableShowingAnimatedImages: {
where: 'device',
- default: matchMedia('(prefers-reduced-motion)').matches,
+ default: window.matchMedia('(prefers-reduced-motion)').matches,
},
emojiStyle: {
where: 'device',
@@ -306,6 +314,22 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device',
default: false,
},
+ mediaListWithOneImageAppearance: {
+ where: 'device',
+ default: 'expand' as 'expand' | '16_9' | '1_1' | '2_3',
+ },
+ notificationPosition: {
+ where: 'device',
+ default: 'rightBottom' as 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom',
+ },
+ notificationStackAxis: {
+ where: 'device',
+ default: 'horizontal' as 'vertical' | 'horizontal',
+ },
+ enableCondensedLineForAcct: {
+ where: 'device',
+ default: true,
+ },
}));
// TODO: ä»–ã®ã‚¿ãƒ–ã¨æ°¸ç¶šåŒ–ã•れãŸstateã‚’åŒæœŸ
diff --git a/packages/frontend/src/themes/d-botanical.json5 b/packages/frontend/src/themes/d-botanical.json5
index c03b95e2d7..33cf7aa817 100644
--- a/packages/frontend/src/themes/d-botanical.json5
+++ b/packages/frontend/src/themes/d-botanical.json5
@@ -13,8 +13,7 @@
fgHighlighted: '#fff',
divider: 'rgba(255, 255, 255, 0.14)',
panel: 'rgb(47, 47, 44)',
- panelHeaderBg: '@panel',
- panelHeaderDivider: '@divider',
+ panelHeaderDivider: 'rgba(0, 0, 0, 0)',
header: ':alpha<0.7<@panel',
navBg: '#363636',
renote: '@accent',
diff --git a/packages/frontend/src/themes/d-dark.json5 b/packages/frontend/src/themes/d-dark.json5
index d24ce4df69..63144e88ea 100644
--- a/packages/frontend/src/themes/d-dark.json5
+++ b/packages/frontend/src/themes/d-dark.json5
@@ -13,8 +13,7 @@
fgHighlighted: '#fff',
divider: 'rgba(255, 255, 255, 0.14)',
panel: '#2d2d2d',
- panelHeaderBg: '@panel',
- panelHeaderDivider: '@divider',
+ panelHeaderDivider: 'rgba(0, 0, 0, 0)',
header: ':alpha<0.7<@panel',
navBg: '#363636',
renote: '@accent',
diff --git a/packages/frontend/src/themes/d-future.json5 b/packages/frontend/src/themes/d-future.json5
index b6fa1ab0c1..0962a12411 100644
--- a/packages/frontend/src/themes/d-future.json5
+++ b/packages/frontend/src/themes/d-future.json5
@@ -14,8 +14,7 @@
fgOnAccent: '#000',
divider: 'rgba(255, 255, 255, 0.1)',
panel: '#18181c',
- panelHeaderBg: '@panel',
- panelHeaderDivider: '@divider',
+ panelHeaderDivider: 'rgba(0, 0, 0, 0)',
renote: '@accent',
mention: '#f2c97d',
mentionMe: '@accent',
diff --git a/packages/frontend/src/themes/d-green-lime.json5 b/packages/frontend/src/themes/d-green-lime.json5
index a6983b9ac2..9522f534a4 100644
--- a/packages/frontend/src/themes/d-green-lime.json5
+++ b/packages/frontend/src/themes/d-green-lime.json5
@@ -14,8 +14,7 @@
fgOnAccent: '#192320',
divider: '#e7fffb24',
panel: '#192320',
- panelHeaderBg: '@panel',
- panelHeaderDivider: '@divider',
+ panelHeaderDivider: 'rgba(0, 0, 0, 0)',
popup: '#293330',
renote: '@accent',
mentionMe: '#ffaa00',
diff --git a/packages/frontend/src/themes/d-green-orange.json5 b/packages/frontend/src/themes/d-green-orange.json5
index 62adc39e29..e542782c66 100644
--- a/packages/frontend/src/themes/d-green-orange.json5
+++ b/packages/frontend/src/themes/d-green-orange.json5
@@ -14,8 +14,7 @@
fgOnAccent: '#192320',
divider: '#e7fffb24',
panel: '#192320',
- panelHeaderBg: '@panel',
- panelHeaderDivider: '@divider',
+ panelHeaderDivider: 'rgba(0, 0, 0, 0)',
popup: '#293330',
renote: '@accent',
mentionMe: '#b4e900',
diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts
index eae4f0091c..53042a4ce7 100644
--- a/packages/frontend/src/ui/_common_/common.ts
+++ b/packages/frontend/src/ui/_common_/common.ts
@@ -76,7 +76,7 @@ export function openInstanceMenu(ev: MouseEvent) {
} : undefined],
}, null, {
text: i18n.ts.help,
- icon: 'ti ti-question-circle',
+ icon: 'ti ti-help-circle',
action: () => {
window.open('https://misskey-hub.net/help.html', '_blank');
},
diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue
index 5a32c076a4..71a4285e9d 100644
--- a/packages/frontend/src/ui/_common_/common.vue
+++ b/packages/frontend/src/ui/_common_/common.vue
@@ -10,14 +10,16 @@
<XUpload v-if="uploads.length > 0"/>
<TransitionGroup
- tag="div" :class="$style.notifications"
+ tag="div" :class="[$style.notifications, $style[`notificationsPosition-${defaultStore.state.notificationPosition}`], $style[`notificationsStackAxis-${defaultStore.state.notificationStackAxis}`]]"
:move-class="defaultStore.state.animation ? $style.transition_notification_move : ''"
:enter-active-class="defaultStore.state.animation ? $style.transition_notification_enterActive : ''"
:leave-active-class="defaultStore.state.animation ? $style.transition_notification_leaveActive : ''"
:enter-from-class="defaultStore.state.animation ? $style.transition_notification_enterFrom : ''"
:leave-to-class="defaultStore.state.animation ? $style.transition_notification_leaveTo : ''"
>
- <XNotification v-for="notification in notifications" :key="notification.id" :notification="notification" :class="$style.notification"/>
+ <div v-for="notification in notifications" :key="notification.id" :class="$style.notification">
+ <XNotification :notification="notification"/>
+ </div>
</TransitionGroup>
<XStreamIndicator/>
@@ -30,7 +32,7 @@
</template>
<script lang="ts" setup>
-import { defineAsyncComponent } from 'vue';
+import { defineAsyncComponent, ref } from 'vue';
import * as misskey from 'misskey-js';
import { swInject } from './sw-inject';
import XNotification from './notification.vue';
@@ -85,7 +87,10 @@ if ($i) {
.transition_notification_leaveActive {
transition: opacity 0.3s, transform 0.3s !important;
}
-.transition_notification_enterFrom,
+.transition_notification_enterFrom {
+ opacity: 0;
+ transform: translateX(250px);
+}
.transition_notification_leaveTo {
opacity: 0;
transform: translateX(-250px);
@@ -94,35 +99,90 @@ if ($i) {
.notifications {
position: fixed;
z-index: 3900000;
- left: 0;
- width: 250px;
- top: 32px;
- padding: 0 32px;
+ padding: 0 var(--margin);
pointer-events: none;
- container-type: inline-size;
-}
+ display: flex;
-.notification {
- & + .notification {
- margin-top: 8px;
+ &.notificationsPosition-leftTop {
+ top: var(--margin);
+ left: 0;
+ }
+
+ &.notificationsPosition-rightTop {
+ top: var(--margin);
+ right: 0;
+ }
+
+ &.notificationsPosition-leftBottom {
+ bottom: calc(var(--minBottomSpacing) + var(--margin));
+ left: 0;
}
-}
-@media (max-width: 500px) {
- .notifications {
- top: initial;
+ &.notificationsPosition-rightBottom {
bottom: calc(var(--minBottomSpacing) + var(--margin));
- padding: 0 var(--margin);
- display: flex;
- flex-direction: column-reverse;
+ right: 0;
}
- .notification {
- & + .notification {
- margin-top: 0;
- margin-bottom: 8px;
+ &.notificationsStackAxis-vertical {
+ width: 250px;
+
+ &.notificationsPosition-leftTop,
+ &.notificationsPosition-rightTop {
+ flex-direction: column;
+
+ .notification {
+ & + .notification {
+ margin-top: 8px;
+ }
+ }
+ }
+
+ &.notificationsPosition-leftBottom,
+ &.notificationsPosition-rightBottom {
+ flex-direction: column-reverse;
+
+ .notification {
+ & + .notification {
+ margin-bottom: 8px;
+ }
+ }
}
}
+
+ &.notificationsStackAxis-horizontal {
+ width: 100%;
+
+ &.notificationsPosition-leftTop,
+ &.notificationsPosition-leftBottom {
+ flex-direction: row;
+
+ .notification {
+ & + .notification {
+ margin-left: 8px;
+ }
+ }
+ }
+
+ &.notificationsPosition-rightTop,
+ &.notificationsPosition-rightBottom {
+ flex-direction: row-reverse;
+
+ .notification {
+ & + .notification {
+ margin-right: 8px;
+ }
+ }
+ }
+
+ .notification {
+ width: 250px;
+ flex-shrink: 0;
+ }
+ }
+}
+
+.notification {
+ container-type: inline-size;
}
</style>
diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue
index ff0cba33ac..9605d1b22e 100644
--- a/packages/frontend/src/ui/deck/channel-column.vue
+++ b/packages/frontend/src/ui/deck/channel-column.vue
@@ -40,7 +40,7 @@ if (props.column.channelId == null) {
}
async function setChannel() {
- const channels = await os.api('channels/followed', {
+ const channels = await os.api('channels/my-favorites', {
limit: 100,
});
const { canceled, result: channel } = await os.select({
diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue
index e895847bd9..402bbe0352 100644
--- a/packages/frontend/src/ui/deck/column.vue
+++ b/packages/frontend/src/ui/deck/column.vue
@@ -22,7 +22,7 @@
<span :class="$style.title"><slot name="header"></slot></span>
<button v-tooltip="i18n.ts.settings" :class="$style.menu" class="_button" @click.stop="showSettingsMenu"><i class="ti ti-dots"></i></button>
</header>
- <div v-show="active" ref="body" v-container :class="$style.body">
+ <div v-show="active" ref="body" :class="$style.body">
<slot></slot>
</div>
</section>
@@ -243,7 +243,7 @@ function onDrop(ev) {
<style lang="scss" module>
.root {
--root-margin: 10px;
- --deckColumnHeaderHeight: 40px;
+ --deckColumnHeaderHeight: 38px;
height: 100%;
overflow: clip;
@@ -318,10 +318,7 @@ function onDrop(ev) {
background: var(--panelHeaderBg);
box-shadow: 0 1px 0 0 var(--panelHeaderDivider);
cursor: pointer;
-
- &, * {
- user-select: none;
- }
+ user-select: none;
}
.title {
@@ -365,7 +362,7 @@ function onDrop(ev) {
overflow-x: clip;
-webkit-overflow-scrolling: touch;
box-sizing: border-box;
- container-type: inline-size;
+ container-type: size;
background-color: var(--bg);
}
</style>
diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue
index 2462967515..27d0c26ac4 100644
--- a/packages/frontend/src/ui/universal.vue
+++ b/packages/frontend/src/ui/universal.vue
@@ -2,7 +2,7 @@
<div :class="[$style.root, { [$style.withWallpaper]: wallpaper }]">
<XSidebar v-if="!isMobile" :class="$style.sidebar"/>
- <MkStickyContainer v-container :class="$style.contents">
+ <MkStickyContainer :class="$style.contents">
<template #header><XStatusBars :class="$style.statusbars"/></template>
<main style="min-width: 0;" :style="{ background: pageMetadata?.value?.bg }" @contextmenu.stop="onContextmenu">
<div :class="$style.content" style="container-type: inline-size;">
diff --git a/packages/frontend/src/ui/universal.widgets.vue b/packages/frontend/src/ui/universal.widgets.vue
index d11649c603..3e0c38bb83 100644
--- a/packages/frontend/src/ui/universal.widgets.vue
+++ b/packages/frontend/src/ui/universal.widgets.vue
@@ -3,7 +3,7 @@
<XWidgets :class="$style.widgets" :edit="editMode" :widgets="widgets" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="editMode = false"/>
<button v-if="editMode" class="_textButton" style="font-size: 0.9em;" @click="editMode = false"><i class="ti ti-check"></i> {{ i18n.ts.editWidgetsExit }}</button>
- <button v-else class="_textButton mk-widget-edit" :class="$style.edit" style="font-size: 0.9em;" @click="editMode = true"><i class="ti ti-pencil"></i> {{ i18n.ts.editWidgets }}</button>
+ <button v-else class="_textButton" data-cy-widget-edit :class="$style.edit" style="font-size: 0.9em;" @click="editMode = true"><i class="ti ti-pencil"></i> {{ i18n.ts.editWidgets }}</button>
</div>
</template>
diff --git a/packages/frontend/src/ui/visitor.vue b/packages/frontend/src/ui/visitor.vue
index 6c96440ebd..623abbda39 100644
--- a/packages/frontend/src/ui/visitor.vue
+++ b/packages/frontend/src/ui/visitor.vue
@@ -1,19 +1,286 @@
<template>
-<DesignB/>
+<div class="mk-app">
+ <a v-if="root" href="https://github.com/misskey-dev/misskey" target="_blank" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:var(--panel); color:var(--fg); position: fixed; z-index: 10; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>
+
+ <div v-if="!narrow && !root" class="side">
+ <div class="banner" :style="{ backgroundImage: instance.backgroundImageUrl ? `url(${ instance.backgroundImageUrl })` : 'none' }"></div>
+ <div class="dashboard">
+ <MkVisitorDashboard/>
+ </div>
+ </div>
+
+ <div class="main">
+ <div v-if="!root" class="header">
+ <div v-if="narrow === false" class="wide">
+ <MkA to="/" class="link" active-class="active"><i class="ti ti-home icon"></i> {{ i18n.ts.home }}</MkA>
+ <MkA v-if="isTimelineAvailable" to="/timeline" class="link" active-class="active"><i class="ti ti-message icon"></i> {{ i18n.ts.timeline }}</MkA>
+ <MkA to="/explore" class="link" active-class="active"><i class="ti ti-hash icon"></i> {{ i18n.ts.explore }}</MkA>
+ <MkA to="/channels" class="link" active-class="active"><i class="ti ti-device-tv icon"></i> {{ i18n.ts.channel }}</MkA>
+ </div>
+ <div v-else-if="narrow === true" class="narrow">
+ <button class="menu _button" @click="showMenu = true">
+ <i class="ti ti-menu-2 icon"></i>
+ </button>
+ </div>
+ </div>
+ <div class="contents">
+ <main v-if="!root" style="container-type: inline-size;">
+ <RouterView/>
+ </main>
+ <main v-else>
+ <RouterView/>
+ </main>
+ </div>
+ </div>
+
+ <Transition :name="'tray-back'">
+ <div
+ v-if="showMenu"
+ class="menu-back _modalBg"
+ @click="showMenu = false"
+ @touchstart.passive="showMenu = false"
+ ></div>
+ </Transition>
+
+ <Transition :name="'tray'">
+ <div v-if="showMenu" class="menu">
+ <MkA to="/" class="link" active-class="active"><i class="ti ti-home icon"></i>{{ i18n.ts.home }}</MkA>
+ <MkA v-if="isTimelineAvailable" to="/timeline" class="link" active-class="active"><i class="ti ti-message icon"></i>{{ i18n.ts.timeline }}</MkA>
+ <MkA to="/explore" class="link" active-class="active"><i class="ti ti-hash icon"></i>{{ i18n.ts.explore }}</MkA>
+ <MkA to="/announcements" class="link" active-class="active"><i class="ti ti-speakerphone icon"></i>{{ i18n.ts.announcements }}</MkA>
+ <MkA to="/channels" class="link" active-class="active"><i class="ti ti-device-tv icon"></i>{{ i18n.ts.channel }}</MkA>
+ <div class="divider"></div>
+ <MkA to="/pages" class="link" active-class="active"><i class="ti ti-news icon"></i>{{ i18n.ts.pages }}</MkA>
+ <MkA to="/play" class="link" active-class="active"><i class="ti ti-player-play icon"></i>Play</MkA>
+ <MkA to="/gallery" class="link" active-class="active"><i class="ti ti-icons icon"></i>{{ i18n.ts.gallery }}</MkA>
+ <div class="action">
+ <button class="_buttonPrimary" @click="signup()">{{ i18n.ts.signup }}</button>
+ <button class="_button" @click="signin()">{{ i18n.ts.login }}</button>
+ </div>
+ </div>
+ </Transition>
+</div>
<XCommon/>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
-//import DesignA from './visitor/a.vue';
-import DesignB from './visitor/b.vue';
+<script lang="ts" setup>
+import { ComputedRef, onMounted, provide } from 'vue';
import XCommon from './_common_/common.vue';
+import { host, instanceName } from '@/config';
+import * as os from '@/os';
+import { instance } from '@/instance';
+import XSigninDialog from '@/components/MkSigninDialog.vue';
+import XSignupDialog from '@/components/MkSignupDialog.vue';
+import { ColdDeviceStorage, defaultStore } from '@/store';
+import { mainRouter } from '@/router';
+import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
+import { i18n } from '@/i18n';
+import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue';
+
+const DESKTOP_THRESHOLD = 1100;
+
+let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
+
+provide('router', mainRouter);
+provideMetadataReceiver((info) => {
+ pageMetadata = info;
+ if (pageMetadata.value) {
+ document.title = `${pageMetadata.value.title} | ${instanceName}`;
+ }
+});
+
+const announcements = {
+ endpoint: 'announcements',
+ limit: 10,
+};
+
+const isTimelineAvailable = $ref(instance.policies?.ltlAvailable || instance.policies?.gtlAvailable);
+
+let showMenu = $ref(false);
+let isDesktop = $ref(window.innerWidth >= DESKTOP_THRESHOLD);
+let narrow = $ref(window.innerWidth < 1280);
+let meta = $ref();
-export default defineComponent({
- components: {
- XCommon,
- //DesignA,
- DesignB,
- },
+const keymap = $computed(() => {
+ return {
+ 'd': () => {
+ if (ColdDeviceStorage.get('syncDeviceDarkMode')) return;
+ defaultStore.set('darkMode', !defaultStore.state.darkMode);
+ },
+ 's': () => {
+ mainRouter.push('/search');
+ },
+ };
+});
+
+const root = $computed(() => mainRouter.currentRoute.value.name === 'index');
+
+os.api('meta', { detail: true }).then(res => {
+ meta = res;
+});
+
+function signin() {
+ os.popup(XSigninDialog, {
+ autoSet: true,
+ }, {}, 'closed');
+}
+
+function signup() {
+ os.popup(XSignupDialog, {
+ autoSet: true,
+ }, {}, 'closed');
+}
+
+onMounted(() => {
+ if (!isDesktop) {
+ window.addEventListener('resize', () => {
+ if (window.innerWidth >= DESKTOP_THRESHOLD) isDesktop = true;
+ }, { passive: true });
+ }
+});
+
+defineExpose({
+ showMenu: $$(showMenu),
});
</script>
+
+<style>
+.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}
+</style>
+
+<style lang="scss" scoped>
+.tray-enter-active,
+.tray-leave-active {
+ opacity: 1;
+ transform: translateX(0);
+ transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
+}
+.tray-enter-from,
+.tray-leave-active {
+ opacity: 0;
+ transform: translateX(-240px);
+}
+
+.tray-back-enter-active,
+.tray-back-leave-active {
+ opacity: 1;
+ transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
+}
+.tray-back-enter-from,
+.tray-back-leave-active {
+ opacity: 0;
+}
+
+.mk-app {
+ display: flex;
+ min-height: 100vh;
+
+ > .side {
+ position: sticky;
+ top: 0;
+ left: 0;
+ width: 500px;
+ height: 100vh;
+ background: var(--accent);
+
+ > .banner {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ aspect-ratio: 1.5;
+ background-position: center;
+ background-size: cover;
+ -webkit-mask-image: linear-gradient(rgba(0, 0, 0, 1.0), transparent);
+ mask-image: linear-gradient(rgba(0, 0, 0, 1.0), transparent);
+ }
+
+ > .dashboard {
+ position: relative;
+ padding: 32px;
+ box-sizing: border-box;
+ max-height: 100%;
+ overflow: auto;
+ }
+ }
+
+ > .main {
+ flex: 1;
+ min-width: 0;
+
+ > .header {
+ background: var(--panel);
+
+ > .wide {
+ line-height: 50px;
+ padding: 0 16px;
+
+ > .link {
+ padding: 0 16px;
+ }
+ }
+
+ > .narrow {
+ > .menu {
+ padding: 16px;
+ }
+ }
+ }
+ }
+
+ > .menu-back {
+ position: fixed;
+ z-index: 1001;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ }
+
+ > .menu {
+ position: fixed;
+ z-index: 1001;
+ top: 0;
+ left: 0;
+ width: 240px;
+ height: 100vh;
+ background: var(--panel);
+
+ > .link {
+ display: block;
+ padding: 16px;
+
+ > .icon {
+ margin-right: 1em;
+ }
+ }
+
+ > .divider {
+ margin: 8px auto;
+ width: calc(100% - 32px);
+ border-top: solid 0.5px var(--divider);
+ }
+
+ > .action {
+ padding: 16px;
+
+ > button {
+ display: block;
+ width: 100%;
+ padding: 10px;
+ box-sizing: border-box;
+ text-align: center;
+ border-radius: 999px;
+
+ &._button {
+ background: var(--panel);
+ }
+
+ &:first-child {
+ margin-bottom: 16px;
+ }
+ }
+ }
+ }
+}
+</style>
diff --git a/packages/frontend/src/ui/visitor/a.vue b/packages/frontend/src/ui/visitor/a.vue
deleted file mode 100644
index 4761036075..0000000000
--- a/packages/frontend/src/ui/visitor/a.vue
+++ /dev/null
@@ -1,263 +0,0 @@
-<template>
-<div class="mk-app">
- <div v-if="mainRouter.currentRoute?.name === 'index'" class="banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }">
- <div>
- <h1 v-if="meta"><img v-if="meta.logoImageUrl" class="logo" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span></h1>
- <div v-if="meta" class="about">
- <!-- eslint-disable-next-line vue/no-v-html -->
- <div class="desc" v-html="meta.description || i18n.ts.introMisskey"></div>
- </div>
- <div class="action">
- <button class="_button primary" @click="signup()">{{ i18n.ts.signup }}</button>
- <button class="_button" @click="signin()">{{ i18n.ts.login }}</button>
- </div>
- </div>
- </div>
- <div v-else class="banner-mini" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }">
- <div>
- <h1 v-if="meta"><img v-if="meta.logoImageUrl" class="logo" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span></h1>
- </div>
- </div>
-
- <div class="main">
- <div ref="contents" class="contents" :class="{ wallpaper }">
- <header v-show="mainRouter.currentRoute?.name !== 'index'" ref="header" class="header">
- <XHeader :info="pageInfo"/>
- </header>
- <main ref="main" style="container-type: inline-size;">
- <RouterView/>
- </main>
- <div class="powered-by">
- <b><MkA to="/">{{ host }}</MkA></b>
- <small>Powered by <a href="https://github.com/misskey-dev/misskey" target="_blank">Misskey</a></small>
- </div>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-import XHeader from './header.vue';
-import { host, instanceName } from '@/config';
-import * as os from '@/os';
-import MkButton from '@/components/MkButton.vue';
-import { defaultStore, ColdDeviceStorage } from '@/store';
-import { mainRouter } from '@/router';
-import { instance } from '@/instance';
-import { i18n } from '@/i18n';
-
-const DESKTOP_THRESHOLD = 1100;
-
-export default defineComponent({
- components: {
- XHeader,
- MkButton,
- },
-
- data() {
- return {
- host,
- instanceName,
- pageInfo: null,
- meta: null,
- narrow: window.innerWidth < 1280,
- announcements: {
- endpoint: 'announcements',
- limit: 10,
- },
- mainRouter,
- isDesktop: window.innerWidth >= DESKTOP_THRESHOLD,
- defaultStore,
- instance,
- i18n,
- };
- },
-
- computed: {
- keymap(): any {
- return {
- 'd': () => {
- if (ColdDeviceStorage.get('syncDeviceDarkMode')) return;
- this.defaultStore.set('darkMode', !this.defaultStore.state.darkMode);
- },
- 's': () => {
- mainRouter.push('/search');
- },
- 'h|/': this.help,
- };
- },
- },
-
- created() {
- document.documentElement.style.overflowY = 'scroll';
-
- os.api('meta', { detail: true }).then(meta => {
- this.meta = meta;
- });
- },
-
- mounted() {
- if (!this.isDesktop) {
- window.addEventListener('resize', () => {
- if (window.innerWidth >= DESKTOP_THRESHOLD) this.isDesktop = true;
- }, { passive: true });
- }
- },
-
- methods: {
- // @ThatOneCalculator: Are these methods even used?
- // I can't find references to them anywhere else in the code...
-
- // setParallax(el) {
- // new simpleParallax(el);
- // },
-
- changePage(page) {
- if (page == null) return;
- // eslint-disable-next-line no-undef
- if (page[symbols.PAGE_INFO]) {
- // eslint-disable-next-line no-undef
- this.pageInfo = page[symbols.PAGE_INFO];
- }
- },
-
- top() {
- window.scroll({ top: 0, behavior: 'smooth' });
- },
-
- help() {
- window.open('https://misskey-hub.net/docs/keyboard-shortcut.md', '_blank');
- },
- },
-});
-</script>
-
-<style lang="scss" scoped>
-.mk-app {
- min-height: 100vh;
-
- > .banner {
- position: relative;
- width: 100%;
- text-align: center;
- background-position: center;
- background-size: cover;
-
- > div {
- height: 100%;
- background: rgba(0, 0, 0, 0.3);
-
- * {
- color: #fff;
- }
-
- > h1 {
- margin: 0;
- padding: 96px 32px 0 32px;
- text-shadow: 0 0 8px black;
-
- > .logo {
- vertical-align: bottom;
- max-height: 150px;
- }
- }
-
- > .about {
- padding: 32px;
- max-width: 580px;
- margin: 0 auto;
- box-sizing: border-box;
- text-shadow: 0 0 8px black;
- }
-
- > .action {
- padding-bottom: 64px;
-
- > button {
- display: inline-block;
- padding: 10px 20px;
- box-sizing: border-box;
- text-align: center;
- border-radius: 999px;
- background: var(--panel);
- color: var(--fg);
-
- &.primary {
- background: var(--accent);
- color: #fff;
- }
-
- &:first-child {
- margin-right: 16px;
- }
- }
- }
- }
- }
-
- > .banner-mini {
- position: relative;
- width: 100%;
- text-align: center;
- background-position: center;
- background-size: cover;
-
- > div {
- position: relative;
- z-index: 1;
- height: 100%;
- background: rgba(0, 0, 0, 0.3);
-
- * {
- color: #fff !important;
- }
-
- > header {
-
- }
-
- > h1 {
- margin: 0;
- padding: 32px;
- text-shadow: 0 0 8px black;
-
- > .logo {
- vertical-align: bottom;
- max-height: 100px;
- }
- }
- }
- }
-
- > .main {
- > .contents {
- position: relative;
- z-index: 1;
-
- > .header {
- position: sticky;
- top: 0;
- left: 0;
- z-index: 1000;
- }
-
- > .powered-by {
- padding: 28px;
- font-size: 14px;
- text-align: center;
- border-top: 1px solid var(--divider);
-
- > small {
- display: block;
- margin-top: 8px;
- opacity: 0.5;
- }
- }
- }
- }
-}
-</style>
-
-<style lang="scss">
-</style>
diff --git a/packages/frontend/src/ui/visitor/b.vue b/packages/frontend/src/ui/visitor/b.vue
deleted file mode 100644
index 5287a670c5..0000000000
--- a/packages/frontend/src/ui/visitor/b.vue
+++ /dev/null
@@ -1,266 +0,0 @@
-<template>
-<div class="mk-app">
- <a v-if="root" href="https://github.com/misskey-dev/misskey" target="_blank" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:var(--panel); color:var(--fg); position: fixed; z-index: 10; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>
-
- <div v-if="!narrow && !root" class="side">
- <XKanban class="kanban" full/>
- </div>
-
- <div class="main">
- <XKanban v-if="narrow && !root" class="banner" :powered-by="root"/>
-
- <div class="contents">
- <XHeader v-if="!root" class="header"/>
- <main v-if="!root" style="container-type: inline-size;">
- <RouterView/>
- </main>
- <main v-else>
- <RouterView/>
- </main>
- <div v-if="!root" class="powered-by">
- <b><MkA to="/">{{ host }}</MkA></b>
- <small>Powered by <a href="https://github.com/misskey-dev/misskey" target="_blank">Misskey</a></small>
- </div>
- </div>
- </div>
-
- <Transition :name="'tray-back'">
- <div
- v-if="showMenu"
- class="menu-back _modalBg"
- @click="showMenu = false"
- @touchstart.passive="showMenu = false"
- ></div>
- </Transition>
-
- <Transition :name="'tray'">
- <div v-if="showMenu" class="menu">
- <MkA to="/" class="link" active-class="active"><i class="ti ti-home icon"></i>{{ i18n.ts.home }}</MkA>
- <MkA v-if="isTimelineAvailable" to="/timeline" class="link" active-class="active"><i class="ti ti-message icon"></i>{{ i18n.ts.timeline }}</MkA>
- <MkA to="/explore" class="link" active-class="active"><i class="ti ti-hash icon"></i>{{ i18n.ts.explore }}</MkA>
- <MkA to="/announcements" class="link" active-class="active"><i class="ti ti-speakerphone icon"></i>{{ i18n.ts.announcements }}</MkA>
- <MkA to="/channels" class="link" active-class="active"><i class="ti ti-device-tv icon"></i>{{ i18n.ts.channel }}</MkA>
- <div class="divider"></div>
- <MkA to="/pages" class="link" active-class="active"><i class="ti ti-news icon"></i>{{ i18n.ts.pages }}</MkA>
- <MkA to="/play" class="link" active-class="active"><i class="ti ti-player-play icon"></i>Play</MkA>
- <MkA to="/gallery" class="link" active-class="active"><i class="ti ti-icons icon"></i>{{ i18n.ts.gallery }}</MkA>
- <div class="action">
- <button class="_buttonPrimary" @click="signup()">{{ i18n.ts.signup }}</button>
- <button class="_button" @click="signin()">{{ i18n.ts.login }}</button>
- </div>
- </div>
- </Transition>
-</div>
-</template>
-
-<script lang="ts" setup>
-import { ComputedRef, onMounted, provide } from 'vue';
-import XHeader from './header.vue';
-import XKanban from './kanban.vue';
-import { host, instanceName } from '@/config';
-import * as os from '@/os';
-import { instance } from '@/instance';
-import XSigninDialog from '@/components/MkSigninDialog.vue';
-import XSignupDialog from '@/components/MkSignupDialog.vue';
-import { ColdDeviceStorage, defaultStore } from '@/store';
-import { mainRouter } from '@/router';
-import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
-
-const DESKTOP_THRESHOLD = 1100;
-
-let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
-
-provide('router', mainRouter);
-provideMetadataReceiver((info) => {
- pageMetadata = info;
- if (pageMetadata.value) {
- document.title = `${pageMetadata.value.title} | ${instanceName}`;
- }
-});
-
-const announcements = {
- endpoint: 'announcements',
- limit: 10,
-};
-
-const isTimelineAvailable = $ref(instance.policies?.ltlAvailable || instance.policies?.gtlAvailable);
-
-let showMenu = $ref(false);
-let isDesktop = $ref(window.innerWidth >= DESKTOP_THRESHOLD);
-let narrow = $ref(window.innerWidth < 1280);
-let meta = $ref();
-
-const keymap = $computed(() => {
- return {
- 'd': () => {
- if (ColdDeviceStorage.get('syncDeviceDarkMode')) return;
- defaultStore.set('darkMode', !defaultStore.state.darkMode);
- },
- 's': () => {
- mainRouter.push('/search');
- },
- };
-});
-
-const root = $computed(() => mainRouter.currentRoute.value.name === 'index');
-
-os.api('meta', { detail: true }).then(res => {
- meta = res;
-});
-
-function signin() {
- os.popup(XSigninDialog, {
- autoSet: true,
- }, {}, 'closed');
-}
-
-function signup() {
- os.popup(XSignupDialog, {
- autoSet: true,
- }, {}, 'closed');
-}
-
-onMounted(() => {
- if (!isDesktop) {
- window.addEventListener('resize', () => {
- if (window.innerWidth >= DESKTOP_THRESHOLD) isDesktop = true;
- }, { passive: true });
- }
-});
-
-defineExpose({
- showMenu: $$(showMenu),
-});
-</script>
-
-<style>
-.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}
-</style>
-
-<style lang="scss" scoped>
-.tray-enter-active,
-.tray-leave-active {
- opacity: 1;
- transform: translateX(0);
- transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
-}
-.tray-enter-from,
-.tray-leave-active {
- opacity: 0;
- transform: translateX(-240px);
-}
-
-.tray-back-enter-active,
-.tray-back-leave-active {
- opacity: 1;
- transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
-}
-.tray-back-enter-from,
-.tray-back-leave-active {
- opacity: 0;
-}
-
-.mk-app {
- display: flex;
- min-height: 100vh;
- background-position: center;
- background-size: cover;
- background-attachment: fixed;
-
- > .side {
- width: 500px;
- height: 100vh;
-
- > .kanban {
- position: fixed;
- top: 0;
- left: 0;
- width: 500px;
- height: 100vh;
- overflow: auto;
- }
- }
-
- > .main {
- flex: 1;
- min-width: 0;
-
- > .banner {
- }
-
- > .contents {
- position: relative;
- z-index: 1;
-
- > .powered-by {
- padding: 28px;
- font-size: 14px;
- text-align: center;
- border-top: 1px solid var(--divider);
-
- > small {
- display: block;
- margin-top: 8px;
- opacity: 0.5;
- }
- }
- }
- }
-
- > .menu-back {
- position: fixed;
- z-index: 1001;
- top: 0;
- left: 0;
- width: 100vw;
- height: 100vh;
- }
-
- > .menu {
- position: fixed;
- z-index: 1001;
- top: 0;
- left: 0;
- width: 240px;
- height: 100vh;
- background: var(--panel);
-
- > .link {
- display: block;
- padding: 16px;
-
- > .icon {
- margin-right: 1em;
- }
- }
-
- > .divider {
- margin: 8px auto;
- width: calc(100% - 32px);
- border-top: solid 0.5px var(--divider);
- }
-
- > .action {
- padding: 16px;
-
- > button {
- display: block;
- width: 100%;
- padding: 10px;
- box-sizing: border-box;
- text-align: center;
- border-radius: 999px;
-
- &._button {
- background: var(--panel);
- }
-
- &:first-child {
- margin-bottom: 16px;
- }
- }
- }
- }
-}
-</style>
diff --git a/packages/frontend/src/ui/visitor/header.vue b/packages/frontend/src/ui/visitor/header.vue
deleted file mode 100644
index 7de81f6431..0000000000
--- a/packages/frontend/src/ui/visitor/header.vue
+++ /dev/null
@@ -1,211 +0,0 @@
-<template>
-<div class="sqxihjet">
- <div v-if="narrow === false" class="wide">
- <div class="content">
- <MkA to="/" class="link" active-class="active"><i class="ti ti-home icon"></i>{{ i18n.ts.home }}</MkA>
- <MkA v-if="isTimelineAvailable" to="/timeline" class="link" active-class="active"><i class="ti ti-message icon"></i>{{ i18n.ts.timeline }}</MkA>
- <MkA to="/explore" class="link" active-class="active"><i class="ti ti-hash icon"></i>{{ i18n.ts.explore }}</MkA>
- <MkA to="/channels" class="link" active-class="active"><i class="ti ti-device-tv icon"></i>{{ i18n.ts.channel }}</MkA>
- <div class="right">
- <button class="_button search" @click="search()"><i class="ti ti-search icon"></i><span>{{ i18n.ts.search }}</span></button>
- <button class="_buttonPrimary signup" @click="signup()">{{ i18n.ts.signup }}</button>
- <button class="_button login" @click="signin()">{{ i18n.ts.login }}</button>
- </div>
- </div>
- </div>
- <div v-else-if="narrow === true" class="narrow">
- <button class="menu _button" @click="$parent.showMenu = true">
- <i class="ti ti-menu-2 icon"></i>
- </button>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-import XSigninDialog from '@/components/MkSigninDialog.vue';
-import XSignupDialog from '@/components/MkSignupDialog.vue';
-import * as os from '@/os';
-import { instance } from '@/instance';
-import { mainRouter } from '@/router';
-import { i18n } from '@/i18n';
-
-export default defineComponent({
- data() {
- return {
- narrow: null,
- showMenu: false,
- isTimelineAvailable: instance.policies.ltlAvailable || instance.policies.gtlAvailable,
- i18n,
- };
- },
-
- mounted() {
- this.narrow = this.$el.clientWidth < 1300;
- },
-
- methods: {
- signin() {
- os.popup(XSigninDialog, {
- autoSet: true,
- }, {}, 'closed');
- },
-
- signup() {
- os.popup(XSignupDialog, {
- autoSet: true,
- }, {}, 'closed');
- },
-
- search() {
- mainRouter.push('/search');
- },
- },
-});
-</script>
-
-<style lang="scss" scoped>
-.sqxihjet {
- $height: 50px;
- position: sticky;
- width: 50px;
- top: 0;
- left: 0;
- z-index: 1000;
- line-height: $height;
- -webkit-backdrop-filter: var(--blur, blur(32px));
- backdrop-filter: var(--blur, blur(32px));
- background-color: var(--X16);
-
- > .wide {
- > .content {
- max-width: 1400px;
- margin: 0 auto;
- display: flex;
- align-items: center;
-
- > .link {
- $line: 3px;
- display: inline-block;
- padding: 0 16px;
- line-height: $height - ($line * 2);
- border-top: solid $line transparent;
- border-bottom: solid $line transparent;
-
- > .icon {
- margin-right: 0.5em;
- }
-
- &.page {
- border-bottom-color: var(--accent);
- }
- }
-
- > .page {
- > .title {
- display: inline-block;
- vertical-align: bottom;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- position: relative;
-
- > .icon + .text {
- margin-left: 8px;
- }
-
- > .avatar {
- $size: 32px;
- display: inline-block;
- width: $size;
- height: $size;
- vertical-align: middle;
- margin-right: 8px;
- pointer-events: none;
- }
-
- &._button {
- &:hover {
- color: var(--fgHighlighted);
- }
- }
-
- &.selected {
- box-shadow: 0 -2px 0 0 var(--accent) inset;
- color: var(--fgHighlighted);
- }
- }
-
- > .action {
- padding: 0 0 0 16px;
- }
- }
-
- > .right {
- margin-left: auto;
-
- > .search {
- background: var(--bg);
- border-radius: 999px;
- width: 230px;
- line-height: $height - 20px;
- margin-right: 16px;
- text-align: left;
-
- > * {
- opacity: 0.7;
- }
-
- > .icon {
- padding: 0 16px;
- }
- }
-
- > .signup {
- border-radius: 999px;
- padding: 0 24px;
- line-height: $height - 20px;
- }
-
- > .login {
- padding: 0 16px;
- }
- }
- }
- }
-
- > .narrow {
- display: flex;
-
- > .menu,
- > .action {
- width: $height;
- height: $height;
- font-size: 20px;
- }
-
- > .title {
- flex: 1;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- position: relative;
- text-align: center;
-
- > .icon + .text {
- margin-left: 8px;
- }
-
- > .avatar {
- $size: 32px;
- display: inline-block;
- width: $size;
- height: $size;
- vertical-align: middle;
- margin-right: 8px;
- pointer-events: none;
- }
- }
- }
-}
-</style>
diff --git a/packages/frontend/src/ui/visitor/kanban.vue b/packages/frontend/src/ui/visitor/kanban.vue
deleted file mode 100644
index ce7fcfe944..0000000000
--- a/packages/frontend/src/ui/visitor/kanban.vue
+++ /dev/null
@@ -1,261 +0,0 @@
-<!-- eslint-disable vue/no-v-html -->
-<template>
-<div class="rwqkcmrc" :style="{ backgroundImage: transparent ? 'none' : `url(${ instance.backgroundImageUrl })` }">
- <div class="back" :class="{ transparent }"></div>
- <div class="contents">
- <div class="wrapper">
- <h1 v-if="meta" :class="{ full }">
- <MkA to="/" class="link"><img v-if="meta.logoImageUrl" class="logo" :src="meta.logoImageUrl" alt="logo"><span v-else class="text">{{ instanceName }}</span></MkA>
- </h1>
- <template v-if="full">
- <div v-if="meta" class="about">
- <div class="desc" v-html="meta.description || i18n.ts.introMisskey"></div>
- </div>
- <div class="action">
- <button class="_buttonPrimary" @click="signup()">{{ i18n.ts.signup }}</button>
- <button class="_button" @click="signin()">{{ i18n.ts.login }}</button>
- </div>
- <div class="announcements panel">
- <header>{{ i18n.ts.announcements }}</header>
- <MkPagination v-slot="{items}" :pagination="announcements" class="list">
- <section v-for="announcement in items" :key="announcement.id" class="item">
- <div class="title">{{ announcement.title }}</div>
- <div class="content">
- <Mfm :text="announcement.text"/>
- <img v-if="announcement.imageUrl" :src="announcement.imageUrl" alt="announcement image"/>
- </div>
- </section>
- </MkPagination>
- </div>
- <div v-if="poweredBy" class="powered-by">
- <b><MkA to="/">{{ host }}</MkA></b>
- <small>Powered by <a href="https://github.com/misskey-dev/misskey" target="_blank">Misskey</a></small>
- </div>
- </template>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-import { host, instanceName } from '@/config';
-import * as os from '@/os';
-import MkPagination from '@/components/MkPagination.vue';
-import XSigninDialog from '@/components/MkSigninDialog.vue';
-import XSignupDialog from '@/components/MkSignupDialog.vue';
-import MkButton from '@/components/MkButton.vue';
-import { instance } from '@/instance';
-import { i18n } from '@/i18n';
-
-export default defineComponent({
- components: {
- MkPagination,
- MkButton,
- },
-
- props: {
- full: {
- type: Boolean,
- required: false,
- default: false,
- },
- transparent: {
- type: Boolean,
- required: false,
- default: false,
- },
- poweredBy: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
-
- data() {
- return {
- host,
- instanceName,
- pageInfo: null,
- meta: null,
- narrow: window.innerWidth < 1280,
- announcements: {
- endpoint: 'announcements',
- limit: 10,
- },
- instance,
- i18n,
- };
- },
-
- created() {
- os.api('meta', { detail: true }).then(meta => {
- this.meta = meta;
- });
- },
-
- methods: {
- signin() {
- os.popup(XSigninDialog, {
- autoSet: true,
- }, {}, 'closed');
- },
-
- signup() {
- os.popup(XSignupDialog, {
- autoSet: true,
- }, {}, 'closed');
- },
- },
-});
-</script>
-
-<style lang="scss" scoped>
-.rwqkcmrc {
- position: relative;
- text-align: center;
- background-position: center;
- background-size: cover;
- // TODO: パララックスã«ã—ãŸã„
-
- > .back {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: rgba(0, 0, 0, 0.3);
-
- &.transparent {
- -webkit-backdrop-filter: var(--blur, blur(12px));
- backdrop-filter: var(--blur, blur(12px));
- }
- }
-
- > .contents {
- position: relative;
- z-index: 1;
- height: inherit;
- overflow: auto;
-
- > .wrapper {
- max-width: 380px;
- padding: 0 16px;
- box-sizing: border-box;
- margin: 0 auto;
-
- > .panel {
- -webkit-backdrop-filter: var(--blur, blur(8px));
- backdrop-filter: var(--blur, blur(8px));
- background: rgba(0, 0, 0, 0.5);
- border-radius: var(--radius);
-
- &, * {
- color: #fff !important;
- }
- }
-
- > h1 {
- display: block;
- margin: 0;
- padding: 32px 0 32px 0;
- color: #fff;
-
- &.full {
- padding: 64px 0 0 0;
-
- > .link {
- > ::v-deep(.logo) {
- max-height: 130px;
- }
- }
- }
-
- > .link {
- display: block;
-
- > ::v-deep(.logo) {
- vertical-align: bottom;
- max-height: 100px;
- }
- }
- }
-
- > .about {
- display: block;
- margin: 24px 0;
- text-align: center;
- box-sizing: border-box;
- text-shadow: 0 0 8px black;
- color: #fff;
- }
-
- > .action {
- > button {
- display: block;
- width: 100%;
- padding: 10px;
- box-sizing: border-box;
- text-align: center;
- border-radius: 999px;
-
- &._button {
- background: var(--panel);
- }
-
- &:first-child {
- margin-bottom: 16px;
- }
- }
- }
-
- > .announcements {
- margin: 32px 0;
- text-align: left;
-
- > header {
- padding: 12px 16px;
- border-bottom: solid 1px rgba(255, 255, 255, 0.5);
- }
-
- > .list {
- max-height: 300px;
- overflow: auto;
-
- > .item {
- padding: 12px 16px;
-
- & + .item {
- border-top: solid 1px rgba(255, 255, 255, 0.5);
- }
-
- > .title {
- font-weight: bold;
- }
-
- > .content {
- > img {
- max-width: 100%;
- }
- }
- }
- }
- }
-
- > .powered-by {
- padding: 28px;
- font-size: 14px;
- text-align: center;
- border-top: 1px solid rgba(255, 255, 255, 0.5);
- color: #fff;
-
- > small {
- display: block;
- margin-top: 8px;
- opacity: 0.5;
- }
- }
- }
- }
-}
-</style>
diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json
index 4d582daa3c..514b304246 100644
--- a/packages/frontend/tsconfig.json
+++ b/packages/frontend/tsconfig.json
@@ -27,10 +27,12 @@
},
"typeRoots": [
"node_modules/@types",
+ "node_modules/@vue-macros",
"@types",
],
"types": [
"vite/client",
+ "reactivity-transform/macros-global"
],
"lib": [
"esnext",
diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts
index 425f3aa45d..fad0dd0177 100644
--- a/packages/frontend/vite.config.ts
+++ b/packages/frontend/vite.config.ts
@@ -1,6 +1,9 @@
import path from 'path';
+import pluginReplace from '@rollup/plugin-replace';
import pluginVue from '@vitejs/plugin-vue';
import { type UserConfig, defineConfig } from 'vite';
+// @ts-expect-error https://github.com/sxzz/unplugin-vue-macros/issues/257#issuecomment-1410752890
+import ReactivityTransform from '@vue-macros/reactivity-transform/vite';
import locales from '../../locales';
import meta from '../../package.json';
@@ -41,11 +44,26 @@ export function getConfig(): UserConfig {
return {
base: '/vite/',
+ server: {
+ port: 5173,
+ },
+
plugins: [
pluginVue({
reactivityTransform: true,
}),
+ ReactivityTransform(),
pluginJson5(),
+ ...process.env.NODE_ENV === 'production'
+ ? [
+ pluginReplace({
+ preventAssignment: true,
+ values: {
+ 'isChromatic()': JSON.stringify(false),
+ },
+ }),
+ ]
+ : [],
],
resolve: {
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 67d12000b8..19e5b75443 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -1357,10 +1357,6 @@ export type Endpoints = {
req: TODO;
res: TODO;
};
- 'i/known-as': {
- req: TODO;
- res: TODO;
- };
'i/notifications': {
req: {
limit?: number;
@@ -1511,6 +1507,7 @@ export type Endpoints = {
mutedWords?: string[][];
mutingNotificationTypes?: Notification_2['type'][];
emailNotificationTypes?: string[];
+ alsoKnownAs?: string[];
};
res: MeDetailed;
};
@@ -2348,6 +2345,7 @@ type LiteInstanceMetadata = {
imageUrl: string;
}[];
translatorAvailable: boolean;
+ serverRules: string[];
};
// @public (undocumented)
@@ -2633,6 +2631,7 @@ type User = UserLite | UserDetailed;
// @public (undocumented)
type UserDetailed = UserLite & {
+ alsoKnownAs: string[];
bannerBlurhash: string | null;
bannerColor: string | null;
bannerUrl: string | null;
@@ -2663,6 +2662,7 @@ type UserDetailed = UserLite & {
lang: string | null;
lastFetchedAt?: DateString;
location: string | null;
+ movedTo: string;
notesCount: number;
pinnedNoteIds: ID[];
pinnedNotes: Note[];
@@ -2696,8 +2696,6 @@ type UserLite = {
onlineStatus: 'online' | 'active' | 'offline' | 'unknown';
avatarUrl: string;
avatarBlurhash: string;
- alsoKnownAs: string[];
- movedToUri: any;
emojis: {
name: string;
url: string;
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 76e0452444..2e9bc76059 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -20,27 +20,27 @@
"url": "git+https://github.com/misskey-dev/misskey.js.git"
},
"devDependencies": {
- "@microsoft/api-extractor": "7.34.4",
- "@swc/jest": "0.2.24",
- "@types/jest": "29.5.0",
- "@types/node": "18.15.11",
- "@typescript-eslint/eslint-plugin": "5.57.1",
- "@typescript-eslint/parser": "5.57.1",
- "eslint": "8.37.0",
+ "@microsoft/api-extractor": "7.34.7",
+ "@swc/jest": "0.2.26",
+ "@types/jest": "29.5.1",
+ "@types/node": "18.16.3",
+ "@typescript-eslint/eslint-plugin": "5.59.2",
+ "@typescript-eslint/parser": "5.59.2",
+ "eslint": "8.39.0",
"jest": "29.5.0",
"jest-fetch-mock": "3.0.3",
"jest-websocket-mock": "2.4.0",
"mock-socket": "9.2.1",
"tsd": "0.28.1",
- "typescript": "5.0.3"
+ "typescript": "5.0.4"
},
"files": [
"built"
],
"dependencies": {
"@swc/cli": "0.1.62",
- "@swc/core": "1.3.46",
- "eventemitter3": "5.0.0",
+ "@swc/core": "1.3.56",
+ "eventemitter3": "5.0.1",
"reconnecting-websocket": "4.4.0"
}
}
diff --git a/packages/misskey-js/src/api.types.ts b/packages/misskey-js/src/api.types.ts
index aed9f5bf84..cc88c4b1a4 100644
--- a/packages/misskey-js/src/api.types.ts
+++ b/packages/misskey-js/src/api.types.ts
@@ -363,7 +363,6 @@ export type Endpoints = {
'i/import-following': { req: TODO; res: TODO; };
'i/import-user-lists': { req: TODO; res: TODO; };
'i/move': { req: TODO; res: TODO; };
- 'i/known-as': { req: TODO; res: TODO; };
'i/notifications': { req: {
limit?: number;
sinceId?: Notification['id'];
@@ -421,6 +420,7 @@ export type Endpoints = {
mutedWords?: string[][];
mutingNotificationTypes?: Notification['type'][];
emailNotificationTypes?: string[];
+ alsoKnownAs?: string[];
}; res: MeDetailed; };
'i/user-group-invites': { req: TODO; res: TODO; };
'i/2fa/done': { req: TODO; res: TODO; };
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 0c90e44494..04065c51c9 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -14,8 +14,6 @@ export type UserLite = {
onlineStatus: 'online' | 'active' | 'offline' | 'unknown';
avatarUrl: string;
avatarBlurhash: string;
- alsoKnownAs: string[];
- movedToUri: any;
emojis: {
name: string;
url: string;
@@ -31,6 +29,7 @@ export type UserLite = {
};
export type UserDetailed = UserLite & {
+ alsoKnownAs: string[];
bannerBlurhash: string | null;
bannerColor: string | null;
bannerUrl: string | null;
@@ -58,6 +57,7 @@ export type UserDetailed = UserLite & {
lang: string | null;
lastFetchedAt?: DateString;
location: string | null;
+ movedTo: string;
notesCount: number;
pinnedNoteIds: ID[];
pinnedNotes: Note[];
@@ -315,6 +315,7 @@ export type LiteInstanceMetadata = {
imageUrl: string;
}[];
translatorAvailable: boolean;
+ serverRules: string[];
};
export type DetailedInstanceMetadata = LiteInstanceMetadata & {
diff --git a/packages/sw/package.json b/packages/sw/package.json
index d7a4a2a4b6..75bb40ccb1 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -9,15 +9,15 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"dependencies": {
- "esbuild": "0.17.15",
+ "esbuild": "0.17.18",
"idb-keyval": "6.2.0",
"misskey-js": "workspace:*"
},
"devDependencies": {
- "@typescript-eslint/parser": "5.57.1",
+ "@typescript-eslint/parser": "5.59.2",
"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
- "eslint": "8.37.0",
+ "eslint": "8.39.0",
"eslint-plugin-import": "2.27.5",
- "typescript": "5.0.3"
+ "typescript": "5.0.4"
}
}
diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts
index 9e0d9f0d1e..2783f2bfb5 100644
--- a/packages/sw/src/sw.ts
+++ b/packages/sw/src/sw.ts
@@ -56,7 +56,7 @@ globalThis.addEventListener('push', ev => {
return createNotification(data);
case 'readAllNotifications':
await globalThis.registration.getNotifications()
- .then(notifications => notifications.forEach(n => n.close()));
+ .then(notifications => notifications.forEach(n => n.tag !== 'read_notification' && n.close()));
break;
}
@@ -133,7 +133,7 @@ globalThis.addEventListener('notificationclick', (ev: ServiceWorkerGlobalScopeEv
switch (action) {
case 'markAllAsRead':
await globalThis.registration.getNotifications()
- .then(notifications => notifications.forEach(n => n.close()));
+ .then(notifications => notifications.forEach(n => n.tag !== 'read_notification' && n.close()));
await get('accounts').then(accounts => {
return Promise.all(accounts.map(async account => {
await swos.sendMarkAllAsRead(account.id);
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7325659f80..04d2fcf36b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -30,12 +30,12 @@ importers:
specifier: 4.1.0
version: 4.1.0
typescript:
- specifier: 5.0.3
- version: 5.0.3
+ specifier: 5.0.4
+ version: 5.0.4
optionalDependencies:
'@tensorflow/tfjs-core':
- specifier: 4.2.0
- version: 4.2.0
+ specifier: 4.4.0
+ version: 4.4.0
devDependencies:
'@types/gulp':
specifier: 4.0.10
@@ -44,20 +44,20 @@ importers:
specifier: 2.0.1
version: 2.0.1
'@typescript-eslint/eslint-plugin':
- specifier: 5.57.1
- version: 5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.37.0)(typescript@5.0.3)
+ specifier: 5.59.2
+ version: 5.59.2(@typescript-eslint/parser@5.59.2)(eslint@8.39.0)(typescript@5.0.4)
'@typescript-eslint/parser':
- specifier: 5.57.1
- version: 5.57.1(eslint@8.37.0)(typescript@5.0.3)
+ specifier: 5.59.2
+ version: 5.59.2(eslint@8.39.0)(typescript@5.0.4)
cross-env:
specifier: 7.0.3
version: 7.0.3
cypress:
- specifier: 12.9.0
- version: 12.9.0
+ specifier: 12.11.0
+ version: 12.11.0
eslint:
- specifier: 8.37.0
- version: 8.37.0
+ specifier: 8.39.0
+ version: 8.39.0
start-server-and-test:
specifier: 2.0.0
version: 2.0.0
@@ -65,23 +65,23 @@ importers:
packages/backend:
dependencies:
'@aws-sdk/client-s3':
- specifier: 3.306.0
- version: 3.306.0
+ specifier: 3.321.1
+ version: 3.321.1
'@aws-sdk/lib-storage':
- specifier: 3.306.0
- version: 3.306.0(@aws-sdk/abort-controller@3.306.0)(@aws-sdk/client-s3@3.306.0)
+ specifier: 3.321.1
+ version: 3.321.1(@aws-sdk/abort-controller@3.310.0)(@aws-sdk/client-s3@3.321.1)
'@aws-sdk/node-http-handler':
- specifier: 3.306.0
- version: 3.306.0
+ specifier: 3.321.1
+ version: 3.321.1
'@bull-board/api':
- specifier: 5.0.0
- version: 5.0.0
+ specifier: 5.1.2
+ version: 5.1.2(@bull-board/ui@5.1.2)
'@bull-board/fastify':
- specifier: 5.0.0
- version: 5.0.0
+ specifier: 5.1.2
+ version: 5.1.2
'@bull-board/ui':
- specifier: 5.0.0
- version: 5.0.0
+ specifier: 5.1.2
+ version: 5.1.2
'@discordapp/twemoji':
specifier: 14.1.2
version: 14.1.2
@@ -98,20 +98,20 @@ importers:
specifier: 9.0.0
version: 9.0.0
'@fastify/multipart':
- specifier: 7.5.0
- version: 7.5.0
+ specifier: 7.6.0
+ version: 7.6.0
'@fastify/static':
- specifier: 6.10.0
- version: 6.10.0
+ specifier: 6.10.1
+ version: 6.10.1
'@fastify/view':
specifier: 7.4.1
version: 7.4.1
'@nestjs/common':
specifier: 9.4.0
- version: 9.4.0(reflect-metadata@0.1.13)(rxjs@7.8.0)
+ version: 9.4.0(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/core':
specifier: 9.4.0
- version: 9.4.0(@nestjs/common@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0)
+ version: 9.4.0(@nestjs/common@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/testing':
specifier: 9.4.0
version: 9.4.0(@nestjs/common@9.4.0)(@nestjs/core@9.4.0)
@@ -123,10 +123,10 @@ importers:
version: 10.0.2
'@swc/cli':
specifier: 0.1.62
- version: 0.1.62(@swc/core@1.3.46)(chokidar@3.5.3)
+ version: 0.1.62(@swc/core@1.3.56)(chokidar@3.5.3)
'@swc/core':
- specifier: 1.3.46
- version: 1.3.46(@swc/helpers@0.5.0)
+ specifier: 1.3.56
+ version: 1.3.56
accepts:
specifier: 1.3.8
version: 1.3.8
@@ -173,8 +173,8 @@ importers:
specifier: 0.5.4
version: 0.5.4
date-fns:
- specifier: 2.29.3
- version: 2.29.3
+ specifier: 2.30.0
+ version: 2.30.0
deep-email-validator:
specifier: 0.1.21
version: 0.1.21
@@ -182,14 +182,14 @@ importers:
specifier: 0.0.1
version: 0.0.1
fastify:
- specifier: 4.15.0
- version: 4.15.0
+ specifier: 4.17.0
+ version: 4.17.0
feed:
specifier: 4.2.2
version: 4.2.2
file-type:
- specifier: 18.2.1
- version: 18.2.1
+ specifier: 18.3.0
+ version: 18.3.0
fluent-ffmpeg:
specifier: 2.1.2
version: 2.1.2
@@ -200,14 +200,14 @@ importers:
specifier: 12.6.0
version: 12.6.0
happy-dom:
- specifier: 8.9.0
- version: 8.9.0
+ specifier: 9.10.2
+ version: 9.10.2
hpagent:
specifier: 1.2.0
version: 1.2.0
ioredis:
- specifier: 4.28.5
- version: 4.28.5
+ specifier: 5.3.2
+ version: 5.3.2
ip-cidr:
specifier: 3.1.0
version: 3.1.0
@@ -227,8 +227,11 @@ importers:
specifier: 8.1.1
version: 8.1.1
jsrsasign:
- specifier: 10.7.0
- version: 10.7.0
+ specifier: 10.8.6
+ version: 10.8.6
+ meilisearch:
+ specifier: 0.32.3
+ version: 0.32.3
mfm-js:
specifier: 0.23.3
version: 0.23.3
@@ -252,7 +255,7 @@ importers:
version: 6.9.1
nsfwjs:
specifier: 2.4.2
- version: 2.4.2(@tensorflow/tfjs@4.2.0)
+ version: 2.4.2(@tensorflow/tfjs@4.4.0)
oauth:
specifier: 0.10.0
version: 0.10.0
@@ -287,8 +290,8 @@ importers:
specifier: 0.3.17
version: 0.3.17
qrcode:
- specifier: 1.5.1
- version: 1.5.1
+ specifier: 1.5.3
+ version: 1.5.3
random-seed:
specifier: 0.3.0
version: 0.3.0
@@ -311,11 +314,11 @@ importers:
specifier: 1.0.0
version: 1.0.0
rss-parser:
- specifier: 3.12.0
- version: 3.12.0
+ specifier: 3.13.0
+ version: 3.13.0
rxjs:
- specifier: 7.8.0
- version: 7.8.0
+ specifier: 7.8.1
+ version: 7.8.1
s-age:
specifier: 1.1.2
version: 1.1.2
@@ -326,14 +329,17 @@ importers:
specifier: 3.0.5
version: 3.0.5
semver:
- specifier: 7.3.8
- version: 7.3.8
+ specifier: 7.5.0
+ version: 7.5.0
sharp:
- specifier: 0.32.0
- version: 0.32.0
+ specifier: 0.32.1
+ version: 0.32.1
sharp-read-bmp:
specifier: github:misskey-dev/sharp-read-bmp
version: github.com/misskey-dev/sharp-read-bmp/02d9dc189fa7df0c4bea09330be26741772dac01
+ slacc:
+ specifier: 0.0.7
+ version: 0.0.7
strict-event-emitter-types:
specifier: 2.0.0
version: 2.0.0
@@ -342,7 +348,7 @@ importers:
version: 2.1.0
summaly:
specifier: github:misskey-dev/summaly
- version: github.com/misskey-dev/summaly/1bab7afee616429b8bbf7a7cbcbb8ebcef66d992
+ version: github.com/misskey-dev/summaly/2d63e2a0066f89871e777cc81d43c1ade8c97517
systeminformation:
specifier: 5.17.12
version: 5.17.12
@@ -353,8 +359,8 @@ importers:
specifier: 0.2.1
version: 0.2.1
tsc-alias:
- specifier: 1.8.5
- version: 1.8.5
+ specifier: 1.8.6
+ version: 1.8.6
tsconfig-paths:
specifier: 4.2.0
version: 4.2.0
@@ -362,11 +368,11 @@ importers:
specifier: 14.0.0
version: 14.0.0
typeorm:
- specifier: 0.3.13
- version: 0.3.13(ioredis@4.28.5)(pg@8.10.0)
+ specifier: 0.3.15
+ version: 0.3.15(ioredis@5.3.2)(pg@8.10.0)
typescript:
- specifier: 5.0.3
- version: 5.0.3
+ specifier: 5.0.4
+ version: 5.0.4
ulid:
specifier: 2.3.0
version: 2.3.0
@@ -380,8 +386,8 @@ importers:
specifier: 1.1.2
version: 1.1.2
web-push:
- specifier: 3.5.0
- version: 3.5.0
+ specifier: 3.6.1
+ version: 3.6.1
websocket:
specifier: 1.0.34
version: 1.0.34
@@ -396,48 +402,81 @@ importers:
specifier: 1.3.11
version: 1.3.11
'@swc/core-darwin-arm64':
- specifier: 1.3.46
- version: 1.3.46
+ specifier: 1.3.56
+ version: 1.3.56
'@swc/core-darwin-x64':
- specifier: 1.3.46
- version: 1.3.46
+ specifier: 1.3.56
+ version: 1.3.56
'@swc/core-linux-arm-gnueabihf':
- specifier: 1.3.46
- version: 1.3.46
+ specifier: 1.3.56
+ version: 1.3.56
'@swc/core-linux-arm64-gnu':
- specifier: 1.3.46
- version: 1.3.46
+ specifier: 1.3.56
+ version: 1.3.56
'@swc/core-linux-arm64-musl':
- specifier: 1.3.46
- version: 1.3.46
+ specifier: 1.3.56
+ version: 1.3.56
'@swc/core-linux-x64-gnu':
- specifier: 1.3.46
- version: 1.3.46
+ specifier: 1.3.56
+ version: 1.3.56
'@swc/core-linux-x64-musl':
- specifier: 1.3.46
- version: 1.3.46
+ specifier: 1.3.56
+ version: 1.3.56
'@swc/core-win32-arm64-msvc':
- specifier: 1.3.46
- version: 1.3.46
+ specifier: 1.3.56
+ version: 1.3.56
'@swc/core-win32-ia32-msvc':
- specifier: 1.3.46
- version: 1.3.46
+ specifier: 1.3.56
+ version: 1.3.56
'@swc/core-win32-x64-msvc':
- specifier: 1.3.46
- version: 1.3.46
+ specifier: 1.3.56
+ version: 1.3.56
'@tensorflow/tfjs':
- specifier: 4.2.0
- version: 4.2.0(seedrandom@3.0.5)
+ specifier: 4.4.0
+ version: 4.4.0(seedrandom@3.0.5)
'@tensorflow/tfjs-node':
- specifier: 4.2.0
- version: 4.2.0(seedrandom@3.0.5)
+ specifier: 4.4.0
+ version: 4.4.0(seedrandom@3.0.5)
+ slacc-android-arm-eabi:
+ specifier: 0.0.7
+ version: 0.0.7
+ slacc-android-arm64:
+ specifier: 0.0.7
+ version: 0.0.7
+ slacc-darwin-arm64:
+ specifier: 0.0.7
+ version: 0.0.7
+ slacc-darwin-universal:
+ specifier: 0.0.7
+ version: 0.0.7
+ slacc-darwin-x64:
+ specifier: 0.0.7
+ version: 0.0.7
+ slacc-linux-arm-gnueabihf:
+ specifier: 0.0.7
+ version: 0.0.7
+ slacc-linux-arm64-gnu:
+ specifier: 0.0.7
+ version: 0.0.7
+ slacc-linux-arm64-musl:
+ specifier: 0.0.7
+ version: 0.0.7
+ slacc-linux-x64-gnu:
+ specifier: 0.0.7
+ version: 0.0.7
+ slacc-win32-arm64-msvc:
+ specifier: 0.0.7
+ version: 0.0.7
+ slacc-win32-x64-msvc:
+ specifier: 0.0.7
+ version: 0.0.7
devDependencies:
'@jest/globals':
specifier: 29.5.0
version: 29.5.0
'@swc/jest':
- specifier: 0.2.24
- version: 0.2.24(@swc/core@1.3.46)
+ specifier: 0.2.26
+ version: 0.2.26(@swc/core@1.3.56)
'@types/accepts':
specifier: 1.3.5
version: 1.3.5
@@ -465,12 +504,9 @@ importers:
'@types/fluent-ffmpeg':
specifier: 2.1.21
version: 2.1.21
- '@types/ioredis':
- specifier: 4.28.10
- version: 4.28.10
'@types/jest':
- specifier: 29.5.0
- version: 29.5.0
+ specifier: 29.5.1
+ version: 29.5.1
'@types/js-yaml':
specifier: 4.0.5
version: 4.0.5
@@ -487,8 +523,8 @@ importers:
specifier: 2.1.1
version: 2.1.1
'@types/node':
- specifier: 18.15.11
- version: 18.15.11
+ specifier: 18.16.3
+ version: 18.16.3
'@types/node-fetch':
specifier: 3.0.3
version: 3.0.3
@@ -529,8 +565,8 @@ importers:
specifier: 7.3.13
version: 7.3.13
'@types/sharp':
- specifier: 0.31.1
- version: 0.31.1
+ specifier: 0.32.0
+ version: 0.32.0
'@types/sinonjs__fake-timers':
specifier: 8.1.2
version: 8.1.2
@@ -559,11 +595,11 @@ importers:
specifier: 8.5.4
version: 8.5.4
'@typescript-eslint/eslint-plugin':
- specifier: 5.57.1
- version: 5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.37.0)(typescript@5.0.3)
+ specifier: 5.59.2
+ version: 5.59.2(@typescript-eslint/parser@5.59.2)(eslint@8.39.0)(typescript@5.0.4)
'@typescript-eslint/parser':
- specifier: 5.57.1
- version: 5.57.1(eslint@8.37.0)(typescript@5.0.3)
+ specifier: 5.59.2
+ version: 5.59.2(eslint@8.39.0)(typescript@5.0.4)
aws-sdk-client-mock:
specifier: ^2.1.1
version: 2.1.1
@@ -571,17 +607,17 @@ importers:
specifier: 7.0.3
version: 7.0.3
eslint:
- specifier: 8.37.0
- version: 8.37.0
+ specifier: 8.39.0
+ version: 8.39.0
eslint-plugin-import:
specifier: 2.27.5
- version: 2.27.5(@typescript-eslint/parser@5.57.1)(eslint@8.37.0)
+ version: 2.27.5(@typescript-eslint/parser@5.59.2)(eslint@8.39.0)
execa:
specifier: 6.1.0
version: 6.1.0
jest:
specifier: 29.5.0
- version: 29.5.0(@types/node@18.15.11)
+ version: 29.5.0(@types/node@18.16.3)
jest-mock:
specifier: 29.5.0
version: 29.5.0
@@ -592,23 +628,29 @@ importers:
specifier: 14.1.2
version: 14.1.2
'@rollup/plugin-alias':
- specifier: 4.0.3
- version: 4.0.3(rollup@3.20.2)
+ specifier: 5.0.0
+ version: 5.0.0(rollup@3.21.3)
'@rollup/plugin-json':
specifier: 6.0.0
- version: 6.0.0(rollup@3.20.2)
+ version: 6.0.0(rollup@3.21.3)
+ '@rollup/plugin-replace':
+ specifier: ^5.0.2
+ version: 5.0.2(rollup@3.21.3)
'@rollup/pluginutils':
specifier: 5.0.2
- version: 5.0.2(rollup@3.20.2)
+ version: 5.0.2(rollup@3.21.3)
'@syuilo/aiscript':
- specifier: 0.13.1
- version: 0.13.1
+ specifier: 0.13.2
+ version: 0.13.2
'@tabler/icons-webfont':
- specifier: 2.12.0
- version: 2.12.0
+ specifier: 2.17.0
+ version: 2.17.0
'@vitejs/plugin-vue':
- specifier: 4.1.0
- version: 4.1.0(vite@4.2.1)(vue@3.2.47)
+ specifier: 4.2.1
+ version: 4.2.1(vite@4.3.4)(vue@3.2.47)
+ '@vue-macros/reactivity-transform':
+ specifier: ^0.3.5
+ version: 0.3.5(rollup@3.21.3)(vue@3.2.47)
'@vue/compiler-sfc':
specifier: 3.2.47
version: 3.2.47
@@ -628,20 +670,23 @@ importers:
specifier: 1.6.0
version: 1.6.0
chart.js:
- specifier: 4.2.1
- version: 4.2.1
+ specifier: 4.3.0
+ version: 4.3.0
chartjs-adapter-date-fns:
specifier: 3.0.0
- version: 3.0.0(chart.js@4.2.1)(date-fns@2.29.3)
+ version: 3.0.0(chart.js@4.3.0)(date-fns@2.30.0)
chartjs-chart-matrix:
specifier: 2.0.1
- version: 2.0.1(chart.js@4.2.1)
+ version: 2.0.1(chart.js@4.3.0)
chartjs-plugin-gradient:
specifier: 0.6.1
- version: 0.6.1(chart.js@4.2.1)
+ version: 0.6.1(chart.js@4.3.0)
chartjs-plugin-zoom:
specifier: 2.0.1
- version: 2.0.1(chart.js@4.2.1)
+ version: 2.0.1(chart.js@4.3.0)
+ chromatic:
+ specifier: 6.17.3
+ version: 6.17.3
compare-versions:
specifier: 5.0.1
version: 5.0.1
@@ -649,14 +694,14 @@ importers:
specifier: 2.0.0-beta.2
version: 2.0.0-beta.2
date-fns:
- specifier: 2.29.3
- version: 2.29.3
+ specifier: 2.30.0
+ version: 2.30.0
escape-regexp:
specifier: 0.0.1
version: 0.0.1
eventemitter3:
- specifier: 5.0.0
- version: 5.0.0
+ specifier: 5.0.1
+ version: 5.0.1
gsap:
specifier: 3.11.5
version: 3.11.5
@@ -697,8 +742,8 @@ importers:
specifier: 1.0.0
version: 1.0.0
rollup:
- specifier: 3.20.2
- version: 3.20.2
+ specifier: 3.21.3
+ version: 3.21.3
s-age:
specifier: 1.1.2
version: 1.1.2
@@ -706,8 +751,8 @@ importers:
specifier: 2.10.0
version: 2.10.0
sass:
- specifier: 1.60.0
- version: 1.60.0
+ specifier: 1.62.1
+ version: 1.62.1
seedrandom:
specifier: 3.0.5
version: 3.0.5
@@ -730,8 +775,8 @@ importers:
specifier: 1.6.0
version: 1.6.0
tsc-alias:
- specifier: 1.8.5
- version: 1.8.5
+ specifier: 1.8.6
+ version: 1.8.6
tsconfig-paths:
specifier: 4.2.0
version: 4.2.0
@@ -739,8 +784,8 @@ importers:
specifier: 14.0.0
version: 14.0.0
typescript:
- specifier: 5.0.3
- version: 5.0.3
+ specifier: 5.0.4
+ version: 5.0.4
uuid:
specifier: 9.0.0
version: 9.0.0
@@ -748,8 +793,8 @@ importers:
specifier: 1.8.0
version: 1.8.0
vite:
- specifier: 4.2.1
- version: 4.2.1(@types/node@18.15.11)(sass@1.60.0)
+ specifier: 4.3.4
+ version: 4.3.4(@types/node@18.16.3)(sass@1.62.1)
vue:
specifier: 3.2.47
version: 3.2.47
@@ -763,57 +808,60 @@ importers:
specifier: next
version: 4.1.0(vue@3.2.47)
devDependencies:
+ '@storybook/addon-actions':
+ specifier: 7.0.7
+ version: 7.0.7(react-dom@18.2.0)(react@18.2.0)
'@storybook/addon-essentials':
- specifier: 7.0.2
- version: 7.0.2(react-dom@18.2.0)(react@18.2.0)
+ specifier: 7.0.7
+ version: 7.0.7(react-dom@18.2.0)(react@18.2.0)
'@storybook/addon-interactions':
- specifier: 7.0.2
- version: 7.0.2(react-dom@18.2.0)(react@18.2.0)
+ specifier: 7.0.7
+ version: 7.0.7(react-dom@18.2.0)(react@18.2.0)
'@storybook/addon-links':
- specifier: 7.0.2
- version: 7.0.2(react-dom@18.2.0)(react@18.2.0)
+ specifier: 7.0.7
+ version: 7.0.7(react-dom@18.2.0)(react@18.2.0)
'@storybook/addon-storysource':
- specifier: 7.0.2
- version: 7.0.2(react-dom@18.2.0)(react@18.2.0)
+ specifier: 7.0.7
+ version: 7.0.7(react-dom@18.2.0)(react@18.2.0)
'@storybook/addons':
- specifier: 7.0.2
- version: 7.0.2(react-dom@18.2.0)(react@18.2.0)
+ specifier: 7.0.7
+ version: 7.0.7(react-dom@18.2.0)(react@18.2.0)
'@storybook/blocks':
- specifier: 7.0.2
- version: 7.0.2(react-dom@18.2.0)(react@18.2.0)
+ specifier: 7.0.7
+ version: 7.0.7(react-dom@18.2.0)(react@18.2.0)
'@storybook/core-events':
- specifier: 7.0.2
- version: 7.0.2
+ specifier: 7.0.7
+ version: 7.0.7
'@storybook/jest':
specifier: 0.1.0
version: 0.1.0
'@storybook/manager-api':
- specifier: 7.0.2
- version: 7.0.2(react-dom@18.2.0)(react@18.2.0)
+ specifier: 7.0.7
+ version: 7.0.7(react-dom@18.2.0)(react@18.2.0)
'@storybook/preview-api':
- specifier: 7.0.2
- version: 7.0.2
+ specifier: 7.0.7
+ version: 7.0.7
'@storybook/react':
- specifier: 7.0.2
- version: 7.0.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.3)
+ specifier: 7.0.7
+ version: 7.0.7(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.4)
'@storybook/react-vite':
- specifier: 7.0.2
- version: 7.0.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.3)(vite@4.2.1)
+ specifier: 7.0.7
+ version: 7.0.7(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.4)(vite@4.3.4)
'@storybook/testing-library':
- specifier: 0.0.14-next.1
- version: 0.0.14-next.1
+ specifier: 0.1.0
+ version: 0.1.0
'@storybook/theming':
- specifier: 7.0.2
- version: 7.0.2(react-dom@18.2.0)(react@18.2.0)
+ specifier: 7.0.7
+ version: 7.0.7(react-dom@18.2.0)(react@18.2.0)
'@storybook/types':
- specifier: 7.0.2
- version: 7.0.2
+ specifier: 7.0.7
+ version: 7.0.7
'@storybook/vue3':
- specifier: 7.0.2
- version: 7.0.2(vue@3.2.47)
+ specifier: 7.0.7
+ version: 7.0.7(vue@3.2.47)
'@storybook/vue3-vite':
- specifier: 7.0.2
- version: 7.0.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.3)(vite@4.2.1)(vue@3.2.47)
+ specifier: 7.0.7
+ version: 7.0.7(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.4)(vite@4.3.4)(vue@3.2.47)
'@testing-library/jest-dom':
specifier: 5.16.5
version: 5.16.5
@@ -824,8 +872,8 @@ importers:
specifier: 0.0.1
version: 0.0.1
'@types/estree':
- specifier: 1.0.0
- version: 1.0.0
+ specifier: 1.0.1
+ version: 1.0.1
'@types/gulp':
specifier: 4.0.10
version: 4.0.10
@@ -836,11 +884,11 @@ importers:
specifier: 0.18.2
version: 0.18.2
'@types/micromatch':
- specifier: 3.1.1
- version: 3.1.1
+ specifier: 4.0.2
+ version: 4.0.2
'@types/node':
- specifier: 18.15.11
- version: 18.15.11
+ specifier: 18.16.3
+ version: 18.16.3
'@types/punycode':
specifier: 2.1.0
version: 2.1.0
@@ -869,14 +917,14 @@ importers:
specifier: 8.5.4
version: 8.5.4
'@typescript-eslint/eslint-plugin':
- specifier: 5.57.1
- version: 5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.37.0)(typescript@5.0.3)
+ specifier: 5.59.2
+ version: 5.59.2(@typescript-eslint/parser@5.59.2)(eslint@8.39.0)(typescript@5.0.4)
'@typescript-eslint/parser':
- specifier: 5.57.1
- version: 5.57.1(eslint@8.37.0)(typescript@5.0.3)
+ specifier: 5.59.2
+ version: 5.59.2(eslint@8.39.0)(typescript@5.0.4)
'@vitest/coverage-c8':
- specifier: ^0.29.8
- version: 0.29.8(vitest@0.29.8)
+ specifier: 0.30.1
+ version: 0.30.1(vitest@0.30.1)
'@vue/runtime-core':
specifier: 3.2.47
version: 3.2.47
@@ -886,42 +934,39 @@ importers:
chokidar-cli:
specifier: 3.0.0
version: 3.0.0
- chromatic:
- specifier: 6.17.3
- version: 6.17.3
cross-env:
specifier: 7.0.3
version: 7.0.3
cypress:
- specifier: 12.9.0
- version: 12.9.0
+ specifier: 12.11.0
+ version: 12.11.0
eslint:
- specifier: 8.37.0
- version: 8.37.0
+ specifier: 8.39.0
+ version: 8.39.0
eslint-plugin-import:
specifier: 2.27.5
- version: 2.27.5(@typescript-eslint/parser@5.57.1)(eslint@8.37.0)
+ version: 2.27.5(@typescript-eslint/parser@5.59.2)(eslint@8.39.0)
eslint-plugin-vue:
- specifier: 9.10.0
- version: 9.10.0(eslint@8.37.0)
+ specifier: 9.11.0
+ version: 9.11.0(eslint@8.39.0)
fast-glob:
specifier: 3.2.12
version: 3.2.12
happy-dom:
- specifier: 8.9.0
- version: 8.9.0
+ specifier: 9.10.2
+ version: 9.10.2
micromatch:
specifier: 3.1.10
version: 3.1.10
msw:
specifier: 1.2.1
- version: 1.2.1(typescript@5.0.3)
+ version: 1.2.1(typescript@5.0.4)
msw-storybook-addon:
specifier: 1.8.0
version: 1.8.0(msw@1.2.1)
prettier:
- specifier: 2.8.7
- version: 2.8.7
+ specifier: 2.8.8
+ version: 2.8.8
react:
specifier: 18.2.0
version: 18.2.0
@@ -932,69 +977,69 @@ importers:
specifier: 2.0.0
version: 2.0.0
storybook:
- specifier: 7.0.2
- version: 7.0.2
+ specifier: 7.0.7
+ version: 7.0.7
storybook-addon-misskey-theme:
specifier: github:misskey-dev/storybook-addon-misskey-theme
- version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.0.2)(@storybook/components@7.0.2)(@storybook/core-events@7.0.2)(@storybook/manager-api@7.0.2)(@storybook/preview-api@7.0.2)(@storybook/theming@7.0.2)(@storybook/types@7.0.2)(react-dom@18.2.0)(react@18.2.0)
+ version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.0.7)(@storybook/components@7.0.7)(@storybook/core-events@7.0.7)(@storybook/manager-api@7.0.7)(@storybook/preview-api@7.0.7)(@storybook/theming@7.0.7)(@storybook/types@7.0.7)(react-dom@18.2.0)(react@18.2.0)
summaly:
specifier: github:misskey-dev/summaly
- version: github.com/misskey-dev/summaly/1bab7afee616429b8bbf7a7cbcbb8ebcef66d992
+ version: github.com/misskey-dev/summaly/2d63e2a0066f89871e777cc81d43c1ade8c97517
vite-plugin-turbosnap:
- specifier: ^1.0.1
- version: 1.0.1
+ specifier: 1.0.2
+ version: 1.0.2
vitest:
- specifier: 0.29.8
- version: 0.29.8(happy-dom@8.9.0)(sass@1.60.0)
+ specifier: 0.30.1
+ version: 0.30.1(happy-dom@9.10.2)(sass@1.62.1)
vitest-fetch-mock:
specifier: 0.2.2
- version: 0.2.2(vitest@0.29.8)
+ version: 0.2.2(vitest@0.30.1)
vue-eslint-parser:
specifier: 9.1.1
- version: 9.1.1(eslint@8.37.0)
+ version: 9.1.1(eslint@8.39.0)
vue-tsc:
- specifier: 1.2.0
- version: 1.2.0(typescript@5.0.3)
+ specifier: 1.6.3
+ version: 1.6.3(typescript@5.0.4)
packages/misskey-js:
dependencies:
'@swc/cli':
specifier: 0.1.62
- version: 0.1.62(@swc/core@1.3.46)(chokidar@3.5.3)
+ version: 0.1.62(@swc/core@1.3.56)(chokidar@3.5.3)
'@swc/core':
- specifier: 1.3.46
- version: 1.3.46(@swc/helpers@0.5.0)
+ specifier: 1.3.56
+ version: 1.3.56
eventemitter3:
- specifier: 5.0.0
- version: 5.0.0
+ specifier: 5.0.1
+ version: 5.0.1
reconnecting-websocket:
specifier: 4.4.0
version: 4.4.0
devDependencies:
'@microsoft/api-extractor':
- specifier: 7.34.4
- version: 7.34.4(@types/node@18.15.11)
+ specifier: 7.34.7
+ version: 7.34.7(@types/node@18.16.3)
'@swc/jest':
- specifier: 0.2.24
- version: 0.2.24(@swc/core@1.3.46)
+ specifier: 0.2.26
+ version: 0.2.26(@swc/core@1.3.56)
'@types/jest':
- specifier: 29.5.0
- version: 29.5.0
+ specifier: 29.5.1
+ version: 29.5.1
'@types/node':
- specifier: 18.15.11
- version: 18.15.11
+ specifier: 18.16.3
+ version: 18.16.3
'@typescript-eslint/eslint-plugin':
- specifier: 5.57.1
- version: 5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.37.0)(typescript@5.0.3)
+ specifier: 5.59.2
+ version: 5.59.2(@typescript-eslint/parser@5.59.2)(eslint@8.39.0)(typescript@5.0.4)
'@typescript-eslint/parser':
- specifier: 5.57.1
- version: 5.57.1(eslint@8.37.0)(typescript@5.0.3)
+ specifier: 5.59.2
+ version: 5.59.2(eslint@8.39.0)(typescript@5.0.4)
eslint:
- specifier: 8.37.0
- version: 8.37.0
+ specifier: 8.39.0
+ version: 8.39.0
jest:
specifier: 29.5.0
- version: 29.5.0(@types/node@18.15.11)
+ version: 29.5.0(@types/node@18.16.3)
jest-fetch-mock:
specifier: 3.0.3
version: 3.0.3
@@ -1008,14 +1053,14 @@ importers:
specifier: 0.28.1
version: 0.28.1
typescript:
- specifier: 5.0.3
- version: 5.0.3
+ specifier: 5.0.4
+ version: 5.0.4
packages/sw:
dependencies:
esbuild:
- specifier: 0.17.15
- version: 0.17.15
+ specifier: 0.17.18
+ version: 0.17.18
idb-keyval:
specifier: 6.2.0
version: 6.2.0
@@ -1024,20 +1069,20 @@ importers:
version: link:../misskey-js
devDependencies:
'@typescript-eslint/parser':
- specifier: 5.57.1
- version: 5.57.1(eslint@8.37.0)(typescript@5.0.3)
+ specifier: 5.59.2
+ version: 5.59.2(eslint@8.39.0)(typescript@5.0.4)
'@typescript/lib-webworker':
specifier: npm:@types/serviceworker@0.0.67
version: /@types/serviceworker@0.0.67
eslint:
- specifier: 8.37.0
- version: 8.37.0
+ specifier: 8.39.0
+ version: 8.39.0
eslint-plugin-import:
specifier: 2.27.5
- version: 2.27.5(@typescript-eslint/parser@5.57.1)(eslint@8.37.0)
+ version: 2.27.5(@typescript-eslint/parser@5.59.2)(eslint@8.39.0)
typescript:
- specifier: 5.0.3
- version: 5.0.3
+ specifier: 5.0.4
+ version: 5.0.4
packages:
@@ -1057,7 +1102,7 @@ packages:
resolution: {integrity: sha512-xguP2WR2Dv0gQ7Ykbdb7BNCnPnIPB94uTi0Z2NvkRBEnhbwjOQ7QyQKJXrVQg4qDpiD9hA5l5cCwy/z2OXgc3w==}
dependencies:
grapheme-splitter: 1.0.4
- dev: true
+ dev: false
/@aw-web-design/x-default-browser@1.4.88:
resolution: {integrity: sha512-AkEmF0wcwYC2QkhK703Y83fxWARttIWXDmQN8+cof8FmFZ5BRhnNXGymeb1S73bOCLfWjYELxtujL56idCN/XA==}
@@ -1070,7 +1115,7 @@ packages:
resolution: {integrity: sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==}
dependencies:
'@aws-crypto/util': 3.0.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
tslib: 1.14.1
dev: false
@@ -1078,7 +1123,7 @@ packages:
resolution: {integrity: sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==}
dependencies:
'@aws-crypto/util': 3.0.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
tslib: 1.14.1
dev: false
@@ -1094,7 +1139,7 @@ packages:
'@aws-crypto/ie11-detection': 3.0.0
'@aws-crypto/supports-web-crypto': 3.0.0
'@aws-crypto/util': 3.0.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
'@aws-sdk/util-locate-window': 3.208.0
'@aws-sdk/util-utf8-browser': 3.259.0
tslib: 1.14.1
@@ -1107,7 +1152,7 @@ packages:
'@aws-crypto/sha256-js': 3.0.0
'@aws-crypto/supports-web-crypto': 3.0.0
'@aws-crypto/util': 3.0.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
'@aws-sdk/util-locate-window': 3.208.0
'@aws-sdk/util-utf8-browser': 3.259.0
tslib: 1.14.1
@@ -1117,7 +1162,7 @@ packages:
resolution: {integrity: sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==}
dependencies:
'@aws-crypto/util': 3.0.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
tslib: 1.14.1
dev: false
@@ -1130,81 +1175,81 @@ packages:
/@aws-crypto/util@3.0.0:
resolution: {integrity: sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==}
dependencies:
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
'@aws-sdk/util-utf8-browser': 3.259.0
tslib: 1.14.1
dev: false
- /@aws-sdk/abort-controller@3.306.0:
- resolution: {integrity: sha512-ewCvdUrMJMlnkNaqXdG7L2H6O7CDI036y6lkTU8gQqa2lCzZvqBkzz6R5NbWqb8TJPi69Z7lXEITgk2b0+pl6w==}
+ /@aws-sdk/abort-controller@3.310.0:
+ resolution: {integrity: sha512-v1zrRQxDLA1MdPim159Vx/CPHqsB4uybSxRi1CnfHO5ZjHryx3a5htW2gdGAykVCul40+yJXvfpufMrELVxH+g==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/chunked-blob-reader@3.303.0:
- resolution: {integrity: sha512-Cofcujz08TTKA7CtsIcWyFTyfe84KBa72kTjsXA9GQgn0cuUHZgGoFpBzoyDT8Ff3TPMKmAGDXmllRGFZw4mQw==}
+ /@aws-sdk/chunked-blob-reader@3.310.0:
+ resolution: {integrity: sha512-CrJS3exo4mWaLnWxfCH+w88Ou0IcAZSIkk4QbmxiHl/5Dq705OLoxf4385MVyExpqpeVJYOYQ2WaD8i/pQZ2fg==}
dependencies:
tslib: 2.5.0
dev: false
- /@aws-sdk/client-s3@3.306.0:
- resolution: {integrity: sha512-UkR5jFomspBAXSeAX/1T+YdUMusjg6xDHNvngL9JcW6qbtVv2zDuaGQ65474n2eFr4iJfgVL+IhKy+GNTZ03HA==}
+ /@aws-sdk/client-s3@3.321.1:
+ resolution: {integrity: sha512-SndPRdeofP2j1kPDLoPbJL8DzzjSciFb1S+Tda3UljOy9gQl68OAruwKloXHJE8GRkLJnYowlwLu36H1MvADJg==}
engines: {node: '>=14.0.0'}
dependencies:
'@aws-crypto/sha1-browser': 3.0.0
'@aws-crypto/sha256-browser': 3.0.0
'@aws-crypto/sha256-js': 3.0.0
- '@aws-sdk/client-sts': 3.306.0
- '@aws-sdk/config-resolver': 3.306.0
- '@aws-sdk/credential-provider-node': 3.306.0
- '@aws-sdk/eventstream-serde-browser': 3.306.0
- '@aws-sdk/eventstream-serde-config-resolver': 3.306.0
- '@aws-sdk/eventstream-serde-node': 3.306.0
- '@aws-sdk/fetch-http-handler': 3.306.0
- '@aws-sdk/hash-blob-browser': 3.306.0
- '@aws-sdk/hash-node': 3.306.0
- '@aws-sdk/hash-stream-node': 3.306.0
- '@aws-sdk/invalid-dependency': 3.306.0
- '@aws-sdk/md5-js': 3.306.0
- '@aws-sdk/middleware-bucket-endpoint': 3.306.0
- '@aws-sdk/middleware-content-length': 3.306.0
- '@aws-sdk/middleware-endpoint': 3.306.0
- '@aws-sdk/middleware-expect-continue': 3.306.0
- '@aws-sdk/middleware-flexible-checksums': 3.306.0
- '@aws-sdk/middleware-host-header': 3.306.0
- '@aws-sdk/middleware-location-constraint': 3.306.0
- '@aws-sdk/middleware-logger': 3.306.0
- '@aws-sdk/middleware-recursion-detection': 3.306.0
- '@aws-sdk/middleware-retry': 3.306.0
- '@aws-sdk/middleware-sdk-s3': 3.306.0
- '@aws-sdk/middleware-serde': 3.306.0
- '@aws-sdk/middleware-signing': 3.306.0
- '@aws-sdk/middleware-ssec': 3.306.0
- '@aws-sdk/middleware-stack': 3.306.0
- '@aws-sdk/middleware-user-agent': 3.306.0
- '@aws-sdk/node-config-provider': 3.306.0
- '@aws-sdk/node-http-handler': 3.306.0
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/signature-v4-multi-region': 3.306.0
- '@aws-sdk/smithy-client': 3.306.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/url-parser': 3.306.0
- '@aws-sdk/util-base64': 3.303.0
- '@aws-sdk/util-body-length-browser': 3.303.0
- '@aws-sdk/util-body-length-node': 3.303.0
- '@aws-sdk/util-defaults-mode-browser': 3.306.0
- '@aws-sdk/util-defaults-mode-node': 3.306.0
- '@aws-sdk/util-endpoints': 3.306.0
- '@aws-sdk/util-retry': 3.306.0
- '@aws-sdk/util-stream-browser': 3.306.0
- '@aws-sdk/util-stream-node': 3.306.0
- '@aws-sdk/util-user-agent-browser': 3.306.0
- '@aws-sdk/util-user-agent-node': 3.306.0
- '@aws-sdk/util-utf8': 3.303.0
- '@aws-sdk/util-waiter': 3.306.0
- '@aws-sdk/xml-builder': 3.303.0
+ '@aws-sdk/client-sts': 3.321.1
+ '@aws-sdk/config-resolver': 3.310.0
+ '@aws-sdk/credential-provider-node': 3.321.1
+ '@aws-sdk/eventstream-serde-browser': 3.310.0
+ '@aws-sdk/eventstream-serde-config-resolver': 3.310.0
+ '@aws-sdk/eventstream-serde-node': 3.310.0
+ '@aws-sdk/fetch-http-handler': 3.310.0
+ '@aws-sdk/hash-blob-browser': 3.310.0
+ '@aws-sdk/hash-node': 3.310.0
+ '@aws-sdk/hash-stream-node': 3.310.0
+ '@aws-sdk/invalid-dependency': 3.310.0
+ '@aws-sdk/md5-js': 3.310.0
+ '@aws-sdk/middleware-bucket-endpoint': 3.310.0
+ '@aws-sdk/middleware-content-length': 3.310.0
+ '@aws-sdk/middleware-endpoint': 3.310.0
+ '@aws-sdk/middleware-expect-continue': 3.310.0
+ '@aws-sdk/middleware-flexible-checksums': 3.310.0
+ '@aws-sdk/middleware-host-header': 3.310.0
+ '@aws-sdk/middleware-location-constraint': 3.310.0
+ '@aws-sdk/middleware-logger': 3.310.0
+ '@aws-sdk/middleware-recursion-detection': 3.310.0
+ '@aws-sdk/middleware-retry': 3.310.0
+ '@aws-sdk/middleware-sdk-s3': 3.310.0
+ '@aws-sdk/middleware-serde': 3.310.0
+ '@aws-sdk/middleware-signing': 3.310.0
+ '@aws-sdk/middleware-ssec': 3.310.0
+ '@aws-sdk/middleware-stack': 3.310.0
+ '@aws-sdk/middleware-user-agent': 3.319.0
+ '@aws-sdk/node-config-provider': 3.310.0
+ '@aws-sdk/node-http-handler': 3.321.1
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/signature-v4-multi-region': 3.310.0
+ '@aws-sdk/smithy-client': 3.316.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/url-parser': 3.310.0
+ '@aws-sdk/util-base64': 3.310.0
+ '@aws-sdk/util-body-length-browser': 3.310.0
+ '@aws-sdk/util-body-length-node': 3.310.0
+ '@aws-sdk/util-defaults-mode-browser': 3.316.0
+ '@aws-sdk/util-defaults-mode-node': 3.316.0
+ '@aws-sdk/util-endpoints': 3.319.0
+ '@aws-sdk/util-retry': 3.310.0
+ '@aws-sdk/util-stream-browser': 3.310.0
+ '@aws-sdk/util-stream-node': 3.321.1
+ '@aws-sdk/util-user-agent-browser': 3.310.0
+ '@aws-sdk/util-user-agent-node': 3.310.0
+ '@aws-sdk/util-utf8': 3.310.0
+ '@aws-sdk/util-waiter': 3.310.0
+ '@aws-sdk/xml-builder': 3.310.0
fast-xml-parser: 4.1.2
tslib: 2.5.0
transitivePeerDependencies:
@@ -1212,581 +1257,581 @@ packages:
- aws-crt
dev: false
- /@aws-sdk/client-sso-oidc@3.306.0:
- resolution: {integrity: sha512-O27yrApCkbC0/uPRb1aHkENpFSqrkPbXRi76NF/8T97qC8bngRpy6yeafcQRrp9NGQSF/m9xbPWYsQuiurqedw==}
+ /@aws-sdk/client-sso-oidc@3.321.1:
+ resolution: {integrity: sha512-PBVfHQbyrsfzbnO6u9d9Sik8JlXGLhHj3zLd87iBkYXBdHwD5NuvwWu7OtjUtrHjP4SfzodVwfjmTbDAFqbtzw==}
engines: {node: '>=14.0.0'}
dependencies:
'@aws-crypto/sha256-browser': 3.0.0
'@aws-crypto/sha256-js': 3.0.0
- '@aws-sdk/config-resolver': 3.306.0
- '@aws-sdk/fetch-http-handler': 3.306.0
- '@aws-sdk/hash-node': 3.306.0
- '@aws-sdk/invalid-dependency': 3.306.0
- '@aws-sdk/middleware-content-length': 3.306.0
- '@aws-sdk/middleware-endpoint': 3.306.0
- '@aws-sdk/middleware-host-header': 3.306.0
- '@aws-sdk/middleware-logger': 3.306.0
- '@aws-sdk/middleware-recursion-detection': 3.306.0
- '@aws-sdk/middleware-retry': 3.306.0
- '@aws-sdk/middleware-serde': 3.306.0
- '@aws-sdk/middleware-stack': 3.306.0
- '@aws-sdk/middleware-user-agent': 3.306.0
- '@aws-sdk/node-config-provider': 3.306.0
- '@aws-sdk/node-http-handler': 3.306.0
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/smithy-client': 3.306.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/url-parser': 3.306.0
- '@aws-sdk/util-base64': 3.303.0
- '@aws-sdk/util-body-length-browser': 3.303.0
- '@aws-sdk/util-body-length-node': 3.303.0
- '@aws-sdk/util-defaults-mode-browser': 3.306.0
- '@aws-sdk/util-defaults-mode-node': 3.306.0
- '@aws-sdk/util-endpoints': 3.306.0
- '@aws-sdk/util-retry': 3.306.0
- '@aws-sdk/util-user-agent-browser': 3.306.0
- '@aws-sdk/util-user-agent-node': 3.306.0
- '@aws-sdk/util-utf8': 3.303.0
+ '@aws-sdk/config-resolver': 3.310.0
+ '@aws-sdk/fetch-http-handler': 3.310.0
+ '@aws-sdk/hash-node': 3.310.0
+ '@aws-sdk/invalid-dependency': 3.310.0
+ '@aws-sdk/middleware-content-length': 3.310.0
+ '@aws-sdk/middleware-endpoint': 3.310.0
+ '@aws-sdk/middleware-host-header': 3.310.0
+ '@aws-sdk/middleware-logger': 3.310.0
+ '@aws-sdk/middleware-recursion-detection': 3.310.0
+ '@aws-sdk/middleware-retry': 3.310.0
+ '@aws-sdk/middleware-serde': 3.310.0
+ '@aws-sdk/middleware-stack': 3.310.0
+ '@aws-sdk/middleware-user-agent': 3.319.0
+ '@aws-sdk/node-config-provider': 3.310.0
+ '@aws-sdk/node-http-handler': 3.321.1
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/smithy-client': 3.316.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/url-parser': 3.310.0
+ '@aws-sdk/util-base64': 3.310.0
+ '@aws-sdk/util-body-length-browser': 3.310.0
+ '@aws-sdk/util-body-length-node': 3.310.0
+ '@aws-sdk/util-defaults-mode-browser': 3.316.0
+ '@aws-sdk/util-defaults-mode-node': 3.316.0
+ '@aws-sdk/util-endpoints': 3.319.0
+ '@aws-sdk/util-retry': 3.310.0
+ '@aws-sdk/util-user-agent-browser': 3.310.0
+ '@aws-sdk/util-user-agent-node': 3.310.0
+ '@aws-sdk/util-utf8': 3.310.0
tslib: 2.5.0
transitivePeerDependencies:
- aws-crt
dev: false
- /@aws-sdk/client-sso@3.306.0:
- resolution: {integrity: sha512-uqfLUOP9LlBoqXe3P250TPX3fGrabfRt9Q9rlLFK0fVBI7HPIQ/wsPplLoPrMeT04qQmTI03UnVKMNza3GqyIg==}
+ /@aws-sdk/client-sso@3.321.1:
+ resolution: {integrity: sha512-ecoT4tBGtRJR5G7oLBTMXZmgZZlff1amhSdKPEtkWxv6kWc8VPb5rRuRgVPsDR9HuesI6ZVlODptvGtnfkIJwA==}
engines: {node: '>=14.0.0'}
dependencies:
'@aws-crypto/sha256-browser': 3.0.0
'@aws-crypto/sha256-js': 3.0.0
- '@aws-sdk/config-resolver': 3.306.0
- '@aws-sdk/fetch-http-handler': 3.306.0
- '@aws-sdk/hash-node': 3.306.0
- '@aws-sdk/invalid-dependency': 3.306.0
- '@aws-sdk/middleware-content-length': 3.306.0
- '@aws-sdk/middleware-endpoint': 3.306.0
- '@aws-sdk/middleware-host-header': 3.306.0
- '@aws-sdk/middleware-logger': 3.306.0
- '@aws-sdk/middleware-recursion-detection': 3.306.0
- '@aws-sdk/middleware-retry': 3.306.0
- '@aws-sdk/middleware-serde': 3.306.0
- '@aws-sdk/middleware-stack': 3.306.0
- '@aws-sdk/middleware-user-agent': 3.306.0
- '@aws-sdk/node-config-provider': 3.306.0
- '@aws-sdk/node-http-handler': 3.306.0
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/smithy-client': 3.306.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/url-parser': 3.306.0
- '@aws-sdk/util-base64': 3.303.0
- '@aws-sdk/util-body-length-browser': 3.303.0
- '@aws-sdk/util-body-length-node': 3.303.0
- '@aws-sdk/util-defaults-mode-browser': 3.306.0
- '@aws-sdk/util-defaults-mode-node': 3.306.0
- '@aws-sdk/util-endpoints': 3.306.0
- '@aws-sdk/util-retry': 3.306.0
- '@aws-sdk/util-user-agent-browser': 3.306.0
- '@aws-sdk/util-user-agent-node': 3.306.0
- '@aws-sdk/util-utf8': 3.303.0
+ '@aws-sdk/config-resolver': 3.310.0
+ '@aws-sdk/fetch-http-handler': 3.310.0
+ '@aws-sdk/hash-node': 3.310.0
+ '@aws-sdk/invalid-dependency': 3.310.0
+ '@aws-sdk/middleware-content-length': 3.310.0
+ '@aws-sdk/middleware-endpoint': 3.310.0
+ '@aws-sdk/middleware-host-header': 3.310.0
+ '@aws-sdk/middleware-logger': 3.310.0
+ '@aws-sdk/middleware-recursion-detection': 3.310.0
+ '@aws-sdk/middleware-retry': 3.310.0
+ '@aws-sdk/middleware-serde': 3.310.0
+ '@aws-sdk/middleware-stack': 3.310.0
+ '@aws-sdk/middleware-user-agent': 3.319.0
+ '@aws-sdk/node-config-provider': 3.310.0
+ '@aws-sdk/node-http-handler': 3.321.1
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/smithy-client': 3.316.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/url-parser': 3.310.0
+ '@aws-sdk/util-base64': 3.310.0
+ '@aws-sdk/util-body-length-browser': 3.310.0
+ '@aws-sdk/util-body-length-node': 3.310.0
+ '@aws-sdk/util-defaults-mode-browser': 3.316.0
+ '@aws-sdk/util-defaults-mode-node': 3.316.0
+ '@aws-sdk/util-endpoints': 3.319.0
+ '@aws-sdk/util-retry': 3.310.0
+ '@aws-sdk/util-user-agent-browser': 3.310.0
+ '@aws-sdk/util-user-agent-node': 3.310.0
+ '@aws-sdk/util-utf8': 3.310.0
tslib: 2.5.0
transitivePeerDependencies:
- aws-crt
dev: false
- /@aws-sdk/client-sts@3.306.0:
- resolution: {integrity: sha512-LivDrH0OnAZDC3EB6hVrrl25itlMLn/C/epwDjpnH2Qdq+gjbZ0ElVNu8XOX4qaXoo0zyV5pztnzwD/A76mX2g==}
+ /@aws-sdk/client-sts@3.321.1:
+ resolution: {integrity: sha512-AB+N4a1TVEKl9Sd5O2TxTprEZp7Va6zPZLMraFAYMdmJVBmCmmwyBs7ygju685DpQ1dos5PRsKCRcossyY5pDQ==}
engines: {node: '>=14.0.0'}
dependencies:
'@aws-crypto/sha256-browser': 3.0.0
'@aws-crypto/sha256-js': 3.0.0
- '@aws-sdk/config-resolver': 3.306.0
- '@aws-sdk/credential-provider-node': 3.306.0
- '@aws-sdk/fetch-http-handler': 3.306.0
- '@aws-sdk/hash-node': 3.306.0
- '@aws-sdk/invalid-dependency': 3.306.0
- '@aws-sdk/middleware-content-length': 3.306.0
- '@aws-sdk/middleware-endpoint': 3.306.0
- '@aws-sdk/middleware-host-header': 3.306.0
- '@aws-sdk/middleware-logger': 3.306.0
- '@aws-sdk/middleware-recursion-detection': 3.306.0
- '@aws-sdk/middleware-retry': 3.306.0
- '@aws-sdk/middleware-sdk-sts': 3.306.0
- '@aws-sdk/middleware-serde': 3.306.0
- '@aws-sdk/middleware-signing': 3.306.0
- '@aws-sdk/middleware-stack': 3.306.0
- '@aws-sdk/middleware-user-agent': 3.306.0
- '@aws-sdk/node-config-provider': 3.306.0
- '@aws-sdk/node-http-handler': 3.306.0
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/smithy-client': 3.306.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/url-parser': 3.306.0
- '@aws-sdk/util-base64': 3.303.0
- '@aws-sdk/util-body-length-browser': 3.303.0
- '@aws-sdk/util-body-length-node': 3.303.0
- '@aws-sdk/util-defaults-mode-browser': 3.306.0
- '@aws-sdk/util-defaults-mode-node': 3.306.0
- '@aws-sdk/util-endpoints': 3.306.0
- '@aws-sdk/util-retry': 3.306.0
- '@aws-sdk/util-user-agent-browser': 3.306.0
- '@aws-sdk/util-user-agent-node': 3.306.0
- '@aws-sdk/util-utf8': 3.303.0
+ '@aws-sdk/config-resolver': 3.310.0
+ '@aws-sdk/credential-provider-node': 3.321.1
+ '@aws-sdk/fetch-http-handler': 3.310.0
+ '@aws-sdk/hash-node': 3.310.0
+ '@aws-sdk/invalid-dependency': 3.310.0
+ '@aws-sdk/middleware-content-length': 3.310.0
+ '@aws-sdk/middleware-endpoint': 3.310.0
+ '@aws-sdk/middleware-host-header': 3.310.0
+ '@aws-sdk/middleware-logger': 3.310.0
+ '@aws-sdk/middleware-recursion-detection': 3.310.0
+ '@aws-sdk/middleware-retry': 3.310.0
+ '@aws-sdk/middleware-sdk-sts': 3.310.0
+ '@aws-sdk/middleware-serde': 3.310.0
+ '@aws-sdk/middleware-signing': 3.310.0
+ '@aws-sdk/middleware-stack': 3.310.0
+ '@aws-sdk/middleware-user-agent': 3.319.0
+ '@aws-sdk/node-config-provider': 3.310.0
+ '@aws-sdk/node-http-handler': 3.321.1
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/smithy-client': 3.316.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/url-parser': 3.310.0
+ '@aws-sdk/util-base64': 3.310.0
+ '@aws-sdk/util-body-length-browser': 3.310.0
+ '@aws-sdk/util-body-length-node': 3.310.0
+ '@aws-sdk/util-defaults-mode-browser': 3.316.0
+ '@aws-sdk/util-defaults-mode-node': 3.316.0
+ '@aws-sdk/util-endpoints': 3.319.0
+ '@aws-sdk/util-retry': 3.310.0
+ '@aws-sdk/util-user-agent-browser': 3.310.0
+ '@aws-sdk/util-user-agent-node': 3.310.0
+ '@aws-sdk/util-utf8': 3.310.0
fast-xml-parser: 4.1.2
tslib: 2.5.0
transitivePeerDependencies:
- aws-crt
dev: false
- /@aws-sdk/config-resolver@3.306.0:
- resolution: {integrity: sha512-kpqHu6LvNMYxullm+tLCsY6KQ2mZUxZTdyWJKTYLZCTxj4HcGJxf4Jxj9dwFAZVl/clcVPGWcHJaQJjyjwzBzw==}
+ /@aws-sdk/config-resolver@3.310.0:
+ resolution: {integrity: sha512-8vsT+/50lOqfDxka9m/rRt6oxv1WuGZoP8oPMk0Dt+TxXMbAzf4+rejBgiB96wshI1k3gLokYRjSQZn+dDtT8g==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/util-config-provider': 3.295.0
- '@aws-sdk/util-middleware': 3.306.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/util-config-provider': 3.310.0
+ '@aws-sdk/util-middleware': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/credential-provider-env@3.306.0:
- resolution: {integrity: sha512-DTH+aMvMu+LAoWW+yfPkWzFXt/CPNFQ7+/4xiMnc7FWf+tjt+HZIrPECAV2rBVppNCkh7PC+xDSN61PFvBYOsw==}
+ /@aws-sdk/credential-provider-env@3.310.0:
+ resolution: {integrity: sha512-vvIPQpI16fj95xwS7M3D48F7QhZJBnnCgB5lR+b7So+vsG9ibm1mZRVGzVpdxCvgyOhHFbvrby9aalNJmmIP1A==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/property-provider': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/property-provider': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/credential-provider-imds@3.306.0:
- resolution: {integrity: sha512-WdrNhq2MwvjZk2I8Of+bZ/qWHG2hREQpwlBiG3tMeEkuywx7M1x3Rt0eHgiR1sTcm05kxNn0rB4OeWOeek37cA==}
+ /@aws-sdk/credential-provider-imds@3.310.0:
+ resolution: {integrity: sha512-baxK7Zp6dai5AGW01FIW27xS2KAaPUmKLIXv5SvFYsUgXXvNW55im4uG3b+2gA0F7V+hXvVBH08OEqmwW6we5w==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/node-config-provider': 3.306.0
- '@aws-sdk/property-provider': 3.306.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/url-parser': 3.306.0
+ '@aws-sdk/node-config-provider': 3.310.0
+ '@aws-sdk/property-provider': 3.310.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/url-parser': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/credential-provider-ini@3.306.0:
- resolution: {integrity: sha512-6VvP0YmXVd+pCnlD2iTDhNvO2Ikzyk9Ade/t5R1eZ4Vf1gKhDiNA2/AgDt9XlzQHk7iw1okTmYCeQsK1j+7+NQ==}
+ /@aws-sdk/credential-provider-ini@3.321.1:
+ resolution: {integrity: sha512-prndSVQhiikNaI40bYnM2Q8PkC35FCwhbQnBk6KXNvdtfo9RqatMC639F+6oryb3BuMy++Ij4Yoi8WnPBs5Sww==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/credential-provider-env': 3.306.0
- '@aws-sdk/credential-provider-imds': 3.306.0
- '@aws-sdk/credential-provider-process': 3.306.0
- '@aws-sdk/credential-provider-sso': 3.306.0
- '@aws-sdk/credential-provider-web-identity': 3.306.0
- '@aws-sdk/property-provider': 3.306.0
- '@aws-sdk/shared-ini-file-loader': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/credential-provider-env': 3.310.0
+ '@aws-sdk/credential-provider-imds': 3.310.0
+ '@aws-sdk/credential-provider-process': 3.310.0
+ '@aws-sdk/credential-provider-sso': 3.321.1
+ '@aws-sdk/credential-provider-web-identity': 3.310.0
+ '@aws-sdk/property-provider': 3.310.0
+ '@aws-sdk/shared-ini-file-loader': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
transitivePeerDependencies:
- aws-crt
dev: false
- /@aws-sdk/credential-provider-node@3.306.0:
- resolution: {integrity: sha512-HYuMmABRzbVWo03CElRUa+T+yenyUmLkwNCVAAvIRmbr9TnLT/bJbplXpUSzgSCS6T3TgwbQ9zf9xY9tX+gHzA==}
+ /@aws-sdk/credential-provider-node@3.321.1:
+ resolution: {integrity: sha512-5B1waOwSvY2JMLGRebo7IUqnTaGoCnby9cRbG/dhi7Ke97M3V8380S9THDJ/bktjL8zHEVfBVZy7HhXHzhSjEg==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/credential-provider-env': 3.306.0
- '@aws-sdk/credential-provider-imds': 3.306.0
- '@aws-sdk/credential-provider-ini': 3.306.0
- '@aws-sdk/credential-provider-process': 3.306.0
- '@aws-sdk/credential-provider-sso': 3.306.0
- '@aws-sdk/credential-provider-web-identity': 3.306.0
- '@aws-sdk/property-provider': 3.306.0
- '@aws-sdk/shared-ini-file-loader': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/credential-provider-env': 3.310.0
+ '@aws-sdk/credential-provider-imds': 3.310.0
+ '@aws-sdk/credential-provider-ini': 3.321.1
+ '@aws-sdk/credential-provider-process': 3.310.0
+ '@aws-sdk/credential-provider-sso': 3.321.1
+ '@aws-sdk/credential-provider-web-identity': 3.310.0
+ '@aws-sdk/property-provider': 3.310.0
+ '@aws-sdk/shared-ini-file-loader': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
transitivePeerDependencies:
- aws-crt
dev: false
- /@aws-sdk/credential-provider-process@3.306.0:
- resolution: {integrity: sha512-2RezGskHqJeHtGbK7CqhGNAoqXgQJb7FfPFqwUQ9oVDZS8f145jVwajjHcc7Qn3IwGoqylMF3uXIljUv89uDzA==}
+ /@aws-sdk/credential-provider-process@3.310.0:
+ resolution: {integrity: sha512-h73sg6GPMUWC+3zMCbA1nZ2O03nNJt7G96JdmnantiXBwHpRKWW8nBTLzx5uhXn6hTuTaoQRP/P+oxQJKYdMmA==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/property-provider': 3.306.0
- '@aws-sdk/shared-ini-file-loader': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/property-provider': 3.310.0
+ '@aws-sdk/shared-ini-file-loader': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/credential-provider-sso@3.306.0:
- resolution: {integrity: sha512-6msBUisMdOzk0ywJQNunZIb0rVMaA6GTx7ek8aCuWInX+lJm0oEPPVp+b3ewwVheih1rRC2bgNk8eAjfC9YcKw==}
+ /@aws-sdk/credential-provider-sso@3.321.1:
+ resolution: {integrity: sha512-kg0rc1OacJFgAvmZj0TOu+BSc+yRdnC5dO/RAag3XU6+hlQI5/C080RQp9Qj6V7ga0HtAJMRwJcUlCPA3RJPug==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/client-sso': 3.306.0
- '@aws-sdk/property-provider': 3.306.0
- '@aws-sdk/shared-ini-file-loader': 3.306.0
- '@aws-sdk/token-providers': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/client-sso': 3.321.1
+ '@aws-sdk/property-provider': 3.310.0
+ '@aws-sdk/shared-ini-file-loader': 3.310.0
+ '@aws-sdk/token-providers': 3.321.1
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
transitivePeerDependencies:
- aws-crt
dev: false
- /@aws-sdk/credential-provider-web-identity@3.306.0:
- resolution: {integrity: sha512-MOQGQaOtdo4zLQZ1bRjD2n1PUzfNty+sKe+1wlm5bIqTN93UX3S8f0QznucZr7uJxI4Z14ZLwuYeAUV4Tgchlw==}
+ /@aws-sdk/credential-provider-web-identity@3.310.0:
+ resolution: {integrity: sha512-H4SzuZXILNhK6/IR1uVvsUDZvzc051hem7GLyYghBCu8mU+tq28YhKE8MfSroi6eL2e5Vujloij1OM2EQQkPkw==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/property-provider': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/property-provider': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/eventstream-codec@3.306.0:
- resolution: {integrity: sha512-tQCo0tl/NdD3rVt9hDkGO3SLPXdohlRx6j9IMor5HpM7tCFcB4WNpDHPHyClxRoprLT04BZksJZcbVUybAeTcA==}
+ /@aws-sdk/eventstream-codec@3.310.0:
+ resolution: {integrity: sha512-clIeSgWbZbxwtsxZ/yoedNM0/kJFSIjjHPikuDGhxhqc+vP6TN3oYyVMFrYwFaTFhk2+S5wZcWYMw8Op1pWo+A==}
dependencies:
'@aws-crypto/crc32': 3.0.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/util-hex-encoding': 3.295.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/util-hex-encoding': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/eventstream-serde-browser@3.306.0:
- resolution: {integrity: sha512-Vbx0KQzZ5YAeYwC3tbCGsJZ2nSqKQlQFbuwhXe/Btz/dh1HaJPMxoIC8Dr0b6Z7S8l2Pq4ucqdNaUzLeJJbezw==}
+ /@aws-sdk/eventstream-serde-browser@3.310.0:
+ resolution: {integrity: sha512-3S6ziuQVALgEyz0TANGtYDVeG8ArK4Y05mcgrs8qUTmsvlDIXX37cR/DvmVbNB76M4IrsZeSAIajL9644CywkA==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/eventstream-serde-universal': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/eventstream-serde-universal': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/eventstream-serde-config-resolver@3.306.0:
- resolution: {integrity: sha512-MBNJspHTbP3+bZeDgfH+FcAxBodKH3NnXmSCyUosMJMaNQ8JtqsaW+y6FfvC6aVht3FyC6qKWBjuKfEeG2jLCA==}
+ /@aws-sdk/eventstream-serde-config-resolver@3.310.0:
+ resolution: {integrity: sha512-8s1Qdn9STj+sV75nUp9yt0W6fHS4BZ2jTm4Z/1Pcbvh2Gqs0WjH5n2StS+pDW5Y9J/HSGBl0ogmUr5lC5bXFHg==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/eventstream-serde-node@3.306.0:
- resolution: {integrity: sha512-Fq6n8vUMxMSqWFD2xSjh/X7Wyr+CggGHjsVgIFfXIXEK14iTOnOK7/yS86mDR6GBRupIfrw/Q+Jf0A7b8o2Wlg==}
+ /@aws-sdk/eventstream-serde-node@3.310.0:
+ resolution: {integrity: sha512-kSnRomCgW43K9TmQYuwN9+AoYPnhyOKroanUMyZEzJk7rpCPMj4OzaUpXfDYOvznFNYn7NLaH6nHLJAr0VPlJA==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/eventstream-serde-universal': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/eventstream-serde-universal': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/eventstream-serde-universal@3.306.0:
- resolution: {integrity: sha512-Xp2JCRkZm3dyVv2O/EJnOP1vwUwEmXXminbX1MgnuVIMifSbbjeo6lGqCsjrIkXyymkx4TvrmPmSWlwzPCnyVw==}
+ /@aws-sdk/eventstream-serde-universal@3.310.0:
+ resolution: {integrity: sha512-Qyjt5k/waV5cDukpgT824ISZAz5U0pwzLz5ztR409u85AGNkF/9n7MS+LSyBUBSb0WJ5pUeSD47WBk+nLq9Nhw==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/eventstream-codec': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/eventstream-codec': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/fetch-http-handler@3.306.0:
- resolution: {integrity: sha512-T8OODOnPpDqkXS+XSMIkd6hf90h833JLN93wq3ibbyD/WvGveufFFHsbsNyccE9+CSv/BjEuN5QbHqTKTp3BlA==}
+ /@aws-sdk/fetch-http-handler@3.310.0:
+ resolution: {integrity: sha512-Bi9vIwzdkw1zMcvi/zGzlWS9KfIEnAq4NNhsnCxbQ4OoIRU9wvU+WGZdBBhxg0ZxZmpp1j1aZhU53lLjA07MHw==}
dependencies:
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/querystring-builder': 3.306.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/util-base64': 3.303.0
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/querystring-builder': 3.310.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/util-base64': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/hash-blob-browser@3.306.0:
- resolution: {integrity: sha512-OK3xQ60Vz1VTDgMGk8PjFESQlDjXCLR/q1xc9ODyXBO2/A+ntUxMBcGfu7KYjtGI69g91H/BGhPAy+W5Gg+rmw==}
+ /@aws-sdk/hash-blob-browser@3.310.0:
+ resolution: {integrity: sha512-OoR8p0cbypToysLT0v3o2oyjy6+DKrY7GNCAzHOHJK9xmqXCt+DsjKoPeiY7o1sWX2aN6Plmvubj/zWxMKEn/A==}
dependencies:
- '@aws-sdk/chunked-blob-reader': 3.303.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/chunked-blob-reader': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/hash-node@3.306.0:
- resolution: {integrity: sha512-EcSLd6gKoDEEBPZqEv+Ky9gIyefwyyrAJGILGKoYBmcOIY7Y0xKId0hxCa9/1xvWTaVC1u+rA06DGgksZOa78w==}
+ /@aws-sdk/hash-node@3.310.0:
+ resolution: {integrity: sha512-NvE2fhRc8GRwCXBfDehxVAWCmVwVMILliAKVPAEr4yz2CkYs0tqU51S48x23dtna07H4qHtgpeNqVTthcIQOEQ==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/util-buffer-from': 3.303.0
- '@aws-sdk/util-utf8': 3.303.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/util-buffer-from': 3.310.0
+ '@aws-sdk/util-utf8': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/hash-stream-node@3.306.0:
- resolution: {integrity: sha512-9owhr/kmCRwRZWAeGnf3k+L5tD/DcLAEMfbIuH8uolEigI+YGRMXnJB0PpyzIyd1N8TM/bm6WW3uvQiFHy7Hxw==}
+ /@aws-sdk/hash-stream-node@3.310.0:
+ resolution: {integrity: sha512-ZoXdybNgvMz1Hl6k/e32xVL3jmG5p2IEk5mTtLfFEuskTJ74Z+VMYKkkF1whyy7KQfH83H+TQGnsGtlRCchQKw==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/util-utf8': 3.303.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/util-utf8': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/invalid-dependency@3.306.0:
- resolution: {integrity: sha512-9Mkcr+qG7QR4R5bJcA8bBNd8E2x6WaZStsQ3QeFbdQr3V3Tunvra/KlCFsEL55GgU8BZt5isOaHqq7uxs5ILtQ==}
+ /@aws-sdk/invalid-dependency@3.310.0:
+ resolution: {integrity: sha512-1s5RG5rSPXoa/aZ/Kqr5U/7lqpx+Ry81GprQ2bxWqJvWQIJ0IRUwo5pk8XFxbKVr/2a+4lZT/c3OGoBOM1yRRA==}
dependencies:
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/is-array-buffer@3.303.0:
- resolution: {integrity: sha512-IitBTr+pou7v5BrYLFH/SbIf3g1LIgMhcI3bDXBq2FjzmDftj4bW8BOmg05b9YKf2TrrggvJ4yk/jH+yYFXoJQ==}
+ /@aws-sdk/is-array-buffer@3.310.0:
+ resolution: {integrity: sha512-urnbcCR+h9NWUnmOtet/s4ghvzsidFmspfhYaHAmSRdy9yDjdjBJMFjjsn85A1ODUktztm+cVncXjQ38WCMjMQ==}
engines: {node: '>=14.0.0'}
dependencies:
tslib: 2.5.0
dev: false
- /@aws-sdk/lib-storage@3.306.0(@aws-sdk/abort-controller@3.306.0)(@aws-sdk/client-s3@3.306.0):
- resolution: {integrity: sha512-/XPTLZ39gxaQGpcZhYW+o/cKpkELt94Exurs/2YSjNv0BiTueDLoY8pPMkXeuspOKlcPLQneX2HnjbvaQXjUhQ==}
+ /@aws-sdk/lib-storage@3.321.1(@aws-sdk/abort-controller@3.310.0)(@aws-sdk/client-s3@3.321.1):
+ resolution: {integrity: sha512-a9JZRuq1sc64zgoIIMipVovDgdCuiCaTRB0pY4rjVIvhQVgdyg734rlZbvdDOduiKYz7QvYkqMAdsp0ASV14Gg==}
engines: {node: '>=14.0.0'}
peerDependencies:
'@aws-sdk/abort-controller': ^3.0.0
'@aws-sdk/client-s3': ^3.0.0
dependencies:
- '@aws-sdk/abort-controller': 3.306.0
- '@aws-sdk/client-s3': 3.306.0
- '@aws-sdk/middleware-endpoint': 3.306.0
- '@aws-sdk/smithy-client': 3.306.0
+ '@aws-sdk/abort-controller': 3.310.0
+ '@aws-sdk/client-s3': 3.321.1
+ '@aws-sdk/middleware-endpoint': 3.310.0
+ '@aws-sdk/smithy-client': 3.316.0
buffer: 5.6.0
events: 3.3.0
stream-browserify: 3.0.0
tslib: 2.5.0
dev: false
- /@aws-sdk/md5-js@3.306.0:
- resolution: {integrity: sha512-QCv5hfFL3AsSQPZvO8YOaBoNlRu6+B2lVSVZHLaWrortlaRGQi5+fYgYPhpTho/2cw0qeogB4pQIho3UgCAmfg==}
+ /@aws-sdk/md5-js@3.310.0:
+ resolution: {integrity: sha512-x5sRBUrEfLWAS1EhwbbDQ7cXq6uvBxh3qR2XAsnGvFFceTeAadk7cVogWxlk3PC+OCeeym7c3/6Bv2HQ2f1YyQ==}
dependencies:
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/util-utf8': 3.303.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/util-utf8': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/middleware-bucket-endpoint@3.306.0:
- resolution: {integrity: sha512-DL8Q146s0nJ7MSHxt8sr6MDR4tIh+q7z9Yki7o1soCMNGTaYCUC20nSffs7IhQAUo1/q3pRh8LoJNtIpa7Q3pA==}
+ /@aws-sdk/middleware-bucket-endpoint@3.310.0:
+ resolution: {integrity: sha512-uJJfHI7v4AgbJZRLtyI8ap2QRWkBokGc3iyUoQ+dVNT3/CE2ZCu694A6W+H0dRqg79dIE+f9CRNdtLGa/Ehhvg==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/util-arn-parser': 3.295.0
- '@aws-sdk/util-config-provider': 3.295.0
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/util-arn-parser': 3.310.0
+ '@aws-sdk/util-config-provider': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/middleware-content-length@3.306.0:
- resolution: {integrity: sha512-JbONf2Ms+/DVRcpFNsKGdOQU94Js56KV+AhlPJmCwLxfyWvQjTt0KxFC1Dd+cjeNEXUduvBarrehgsqFlWnoHQ==}
+ /@aws-sdk/middleware-content-length@3.310.0:
+ resolution: {integrity: sha512-P8tQZxgDt6CAh1wd/W6WPzjc+uWPJwQkm+F7rAwRlM+k9q17HrhnksGDKcpuuLyIhPQYdmOMIkpKVgXGa4avhQ==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/middleware-endpoint@3.306.0:
- resolution: {integrity: sha512-i3QRiwgkcsuVN55O7l8I/QGwCypGRZXdYkPjU56LI2w2oiZ82f/nVMNXVc+ZFm2YH7WbCE+5jguw2J7HXdOlyQ==}
+ /@aws-sdk/middleware-endpoint@3.310.0:
+ resolution: {integrity: sha512-Z+N2vOL8K354/lstkClxLLsr6hCpVRh+0tCMXrVj66/NtKysCEZ/0b9LmqOwD9pWHNiI2mJqXwY0gxNlKAroUg==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/middleware-serde': 3.306.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/url-parser': 3.306.0
- '@aws-sdk/util-middleware': 3.306.0
+ '@aws-sdk/middleware-serde': 3.310.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/url-parser': 3.310.0
+ '@aws-sdk/util-middleware': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/middleware-expect-continue@3.306.0:
- resolution: {integrity: sha512-k+UgyQpTqFaizClQRdVTpcGJduRXKS0GCDDV/pdz1NVx09YHCIxVpkpdkYj+2T1Ufp3bK7ju2FLaXJwoO6/r+Q==}
+ /@aws-sdk/middleware-expect-continue@3.310.0:
+ resolution: {integrity: sha512-l3d1z2gt+gINJDnPSyu84IxfzjzPfCQrqC1sunw2cZGo/sXtEiq698Q3SiTcO2PGP4LBQAy2RHb5wVBJP708CQ==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/middleware-flexible-checksums@3.306.0:
- resolution: {integrity: sha512-0Z3eKIVdP9son45Id18pDWwfcqoYTzDsSfePMD//HByBQQusL8pS1D1mlWqVpnV++ZfNmPcnph+v6cIfHYVM4Q==}
+ /@aws-sdk/middleware-flexible-checksums@3.310.0:
+ resolution: {integrity: sha512-5ndnLgzgGVpWkmHBAiYkagHqiSuow8q62J4J6E2PzaQ77+fm8W3nfdy7hK5trHokEyouCZdxT/XK/IRhgj/4PA==}
engines: {node: '>=14.0.0'}
dependencies:
'@aws-crypto/crc32': 3.0.0
'@aws-crypto/crc32c': 3.0.0
- '@aws-sdk/is-array-buffer': 3.303.0
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/util-utf8': 3.303.0
+ '@aws-sdk/is-array-buffer': 3.310.0
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/util-utf8': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/middleware-host-header@3.306.0:
- resolution: {integrity: sha512-mHDHK9E+c7HwMlrCJ+VFSB6tkq8oJVkYEHCvPkdrnzN/g9P/d/UhPIeGapZXMbAIZEaLpEGqs536mYzeRKZG8A==}
+ /@aws-sdk/middleware-host-header@3.310.0:
+ resolution: {integrity: sha512-QWSA+46/hXorXyWa61ic2K7qZzwHTiwfk2e9mRRjeIRepUgI3qxFjsYqrWtrOGBjmFmq0pYIY8Bb/DCJuQqcoA==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/middleware-location-constraint@3.306.0:
- resolution: {integrity: sha512-TfSPLDcl/QTs2OHplJhbJMzdWe3/du5wrNn0LECligzX4f7Ygn2zV/UtqHet36tv206ssDm6b9g8KvRvG3FT2g==}
+ /@aws-sdk/middleware-location-constraint@3.310.0:
+ resolution: {integrity: sha512-LFm0JTQWwTPWL/tZU2wsQTl8J5PpDEkXjEhaXVKamtyH0xhysRqd+0n92n65dc8oztAuQkb9xUbErGn5b6gsew==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/middleware-logger@3.306.0:
- resolution: {integrity: sha512-1FRHp/QB0Lb+CgP+c9CYW6BZh+q+5pnuOKo/Rd6hjYiM+kT1G/cWdXnMJQBR4rbTCTixbqCnObNJ1EyP/ofQhQ==}
+ /@aws-sdk/middleware-logger@3.310.0:
+ resolution: {integrity: sha512-Lurm8XofrASBRnAVtiSNuDSRsRqPNg27RIFLLsLp/pqog9nFJ0vz0kgdb9S5Z+zw83Mm+UlqOe6D8NTUNp4fVg==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/middleware-recursion-detection@3.306.0:
- resolution: {integrity: sha512-Hpj42ZLmwCy/CtVxi57NTeOEPoUJlivF3VIgowZ9JhaF61cakVKyrJ+f3jwXciDUtuYrdKm5Wf6prW6apWo0YA==}
+ /@aws-sdk/middleware-recursion-detection@3.310.0:
+ resolution: {integrity: sha512-SuB75/xk/gyue24gkriTwO2jFd7YcUGZDClQYuRejgbXSa3CO0lWyawQtfLcSSEBp9izrEVXuFH24K1eAft5nQ==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/middleware-retry@3.306.0:
- resolution: {integrity: sha512-eMyfr/aeurXXDz4x+WVrvLI8fVDP6klJOjziBEWZ/MUNP/hTFhkiQsMVbvT6O4Pspp7+FgCSdcUPG6Os2gK+CQ==}
+ /@aws-sdk/middleware-retry@3.310.0:
+ resolution: {integrity: sha512-oTPsRy2W4s+dfxbJPW7Km+hHtv/OMsNsVfThAq8DDYKC13qlr1aAyOqGLD+dpBy2aKe7ss517Sy2HcHtHqm7/g==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/service-error-classification': 3.306.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/util-middleware': 3.306.0
- '@aws-sdk/util-retry': 3.306.0
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/service-error-classification': 3.310.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/util-middleware': 3.310.0
+ '@aws-sdk/util-retry': 3.310.0
tslib: 2.5.0
uuid: 8.3.2
dev: false
- /@aws-sdk/middleware-sdk-s3@3.306.0:
- resolution: {integrity: sha512-x1V4NKpY8waDBTUjg2TLcqLZjD6OoED0NMFvKPxul/l2rzInvcNK6L2ifxxhYb7YG9N4MtLnCvAUqEW5h3eLYw==}
+ /@aws-sdk/middleware-sdk-s3@3.310.0:
+ resolution: {integrity: sha512-QK9x9g2ksg0hOjjYgqddeFcn5ctUEGdxJVu4OumPXceulefMcSO2jyH2qTybYSA93nqNQFdFmg5wQfvIRUWFCQ==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/util-arn-parser': 3.295.0
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/util-arn-parser': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/middleware-sdk-sts@3.306.0:
- resolution: {integrity: sha512-2rSAR3nc5faYuEnh1KxQMCMCkEkJyaDfA3zwWLqZ+/TBCH0PlPkBv+Z9yXmteEki0vI5Hr+e+atTutJZoyG13g==}
+ /@aws-sdk/middleware-sdk-sts@3.310.0:
+ resolution: {integrity: sha512-+5PFwlYNLvLLIfw0ASAoWV/iIF8Zv6R6QGtyP0CclhRSvNjgbQDVnV0g95MC5qvh+GB/Yjlkt8qAjLSPjHfsrQ==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/middleware-signing': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/middleware-signing': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/middleware-serde@3.306.0:
- resolution: {integrity: sha512-M3gyPLPduZXMvdgt4XEpVO+3t0ZVPdgeQQwG6JnXv0dgyUizshYs4lrVOAb1KwF6StsmkrAgSN+I273elLiKjA==}
+ /@aws-sdk/middleware-serde@3.310.0:
+ resolution: {integrity: sha512-RNeeTVWSLTaentUeCgQKZhAl+C6hxtwD78cQWS10UymWpQFwbaxztzKUu4UQS5xA2j6PxwPRRUjqa4jcFjfLsg==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/middleware-signing@3.306.0:
- resolution: {integrity: sha512-JhpSriN4xa4a/p5gAPL0OWFKJF4eWYU3K+LLlXBNGMbxg/qNL4skgT4dMFe3ii9EW8kI+r6tpvSgC+lP7/Tyng==}
+ /@aws-sdk/middleware-signing@3.310.0:
+ resolution: {integrity: sha512-f9mKq+XMdW207Af3hKjdTnpNhdtwqWuvFs/ZyXoOkp/g1MY1O6L23Jy6i52m29LxbT4AuNRG1oKODfXM0vYVjQ==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/property-provider': 3.306.0
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/signature-v4': 3.306.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/util-middleware': 3.306.0
+ '@aws-sdk/property-provider': 3.310.0
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/signature-v4': 3.310.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/util-middleware': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/middleware-ssec@3.306.0:
- resolution: {integrity: sha512-4oXDBsQSPWxyr5K9OjQA1Y+WChGYLD85mvcycd3WqKn9BKsQ9F4enE3XHmRg0qlgXV9CJ/fG1axLC3R9Aa6u/g==}
+ /@aws-sdk/middleware-ssec@3.310.0:
+ resolution: {integrity: sha512-CnEwNKVpd5bXnrCKPaePF8mWTA9ET21OMBb54y9b0fd8K02zoOcdBz4DWfh1SjFD4HkgCdja4egd8l2ivyvqmw==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/middleware-stack@3.306.0:
- resolution: {integrity: sha512-G//a6MVSxyFVpOMZ+dzT3+w7XblOd2tRJ5g+/okjn3pNBLbo5o9Hu33K/bz0SQjT/m5mU2F9m0wcdCPYbRPysg==}
+ /@aws-sdk/middleware-stack@3.310.0:
+ resolution: {integrity: sha512-010O1PD+UAcZVKRvqEusE1KJqN96wwrf6QsqbRM0ywsKQ21NDweaHvEDlds2VHpgmofxkRLRu/IDrlPkKRQrRg==}
engines: {node: '>=14.0.0'}
dependencies:
tslib: 2.5.0
dev: false
- /@aws-sdk/middleware-user-agent@3.306.0:
- resolution: {integrity: sha512-tP6I+Lbs68muPfdMA6Rfc+8fYo49nEn9A3RMiOU2COClWsmiZatpbK9UYlqIOxeGB/s2jI7hXmQq6tT2LStLSg==}
+ /@aws-sdk/middleware-user-agent@3.319.0:
+ resolution: {integrity: sha512-ytaLx2dlR5AdMSne6FuDCISVg8hjyKj+cHU20b2CRA/E/z+XXrLrssp4JrCgizRKPPUep0psMIa22Zd6osTT5Q==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/util-endpoints': 3.306.0
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/util-endpoints': 3.319.0
tslib: 2.5.0
dev: false
- /@aws-sdk/node-config-provider@3.306.0:
- resolution: {integrity: sha512-+m+ALxNx5E1zLPPijO1pAbT5tnofLzZFWlnSYBEiOIwzaRU44rLYDqAhgXJkMMbOECkffDrv6ym0oWJIwJI+DA==}
+ /@aws-sdk/node-config-provider@3.310.0:
+ resolution: {integrity: sha512-T/Pp6htc6hq/Cq+MLNDSyiwWCMVF6GqbBbXKVlO5L8rdHx4sq9xPdoPveZhGWrxvkanjA6eCwUp6E0riBOSVng==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/property-provider': 3.306.0
- '@aws-sdk/shared-ini-file-loader': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/property-provider': 3.310.0
+ '@aws-sdk/shared-ini-file-loader': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/node-http-handler@3.306.0:
- resolution: {integrity: sha512-qvNSIVdGf0pnWEXsAulIqXk7LML25Zc1yxbujxoAj8oX5y+mDhzQdHKrMgc0FuI4RKoEd9px4DYoUbmTWrrxwA==}
+ /@aws-sdk/node-http-handler@3.321.1:
+ resolution: {integrity: sha512-DdQBrtFFDNtzphJIN3s93Vf+qd9LHSzH6WTQRrWoXhTDMHDzSI2Cn+c5KWfk89Nggp/n3+OTwUPQeCiBT5EBuw==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/abort-controller': 3.306.0
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/querystring-builder': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/abort-controller': 3.310.0
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/querystring-builder': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/property-provider@3.306.0:
- resolution: {integrity: sha512-37PnbjpANjHys0Y+DVmKUz1JbSGZ/mAndZeplTUsFDUtbNwJRw/fDyWUvGC82JWB4gNSP5muWscFvetZnK2l8A==}
+ /@aws-sdk/property-provider@3.310.0:
+ resolution: {integrity: sha512-3lxDb0akV6BBzmFe4nLPaoliQbAifyWJhuvuDOu7e8NzouvpQXs0275w9LePhhcgjKAEVXUIse05ZW2DLbxo/g==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/protocol-http@3.306.0:
- resolution: {integrity: sha512-6Z8bqB8Ydz/qG7+lJzjwsjIca2w2zp4nZ2HjxMoUm0NBbVXGDx7H9qy9eOUqEiCbdXbsfK2BmVQreLhFLt056Q==}
+ /@aws-sdk/protocol-http@3.310.0:
+ resolution: {integrity: sha512-fgZ1aw/irQtnrsR58pS8ThKOWo57Py3xX6giRvwSgZDEcxHfVzuQjy9yPuV++v04fdmdtgpbGf8WfvAAJ11yXQ==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/querystring-builder@3.306.0:
- resolution: {integrity: sha512-kvz6fLwE4KojTxbphuo9JPwKKuhau2mmSurnqhtf77t9+0cOh2uzyYhIUtOFewpLj+qGoh4b2EODlJqczc7IKg==}
+ /@aws-sdk/querystring-builder@3.310.0:
+ resolution: {integrity: sha512-ZHH8GV/80+pWGo7DzsvwvXR5xVxUHXUvPJPFAkhr6nCf78igdoF8gR10ScFoEKbtEapoNTaZlKHPXxpD8aPG7A==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/util-uri-escape': 3.303.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/util-uri-escape': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/querystring-parser@3.306.0:
- resolution: {integrity: sha512-YjOdLcyS/8sNkFPgnxyUx+cM/P2XFGCA2WjQ0e9AXX8xFFkmnY6U5w2EknQ5zyvKy+R/KAV0KAMJBUB+ofjg0A==}
+ /@aws-sdk/querystring-parser@3.310.0:
+ resolution: {integrity: sha512-YkIznoP6lsiIUHinx++/lbb3tlMURGGqMpo0Pnn32zYzGrJXA6eC3D0as2EcMjo55onTfuLcIiX4qzXes2MYOA==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/service-error-classification@3.306.0:
- resolution: {integrity: sha512-lmXIVHWU5J60GmmTgyj79kupWYg5ntyNrUPt1P9FYTsXz+tdk4YYH7/2IxZ1XjBr4jEsN56gfSI0cfT07ztQJA==}
+ /@aws-sdk/service-error-classification@3.310.0:
+ resolution: {integrity: sha512-PuyC7k3qfIKeH2LCnDwbttMOKq3qAx4buvg0yfnJtQOz6t1AR8gsnAq0CjKXXyfkXwNKWTqCpE6lVNUIkXgsMw==}
engines: {node: '>=14.0.0'}
dev: false
- /@aws-sdk/shared-ini-file-loader@3.306.0:
- resolution: {integrity: sha512-mDmBRN+Y0+EBD5megId97UIJGV/rmRsAds22qy0mmVdD3X7qlxn974btXVgfZyda6qw/pX6hgi8X99Qj6Wjb0w==}
+ /@aws-sdk/shared-ini-file-loader@3.310.0:
+ resolution: {integrity: sha512-N0q9pG0xSjQwc690YQND5bofm+4nfUviQ/Ppgan2kU6aU0WUq8KwgHJBto/YEEI+VlrME30jZJnxtOvcZJc2XA==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/signature-v4-multi-region@3.306.0:
- resolution: {integrity: sha512-c+J/7oYZf1MWgEqHyzW6EM25FZfqHer+pMWwyaDCEQFsAuuNeqbvr5MWFrjYpSeF8MpfVbFsEfho5XmjHMq6jw==}
+ /@aws-sdk/signature-v4-multi-region@3.310.0:
+ resolution: {integrity: sha512-q8W+RIomTS/q85Ntgks/CoDElwqkC9+4OCicee5YznNHjQ4gtNWhUkYIyIRWRmXa/qx/AUreW9DM8FAecCOdng==}
engines: {node: '>=14.0.0'}
peerDependencies:
'@aws-sdk/signature-v4-crt': ^3.118.0
@@ -1794,137 +1839,137 @@ packages:
'@aws-sdk/signature-v4-crt':
optional: true
dependencies:
- '@aws-sdk/protocol-http': 3.306.0
- '@aws-sdk/signature-v4': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/protocol-http': 3.310.0
+ '@aws-sdk/signature-v4': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/signature-v4@3.306.0:
- resolution: {integrity: sha512-yoQTo6wLirKHg34Zhm8tKmfEaK8fOn+psVdMtRs2vGq3uzKLb+YW5zywnujoVwBvygQTWxiDMwRxDduWAisccA==}
+ /@aws-sdk/signature-v4@3.310.0:
+ resolution: {integrity: sha512-1M60P1ZBNAjCFv9sYW29OF6okktaeibWyW3lMXqzoHF70lHBZh+838iUchznXUA5FLabfn4jBFWMRxlAXJUY2Q==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/is-array-buffer': 3.303.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/util-hex-encoding': 3.295.0
- '@aws-sdk/util-middleware': 3.306.0
- '@aws-sdk/util-uri-escape': 3.303.0
- '@aws-sdk/util-utf8': 3.303.0
+ '@aws-sdk/is-array-buffer': 3.310.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/util-hex-encoding': 3.310.0
+ '@aws-sdk/util-middleware': 3.310.0
+ '@aws-sdk/util-uri-escape': 3.310.0
+ '@aws-sdk/util-utf8': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/smithy-client@3.306.0:
- resolution: {integrity: sha512-AFdNkto0Md6laio9t70WtvocoZqVcAydbY5csimXQh+lhKVmy/C+ZcKarDvaa0JD6PjSHb4snYzcINFpHW5LJQ==}
+ /@aws-sdk/smithy-client@3.316.0:
+ resolution: {integrity: sha512-6YXOKbRnXeS8r8RWzuL6JMBolDYM5Wa4fD/VY6x/wK78i2xErHOvqzHgyyeLI1MMw4uqyd4wRNJNWC9TMPduXw==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/middleware-stack': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/middleware-stack': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/token-providers@3.306.0:
- resolution: {integrity: sha512-GQlUx9u+fHLjOJedudLM//j7RSZAip57n59bjn/I3TRVjDs065opNu2xSWMPm1n46kPx6VA5z+DktvuFeAblxQ==}
+ /@aws-sdk/token-providers@3.321.1:
+ resolution: {integrity: sha512-I1sXS4qXirSvgvrOIPf+e1D7GvC83DdeyMxHZvuhHgeMCqDAzToS8OLxOX0enN9xZRHWAQYja8xyeGbDL2I0Zw==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/client-sso-oidc': 3.306.0
- '@aws-sdk/property-provider': 3.306.0
- '@aws-sdk/shared-ini-file-loader': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/client-sso-oidc': 3.321.1
+ '@aws-sdk/property-provider': 3.310.0
+ '@aws-sdk/shared-ini-file-loader': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
transitivePeerDependencies:
- aws-crt
dev: false
- /@aws-sdk/types@3.306.0:
- resolution: {integrity: sha512-RnyknWWpQcRmNH7AsNr89sdhOoltCU/4YEwBMw34Eh+/36l7HfA5PdEKbsOkO7MO4+2g5qmmm/AHcnHRvymApg==}
+ /@aws-sdk/types@3.310.0:
+ resolution: {integrity: sha512-j8eamQJ7YcIhw7fneUfs8LYl3t01k4uHi4ZDmNRgtbmbmTTG3FZc2MotStZnp3nZB6vLiPF1o5aoJxWVvkzS6A==}
engines: {node: '>=14.0.0'}
dependencies:
tslib: 2.5.0
dev: false
- /@aws-sdk/url-parser@3.306.0:
- resolution: {integrity: sha512-mhyOjtycZgxKYo2CoDhDQONuRd5TLfEwmyGWVgFrfubF0LejQ3rkBRLC5zT9TBZ8RJHNlqU2oGdsZCy3JV6Rlw==}
+ /@aws-sdk/url-parser@3.310.0:
+ resolution: {integrity: sha512-mCLnCaSB9rQvAgx33u0DujLvr4d5yEm/W5r789GblwwQnlNXedVu50QRizMLTpltYWyAUoXjJgQnJHmJMaKXhw==}
dependencies:
- '@aws-sdk/querystring-parser': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/querystring-parser': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/util-arn-parser@3.295.0:
- resolution: {integrity: sha512-kSSVymcbjyQQHvCZaTt1teKKW4MSSMPRdPNxSNO1aLsVwxrWdnAggDrpHwFjvPCRUcKtpThepATOz75PfUm9Bg==}
+ /@aws-sdk/util-arn-parser@3.310.0:
+ resolution: {integrity: sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==}
engines: {node: '>=14.0.0'}
dependencies:
tslib: 2.5.0
dev: false
- /@aws-sdk/util-base64@3.303.0:
- resolution: {integrity: sha512-oj+p/GHHPcZEKjiiOHU/CyNQeh8i+8dfMMzU+VGdoK5jHaVG8h2b+V7GPf7I4wDkG2ySCK5b5Jw5NUHwdTJ13Q==}
+ /@aws-sdk/util-base64@3.310.0:
+ resolution: {integrity: sha512-v3+HBKQvqgdzcbL+pFswlx5HQsd9L6ZTlyPVL2LS9nNXnCcR3XgGz9jRskikRUuUvUXtkSG1J88GAOnJ/apTPg==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/util-buffer-from': 3.303.0
+ '@aws-sdk/util-buffer-from': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/util-body-length-browser@3.303.0:
- resolution: {integrity: sha512-T643m0pKzgjAvPFy4W8zL+aszG3T22U8hb6stlMvT0z++Smv8QfIvkIkXjWyH2KlOt5GKliHwdOv8SAi0FSMJQ==}
+ /@aws-sdk/util-body-length-browser@3.310.0:
+ resolution: {integrity: sha512-sxsC3lPBGfpHtNTUoGXMQXLwjmR0zVpx0rSvzTPAuoVILVsp5AU/w5FphNPxD5OVIjNbZv9KsKTuvNTiZjDp9g==}
dependencies:
tslib: 2.5.0
dev: false
- /@aws-sdk/util-body-length-node@3.303.0:
- resolution: {integrity: sha512-/hS8z6e18Le60hJr2TUIFoUjUiAsnQsuDn6DxX74GXhMOHeSwZDJ9jHF39quYkNMmAE37GrVH4MI9vE0pN27qw==}
+ /@aws-sdk/util-body-length-node@3.310.0:
+ resolution: {integrity: sha512-2tqGXdyKhyA6w4zz7UPoS8Ip+7sayOg9BwHNidiGm2ikbDxm1YrCfYXvCBdwaJxa4hJfRVz+aL9e+d3GqPI9pQ==}
engines: {node: '>=14.0.0'}
dependencies:
tslib: 2.5.0
dev: false
- /@aws-sdk/util-buffer-from@3.303.0:
- resolution: {integrity: sha512-hUU+NW+SW6RNojtAKnnmz+tDShVKlEx2YsS4a5fSfrKRUes+zWz10cxVX0RQfysd3R6tdSHhbjsSj8eCIybheg==}
+ /@aws-sdk/util-buffer-from@3.310.0:
+ resolution: {integrity: sha512-i6LVeXFtGih5Zs8enLrt+ExXY92QV25jtEnTKHsmlFqFAuL3VBeod6boeMXkN2p9lbSVVQ1sAOOYZOHYbYkntw==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/is-array-buffer': 3.303.0
+ '@aws-sdk/is-array-buffer': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/util-config-provider@3.295.0:
- resolution: {integrity: sha512-/5Dl1aV2yI8YQjqwmg4RTnl/E9NmNsx7HIwBZt+dTcOrM0LMUwczQBFFcLyqCj/qv5y+VsvLoAAA/OiBT7hb3w==}
+ /@aws-sdk/util-config-provider@3.310.0:
+ resolution: {integrity: sha512-xIBaYo8dwiojCw8vnUcIL4Z5tyfb1v3yjqyJKJWV/dqKUFOOS0U591plmXbM+M/QkXyML3ypon1f8+BoaDExrg==}
engines: {node: '>=14.0.0'}
dependencies:
tslib: 2.5.0
dev: false
- /@aws-sdk/util-defaults-mode-browser@3.306.0:
- resolution: {integrity: sha512-XczPC/klGngMNDcNvThloyeKoPoG61ts1tZVcDbyRaOqmoMH80fn+c6Ah4A/BPzbo8wm1MIA9kqeJI0ypps6qQ==}
+ /@aws-sdk/util-defaults-mode-browser@3.316.0:
+ resolution: {integrity: sha512-6FSqLhYmaihtH2n1s4b2rlLW0ABU8N6VZIfzLfe2ING4PF0MzfaMMhnTFUHVXfKCVGoR8yP6iyFTRCyHGVEL1w==}
engines: {node: '>= 10.0.0'}
dependencies:
- '@aws-sdk/property-provider': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/property-provider': 3.310.0
+ '@aws-sdk/types': 3.310.0
bowser: 2.11.0
tslib: 2.5.0
dev: false
- /@aws-sdk/util-defaults-mode-node@3.306.0:
- resolution: {integrity: sha512-0hs/cS7Pu4sEO78n0Uv7ybBEFq5j23TOu3QNH+YMzF8n4yuQtaMwNM8DI2s03/pVGXYsPzO7036jREGcu+enXw==}
+ /@aws-sdk/util-defaults-mode-node@3.316.0:
+ resolution: {integrity: sha512-dkYy10hdjPSScXXvnjGpZpnJxllkb6ICHgLMwZ4JczLHhPM12T/4PQ758YN8HS+muiYDGX1Bl2z1jd/bMcewBQ==}
engines: {node: '>= 10.0.0'}
dependencies:
- '@aws-sdk/config-resolver': 3.306.0
- '@aws-sdk/credential-provider-imds': 3.306.0
- '@aws-sdk/node-config-provider': 3.306.0
- '@aws-sdk/property-provider': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/config-resolver': 3.310.0
+ '@aws-sdk/credential-provider-imds': 3.310.0
+ '@aws-sdk/node-config-provider': 3.310.0
+ '@aws-sdk/property-provider': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/util-endpoints@3.306.0:
- resolution: {integrity: sha512-aPTqU4VGhec8LDhKZrfA3/sBHTYRa0favKEo8aEa/vIZJTNBAFlUhvr5z7peAr8gBOtZZcElzX8PiK3jjn3ILw==}
+ /@aws-sdk/util-endpoints@3.319.0:
+ resolution: {integrity: sha512-3I64UMoYA2e2++oOUJXRcFtYLpLylnZFRltWfPo1B3dLlf+MIWat9djT+mMus+hW1ntLsvAIVu1hLVePJC0gvw==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/util-hex-encoding@3.295.0:
- resolution: {integrity: sha512-XJcoVo41kHzhe28PBm/rqt5mdCp8R6abwiW9ug1dA6FOoPUO8kBUxDv6xaOmA2hfRvd2ocFfBXaUCBqUowkGcQ==}
+ /@aws-sdk/util-hex-encoding@3.310.0:
+ resolution: {integrity: sha512-sVN7mcCCDSJ67pI1ZMtk84SKGqyix6/0A1Ab163YKn+lFBQRMKexleZzpYzNGxYzmQS6VanP/cfU7NiLQOaSfA==}
engines: {node: '>=14.0.0'}
dependencies:
tslib: 2.5.0
@@ -1937,59 +1982,59 @@ packages:
tslib: 2.5.0
dev: false
- /@aws-sdk/util-middleware@3.306.0:
- resolution: {integrity: sha512-14CSm1mTrfSNBGbkZu8vSjXYg7DUMfZc74IinOajcFtTswa/6SyiyhU9DK0a837qqwxSfFGpnE2thVeJIF/7FA==}
+ /@aws-sdk/util-middleware@3.310.0:
+ resolution: {integrity: sha512-FTSUKL/eRb9X6uEZClrTe27QFXUNNp7fxYrPndZwk1hlaOP5ix+MIHBcI7pIiiY/JPfOUmPyZOu+HetlFXjWog==}
engines: {node: '>=14.0.0'}
dependencies:
tslib: 2.5.0
dev: false
- /@aws-sdk/util-retry@3.306.0:
- resolution: {integrity: sha512-zcgTEIehQAIAm4vBNWfXZpDNbIrDM095vZmpbozQwK/pfDqMGvq7j3r9atKuEGTtoomoGoYwj3x/KEhO6JXJLg==}
+ /@aws-sdk/util-retry@3.310.0:
+ resolution: {integrity: sha512-FwWGhCBLfoivTMUHu1LIn4NjrN9JLJ/aX5aZmbcPIOhZVFJj638j0qDgZXyfvVqBuBZh7M8kGq0Oahy3dp69OA==}
engines: {node: '>= 14.0.0'}
dependencies:
- '@aws-sdk/service-error-classification': 3.306.0
+ '@aws-sdk/service-error-classification': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/util-stream-browser@3.306.0:
- resolution: {integrity: sha512-g/UdUlVtMrdctzu8Yk6UKIlN9OtqGyTDC3bCzzr6dNmfdRMuilL5LZCAgusEEzH2MZGTNtw5BbYZBN//UNhxwQ==}
+ /@aws-sdk/util-stream-browser@3.310.0:
+ resolution: {integrity: sha512-bysXZHwFwvbqOTCScCdCnoLk1K3GCo0HRIYEZuL7O7MHrQmfaYRXcaft/p22+GUv9VeFXS/eJJZ5r4u32az94w==}
dependencies:
- '@aws-sdk/fetch-http-handler': 3.306.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/util-base64': 3.303.0
- '@aws-sdk/util-hex-encoding': 3.295.0
- '@aws-sdk/util-utf8': 3.303.0
+ '@aws-sdk/fetch-http-handler': 3.310.0
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/util-base64': 3.310.0
+ '@aws-sdk/util-hex-encoding': 3.310.0
+ '@aws-sdk/util-utf8': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/util-stream-node@3.306.0:
- resolution: {integrity: sha512-hF8P52E3borUp6Z4K8cQOGm+bJgkbkE/VSl0EiO29E563ZiPnT/VBYVICX7MIDNPgHKDN5n/OSVfe1egjhOigQ==}
+ /@aws-sdk/util-stream-node@3.321.1:
+ resolution: {integrity: sha512-jvfff1zeA8q16hQWSC0BGwcHJPCwoh+bwiuAjihfl9q1tFLYuqaTzJzzkL1bntUsbW+y/ac5DO7fWcYPq0jWkw==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/node-http-handler': 3.306.0
- '@aws-sdk/types': 3.306.0
- '@aws-sdk/util-buffer-from': 3.303.0
+ '@aws-sdk/node-http-handler': 3.321.1
+ '@aws-sdk/types': 3.310.0
+ '@aws-sdk/util-buffer-from': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/util-uri-escape@3.303.0:
- resolution: {integrity: sha512-N3ULNuHCL3QzAlCTY+XRRkRQTYCTU8RRuzFCJX0pDpz9t2K+tLT7DbxqupWGNFGl5Xlulf1Is14J3BP/Dx91rA==}
+ /@aws-sdk/util-uri-escape@3.310.0:
+ resolution: {integrity: sha512-drzt+aB2qo2LgtDoiy/3sVG8w63cgLkqFIa2NFlGpUgHFWTXkqtbgf4L5QdjRGKWhmZsnqkbtL7vkSWEcYDJ4Q==}
engines: {node: '>=14.0.0'}
dependencies:
tslib: 2.5.0
dev: false
- /@aws-sdk/util-user-agent-browser@3.306.0:
- resolution: {integrity: sha512-uZAtpvCasUdWRlB/nEjN0gf6G7810hT50VyWjpd6mQW78myV8M5fu/R03UFAZ+D8fhqqIdzR/IXDY1QUGp8bCA==}
+ /@aws-sdk/util-user-agent-browser@3.310.0:
+ resolution: {integrity: sha512-yU/4QnHHuQ5z3vsUqMQVfYLbZGYwpYblPiuZx4Zo9+x0PBkNjYMqctdDcrpoH9Z2xZiDN16AmQGK1tix117ZKw==}
dependencies:
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/types': 3.310.0
bowser: 2.11.0
tslib: 2.5.0
dev: false
- /@aws-sdk/util-user-agent-node@3.306.0:
- resolution: {integrity: sha512-zLp9wIx7FZ0qFLimYW3lJ1uJM5gqxmmcQjNimUaUq/4a1caDkaiF/QeyyMFva+wIjyHRv22P5abUBjIEZrs5WA==}
+ /@aws-sdk/util-user-agent-node@3.310.0:
+ resolution: {integrity: sha512-Ra3pEl+Gn2BpeE7KiDGpi4zj7WJXZA5GXnGo3mjbi9+Y3zrbuhJAbdZO3mO/o7xDgMC6ph4xCTbaSGzU6b6EDg==}
engines: {node: '>=14.0.0'}
peerDependencies:
aws-crt: '>=1.0.0'
@@ -1997,8 +2042,8 @@ packages:
aws-crt:
optional: true
dependencies:
- '@aws-sdk/node-config-provider': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/node-config-provider': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
@@ -2008,25 +2053,25 @@ packages:
tslib: 2.5.0
dev: false
- /@aws-sdk/util-utf8@3.303.0:
- resolution: {integrity: sha512-tZXVuMOIONPOuOGBs/XRdzxv6jUvTM620dRFFIHZwlGiW8bo0x0LlonrzDAJZA4e9ZwmxJIj8Ji13WVRBGvZWg==}
+ /@aws-sdk/util-utf8@3.310.0:
+ resolution: {integrity: sha512-DnLfFT8uCO22uOJc0pt0DsSNau1GTisngBCDw8jQuWT5CqogMJu4b/uXmwEqfj8B3GX6Xsz8zOd6JpRlPftQoA==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/util-buffer-from': 3.303.0
+ '@aws-sdk/util-buffer-from': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/util-waiter@3.306.0:
- resolution: {integrity: sha512-/cCmEaxGJOVKHuuzm4zM3aY2Un7pJGyewcd9WWvLjZIoF9jCCqyjmsxM+OXdCjs7NOdo41cValYhILYI+nD8Tg==}
+ /@aws-sdk/util-waiter@3.310.0:
+ resolution: {integrity: sha512-AV5j3guH/Y4REu+Qh3eXQU9igljHuU4XjX2sADAgf54C0kkhcCCkkiuzk3IsX089nyJCqIcj5idbjdvpnH88Vw==}
engines: {node: '>=14.0.0'}
dependencies:
- '@aws-sdk/abort-controller': 3.306.0
- '@aws-sdk/types': 3.306.0
+ '@aws-sdk/abort-controller': 3.310.0
+ '@aws-sdk/types': 3.310.0
tslib: 2.5.0
dev: false
- /@aws-sdk/xml-builder@3.303.0:
- resolution: {integrity: sha512-Oht8XdmCkLhwZx2WTjOOLN8rt9000zJS4Hehv9NG7+kKfaA6sKFGIculmumaS+h8hAwWFndtgpOTlKC95zHSWQ==}
+ /@aws-sdk/xml-builder@3.310.0:
+ resolution: {integrity: sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==}
engines: {node: '>=14.0.0'}
dependencies:
tslib: 2.5.0
@@ -2039,11 +2084,6 @@ packages:
'@babel/highlight': 7.18.6
dev: true
- /@babel/compat-data@7.20.10:
- resolution: {integrity: sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==}
- engines: {node: '>=6.9.0'}
- dev: true
-
/@babel/compat-data@7.21.4:
resolution: {integrity: sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==}
engines: {node: '>=6.9.0'}
@@ -2056,13 +2096,13 @@ packages:
'@ampproject/remapping': 2.2.0
'@babel/code-frame': 7.18.6
'@babel/generator': 7.21.3
- '@babel/helper-compilation-targets': 7.20.7(@babel/core@7.21.3)
+ '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.3)
'@babel/helper-module-transforms': 7.21.2
'@babel/helpers': 7.21.0
- '@babel/parser': 7.21.3
+ '@babel/parser': 7.21.4
'@babel/template': 7.20.7
'@babel/traverse': 7.21.3
- '@babel/types': 7.21.3
+ '@babel/types': 7.21.4
convert-source-map: 1.9.0
debug: 4.3.4(supports-color@8.1.1)
gensync: 1.0.0-beta.2
@@ -2076,7 +2116,7 @@ packages:
resolution: {integrity: sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.21.3
+ '@babel/types': 7.21.4
'@jridgewell/gen-mapping': 0.3.2
'@jridgewell/trace-mapping': 0.3.17
jsesc: 2.5.2
@@ -2097,20 +2137,6 @@ packages:
'@babel/types': 7.21.4
dev: true
- /@babel/helper-compilation-targets@7.20.7(@babel/core@7.21.3):
- resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
- dependencies:
- '@babel/compat-data': 7.20.10
- '@babel/core': 7.21.3
- '@babel/helper-validator-option': 7.21.0
- browserslist: 4.21.5
- lru-cache: 5.1.1
- semver: 6.3.0
- dev: true
-
/@babel/helper-compilation-targets@7.21.4(@babel/core@7.21.3):
resolution: {integrity: sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==}
engines: {node: '>=6.9.0'}
@@ -2342,12 +2368,12 @@ packages:
dependencies:
'@babel/types': 7.21.4
- /@babel/parser@7.21.3:
- resolution: {integrity: sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==}
+ /@babel/parser@7.21.4:
+ resolution: {integrity: sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
- '@babel/types': 7.21.3
+ '@babel/types': 7.21.4
/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.21.3):
resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==}
@@ -3052,7 +3078,7 @@ packages:
'@babel/helper-module-imports': 7.18.6
'@babel/helper-plugin-utils': 7.20.2
'@babel/plugin-syntax-jsx': 7.18.6(@babel/core@7.21.3)
- '@babel/types': 7.21.3
+ '@babel/types': 7.21.4
dev: true
/@babel/plugin-transform-regenerator@7.20.5(@babel/core@7.21.3):
@@ -3163,92 +3189,6 @@ packages:
'@babel/helper-plugin-utils': 7.20.2
dev: true
- /@babel/preset-env@7.20.2(@babel/core@7.21.3):
- resolution: {integrity: sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
- dependencies:
- '@babel/compat-data': 7.20.10
- '@babel/core': 7.21.3
- '@babel/helper-compilation-targets': 7.20.7(@babel/core@7.21.3)
- '@babel/helper-plugin-utils': 7.20.2
- '@babel/helper-validator-option': 7.21.0
- '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7(@babel/core@7.21.3)
- '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.21.3)
- '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-proposal-class-static-block': 7.21.0(@babel/core@7.21.3)
- '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.21.3)
- '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.21.3)
- '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.21.3)
- '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.3)
- '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-proposal-private-property-in-object': 7.21.0(@babel/core@7.21.3)
- '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.3)
- '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.3)
- '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.21.3)
- '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.21.3)
- '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.21.3)
- '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.21.3)
- '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.3)
- '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.3)
- '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.3)
- '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.3)
- '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.3)
- '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.3)
- '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.3)
- '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.21.3)
- '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.3)
- '@babel/plugin-transform-arrow-functions': 7.20.7(@babel/core@7.21.3)
- '@babel/plugin-transform-async-to-generator': 7.20.7(@babel/core@7.21.3)
- '@babel/plugin-transform-block-scoped-functions': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-transform-block-scoping': 7.21.0(@babel/core@7.21.3)
- '@babel/plugin-transform-classes': 7.21.0(@babel/core@7.21.3)
- '@babel/plugin-transform-computed-properties': 7.20.7(@babel/core@7.21.3)
- '@babel/plugin-transform-destructuring': 7.21.3(@babel/core@7.21.3)
- '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-transform-duplicate-keys': 7.18.9(@babel/core@7.21.3)
- '@babel/plugin-transform-exponentiation-operator': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-transform-for-of': 7.21.0(@babel/core@7.21.3)
- '@babel/plugin-transform-function-name': 7.18.9(@babel/core@7.21.3)
- '@babel/plugin-transform-literals': 7.18.9(@babel/core@7.21.3)
- '@babel/plugin-transform-member-expression-literals': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-transform-modules-amd': 7.20.11(@babel/core@7.21.3)
- '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.21.3)
- '@babel/plugin-transform-modules-systemjs': 7.20.11(@babel/core@7.21.3)
- '@babel/plugin-transform-modules-umd': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5(@babel/core@7.21.3)
- '@babel/plugin-transform-new-target': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-transform-object-super': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.21.3)
- '@babel/plugin-transform-property-literals': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-transform-regenerator': 7.20.5(@babel/core@7.21.3)
- '@babel/plugin-transform-reserved-words': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-transform-shorthand-properties': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-transform-spread': 7.20.7(@babel/core@7.21.3)
- '@babel/plugin-transform-sticky-regex': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.21.3)
- '@babel/plugin-transform-typeof-symbol': 7.18.9(@babel/core@7.21.3)
- '@babel/plugin-transform-unicode-escapes': 7.18.10(@babel/core@7.21.3)
- '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.21.3)
- '@babel/preset-modules': 0.1.5(@babel/core@7.21.3)
- '@babel/types': 7.21.3
- babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.21.3)
- babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.21.3)
- babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.21.3)
- core-js-compat: 3.29.1
- semver: 6.3.0
- transitivePeerDependencies:
- - supports-color
- dev: true
-
/@babel/preset-env@7.21.4(@babel/core@7.21.3):
resolution: {integrity: sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw==}
engines: {node: '>=6.9.0'}
@@ -3403,14 +3343,13 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.13.11
- dev: true
/@babel/template@7.20.7:
resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
- '@babel/parser': 7.21.3
+ '@babel/parser': 7.21.4
'@babel/types': 7.21.4
dev: true
@@ -3424,7 +3363,7 @@ packages:
'@babel/helper-function-name': 7.21.0
'@babel/helper-hoist-variables': 7.18.6
'@babel/helper-split-export-declaration': 7.18.6
- '@babel/parser': 7.21.3
+ '@babel/parser': 7.21.4
'@babel/types': 7.21.4
debug: 4.3.4(supports-color@8.1.1)
globals: 11.12.0
@@ -3432,14 +3371,6 @@ packages:
- supports-color
dev: true
- /@babel/types@7.21.3:
- resolution: {integrity: sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==}
- engines: {node: '>=6.9.0'}
- dependencies:
- '@babel/helper-string-parser': 7.19.4
- '@babel/helper-validator-identifier': 7.19.1
- to-fast-properties: 2.0.0
-
/@babel/types@7.21.4:
resolution: {integrity: sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==}
engines: {node: '>=6.9.0'}
@@ -3456,26 +3387,29 @@ packages:
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
dev: true
- /@bull-board/api@5.0.0:
- resolution: {integrity: sha512-nOB8TUSeX8h2wgMxmPZwy0p3XqRX/DotR71M5FT5OS/bZCZIKRpEc36TIeKPimZli6voFyX+rsEqVhGenpIKUQ==}
+ /@bull-board/api@5.1.2(@bull-board/ui@5.1.2):
+ resolution: {integrity: sha512-NLV88eDnOMd0XuYUNBcaYL6UdxbGYY91kP+P5Jled6CGcYmXLXv/mzAopxmgctqmP07cjC2EXN/4Oc7dyalDqA==}
+ peerDependencies:
+ '@bull-board/ui': 5.1.2
dependencies:
+ '@bull-board/ui': 5.1.2
redis-info: 3.1.0
dev: false
- /@bull-board/fastify@5.0.0:
- resolution: {integrity: sha512-g7eLQhap9HiRjWv3NkgbVrdiG8RDUwyA3qDW2jES1dRcpWPBD9/3rxESwzuIoxa2h0OJ34VLY9hsgpu0vRyhZg==}
+ /@bull-board/fastify@5.1.2:
+ resolution: {integrity: sha512-qiURhcqMfER5hp4RtgepMDbPj5H4ZKNOgK+7RIG3bd3d0tBoLjXzooXFryxzd6w130pXU9/crUMtcMP+Ulaj6g==}
dependencies:
- '@bull-board/api': 5.0.0
- '@bull-board/ui': 5.0.0
- '@fastify/static': 6.10.0
+ '@bull-board/api': 5.1.2(@bull-board/ui@5.1.2)
+ '@bull-board/ui': 5.1.2
+ '@fastify/static': 6.10.1
'@fastify/view': 7.4.1
ejs: 3.1.8
dev: false
- /@bull-board/ui@5.0.0:
- resolution: {integrity: sha512-WznLX8dGthUWimZDqN5Zft3Axp6SStlI244BhiQYXB7xXZRMgaihNZxh7vgpwg0Osmw/gPFvAI+NFVGc3tvj9g==}
+ /@bull-board/ui@5.1.2:
+ resolution: {integrity: sha512-DXXbKA4NLo5D19Vssrg4pPFaFjXVzjFN0ht4GVuoJQejy7t/RVrWzZCjdyVuSiOFTlG3SyB39zW5a95Q5EXUTg==}
dependencies:
- '@bull-board/api': 5.0.0
+ '@bull-board/api': 5.1.2(@bull-board/ui@5.1.2)
dev: false
/@canvas/image-data@1.0.0:
@@ -3637,7 +3571,6 @@ packages:
/@discoveryjs/json-ext@0.5.7:
resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
engines: {node: '>=10.0.0'}
- dev: true
/@emotion/use-insertion-effect-with-fallbacks@1.0.0(react@18.2.0):
resolution: {integrity: sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==}
@@ -3647,365 +3580,189 @@ packages:
react: 18.2.0
dev: true
- /@esbuild/android-arm64@0.17.14:
- resolution: {integrity: sha512-eLOpPO1RvtsP71afiFTvS7tVFShJBCT0txiv/xjFBo5a7R7Gjw7X0IgIaFoLKhqXYAXhahoXm7qAmRXhY4guJg==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [android]
- requiresBuild: true
- optional: true
-
- /@esbuild/android-arm64@0.17.15:
- resolution: {integrity: sha512-0kOB6Y7Br3KDVgHeg8PRcvfLkq+AccreK///B4Z6fNZGr/tNHX0z2VywCc7PTeWp+bPvjA5WMvNXltHw5QjAIA==}
+ /@esbuild/android-arm64@0.17.18:
+ resolution: {integrity: sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
optional: true
- /@esbuild/android-arm@0.17.14:
- resolution: {integrity: sha512-0CnlwnjDU8cks0yJLXfkaU/uoLyRf9VZJs4p1PskBr2AlAHeEsFEwJEo0of/Z3g+ilw5mpyDwThlxzNEIxOE4g==}
- engines: {node: '>=12'}
- cpu: [arm]
- os: [android]
- requiresBuild: true
- optional: true
-
- /@esbuild/android-arm@0.17.15:
- resolution: {integrity: sha512-sRSOVlLawAktpMvDyJIkdLI/c/kdRTOqo8t6ImVxg8yT7LQDUYV5Rp2FKeEosLr6ZCja9UjYAzyRSxGteSJPYg==}
+ /@esbuild/android-arm@0.17.18:
+ resolution: {integrity: sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
requiresBuild: true
optional: true
- /@esbuild/android-x64@0.17.14:
- resolution: {integrity: sha512-nrfQYWBfLGfSGLvRVlt6xi63B5IbfHm3tZCdu/82zuFPQ7zez4XjmRtF/wIRYbJQ/DsZrxJdEvYFE67avYXyng==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [android]
- requiresBuild: true
- optional: true
-
- /@esbuild/android-x64@0.17.15:
- resolution: {integrity: sha512-MzDqnNajQZ63YkaUWVl9uuhcWyEyh69HGpMIrf+acR4otMkfLJ4sUCxqwbCyPGicE9dVlrysI3lMcDBjGiBBcQ==}
+ /@esbuild/android-x64@0.17.18:
+ resolution: {integrity: sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
optional: true
- /@esbuild/darwin-arm64@0.17.14:
- resolution: {integrity: sha512-eoSjEuDsU1ROwgBH/c+fZzuSyJUVXQTOIN9xuLs9dE/9HbV/A5IqdXHU1p2OfIMwBwOYJ9SFVGGldxeRCUJFyw==}
+ /@esbuild/darwin-arm64@0.17.18:
+ resolution: {integrity: sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
optional: true
- /@esbuild/darwin-arm64@0.17.15:
- resolution: {integrity: sha512-7siLjBc88Z4+6qkMDxPT2juf2e8SJxmsbNVKFY2ifWCDT72v5YJz9arlvBw5oB4W/e61H1+HDB/jnu8nNg0rLA==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [darwin]
- requiresBuild: true
- optional: true
-
- /@esbuild/darwin-x64@0.17.14:
- resolution: {integrity: sha512-zN0U8RWfrDttdFNkHqFYZtOH8hdi22z0pFm0aIJPsNC4QQZv7je8DWCX5iA4Zx6tRhS0CCc0XC2m7wKsbWEo5g==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [darwin]
- requiresBuild: true
- optional: true
-
- /@esbuild/darwin-x64@0.17.15:
- resolution: {integrity: sha512-NbImBas2rXwYI52BOKTW342Tm3LTeVlaOQ4QPZ7XuWNKiO226DisFk/RyPk3T0CKZkKMuU69yOvlapJEmax7cg==}
+ /@esbuild/darwin-x64@0.17.18:
+ resolution: {integrity: sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
optional: true
- /@esbuild/freebsd-arm64@0.17.14:
- resolution: {integrity: sha512-z0VcD4ibeZWVQCW1O7szaLxGsx54gcCnajEJMdYoYjLiq4g1jrP2lMq6pk71dbS5+7op/L2Aod+erw+EUr28/A==}
+ /@esbuild/freebsd-arm64@0.17.18:
+ resolution: {integrity: sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
optional: true
- /@esbuild/freebsd-arm64@0.17.15:
- resolution: {integrity: sha512-Xk9xMDjBVG6CfgoqlVczHAdJnCs0/oeFOspFap5NkYAmRCT2qTn1vJWA2f419iMtsHSLm+O8B6SLV/HlY5cYKg==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [freebsd]
- requiresBuild: true
- optional: true
-
- /@esbuild/freebsd-x64@0.17.14:
- resolution: {integrity: sha512-hd9mPcxfTgJlolrPlcXkQk9BMwNBvNBsVaUe5eNUqXut6weDQH8whcNaKNF2RO8NbpT6GY8rHOK2A9y++s+ehw==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [freebsd]
- requiresBuild: true
- optional: true
-
- /@esbuild/freebsd-x64@0.17.15:
- resolution: {integrity: sha512-3TWAnnEOdclvb2pnfsTWtdwthPfOz7qAfcwDLcfZyGJwm1SRZIMOeB5FODVhnM93mFSPsHB9b/PmxNNbSnd0RQ==}
+ /@esbuild/freebsd-x64@0.17.18:
+ resolution: {integrity: sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
optional: true
- /@esbuild/linux-arm64@0.17.14:
- resolution: {integrity: sha512-FhAMNYOq3Iblcj9i+K0l1Fp/MHt+zBeRu/Qkf0LtrcFu3T45jcwB6A1iMsemQ42vR3GBhjNZJZTaCe3VFPbn9g==}
+ /@esbuild/linux-arm64@0.17.18:
+ resolution: {integrity: sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
optional: true
- /@esbuild/linux-arm64@0.17.15:
- resolution: {integrity: sha512-T0MVnYw9KT6b83/SqyznTs/3Jg2ODWrZfNccg11XjDehIved2oQfrX/wVuev9N936BpMRaTR9I1J0tdGgUgpJA==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [linux]
- requiresBuild: true
- optional: true
-
- /@esbuild/linux-arm@0.17.14:
- resolution: {integrity: sha512-BNTl+wSJ1omsH8s3TkQmIIIQHwvwJrU9u1ggb9XU2KTVM4TmthRIVyxSp2qxROJHhZuW/r8fht46/QE8hU8Qvg==}
- engines: {node: '>=12'}
- cpu: [arm]
- os: [linux]
- requiresBuild: true
- optional: true
-
- /@esbuild/linux-arm@0.17.15:
- resolution: {integrity: sha512-MLTgiXWEMAMr8nmS9Gigx43zPRmEfeBfGCwxFQEMgJ5MC53QKajaclW6XDPjwJvhbebv+RzK05TQjvH3/aM4Xw==}
+ /@esbuild/linux-arm@0.17.18:
+ resolution: {integrity: sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
optional: true
- /@esbuild/linux-ia32@0.17.14:
- resolution: {integrity: sha512-91OK/lQ5y2v7AsmnFT+0EyxdPTNhov3y2CWMdizyMfxSxRqHazXdzgBKtlmkU2KYIc+9ZK3Vwp2KyXogEATYxQ==}
+ /@esbuild/linux-ia32@0.17.18:
+ resolution: {integrity: sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
optional: true
- /@esbuild/linux-ia32@0.17.15:
- resolution: {integrity: sha512-wp02sHs015T23zsQtU4Cj57WiteiuASHlD7rXjKUyAGYzlOKDAjqK6bk5dMi2QEl/KVOcsjwL36kD+WW7vJt8Q==}
- engines: {node: '>=12'}
- cpu: [ia32]
- os: [linux]
- requiresBuild: true
- optional: true
-
- /@esbuild/linux-loong64@0.17.14:
- resolution: {integrity: sha512-vp15H+5NR6hubNgMluqqKza85HcGJgq7t6rMH7O3Y6ApiOWPkvW2AJfNojUQimfTp6OUrACUXfR4hmpcENXoMQ==}
- engines: {node: '>=12'}
- cpu: [loong64]
- os: [linux]
- requiresBuild: true
- optional: true
-
- /@esbuild/linux-loong64@0.17.15:
- resolution: {integrity: sha512-k7FsUJjGGSxwnBmMh8d7IbObWu+sF/qbwc+xKZkBe/lTAF16RqxRCnNHA7QTd3oS2AfGBAnHlXL67shV5bBThQ==}
+ /@esbuild/linux-loong64@0.17.18:
+ resolution: {integrity: sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
requiresBuild: true
optional: true
- /@esbuild/linux-mips64el@0.17.14:
- resolution: {integrity: sha512-90TOdFV7N+fgi6c2+GO9ochEkmm9kBAKnuD5e08GQMgMINOdOFHuYLPQ91RYVrnWwQ5683sJKuLi9l4SsbJ7Hg==}
- engines: {node: '>=12'}
- cpu: [mips64el]
- os: [linux]
- requiresBuild: true
- optional: true
-
- /@esbuild/linux-mips64el@0.17.15:
- resolution: {integrity: sha512-ZLWk6czDdog+Q9kE/Jfbilu24vEe/iW/Sj2d8EVsmiixQ1rM2RKH2n36qfxK4e8tVcaXkvuV3mU5zTZviE+NVQ==}
+ /@esbuild/linux-mips64el@0.17.18:
+ resolution: {integrity: sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
optional: true
- /@esbuild/linux-ppc64@0.17.14:
- resolution: {integrity: sha512-NnBGeoqKkTugpBOBZZoktQQ1Yqb7aHKmHxsw43NddPB2YWLAlpb7THZIzsRsTr0Xw3nqiPxbA1H31ZMOG+VVPQ==}
- engines: {node: '>=12'}
- cpu: [ppc64]
- os: [linux]
- requiresBuild: true
- optional: true
-
- /@esbuild/linux-ppc64@0.17.15:
- resolution: {integrity: sha512-mY6dPkIRAiFHRsGfOYZC8Q9rmr8vOBZBme0/j15zFUKM99d4ILY4WpOC7i/LqoY+RE7KaMaSfvY8CqjJtuO4xg==}
+ /@esbuild/linux-ppc64@0.17.18:
+ resolution: {integrity: sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
optional: true
- /@esbuild/linux-riscv64@0.17.14:
- resolution: {integrity: sha512-0qdlKScLXA8MGVy21JUKvMzCYWovctuP8KKqhtE5A6IVPq4onxXhSuhwDd2g5sRCzNDlDjitc5sX31BzDoL5Fw==}
- engines: {node: '>=12'}
- cpu: [riscv64]
- os: [linux]
- requiresBuild: true
- optional: true
-
- /@esbuild/linux-riscv64@0.17.15:
- resolution: {integrity: sha512-EcyUtxffdDtWjjwIH8sKzpDRLcVtqANooMNASO59y+xmqqRYBBM7xVLQhqF7nksIbm2yHABptoioS9RAbVMWVA==}
+ /@esbuild/linux-riscv64@0.17.18:
+ resolution: {integrity: sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
optional: true
- /@esbuild/linux-s390x@0.17.14:
- resolution: {integrity: sha512-Hdm2Jo1yaaOro4v3+6/zJk6ygCqIZuSDJHdHaf8nVH/tfOuoEX5Riv03Ka15LmQBYJObUTNS1UdyoMk0WUn9Ww==}
- engines: {node: '>=12'}
- cpu: [s390x]
- os: [linux]
- requiresBuild: true
- optional: true
-
- /@esbuild/linux-s390x@0.17.15:
- resolution: {integrity: sha512-BuS6Jx/ezxFuHxgsfvz7T4g4YlVrmCmg7UAwboeyNNg0OzNzKsIZXpr3Sb/ZREDXWgt48RO4UQRDBxJN3B9Rbg==}
+ /@esbuild/linux-s390x@0.17.18:
+ resolution: {integrity: sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
optional: true
- /@esbuild/linux-x64@0.17.14:
- resolution: {integrity: sha512-8KHF17OstlK4DuzeF/KmSgzrTWQrkWj5boluiiq7kvJCiQVzUrmSkaBvcLB2UgHpKENO2i6BthPkmUhNDaJsVw==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [linux]
- requiresBuild: true
- optional: true
-
- /@esbuild/linux-x64@0.17.15:
- resolution: {integrity: sha512-JsdS0EgEViwuKsw5tiJQo9UdQdUJYuB+Mf6HxtJSPN35vez1hlrNb1KajvKWF5Sa35j17+rW1ECEO9iNrIXbNg==}
+ /@esbuild/linux-x64@0.17.18:
+ resolution: {integrity: sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
optional: true
- /@esbuild/netbsd-x64@0.17.14:
- resolution: {integrity: sha512-nVwpqvb3yyXztxIT2+VsxJhB5GCgzPdk1n0HHSnchRAcxqKO6ghXwHhJnr0j/B+5FSyEqSxF4q03rbA2fKXtUQ==}
+ /@esbuild/netbsd-x64@0.17.18:
+ resolution: {integrity: sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
optional: true
- /@esbuild/netbsd-x64@0.17.15:
- resolution: {integrity: sha512-R6fKjtUysYGym6uXf6qyNephVUQAGtf3n2RCsOST/neIwPqRWcnc3ogcielOd6pT+J0RDR1RGcy0ZY7d3uHVLA==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [netbsd]
- requiresBuild: true
- optional: true
-
- /@esbuild/openbsd-x64@0.17.14:
- resolution: {integrity: sha512-1RZ7uQQ9zcy/GSAJL1xPdN7NDdOOtNEGiJalg/MOzeakZeTrgH/DoCkbq7TaPDiPhWqnDF+4bnydxRqQD7il6g==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [openbsd]
- requiresBuild: true
- optional: true
-
- /@esbuild/openbsd-x64@0.17.15:
- resolution: {integrity: sha512-mVD4PGc26b8PI60QaPUltYKeSX0wxuy0AltC+WCTFwvKCq2+OgLP4+fFd+hZXzO2xW1HPKcytZBdjqL6FQFa7w==}
+ /@esbuild/openbsd-x64@0.17.18:
+ resolution: {integrity: sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
optional: true
- /@esbuild/sunos-x64@0.17.14:
- resolution: {integrity: sha512-nqMjDsFwv7vp7msrwWRysnM38Sd44PKmW8EzV01YzDBTcTWUpczQg6mGao9VLicXSgW/iookNK6AxeogNVNDZA==}
+ /@esbuild/sunos-x64@0.17.18:
+ resolution: {integrity: sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
optional: true
- /@esbuild/sunos-x64@0.17.15:
- resolution: {integrity: sha512-U6tYPovOkw3459t2CBwGcFYfFRjivcJJc1WC8Q3funIwX8x4fP+R6xL/QuTPNGOblbq/EUDxj9GU+dWKX0oWlQ==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [sunos]
- requiresBuild: true
- optional: true
-
- /@esbuild/win32-arm64@0.17.14:
- resolution: {integrity: sha512-xrD0mccTKRBBIotrITV7WVQAwNJ5+1va6L0H9zN92v2yEdjfAN7864cUaZwJS7JPEs53bDTzKFbfqVlG2HhyKQ==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [win32]
- requiresBuild: true
- optional: true
-
- /@esbuild/win32-arm64@0.17.15:
- resolution: {integrity: sha512-W+Z5F++wgKAleDABemiyXVnzXgvRFs+GVKThSI+mGgleLWluv0D7Diz4oQpgdpNzh4i2nNDzQtWbjJiqutRp6Q==}
+ /@esbuild/win32-arm64@0.17.18:
+ resolution: {integrity: sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
optional: true
- /@esbuild/win32-ia32@0.17.14:
- resolution: {integrity: sha512-nXpkz9bbJrLLyUTYtRotSS3t5b+FOuljg8LgLdINWFs3FfqZMtbnBCZFUmBzQPyxqU87F8Av+3Nco/M3hEcu1w==}
+ /@esbuild/win32-ia32@0.17.18:
+ resolution: {integrity: sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
optional: true
- /@esbuild/win32-ia32@0.17.15:
- resolution: {integrity: sha512-Muz/+uGgheShKGqSVS1KsHtCyEzcdOn/W/Xbh6H91Etm+wiIfwZaBn1W58MeGtfI8WA961YMHFYTthBdQs4t+w==}
- engines: {node: '>=12'}
- cpu: [ia32]
- os: [win32]
- requiresBuild: true
- optional: true
-
- /@esbuild/win32-x64@0.17.14:
- resolution: {integrity: sha512-gPQmsi2DKTaEgG14hc3CHXHp62k8g6qr0Pas+I4lUxRMugGSATh/Bi8Dgusoz9IQ0IfdrvLpco6kujEIBoaogA==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [win32]
- requiresBuild: true
- optional: true
-
- /@esbuild/win32-x64@0.17.15:
- resolution: {integrity: sha512-DjDa9ywLUUmjhV2Y9wUTIF+1XsmuFGvZoCmOWkli1XcNAh5t25cc7fgsCx4Zi/Uurep3TTLyDiKATgGEg61pkA==}
+ /@esbuild/win32-x64@0.17.18:
+ resolution: {integrity: sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
optional: true
- /@eslint-community/eslint-utils@4.4.0(eslint@8.37.0):
+ /@eslint-community/eslint-utils@4.4.0(eslint@8.39.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
dependencies:
- eslint: 8.37.0
+ eslint: 8.39.0
eslint-visitor-keys: 3.4.0
dev: true
@@ -4031,8 +3788,8 @@ packages:
- supports-color
dev: true
- /@eslint/js@8.37.0:
- resolution: {integrity: sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A==}
+ /@eslint/js@8.39.0:
+ resolution: {integrity: sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
@@ -4089,10 +3846,10 @@ packages:
resolution: {integrity: sha512-KAfcLa+CnknwVi5fWogrLXgidLic+GXnLjijXdpl8pvkvbXU5BGa37iZO9FGvsh9ZL4y+oFi5cbHBm5UOG+dmQ==}
dev: false
- /@fastify/fast-json-stringify-compiler@4.2.0:
- resolution: {integrity: sha512-ypZynRvXA3dibfPykQN3RB5wBdEUgSGgny8Qc6k163wYPLD4mEGEDkACp+00YmqkGvIm8D/xYoHajwyEdWD/eg==}
+ /@fastify/fast-json-stringify-compiler@4.3.0:
+ resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==}
dependencies:
- fast-json-stringify: 5.5.0
+ fast-json-stringify: 5.7.0
dev: false
/@fastify/http-proxy@9.0.0:
@@ -4106,8 +3863,8 @@ packages:
- utf-8-validate
dev: false
- /@fastify/multipart@7.5.0:
- resolution: {integrity: sha512-cAQzz+vVRaNomTppActeqA3xZstF18WK9gjwCMAkoU6xKFljsnKewdGw2RkcYAaiWgFzGBQUD8owSKPFgzhJAA==}
+ /@fastify/multipart@7.6.0:
+ resolution: {integrity: sha512-mQoz0SMxk1WAYLYvQypoj3GYhCbmhnFl2LdleTzKQ4HgPfhVcMT014uln2wgzRh2y+gL74b/+j0mMVgvOKpqpQ==}
dependencies:
'@fastify/busboy': 1.1.0
'@fastify/deepmerge': 1.3.0
@@ -4141,8 +3898,8 @@ packages:
mime: 3.0.0
dev: false
- /@fastify/static@6.10.0:
- resolution: {integrity: sha512-TGruNm6ZabkQz2oRNoarPnY2BvS9i9DNf8Nn1aDcZp+WjOQRPCq0Wy2ko78yGB5JHytdCWoHpprc128QtLl8hw==}
+ /@fastify/static@6.10.1:
+ resolution: {integrity: sha512-DNnG+5QenQcTQw37qk0/191STThnN6SbU+2XMpWtpYR3gQUfUvMax14jTT/jqNINNbCkQJaKMnPtpFPKo4/68g==}
dependencies:
'@fastify/accept-negotiator': 1.0.0
'@fastify/send': 2.0.1
@@ -4218,7 +3975,7 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/types': 29.5.0
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
chalk: 4.1.2
jest-message-util: 29.5.0
jest-util: 29.5.0
@@ -4239,14 +3996,14 @@ packages:
'@jest/test-result': 29.5.0
'@jest/transform': 29.5.0
'@jest/types': 29.5.0
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
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.5.0
- jest-config: 29.5.0(@types/node@18.15.11)
+ jest-config: 29.5.0(@types/node@18.16.3)
jest-haste-map: 29.5.0
jest-message-util: 29.5.0
jest-regex-util: 29.4.3
@@ -4280,7 +4037,7 @@ packages:
dependencies:
'@jest/fake-timers': 29.5.0
'@jest/types': 29.5.0
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
jest-mock: 29.5.0
dev: true
@@ -4307,7 +4064,7 @@ packages:
dependencies:
'@jest/types': 29.5.0
'@sinonjs/fake-timers': 10.0.2
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
jest-message-util: 29.5.0
jest-mock: 29.5.0
jest-util: 29.5.0
@@ -4340,7 +4097,7 @@ packages:
'@jest/transform': 29.5.0
'@jest/types': 29.5.0
'@jridgewell/trace-mapping': 0.3.17
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
chalk: 4.1.2
collect-v8-coverage: 1.0.1
exit: 0.1.2
@@ -4434,7 +4191,7 @@ packages:
dependencies:
'@types/istanbul-lib-coverage': 2.0.4
'@types/istanbul-reports': 3.0.1
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
'@types/yargs': 16.0.5
chalk: 4.1.2
dev: true
@@ -4446,12 +4203,12 @@ packages:
'@jest/schemas': 29.4.3
'@types/istanbul-lib-coverage': 2.0.4
'@types/istanbul-reports': 3.0.1
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
'@types/yargs': 17.0.19
chalk: 4.1.2
dev: true
- /@joshwooding/vite-plugin-react-docgen-typescript@0.2.1(typescript@5.0.3)(vite@4.2.1):
+ /@joshwooding/vite-plugin-react-docgen-typescript@0.2.1(typescript@5.0.4)(vite@4.3.4):
resolution: {integrity: sha512-ou4ZJSXMMWHqGS4g8uNRbC5TiTWxAgQZiVucoUrOCWuPrTbkpJbmVyIi9jU72SBry7gQtuMEDp4YR8EEXAg7VQ==}
peerDependencies:
typescript: '>= 4.3.x'
@@ -4463,9 +4220,9 @@ packages:
glob: 7.2.3
glob-promise: 4.2.2(glob@7.2.3)
magic-string: 0.27.0
- react-docgen-typescript: 2.2.2(typescript@5.0.3)
- typescript: 5.0.3
- vite: 4.2.1(@types/node@18.15.11)(sass@1.60.0)
+ react-docgen-typescript: 2.2.2(typescript@5.0.4)
+ typescript: 5.0.4
+ vite: 4.3.4(@types/node@18.16.3)(sass@1.62.1)
dev: true
/@jridgewell/gen-mapping@0.1.1:
@@ -4528,6 +4285,7 @@ packages:
/@mapbox/node-pre-gyp@1.0.9:
resolution: {integrity: sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==}
+ hasBin: true
dependencies:
detect-libc: 2.0.1
https-proxy-agent: 5.0.1
@@ -4536,7 +4294,7 @@ packages:
nopt: 5.0.0
npmlog: 5.0.1
rimraf: 3.0.2
- semver: 7.3.8
+ semver: 7.5.0
tar: 6.1.13
transitivePeerDependencies:
- encoding
@@ -4554,24 +4312,24 @@ packages:
react: 18.2.0
dev: true
- /@microsoft/api-extractor-model@7.26.4(@types/node@18.15.11):
- resolution: {integrity: sha512-PDCgCzXDo+SLY5bsfl4bS7hxaeEtnXj7XtuzEE+BtALp7B5mK/NrS2kHWU69pohgsRmEALycQdaQPXoyT2i5MQ==}
+ /@microsoft/api-extractor-model@7.26.7(@types/node@18.16.3):
+ resolution: {integrity: sha512-rx3Tq632VG3ddR74kPuPbv1qmUgO2IuCvn1z16hbNWNS5RhnTQqNPWIm7NVoi6lCh2E7uxzfmdnWXIXiJhM5IQ==}
dependencies:
'@microsoft/tsdoc': 0.14.2
'@microsoft/tsdoc-config': 0.16.2
- '@rushstack/node-core-library': 3.55.2(@types/node@18.15.11)
+ '@rushstack/node-core-library': 3.58.0(@types/node@18.16.3)
transitivePeerDependencies:
- '@types/node'
dev: true
- /@microsoft/api-extractor@7.34.4(@types/node@18.15.11):
- resolution: {integrity: sha512-HOdcci2nT40ejhwPC3Xja9G+WSJmWhCUKKryRfQYsmE9cD+pxmBaKBKCbuS9jUcl6bLLb4Gz+h7xEN5r0QiXnQ==}
+ /@microsoft/api-extractor@7.34.7(@types/node@18.16.3):
+ resolution: {integrity: sha512-8CrrYyOBWqc4XFviR1KSuHSlmJjlnC5CVpPkcFB8HXiLABUVaVWFTsOzJIwUU6z8mc4BZPZ8tSGAg/mwFDU31Q==}
hasBin: true
dependencies:
- '@microsoft/api-extractor-model': 7.26.4(@types/node@18.15.11)
+ '@microsoft/api-extractor-model': 7.26.7(@types/node@18.16.3)
'@microsoft/tsdoc': 0.14.2
'@microsoft/tsdoc-config': 0.16.2
- '@rushstack/node-core-library': 3.55.2(@types/node@18.15.11)
+ '@rushstack/node-core-library': 3.58.0(@types/node@18.16.3)
'@rushstack/rig-package': 0.3.18
'@rushstack/ts-command-line': 4.13.2
colors: 1.2.5
@@ -4685,7 +4443,7 @@ packages:
tar-fs: 2.1.1
dev: true
- /@nestjs/common@9.4.0(reflect-metadata@0.1.13)(rxjs@7.8.0):
+ /@nestjs/common@9.4.0(reflect-metadata@0.1.13)(rxjs@7.8.1):
resolution: {integrity: sha512-RUcVAQsEF4WPrmzFXEOUfZnPwrLTe1UVlzXTlSyfqfqbdWDPKDGlIPVelBLfc5/+RRUQ0I5iE4+CQvpCmkqldw==}
peerDependencies:
cache-manager: <=5
@@ -4703,12 +4461,12 @@ packages:
dependencies:
iterare: 1.2.1
reflect-metadata: 0.1.13
- rxjs: 7.8.0
+ rxjs: 7.8.1
tslib: 2.5.0
uid: 2.0.2
dev: false
- /@nestjs/core@9.4.0(@nestjs/common@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0):
+ /@nestjs/core@9.4.0(@nestjs/common@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.1):
resolution: {integrity: sha512-yTLryCgFD0462wPe4HIzhyTcDgibt8Stfwb5YzcX7Ma0NM4m8uBIpcPG109KBubp8ZmV85e5mw4rl20qLQQVsQ==}
requiresBuild: true
peerDependencies:
@@ -4726,13 +4484,13 @@ packages:
'@nestjs/websockets':
optional: true
dependencies:
- '@nestjs/common': 9.4.0(reflect-metadata@0.1.13)(rxjs@7.8.0)
+ '@nestjs/common': 9.4.0(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nuxtjs/opencollective': 0.3.2
fast-safe-stringify: 2.1.1
iterare: 1.2.1
path-to-regexp: 3.2.0
reflect-metadata: 0.1.13
- rxjs: 7.8.0
+ rxjs: 7.8.1
tslib: 2.5.0
uid: 2.0.2
transitivePeerDependencies:
@@ -4752,8 +4510,8 @@ packages:
'@nestjs/platform-express':
optional: true
dependencies:
- '@nestjs/common': 9.4.0(reflect-metadata@0.1.13)(rxjs@7.8.0)
- '@nestjs/core': 9.4.0(@nestjs/common@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.0)
+ '@nestjs/common': 9.4.0(reflect-metadata@0.1.13)(rxjs@7.8.1)
+ '@nestjs/core': 9.4.0(@nestjs/common@9.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.1)
tslib: 2.5.0
dev: false
@@ -4780,7 +4538,7 @@ packages:
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
dependencies:
'@gar/promisify': 1.1.3
- semver: 7.3.8
+ semver: 7.5.0
dev: false
/@npmcli/move-file@2.0.1:
@@ -4883,8 +4641,8 @@ packages:
'@redis/client': 1.4.2
dev: true
- /@rollup/plugin-alias@4.0.3(rollup@3.20.2):
- resolution: {integrity: sha512-ZuDWE1q4PQDhvm/zc5Prun8sBpLJy41DMptYrS6MhAy9s9kL/doN1613BWfEchGVfKxzliJ3BjbOPizXX38DbQ==}
+ /@rollup/plugin-alias@5.0.0(rollup@3.21.3):
+ resolution: {integrity: sha512-l9hY5chSCjuFRPsnRm16twWBiSApl2uYFLsepQYwtBuAxNMQ/1dJqADld40P0Jkqm65GRTLy/AC6hnpVebtLsA==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0
@@ -4892,11 +4650,11 @@ packages:
rollup:
optional: true
dependencies:
- rollup: 3.20.2
+ rollup: 3.21.3
slash: 4.0.0
dev: false
- /@rollup/plugin-json@6.0.0(rollup@3.20.2):
+ /@rollup/plugin-json@6.0.0(rollup@3.21.3):
resolution: {integrity: sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==}
engines: {node: '>=14.0.0'}
peerDependencies:
@@ -4905,8 +4663,22 @@ packages:
rollup:
optional: true
dependencies:
- '@rollup/pluginutils': 5.0.2(rollup@3.20.2)
- rollup: 3.20.2
+ '@rollup/pluginutils': 5.0.2(rollup@3.21.3)
+ rollup: 3.21.3
+ dev: false
+
+ /@rollup/plugin-replace@5.0.2(rollup@3.21.3):
+ resolution: {integrity: sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+ dependencies:
+ '@rollup/pluginutils': 5.0.2(rollup@3.21.3)
+ magic-string: 0.27.0
+ rollup: 3.21.3
dev: false
/@rollup/pluginutils@4.2.1:
@@ -4917,7 +4689,7 @@ packages:
picomatch: 2.3.1
dev: true
- /@rollup/pluginutils@5.0.2(rollup@3.20.2):
+ /@rollup/pluginutils@5.0.2(rollup@3.21.3):
resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==}
engines: {node: '>=14.0.0'}
peerDependencies:
@@ -4926,21 +4698,21 @@ packages:
rollup:
optional: true
dependencies:
- '@types/estree': 1.0.0
+ '@types/estree': 1.0.1
estree-walker: 2.0.2
picomatch: 2.3.1
- rollup: 3.20.2
+ rollup: 3.21.3
dev: false
- /@rushstack/node-core-library@3.55.2(@types/node@18.15.11):
- resolution: {integrity: sha512-SaLe/x/Q/uBVdNFK5V1xXvsVps0y7h1sN7aSJllQyFbugyOaxhNRF25bwEDnicARNEjJw0pk0lYnJQ9Kr6ev0A==}
+ /@rushstack/node-core-library@3.58.0(@types/node@18.16.3):
+ resolution: {integrity: sha512-DHAZ3LTOEq2/EGURznpTJDnB3SNE2CKMDXuviQ6afhru6RykE3QoqXkeyjbpLb5ib5cpIRCPE/wykNe0xmQj3w==}
peerDependencies:
'@types/node': '*'
peerDependenciesMeta:
'@types/node':
optional: true
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
colors: 1.2.5
fs-extra: 7.0.1
import-lazy: 4.0.0
@@ -4991,6 +4763,7 @@ packages:
/@sindresorhus/is@4.6.0:
resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
engines: {node: '>=10'}
+ dev: false
/@sindresorhus/is@5.3.0:
resolution: {integrity: sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==}
@@ -5051,9 +4824,9 @@ packages:
lodash.values: 4.3.0
object-hash: 3.0.0
packageurl-js: 1.0.1
- semver: 7.3.8
+ semver: 7.5.0
tslib: 2.5.0
- dev: true
+ dev: false
/@snyk/graphlib@2.1.9-patch.3:
resolution: {integrity: sha512-bBY9b9ulfLj0v2Eer0yFYa3syVeIxVKl2EpxSrsVeT4mjA0CltZyHsF0JjoaGXP27nItTdJS5uVsj1NA+3aE+Q==}
@@ -5073,14 +4846,14 @@ packages:
lodash.transform: 4.6.0
lodash.union: 4.6.0
lodash.values: 4.3.0
- dev: true
+ dev: false
/@sqltools/formatter@1.2.5:
resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
dev: false
- /@storybook/addon-actions@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-rcj39u9MrmzsrDWYt1zsoVxrogZ1Amrv9xkEofEY/QKUr2R3xpHhTALveY9BKIlG1GoE8zLlLoP2k4nz3sNNwQ==}
+ /@storybook/addon-actions@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-WxsnSjAvdf6NhUfTqcwV+FJmsJV56gh2cY4QnGfqfwO5zoBWTUYnhz57TgxSMhJY0kspyX9Q1Kc//r1d5lt1qA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5090,14 +4863,14 @@ packages:
react-dom:
optional: true
dependencies:
- '@storybook/client-logger': 7.0.2
- '@storybook/components': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/core-events': 7.0.2
+ '@storybook/client-logger': 7.0.7
+ '@storybook/components': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/core-events': 7.0.7
'@storybook/global': 5.0.0
- '@storybook/manager-api': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/preview-api': 7.0.2
- '@storybook/theming': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/types': 7.0.2
+ '@storybook/manager-api': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/preview-api': 7.0.7
+ '@storybook/theming': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/types': 7.0.7
dequal: 2.0.3
lodash: 4.17.21
polished: 4.2.2
@@ -5107,11 +4880,11 @@ packages:
react-inspector: 6.0.1(react@18.2.0)
telejson: 7.0.4
ts-dedent: 2.2.0
- uuid-browser: 3.1.0
+ uuid: 9.0.0
dev: true
- /@storybook/addon-backgrounds@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-yRNHQ4PPRJ+HIORQPhDGxn5xolw1xW0ByQZoNRpMD+AMEyfUNFdWbCsRQAOWjNhawxVMHM7EeA2Exrb41zhEjA==}
+ /@storybook/addon-backgrounds@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-DhT32K1+ti7MXY9oqt36b9jlg7iY68IP0ZQbR3gjShcsIXZpFqh18TQo0vwDY1ldqnBvkTk6Jd5vcxA8tfyshw==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5121,22 +4894,22 @@ packages:
react-dom:
optional: true
dependencies:
- '@storybook/client-logger': 7.0.2
- '@storybook/components': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/core-events': 7.0.2
+ '@storybook/client-logger': 7.0.7
+ '@storybook/components': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/core-events': 7.0.7
'@storybook/global': 5.0.0
- '@storybook/manager-api': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/preview-api': 7.0.2
- '@storybook/theming': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/types': 7.0.2
+ '@storybook/manager-api': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/preview-api': 7.0.7
+ '@storybook/theming': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/types': 7.0.7
memoizerific: 1.11.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
ts-dedent: 2.2.0
dev: true
- /@storybook/addon-controls@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-dMpRtj5cmfC9vEMve5ncvbWCEC+WD9YuzJ+grdc48E/Hd//p+O2FE6klSkrz5FAjrc+rHINixdyssekpEL6nYQ==}
+ /@storybook/addon-controls@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-/QEzleKoWRQ3i7KB32QvqDGcGMw4kG2BxEf0d+ymxd2SjoeL6kX2eHE0b4OxFPXiWUyTfXBFwmcI2Re3fRUJnQ==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5146,15 +4919,15 @@ packages:
react-dom:
optional: true
dependencies:
- '@storybook/blocks': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/client-logger': 7.0.2
- '@storybook/components': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/core-common': 7.0.2
- '@storybook/manager-api': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/node-logger': 7.0.2
- '@storybook/preview-api': 7.0.2
- '@storybook/theming': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/types': 7.0.2
+ '@storybook/blocks': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/client-logger': 7.0.7
+ '@storybook/components': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/core-common': 7.0.7
+ '@storybook/manager-api': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/node-logger': 7.0.7
+ '@storybook/preview-api': 7.0.7
+ '@storybook/theming': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/types': 7.0.7
lodash: 4.17.21
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
@@ -5163,33 +4936,29 @@ packages:
- supports-color
dev: true
- /@storybook/addon-docs@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-q3rDWoZEym6Lkmhqc/HBNfLDAmTY8l0WINGUZo/nF98eP5iu4B7Nk7V6BRGYGQt6Y6ZyIQ8WKH0e/eJww2zIog==}
+ /@storybook/addon-docs@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-5PT7aiTD6QPH+4CZLcv4PiUgWucD9JNGHVMRbQMEyFW6qbs87dHmu1m1uXIvx3BF5h3mTo4FHNAf8IQIq5HH9w==}
peerDependencies:
- '@storybook/mdx1-csf': '>=1.0.0-0'
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
- peerDependenciesMeta:
- '@storybook/mdx1-csf':
- optional: true
dependencies:
'@babel/core': 7.21.3
'@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.21.3)
'@jest/transform': 29.5.0
'@mdx-js/react': 2.3.0(react@18.2.0)
- '@storybook/blocks': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/client-logger': 7.0.2
- '@storybook/components': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/csf-plugin': 7.0.2
- '@storybook/csf-tools': 7.0.2
+ '@storybook/blocks': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/client-logger': 7.0.7
+ '@storybook/components': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/csf-plugin': 7.0.7
+ '@storybook/csf-tools': 7.0.7
'@storybook/global': 5.0.0
'@storybook/mdx2-csf': 1.0.0
- '@storybook/node-logger': 7.0.2
- '@storybook/postinstall': 7.0.2
- '@storybook/preview-api': 7.0.2
- '@storybook/react-dom-shim': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/theming': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/types': 7.0.2
+ '@storybook/node-logger': 7.0.7
+ '@storybook/postinstall': 7.0.7
+ '@storybook/preview-api': 7.0.7
+ '@storybook/react-dom-shim': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/theming': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/types': 7.0.7
fs-extra: 11.1.0
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
@@ -5200,43 +4969,42 @@ packages:
- supports-color
dev: true
- /@storybook/addon-essentials@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-LAsWsXa/Pp2B4Ve2WVgc990FtsiHpFDRsq7S3V7xRrZP8DYRbtJIVdszPMDS5uKC+yzbswFEXz08lqbGvq8zgQ==}
+ /@storybook/addon-essentials@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-uNx0BvN1XP7cNnk/L4oiFQlEB/KABqOeIyI8/mhfIyTvvwo9uAYIQAyiwWuz9MFmofCNm7CgLNOUaEwNDkM4CA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
- '@storybook/addon-actions': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/addon-backgrounds': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/addon-controls': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/addon-docs': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/addon-highlight': 7.0.2
- '@storybook/addon-measure': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/addon-outline': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/addon-toolbars': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/addon-viewport': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/core-common': 7.0.2
- '@storybook/manager-api': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/node-logger': 7.0.2
- '@storybook/preview-api': 7.0.2
+ '@storybook/addon-actions': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/addon-backgrounds': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/addon-controls': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/addon-docs': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/addon-highlight': 7.0.7
+ '@storybook/addon-measure': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/addon-outline': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/addon-toolbars': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/addon-viewport': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/core-common': 7.0.7
+ '@storybook/manager-api': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/node-logger': 7.0.7
+ '@storybook/preview-api': 7.0.7
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
ts-dedent: 2.2.0
transitivePeerDependencies:
- - '@storybook/mdx1-csf'
- supports-color
dev: true
- /@storybook/addon-highlight@7.0.2:
- resolution: {integrity: sha512-9BkL1OOanguuy73S6nLK0isUb045tOkFONd/PQldOJ0PV3agCvKxKHyzlBz7Hsba8KZhY5jQs+nVW2NiREyGYg==}
+ /@storybook/addon-highlight@7.0.7:
+ resolution: {integrity: sha512-expme2GzzCXX7/lL7UjCDi1Tfj+4LeNsAdWiurVLH7glK7yKPPeXXkIldbLP/XjJv4NKlqCwnNRHQx0vDLlE6g==}
dependencies:
- '@storybook/core-events': 7.0.2
+ '@storybook/core-events': 7.0.7
'@storybook/global': 5.0.0
- '@storybook/preview-api': 7.0.2
+ '@storybook/preview-api': 7.0.7
dev: true
- /@storybook/addon-interactions@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-vPWnyGND4s9nVp+U21N/jE00dCRsHcKU68SoL4OiIZioTTRbLvrTG9eAdBkZXsVPpFHq8gndma3nXfplOSSckg==}
+ /@storybook/addon-interactions@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-jBl6O5sSbix0X1G9dFuWvvu4qefgLP9dAB/utVdDadZxlbPfa5B2C2q2YIqjcKZoX8DS8Fh8SUhlX1mdW5tu5w==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5246,16 +5014,16 @@ packages:
react-dom:
optional: true
dependencies:
- '@storybook/client-logger': 7.0.2
- '@storybook/components': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/core-common': 7.0.2
- '@storybook/core-events': 7.0.2
+ '@storybook/client-logger': 7.0.7
+ '@storybook/components': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/core-common': 7.0.7
+ '@storybook/core-events': 7.0.7
'@storybook/global': 5.0.0
- '@storybook/instrumenter': 7.0.2
- '@storybook/manager-api': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/preview-api': 7.0.2
- '@storybook/theming': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/types': 7.0.2
+ '@storybook/instrumenter': 7.0.7
+ '@storybook/manager-api': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/preview-api': 7.0.7
+ '@storybook/theming': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/types': 7.0.7
jest-mock: 27.5.1
polished: 4.2.2
react: 18.2.0
@@ -5265,8 +5033,8 @@ packages:
- supports-color
dev: true
- /@storybook/addon-links@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-lPtfy2MqrcI9YjupBM2eRKGPdFKVPCz7WgO/JQQakGugORJTEGCyJrNJNtWY9jDenv8ynLZ40OxtPBZi54Sr6Q==}
+ /@storybook/addon-links@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-DEjDxjHb3mT8Sdnx4In5Ev9gJ/XdjlHOq4iuy0wnMyrCV4wnzTQnIeSCx8nkrXFb314zc33JPnCcrb5pQoD5GQ==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5276,22 +5044,22 @@ packages:
react-dom:
optional: true
dependencies:
- '@storybook/client-logger': 7.0.2
- '@storybook/core-events': 7.0.2
+ '@storybook/client-logger': 7.0.7
+ '@storybook/core-events': 7.0.7
'@storybook/csf': 0.1.0
'@storybook/global': 5.0.0
- '@storybook/manager-api': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/preview-api': 7.0.2
- '@storybook/router': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/types': 7.0.2
+ '@storybook/manager-api': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/preview-api': 7.0.7
+ '@storybook/router': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/types': 7.0.7
prop-types: 15.8.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
ts-dedent: 2.2.0
dev: true
- /@storybook/addon-measure@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-cf/d5MXpHAjyUiDIVfc8pLn79CPHgnryDmNNlSiP2zEFKcivrRWiu8Rmrad8pGqLkuAh+PXLKCGn9uiqDvg7QQ==}
+ /@storybook/addon-measure@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-lb4wEIvIVF+ePx1sC+n9rDI0+49sRa6MWbcvZ+BhbAoCeGcX7uACQFdW6HyXolmBuZASsTnzVQ4KqzzvY1dSWw==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5301,19 +5069,19 @@ packages:
react-dom:
optional: true
dependencies:
- '@storybook/client-logger': 7.0.2
- '@storybook/components': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/core-events': 7.0.2
+ '@storybook/client-logger': 7.0.7
+ '@storybook/components': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/core-events': 7.0.7
'@storybook/global': 5.0.0
- '@storybook/manager-api': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/preview-api': 7.0.2
- '@storybook/types': 7.0.2
+ '@storybook/manager-api': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/preview-api': 7.0.7
+ '@storybook/types': 7.0.7
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: true
- /@storybook/addon-outline@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-thVISO4NM22xlETisBvAPvz2yFD3qLGOjgzBmj8l8r9Rv0IEdwdPrwm5j0WTv8OtbhC4A8lPpvMsn5FhY5mDXg==}
+ /@storybook/addon-outline@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-AxbNZ4N1fXBTeMYM9tFudfW+Gzq7UikCjPxn5ax3Pde+zZjaEMppUxv5EMz4g5GIJupLYRmKH5pN0YcYoRLY6w==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5323,20 +5091,20 @@ packages:
react-dom:
optional: true
dependencies:
- '@storybook/client-logger': 7.0.2
- '@storybook/components': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/core-events': 7.0.2
+ '@storybook/client-logger': 7.0.7
+ '@storybook/components': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/core-events': 7.0.7
'@storybook/global': 5.0.0
- '@storybook/manager-api': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/preview-api': 7.0.2
- '@storybook/types': 7.0.2
+ '@storybook/manager-api': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/preview-api': 7.0.7
+ '@storybook/types': 7.0.7
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
ts-dedent: 2.2.0
dev: true
- /@storybook/addon-storysource@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-0F47HIc97bNKSDTNnUy0M4ch/tNziWwnw+EDRCNoE6kOMJ9uF6caiTk4QCahCRoA/iNbBWCn91mnU4ZRp22Wiw==}
+ /@storybook/addon-storysource@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-gRdnWN8H5BhQF3rkcfjEIzEsZ/kdlgjiA6YA3GRKlcRCkzWHHXtHDdT3BSUFPb/8mT8DftFZNP2PKWeKd1rZvQ==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5346,13 +5114,13 @@ packages:
react-dom:
optional: true
dependencies:
- '@storybook/client-logger': 7.0.2
- '@storybook/components': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/manager-api': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/preview-api': 7.0.2
- '@storybook/router': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/source-loader': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/theming': 7.0.2(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/client-logger': 7.0.7
+ '@storybook/components': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/manager-api': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/preview-api': 7.0.7
+ '@storybook/router': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/source-loader': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/theming': 7.0.7(react-dom@18.2.0)(react@18.2.0)
estraverse: 5.3.0
prop-types: 15.8.1
react: 18.2.0
@@ -5360,8 +5128,8 @@ packages:
react-syntax-highlighter: 15.5.0(react@18.2.0)
dev: true
- /@storybook/addon-toolbars@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-tAxZ2+nUYsJdT1sx3BrmoMAZFM19+OzWJY6qSnbEq5zoRgvGZaXGR6tLMKydDoHQBU9Ta9YHGo7N7u7h1C23yg==}
+ /@storybook/addon-toolbars@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-/NkYHhU1VAz5lXjWuV8+ADWB84HzktvZv4jfiKX7Zzu6JVzrBu7FotQSWh3pDqqVwCB50RClUGtcHmSSac9CAQ==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5371,17 +5139,17 @@ packages:
react-dom:
optional: true
dependencies:
- '@storybook/client-logger': 7.0.2
- '@storybook/components': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/manager-api': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/preview-api': 7.0.2
- '@storybook/theming': 7.0.2(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/client-logger': 7.0.7
+ '@storybook/components': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/manager-api': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/preview-api': 7.0.7
+ '@storybook/theming': 7.0.7(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: true
- /@storybook/addon-viewport@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-TaHJWIIazPM/TerRbka9RqjMPNpwaRsGRdVRBtVoVosy1FzsEjAdQSO7RBMe4G03m5CacSqdsDiJCblI2AXaew==}
+ /@storybook/addon-viewport@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-znqhd8JFEFoXcAdwYhz1CwrCpVAzhuSyUVBUNDsDs+mgBEfGth4D4abIdWWGcfP6+CmI5ebFHtk443cExZebag==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5391,49 +5159,49 @@ packages:
react-dom:
optional: true
dependencies:
- '@storybook/client-logger': 7.0.2
- '@storybook/components': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/core-events': 7.0.2
+ '@storybook/client-logger': 7.0.7
+ '@storybook/components': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/core-events': 7.0.7
'@storybook/global': 5.0.0
- '@storybook/manager-api': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/preview-api': 7.0.2
- '@storybook/theming': 7.0.2(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/manager-api': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/preview-api': 7.0.7
+ '@storybook/theming': 7.0.7(react-dom@18.2.0)(react@18.2.0)
memoizerific: 1.11.3
prop-types: 15.8.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: true
- /@storybook/addons@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-2+amBRcmJ5kD2ZNeftiyp9Or9Kd7oVjwycZZX5SBB4EFQvRNKyM0w4GVgwbeJAdx5uuw7tJFtQgnakb39mxAgA==}
+ /@storybook/addons@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-it8NWXsdm3dhjc237d9jj7dGJf6eHDfuDv12nirV64J1dzWrnW+lONeZMPMgxxdLlgYfxH52fLgjcw/dAC/E+Q==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
- '@storybook/manager-api': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/preview-api': 7.0.2
- '@storybook/types': 7.0.2
+ '@storybook/manager-api': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/preview-api': 7.0.7
+ '@storybook/types': 7.0.7
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: true
- /@storybook/blocks@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-JzHmU8jZLzeQ6bunzci8j/2Ji18GBTyhrPFLk5RjEbMNGWpGjvER/yR127tZOdbPguVNr4iVbRfGzd1wGHlrzA==}
+ /@storybook/blocks@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-ehR0hAFWNHHqmrmbwYPKhLpgbIBKtyMbeoGClTRSnrVBGONciYJdmxegkCTReUklCY+HBJjtlwNowT+7+5sSaw==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
- '@storybook/channels': 7.0.2
- '@storybook/client-logger': 7.0.2
- '@storybook/components': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/core-events': 7.0.2
+ '@storybook/channels': 7.0.7
+ '@storybook/client-logger': 7.0.7
+ '@storybook/components': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/core-events': 7.0.7
'@storybook/csf': 0.1.0
- '@storybook/docs-tools': 7.0.2
+ '@storybook/docs-tools': 7.0.7
'@storybook/global': 5.0.0
- '@storybook/manager-api': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/preview-api': 7.0.2
- '@storybook/theming': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/types': 7.0.2
+ '@storybook/manager-api': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/preview-api': 7.0.7
+ '@storybook/theming': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/types': 7.0.7
'@types/lodash': 4.14.191
color-convert: 2.0.1
dequal: 2.0.3
@@ -5451,19 +5219,19 @@ packages:
- supports-color
dev: true
- /@storybook/builder-manager@7.0.2:
- resolution: {integrity: sha512-Oej/n8D7eaWgmWF7nN2hXLRM53lcYOdh6umSN8Mh/LcYUfxB+dvUBFzUjoLE0xjhW6xRinrKrENT5LcP/f/HBQ==}
+ /@storybook/builder-manager@7.0.7:
+ resolution: {integrity: sha512-VI/0iEjAlzQDt1yKu8GXugNIz7t46IHIKgMNmltQ05KPypMgInUoMmbfP5AYOVddjLdSqjMLO7EK58pBLOInpw==}
dependencies:
'@fal-works/esbuild-plugin-global-externals': 2.1.2
- '@storybook/core-common': 7.0.2
- '@storybook/manager': 7.0.2
- '@storybook/node-logger': 7.0.2
+ '@storybook/core-common': 7.0.7
+ '@storybook/manager': 7.0.7
+ '@storybook/node-logger': 7.0.7
'@types/ejs': 3.1.2
'@types/find-cache-dir': 3.2.1
- '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.17.15)
+ '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.17.18)
browser-assert: 1.2.1
ejs: 3.1.8
- esbuild: 0.17.15
+ esbuild: 0.17.18
esbuild-plugin-alias: 0.2.1
express: 4.18.2
find-cache-dir: 3.3.2
@@ -5474,34 +5242,31 @@ packages:
- supports-color
dev: true
- /@storybook/builder-vite@7.0.2(typescript@5.0.3)(vite@4.2.1):
- resolution: {integrity: sha512-G6CD2Gf2zwzRslvNvqgz4FeADVEA9XA4Mw6+NM6Twc+Wy/Ah482dvHS9ApSgirtGyBKjOfdHn1xQT4Z+kzbJnw==}
+ /@storybook/builder-vite@7.0.7(typescript@5.0.4)(vite@4.3.4):
+ resolution: {integrity: sha512-2wL6fsFWzij+R155urOLc7EjZtlVWf4FLfaSlLGAuZwRQU40N04YdMaHMp9tjd9Vdr5fxEDwTB51PnVWJMlsEw==}
peerDependencies:
'@preact/preset-vite': '*'
- '@storybook/mdx1-csf': '>=1.0.0-next.1'
typescript: '>= 4.3.x'
vite: ^3.0.0 || ^4.0.0
vite-plugin-glimmerx: '*'
peerDependenciesMeta:
'@preact/preset-vite':
optional: true
- '@storybook/mdx1-csf':
- optional: true
typescript:
optional: true
vite-plugin-glimmerx:
optional: true
dependencies:
- '@storybook/channel-postmessage': 7.0.2
- '@storybook/channel-websocket': 7.0.2
- '@storybook/client-logger': 7.0.2
- '@storybook/core-common': 7.0.2
- '@storybook/csf-plugin': 7.0.2
+ '@storybook/channel-postmessage': 7.0.7
+ '@storybook/channel-websocket': 7.0.7
+ '@storybook/client-logger': 7.0.7
+ '@storybook/core-common': 7.0.7
+ '@storybook/csf-plugin': 7.0.7
'@storybook/mdx2-csf': 1.0.0
- '@storybook/node-logger': 7.0.2
- '@storybook/preview': 7.0.2
- '@storybook/preview-api': 7.0.2
- '@storybook/types': 7.0.2
+ '@storybook/node-logger': 7.0.7
+ '@storybook/preview': 7.0.7
+ '@storybook/preview-api': 7.0.7
+ '@storybook/types': 7.0.7
browser-assert: 1.2.1
es-module-lexer: 0.9.3
express: 4.18.2
@@ -5511,66 +5276,81 @@ packages:
magic-string: 0.27.0
remark-external-links: 8.0.0
remark-slug: 6.1.0
- rollup: 3.20.2
- typescript: 5.0.3
- vite: 4.2.1(@types/node@18.15.11)(sass@1.60.0)
+ rollup: 3.21.3
+ typescript: 5.0.4
+ vite: 4.3.4(@types/node@18.16.3)(sass@1.62.1)
transitivePeerDependencies:
- supports-color
dev: true
- /@storybook/channel-postmessage@7.0.0:
- resolution: {integrity: sha512-Sy3oHL/xDRjUiHnM0ncnkbOE5pK3O72MjOoiLJX4FCI90w03KM4+F/N0eU2cXl6yXHuCyI5eJisEzQxTNsaJiw==}
+ /@storybook/channel-postmessage@7.0.2:
+ resolution: {integrity: sha512-SZ/KqnZcx10W9hJbrzBKcP9dmgaeTaXugUhcgw1IkmjKWdsKazqFZCPwQWZZKAmhO4wYbyYOhkz3wfSIeB4mFw==}
dependencies:
- '@storybook/channels': 7.0.0
- '@storybook/client-logger': 7.0.0
- '@storybook/core-events': 7.0.0
+ '@storybook/channels': 7.0.2
+ '@storybook/client-logger': 7.0.2
+ '@storybook/core-events': 7.0.2
'@storybook/global': 5.0.0
qs: 6.11.1
telejson: 7.0.4
dev: true
- /@storybook/channel-postmessage@7.0.2:
- resolution: {integrity: sha512-SZ/KqnZcx10W9hJbrzBKcP9dmgaeTaXugUhcgw1IkmjKWdsKazqFZCPwQWZZKAmhO4wYbyYOhkz3wfSIeB4mFw==}
+ /@storybook/channel-postmessage@7.0.6:
+ resolution: {integrity: sha512-xBsh/+85GS4bJ08r7z1iRn26EI6hGmMgNpjpFztRigMhsq5SkD9FJb+Nh9bbaHm+yPOCqJcaHQ2aQpuJNT8dHA==}
dependencies:
- '@storybook/channels': 7.0.2
- '@storybook/client-logger': 7.0.2
- '@storybook/core-events': 7.0.2
+ '@storybook/channels': 7.0.6
+ '@storybook/client-logger': 7.0.6
+ '@storybook/core-events': 7.0.6
'@storybook/global': 5.0.0
qs: 6.11.1
telejson: 7.0.4
dev: true
- /@storybook/channel-websocket@7.0.2:
- resolution: {integrity: sha512-YU3lFId6Nsi75ddA+3qfbnLfNUPswboYyx+SALhaLuXqz7zqfzX4ezMgxeS/h0gRlUJ7nf2/yJ5qie/kZaizjw==}
+ /@storybook/channel-postmessage@7.0.7:
+ resolution: {integrity: sha512-XMtYfcaE0UoY/V7K1cTu9PcWETD4iyWb/Yswc4F9VrPw0Ui4UwGS1j4iaAu8DC06yyoJs4XvxYFBMlCQmKja6A==}
dependencies:
- '@storybook/channels': 7.0.2
- '@storybook/client-logger': 7.0.2
+ '@storybook/channels': 7.0.7
+ '@storybook/client-logger': 7.0.7
+ '@storybook/core-events': 7.0.7
'@storybook/global': 5.0.0
+ qs: 6.11.1
telejson: 7.0.4
dev: true
- /@storybook/channels@7.0.0:
- resolution: {integrity: sha512-adPIkvL4q37dGTWCpSzV8ETLdkxsg7BAgzeT9pustZJjRIZqAHGUAm7krDtGT7jbV4dU0Zw0VpUrnmyfxIkOKQ==}
+ /@storybook/channel-websocket@7.0.7:
+ resolution: {integrity: sha512-KDbLiQts4/dCow3qk5WJSPA6SlaX3iP9RhF0Fjj03hoG2TRskrvo+AkUiJr8gF6dpkPndfuCYUCRsO2Ml8B+AA==}
+ dependencies:
+ '@storybook/channels': 7.0.7
+ '@storybook/client-logger': 7.0.7
+ '@storybook/global': 5.0.0
+ telejson: 7.0.4
dev: true
/@storybook/channels@7.0.2:
resolution: {integrity: sha512-qkI8mFy9c8mxN2f01etayKhCaauL6RAsxRzbX1/pKj6UqhHWqqUbtHwymrv4hG5qDYjV1e9pd7ae5eNF8Kui0g==}
dev: true
- /@storybook/cli@7.0.2:
- resolution: {integrity: sha512-xMM2QdXNGg09wuXzAGroKrbsnaHSFPmtmefX1XGALhHuKVwxOoC2apWMpek6gY/9vh5EIRTog2Dvfd2BzNrT6Q==}
+ /@storybook/channels@7.0.6:
+ resolution: {integrity: sha512-+34cVmrXZ3lb1s5tDK+OWd5HLtEPSUMas0VKFJ0k9LBpFlVl9aiCZBJRvSYmWL7beauUfa+HSmJgjlD6228ChQ==}
+ dev: true
+
+ /@storybook/channels@7.0.7:
+ resolution: {integrity: sha512-Om4ovBLNw8pVrBu83MpOKgAuGO9Dpr1Coh2qp8t64WRPkejX1mxOY9IgH723//zH3igx8LCkf9rvBvcrsyaScQ==}
+ dev: true
+
+ /@storybook/cli@7.0.7:
+ resolution: {integrity: sha512-koTkWr7wlaHF14T5moRP/tYM44+Jf4GEzQ/rqx/Jfn7EbNlVUOibdLJj4JnseMGRc7ZP6tKYku2n+B8g7hJX4w==}
hasBin: true
dependencies:
'@babel/core': 7.21.3
- '@babel/preset-env': 7.20.2(@babel/core@7.21.3)
+ '@babel/preset-env': 7.21.4(@babel/core@7.21.3)
'@ndelangen/get-tarball': 3.0.7
- '@storybook/codemod': 7.0.2
- '@storybook/core-common': 7.0.2
- '@storybook/core-server': 7.0.2
- '@storybook/csf-tools': 7.0.2
- '@storybook/node-logger': 7.0.2
- '@storybook/telemetry': 7.0.2
- '@storybook/types': 7.0.2
+ '@storybook/codemod': 7.0.7
+ '@storybook/core-common': 7.0.7
+ '@storybook/core-server': 7.0.7
+ '@storybook/csf-tools': 7.0.7
+ '@storybook/node-logger': 7.0.7
+ '@storybook/telemetry': 7.0.7
+ '@storybook/types': 7.0.7
'@types/semver': 7.3.13
boxen: 5.1.2
chalk: 4.1.2
@@ -5586,13 +5366,13 @@ packages:
get-port: 5.1.1
giget: 1.1.2
globby: 11.1.0
- jscodeshift: 0.14.0(@babel/preset-env@7.20.2)
+ jscodeshift: 0.14.0(@babel/preset-env@7.21.4)
leven: 3.1.0
- prettier: 2.8.7
+ prettier: 2.8.8
prompts: 2.4.2
puppeteer-core: 2.1.1
read-pkg-up: 7.0.1
- semver: 7.3.8
+ semver: 7.5.0
shelljs: 0.8.5
simple-update-notifier: 1.1.0
strip-json-comments: 3.1.1
@@ -5606,49 +5386,55 @@ packages:
- utf-8-validate
dev: true
- /@storybook/client-logger@7.0.0:
- resolution: {integrity: sha512-wRZZiPta37DFc8SVZ8Q3ZqyTrs5qgO6bcCuVDRLQAcO0Oz4xKEVPEVfVVxSPZU/+p2ypqdBBCP2pdL/Jy86AJg==}
+ /@storybook/client-logger@7.0.2:
+ resolution: {integrity: sha512-rv7W2BhzIQHbFpUM5/CP/acS6T5lTmaxT0MbZ9n+9h++9QQU/cFOdkZgSUbLVAb1AeUGoLsk0HYzcqPpV35Xsw==}
dependencies:
'@storybook/global': 5.0.0
dev: true
- /@storybook/client-logger@7.0.2:
- resolution: {integrity: sha512-rv7W2BhzIQHbFpUM5/CP/acS6T5lTmaxT0MbZ9n+9h++9QQU/cFOdkZgSUbLVAb1AeUGoLsk0HYzcqPpV35Xsw==}
+ /@storybook/client-logger@7.0.6:
+ resolution: {integrity: sha512-TC/E5BBkY+WNldNw5p5Ffr9x4UgMe48GmC50ikBpQFk6og1B7XpFGMMbj40EBB0R5cpZkQNEVQh4OvunEygNzg==}
dependencies:
'@storybook/global': 5.0.0
dev: true
- /@storybook/codemod@7.0.2:
- resolution: {integrity: sha512-D9PdByxJlFiaDJcLkM+RN1DHCj4VfQIlSZkADOcNtI4o9H064oiMloWDGZiR1i1FCYMSXuWmW6tMsuCVebA+Nw==}
+ /@storybook/client-logger@7.0.7:
+ resolution: {integrity: sha512-EclHjDs5HwHMKB4X2orn/KKA0DTIDmp4AXAUJGRfxb5ArpKEb7tXLHsgrRBlaoz1j5LAwKTmEyZOONh9G3etjg==}
+ dependencies:
+ '@storybook/global': 5.0.0
+ dev: true
+
+ /@storybook/codemod@7.0.7:
+ resolution: {integrity: sha512-VlkDlkvfbzLe+NOmzs5zGrGb4jnaeAFZqpvIkXxevr6aGcOwgeelNv8gTmgBAcy+xbGW4Pp0XA2BlMweIvKEKA==}
dependencies:
'@babel/core': 7.21.3
'@babel/preset-env': 7.21.4(@babel/core@7.21.3)
- '@babel/types': 7.21.3
+ '@babel/types': 7.21.4
'@storybook/csf': 0.1.0
- '@storybook/csf-tools': 7.0.2
- '@storybook/node-logger': 7.0.2
- '@storybook/types': 7.0.2
+ '@storybook/csf-tools': 7.0.7
+ '@storybook/node-logger': 7.0.7
+ '@storybook/types': 7.0.7
cross-spawn: 7.0.3
globby: 11.1.0
jscodeshift: 0.14.0(@babel/preset-env@7.21.4)
lodash: 4.17.21
- prettier: 2.8.7
+ prettier: 2.8.8
recast: 0.23.1
transitivePeerDependencies:
- supports-color
dev: true
- /@storybook/components@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-Ee9pY6WlpricPUdYiyR0Ov8zgHkUt541yl1CZ6Ytaom2TA12cAnRjKewbLAgVPPhIE1LsMRhOPFYql0JMtnN4Q==}
+ /@storybook/components@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-6PLs9LMkBuhH/w4bSJ72tYgICMbOOIHuoB/fQdVlzhsdnXL2fM/v4RVW2N7v+Oz3lYXp/JtV8V9Ub8h6eDQKXg==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
- '@storybook/client-logger': 7.0.2
+ '@storybook/client-logger': 7.0.7
'@storybook/csf': 0.1.0
'@storybook/global': 5.0.0
- '@storybook/theming': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/types': 7.0.2
+ '@storybook/theming': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/types': 7.0.7
memoizerific: 1.11.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
@@ -5656,23 +5442,23 @@ packages:
util-deprecate: 1.0.2
dev: true
- /@storybook/core-client@7.0.2:
- resolution: {integrity: sha512-tr6Uv41YD2O0xiUrtgujiY1QxuznhbyUI0BRsSh49e8cx3QoW7FgPy7IVZHgb17DXKZ/wY/hgdyTTB87H6IbLA==}
+ /@storybook/core-client@7.0.7:
+ resolution: {integrity: sha512-eydcpR28qV3A3BwR5V6wsixoI1BRLA0SzFiwH/1ajrgX13inv+gV97gHv47Ojf/+YAZ3HqdVaUKFsUfMKwKieA==}
dependencies:
- '@storybook/client-logger': 7.0.2
- '@storybook/preview-api': 7.0.2
+ '@storybook/client-logger': 7.0.7
+ '@storybook/preview-api': 7.0.7
dev: true
- /@storybook/core-common@7.0.2:
- resolution: {integrity: sha512-DayFPTCj695tnEKLuDlogclBim8mzdrbj9U1xzFm23BUReheGSGdLl2zrb3mP1l9Zj4xJ/Ctst1KN9SFbW84vw==}
+ /@storybook/core-common@7.0.7:
+ resolution: {integrity: sha512-c8T24wex9bnCYdZVZFNX4VV+wfhrp47OLzVONZDqxMhq6G//Bgv5zH4Awcx5UfWf/05VcP7KGF1VKj8ebRyEEA==}
dependencies:
- '@storybook/node-logger': 7.0.2
- '@storybook/types': 7.0.2
+ '@storybook/node-logger': 7.0.7
+ '@storybook/types': 7.0.7
'@types/node': 16.18.16
'@types/pretty-hrtime': 1.0.1
chalk: 4.1.2
- esbuild: 0.17.15
- esbuild-register: 3.4.2(esbuild@0.17.15)
+ esbuild: 0.17.18
+ esbuild-register: 3.4.2(esbuild@0.17.18)
file-system-cache: 2.0.2
find-up: 5.0.0
fs-extra: 11.1.0
@@ -5689,31 +5475,35 @@ packages:
- supports-color
dev: true
- /@storybook/core-events@7.0.0:
- resolution: {integrity: sha512-pxzNmgEI1p90bHyAYABHDDtB2XM5pffq6CqIHboK6aSCux7Cdc16IjOYq6BJIhCKaaI+qQHaFLR4JfaFAsxwQQ==}
- dev: true
-
/@storybook/core-events@7.0.2:
resolution: {integrity: sha512-1DCHCwHRL3+rlvnVVc/BCfReP31XaT2WYgcLeGTmkX1E43Po1MkgcM7PnJPSaa9POvSqZ+6YLZv5Bs1SXbufow==}
dev: true
- /@storybook/core-server@7.0.2:
- resolution: {integrity: sha512-7ipGws8YffVaiwkc+D0+MfZc/Sy52aKenG3nDJdK4Ajmp5LPAlelb/sxIhfRvoHDbDsy2FQNz++Mb55Yh03KkA==}
+ /@storybook/core-events@7.0.6:
+ resolution: {integrity: sha512-kGrtjlYtjd4iTVk+Phb4CymZaVkB+MGscKAgcO8gfgJ/Q/gq8HQLVZSIzeoCDcDSHOGlBzbg2WVtdHIHhCKlOQ==}
+ dev: true
+
+ /@storybook/core-events@7.0.7:
+ resolution: {integrity: sha512-XNsR2RgaL2vBwuqsu+KA1DzGmB1UFfrAhpxhmyWTKDCniwtTLlaXgfKbqwcrOrPu/o1YswgIup/9UHepRHaf4A==}
+ dev: true
+
+ /@storybook/core-server@7.0.7:
+ resolution: {integrity: sha512-PB4zoClH7aKG4XeJhxx43iK9n/C9gctXubNN5DSN6thPm4UITOas+/q4N7AHbCPyRbcMyoW7M31KtpzZu4Fjew==}
dependencies:
'@aw-web-design/x-default-browser': 1.4.88
'@discoveryjs/json-ext': 0.5.7
- '@storybook/builder-manager': 7.0.2
- '@storybook/core-common': 7.0.2
- '@storybook/core-events': 7.0.2
+ '@storybook/builder-manager': 7.0.7
+ '@storybook/core-common': 7.0.7
+ '@storybook/core-events': 7.0.7
'@storybook/csf': 0.1.0
- '@storybook/csf-tools': 7.0.2
+ '@storybook/csf-tools': 7.0.7
'@storybook/docs-mdx': 0.1.0
'@storybook/global': 5.0.0
- '@storybook/manager': 7.0.2
- '@storybook/node-logger': 7.0.2
- '@storybook/preview-api': 7.0.2
- '@storybook/telemetry': 7.0.2
- '@storybook/types': 7.0.2
+ '@storybook/manager': 7.0.7
+ '@storybook/node-logger': 7.0.7
+ '@storybook/preview-api': 7.0.7
+ '@storybook/telemetry': 7.0.7
+ '@storybook/types': 7.0.7
'@types/detect-port': 1.3.2
'@types/node': 16.18.16
'@types/node-fetch': 2.6.2
@@ -5735,7 +5525,7 @@ packages:
pretty-hrtime: 1.0.3
prompts: 2.4.2
read-pkg-up: 7.0.1
- semver: 7.3.8
+ semver: 7.5.0
serve-favicon: 2.5.0
telejson: 7.0.4
ts-dedent: 2.2.0
@@ -5749,24 +5539,24 @@ packages:
- utf-8-validate
dev: true
- /@storybook/csf-plugin@7.0.2:
- resolution: {integrity: sha512-aGuo+G6G5IwSGkmc+OUA796sOfvJMaQj8QS/Zh5F0nL4ZlQvghHpXON8cRHHvmXHQqUo07KLiy7CZh2I2oq4iQ==}
+ /@storybook/csf-plugin@7.0.7:
+ resolution: {integrity: sha512-uhf2g077gXA6ZEMXIPQ0RnX+IoOTBJbj+6+VQfT7K5tvJeop1z0Fvk0FoknNXcUe7aUA0nzA/cUQ1v4vXqbY3Q==}
dependencies:
- '@storybook/csf-tools': 7.0.2
+ '@storybook/csf-tools': 7.0.7
unplugin: 0.10.2
transitivePeerDependencies:
- supports-color
dev: true
- /@storybook/csf-tools@7.0.2:
- resolution: {integrity: sha512-sOp355yQSpYiMqNSopmFYWZkPPRJdGgy4tpxGGLxpOZMygK3j1wQ/WQtl2Z0h61KP0S0dl6hrs0pHQz3A/eVrw==}
+ /@storybook/csf-tools@7.0.7:
+ resolution: {integrity: sha512-KbO5K2RS0oFm94eR49bAPvoyXY3Q6+ozvBek/F05RP7iAV790icQc59Xci9YDM1ONgb3afS+gSJGFBsE0h4pmg==}
dependencies:
'@babel/generator': 7.21.3
- '@babel/parser': 7.21.3
+ '@babel/parser': 7.21.4
'@babel/traverse': 7.21.3
- '@babel/types': 7.21.3
+ '@babel/types': 7.21.4
'@storybook/csf': 0.1.0
- '@storybook/types': 7.0.2
+ '@storybook/types': 7.0.7
fs-extra: 11.1.0
recast: 0.23.1
ts-dedent: 2.2.0
@@ -5774,12 +5564,6 @@ packages:
- supports-color
dev: true
- /@storybook/csf@0.0.2-next.11:
- resolution: {integrity: sha512-xGt0YSVxZb43sKmEf1GIQD8xEbo+c+S6khDEL7Qu/pYA0gh5z3WUuhOlovnelYj/YJod+XRsfVvk23AaRfUJ4Q==}
- dependencies:
- type-fest: 2.19.0
- dev: true
-
/@storybook/csf@0.1.0:
resolution: {integrity: sha512-uk+jMXCZ8t38jSTHk2o5btI+aV2Ksbvl6DoOv3r6VaCM1KZqeuMwtwywIQdflkA8/6q/dKT8z8L+g8hC4GC3VQ==}
dependencies:
@@ -5790,13 +5574,13 @@ packages:
resolution: {integrity: sha512-JDaBR9lwVY4eSH5W8EGHrhODjygPd6QImRbwjAuJNEnY0Vw4ie3bPkeGfnacB3OBW6u/agqPv2aRlR46JcAQLg==}
dev: true
- /@storybook/docs-tools@7.0.2:
- resolution: {integrity: sha512-w4D5BURrYjLbLGG9VKAaKU2dSdukszxRE3HWkJyhQU9R1JHvS3n8ntcMqYPqRfoHCOeBLBxP0edDYcAfzGNDYQ==}
+ /@storybook/docs-tools@7.0.7:
+ resolution: {integrity: sha512-VB4Qa33DYNxXALWcXyDid246r9Q6SGM+Q+pGWOuEJsxRxDmrUspXHaHG0CO1NIjMWfbqpOoz61vquZO0GZoAAg==}
dependencies:
'@babel/core': 7.21.3
- '@storybook/core-common': 7.0.2
- '@storybook/preview-api': 7.0.2
- '@storybook/types': 7.0.2
+ '@storybook/core-common': 7.0.7
+ '@storybook/preview-api': 7.0.7
+ '@storybook/types': 7.0.7
'@types/doctrine': 0.0.3
doctrine: 3.0.0
lodash: 4.17.21
@@ -5807,23 +5591,13 @@ packages:
/@storybook/expect@27.5.2-0:
resolution: {integrity: sha512-cP99mhWN/JeCp7VSIiymvj5tmuMY050iFohvp8Zq+kewKsBSZ6/qpTJAGCCZk6pneTcp4S0Fm5BSqyxzbyJ3gw==}
dependencies:
- '@types/jest': 29.5.0
+ '@types/jest': 29.5.1
dev: true
/@storybook/global@5.0.0:
resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==}
dev: true
- /@storybook/instrumenter@7.0.0:
- resolution: {integrity: sha512-A7jBrV7VM3OxRgall8rpjagy3VC78A/OV1g1aYVVLpAF/+Odj+MeHHF179+fR6JBLnBgukNfsG7/ZHHGs0gL5Q==}
- dependencies:
- '@storybook/channels': 7.0.0
- '@storybook/client-logger': 7.0.0
- '@storybook/core-events': 7.0.0
- '@storybook/global': 5.0.0
- '@storybook/preview-api': 7.0.0
- dev: true
-
/@storybook/instrumenter@7.0.2:
resolution: {integrity: sha512-zr9/fuaYtGVUtcL8XgjA4Iq5jtzdcqQyOSH4XLXtz6JtSad3lkRagbJo2Vzbw7dO/4vzjfTMxEzvWjUuPxLOhA==}
dependencies:
@@ -5834,50 +5608,70 @@ packages:
'@storybook/preview-api': 7.0.2
dev: true
+ /@storybook/instrumenter@7.0.6:
+ resolution: {integrity: sha512-JUcDas1cYCE+ZMVOw5CKc5g6PxDe3HH+IGdh/W9wL5vmdOUvAs858m7NLxkjkQGufof+Ohbmf/Yz5gyXaZ5+Yg==}
+ dependencies:
+ '@storybook/channels': 7.0.6
+ '@storybook/client-logger': 7.0.6
+ '@storybook/core-events': 7.0.6
+ '@storybook/global': 5.0.0
+ '@storybook/preview-api': 7.0.6
+ dev: true
+
+ /@storybook/instrumenter@7.0.7:
+ resolution: {integrity: sha512-0zE5lM3laKvCT4GW/XKKw8kakvI4catqK8PObZolRhfxbtGufW4VJZ2E8vXLtgA/+K3zikypjuWE6d45NLbh9w==}
+ dependencies:
+ '@storybook/channels': 7.0.7
+ '@storybook/client-logger': 7.0.7
+ '@storybook/core-events': 7.0.7
+ '@storybook/global': 5.0.0
+ '@storybook/preview-api': 7.0.7
+ dev: true
+
/@storybook/jest@0.1.0:
resolution: {integrity: sha512-TmybnEXlv5Fu2/Hq4nRj7alS9mw4CasLR0RDwaAzS+Vpvu1TC4+j9rh+b1BHtmWebbJh0JMT6mgzPqOyJdgtQA==}
dependencies:
'@storybook/expect': 27.5.2-0
- '@storybook/instrumenter': 7.0.0
+ '@storybook/instrumenter': 7.0.2
'@testing-library/jest-dom': 5.16.5
jest-mock: 27.5.1
dev: true
- /@storybook/manager-api@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-PbLj9Rc5uCMPfMdaXv1wE3koA3+d0rmZ3BJI8jeq+mfZEvpvfI4OOpRioT1q04CkkVomFOVFTyO0Q/o6Rb5N7g==}
+ /@storybook/manager-api@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-QTd/P72peAhofKqK+8yzIO9iWAEfPn8WUGGveV2KGaTlSlgbr87RLHEKilcXMZcYhBWC9izFRmjKum9ROdskrQ==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
- '@storybook/channels': 7.0.2
- '@storybook/client-logger': 7.0.2
- '@storybook/core-events': 7.0.2
+ '@storybook/channels': 7.0.7
+ '@storybook/client-logger': 7.0.7
+ '@storybook/core-events': 7.0.7
'@storybook/csf': 0.1.0
'@storybook/global': 5.0.0
- '@storybook/router': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/theming': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/types': 7.0.2
+ '@storybook/router': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/theming': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/types': 7.0.7
dequal: 2.0.3
lodash: 4.17.21
memoizerific: 1.11.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
- semver: 7.3.8
+ semver: 7.5.0
store2: 2.14.2
telejson: 7.0.4
ts-dedent: 2.2.0
dev: true
- /@storybook/manager@7.0.2:
- resolution: {integrity: sha512-jsFsFKG0rPNYfuRm/WSXGMBy8vnALyFWU330ObDmfU0JID3SeLlVqAOZT1GlwI6vupYpWodsN6qPZKRmC8onRw==}
+ /@storybook/manager@7.0.7:
+ resolution: {integrity: sha512-FhquwUpUOHsjZROf6E6kzUnJ6EmMeJ9b+HYg6yYPyIMYVMjAhnkRKbIj0phGx2lhgKFlmxik+3pgchK5SLdcZA==}
dev: true
/@storybook/mdx2-csf@1.0.0:
resolution: {integrity: sha512-dBAnEL4HfxxJmv7LdEYUoZlQbWj9APZNIbOaq0tgF8XkxiIbzqvgB0jhL/9UOrysSDbQWBiCRTu2wOVxedGfmw==}
dev: true
- /@storybook/node-logger@7.0.2:
- resolution: {integrity: sha512-UENpXxB1yDqP7JXaODJo+pbGt5y3NFBNurBr4+pI4bMAC4ARjpgRE4wp6fxUKFPu9MAR10oCdcLEHkaVUAjYRg==}
+ /@storybook/node-logger@7.0.7:
+ resolution: {integrity: sha512-5Y4LLgKeCStq1ktCKZ5eNPzQQSQ+CYZAlkEdzQ3Pp//0KXaZvVxEvGtaYhAymP2HatLpI8Oneo4lHrJioRfgww==}
dependencies:
'@types/npmlog': 4.1.4
chalk: 4.1.2
@@ -5885,20 +5679,40 @@ packages:
pretty-hrtime: 1.0.3
dev: true
- /@storybook/postinstall@7.0.2:
- resolution: {integrity: sha512-Hhiu3+N3ZDcbrhOCBJTDJbn/mC4l0v3ziyAP3yalq/2ZR9R5kfsEHHakKmswsKKV+ey0gNGijFTy3soU5oSs+A==}
+ /@storybook/postinstall@7.0.7:
+ resolution: {integrity: sha512-APcZ2KaR7z1aJje3pID4Ywmt1/aVcP3Sc4ltzNdH9mCkEsuq0fZHHQrYSa9Ya1IPRmSeLZ5/23q1iyqmGU3zoQ==}
+ dev: true
+
+ /@storybook/preview-api@7.0.2:
+ resolution: {integrity: sha512-QAlJM/r92+dQe/kB7MTTR9b/1mt9UJjxNjazGdEWipA/nw23kOF3o/hBcvKwBYkit4zGYsX70H+vuzW8hCo/lA==}
+ dependencies:
+ '@storybook/channel-postmessage': 7.0.2
+ '@storybook/channels': 7.0.2
+ '@storybook/client-logger': 7.0.2
+ '@storybook/core-events': 7.0.2
+ '@storybook/csf': 0.1.0
+ '@storybook/global': 5.0.0
+ '@storybook/types': 7.0.2
+ '@types/qs': 6.9.7
+ dequal: 2.0.3
+ lodash: 4.17.21
+ memoizerific: 1.11.3
+ qs: 6.11.1
+ synchronous-promise: 2.0.17
+ ts-dedent: 2.2.0
+ util-deprecate: 1.0.2
dev: true
- /@storybook/preview-api@7.0.0:
- resolution: {integrity: sha512-Q0IYYH1gOmx42ClYlQfQPjuERBWM3Ey+3DFsLQaraKXDdgZ9wN7jPNuS7wxuUNylT0oa/3WjxT7qNfiGw8JtBw==}
+ /@storybook/preview-api@7.0.6:
+ resolution: {integrity: sha512-uNsedNyiEccBV2EDUC/xcKTbmiNCYuVHbgOoWTmBz0ZqFo9bX0jxkpyYWHEhJM79qqVqmrpiQ5jbS8QKn8TIxQ==}
dependencies:
- '@storybook/channel-postmessage': 7.0.0
- '@storybook/channels': 7.0.0
- '@storybook/client-logger': 7.0.0
- '@storybook/core-events': 7.0.0
- '@storybook/csf': 0.0.2-next.11
+ '@storybook/channel-postmessage': 7.0.6
+ '@storybook/channels': 7.0.6
+ '@storybook/client-logger': 7.0.6
+ '@storybook/core-events': 7.0.6
+ '@storybook/csf': 0.1.0
'@storybook/global': 5.0.0
- '@storybook/types': 7.0.0
+ '@storybook/types': 7.0.6
'@types/qs': 6.9.7
dequal: 2.0.3
lodash: 4.17.21
@@ -5909,16 +5723,16 @@ packages:
util-deprecate: 1.0.2
dev: true
- /@storybook/preview-api@7.0.2:
- resolution: {integrity: sha512-QAlJM/r92+dQe/kB7MTTR9b/1mt9UJjxNjazGdEWipA/nw23kOF3o/hBcvKwBYkit4zGYsX70H+vuzW8hCo/lA==}
+ /@storybook/preview-api@7.0.7:
+ resolution: {integrity: sha512-R5pmGTodpu6hbwEg2RM2ulWtW3d426YzsisHrZJ+FT9lecWauN1y9xHCz7HdNzEFhT8r4YOa24L9ZS3mosZ7hA==}
dependencies:
- '@storybook/channel-postmessage': 7.0.2
- '@storybook/channels': 7.0.2
- '@storybook/client-logger': 7.0.2
- '@storybook/core-events': 7.0.2
+ '@storybook/channel-postmessage': 7.0.7
+ '@storybook/channels': 7.0.7
+ '@storybook/client-logger': 7.0.7
+ '@storybook/core-events': 7.0.7
'@storybook/csf': 0.1.0
'@storybook/global': 5.0.0
- '@storybook/types': 7.0.2
+ '@storybook/types': 7.0.7
'@types/qs': 6.9.7
dequal: 2.0.3
lodash: 4.17.21
@@ -5929,12 +5743,12 @@ packages:
util-deprecate: 1.0.2
dev: true
- /@storybook/preview@7.0.2:
- resolution: {integrity: sha512-U7MZkDT9bBq7HggLAXmTO9gI4eqhYs26fZS0L6iTE/PCX4Wg2TJBJSq2X8jhDXRqJFOt8SrQ756+V5Vtwrh4Og==}
+ /@storybook/preview@7.0.7:
+ resolution: {integrity: sha512-uL3ZcFao6UvxiSxCIcXKFakxEr9Nn0lvu0zzC2yQCVepzA7a+GDr1cK5VbZ6Mez38CnOvBmb5pkCbgRqSf/oug==}
dev: true
- /@storybook/react-dom-shim@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-fMl0aV7mJ3wyQKvt6z+rZuiIiSd9YinS77IJ1ETHqVZ4SxWriOS0GFKP6sZflrlpShoZBh+zl1lDPG7ZZdrQGw==}
+ /@storybook/react-dom-shim@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-INGwFeu9M+RzpvktSKuwy8Rk/70mXGqxxsb9lPtq7phmETvfpNX7GnLJqiVazTaQiB1DkB0iAPUsK2MNbBu+Kw==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5943,35 +5757,34 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: true
- /@storybook/react-vite@7.0.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.3)(vite@4.2.1):
- resolution: {integrity: sha512-1bDrmGo6imxBzZKJJ+SEHPuDn474JY3Yatm0cPaNVtlYhbnbiTPa3PxhI4U3233l4Qsc6DXNLKvi++j/knXDCw==}
+ /@storybook/react-vite@7.0.7(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.4)(vite@4.3.4):
+ resolution: {integrity: sha512-RuWfP/kiLpuHdcF9dWUUp9SOGMmO0FJ0HGV5yAOhGmi8KmTzvc8zjC+hJjj+sSgn2n71BO8pG/zqGl16FwfwVQ==}
engines: {node: '>=16'}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
vite: ^3.0.0 || ^4.0.0
dependencies:
- '@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@5.0.3)(vite@4.2.1)
+ '@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@5.0.4)(vite@4.3.4)
'@rollup/pluginutils': 4.2.1
- '@storybook/builder-vite': 7.0.2(typescript@5.0.3)(vite@4.2.1)
- '@storybook/react': 7.0.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.3)
- '@vitejs/plugin-react': 3.1.0(vite@4.2.1)
+ '@storybook/builder-vite': 7.0.7(typescript@5.0.4)(vite@4.3.4)
+ '@storybook/react': 7.0.7(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.4)
+ '@vitejs/plugin-react': 3.1.0(vite@4.3.4)
ast-types: 0.14.2
magic-string: 0.27.0
react: 18.2.0
react-docgen: 6.0.0-alpha.3
react-dom: 18.2.0(react@18.2.0)
- vite: 4.2.1(@types/node@18.15.11)(sass@1.60.0)
+ vite: 4.3.4(@types/node@18.16.3)(sass@1.62.1)
transitivePeerDependencies:
- '@preact/preset-vite'
- - '@storybook/mdx1-csf'
- supports-color
- typescript
- vite-plugin-glimmerx
dev: true
- /@storybook/react@7.0.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.3):
- resolution: {integrity: sha512-2P7Oju1XKWMyn75dO0vjL4gthzBL/lLiCBRyAHKXZJ1H2eNdWjXkOOtH1HxnbRcXjWSU4tW96dqKY8m0iR9zAA==}
+ /@storybook/react@7.0.7(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.4):
+ resolution: {integrity: sha512-eEsIfAGumzo7KRi/WKFpn/PGFhwLv72oiEM/8l5MMX/6poIkiekunqJLfx2BoL4cCtiS4g7OYzOdWjN01DwVCg==}
engines: {node: '>=16.0.0'}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5981,13 +5794,13 @@ packages:
typescript:
optional: true
dependencies:
- '@storybook/client-logger': 7.0.2
- '@storybook/core-client': 7.0.2
- '@storybook/docs-tools': 7.0.2
+ '@storybook/client-logger': 7.0.7
+ '@storybook/core-client': 7.0.7
+ '@storybook/docs-tools': 7.0.7
'@storybook/global': 5.0.0
- '@storybook/preview-api': 7.0.2
- '@storybook/react-dom-shim': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/types': 7.0.2
+ '@storybook/preview-api': 7.0.7
+ '@storybook/react-dom-shim': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/types': 7.0.7
'@types/escodegen': 0.0.6
'@types/estree': 0.0.51
'@types/node': 16.18.16
@@ -6003,119 +5816,127 @@ packages:
react-element-to-jsx-string: 15.0.0(react-dom@18.2.0)(react@18.2.0)
ts-dedent: 2.2.0
type-fest: 2.19.0
- typescript: 5.0.3
+ typescript: 5.0.4
util-deprecate: 1.0.2
transitivePeerDependencies:
- supports-color
dev: true
- /@storybook/router@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-ZB2vucfayZUrMLBlXju4v6CNOQQb0YKDLw5RoojdBxOsUFtnp5UiPOE+I8PQR63EBwnRjozeibV1XSM+GlQb5w==}
+ /@storybook/router@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-/lM8/NHQKeshfnC3ayFuO8Y9TCSHnCAPRhIsVxvanBzcj+ILbCIyZ+TspvB3hT4MbX/Ez+JR8VrMbjXIGwmH8w==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
- '@storybook/client-logger': 7.0.2
+ '@storybook/client-logger': 7.0.7
memoizerific: 1.11.3
qs: 6.11.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: true
- /@storybook/source-loader@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-YIEluNmRHxDZByhwnwTwo+NXKuPrxlCDjdtkMHqvlLQW60KV4l8zaBaBP4//Jq4ycYI1Qt85UKzeZPYzJVT50g==}
+ /@storybook/source-loader@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-vAURuD0WmafHe8XGbkwcOn1dCLcU4ociL5xakCw0upgW0cUczp4BMRfwmP0Az5YDhfoQRjBVmUaFfjONhTDawg==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
'@storybook/csf': 0.1.0
- '@storybook/types': 7.0.2
+ '@storybook/types': 7.0.7
estraverse: 5.3.0
lodash: 4.17.21
- prettier: 2.8.7
+ prettier: 2.8.8
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: true
- /@storybook/telemetry@7.0.2:
- resolution: {integrity: sha512-s2PIwI9nVYQBf3h40EFHLynYUfdqzRJMXyaCWJdVQuvdQfRkAn3CLXaubK+VdjC869z3ZfW20EMu3Mbgzcc0HA==}
+ /@storybook/telemetry@7.0.7:
+ resolution: {integrity: sha512-Ka6pwWr3sWs3A/6WQ0wsoSYzXx3Mhr7eByNZZKuuCu9jnw3I8AbIOqQX2iOVzaQBLZsvXEeqvYY8iZ+GuRbbGQ==}
dependencies:
- '@storybook/client-logger': 7.0.2
- '@storybook/core-common': 7.0.2
+ '@storybook/client-logger': 7.0.7
+ '@storybook/core-common': 7.0.7
chalk: 4.1.2
detect-package-manager: 2.0.1
fetch-retry: 5.0.4
fs-extra: 11.1.0
isomorphic-unfetch: 3.1.0
- nanoid: 3.3.4
+ nanoid: 3.3.6
read-pkg-up: 7.0.1
transitivePeerDependencies:
- encoding
- supports-color
dev: true
- /@storybook/testing-library@0.0.14-next.1:
- resolution: {integrity: sha512-1CAl40IKIhcPaCC4pYCG0b9IiYNymktfV/jTrX7ctquRY3akaN7f4A1SippVHosksft0M+rQTFE0ccfWW581fw==}
+ /@storybook/testing-library@0.1.0:
+ resolution: {integrity: sha512-g947f4LJZw3IluBhysMKLJXByAFiSxnGuooENqU+ZPt/GTrz1I9GDBlhmoTJahuFkVbwHvziAl/8riY2Re921g==}
dependencies:
- '@storybook/client-logger': 7.0.2
- '@storybook/instrumenter': 7.0.2
+ '@storybook/client-logger': 7.0.6
+ '@storybook/instrumenter': 7.0.6
'@testing-library/dom': 8.20.0
'@testing-library/user-event': 13.5.0(@testing-library/dom@8.20.0)
ts-dedent: 2.2.0
dev: true
- /@storybook/theming@7.0.2(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-c9sE+QAZNbopPvLiJ6BMxBERfTaq1ATyIri97FBvTucuSotNXw7X5q+ip5/nrCOPZuvK2f5wF4DRyD2HnB/rIQ==}
+ /@storybook/theming@7.0.7(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-InTZe+Sgco1NsxgiG+cyUKWQe3GsjlIyU/o5qDdtOTXcZ64HzyBuAZlAequSddqfDeMDqxRFPc2w1J28MAUHxA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
'@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0)
- '@storybook/client-logger': 7.0.2
+ '@storybook/client-logger': 7.0.7
'@storybook/global': 5.0.0
memoizerific: 1.11.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: true
- /@storybook/types@7.0.0:
- resolution: {integrity: sha512-eCMW/xTVMswgD/58itibw8s8f2hZ7tciT3saRdGCymg9tPUhMC/9eGIIUSr/C+xfnCJEZm6J6DgEUo1xlifonw==}
+ /@storybook/types@7.0.2:
+ resolution: {integrity: sha512-0OCt/kAexa8MCcljxA+yZxGMn0n2U2Ync0KxotItqNbKBKVkaLQUls0+IXTWSCpC/QJvNZ049jxUHHanNi/96w==}
dependencies:
- '@storybook/channels': 7.0.0
+ '@storybook/channels': 7.0.2
'@types/babel__core': 7.20.0
'@types/express': 4.17.17
file-system-cache: 2.0.2
dev: true
- /@storybook/types@7.0.2:
- resolution: {integrity: sha512-0OCt/kAexa8MCcljxA+yZxGMn0n2U2Ync0KxotItqNbKBKVkaLQUls0+IXTWSCpC/QJvNZ049jxUHHanNi/96w==}
+ /@storybook/types@7.0.6:
+ resolution: {integrity: sha512-dFASQxzvldU2Nx/eJG+oL4wCchUWAKOmOSYJYhKgtGpx99oXOiWUyC0SgCpTveBJ7AppoiseyasQ9Gd/Ccycdw==}
dependencies:
- '@storybook/channels': 7.0.2
+ '@storybook/channels': 7.0.6
+ '@types/babel__core': 7.20.0
+ '@types/express': 4.17.17
+ file-system-cache: 2.0.2
+ dev: true
+
+ /@storybook/types@7.0.7:
+ resolution: {integrity: sha512-v9piuwp8FvTiHXIOOi5lEyTEJKhnbcbhVxgJ3VFhhXYFd0DTz6Bst0XIIgkgs21ITb3xhkfPbCRUueMcbXO1MA==}
+ dependencies:
+ '@storybook/channels': 7.0.7
'@types/babel__core': 7.20.0
'@types/express': 4.17.17
file-system-cache: 2.0.2
dev: true
- /@storybook/vue3-vite@7.0.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.3)(vite@4.2.1)(vue@3.2.47):
- resolution: {integrity: sha512-lmxnHA9wHkgbNq+oW6dVnXbe9QOFjOz4Ejkl1AAjjg0blJ+VGautVa3mSeYM99szx5EigSfQjFAkv/TAJVC80Q==}
+ /@storybook/vue3-vite@7.0.7(react-dom@18.2.0)(react@18.2.0)(typescript@5.0.4)(vite@4.3.4)(vue@3.2.47):
+ resolution: {integrity: sha512-ePAOd5TBYq6WJTHgSP6B/k3PXzv8JU5sm956FMmpnDXvttZGKlp+a1qjabA8IbKD8SKfmjQmV7719Uh/ZPyQ6Q==}
engines: {node: ^14.18 || >=16}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
vite: ^3.0.0 || ^4.0.0
dependencies:
- '@storybook/builder-vite': 7.0.2(typescript@5.0.3)(vite@4.2.1)
- '@storybook/core-server': 7.0.2
- '@storybook/vue3': 7.0.2(vue@3.2.47)
- '@vitejs/plugin-vue': 4.1.0(vite@4.2.1)(vue@3.2.47)
+ '@storybook/builder-vite': 7.0.7(typescript@5.0.4)(vite@4.3.4)
+ '@storybook/core-server': 7.0.7
+ '@storybook/vue3': 7.0.7(vue@3.2.47)
+ '@vitejs/plugin-vue': 4.2.1(vite@4.3.4)(vue@3.2.47)
magic-string: 0.27.0
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
- vite: 4.2.1(@types/node@18.15.11)(sass@1.60.0)
+ vite: 4.3.4(@types/node@18.16.3)(sass@1.62.1)
vue-docgen-api: 4.64.1(vue@3.2.47)
transitivePeerDependencies:
- '@preact/preset-vite'
- - '@storybook/mdx1-csf'
- bufferutil
- encoding
- supports-color
@@ -6125,17 +5946,17 @@ packages:
- vue
dev: true
- /@storybook/vue3@7.0.2(vue@3.2.47):
- resolution: {integrity: sha512-0KDwhDYg9TQTB9awEcm8IeBMkxD6ZQFHuSHfE7TEWSx83H9pGb6CkVkAgjZlmfMiSY6gI160G4mJt2/tgTxbiA==}
+ /@storybook/vue3@7.0.7(vue@3.2.47):
+ resolution: {integrity: sha512-SPZYm46zVkL9julVrQsUpPY02M/XzaDVsODlC75dXgL1jBskmbgW6WkjFEOoNr1RtEblb+t+pqxLoK4/LA09qw==}
engines: {node: '>=16.0.0'}
peerDependencies:
vue: ^3.0.0
dependencies:
- '@storybook/core-client': 7.0.2
- '@storybook/docs-tools': 7.0.2
+ '@storybook/core-client': 7.0.7
+ '@storybook/docs-tools': 7.0.7
'@storybook/global': 5.0.0
- '@storybook/preview-api': 7.0.2
- '@storybook/types': 7.0.2
+ '@storybook/preview-api': 7.0.7
+ '@storybook/types': 7.0.7
ts-dedent: 2.2.0
type-fest: 2.19.0
vue: 3.2.47
@@ -6143,7 +5964,7 @@ packages:
- supports-color
dev: true
- /@swc/cli@0.1.62(@swc/core@1.3.46)(chokidar@3.5.3):
+ /@swc/cli@0.1.62(@swc/core@1.3.56)(chokidar@3.5.3):
resolution: {integrity: sha512-kOFLjKY3XH1DWLfXL1/B5MizeNorHR8wHKEi92S/Zi9Md/AK17KSqR8MgyRJ6C1fhKHvbBCl8wboyKAFXStkYw==}
engines: {node: '>= 12.13'}
hasBin: true
@@ -6155,11 +5976,11 @@ packages:
optional: true
dependencies:
'@mole-inc/bin-wrapper': 8.0.1
- '@swc/core': 1.3.46(@swc/helpers@0.5.0)
+ '@swc/core': 1.3.56
chokidar: 3.5.3
commander: 7.2.0
fast-glob: 3.2.12
- semver: 7.3.8
+ semver: 7.5.0
slash: 3.0.0
source-map: 0.7.4
dev: false
@@ -6175,119 +5996,115 @@ packages:
dev: false
optional: true
- /@swc/core-darwin-arm64@1.3.46:
- resolution: {integrity: sha512-kY4ASe7SsntDw2B1T70H9K1CFmK8POi+LyIpeCyC96EB9wbH2Sax+ploBB/wZALbYzr/dMJzOCU8QXzdmVS4Rg==}
+ /@swc/core-darwin-arm64@1.3.56:
+ resolution: {integrity: sha512-DZcu7BzDaLEdWHabz9DRTP0yEBLqkrWmskFcD5BX0lGAvoIvE4duMnAqi5F2B3X7630QioHRCYFoRw2WkeE3Cw==}
engines: {node: '>=10'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
optional: true
- /@swc/core-darwin-x64@1.3.46:
- resolution: {integrity: sha512-kE3PMk8xW+2BZ3oZiTxxsUU/GzrGwM+qS4frOBz9TYHZe+W1dTtj4F9vBit4PFJ+tv4O6DPt9neGobzdq0UmRw==}
+ /@swc/core-darwin-x64@1.3.56:
+ resolution: {integrity: sha512-VH5saqYFasdRXJy6RAT+MXm0+IjkMZvOkohJwUei+oA65cKJofQwrJ1jZro8yOJFYvUSI3jgNRGsdBkmo/4hMw==}
engines: {node: '>=10'}
cpu: [x64]
os: [darwin]
requiresBuild: true
optional: true
- /@swc/core-linux-arm-gnueabihf@1.3.46:
- resolution: {integrity: sha512-7TbiUr9MYxT+mC7sVrayag/isFoaZUG/ogkEK8B/ouA1pnIYqWh3N5ifqCzfcSRiOURt+vVqPyoO1puSiNzVuQ==}
+ /@swc/core-linux-arm-gnueabihf@1.3.56:
+ resolution: {integrity: sha512-LWwPo6NnJkH01+ukqvkoNIOpMdw+Zundm4vBeicwyVrkP+mC3kwVfi03TUFpQUz3kRKdw/QEnxGTj+MouCPbtw==}
engines: {node: '>=10'}
cpu: [arm]
os: [linux]
requiresBuild: true
optional: true
- /@swc/core-linux-arm64-gnu@1.3.46:
- resolution: {integrity: sha512-Ycw4LU/wsUK9R+Y/2qFOPQseZDfM5D5gbWGrrYj5RoTm57FbnUsSsO26QeZxUNvams1oAQDkZDuerCc9qBRzIQ==}
+ /@swc/core-linux-arm64-gnu@1.3.56:
+ resolution: {integrity: sha512-GzsUy/4egJ4cMlxbM+Ub7AMi5CKAc+pxBxrh8MUPQbyStW8jGgnQsJouTnGy0LHawtdEnsCOl6PcO6OgvktXuQ==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
optional: true
- /@swc/core-linux-arm64-musl@1.3.46:
- resolution: {integrity: sha512-cBclyr6IW1PBr8l9D4FkebgbqlkiIYnSJCbY84J/6PfTzQlD6w9a1TAoYxdGZpJ7SGHdmB0oDiZS1rhxCSCV/Q==}
+ /@swc/core-linux-arm64-musl@1.3.56:
+ resolution: {integrity: sha512-9gxL09BIiAv8zY0DjfnFf19bo8+P4T9tdhzPwcm+1yPJcY5yr1+YFWLNFzz01agtOj6VlZ2/wUJTaOfdjjtc+A==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
optional: true
- /@swc/core-linux-x64-gnu@1.3.46:
- resolution: {integrity: sha512-amqMhTA2CXB6t11hVAZSSPKq4DZ9/sWbW3wYYQHxzqrMJML0726OJs4pt0XnlU7FzdP/9M9j2B/gWCRaCMxXVA==}
+ /@swc/core-linux-x64-gnu@1.3.56:
+ resolution: {integrity: sha512-n0ORNknl50vMRkll3BDO1E4WOqY6iISlPV1ZQCRLWQ6YQ2q8/WAryBxc2OAybcGHBUFkxyACpJukeU1QZ/9tNw==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
requiresBuild: true
optional: true
- /@swc/core-linux-x64-musl@1.3.46:
- resolution: {integrity: sha512-WOQZTIkJ9khIj5Z2unf6OTrWV9k8br+HZ93RvnamEmJBlLPUuT9IjB+agNhjaDgOpz9/ZldSGqV7vzl5FGQl1Q==}
+ /@swc/core-linux-x64-musl@1.3.56:
+ resolution: {integrity: sha512-r+D34WLAOAlJtfw1gaVWpHRwCncU9nzW9i7w9kSw4HpWYnHJOz54jLGSEmNsrhdTCz1VK2ar+V2ktFUsrlGlDA==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
requiresBuild: true
optional: true
- /@swc/core-win32-arm64-msvc@1.3.46:
- resolution: {integrity: sha512-4JSREbqaTRQ6QO0EeoiB6G5vuFT8zI8aTOLu5At7Cvlw+X7bOGNO+wJ3Tqw7O+68OL+0bPHzHGTXKL9kUccY1A==}
+ /@swc/core-win32-arm64-msvc@1.3.56:
+ resolution: {integrity: sha512-29Yt75Is6X24z3x8h/xZC1HnDPkPpyLH9mDQiM6Cuc0I9mVr1XSriPEUB2N/awf5IE4SA8c+3IVq1DtKWbkJIw==}
engines: {node: '>=10'}
cpu: [arm64]
os: [win32]
requiresBuild: true
optional: true
- /@swc/core-win32-ia32-msvc@1.3.46:
- resolution: {integrity: sha512-kC8dIDzcArm1e85yHJsEZFxcNq5NztLkrqkP1nVOQ+9QXD9DKhjbZtWy2gnpclinii6KEGng8SieWiJiOA0CBQ==}
+ /@swc/core-win32-ia32-msvc@1.3.56:
+ resolution: {integrity: sha512-mplp0zbYDrcHtfvkniXlXdB04e2qIjz2Gq/XHKr4Rnc6xVORJjjXF91IemXKpavx2oZYJws+LNJL7UFQ8jyCdQ==}
engines: {node: '>=10'}
cpu: [ia32]
os: [win32]
requiresBuild: true
optional: true
- /@swc/core-win32-x64-msvc@1.3.46:
- resolution: {integrity: sha512-rrSAfq+DvpJioBxUsnuH+sKl0eXid1DwkwNzkVGHEreN9GoP7GospWtFq7VDcO6DrS/s3HtR4/TzoIYFEBCRIg==}
+ /@swc/core-win32-x64-msvc@1.3.56:
+ resolution: {integrity: sha512-zp8MBnrw/bjdLenO/ifYzHrImSjKunqL0C2IF4LXYNRfcbYFh2NwobsVQMZ20IT0474lKRdlP8Oxdt+bHuXrzA==}
engines: {node: '>=10'}
cpu: [x64]
os: [win32]
requiresBuild: true
optional: true
- /@swc/core@1.3.46(@swc/helpers@0.5.0):
- resolution: {integrity: sha512-WxzgJMWUBVJ95HsvEqlWzM3Qxp2FQrPa4QdAkQQuuvCMnfdctGUbhX/c3LiSRlWrl2LIkYAi4bLansTOol4QcQ==}
+ /@swc/core@1.3.56:
+ resolution: {integrity: sha512-yz/EeXT+PMZucUNrYceRUaTfuNS4IIu5EDZSOlvCEvm4jAmZi7CYH1B/kvzEzoAOzr7zkQiDPNJftcQXLkjbjA==}
engines: {node: '>=10'}
requiresBuild: true
peerDependencies:
'@swc/helpers': ^0.5.0
- dependencies:
- '@swc/helpers': 0.5.0
+ peerDependenciesMeta:
+ '@swc/helpers':
+ optional: true
optionalDependencies:
- '@swc/core-darwin-arm64': 1.3.46
- '@swc/core-darwin-x64': 1.3.46
- '@swc/core-linux-arm-gnueabihf': 1.3.46
- '@swc/core-linux-arm64-gnu': 1.3.46
- '@swc/core-linux-arm64-musl': 1.3.46
- '@swc/core-linux-x64-gnu': 1.3.46
- '@swc/core-linux-x64-musl': 1.3.46
- '@swc/core-win32-arm64-msvc': 1.3.46
- '@swc/core-win32-ia32-msvc': 1.3.46
- '@swc/core-win32-x64-msvc': 1.3.46
-
- /@swc/helpers@0.5.0:
- resolution: {integrity: sha512-SjY/p4MmECVVEWspzSRpQEM3sjR17sP8PbGxELWrT+YZMBfiUyt1MRUNjMV23zohwlG2HYtCQOsCwsTHguXkyg==}
- dependencies:
- tslib: 2.5.0
+ '@swc/core-darwin-arm64': 1.3.56
+ '@swc/core-darwin-x64': 1.3.56
+ '@swc/core-linux-arm-gnueabihf': 1.3.56
+ '@swc/core-linux-arm64-gnu': 1.3.56
+ '@swc/core-linux-arm64-musl': 1.3.56
+ '@swc/core-linux-x64-gnu': 1.3.56
+ '@swc/core-linux-x64-musl': 1.3.56
+ '@swc/core-win32-arm64-msvc': 1.3.56
+ '@swc/core-win32-ia32-msvc': 1.3.56
+ '@swc/core-win32-x64-msvc': 1.3.56
- /@swc/jest@0.2.24(@swc/core@1.3.46):
- resolution: {integrity: sha512-fwgxQbM1wXzyKzl1+IW0aGrRvAA8k0Y3NxFhKigbPjOJ4mCKnWEcNX9HQS3gshflcxq8YKhadabGUVfdwjCr6Q==}
+ /@swc/jest@0.2.26(@swc/core@1.3.56):
+ resolution: {integrity: sha512-7lAi7q7ShTO3E5Gt1Xqf3pIhRbERxR1DUxvtVa9WKzIB+HGQ7wZP5sYx86zqnaEoKKGhmOoZ7gyW0IRu8Br5+A==}
engines: {npm: '>= 7.0.0'}
peerDependencies:
'@swc/core': '*'
dependencies:
'@jest/create-cache-key-function': 27.5.1
- '@swc/core': 1.3.46(@swc/helpers@0.5.0)
+ '@swc/core': 1.3.56
jsonc-parser: 3.2.0
dev: true
@@ -6297,8 +6114,8 @@ packages:
dev: false
optional: true
- /@syuilo/aiscript@0.13.1:
- resolution: {integrity: sha512-WJduqlsm7pq8r1oYbPSAf5cqR6Pve6ipGoPUUSgti51dMytMnxE2dn7d7i8759RF3yftyoclZbL/DC7IjPsTng==}
+ /@syuilo/aiscript@0.13.2:
+ resolution: {integrity: sha512-1aqQSH6U+vV01UDUotXUEjIwJKcZZPASJyIJ9msxXRpSInPGJJ/q1kGkZMgSpVhzYFT7/QBxo0UC1ZVEOsrDTw==}
dependencies:
autobind-decorator: 2.4.0
seedrandom: 3.0.5
@@ -6311,6 +6128,7 @@ packages:
engines: {node: '>=10'}
dependencies:
defer-to-connect: 2.0.1
+ dev: false
/@szmarczak/http-timer@5.0.1:
resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
@@ -6318,59 +6136,58 @@ packages:
dependencies:
defer-to-connect: 2.0.1
- /@tabler/icons-webfont@2.12.0:
- resolution: {integrity: sha512-5I4jub4zwBo4WdnZhRRmfjPutY7JL/ZbdGjH6MJ56x9RuYeJu62geJIAr8eHDdOSTWguUzZI3lQMygXoz63grw==}
+ /@tabler/icons-webfont@2.17.0:
+ resolution: {integrity: sha512-jgRZWiWCaG++jFTIU/dbOT+JmSgoFlALwBUUS31mt1b5py7B0YWelnfxf5s3ctE+0dlnoIS+r7rDOeDSAWx8SA==}
dependencies:
- '@tabler/icons': 2.12.0
+ '@tabler/icons': 2.17.0
dev: false
- /@tabler/icons@2.12.0:
- resolution: {integrity: sha512-PiYD2zUOewNAtfJKU9tBZfyv3NeFlTlUe5LSGTTplvBcM0iuHrNVnd30+HIcUGJQhi3Uf0Ng9FRi/kPqm/q6ZA==}
+ /@tabler/icons@2.17.0:
+ resolution: {integrity: sha512-UeJaylOGNRhQKyDlgZfrQ3UPSGlfVQuXcmCsTYeXioKKepibW6VZ3H36Lo1jvBTBkQD2e9m+k2NxwkztOTXwrA==}
dev: false
- /@tensorflow/tfjs-backend-cpu@4.2.0(@tensorflow/tfjs-core@4.2.0):
- resolution: {integrity: sha512-8HWg9J69m0Ovc6w8TVhhixMOcwA3t/NPXLblOA/sgJ+/JD5gsbpLWJk4QISQyb1RnpSVzw6PX3BSMTJU7hWVOg==}
+ /@tensorflow/tfjs-backend-cpu@4.4.0(@tensorflow/tfjs-core@4.4.0):
+ resolution: {integrity: sha512-d4eln500/qNym78z9IrUUzF0ITBoJGLrxV8xd92kLVoXhg35Mm+zqUXShjFcrH8joOHOFuST0qZ0TbDDqcPzPA==}
engines: {yarn: '>= 1.3.2'}
peerDependencies:
- '@tensorflow/tfjs-core': 4.2.0
+ '@tensorflow/tfjs-core': 4.4.0
dependencies:
- '@tensorflow/tfjs-core': 4.2.0
+ '@tensorflow/tfjs-core': 4.4.0
'@types/seedrandom': 2.4.30
seedrandom: 3.0.5
dev: false
- /@tensorflow/tfjs-backend-webgl@4.2.0(@tensorflow/tfjs-core@4.2.0):
- resolution: {integrity: sha512-Qvf+hD5pSh+xi48kChSGzcDKJemkc4EKfoVVjuxl4k25ZUPwuEd7zZUAtinkLu1dzgHNyvePZY8k+9rVm59HJA==}
+ /@tensorflow/tfjs-backend-webgl@4.4.0(@tensorflow/tfjs-core@4.4.0):
+ resolution: {integrity: sha512-TzQKvfAPgGt9cMG+5bVoTckoG1xr/PVJM/uODkPvzcMqi3j97kuWDXwkYJIgXldStmfiKkU7f5CmyD3Cq3E6BA==}
engines: {yarn: '>= 1.3.2'}
peerDependencies:
- '@tensorflow/tfjs-core': 4.2.0
+ '@tensorflow/tfjs-core': 4.4.0
dependencies:
- '@tensorflow/tfjs-backend-cpu': 4.2.0(@tensorflow/tfjs-core@4.2.0)
- '@tensorflow/tfjs-core': 4.2.0
+ '@tensorflow/tfjs-backend-cpu': 4.4.0(@tensorflow/tfjs-core@4.4.0)
+ '@tensorflow/tfjs-core': 4.4.0
'@types/offscreencanvas': 2019.3.0
'@types/seedrandom': 2.4.30
'@types/webgl-ext': 0.0.30
seedrandom: 3.0.5
dev: false
- /@tensorflow/tfjs-converter@4.2.0(@tensorflow/tfjs-core@4.2.0):
- resolution: {integrity: sha512-m+E2KJM6yGQdi8ElzWpChdD/JaqhWMCi9yK70v/ndkOaCL2q2UN48nYP2T5S15vkDvMIgzAQyZfh7hxQsMuvRQ==}
+ /@tensorflow/tfjs-converter@4.4.0(@tensorflow/tfjs-core@4.4.0):
+ resolution: {integrity: sha512-JUjpRStrAuw37tgPd5UENu0UjQVuJT09yF7KpOur4BriJ0uQqrbEZHMPHmvUtr5nYzkqlXJTuXIyxvEY/olNpg==}
peerDependencies:
- '@tensorflow/tfjs-core': 4.2.0
+ '@tensorflow/tfjs-core': 4.4.0
dependencies:
- '@tensorflow/tfjs-core': 4.2.0
+ '@tensorflow/tfjs-core': 4.4.0
dev: false
- /@tensorflow/tfjs-core@4.2.0:
- resolution: {integrity: sha512-uuHkiWVC8b00ngFbHvAV7J7haRlN/9PEdeenCi0CzBjgKd7aN25wPWaoN0TSQcU+GT4FJ8mofMZ9VBYZ/s/WLg==}
+ /@tensorflow/tfjs-core@4.4.0:
+ resolution: {integrity: sha512-Anxpc7cAOA0Q7EUXdTbQKMg3reFvrdkgDlaYzH9ZfkMq2CgLV4Au6E/s6HmbYn/VrAtWy9mLY5c/lLJqh4764g==}
engines: {yarn: '>= 1.3.2'}
- requiresBuild: true
dependencies:
'@types/long': 4.0.2
'@types/offscreencanvas': 2019.7.0
'@types/seedrandom': 2.4.30
'@types/webgl-ext': 0.0.30
- '@webgpu/types': 0.1.21
+ '@webgpu/types': 0.1.30
long: 4.0.0
node-fetch: 2.6.7
seedrandom: 3.0.5
@@ -6378,13 +6195,13 @@ packages:
- encoding
dev: false
- /@tensorflow/tfjs-data@4.2.0(@tensorflow/tfjs-core@4.2.0)(seedrandom@3.0.5):
- resolution: {integrity: sha512-11t7Q+ikseduJgkd9iSeRrtor1aA3o5PVCFhC5yYvR3JLO55ic1+4Ryo0EJfhRoismS6zBUJrpzX4K0zlLbIfw==}
+ /@tensorflow/tfjs-data@4.4.0(@tensorflow/tfjs-core@4.4.0)(seedrandom@3.0.5):
+ resolution: {integrity: sha512-aY4eq4cgrsrXeBU6ABZAAN3tV0fG4YcHd0z+cYuNXnCo+VEQLJnPmhn+xymZ4VQZQH4GXbVS4dV9pXMclFNRFw==}
peerDependencies:
- '@tensorflow/tfjs-core': 4.2.0
+ '@tensorflow/tfjs-core': 4.4.0
seedrandom: ^3.0.5
dependencies:
- '@tensorflow/tfjs-core': 4.2.0
+ '@tensorflow/tfjs-core': 4.4.0
'@types/node-fetch': 2.6.2
node-fetch: 2.6.7
seedrandom: 3.0.5
@@ -6393,21 +6210,21 @@ packages:
- encoding
dev: false
- /@tensorflow/tfjs-layers@4.2.0(@tensorflow/tfjs-core@4.2.0):
- resolution: {integrity: sha512-SO0KTmCFOjrW+PlP9nKYXz07XGFq6uE7am9yH2bRaRPWpEeaKT/+k0C9vFMxI/GzRwY8AK4sLe4U+jE1mhYxGw==}
+ /@tensorflow/tfjs-layers@4.4.0(@tensorflow/tfjs-core@4.4.0):
+ resolution: {integrity: sha512-OGC7shfiD9Gc698hINHK4y9slOJvu5m54tVNm4xf+WSNrw/avvgpar6yyoL5bakYIZNQvFNK75Yr8VRPR7oPeQ==}
peerDependencies:
- '@tensorflow/tfjs-core': 4.2.0
+ '@tensorflow/tfjs-core': 4.4.0
dependencies:
- '@tensorflow/tfjs-core': 4.2.0
+ '@tensorflow/tfjs-core': 4.4.0
dev: false
- /@tensorflow/tfjs-node@4.2.0(seedrandom@3.0.5):
- resolution: {integrity: sha512-dkyExfqTjMVDpKLBzHH2b7JWzGK+QORtYswvoNHbeHwx7kvYYRAQeiHxp+xrgry6LdbWfZs88IWCwNGoOiZ21w==}
+ /@tensorflow/tfjs-node@4.4.0(seedrandom@3.0.5):
+ resolution: {integrity: sha512-+JSAddsupjSQUDZeb7QGOFkL3Tty3kjPHx8ethiYFzwTZJHCMvM7wZJd0Fqnjxym6A0KpsmB7SPZgwRRXVIlPA==}
engines: {node: '>=8.11.0'}
requiresBuild: true
dependencies:
'@mapbox/node-pre-gyp': 1.0.9
- '@tensorflow/tfjs': 4.2.0(seedrandom@3.0.5)
+ '@tensorflow/tfjs': 4.4.0(seedrandom@3.0.5)
adm-zip: 0.5.10
google-protobuf: 3.21.2
https-proxy-agent: 2.2.4
@@ -6421,18 +6238,19 @@ packages:
dev: false
optional: true
- /@tensorflow/tfjs@4.2.0(seedrandom@3.0.5):
- resolution: {integrity: sha512-iZmtyGC9IJkx+TpFnkgDol8BHv2BU3zJ01HyNcuvnm1w1EqoNe+1n8bwvLzI/sxHMcHTqzuu7VugMaphryxE+A==}
+ /@tensorflow/tfjs@4.4.0(seedrandom@3.0.5):
+ resolution: {integrity: sha512-EmCsnzdvawyk4b+4JKaLLuicHcJQRZtL1zSy9AWJLiiHTbDDseYgLxfaCEfLk8v2bUe7SBXwl3n3B7OjgvH11Q==}
+ hasBin: true
dependencies:
- '@tensorflow/tfjs-backend-cpu': 4.2.0(@tensorflow/tfjs-core@4.2.0)
- '@tensorflow/tfjs-backend-webgl': 4.2.0(@tensorflow/tfjs-core@4.2.0)
- '@tensorflow/tfjs-converter': 4.2.0(@tensorflow/tfjs-core@4.2.0)
- '@tensorflow/tfjs-core': 4.2.0
- '@tensorflow/tfjs-data': 4.2.0(@tensorflow/tfjs-core@4.2.0)(seedrandom@3.0.5)
- '@tensorflow/tfjs-layers': 4.2.0(@tensorflow/tfjs-core@4.2.0)
+ '@tensorflow/tfjs-backend-cpu': 4.4.0(@tensorflow/tfjs-core@4.4.0)
+ '@tensorflow/tfjs-backend-webgl': 4.4.0(@tensorflow/tfjs-core@4.4.0)
+ '@tensorflow/tfjs-converter': 4.4.0(@tensorflow/tfjs-core@4.4.0)
+ '@tensorflow/tfjs-core': 4.4.0
+ '@tensorflow/tfjs-data': 4.4.0(@tensorflow/tfjs-core@4.4.0)(seedrandom@3.0.5)
+ '@tensorflow/tfjs-layers': 4.4.0(@tensorflow/tfjs-core@4.4.0)
argparse: 1.0.10
chalk: 4.1.2
- core-js: 3.27.1
+ core-js: 3.29.1
regenerator-runtime: 0.13.11
yargs: 16.2.0
transitivePeerDependencies:
@@ -6523,7 +6341,7 @@ packages:
/@types/accepts@1.3.5:
resolution: {integrity: sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/archiver@5.3.2:
@@ -6543,8 +6361,8 @@ packages:
/@types/babel__core@7.20.0:
resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==}
dependencies:
- '@babel/parser': 7.21.3
- '@babel/types': 7.21.3
+ '@babel/parser': 7.21.4
+ '@babel/types': 7.21.4
'@types/babel__generator': 7.6.4
'@types/babel__template': 7.4.1
'@types/babel__traverse': 7.18.3
@@ -6553,20 +6371,20 @@ packages:
/@types/babel__generator@7.6.4:
resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==}
dependencies:
- '@babel/types': 7.21.3
+ '@babel/types': 7.21.4
dev: true
/@types/babel__template@7.4.1:
resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==}
dependencies:
- '@babel/parser': 7.21.3
- '@babel/types': 7.21.3
+ '@babel/parser': 7.21.4
+ '@babel/types': 7.21.4
dev: true
/@types/babel__traverse@7.18.3:
resolution: {integrity: sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==}
dependencies:
- '@babel/types': 7.21.3
+ '@babel/types': 7.21.4
dev: true
/@types/bcryptjs@2.4.2:
@@ -6577,7 +6395,7 @@ packages:
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
dependencies:
'@types/connect': 3.4.35
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/braces@3.0.1:
@@ -6597,8 +6415,9 @@ packages:
dependencies:
'@types/http-cache-semantics': 4.0.1
'@types/keyv': 3.1.4
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
'@types/responselike': 1.0.0
+ dev: false
/@types/cbor@6.0.0:
resolution: {integrity: sha512-mGQ1lbYOwVti5Xlarn1bTeBZqgY0kstsdjnkoEovgohYKdBjGejHyNGXHdMBeqyQazIv32Jjp33+5pBEaSRy2w==}
@@ -6629,7 +6448,7 @@ packages:
/@types/connect@3.4.35:
resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/content-disposition@0.5.5:
@@ -6664,7 +6483,7 @@ packages:
/@types/emscripten@1.39.6:
resolution: {integrity: sha512-H90aoynNhhkQP6DRweEjJp5vfUVdIj7tdPLsu7pq89vODD/lcugKfZOsfgwpvM6XUewEp2N5dCg1Uf3Qe55Dcg==}
- dev: true
+ dev: false
/@types/escape-regexp@0.0.1:
resolution: {integrity: sha512-ogj/ZTIdeFkiuxDwawYuZSIgC6suFGgBeZPr6Xs5lHEcvIXTjXGtH+/n8f1XhZhespaUwJ5LIGRICPji972FLw==}
@@ -6677,7 +6496,7 @@ packages:
/@types/eslint@7.29.0:
resolution: {integrity: sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==}
dependencies:
- '@types/estree': 1.0.0
+ '@types/estree': 1.0.1
'@types/json-schema': 7.0.11
dev: true
@@ -6685,8 +6504,8 @@ packages:
resolution: {integrity: sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==}
dev: true
- /@types/estree@1.0.0:
- resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==}
+ /@types/estree@1.0.1:
+ resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==}
/@types/expect@1.20.4:
resolution: {integrity: sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==}
@@ -6694,7 +6513,7 @@ packages:
/@types/express-serve-static-core@4.17.33:
resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
'@types/qs': 6.9.7
'@types/range-parser': 1.2.4
dev: true
@@ -6715,34 +6534,34 @@ packages:
/@types/fluent-ffmpeg@2.1.21:
resolution: {integrity: sha512-+n3dy/Tegt6n+YwGZUiGq6i8Jrnt8+MoyPiW1L6J5EWUl7GSt18a/VyReecfCsvTTNBXNMIKOMHDstiQM8nJLA==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/glob-stream@6.1.1:
resolution: {integrity: sha512-AGOUTsTdbPkRS0qDeyeS+6KypmfVpbT5j23SN8UPG63qjKXNKjXn6V9wZUr8Fin0m9l8oGYaPK8b2WUMF8xI1A==}
dependencies:
'@types/glob': 8.1.0
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/glob@7.2.0:
resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
dependencies:
'@types/minimatch': 5.1.2
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/glob@8.1.0:
resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==}
dependencies:
'@types/minimatch': 5.1.2
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/graceful-fs@4.1.6:
resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/gulp-rename@2.0.1:
@@ -6769,12 +6588,6 @@ packages:
/@types/http-cache-semantics@4.0.1:
resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==}
- /@types/ioredis@4.28.10:
- resolution: {integrity: sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==}
- dependencies:
- '@types/node': 18.15.11
- dev: true
-
/@types/istanbul-lib-coverage@2.0.4:
resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==}
dev: true
@@ -6791,8 +6604,8 @@ packages:
'@types/istanbul-lib-report': 3.0.0
dev: true
- /@types/jest@29.5.0:
- resolution: {integrity: sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==}
+ /@types/jest@29.5.1:
+ resolution: {integrity: sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ==}
dependencies:
expect: 29.5.0
pretty-format: 29.5.0
@@ -6809,7 +6622,7 @@ packages:
/@types/jsdom@21.1.1:
resolution: {integrity: sha512-cZFuoVLtzKP3gmq9eNosUL1R50U+USkbLtUQ1bYVgl/lKp0FZM7Cq4aIHAL8oIvQ17uSHi7jXPtfDOdjPwBE7A==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
'@types/tough-cookie': 4.0.2
parse5: 7.1.2
dev: true
@@ -6833,7 +6646,8 @@ packages:
/@types/keyv@3.1.4:
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
+ dev: false
/@types/lodash@4.14.191:
resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==}
@@ -6851,8 +6665,8 @@ packages:
resolution: {integrity: sha512-IgHxcT3RC8LzFLhKwP3gbMPeaK7BM9eBH46OdapPA7yvuIUJ8H6zHZV53J8hGZcTSnt95jANt+rTBNUUc22ACQ==}
dev: true
- /@types/micromatch@3.1.1:
- resolution: {integrity: sha512-Wr5y4uv3r7JP4jEUqv7rZeYiMBGRHcbojDVsl11wq6gw1v/ZZQvJexd9rtvVx3EIVqw8dwtcRjSs8m2DV9qHjQ==}
+ /@types/micromatch@4.0.2:
+ resolution: {integrity: sha512-oqXqVb0ci19GtH0vOA/U2TmHTcRY9kuZl4mqUxe0QmJAlIW13kzhuK5pi1i9+ngav8FjpSb9FVS/GE00GLX1VA==}
dependencies:
'@types/braces': 3.0.1
dev: true
@@ -6880,7 +6694,7 @@ packages:
/@types/node-fetch@2.6.2:
resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
form-data: 3.0.1
/@types/node-fetch@3.0.3:
@@ -6891,7 +6705,7 @@ packages:
/@types/node@13.13.52:
resolution: {integrity: sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==}
- dev: true
+ dev: false
/@types/node@14.18.36:
resolution: {integrity: sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==}
@@ -6904,13 +6718,13 @@ packages:
/@types/node@18.11.18:
resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==}
- /@types/node@18.15.11:
- resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==}
+ /@types/node@18.16.3:
+ resolution: {integrity: sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q==}
/@types/nodemailer@6.4.7:
resolution: {integrity: sha512-f5qCBGAn/f0qtRcd4SEn88c8Fp3Swct1731X4ryPKqS61/A3LmmzN8zaEz7hneJvpjFbUUgY7lru/B/7ODTazg==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/normalize-package-data@2.4.1:
@@ -6924,7 +6738,7 @@ packages:
/@types/oauth@0.9.1:
resolution: {integrity: sha512-a1iY62/a3yhZ7qH7cNUsxoI3U/0Fe9+RnuFrpTKr+0WVOzbKlSLojShCKe20aOD1Sppv+i8Zlq0pLDuTJnwS4A==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/offscreencanvas@2019.3.0:
@@ -6938,7 +6752,7 @@ packages:
/@types/pg@8.6.6:
resolution: {integrity: sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
pg-protocol: 1.5.0
pg-types: 2.2.0
dev: true
@@ -6966,7 +6780,7 @@ packages:
/@types/qrcode@1.5.0:
resolution: {integrity: sha512-x5ilHXRxUPIMfjtM+1vf/GPTRWZ81nqscursm5gMznJeK9M0YnZ1c3bEvRLQ0zSSgedLx1J6MGL231ObQGGhaA==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/qs@6.9.7:
@@ -6996,7 +6810,7 @@ packages:
/@types/readdir-glob@1.1.1:
resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/redis@4.0.11:
@@ -7012,7 +6826,8 @@ packages:
/@types/responselike@1.0.0:
resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
+ dev: false
/@types/sanitize-html@2.9.0:
resolution: {integrity: sha512-4fP/kEcKNj2u39IzrxWYuf/FnCCwwQCpif6wwY6ROUS1EPRIfWJjGkY3HIowY1EX/VbX5e86yq8AAE7UPMgATg==}
@@ -7034,13 +6849,12 @@ packages:
/@types/semver@7.3.13:
resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==}
- dev: true
/@types/serve-static@1.15.1:
resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==}
dependencies:
'@types/mime': 3.0.1
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/serviceworker@0.0.67:
@@ -7050,13 +6864,14 @@ packages:
/@types/set-cookie-parser@2.4.2:
resolution: {integrity: sha512-fBZgytwhYAUkj/jC/FAV4RQ5EerRup1YQsXQCh8rZfiHkc4UahC192oH0smGwsXol3cL3A5oETuAHeQHmhXM4w==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
- /@types/sharp@0.31.1:
- resolution: {integrity: sha512-5nWwamN9ZFHXaYEincMSuza8nNfOof8nmO+mcI+Agx1uMUk4/pQnNIcix+9rLPXzKrm1pS34+6WRDbDV0Jn7ag==}
+ /@types/sharp@0.32.0:
+ resolution: {integrity: sha512-OOi3kL+FZDnPhVzsfD37J88FNeZh6gQsGcLc95NbeURRGvmSjeXiDcyWzF2o3yh/gQAUn2uhh/e+CPCa5nwAxw==}
+ deprecated: This is a stub types definition. sharp provides its own type definitions, so you do not need this installed.
dependencies:
- '@types/node': 18.15.11
+ sharp: 0.32.1
dev: true
/@types/sinon@10.0.13:
@@ -7084,7 +6899,7 @@ packages:
/@types/testing-library__jest-dom@5.14.5:
resolution: {integrity: sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==}
dependencies:
- '@types/jest': 29.5.0
+ '@types/jest': 29.5.1
dev: true
/@types/throttle-debounce@5.0.0:
@@ -7105,7 +6920,7 @@ packages:
/@types/treeify@1.0.0:
resolution: {integrity: sha512-ONpcZAEYlbPx4EtJwfTyCDQJGUpKf4sEcuySdCVjK5Fj/3vHp5HII1fqa1/+qrsLnpYELCQTfVW/awsGJePoIg==}
- dev: true
+ dev: false
/@types/undertaker-registry@1.0.1:
resolution: {integrity: sha512-Z4TYuEKn9+RbNVk1Ll2SS4x1JeLHecolIbM/a8gveaHsW0Hr+RQMraZACwTO2VD7JvepgA6UO1A1VrbktQrIbQ==}
@@ -7114,7 +6929,7 @@ packages:
/@types/undertaker@1.2.8:
resolution: {integrity: sha512-gW3PRqCHYpo45XFQHJBhch7L6hytPsIe0QeLujlnFsjHPnXLhJcPdN6a9368d7aIQgH2I/dUTPFBlGeSNA3qOg==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
'@types/undertaker-registry': 1.0.1
async-done: 1.3.2
dev: true
@@ -7126,7 +6941,7 @@ packages:
/@types/unzipper@0.10.5:
resolution: {integrity: sha512-NrLJb29AdnBARpg9S/4ktfPEisbJ0AvaaAr3j7Q1tg8AgcEUsq2HqbNzvgLRoWyRtjzeLEv7vuL39u1mrNIyNA==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/uuid@9.0.1:
@@ -7136,14 +6951,14 @@ packages:
/@types/vary@1.1.0:
resolution: {integrity: sha512-LQWqrIa0dvEOOH37lGksMEXbypRLUFqu6Gx0pmX7zIUisD2I/qaVgEX/vJ/PSVSW0Hk6yz1BNkFpqg6dZm3Wug==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/vinyl-fs@2.4.12:
resolution: {integrity: sha512-LgBpYIWuuGsihnlF+OOWWz4ovwCYlT03gd3DuLwex50cYZLmX3yrW+sFF9ndtmh7zcZpS6Ri47PrIu+fV+sbXw==}
dependencies:
'@types/glob-stream': 6.1.1
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
'@types/vinyl': 2.0.7
dev: true
@@ -7151,12 +6966,12 @@ packages:
resolution: {integrity: sha512-4UqPv+2567NhMQuMLdKAyK4yzrfCqwaTt6bLhHEs8PFcxbHILsrxaY63n4wgE/BRLDWDQeI+WcTmkXKExh9hQg==}
dependencies:
'@types/expect': 1.20.4
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
/@types/web-push@3.3.2:
resolution: {integrity: sha512-JxWGVL/m7mWTIg4mRYO+A6s0jPmBkr4iJr39DqJpRJAc+jrPiEe1/asmkwerzRon8ZZDxaZJpsxpv0Z18Wo9gw==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/webgl-ext@0.0.30:
@@ -7165,18 +6980,18 @@ packages:
/@types/webpack-env@1.18.0:
resolution: {integrity: sha512-56/MAlX5WMsPVbOg7tAxnYvNYMMWr/QJiIp6BxVSW3JJXUVzzOn64qW8TzQyMSqSUFM2+PVI4aUHcHOzIz/1tg==}
- dev: true
+ dev: false
/@types/websocket@1.0.5:
resolution: {integrity: sha512-NbsqiNX9CnEfC1Z0Vf4mE1SgAJ07JnRYcNex7AJ9zAVzmiGHmjKFEk7O4TJIsgv2B1sLEb6owKFZrACwdYngsQ==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/ws@8.5.4:
resolution: {integrity: sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/@types/yargs-parser@21.0.0:
@@ -7199,12 +7014,12 @@ packages:
resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
requiresBuild: true
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
optional: true
- /@typescript-eslint/eslint-plugin@5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.37.0)(typescript@5.0.3):
- resolution: {integrity: sha512-1MeobQkQ9tztuleT3v72XmY0XuKXVXusAhryoLuU5YZ+mXoYKZP9SQ7Flulh1NX4DTjpGTc2b/eMu4u7M7dhnQ==}
+ /@typescript-eslint/eslint-plugin@5.59.2(@typescript-eslint/parser@5.59.2)(eslint@8.39.0)(typescript@5.0.4):
+ resolution: {integrity: sha512-yVrXupeHjRxLDcPKL10sGQ/QlVrA8J5IYOEWVqk0lJaSZP7X5DfnP7Ns3cc74/blmbipQ1htFNVGsHX6wsYm0A==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
'@typescript-eslint/parser': ^5.0.0
@@ -7215,24 +7030,24 @@ packages:
optional: true
dependencies:
'@eslint-community/regexpp': 4.5.0
- '@typescript-eslint/parser': 5.57.1(eslint@8.37.0)(typescript@5.0.3)
- '@typescript-eslint/scope-manager': 5.57.1
- '@typescript-eslint/type-utils': 5.57.1(eslint@8.37.0)(typescript@5.0.3)
- '@typescript-eslint/utils': 5.57.1(eslint@8.37.0)(typescript@5.0.3)
+ '@typescript-eslint/parser': 5.59.2(eslint@8.39.0)(typescript@5.0.4)
+ '@typescript-eslint/scope-manager': 5.59.2
+ '@typescript-eslint/type-utils': 5.59.2(eslint@8.39.0)(typescript@5.0.4)
+ '@typescript-eslint/utils': 5.59.2(eslint@8.39.0)(typescript@5.0.4)
debug: 4.3.4(supports-color@8.1.1)
- eslint: 8.37.0
+ eslint: 8.39.0
grapheme-splitter: 1.0.4
ignore: 5.2.4
natural-compare-lite: 1.4.0
- semver: 7.3.8
- tsutils: 3.21.0(typescript@5.0.3)
- typescript: 5.0.3
+ semver: 7.5.0
+ tsutils: 3.21.0(typescript@5.0.4)
+ typescript: 5.0.4
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/parser@5.57.1(eslint@8.37.0)(typescript@5.0.3):
- resolution: {integrity: sha512-hlA0BLeVSA/wBPKdPGxoVr9Pp6GutGoY380FEhbVi0Ph4WNe8kLvqIRx76RSQt1lynZKfrXKs0/XeEk4zZycuA==}
+ /@typescript-eslint/parser@5.59.2(eslint@8.39.0)(typescript@5.0.4):
+ resolution: {integrity: sha512-uq0sKyw6ao1iFOZZGk9F8Nro/8+gfB5ezl1cA06SrqbgJAt0SRoFhb9pXaHvkrxUpZaoLxt8KlovHNk8Gp6/HQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
@@ -7241,26 +7056,26 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/scope-manager': 5.57.1
- '@typescript-eslint/types': 5.57.1
- '@typescript-eslint/typescript-estree': 5.57.1(typescript@5.0.3)
+ '@typescript-eslint/scope-manager': 5.59.2
+ '@typescript-eslint/types': 5.59.2
+ '@typescript-eslint/typescript-estree': 5.59.2(typescript@5.0.4)
debug: 4.3.4(supports-color@8.1.1)
- eslint: 8.37.0
- typescript: 5.0.3
+ eslint: 8.39.0
+ typescript: 5.0.4
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/scope-manager@5.57.1:
- resolution: {integrity: sha512-N/RrBwEUKMIYxSKl0oDK5sFVHd6VI7p9K5MyUlVYAY6dyNb/wHUqndkTd3XhpGlXgnQsBkRZuu4f9kAHghvgPw==}
+ /@typescript-eslint/scope-manager@5.59.2:
+ resolution: {integrity: sha512-dB1v7ROySwQWKqQ8rEWcdbTsFjh2G0vn8KUyvTXdPoyzSL6lLGkiXEV5CvpJsEe9xIdKV+8Zqb7wif2issoOFA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
- '@typescript-eslint/types': 5.57.1
- '@typescript-eslint/visitor-keys': 5.57.1
+ '@typescript-eslint/types': 5.59.2
+ '@typescript-eslint/visitor-keys': 5.59.2
dev: true
- /@typescript-eslint/type-utils@5.57.1(eslint@8.37.0)(typescript@5.0.3):
- resolution: {integrity: sha512-/RIPQyx60Pt6ga86hKXesXkJ2WOS4UemFrmmq/7eOyiYjYv/MUSHPlkhU6k9T9W1ytnTJueqASW+wOmW4KrViw==}
+ /@typescript-eslint/type-utils@5.59.2(eslint@8.39.0)(typescript@5.0.4):
+ resolution: {integrity: sha512-b1LS2phBOsEy/T381bxkkywfQXkV1dWda/z0PhnIy3bC5+rQWQDS7fk9CSpcXBccPY27Z6vBEuaPBCKCgYezyQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: '*'
@@ -7269,23 +7084,23 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/typescript-estree': 5.57.1(typescript@5.0.3)
- '@typescript-eslint/utils': 5.57.1(eslint@8.37.0)(typescript@5.0.3)
+ '@typescript-eslint/typescript-estree': 5.59.2(typescript@5.0.4)
+ '@typescript-eslint/utils': 5.59.2(eslint@8.39.0)(typescript@5.0.4)
debug: 4.3.4(supports-color@8.1.1)
- eslint: 8.37.0
- tsutils: 3.21.0(typescript@5.0.3)
- typescript: 5.0.3
+ eslint: 8.39.0
+ tsutils: 3.21.0(typescript@5.0.4)
+ typescript: 5.0.4
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/types@5.57.1:
- resolution: {integrity: sha512-bSs4LOgyV3bJ08F5RDqO2KXqg3WAdwHCu06zOqcQ6vqbTJizyBhuh1o1ImC69X4bV2g1OJxbH71PJqiO7Y1RuA==}
+ /@typescript-eslint/types@5.59.2:
+ resolution: {integrity: sha512-LbJ/HqoVs2XTGq5shkiKaNTuVv5tTejdHgfdjqRUGdYhjW1crm/M7og2jhVskMt8/4wS3T1+PfFvL1K3wqYj4w==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
- /@typescript-eslint/typescript-estree@5.57.1(typescript@5.0.3):
- resolution: {integrity: sha512-A2MZqD8gNT0qHKbk2wRspg7cHbCDCk2tcqt6ScCFLr5Ru8cn+TCfM786DjPhqwseiS+PrYwcXht5ztpEQ6TFTw==}
+ /@typescript-eslint/typescript-estree@5.59.2(typescript@5.0.4):
+ resolution: {integrity: sha512-+j4SmbwVmZsQ9jEyBMgpuBD0rKwi9RxRpjX71Brr73RsYnEr3Lt5QZ624Bxphp8HUkSKfqGnPJp1kA5nl0Sh7Q==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
typescript: '*'
@@ -7293,47 +7108,47 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/types': 5.57.1
- '@typescript-eslint/visitor-keys': 5.57.1
+ '@typescript-eslint/types': 5.59.2
+ '@typescript-eslint/visitor-keys': 5.59.2
debug: 4.3.4(supports-color@8.1.1)
globby: 11.1.0
is-glob: 4.0.3
- semver: 7.3.8
- tsutils: 3.21.0(typescript@5.0.3)
- typescript: 5.0.3
+ semver: 7.5.0
+ tsutils: 3.21.0(typescript@5.0.4)
+ typescript: 5.0.4
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/utils@5.57.1(eslint@8.37.0)(typescript@5.0.3):
- resolution: {integrity: sha512-kN6vzzf9NkEtawECqze6v99LtmDiUJCVpvieTFA1uL7/jDghiJGubGZ5csicYHU1Xoqb3oH/R5cN5df6W41Nfg==}
+ /@typescript-eslint/utils@5.59.2(eslint@8.39.0)(typescript@5.0.4):
+ resolution: {integrity: sha512-kSuF6/77TZzyGPhGO4uVp+f0SBoYxCDf+lW3GKhtKru/L8k/Hd7NFQxyWUeY7Z/KGB2C6Fe3yf2vVi4V9TsCSQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.37.0)
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.39.0)
'@types/json-schema': 7.0.11
'@types/semver': 7.3.13
- '@typescript-eslint/scope-manager': 5.57.1
- '@typescript-eslint/types': 5.57.1
- '@typescript-eslint/typescript-estree': 5.57.1(typescript@5.0.3)
- eslint: 8.37.0
+ '@typescript-eslint/scope-manager': 5.59.2
+ '@typescript-eslint/types': 5.59.2
+ '@typescript-eslint/typescript-estree': 5.59.2(typescript@5.0.4)
+ eslint: 8.39.0
eslint-scope: 5.1.1
- semver: 7.3.8
+ semver: 7.5.0
transitivePeerDependencies:
- supports-color
- typescript
dev: true
- /@typescript-eslint/visitor-keys@5.57.1:
- resolution: {integrity: sha512-RjQrAniDU0CEk5r7iphkm731zKlFiUjvcBS2yHAg8WWqFMCaCrD0rKEVOMUyMMcbGPZ0bPp56srkGWrgfZqLRA==}
+ /@typescript-eslint/visitor-keys@5.59.2:
+ resolution: {integrity: sha512-EEpsO8m3RASrKAHI9jpavNv9NlEUebV4qmF1OWxSTtKSFBpC1NCmWazDQHFivRf0O1DV11BA645yrLEVQ0/Lig==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
- '@typescript-eslint/types': 5.57.1
+ '@typescript-eslint/types': 5.59.2
eslint-visitor-keys: 3.4.0
dev: true
- /@vitejs/plugin-react@3.1.0(vite@4.2.1):
+ /@vitejs/plugin-react@3.1.0(vite@4.3.4):
resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
@@ -7344,121 +7159,203 @@ packages:
'@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.21.3)
magic-string: 0.27.0
react-refresh: 0.14.0
- vite: 4.2.1(@types/node@18.15.11)(sass@1.60.0)
+ vite: 4.3.4(@types/node@18.16.3)(sass@1.62.1)
transitivePeerDependencies:
- supports-color
dev: true
- /@vitejs/plugin-vue@4.1.0(vite@4.2.1)(vue@3.2.47):
- resolution: {integrity: sha512-++9JOAFdcXI3lyer9UKUV4rfoQ3T1RN8yDqoCLar86s0xQct5yblxAE+yWgRnU5/0FOlVCpTZpYSBV/bGWrSrQ==}
+ /@vitejs/plugin-vue@4.2.1(vite@4.3.4)(vue@3.2.47):
+ resolution: {integrity: sha512-ZTZjzo7bmxTRTkb8GSTwkPOYDIP7pwuyV+RV53c9PYUouwcbkIZIvWvNWlX2b1dYZqtOv7D6iUAnJLVNGcLrSw==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
vite: ^4.0.0
vue: ^3.2.25
dependencies:
- vite: 4.2.1(@types/node@18.15.11)(sass@1.60.0)
+ vite: 4.3.4(@types/node@18.16.3)(sass@1.62.1)
vue: 3.2.47
- /@vitest/coverage-c8@0.29.8(vitest@0.29.8):
- resolution: {integrity: sha512-y+sEMQMctWokjnSqm3FCQEYFkjLrYaznsxEZHxcx8z2aftpYg3A5tvI1S5himfdEFo7o+OeHzh40bPSWZHW4oQ==}
+ /@vitest/coverage-c8@0.30.1(vitest@0.30.1):
+ resolution: {integrity: sha512-/Wa3dtSuckpdngAmiCwowaEXXgJkqPrtfvrs9HTB9QoEfNbZWPu4E4cjEn4lJZb4qcGf4fxFtUA2f9DnDNAzBA==}
peerDependencies:
- vitest: '>=0.29.0 <1'
+ vitest: '>=0.30.0 <1'
dependencies:
c8: 7.13.0
picocolors: 1.0.0
std-env: 3.3.2
- vitest: 0.29.8(happy-dom@8.9.0)(sass@1.60.0)
+ vitest: 0.30.1(happy-dom@9.10.2)(sass@1.62.1)
dev: true
- /@vitest/expect@0.29.8:
- resolution: {integrity: sha512-xlcVXn5I5oTq6NiZSY3ykyWixBxr5mG8HYtjvpgg6KaqHm0mvhX18xuwl5YGxIRNt/A5jidd7CWcNHrSvgaQqQ==}
+ /@vitest/expect@0.30.1:
+ resolution: {integrity: sha512-c3kbEtN8XXJSeN81iDGq29bUzSjQhjES2WR3aColsS4lPGbivwLtas4DNUe0jD9gg/FYGIteqOenfU95EFituw==}
dependencies:
- '@vitest/spy': 0.29.8
- '@vitest/utils': 0.29.8
+ '@vitest/spy': 0.30.1
+ '@vitest/utils': 0.30.1
chai: 4.3.7
dev: true
- /@vitest/runner@0.29.8:
- resolution: {integrity: sha512-FzdhnRDwEr/A3Oo1jtIk/B952BBvP32n1ObMEb23oEJNO+qO5cBet6M2XWIDQmA7BDKGKvmhUf2naXyp/2JEwQ==}
+ /@vitest/runner@0.30.1:
+ resolution: {integrity: sha512-W62kT/8i0TF1UBCNMRtRMOBWJKRnNyv9RrjIgdUryEe0wNpGZvvwPDLuzYdxvgSckzjp54DSpv1xUbv4BQ0qVA==}
dependencies:
- '@vitest/utils': 0.29.8
+ '@vitest/utils': 0.30.1
+ concordance: 5.0.4
p-limit: 4.0.0
pathe: 1.1.0
dev: true
- /@vitest/spy@0.29.8:
- resolution: {integrity: sha512-VdjBe9w34vOMl5I5mYEzNX8inTxrZ+tYUVk9jxaZJmHFwmDFC/GV3KBFTA/JKswr3XHvZL+FE/yq5EVhb6pSAw==}
+ /@vitest/snapshot@0.30.1:
+ resolution: {integrity: sha512-fJZqKrE99zo27uoZA/azgWyWbFvM1rw2APS05yB0JaLwUIg9aUtvvnBf4q7JWhEcAHmSwbrxKFgyBUga6tq9Tw==}
dependencies:
- tinyspy: 1.1.1
+ magic-string: 0.30.0
+ pathe: 1.1.0
+ pretty-format: 27.5.1
dev: true
- /@vitest/utils@0.29.8:
- resolution: {integrity: sha512-qGzuf3vrTbnoY+RjjVVIBYfuWMjn3UMUqyQtdGNZ6ZIIyte7B37exj6LaVkrZiUTvzSadVvO/tJm8AEgbGCBPg==}
+ /@vitest/spy@0.30.1:
+ resolution: {integrity: sha512-YfJeIf37GvTZe04ZKxzJfnNNuNSmTEGnla2OdL60C8od16f3zOfv9q9K0nNii0NfjDJRt/CVN/POuY5/zTS+BA==}
dependencies:
- cli-truncate: 3.1.0
- diff: 5.1.0
+ tinyspy: 2.1.0
+ dev: true
+
+ /@vitest/utils@0.30.1:
+ resolution: {integrity: sha512-/c8Xv2zUVc+rnNt84QF0Y0zkfxnaGhp87K2dYJMLtLOIckPzuxLVzAtFCicGFdB4NeBHNzTRr1tNn7rCtQcWFA==}
+ dependencies:
+ concordance: 5.0.4
loupe: 2.3.6
pretty-format: 27.5.1
dev: true
- /@volar/language-core@1.3.0-alpha.0:
- resolution: {integrity: sha512-W3uMzecHPcbwddPu4SJpUcPakRBK/y/BP+U0U6NiPpUX1tONLC4yCawt+QBJqtgJ+sfD6ztf5PyvPL3hQRqfOA==}
+ /@volar/language-core@1.4.1:
+ resolution: {integrity: sha512-EIY+Swv+TjsWpxOxujjMf1ZXqOjg9MT2VMXZ+1dKva0wD8W0L6EtptFFcCJdBbcKmGMFkr57Qzz9VNMWhs3jXQ==}
dependencies:
- '@volar/source-map': 1.3.0-alpha.0
+ '@volar/source-map': 1.4.1
dev: true
- /@volar/source-map@1.3.0-alpha.0:
- resolution: {integrity: sha512-jSdizxWFvDTvkPYZnO6ew3sBZUnS0abKCbuopkc0JrIlFbznWC/fPH3iPFIMS8/IIkRxq1Jh9VVG60SmtsdaMQ==}
+ /@volar/source-map@1.4.1:
+ resolution: {integrity: sha512-bZ46ad72dsbzuOWPUtJjBXkzSQzzSejuR3CT81+GvTEI2E994D8JPXzM3tl98zyCNnjgs4OkRyliImL1dvJ5BA==}
dependencies:
muggle-string: 0.2.2
dev: true
- /@volar/typescript@1.3.0-alpha.0:
- resolution: {integrity: sha512-5UItyW2cdH2mBLu4RrECRNJRgtvvzKrSCn2y3v/D61QwIDkGx4aeil6x8RFuUL5TFtV6QvVHXnsOHxNgd+sCow==}
+ /@volar/typescript@1.4.1(typescript@5.0.4):
+ resolution: {integrity: sha512-phTy6p9yG6bgMIKQWEeDOi/aeT0njZsb1a/G1mrEuDsLmAn24Le4gDwSsGNhea6Uhu+3gdpUZn2PmZXa+WG2iQ==}
+ peerDependencies:
+ typescript: '*'
dependencies:
- '@volar/language-core': 1.3.0-alpha.0
+ '@volar/language-core': 1.4.1
+ typescript: 5.0.4
dev: true
- /@volar/vue-language-core@1.2.0:
- resolution: {integrity: sha512-w7yEiaITh2WzKe6u8ZdeLKCUz43wdmY/OqAmsB/PGDvvhTcVhCJ6f0W/RprZL1IhqH8wALoWiwEh/Wer7ZviMQ==}
+ /@volar/vue-language-core@1.6.3:
+ resolution: {integrity: sha512-e9OTDCPa8Wuh0ORhD4z++qTIcrsrqcI9waspr93YcQCq6j+Q+JTFuy7HBSQgyezSAsP6x1WWokKVk4fWWDJQOw==}
dependencies:
- '@volar/language-core': 1.3.0-alpha.0
- '@volar/source-map': 1.3.0-alpha.0
- '@vue/compiler-dom': 3.2.47
- '@vue/compiler-sfc': 3.2.47
- '@vue/reactivity': 3.2.47
- '@vue/shared': 3.2.47
- minimatch: 6.2.0
+ '@volar/language-core': 1.4.1
+ '@volar/source-map': 1.4.1
+ '@vue/compiler-dom': 3.3.0-beta.3
+ '@vue/compiler-sfc': 3.3.0-beta.3
+ '@vue/reactivity': 3.3.0-beta.3
+ '@vue/shared': 3.3.0-beta.3
+ minimatch: 9.0.0
muggle-string: 0.2.2
vue-template-compiler: 2.7.14
dev: true
- /@volar/vue-typescript@1.2.0:
- resolution: {integrity: sha512-zjmRi9y3J1EkG+pfuHp8IbHmibihrKK485cfzsHjiuvJMGrpkWvlO5WVEk8oslMxxeGC5XwBFE9AOlvh378EPA==}
+ /@volar/vue-typescript@1.6.3(typescript@5.0.4):
+ resolution: {integrity: sha512-Dz29Qym33P1MSZDTZJ6PPLN1TLQfkX+g2pRnHqLCsFdSUu4yWYnElBURCn5WJkekxV/v+k2T43aur2RCSY3Ovg==}
+ peerDependencies:
+ typescript: '*'
dependencies:
- '@volar/typescript': 1.3.0-alpha.0
- '@volar/vue-language-core': 1.2.0
+ '@volar/typescript': 1.4.1(typescript@5.0.4)
+ '@volar/vue-language-core': 1.6.3
+ typescript: 5.0.4
dev: true
+ /@vue-macros/common@1.3.0(rollup@3.21.3)(vue@3.2.47):
+ resolution: {integrity: sha512-oRK9vdKryXtJbfucRla8XdnQiWVVNHEBid0waacdfMJn+LOunWeU/3k8VoZZc328HmmZj69MGkUoMWixsHCHGg==}
+ engines: {node: '>=14.19.0'}
+ peerDependencies:
+ vue: ^2.7.0 || ^3.2.25
+ peerDependenciesMeta:
+ vue:
+ optional: true
+ dependencies:
+ '@babel/types': 7.21.4
+ '@rollup/pluginutils': 5.0.2(rollup@3.21.3)
+ '@vue/compiler-sfc': 3.3.0-beta.2
+ local-pkg: 0.4.3
+ magic-string-ast: 0.1.2
+ vue: 3.2.47
+ transitivePeerDependencies:
+ - rollup
+ dev: false
+
+ /@vue-macros/reactivity-transform@0.3.5(rollup@3.21.3)(vue@3.2.47):
+ resolution: {integrity: sha512-HDWPMytAp32uC4aXuLITsBkxGI8yppmthGSSYJENXPvovnIctGV7q6mMNkr9cJMjyr6pjE1rv0y0Vc7SUhx/Xw==}
+ engines: {node: '>=14.19.0'}
+ peerDependencies:
+ vue: ^2.7.0 || ^3.2.25
+ dependencies:
+ '@babel/parser': 7.21.4
+ '@vue-macros/common': 1.3.0(rollup@3.21.3)(vue@3.2.47)
+ '@vue/compiler-core': 3.3.0-beta.2
+ '@vue/shared': 3.3.0-beta.2
+ magic-string: 0.30.0
+ unplugin: 1.3.1
+ vue: 3.2.47
+ transitivePeerDependencies:
+ - rollup
+ dev: false
+
/@vue/compiler-core@3.2.47:
resolution: {integrity: sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==}
dependencies:
- '@babel/parser': 7.21.3
+ '@babel/parser': 7.21.4
'@vue/shared': 3.2.47
estree-walker: 2.0.2
source-map: 0.6.1
+ /@vue/compiler-core@3.3.0-beta.2:
+ resolution: {integrity: sha512-Z2VZCL9Rr1gVgyALHIRP+lNFjgfs/K4aTxvJYQ2vhgEAaI0/L6wtG5sr/gOP+MgxwGQV0PvA+iDG3Y3PC7rTEg==}
+ dependencies:
+ '@babel/parser': 7.21.4
+ '@vue/shared': 3.3.0-beta.2
+ estree-walker: 2.0.2
+ source-map-js: 1.0.2
+ dev: false
+
+ /@vue/compiler-core@3.3.0-beta.3:
+ resolution: {integrity: sha512-mv2rPo4JHou6ebm7+U/wO1HpA6W1zDfTqbt4fqjoXrMwU4DWNgRcLKTXG6G3cXV4mOe+2YgWspfxEzo7fPTMKg==}
+ dependencies:
+ '@babel/parser': 7.21.4
+ '@vue/shared': 3.3.0-beta.3
+ estree-walker: 2.0.2
+ source-map-js: 1.0.2
+ dev: true
+
/@vue/compiler-dom@3.2.47:
resolution: {integrity: sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==}
dependencies:
'@vue/compiler-core': 3.2.47
'@vue/shared': 3.2.47
+ /@vue/compiler-dom@3.3.0-beta.2:
+ resolution: {integrity: sha512-9LPRdCj66OwmUiPa9nuKiaoyKxlFT56j+io8nK/aW5OLl1UkY//Lj661fmDkTY20oLmArt73fAuHD913w4hRqA==}
+ dependencies:
+ '@vue/compiler-core': 3.3.0-beta.2
+ '@vue/shared': 3.3.0-beta.2
+ dev: false
+
+ /@vue/compiler-dom@3.3.0-beta.3:
+ resolution: {integrity: sha512-e7VpjN9wYiuJdJos6Uoe501CzdMkfaEr/27Ks4Ss7Irtcj5YA/S1OROZ35Xl2Pc3ctx6beq5RpcOvnMqh0hcaA==}
+ dependencies:
+ '@vue/compiler-core': 3.3.0-beta.3
+ '@vue/shared': 3.3.0-beta.3
+ dev: true
+
/@vue/compiler-sfc@2.7.14:
resolution: {integrity: sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==}
dependencies:
- '@babel/parser': 7.21.3
- postcss: 8.4.21
+ '@babel/parser': 7.21.4
+ postcss: 8.4.23
source-map: 0.6.1
dev: false
@@ -7476,26 +7373,96 @@ packages:
postcss: 8.4.21
source-map: 0.6.1
+ /@vue/compiler-sfc@3.3.0-beta.2:
+ resolution: {integrity: sha512-5FmcQ5LIpM/Y22dTxnxWPD04jC2gr6XSVVqQNY0y776F1P9x4f06fIpMibL58aKU07Th2z4Ab3oPg/Cg1QNVmA==}
+ dependencies:
+ '@babel/parser': 7.21.4
+ '@vue/compiler-core': 3.3.0-beta.2
+ '@vue/compiler-dom': 3.3.0-beta.2
+ '@vue/compiler-ssr': 3.3.0-beta.2
+ '@vue/reactivity-transform': 3.3.0-beta.2
+ '@vue/shared': 3.3.0-beta.2
+ estree-walker: 2.0.2
+ magic-string: 0.30.0
+ postcss: 8.4.21
+ source-map-js: 1.0.2
+ dev: false
+
+ /@vue/compiler-sfc@3.3.0-beta.3:
+ resolution: {integrity: sha512-6shZNooetShjSMHJvgVoE0EM8pOMV5vnrzsHoCU06stzV+kqRJQpbN7xf2s9wK2fgHMIBSMINrM9AuZiQnNCJg==}
+ dependencies:
+ '@babel/parser': 7.21.4
+ '@vue/compiler-core': 3.3.0-beta.3
+ '@vue/compiler-dom': 3.3.0-beta.3
+ '@vue/compiler-ssr': 3.3.0-beta.3
+ '@vue/reactivity-transform': 3.3.0-beta.3
+ '@vue/shared': 3.3.0-beta.3
+ estree-walker: 2.0.2
+ magic-string: 0.30.0
+ postcss: 8.4.23
+ source-map-js: 1.0.2
+ dev: true
+
/@vue/compiler-ssr@3.2.47:
resolution: {integrity: sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==}
dependencies:
'@vue/compiler-dom': 3.2.47
'@vue/shared': 3.2.47
+ /@vue/compiler-ssr@3.3.0-beta.2:
+ resolution: {integrity: sha512-Xg9od6GvHwfEpnTxMQR+KlKG1nbOHWRLHCiSA0FENiSDTjCDHh0ClzZLhIZUZJD75miyE9ia5ZQF6vpw680rCw==}
+ dependencies:
+ '@vue/compiler-dom': 3.3.0-beta.2
+ '@vue/shared': 3.3.0-beta.2
+ dev: false
+
+ /@vue/compiler-ssr@3.3.0-beta.3:
+ resolution: {integrity: sha512-egJ0lEVAod3Hpnw96cJ/0a9qv5f5h5/VCBpKYT8scqkzoMsikh8AJant2omokBCL/Ut5UAMLVQlA5b66+2Ys/g==}
+ dependencies:
+ '@vue/compiler-dom': 3.3.0-beta.3
+ '@vue/shared': 3.3.0-beta.3
+ dev: true
+
/@vue/reactivity-transform@3.2.47:
resolution: {integrity: sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==}
dependencies:
- '@babel/parser': 7.21.3
+ '@babel/parser': 7.21.4
'@vue/compiler-core': 3.2.47
'@vue/shared': 3.2.47
estree-walker: 2.0.2
magic-string: 0.25.9
+ /@vue/reactivity-transform@3.3.0-beta.2:
+ resolution: {integrity: sha512-EUL53/rsd+hrqhCa/SrhXQ6PzMZJfLQt39xQlzr0Sxsdv/bg5lqbcK9YtGkjYohRuSp1QneFU78LEsQ9j4B2Dw==}
+ dependencies:
+ '@babel/parser': 7.21.4
+ '@vue/compiler-core': 3.3.0-beta.2
+ '@vue/shared': 3.3.0-beta.2
+ estree-walker: 2.0.2
+ magic-string: 0.30.0
+ dev: false
+
+ /@vue/reactivity-transform@3.3.0-beta.3:
+ resolution: {integrity: sha512-aM3TgBca9QMMu/9B9ASRVvckeZpAdJO9nmQh5UCznhoDYjVxQPS+sCQvH6TLOjPB1MDQMVQYg4ZiPqfVVo7NbA==}
+ dependencies:
+ '@babel/parser': 7.21.4
+ '@vue/compiler-core': 3.3.0-beta.3
+ '@vue/shared': 3.3.0-beta.3
+ estree-walker: 2.0.2
+ magic-string: 0.30.0
+ dev: true
+
/@vue/reactivity@3.2.47:
resolution: {integrity: sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==}
dependencies:
'@vue/shared': 3.2.47
+ /@vue/reactivity@3.3.0-beta.3:
+ resolution: {integrity: sha512-9VjWfWgZJ18YXEkfnDfZr33RyLBa6zc0RARLkMqMApWvM26eusZAZ4hhyxlgODBU/mEFk4XOGIAtwwSQedA0MQ==}
+ dependencies:
+ '@vue/shared': 3.3.0-beta.3
+ dev: true
+
/@vue/runtime-core@3.2.47:
resolution: {integrity: sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==}
dependencies:
@@ -7521,6 +7488,14 @@ packages:
/@vue/shared@3.2.47:
resolution: {integrity: sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==}
+ /@vue/shared@3.3.0-beta.2:
+ resolution: {integrity: sha512-AsHYKYiYUnL/LHog6iV/G9tctFZYOsaxHDbSnfeyip94rjndO46XSDbHek7wDlcj3NHGaf8jAQQKfva/7mypjA==}
+ dev: false
+
+ /@vue/shared@3.3.0-beta.3:
+ resolution: {integrity: sha512-st1SnB/Bkbb9TsieeI4TRX9TqHYIR5wvIma3ZtEben55EYSWa1q5u2BhTNgABSdH+rv3Xwfrvpwh5PmCw6Y53g==}
+ dev: true
+
/@vue/test-utils@2.3.2(vue@3.2.47):
resolution: {integrity: sha512-hJnVaYhbrIm0yBS0+e1Y0Sj85cMyAi+PAbK4JHqMRUZ6S622Goa+G7QzkRSyvCteG8wop7tipuEbHoZo26wsSA==}
peerDependencies:
@@ -7533,8 +7508,8 @@ packages:
'@vue/server-renderer': 3.2.47(vue@3.2.47)
dev: true
- /@webgpu/types@0.1.21:
- resolution: {integrity: sha512-pUrWq3V5PiSGFLeLxoGqReTZmiiXwY3jRkIG5sLLKjyqNxrwm/04b4nw7LSmGWJcKk59XOM/YRTUwOzo4MMlow==}
+ /@webgpu/types@0.1.30:
+ resolution: {integrity: sha512-9AXJSmL3MzY8ZL//JjudA//q+2kBRGhLBFpkdGksWIuxrMy81nFrCzj2Am+mbh8WoU6rXmv7cY5E3rdlyru2Qg==}
dev: false
/@xmldom/xmldom@0.8.6:
@@ -7571,21 +7546,21 @@ packages:
p-limit: 2.3.0
pluralize: 7.0.0
pretty-bytes: 5.6.0
- semver: 7.3.8
+ semver: 7.5.0
stream-to-promise: 2.2.0
tar-stream: 2.2.0
treeify: 1.1.0
tslib: 1.14.1
tunnel: 0.0.6
- dev: true
+ dev: false
- /@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.17.15):
+ /@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.17.18):
resolution: {integrity: sha512-kYzDJO5CA9sy+on/s2aIW0411AklfCi8Ck/4QDivOqsMKpStZA2SsR+X27VTggGwpStWaLrjJcDcdDMowtG8MA==}
engines: {node: '>=14.15.0'}
peerDependencies:
esbuild: '>=0.10.0'
dependencies:
- esbuild: 0.17.15
+ esbuild: 0.17.18
tslib: 2.5.0
dev: true
@@ -7595,7 +7570,7 @@ packages:
dependencies:
'@yarnpkg/libzip': 2.3.0
tslib: 1.14.1
- dev: true
+ dev: false
/@yarnpkg/json-proxy@2.1.1:
resolution: {integrity: sha512-meUiCAgCYpXTH1qJfqfz+dX013ohW9p2dKfwIzUYAFutH+lsz1eHPBIk72cuCV84adh9gX6j66ekBKH/bIhCQw==}
@@ -7603,7 +7578,7 @@ packages:
dependencies:
'@yarnpkg/fslib': 2.10.2
tslib: 1.14.1
- dev: true
+ dev: false
/@yarnpkg/libzip@2.3.0:
resolution: {integrity: sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg==}
@@ -7611,11 +7586,11 @@ packages:
dependencies:
'@types/emscripten': 1.39.6
tslib: 1.14.1
- dev: true
+ dev: false
/@yarnpkg/lockfile@1.1.0:
resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==}
- dev: true
+ dev: false
/@yarnpkg/parsers@2.5.1:
resolution: {integrity: sha512-KtYN6Ez3x753vPF9rETxNTPnPjeaHY11Exlpqb4eTII7WRlnGiZ5rvvQBau4R20Ik5KBv+vS3EJEcHyCunwzzw==}
@@ -7623,7 +7598,7 @@ packages:
dependencies:
js-yaml: 3.14.1
tslib: 1.14.1
- dev: true
+ dev: false
/@yarnpkg/pnp@2.3.2:
resolution: {integrity: sha512-JdwHu1WBCISqJEhIwx6Hbpe8MYsYbkGMxoxolkDiAeJ9IGEe08mQcbX1YmUDV1ozSWlm9JZE90nMylcDsXRFpA==}
@@ -7632,7 +7607,7 @@ packages:
'@types/node': 13.13.52
'@yarnpkg/fslib': 2.10.2
tslib: 1.14.1
- dev: true
+ dev: false
/@yarnpkg/shell@2.4.1:
resolution: {integrity: sha512-oNNJkH8ZI5uwu0dMkJf737yMSY1WXn9gp55DqSA5wAOhKvV5DJTXFETxkVgBQhO6Bow9tMGSpvowTMD/oAW/9g==}
@@ -7647,7 +7622,7 @@ packages:
micromatch: 4.0.5
stream-buffers: 3.0.2
tslib: 1.14.1
- dev: true
+ dev: false
/@zxing/text-encoding@0.9.0:
resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==}
@@ -7850,11 +7825,6 @@ packages:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
- /ansi-regex@6.0.1:
- resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
- engines: {node: '>=12'}
- dev: true
-
/ansi-styles@2.2.1:
resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==}
engines: {node: '>=0.10.0'}
@@ -7878,11 +7848,6 @@ packages:
engines: {node: '>=10'}
dev: true
- /ansi-styles@6.2.1:
- resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
- engines: {node: '>=12'}
- dev: true
-
/ansi-wrap@0.1.0:
resolution: {integrity: sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==}
engines: {node: '>=0.10.0'}
@@ -7890,6 +7855,7 @@ packages:
/any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
+ dev: false
/anymatch@2.0.0:
resolution: {integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==}
@@ -8480,7 +8446,7 @@ packages:
engines: {node: '>=12'}
dependencies:
bin-version: 6.0.0
- semver: 7.3.8
+ semver: 7.5.0
semver-truncate: 2.0.0
dev: false
@@ -8511,7 +8477,7 @@ packages:
/binjumper@0.1.4:
resolution: {integrity: sha512-Gdxhj+U295tIM6cO4bJO1jsvSjBVHNpj2o/OwW7pqDEtaqF6KdOxjtbo93jMMKAkP7+u09+bV8DhSqjIv4qR3w==}
engines: {node: '>=10.12.0'}
- dev: true
+ dev: false
/bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
@@ -8532,6 +8498,10 @@ packages:
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
dev: true
+ /blueimp-md5@2.19.0:
+ resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==}
+ dev: true
+
/blurhash@2.0.5:
resolution: {integrity: sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==}
dev: false
@@ -8733,10 +8703,10 @@ packages:
cron-parser: 4.7.1
debuglog: 1.0.1
get-port: 5.1.1
- ioredis: 5.2.4
+ ioredis: 5.3.2
lodash: 4.17.21
msgpackr: 1.8.1
- semver: 7.3.8
+ semver: 7.5.0
uuid: 8.3.2
transitivePeerDependencies:
- supports-color
@@ -8825,6 +8795,7 @@ packages:
/cacheable-lookup@5.0.4:
resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==}
engines: {node: '>=10.6.0'}
+ dev: false
/cacheable-lookup@6.1.0:
resolution: {integrity: sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==}
@@ -8858,6 +8829,7 @@ packages:
lowercase-keys: 2.0.0
normalize-url: 6.1.0
responselike: 2.0.1
+ dev: false
/cachedir@2.3.0:
resolution: {integrity: sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==}
@@ -8984,7 +8956,6 @@ packages:
dependencies:
ansi-styles: 4.3.0
supports-color: 7.2.0
- dev: true
/chalk@4.1.1:
resolution: {integrity: sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==}
@@ -9031,45 +9002,45 @@ packages:
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
dev: true
- /chart.js@4.2.1:
- resolution: {integrity: sha512-6YbpQ0nt3NovAgOzbkSSeeAQu/3za1319dPUQTXn9WcOpywM8rGKxJHrhS8V8xEkAlk8YhEfjbuAPfUyp6jIsw==}
- engines: {pnpm: ^7.0.0}
+ /chart.js@4.3.0:
+ resolution: {integrity: sha512-ynG0E79xGfMaV2xAHdbhwiPLczxnNNnasrmPEXriXsPJGjmhOBYzFVEsB65w2qMDz+CaBJJuJD0inE/ab/h36g==}
+ engines: {pnpm: '>=7'}
dependencies:
'@kurkle/color': 0.3.2
dev: false
- /chartjs-adapter-date-fns@3.0.0(chart.js@4.2.1)(date-fns@2.29.3):
+ /chartjs-adapter-date-fns@3.0.0(chart.js@4.3.0)(date-fns@2.30.0):
resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==}
peerDependencies:
chart.js: '>=2.8.0'
date-fns: '>=2.0.0'
dependencies:
- chart.js: 4.2.1
- date-fns: 2.29.3
+ chart.js: 4.3.0
+ date-fns: 2.30.0
dev: false
- /chartjs-chart-matrix@2.0.1(chart.js@4.2.1):
+ /chartjs-chart-matrix@2.0.1(chart.js@4.3.0):
resolution: {integrity: sha512-BGfeY+/PHnITyDlc7WfnKJ1RyOfgOzIqWp/gxzzl7pUjyoGzHDcw51qd2xJF9gdT9Def7ZwOnOMm8GJUXDxI0w==}
peerDependencies:
chart.js: '>=3.0.0'
dependencies:
- chart.js: 4.2.1
+ chart.js: 4.3.0
dev: false
- /chartjs-plugin-gradient@0.6.1(chart.js@4.2.1):
+ /chartjs-plugin-gradient@0.6.1(chart.js@4.3.0):
resolution: {integrity: sha512-TGHNIh8KqQMLdb+UfY80cBHYRyOC47eeokmgkeajRdKGbFt462lJiyiq4ZJ25fiM7BGsmzoBLhmVyEw4B3gQxw==}
peerDependencies:
chart.js: '>=2.6.0'
dependencies:
- chart.js: 4.2.1
+ chart.js: 4.3.0
dev: false
- /chartjs-plugin-zoom@2.0.1(chart.js@4.2.1):
+ /chartjs-plugin-zoom@2.0.1(chart.js@4.3.0):
resolution: {integrity: sha512-ogOmLu6e+Q7E1XWOCOz9YwybMslz9qNfGV2a+qjfmqJYpsw5ZMoRHZBUyW+NGhkpQ5PwwPA/+rikHpBZb7PZuA==}
peerDependencies:
chart.js: '>=3.2.0'
dependencies:
- chart.js: 4.2.1
+ chart.js: 4.3.0
hammerjs: 2.0.8
dev: false
@@ -9145,11 +9116,11 @@ packages:
snyk-nodejs-lockfile-parser: 1.48.2
transitivePeerDependencies:
- supports-color
- dev: true
+ dev: false
/ci-info@2.0.0:
resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==}
- dev: true
+ dev: false
/ci-info@3.7.1:
resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==}
@@ -9226,14 +9197,6 @@ packages:
string-width: 4.2.3
dev: true
- /cli-truncate@3.1.0:
- resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==}
- engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
- dependencies:
- slice-ansi: 5.0.0
- string-width: 5.1.2
- dev: true
-
/cli-width@3.0.0:
resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==}
engines: {node: '>= 10'}
@@ -9241,7 +9204,7 @@ packages:
/clipanion@2.6.2:
resolution: {integrity: sha512-0tOHJNMF9+4R3qcbBL+4IxLErpaYSYvzs10aXuECDbZdJOuJHdagJMAqvLdeaUQTI/o2uSCDRpet6ywDiKOAYw==}
- dev: true
+ dev: false
/cliui@3.2.0:
resolution: {integrity: sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==}
@@ -9300,6 +9263,7 @@ packages:
resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==}
dependencies:
mimic-response: 1.0.1
+ dev: false
/clone-stats@1.0.0:
resolution: {integrity: sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==}
@@ -9396,7 +9360,6 @@ packages:
dependencies:
color-name: 1.1.4
simple-swizzle: 0.2.2
- dev: false
/color-support@1.1.3:
resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
@@ -9416,7 +9379,6 @@ packages:
dependencies:
color-convert: 2.0.1
color-string: 1.9.1
- dev: false
/colorette@2.0.19:
resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
@@ -9453,11 +9415,6 @@ packages:
/commander@2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
- /commander@5.1.0:
- resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==}
- engines: {node: '>= 6'}
- dev: true
-
/commander@6.2.1:
resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==}
engines: {node: '>= 6'}
@@ -9532,6 +9489,20 @@ packages:
readable-stream: 2.3.7
typedarray: 0.0.6
+ /concordance@5.0.4:
+ resolution: {integrity: sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==}
+ engines: {node: '>=10.18.0 <11 || >=12.14.0 <13 || >=14'}
+ dependencies:
+ date-time: 3.1.0
+ esutils: 2.0.3
+ fast-diff: 1.2.0
+ js-string-escape: 1.0.1
+ lodash: 4.17.21
+ md5-hex: 3.0.1
+ semver: 7.5.0
+ well-known-symbols: 2.0.0
+ dev: true
+
/config-chain@1.1.13:
resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
dependencies:
@@ -9549,7 +9520,7 @@ packages:
/constantinople@4.0.1:
resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==}
dependencies:
- '@babel/parser': 7.21.3
+ '@babel/parser': 7.21.4
'@babel/types': 7.21.4
/content-disposition@0.5.4:
@@ -9600,11 +9571,6 @@ packages:
browserslist: 4.21.5
dev: true
- /core-js@3.27.1:
- resolution: {integrity: sha512-GutwJLBChfGCpwwhbYoqfv03LAfmiz7e7D/BNxzeMxwQf10GRSzqiOjx7AmtEk+heiD/JWmBuyBPgFtx0Sg1ww==}
- requiresBuild: true
- dev: false
-
/core-js@3.29.1:
resolution: {integrity: sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==}
requiresBuild: true
@@ -9655,7 +9621,6 @@ packages:
node-fetch: 2.6.7
transitivePeerDependencies:
- encoding
- dev: true
/cross-spawn@5.1.0:
resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
@@ -9772,8 +9737,8 @@ packages:
uniq: 1.0.1
dev: false
- /cypress@12.9.0:
- resolution: {integrity: sha512-Ofe09LbHKgSqX89Iy1xen2WvpgbvNxDzsWx3mgU1mfILouELeXYGwIib3ItCwoRrRifoQwcBFmY54Vs0zw7QCg==}
+ /cypress@12.11.0:
+ resolution: {integrity: sha512-TJE+CCWI26Hwr5Msb9GpQhFLubdYooW0fmlPwTsfiyxmngqc7+SZGLPeIkj2dTSSZSEtpQVzOzvcnzH0o8G7Vw==}
engines: {node: ^14.0.0 || ^16.0.0 || >=18.0.0}
hasBin: true
requiresBuild: true
@@ -9792,7 +9757,7 @@ packages:
check-more-types: 2.24.0
cli-cursor: 3.1.0
cli-table3: 0.6.3
- commander: 5.1.0
+ commander: 6.2.1
common-tags: 1.8.2
dayjs: 1.11.7
debug: 4.3.4(supports-color@8.1.1)
@@ -9810,12 +9775,12 @@ packages:
listr2: 3.14.0(enquirer@2.3.6)
lodash: 4.17.21
log-symbols: 4.1.0
- minimist: 1.2.7
+ minimist: 1.2.8
ospath: 1.2.2
pretty-bytes: 5.6.0
proxy-from-env: 1.0.0
request-progress: 3.0.0
- semver: 7.3.8
+ semver: 7.5.0
supports-color: 8.1.1
tmp: 0.2.1
untildify: 4.0.0
@@ -9852,11 +9817,20 @@ packages:
whatwg-url: 12.0.1
dev: false
- /date-fns@2.29.3:
- resolution: {integrity: sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==}
+ /date-fns@2.30.0:
+ resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
engines: {node: '>=0.11'}
+ dependencies:
+ '@babel/runtime': 7.21.0
dev: false
+ /date-time@3.1.0:
+ resolution: {integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==}
+ engines: {node: '>=6'}
+ dependencies:
+ time-zone: 1.0.0
+ dev: true
+
/dayjs@1.11.7:
resolution: {integrity: sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==}
dev: true
@@ -9991,7 +9965,6 @@ packages:
/deep-extend@0.6.0:
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
engines: {node: '>=4.0.0'}
- dev: false
/deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
@@ -10090,11 +10063,6 @@ packages:
/delegates@1.0.0:
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
- /denque@1.5.1:
- resolution: {integrity: sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==}
- engines: {node: '>=0.10'}
- dev: false
-
/denque@2.1.0:
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
engines: {node: '>=0.10'}
@@ -10131,7 +10099,6 @@ packages:
/detect-libc@2.0.1:
resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==}
engines: {node: '>=8'}
- dev: false
/detect-newline@3.1.0:
resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
@@ -10168,7 +10135,7 @@ packages:
/diff@4.0.2:
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
engines: {node: '>=0.3.1'}
- dev: true
+ dev: false
/diff@5.1.0:
resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==}
@@ -10274,10 +10241,6 @@ packages:
object.defaults: 1.1.0
dev: false
- /eastasianwidth@0.2.0:
- resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
- dev: true
-
/ecc-jsbn@0.1.2:
resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==}
dependencies:
@@ -10326,10 +10289,6 @@ packages:
/emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
- /emoji-regex@9.2.2:
- resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
- dev: true
-
/encode-utf8@1.0.3:
resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==}
dev: false
@@ -10351,7 +10310,7 @@ packages:
resolution: {integrity: sha512-EoulkdKF/1xa92q25PbjuDcgJ9RDHYU2Rs3SCIvs2/dSQ3BpmxneNHmA/M7fe60M3PrV7nNGTTNbkK62l6vXiQ==}
dependencies:
once: 1.3.3
- dev: true
+ dev: false
/end-of-stream@1.4.4:
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
@@ -10510,74 +10469,45 @@ packages:
resolution: {integrity: sha512-jyfL/pwPqaFXyKnj8lP8iLk6Z0m099uXR45aSN8Av1XD4vhvQutxxPzgA2bTcAwQpa1zCXDcWOlhFgyP3GKqhQ==}
dev: true
- /esbuild-register@3.4.2(esbuild@0.17.15):
+ /esbuild-register@3.4.2(esbuild@0.17.18):
resolution: {integrity: sha512-kG/XyTDyz6+YDuyfB9ZoSIOOmgyFCH+xPRtsCa8W85HLRV5Csp+o3jWVbOSHgSLfyLc5DmP+KFDNwty4mEjC+Q==}
peerDependencies:
esbuild: '>=0.12 <1'
dependencies:
debug: 4.3.4(supports-color@8.1.1)
- esbuild: 0.17.15
+ esbuild: 0.17.18
transitivePeerDependencies:
- supports-color
dev: true
- /esbuild@0.17.14:
- resolution: {integrity: sha512-vOO5XhmVj/1XQR9NQ1UPq6qvMYL7QFJU57J5fKBKBKxp17uDt5PgxFDb4A2nEiXhr1qQs4x0F5+66hVVw4ruNw==}
- engines: {node: '>=12'}
- hasBin: true
- requiresBuild: true
- optionalDependencies:
- '@esbuild/android-arm': 0.17.14
- '@esbuild/android-arm64': 0.17.14
- '@esbuild/android-x64': 0.17.14
- '@esbuild/darwin-arm64': 0.17.14
- '@esbuild/darwin-x64': 0.17.14
- '@esbuild/freebsd-arm64': 0.17.14
- '@esbuild/freebsd-x64': 0.17.14
- '@esbuild/linux-arm': 0.17.14
- '@esbuild/linux-arm64': 0.17.14
- '@esbuild/linux-ia32': 0.17.14
- '@esbuild/linux-loong64': 0.17.14
- '@esbuild/linux-mips64el': 0.17.14
- '@esbuild/linux-ppc64': 0.17.14
- '@esbuild/linux-riscv64': 0.17.14
- '@esbuild/linux-s390x': 0.17.14
- '@esbuild/linux-x64': 0.17.14
- '@esbuild/netbsd-x64': 0.17.14
- '@esbuild/openbsd-x64': 0.17.14
- '@esbuild/sunos-x64': 0.17.14
- '@esbuild/win32-arm64': 0.17.14
- '@esbuild/win32-ia32': 0.17.14
- '@esbuild/win32-x64': 0.17.14
-
- /esbuild@0.17.15:
- resolution: {integrity: sha512-LBUV2VsUIc/iD9ME75qhT4aJj0r75abCVS0jakhFzOtR7TQsqQA5w0tZ+KTKnwl3kXE0MhskNdHDh/I5aCR1Zw==}
+ /esbuild@0.17.18:
+ resolution: {integrity: sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
- '@esbuild/android-arm': 0.17.15
- '@esbuild/android-arm64': 0.17.15
- '@esbuild/android-x64': 0.17.15
- '@esbuild/darwin-arm64': 0.17.15
- '@esbuild/darwin-x64': 0.17.15
- '@esbuild/freebsd-arm64': 0.17.15
- '@esbuild/freebsd-x64': 0.17.15
- '@esbuild/linux-arm': 0.17.15
- '@esbuild/linux-arm64': 0.17.15
- '@esbuild/linux-ia32': 0.17.15
- '@esbuild/linux-loong64': 0.17.15
- '@esbuild/linux-mips64el': 0.17.15
- '@esbuild/linux-ppc64': 0.17.15
- '@esbuild/linux-riscv64': 0.17.15
- '@esbuild/linux-s390x': 0.17.15
- '@esbuild/linux-x64': 0.17.15
- '@esbuild/netbsd-x64': 0.17.15
- '@esbuild/openbsd-x64': 0.17.15
- '@esbuild/sunos-x64': 0.17.15
- '@esbuild/win32-arm64': 0.17.15
- '@esbuild/win32-ia32': 0.17.15
- '@esbuild/win32-x64': 0.17.15
+ '@esbuild/android-arm': 0.17.18
+ '@esbuild/android-arm64': 0.17.18
+ '@esbuild/android-x64': 0.17.18
+ '@esbuild/darwin-arm64': 0.17.18
+ '@esbuild/darwin-x64': 0.17.18
+ '@esbuild/freebsd-arm64': 0.17.18
+ '@esbuild/freebsd-x64': 0.17.18
+ '@esbuild/linux-arm': 0.17.18
+ '@esbuild/linux-arm64': 0.17.18
+ '@esbuild/linux-ia32': 0.17.18
+ '@esbuild/linux-loong64': 0.17.18
+ '@esbuild/linux-mips64el': 0.17.18
+ '@esbuild/linux-ppc64': 0.17.18
+ '@esbuild/linux-riscv64': 0.17.18
+ '@esbuild/linux-s390x': 0.17.18
+ '@esbuild/linux-x64': 0.17.18
+ '@esbuild/netbsd-x64': 0.17.18
+ '@esbuild/openbsd-x64': 0.17.18
+ '@esbuild/sunos-x64': 0.17.18
+ '@esbuild/win32-arm64': 0.17.18
+ '@esbuild/win32-ia32': 0.17.18
+ '@esbuild/win32-x64': 0.17.18
/escalade@3.1.1:
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
@@ -10643,7 +10573,7 @@ packages:
- supports-color
dev: true
- /eslint-module-utils@2.7.4(@typescript-eslint/parser@5.57.1)(eslint-import-resolver-node@0.3.7)(eslint@8.37.0):
+ /eslint-module-utils@2.7.4(@typescript-eslint/parser@5.59.2)(eslint-import-resolver-node@0.3.7)(eslint@8.39.0):
resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==}
engines: {node: '>=4'}
peerDependencies:
@@ -10664,15 +10594,15 @@ packages:
eslint-import-resolver-webpack:
optional: true
dependencies:
- '@typescript-eslint/parser': 5.57.1(eslint@8.37.0)(typescript@5.0.3)
+ '@typescript-eslint/parser': 5.59.2(eslint@8.39.0)(typescript@5.0.4)
debug: 3.2.7(supports-color@8.1.1)
- eslint: 8.37.0
+ eslint: 8.39.0
eslint-import-resolver-node: 0.3.7
transitivePeerDependencies:
- supports-color
dev: true
- /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.57.1)(eslint@8.37.0):
+ /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.59.2)(eslint@8.39.0):
resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==}
engines: {node: '>=4'}
peerDependencies:
@@ -10682,15 +10612,15 @@ packages:
'@typescript-eslint/parser':
optional: true
dependencies:
- '@typescript-eslint/parser': 5.57.1(eslint@8.37.0)(typescript@5.0.3)
+ '@typescript-eslint/parser': 5.59.2(eslint@8.39.0)(typescript@5.0.4)
array-includes: 3.1.6
array.prototype.flat: 1.3.1
array.prototype.flatmap: 1.3.1
debug: 3.2.7(supports-color@8.1.1)
doctrine: 2.1.0
- eslint: 8.37.0
+ eslint: 8.39.0
eslint-import-resolver-node: 0.3.7
- eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.57.1)(eslint-import-resolver-node@0.3.7)(eslint@8.37.0)
+ eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.59.2)(eslint-import-resolver-node@0.3.7)(eslint@8.39.0)
has: 1.0.3
is-core-module: 2.11.0
is-glob: 4.0.3
@@ -10705,19 +10635,19 @@ packages:
- supports-color
dev: true
- /eslint-plugin-vue@9.10.0(eslint@8.37.0):
- resolution: {integrity: sha512-2MgP31OBf8YilUvtakdVMc8xVbcMp7z7/iQj8LHVpXrSXHPXSJRUIGSPFI6b6pyCx/buKaFJ45ycqfHvQRiW2g==}
+ /eslint-plugin-vue@9.11.0(eslint@8.39.0):
+ resolution: {integrity: sha512-bBCJAZnkBV7ATH4Z1E7CvN3nmtS4H7QUU3UBxPdo8WohRU+yHjnQRALpTbxMVcz0e4Mx3IyxIdP5HYODMxK9cQ==}
engines: {node: ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.37.0)
- eslint: 8.37.0
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.39.0)
+ eslint: 8.39.0
natural-compare: 1.4.0
nth-check: 2.1.1
postcss-selector-parser: 6.0.11
- semver: 7.3.8
- vue-eslint-parser: 9.1.1(eslint@8.37.0)
+ semver: 7.5.0
+ vue-eslint-parser: 9.1.1(eslint@8.39.0)
xml-name-validator: 4.0.0
transitivePeerDependencies:
- supports-color
@@ -10743,20 +10673,28 @@ packages:
estraverse: 5.3.0
dev: true
+ /eslint-scope@7.2.0:
+ resolution: {integrity: sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+ dev: true
+
/eslint-visitor-keys@3.4.0:
resolution: {integrity: sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
- /eslint@8.37.0:
- resolution: {integrity: sha512-NU3Ps9nI05GUoVMxcZx1J8CNR6xOvUT4jAUMH5+z8lpp3aEdPVCImKw6PWG4PY+Vfkpr+jvMpxs/qoE7wq0sPw==}
+ /eslint@8.39.0:
+ resolution: {integrity: sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
hasBin: true
dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.37.0)
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.39.0)
'@eslint-community/regexpp': 4.5.0
'@eslint/eslintrc': 2.0.2
- '@eslint/js': 8.37.0
+ '@eslint/js': 8.39.0
'@humanwhocodes/config-array': 0.11.8
'@humanwhocodes/module-importer': 1.0.1
'@nodelib/fs.walk': 1.2.8
@@ -10766,7 +10704,7 @@ packages:
debug: 4.3.4(supports-color@8.1.1)
doctrine: 3.0.0
escape-string-regexp: 4.0.0
- eslint-scope: 7.1.1
+ eslint-scope: 7.2.0
eslint-visitor-keys: 3.4.0
espree: 9.5.1
esquery: 1.4.2
@@ -10845,7 +10783,7 @@ packages:
engines: {node: '>=8.3.0'}
dependencies:
'@babel/traverse': 7.21.3
- '@babel/types': 7.21.3
+ '@babel/types': 7.21.4
c8: 7.13.0
transitivePeerDependencies:
- supports-color
@@ -10867,7 +10805,7 @@ packages:
resolution: {integrity: sha512-KB44sV4Mv7uLIkJHJ5qhiZe5um6th2g57nHQL/uqnPHKP2IswoTRWUteEXTJQL4gW++1zqWUni+H2hGkP51c9w==}
dependencies:
tslib: 2.5.0
- dev: true
+ dev: false
/event-stream@3.3.4:
resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==}
@@ -10894,8 +10832,8 @@ packages:
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
dev: false
- /eventemitter3@5.0.0:
- resolution: {integrity: sha512-riuVbElZZNXLeLEoprfNYoDSwTBRR44X3mnhdI1YcnENpWTCsTTVZ2zFuqQcpoyqPQIUXdiPEU0ECAq0KQRaHg==}
+ /eventemitter3@5.0.1:
+ resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
dev: false
/events@3.3.0:
@@ -10987,7 +10925,6 @@ packages:
/expand-template@2.0.3:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
- dev: false
/expand-tilde@2.0.2:
resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==}
@@ -11158,6 +11095,10 @@ packages:
/fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+ /fast-diff@1.2.0:
+ resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==}
+ dev: true
+
/fast-glob@3.2.12:
resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==}
engines: {node: '>=8.6.0'}
@@ -11171,8 +11112,8 @@ packages:
/fast-json-stable-stringify@2.1.0:
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
- /fast-json-stringify@5.5.0:
- resolution: {integrity: sha512-rmw2Z8/mLkND8zI+3KTYIkNPEoF5v6GqDP/o+g7H3vjdWjBwuKpgAYFHIzL6ORRB+iqDjjtJnLIW9Mzxn5szOA==}
+ /fast-json-stringify@5.7.0:
+ resolution: {integrity: sha512-sBVPTgnAZseLu1Qgj6lUbQ0HfjFhZWXAmpZ5AaSGkyLh5gAXBga/uPJjQPHpDFjC9adWIpdOcCLSDTgrZ7snoQ==}
dependencies:
'@fastify/deepmerge': 1.3.0
ajv: 8.12.0
@@ -11225,15 +11166,16 @@ packages:
resolution: {integrity: sha512-79ak0JxddO0utAXAQ5ccKhvs6vX2MGyHHMMsmZkBANrq3hXc1CHzvNPHOcvTsVMEPl5I+NT+RO4YKMGehOfSIg==}
dev: false
- /fastify@4.15.0:
- resolution: {integrity: sha512-m/CaRN8nf5uyYdrDe2qqq+0z3oGyE+A++qlKQoLJTI4WI0nWK9D6R3FxXQ3MVwt/md977GMR4F43pE9oqrS2zw==}
+ /fastify@4.17.0:
+ resolution: {integrity: sha512-tzuY1tgWJo2Y6qEKwmLhFvACUmr68Io2pqP/sDKU71KRM6A6R3DrCDqLGqANbeLZcKUfdfY58ut35CGqemcTgg==}
dependencies:
'@fastify/ajv-compiler': 3.5.0
'@fastify/error': 3.2.0
- '@fastify/fast-json-stringify-compiler': 4.2.0
+ '@fastify/fast-json-stringify-compiler': 4.3.0
abstract-logging: 2.0.1
avvio: 8.2.0
fast-content-type-parse: 1.0.0
+ fast-json-stringify: 5.7.0
find-my-way: 7.6.0
light-my-request: 5.8.0
pino: 8.8.0
@@ -11241,8 +11183,8 @@ packages:
proxy-addr: 2.0.7
rfdc: 1.3.0
secure-json-parse: 2.7.0
- semver: 7.3.8
- tiny-lru: 10.0.1
+ semver: 7.5.0
+ tiny-lru: 11.0.1
transitivePeerDependencies:
- supports-color
dev: false
@@ -11318,8 +11260,8 @@ packages:
token-types: 5.0.1
dev: false
- /file-type@18.2.1:
- resolution: {integrity: sha512-Yw5MtnMv7vgD2/6Bjmmuegc8bQEVA9GmAyaR18bMYWKqsWDG9wgYZ1j4I6gNMF5Y5JBDcUcjRQqNQx7Y8uotcg==}
+ /file-type@18.3.0:
+ resolution: {integrity: sha512-pkPZ5OGIq0TYb37b8bHDLNeQSe1H2KlaQ2ySGpJkkr2KZdaWsO4QhPzHA0mQcsUW2cSqJk+4gM/UyLz/UFbXdQ==}
engines: {node: '>=14.16'}
dependencies:
readable-web-to-node-stream: 3.0.2
@@ -11860,7 +11802,6 @@ packages:
/github-from-package@0.0.0:
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
- dev: false
/github-slugger@1.5.0:
resolution: {integrity: sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==}
@@ -12044,6 +11985,7 @@ packages:
lowercase-keys: 2.0.0
p-cancelable: 2.1.1
responselike: 2.0.1
+ dev: false
/got@12.6.0:
resolution: {integrity: sha512-WTcaQ963xV97MN3x0/CbAriXFZcXCfgxVp91I+Ze6pawQOa7SgzwSx2zIJJsX+kTajMnVs0xcFD1TxZKFqhdnQ==}
@@ -12070,7 +12012,6 @@ packages:
/grapheme-splitter@1.0.4:
resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
- dev: true
/graphql@16.6.0:
resolution: {integrity: sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==}
@@ -12184,7 +12125,7 @@ packages:
engines: {node: '>=0.4.7'}
hasBin: true
dependencies:
- minimist: 1.2.7
+ minimist: 1.2.8
neo-async: 2.6.2
source-map: 0.6.1
wordwrap: 1.0.0
@@ -12192,18 +12133,15 @@ packages:
uglify-js: 3.17.4
dev: true
- /happy-dom@8.9.0:
- resolution: {integrity: sha512-JZwJuGdR7ko8L61136YzmrLv7LgTh5b8XaEM3P709mLjyQuXJ3zHTDXvUtBBahRjGlcYW0zGjIiEWizoTUGKfA==}
+ /happy-dom@9.10.2:
+ resolution: {integrity: sha512-PZ+UzoU7S8/has8Zw7tr8WGPAAr1QVrAc9o2Llhtuf6aewnR4DDmhPU1Z8KzWwSjXOE+d+b38qNfwHU35PSMIw==}
dependencies:
css.escape: 1.5.1
he: 1.2.0
iconv-lite: 0.6.3
- node-fetch: 2.6.7
webidl-conversions: 7.0.0
whatwg-encoding: 2.0.0
whatwg-mimetype: 3.0.0
- transitivePeerDependencies:
- - encoding
/har-schema@2.0.0:
resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==}
@@ -12438,6 +12376,7 @@ packages:
dependencies:
quick-lru: 5.1.1
resolve-alpn: 1.2.1
+ dev: false
/http2-wrapper@2.2.0:
resolution: {integrity: sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==}
@@ -12600,7 +12539,7 @@ packages:
mute-stream: 0.0.8
ora: 5.4.1
run-async: 2.4.1
- rxjs: 7.8.0
+ rxjs: 7.8.1
string-width: 4.2.3
strip-ansi: 6.0.1
through: 2.3.8
@@ -12633,27 +12572,8 @@ packages:
engines: {node: '>=0.10.0'}
dev: false
- /ioredis@4.28.5:
- resolution: {integrity: sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A==}
- engines: {node: '>=6'}
- dependencies:
- cluster-key-slot: 1.1.2
- debug: 4.3.4(supports-color@8.1.1)
- denque: 1.5.1
- lodash.defaults: 4.2.0
- lodash.flatten: 4.4.0
- lodash.isarguments: 3.1.0
- p-map: 2.1.0
- redis-commands: 1.7.0
- redis-errors: 1.2.0
- redis-parser: 3.0.0
- standard-as-callback: 2.1.0
- transitivePeerDependencies:
- - supports-color
- dev: false
-
- /ioredis@5.2.4:
- resolution: {integrity: sha512-qIpuAEt32lZJQ0XyrloCRdlEdUUNGG9i0UOk6zgzK6igyudNWqEBxfH6OlbnOOoBBvr1WB02mm8fR55CnikRng==}
+ /ioredis@5.3.2:
+ resolution: {integrity: sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==}
engines: {node: '>=12.22.0'}
dependencies:
'@ioredis/commands': 1.2.0
@@ -12775,7 +12695,6 @@ packages:
/is-arrayish@0.3.2:
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
- dev: false
/is-bigint@1.0.4:
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
@@ -12803,7 +12722,6 @@ packages:
/is-callable@1.2.7:
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
engines: {node: '>= 0.4'}
- dev: true
/is-ci@3.0.1:
resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==}
@@ -12906,11 +12824,6 @@ packages:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
- /is-fullwidth-code-point@4.0.0:
- resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
- engines: {node: '>=12'}
- dev: true
-
/is-generator-fn@2.1.0:
resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==}
engines: {node: '>=6'}
@@ -13182,7 +13095,7 @@ packages:
/is@3.3.0:
resolution: {integrity: sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==}
- dev: true
+ dev: false
/isarray@0.0.1:
resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==}
@@ -13229,7 +13142,7 @@ packages:
engines: {node: '>=8'}
dependencies:
'@babel/core': 7.21.3
- '@babel/parser': 7.21.3
+ '@babel/parser': 7.21.4
'@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.0
semver: 6.3.0
@@ -13304,7 +13217,7 @@ packages:
'@jest/expect': 29.5.0
'@jest/test-result': 29.5.0
'@jest/types': 29.5.0
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
chalk: 4.1.2
co: 4.6.0
dedent: 0.7.0
@@ -13324,7 +13237,7 @@ packages:
- supports-color
dev: true
- /jest-cli@29.5.0(@types/node@18.15.11):
+ /jest-cli@29.5.0(@types/node@18.16.3):
resolution: {integrity: sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
hasBin: true
@@ -13341,7 +13254,7 @@ packages:
exit: 0.1.2
graceful-fs: 4.2.11
import-local: 3.1.0
- jest-config: 29.5.0(@types/node@18.15.11)
+ jest-config: 29.5.0(@types/node@18.16.3)
jest-util: 29.5.0
jest-validate: 29.5.0
prompts: 2.4.2
@@ -13352,7 +13265,7 @@ packages:
- ts-node
dev: true
- /jest-config@29.5.0(@types/node@18.15.11):
+ /jest-config@29.5.0(@types/node@18.16.3):
resolution: {integrity: sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
@@ -13367,7 +13280,7 @@ packages:
'@babel/core': 7.21.3
'@jest/test-sequencer': 29.5.0
'@jest/types': 29.5.0
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
babel-jest: 29.5.0(@babel/core@7.21.3)
chalk: 4.1.2
ci-info: 3.7.1
@@ -13436,7 +13349,7 @@ packages:
'@jest/environment': 29.5.0
'@jest/fake-timers': 29.5.0
'@jest/types': 29.5.0
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
jest-mock: 29.5.0
jest-util: 29.5.0
dev: true
@@ -13466,7 +13379,7 @@ packages:
dependencies:
'@jest/types': 29.5.0
'@types/graceful-fs': 4.1.6
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
anymatch: 3.1.3
fb-watchman: 2.0.2
graceful-fs: 4.2.11
@@ -13517,7 +13430,7 @@ packages:
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
dependencies:
'@jest/types': 27.5.1
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
dev: true
/jest-mock@29.5.0:
@@ -13525,7 +13438,7 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/types': 29.5.0
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
jest-util: 29.5.0
dev: true
@@ -13580,7 +13493,7 @@ packages:
'@jest/test-result': 29.5.0
'@jest/transform': 29.5.0
'@jest/types': 29.5.0
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
chalk: 4.1.2
emittery: 0.13.1
graceful-fs: 4.2.11
@@ -13611,7 +13524,7 @@ packages:
'@jest/test-result': 29.5.0
'@jest/transform': 29.5.0
'@jest/types': 29.5.0
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
chalk: 4.1.2
cjs-module-lexer: 1.2.2
collect-v8-coverage: 1.0.1
@@ -13656,7 +13569,7 @@ packages:
jest-util: 29.5.0
natural-compare: 1.4.0
pretty-format: 29.5.0
- semver: 7.3.8
+ semver: 7.5.0
transitivePeerDependencies:
- supports-color
dev: true
@@ -13666,7 +13579,7 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/types': 29.5.0
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
chalk: 4.1.2
ci-info: 3.7.1
graceful-fs: 4.2.11
@@ -13691,7 +13604,7 @@ packages:
dependencies:
'@jest/test-result': 29.5.0
'@jest/types': 29.5.0
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
ansi-escapes: 4.3.2
chalk: 4.1.2
emittery: 0.13.1
@@ -13710,13 +13623,13 @@ packages:
resolution: {integrity: sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@types/node': 18.15.11
+ '@types/node': 18.16.3
jest-util: 29.5.0
merge-stream: 2.0.0
supports-color: 8.1.1
dev: true
- /jest@29.5.0(@types/node@18.15.11):
+ /jest@29.5.0(@types/node@18.16.3):
resolution: {integrity: sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
hasBin: true
@@ -13729,7 +13642,7 @@ packages:
'@jest/core': 29.5.0
'@jest/types': 29.5.0
import-local: 3.1.0
- jest-cli: 29.5.0(@types/node@18.15.11)
+ jest-cli: 29.5.0(@types/node@18.16.3)
transitivePeerDependencies:
- '@types/node'
- supports-color
@@ -13782,6 +13695,11 @@ packages:
resolution: {integrity: sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==}
dev: true
+ /js-string-escape@1.0.1:
+ resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==}
+ engines: {node: '>= 0.8'}
+ dev: true
+
/js-stringify@1.0.2:
resolution: {integrity: sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==}
@@ -13795,7 +13713,6 @@ packages:
dependencies:
argparse: 1.0.10
esprima: 4.0.1
- dev: true
/js-yaml@3.7.0:
resolution: {integrity: sha512-eIlkGty7HGmntbV6P/ZlAsoncFLGsNoM27lkTzS+oneY/EiNhj+geqD9ezg/ip+SW6Var0BJU2JtV0vEUZpWVQ==}
@@ -13821,36 +13738,6 @@ packages:
resolution: {integrity: sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ==}
engines: {node: '>=0.1.90'}
- /jscodeshift@0.14.0(@babel/preset-env@7.20.2):
- resolution: {integrity: sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA==}
- hasBin: true
- peerDependencies:
- '@babel/preset-env': ^7.1.6
- dependencies:
- '@babel/core': 7.21.3
- '@babel/parser': 7.21.3
- '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.21.3)
- '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.3)
- '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.21.3)
- '@babel/preset-env': 7.20.2(@babel/core@7.21.3)
- '@babel/preset-flow': 7.18.6(@babel/core@7.21.3)
- '@babel/preset-typescript': 7.21.0(@babel/core@7.21.3)
- '@babel/register': 7.21.0(@babel/core@7.21.3)
- babel-core: 7.0.0-bridge.0(@babel/core@7.21.3)
- chalk: 4.1.2
- flow-parser: 0.202.0
- graceful-fs: 4.2.11
- micromatch: 4.0.5
- neo-async: 2.6.2
- node-dir: 0.1.17
- recast: 0.21.5
- temp: 0.8.4
- write-file-atomic: 2.4.3
- transitivePeerDependencies:
- - supports-color
- dev: true
-
/jscodeshift@0.14.0(@babel/preset-env@7.21.4):
resolution: {integrity: sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA==}
hasBin: true
@@ -13858,7 +13745,7 @@ packages:
'@babel/preset-env': ^7.1.6
dependencies:
'@babel/core': 7.21.3
- '@babel/parser': 7.21.3
+ '@babel/parser': 7.21.4
'@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.21.3)
'@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.21.3)
'@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.3)
@@ -13945,7 +13832,7 @@ packages:
object.assign: 4.1.4
promiseback: 2.0.3
safer-buffer: 2.1.2
- dev: true
+ dev: false
/json-parse-even-better-errors@2.3.1:
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
@@ -13971,7 +13858,7 @@ packages:
resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==}
hasBin: true
dependencies:
- minimist: 1.2.7
+ minimist: 1.2.8
dev: true
/json5@2.2.3:
@@ -14035,8 +13922,8 @@ packages:
verror: 1.10.0
dev: true
- /jsrsasign@10.7.0:
- resolution: {integrity: sha512-D5V2gGpYGtwbAtQHoglTVrpYf7QJuNoPEhaLOsTFONS2jXUl3qyR1hnYrNpASAybqQeiDYA3zGthR0ubgPRoQA==}
+ /jsrsasign@10.8.6:
+ resolution: {integrity: sha512-bQmbVtsfbgaKBTWCKiDCPlUPbdlRIK/FzSwT3BzIgZl/cU6TqXu6pZJsCI/dJVrZ9Gir5GC4woqw9shH/v7MBw==}
dev: false
/jssha@3.3.0:
@@ -14232,7 +14119,7 @@ packages:
log-update: 4.0.0
p-map: 4.0.0
rfdc: 1.3.0
- rxjs: 7.8.0
+ rxjs: 7.8.1
through: 2.3.8
wrap-ansi: 7.0.0
dev: true
@@ -14255,7 +14142,6 @@ packages:
/local-pkg@0.4.3:
resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==}
engines: {node: '>=14'}
- dev: true
/locate-path@3.0.0:
resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==}
@@ -14280,15 +14166,15 @@ packages:
/lodash.clone@4.5.0:
resolution: {integrity: sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg==}
- dev: true
+ dev: false
/lodash.clonedeep@4.5.0:
resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==}
- dev: true
+ dev: false
/lodash.constant@3.0.0:
resolution: {integrity: sha512-X5XMrB+SdI1mFa81162NSTo/YNd23SLdLOLzcXTwS4inDZ5YCL8X67UFzZJAH4CqIa6R8cr56CShfA5K5MFiYQ==}
- dev: true
+ dev: false
/lodash.debounce@4.0.8:
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
@@ -14303,11 +14189,11 @@ packages:
/lodash.filter@4.6.0:
resolution: {integrity: sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==}
- dev: true
+ dev: false
/lodash.flatmap@4.5.0:
resolution: {integrity: sha512-/OcpcAGWlrZyoHGeHh3cAoa6nGdX6QYtmzNP84Jqol6UEQQ2gIaU3H+0eICcjcKGl0/XF8LWOujNn9lffsnaOg==}
- dev: true
+ dev: false
/lodash.flatten@4.4.0:
resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==}
@@ -14315,7 +14201,7 @@ packages:
/lodash.foreach@4.5.0:
resolution: {integrity: sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==}
- dev: true
+ dev: false
/lodash.get@4.4.2:
resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
@@ -14323,22 +14209,21 @@ packages:
/lodash.has@4.5.2:
resolution: {integrity: sha512-rnYUdIo6xRCJnQmbVFEwcxF144erlD+M3YcJUVesflU9paQaE8p+fJDcIQrlMYbxoANFL+AB9hZrzSBBk5PL+g==}
- dev: true
+ dev: false
/lodash.isarguments@3.1.0:
resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
/lodash.isempty@4.4.0:
resolution: {integrity: sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==}
- dev: true
+ dev: false
/lodash.isequal@4.5.0:
resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
- dev: true
/lodash.isfunction@3.0.9:
resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==}
- dev: true
+ dev: false
/lodash.isplainobject@4.0.6:
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
@@ -14346,15 +14231,15 @@ packages:
/lodash.isundefined@3.0.1:
resolution: {integrity: sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==}
- dev: true
+ dev: false
/lodash.keys@4.2.0:
resolution: {integrity: sha512-J79MkJcp7Df5mizHiVNpjoHXLi4HLjh9VLS/M7lQSGoQ+0oQ+lWEigREkqKyizPB1IawvQLLKY8mzEcm1tkyxQ==}
- dev: true
+ dev: false
/lodash.map@4.6.0:
resolution: {integrity: sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==}
- dev: true
+ dev: false
/lodash.memoize@4.1.2:
resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==}
@@ -14369,11 +14254,11 @@ packages:
/lodash.reduce@4.6.0:
resolution: {integrity: sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==}
- dev: true
+ dev: false
/lodash.size@4.2.0:
resolution: {integrity: sha512-wbu3SF1XC5ijqm0piNxw59yCbuUf2kaShumYBLWUrcCvwh6C8odz6SY/wGVzCWTQTFL/1Ygbvqg2eLtspUVVAQ==}
- dev: true
+ dev: false
/lodash.throttle@4.1.1:
resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==}
@@ -14381,14 +14266,15 @@ packages:
/lodash.topairs@4.3.0:
resolution: {integrity: sha512-qrRMbykBSEGdOgQLJJqVSdPWMD7Q+GJJ5jMRfQYb+LTLsw3tYVIabnCzRqTJb2WTo17PG5gNzXuFaZgYH/9SAQ==}
- dev: true
+ dev: false
/lodash.transform@4.6.0:
resolution: {integrity: sha512-LO37ZnhmBVx0GvOU/caQuipEh4GN82TcWv3yHlebGDgOxbxiwwzW5Pcx2AcvpIv2WmvmSMoC492yQFNhy/l/UQ==}
- dev: true
+ dev: false
/lodash.union@4.6.0:
resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==}
+ dev: false
/lodash.uniq@4.5.0:
resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==}
@@ -14396,7 +14282,7 @@ packages:
/lodash.values@4.3.0:
resolution: {integrity: sha512-r0RwvdCv8id9TUblb/O7rYPwVy6lerCbcawrfdo9iC/1t1wsNMJknO79WNBgwkH0hIeJ08jmvvESbFpNb4jH0Q==}
- dev: true
+ dev: false
/lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
@@ -14439,6 +14325,7 @@ packages:
/lowercase-keys@2.0.0:
resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
engines: {node: '>=8'}
+ dev: false
/lowercase-keys@3.0.0:
resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==}
@@ -14488,6 +14375,13 @@ packages:
hasBin: true
dev: true
+ /magic-string-ast@0.1.2:
+ resolution: {integrity: sha512-P53AZrzq7hclCU6HWj88xNZHmP15DKjMmK/vBytO1qnpYP3ul4IEZlyCE0aU3JRnmgWmZPmoTKj4Bls7v0pMyA==}
+ engines: {node: '>=14.19.0'}
+ dependencies:
+ magic-string: 0.30.0
+ dev: false
+
/magic-string@0.25.9:
resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
dependencies:
@@ -14498,7 +14392,12 @@ packages:
engines: {node: '>=12'}
dependencies:
'@jridgewell/sourcemap-codec': 1.4.14
- dev: true
+
+ /magic-string@0.30.0:
+ resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==}
+ engines: {node: '>=12'}
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.4.14
/mailcheck@1.1.1:
resolution: {integrity: sha512-3WjL8+ZDouZwKlyJBMp/4LeziLFXgleOdsYu87piGcMLqhBzCsy2QFdbtAwv757TFC/rtqd738fgJw1tFQCSgA==}
@@ -14613,6 +14512,13 @@ packages:
resolution: {integrity: sha512-v2huwvQGOHTGOkMqtHd2hercCG3f6QAObTisPPHg8TZqq2lz7eIY/5i/5YUV8Ibf3mEioFEmwibcPUF2/fnKKQ==}
dev: false
+ /md5-hex@3.0.1:
+ resolution: {integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==}
+ engines: {node: '>=8'}
+ dependencies:
+ blueimp-md5: 2.19.0
+ dev: true
+
/mdast-util-definitions@4.0.0:
resolution: {integrity: sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==}
dependencies:
@@ -14628,6 +14534,14 @@ packages:
engines: {node: '>= 0.6'}
dev: true
+ /meilisearch@0.32.3:
+ resolution: {integrity: sha512-EOgfBuRE5SiIPIpEDYe2HO0D7a4z5bexIgaAdJFma/dH5hx1kwO+u/qb2g3qKyjG+iA3l8MlmTj/Xd72uahaAw==}
+ dependencies:
+ cross-fetch: 3.1.5
+ transitivePeerDependencies:
+ - encoding
+ dev: false
+
/memoizerific@1.11.3:
resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==}
dependencies:
@@ -14741,6 +14655,7 @@ packages:
/mimic-response@1.0.1:
resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==}
engines: {node: '>=4'}
+ dev: false
/mimic-response@3.1.0:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
@@ -14770,9 +14685,9 @@ packages:
dependencies:
brace-expansion: 2.0.1
- /minimatch@6.2.0:
- resolution: {integrity: sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg==}
- engines: {node: '>=10'}
+ /minimatch@9.0.0:
+ resolution: {integrity: sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==}
+ engines: {node: '>=16 || 14 >=14.17'}
dependencies:
brace-expansion: 2.0.1
dev: true
@@ -14788,6 +14703,10 @@ packages:
/minimist@1.2.7:
resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==}
+ dev: false
+
+ /minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
/minipass-collect@1.0.2:
resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==}
@@ -14874,7 +14793,7 @@ packages:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
hasBin: true
dependencies:
- minimist: 1.2.7
+ minimist: 1.2.8
/mkdirp@1.0.4:
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
@@ -14887,8 +14806,8 @@ packages:
hasBin: true
dev: false
- /mlly@1.1.1:
- resolution: {integrity: sha512-Jnlh4W/aI4GySPo6+DyTN17Q75KKbLTyFK8BrGhjNP4rxuUjbRWhE6gHg3bs33URWAF44FRm7gdQA348i3XxRw==}
+ /mlly@1.2.0:
+ resolution: {integrity: sha512-+c7A3CV0KGdKcylsI6khWyts/CYrGTrRVo4R/I7u/cUsy0Conxa6LUhiEzVKIw14lc2L5aiO4+SeVe4TeGRKww==}
dependencies:
acorn: 8.8.2
pathe: 1.1.0
@@ -14955,10 +14874,10 @@ packages:
msw: '>=0.35.0 <2.0.0'
dependencies:
is-node-process: 1.0.1
- msw: 1.2.1(typescript@5.0.3)
+ msw: 1.2.1(typescript@5.0.4)
dev: true
- /msw@1.2.1(typescript@5.0.3):
+ /msw@1.2.1(typescript@5.0.4):
resolution: {integrity: sha512-bF7qWJQSmKn6bwGYVPXOxhexTCGD5oJSZg8yt8IBClxvo3Dx/1W0zqE1nX9BSWmzRsCKWfeGWcB/vpqV6aclpw==}
engines: {node: '>=14'}
hasBin: true
@@ -14987,7 +14906,7 @@ packages:
path-to-regexp: 6.2.1
strict-event-emitter: 0.4.6
type-fest: 2.19.0
- typescript: 5.0.3
+ typescript: 5.0.4
yargs: 17.6.2
transitivePeerDependencies:
- encoding
@@ -15033,6 +14952,11 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
+ /nanoid@3.3.6:
+ resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
/nanomatch@1.2.13:
resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==}
engines: {node: '>=0.10.0'}
@@ -15053,7 +14977,6 @@ packages:
/napi-build-utils@1.0.2:
resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==}
- dev: false
/natural-compare-lite@1.4.0:
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
@@ -15135,16 +15058,14 @@ packages:
resolution: {integrity: sha512-eSKV6s+APenqVh8ubJyiu/YhZgxQpGP66ntzUb3lY1xB9ukSRaGnx0AIxI+IM+1+IVYC1oWobgG5L3Lt9ARykQ==}
engines: {node: '>=10'}
dependencies:
- semver: 7.3.8
- dev: false
+ semver: 7.5.0
/node-addon-api@5.0.0:
resolution: {integrity: sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==}
dev: false
- /node-addon-api@6.0.0:
- resolution: {integrity: sha512-GyHvgPvUXBvAkXa0YvYnhilSB1A+FRYMpIVggKzPZqdaZfevZOuzfWzyvgzOwRLHBeo/MMswmJFsrNF4Nw1pmA==}
- dev: false
+ /node-addon-api@6.1.0:
+ resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==}
/node-bitmap@0.0.1:
resolution: {integrity: sha512-Jx5lPaaLdIaOsj2mVLWMWulXF6GQVdyLvNSxmiYCvZ8Ma2hfKX0POoR2kgKOqz+oFsRreq0yYZjQ2wjE9VNzCA==}
@@ -15204,7 +15125,7 @@ packages:
nopt: 6.0.0
npmlog: 6.0.2
rimraf: 3.0.2
- semver: 7.3.8
+ semver: 7.5.0
tar: 6.1.13
which: 2.0.2
transitivePeerDependencies:
@@ -15226,7 +15147,7 @@ packages:
dependencies:
has: 1.0.3
is: 3.3.0
- dev: true
+ dev: false
/nodemailer@6.9.1:
resolution: {integrity: sha512-qHw7dOiU5UKNnQpXktdgQ1d3OFgRAekuvbJLcdG5dnEo/GtcTHRYM7+UfJARdOFU9WUQO8OiIamgWPmiSFHYAA==}
@@ -15267,7 +15188,7 @@ packages:
dependencies:
hosted-git-info: 4.1.0
is-core-module: 2.11.0
- semver: 7.3.8
+ semver: 7.5.0
validate-npm-package-license: 3.0.4
dev: true
@@ -15300,6 +15221,7 @@ packages:
/normalize-url@6.1.0:
resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==}
engines: {node: '>=10'}
+ dev: false
/normalize-url@8.0.0:
resolution: {integrity: sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==}
@@ -15350,13 +15272,13 @@ packages:
set-blocking: 2.0.0
dev: false
- /nsfwjs@2.4.2(@tensorflow/tfjs@4.2.0):
+ /nsfwjs@2.4.2(@tensorflow/tfjs@4.4.0):
resolution: {integrity: sha512-i4Pp2yt59qPQgeZFyg3wXFBX52uSeu/hkDoqdZfe+sILRxNBUu0VDogj7Lmqak0GlrXviS/wLiVeIx40IDUu7A==}
peerDependencies:
'@tensorflow/tfjs': ^3.18.0
dependencies:
'@nsfw-filter/gif-frames': 1.0.2
- '@tensorflow/tfjs': 4.2.0(seedrandom@3.0.5)
+ '@tensorflow/tfjs': 4.4.0(seedrandom@3.0.5)
dev: false
/nth-check@2.1.1:
@@ -15404,7 +15326,7 @@ packages:
/object-hash@3.0.0:
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
engines: {node: '>= 6'}
- dev: true
+ dev: false
/object-inspect@1.12.2:
resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==}
@@ -15510,7 +15432,7 @@ packages:
resolution: {integrity: sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w==}
dependencies:
wrappy: 1.0.2
- dev: true
+ dev: false
/once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
@@ -15635,6 +15557,7 @@ packages:
/p-cancelable@2.1.1:
resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==}
engines: {node: '>=8'}
+ dev: false
/p-cancelable@3.0.0:
resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==}
@@ -15684,11 +15607,6 @@ packages:
p-limit: 3.1.0
dev: true
- /p-map@2.1.0:
- resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
- engines: {node: '>=6'}
- dev: false
-
/p-map@4.0.0:
resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
engines: {node: '>=10'}
@@ -15716,7 +15634,7 @@ packages:
/packageurl-js@1.0.1:
resolution: {integrity: sha512-EtXC0kgLjy/C7S4SN3Kk1SDRWLzIn/LUK0gXlz3gsxDdpI0k7q8C3SASpV0pc+v0yADBTt5rWewq/flGdGxtoQ==}
- dev: true
+ dev: false
/packet-reader@1.0.0:
resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==}
@@ -16091,7 +16009,7 @@ packages:
resolution: {integrity: sha512-hM58GKXOcj8WTqUXnsQyJYXdeAPbythQgEF3nTcEo+nkD49chjQ9IKm/QJy9xf6JakXptz86h7ecP2024rrLaQ==}
dependencies:
jsonc-parser: 3.2.0
- mlly: 1.1.1
+ mlly: 1.2.0
pathe: 1.1.0
dev: true
@@ -16121,7 +16039,7 @@ packages:
/pluralize@7.0.0:
resolution: {integrity: sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==}
engines: {node: '>=4'}
- dev: true
+ dev: false
/pngjs-nozlib@1.0.0:
resolution: {integrity: sha512-N1PggqLp9xDqwAoKvGohmZ3m4/N9xpY0nDZivFqQLcpLHmliHnCp9BuNCsOeqHWMuEEgFjpEaq9dZq6RZyy0fA==}
@@ -16376,6 +16294,14 @@ packages:
picocolors: 1.0.0
source-map-js: 1.0.2
+ /postcss@8.4.23:
+ resolution: {integrity: sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==}
+ engines: {node: ^10 || ^12 || >=14}
+ dependencies:
+ nanoid: 3.3.6
+ picocolors: 1.0.0
+ source-map-js: 1.0.2
+
/postgres-array@2.0.0:
resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
engines: {node: '>=4'}
@@ -16402,7 +16328,7 @@ packages:
detect-libc: 2.0.1
expand-template: 2.0.3
github-from-package: 0.0.0
- minimist: 1.2.7
+ minimist: 1.2.8
mkdirp-classic: 0.5.3
napi-build-utils: 1.0.2
node-abi: 3.31.0
@@ -16411,7 +16337,6 @@ packages:
simple-get: 4.0.1
tar-fs: 2.1.1
tunnel-agent: 0.6.0
- dev: false
/prelude-ls@1.1.2:
resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==}
@@ -16427,8 +16352,8 @@ packages:
engines: {node: '>=0.10.0'}
dev: false
- /prettier@2.8.7:
- resolution: {integrity: sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==}
+ /prettier@2.8.8:
+ resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
engines: {node: '>=10.13.0'}
hasBin: true
dev: true
@@ -16436,7 +16361,6 @@ packages:
/pretty-bytes@5.6.0:
resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
engines: {node: '>=6'}
- dev: true
/pretty-format@27.5.1:
resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
@@ -16527,7 +16451,7 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
promise: 7.3.1
- dev: true
+ dev: false
/promise-inflight@1.0.1:
resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==}
@@ -16565,7 +16489,7 @@ packages:
dependencies:
is-callable: 1.2.7
promise-deferred: 2.0.3
- dev: true
+ dev: false
/prompts@2.4.2:
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
@@ -16758,9 +16682,10 @@ packages:
engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
dev: false
- /qrcode@1.5.1:
- resolution: {integrity: sha512-nS8NJ1Z3md8uTjKtP+SGGhfqmTCs5flU/xR623oI0JX+Wepz9R8UrRVCTBTJm3qGw3rH6jJ6MUHjkDx15cxSSg==}
+ /qrcode@1.5.3:
+ resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==}
engines: {node: '>=10.13.0'}
+ hasBin: true
dependencies:
dijkstrajs: 1.0.2
encode-utf8: 1.0.3
@@ -16875,9 +16800,8 @@ packages:
dependencies:
deep-extend: 0.6.0
ini: 1.3.8
- minimist: 1.2.7
+ minimist: 1.2.8
strip-json-comments: 2.0.1
- dev: false
/rdf-canonize@3.3.0:
resolution: {integrity: sha512-gfSNkMua/VWC1eYbSkVaL/9LQhFeOh0QULwv7Or0f+po8pMgQ1blYQFe1r9Mv2GJZXw88Cz/drnAnB9UlNnHfQ==}
@@ -16908,12 +16832,12 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: true
- /react-docgen-typescript@2.2.2(typescript@5.0.3):
+ /react-docgen-typescript@2.2.2(typescript@5.0.4):
resolution: {integrity: sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==}
peerDependencies:
typescript: '>= 4.3.x'
dependencies:
- typescript: 5.0.3
+ typescript: 5.0.4
dev: true
/react-docgen@6.0.0-alpha.3:
@@ -17155,10 +17079,6 @@ packages:
strip-indent: 3.0.0
dev: true
- /redis-commands@1.7.0:
- resolution: {integrity: sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==}
- dev: false
-
/redis-errors@1.2.0:
resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==}
engines: {node: '>=4'}
@@ -17464,6 +17384,7 @@ packages:
resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==}
dependencies:
lowercase-keys: 2.0.0
+ dev: false
/responselike@3.0.0:
resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==}
@@ -17526,8 +17447,8 @@ packages:
seedrandom: 2.4.2
dev: false
- /rollup@3.20.2:
- resolution: {integrity: sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg==}
+ /rollup@3.21.3:
+ resolution: {integrity: sha512-VnPfEG51nIv2xPLnZaekkuN06q9ZbnyDcLkaBdJa/W7UddyhOfMP2yOPziYQfeY7k++fZM8FdQIummFN5y14kA==}
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
hasBin: true
optionalDependencies:
@@ -17537,11 +17458,11 @@ packages:
resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==}
dev: false
- /rss-parser@3.12.0:
- resolution: {integrity: sha512-aqD3E8iavcCdkhVxNDIdg1nkBI17jgqF+9OqPS1orwNaOgySdpvq6B+DoONLhzjzwV8mWg37sb60e4bmLK117A==}
+ /rss-parser@3.13.0:
+ resolution: {integrity: sha512-7jWUBV5yGN3rqMMj7CZufl/291QAhvrrGpDNE4k/02ZchL0npisiYYqULF71jCEKoIiHvK/Q2e6IkDwPziT7+w==}
dependencies:
entities: 2.2.0
- xml2js: 0.4.23
+ xml2js: 0.5.0
dev: false
/run-async@2.4.1:
@@ -17554,8 +17475,8 @@ packages:
dependencies:
queue-microtask: 1.2.3
- /rxjs@7.8.0:
- resolution: {integrity: sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==}
+ /rxjs@7.8.1:
+ resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
dependencies:
tslib: 2.5.0
@@ -17615,9 +17536,9 @@ packages:
postcss: 8.4.21
dev: false
- /sass@1.60.0:
- resolution: {integrity: sha512-updbwW6fNb5gGm8qMXzVO7V4sWf7LMXnMly/JEyfbfERbVH46Fn6q02BX7/eHTdKpE7d+oTkMMQpFWNUMfFbgQ==}
- engines: {node: '>=12.0.0'}
+ /sass@1.62.1:
+ resolution: {integrity: sha512-NHpxIzN29MXvWiuswfc1W3I0N8SXBd8UR26WntmDlRYf0bSADnwnOjsyMZ3lMezSlArD33Vs3YFhp7dWvL770A==}
+ engines: {node: '>=14.0.0'}
hasBin: true
dependencies:
chokidar: 3.5.3
@@ -17691,6 +17612,14 @@ packages:
hasBin: true
dependencies:
lru-cache: 6.0.0
+ dev: true
+
+ /semver@7.5.0:
+ resolution: {integrity: sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ lru-cache: 6.0.0
/send@0.18.0:
resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
@@ -17782,26 +17711,25 @@ packages:
detect-libc: 2.0.1
node-addon-api: 5.0.0
prebuild-install: 7.1.1
- semver: 7.3.8
+ semver: 7.5.0
simple-get: 4.0.1
tar-fs: 2.1.1
tunnel-agent: 0.6.0
dev: false
- /sharp@0.32.0:
- resolution: {integrity: sha512-yLAypVcqj1toSAqRSwbs86nEzfyZVDYqjuUX8grhFpeij0DDNagKJXELS/auegDBRDg1XBtELdOGfo2X1cCpeA==}
+ /sharp@0.32.1:
+ resolution: {integrity: sha512-kQTFtj7ldpUqSe8kDxoGLZc1rnMFU0AO2pqbX6pLy3b7Oj8ivJIdoKNwxHVQG2HN6XpHPJqCSM2nsma2gOXvOg==}
engines: {node: '>=14.15.0'}
requiresBuild: true
dependencies:
color: 4.2.3
detect-libc: 2.0.1
- node-addon-api: 6.0.0
+ node-addon-api: 6.1.0
prebuild-install: 7.1.1
- semver: 7.3.8
+ semver: 7.5.0
simple-get: 4.0.1
tar-fs: 2.1.1
tunnel-agent: 0.6.0
- dev: false
/shebang-command@1.2.0:
resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
@@ -17856,7 +17784,6 @@ packages:
/simple-concat@1.0.1:
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
- dev: false
/simple-get@4.0.1:
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
@@ -17864,13 +17791,11 @@ packages:
decompress-response: 6.0.0
once: 1.4.0
simple-concat: 1.0.1
- dev: false
/simple-swizzle@0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
dependencies:
is-arrayish: 0.3.2
- dev: false
/simple-update-notifier@1.1.0:
resolution: {integrity: sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==}
@@ -17894,6 +17819,121 @@ packages:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
dev: true
+ /slacc-android-arm-eabi@0.0.7:
+ resolution: {integrity: sha512-6TikZlR1jsQscxwphhrf0U4xbsRy6zKJ0zmEULopTzbohgo5OLdZ7L3tQazkYlaaFe3YjGnVLW3FfGhhrajVog==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [android]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /slacc-android-arm64@0.0.7:
+ resolution: {integrity: sha512-aol/9Rg0Hfqu81hpK+HXcx9sGYu4qqYU+djBCgLtb8I6ZMdWUdE0dp8ACBoTOmYn34hYGcUu4FlJUZ8r7Utucg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [android]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /slacc-darwin-arm64@0.0.7:
+ resolution: {integrity: sha512-PkV7rO/c9AImNYDacP+kxtOjVuxjy06IIOAxbWerIWvoeqsCNRtiF/dh+OqIACRFBuHIDe0oAyUCEMGUTnzjyQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /slacc-darwin-universal@0.0.7:
+ resolution: {integrity: sha512-Y9zXpL40m4Yq3dE5vdnAgfmn0Fxc0Bf0ixC9TSl96gKeIZEd6drkjfpHFdsIDNImzOksIAUo0HHiDdbEfE7zdQ==}
+ engines: {node: '>= 10'}
+ os: [darwin]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /slacc-darwin-x64@0.0.7:
+ resolution: {integrity: sha512-yKaGjX2YJl1QHe4NgqQVsY83jees3hjFxEUPoKpuZEQzWbMNn0XSyceFRGXIk1oDqiKU40UcsdcCedjYjSEd0Q==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /slacc-linux-arm-gnueabihf@0.0.7:
+ resolution: {integrity: sha512-pdWMdQeX6uA9JfSoWo9EHH0yRiwXKMbaKoS9gflDSyt/hjeR3Qx/KK7Wihd7HeXx7njlNdpr9ycTRmm5NgapQQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /slacc-linux-arm64-gnu@0.0.7:
+ resolution: {integrity: sha512-hz9TK/w6fxeNZXyFzuLq5cJD/XRyJbo6BaIdW+VrKKnb9nkLnWlqDQtdtJk7Fw7zHjdY3Uqufjwm0iT6qBVpUQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /slacc-linux-arm64-musl@0.0.7:
+ resolution: {integrity: sha512-wCDAYL7e+lh3XL7g87Ui/Bb2Ap9GcBqeJuj2yHIx6MYC8ontwFSXhqRTmd2zmPLmZA5Nc11aKGN11YNu0Pnwlw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /slacc-linux-x64-gnu@0.0.7:
+ resolution: {integrity: sha512-E5+2cveizpfHXCk/Hu5VfslWFeDVw47nywODiJ8CsofT2l5ITfYPMFEBXm9ORY25mGBTgsO6lJYiF9Hz4FlS9Q==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /slacc-win32-arm64-msvc@0.0.7:
+ resolution: {integrity: sha512-3a+qnkZbP+Pr5RZuzd0Vi1uCal137QiJajRAWT4r7qwu+Zidd50x2oikQ4rAegqZVTm8qTwVmWA+WmH8WHI7iw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /slacc-win32-x64-msvc@0.0.7:
+ resolution: {integrity: sha512-ydFdZ7wEXQPsw2Tg+yG9uJdCGTehyPtrWBVUMa7fojr3j1gbtThXS2l9Ad/6fYYi2VwdaYPLWbwV3GYElPGL8g==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /slacc@0.0.7:
+ resolution: {integrity: sha512-rwi2F3oJaGPST9JdCoUd5fnSZaoZFgTL00GFKhKufT48uwtUEAHlOL0t8gEVmon71X+53f9nEdsGWhwtOutJTQ==}
+ engines: {node: '>= 10'}
+ optionalDependencies:
+ slacc-android-arm-eabi: 0.0.7
+ slacc-android-arm64: 0.0.7
+ slacc-darwin-arm64: 0.0.7
+ slacc-darwin-universal: 0.0.7
+ slacc-darwin-x64: 0.0.7
+ slacc-linux-arm-gnueabihf: 0.0.7
+ slacc-linux-arm64-gnu: 0.0.7
+ slacc-linux-arm64-musl: 0.0.7
+ slacc-linux-x64-gnu: 0.0.7
+ slacc-win32-arm64-msvc: 0.0.7
+ slacc-win32-x64-msvc: 0.0.7
+ dev: false
+
/slash@3.0.0:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}
@@ -17921,14 +17961,6 @@ packages:
is-fullwidth-code-point: 3.0.0
dev: true
- /slice-ansi@5.0.0:
- resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
- engines: {node: '>=12'}
- dependencies:
- ansi-styles: 6.2.1
- is-fullwidth-code-point: 4.0.0
- dev: true
-
/smart-buffer@4.2.0:
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
@@ -17969,10 +18001,10 @@ packages:
async: 3.2.4
debug: 4.3.4(supports-color@8.1.1)
lodash.merge: 4.6.2
- minimist: 1.2.7
+ minimist: 1.2.8
transitivePeerDependencies:
- supports-color
- dev: true
+ dev: false
/snyk-nodejs-lockfile-parser@1.48.2:
resolution: {integrity: sha512-CiuKigz4Ed/prR61T2hDDNnvqCr9JA4zTWE9xD4x+emt2zUVRHKF2RBKq2s82RA8jcts1OOXnREr1v1OvNxIpg==}
@@ -17990,13 +18022,13 @@ packages:
lodash.isempty: 4.4.0
lodash.topairs: 4.3.0
micromatch: 4.0.5
- semver: 7.3.8
+ semver: 7.5.0
snyk-config: 5.1.0
tslib: 1.14.1
uuid: 8.3.2
transitivePeerDependencies:
- supports-color
- dev: true
+ dev: false
/socks-proxy-agent@7.0.0:
resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==}
@@ -18221,11 +18253,11 @@ packages:
resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==}
dev: true
- /storybook@7.0.2:
- resolution: {integrity: sha512-/XBLhT9Vb14yNBcA9rlW15y+C6IsCA3kx5PKvK9kL10sKCi8invcY94UfCSisXe8HqsO3u6peumo2xpYucKMjw==}
+ /storybook@7.0.7:
+ resolution: {integrity: sha512-MaFAhpPm/KsaoIQfGzapnRyXNh1VbS8l38BNZR5ZD97ejGkLukJ7TO4fFS87Hyy6whAXo6tTdtqeCByMQ9gRFA==}
hasBin: true
dependencies:
- '@storybook/cli': 7.0.2
+ '@storybook/cli': 7.0.7
transitivePeerDependencies:
- bufferutil
- encoding
@@ -18243,7 +18275,7 @@ packages:
/stream-buffers@3.0.2:
resolution: {integrity: sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==}
engines: {node: '>= 0.10.0'}
- dev: true
+ dev: false
/stream-combiner@0.0.4:
resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==}
@@ -18269,7 +18301,7 @@ packages:
resolution: {integrity: sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==}
dependencies:
any-promise: 1.3.0
- dev: true
+ dev: false
/stream-to-promise@2.2.0:
resolution: {integrity: sha512-HAGUASw8NT0k8JvIVutB2Y/9iBk7gpgEyAudXwNJmZERdMITGdajOa4VJfD/kNiA3TppQpTP4J+CtcHwdzKBAw==}
@@ -18277,7 +18309,7 @@ packages:
any-promise: 1.3.0
end-of-stream: 1.1.0
stream-to-array: 2.3.0
- dev: true
+ dev: false
/stream-wormhole@1.1.0:
resolution: {integrity: sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==}
@@ -18347,15 +18379,6 @@ packages:
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
- /string-width@5.1.2:
- resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
- engines: {node: '>=12'}
- dependencies:
- eastasianwidth: 0.2.0
- emoji-regex: 9.2.2
- strip-ansi: 7.0.1
- dev: true
-
/string.prototype.trimend@1.0.6:
resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==}
dependencies:
@@ -18412,13 +18435,6 @@ packages:
dependencies:
ansi-regex: 5.0.1
- /strip-ansi@7.0.1:
- resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==}
- engines: {node: '>=12'}
- dependencies:
- ansi-regex: 6.0.1
- dev: true
-
/strip-bom@2.0.0:
resolution: {integrity: sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==}
engines: {node: '>=0.10.0'}
@@ -18459,7 +18475,6 @@ packages:
/strip-json-comments@2.0.1:
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
engines: {node: '>=0.10.0'}
- dev: false
/strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
@@ -18742,11 +18757,21 @@ packages:
engines: {node: '>=0.10.0'}
dev: false
+ /time-zone@1.0.0:
+ resolution: {integrity: sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==}
+ engines: {node: '>=4'}
+ dev: true
+
/tiny-lru@10.0.1:
resolution: {integrity: sha512-Vst+6kEsWvb17Zpz14sRJV/f8bUWKhqm6Dc+v08iShmIJ/WxqWytHzCTd6m88pS33rE2zpX34TRmOpAJPloNCA==}
engines: {node: '>=6'}
dev: false
+ /tiny-lru@11.0.1:
+ resolution: {integrity: sha512-iNgFugVuQgBKrqeO/mpiTTgmBsTP0WL6yeuLfLs/Ctf0pI/ixGqIRm8sDCwMcXGe9WWvt2sGXI5mNqZbValmJg==}
+ engines: {node: '>=12'}
+ dev: false
+
/tinybench@2.4.0:
resolution: {integrity: sha512-iyziEiyFxX4kyxSp+MtY1oCH/lvjH3PxFN8PGCDeqcZWAJ/i+9y+nL85w99PxVzrIvew/GSkSbDYtiGVa85Afg==}
dev: true
@@ -18760,8 +18785,8 @@ packages:
engines: {node: '>=14.0.0'}
dev: true
- /tinyspy@1.1.1:
- resolution: {integrity: sha512-UVq5AXt/gQlti7oxoIg5oi/9r0WpF7DGEVwXgqWSMmyN16+e3tl5lIvTaOpJ3TAtu5xFzWccFRM4R5NaWHF+4g==}
+ /tinyspy@2.1.0:
+ resolution: {integrity: sha512-7eORpyqImoOvkQJCSkL0d0mB4NHHIFAy4b1u8PHdDa7SjGS2njzl6/lyGoZLm+eyYEtlUmFGE0rFj66SWxZgQQ==}
engines: {node: '>=14.0.0'}
dev: true
@@ -18885,7 +18910,7 @@ packages:
/treeify@1.1.0:
resolution: {integrity: sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==}
engines: {node: '>=0.6'}
- dev: true
+ dev: false
/trim-newlines@3.0.1:
resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==}
@@ -18908,8 +18933,8 @@ packages:
resolution: {integrity: sha512-vDWbsl26LIcPGmDpoVzjEP6+hvHZkBkLW7JpvwbCv/5IYPJlsbzCVXY3wsCeAxAUeTclNOUZxnLdGh3VBD/J6w==}
dev: true
- /tsc-alias@1.8.5:
- resolution: {integrity: sha512-Y3ka0olwSRdbHPyX5kXhYY2aoBKuT53DFdeY+PpQUR4hg5M/b8eIRmC8dL4FBdd0wT366iWc6iDUUGe6QwI7mg==}
+ /tsc-alias@1.8.6:
+ resolution: {integrity: sha512-vq+i6VpE83IeMsSJVcFN03ZBofADhr8/gIJXjxpbnTRfN/MFXy0+SBaKG2o7p95QqXBGkeG98HYz3IkOOveFbg==}
hasBin: true
dependencies:
chokidar: 3.5.3
@@ -18925,7 +18950,7 @@ packages:
dependencies:
'@types/json5': 0.0.29
json5: 1.0.1
- minimist: 1.2.7
+ minimist: 1.2.8
strip-bom: 3.0.0
dev: true
@@ -18958,14 +18983,14 @@ packages:
/tslib@2.5.0:
resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==}
- /tsutils@3.21.0(typescript@5.0.3):
+ /tsutils@3.21.0(typescript@5.0.4):
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
engines: {node: '>= 6'}
peerDependencies:
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
dependencies:
tslib: 1.14.1
- typescript: 5.0.3
+ typescript: 5.0.4
dev: true
/tunnel-agent@0.6.0:
@@ -18976,7 +19001,7 @@ packages:
/tunnel@0.0.6:
resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==}
engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'}
- dev: true
+ dev: false
/tweetnacl@0.14.5:
resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==}
@@ -19062,8 +19087,8 @@ packages:
/typedarray@0.0.6:
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
- /typeorm@0.3.13(ioredis@4.28.5)(pg@8.10.0):
- resolution: {integrity: sha512-AqyKG4xtYST2zpgX/LdAleP8WAjwyO7EemRtG67EmmbFpWsPBk9BVr87wkN59huBned3qPEehVOSUQ81pNuH8A==}
+ /typeorm@0.3.15(ioredis@5.3.2)(pg@8.10.0):
+ resolution: {integrity: sha512-R4JSw8QjDP1W+ypeRz/XrCXIqubrLSnNAzJAp9EQSQIPHTv+YmUHZis8g08lOwFpuhqL9m8jkPSz8GWEKlU/ow==}
engines: {node: '>= 12.9.0'}
hasBin: true
peerDependencies:
@@ -19072,7 +19097,7 @@ packages:
better-sqlite3: ^7.1.2 || ^8.0.0
hdb-pool: ^0.1.6
ioredis: ^5.0.4
- mongodb: ^3.6.0
+ mongodb: ^5.2.0
mssql: ^9.1.1
mysql2: ^2.2.5 || ^3.0.1
oracledb: ^5.1.0
@@ -19128,15 +19153,13 @@ packages:
debug: 4.3.4(supports-color@8.1.1)
dotenv: 16.0.3
glob: 8.1.0
- ioredis: 4.28.5
- js-yaml: 4.1.0
+ ioredis: 5.3.2
mkdirp: 2.1.6
pg: 8.10.0
reflect-metadata: 0.1.13
sha.js: 2.4.11
tslib: 2.5.0
uuid: 9.0.0
- xml2js: 0.4.23
yargs: 17.6.2
transitivePeerDependencies:
- supports-color
@@ -19148,8 +19171,8 @@ packages:
hasBin: true
dev: true
- /typescript@5.0.3:
- resolution: {integrity: sha512-xv8mOEDnigb/tN9PSMTwSEqAnUvkoXMQlicOb0IUVDBSQCgBSaAAROUZYy2IcUy5qU6XajK5jjjO7TMWqBTKZA==}
+ /typescript@5.0.4:
+ resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==}
engines: {node: '>=12.20'}
hasBin: true
@@ -19348,6 +19371,15 @@ packages:
webpack-virtual-modules: 0.4.6
dev: true
+ /unplugin@1.3.1:
+ resolution: {integrity: sha512-h4uUTIvFBQRxUKS2Wjys6ivoeofGhxzTe2sRWlooyjHXVttcVfV/JiavNd3d4+jty0SVV0dxGw9AkY9MwiaCEw==}
+ dependencies:
+ acorn: 8.8.2
+ chokidar: 3.5.3
+ webpack-sources: 3.2.3
+ webpack-virtual-modules: 0.5.0
+ dev: false
+
/unset-value@1.0.0:
resolution: {integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==}
engines: {node: '>=0.10.0'}
@@ -19451,10 +19483,6 @@ packages:
engines: {node: '>= 0.4.0'}
dev: true
- /uuid-browser@3.1.0:
- resolution: {integrity: sha512-dsNgbLaTrd6l3MMxTtouOCFw4CBFc/3a+GgYA2YyrJvyQ1u6q4pcu3ktLoUZ/VN/Aw9WsauazbgsgdfVWgAKQg==}
- dev: true
-
/uuid@3.4.0:
resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==}
deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
@@ -19467,7 +19495,6 @@ packages:
/uuid@9.0.0:
resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==}
- dev: false
/v8-to-istanbul@9.0.1:
resolution: {integrity: sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==}
@@ -19575,17 +19602,17 @@ packages:
replace-ext: 1.0.1
dev: false
- /vite-node@0.29.8(@types/node@18.15.11)(sass@1.60.0):
- resolution: {integrity: sha512-b6OtCXfk65L6SElVM20q5G546yu10/kNrhg08afEoWlFRJXFq9/6glsvSVY+aI6YeC1tu2TtAqI2jHEQmOmsFw==}
- engines: {node: '>=v14.16.0'}
+ /vite-node@0.30.1(@types/node@18.16.3)(sass@1.62.1):
+ resolution: {integrity: sha512-vTikpU/J7e6LU/8iM3dzBo8ZhEiKZEKRznEMm+mJh95XhWaPrJQraT/QsT2NWmuEf+zgAoMe64PKT7hfZ1Njmg==}
+ engines: {node: '>=v14.18.0'}
hasBin: true
dependencies:
cac: 6.7.14
debug: 4.3.4(supports-color@8.1.1)
- mlly: 1.1.1
+ mlly: 1.2.0
pathe: 1.1.0
picocolors: 1.0.0
- vite: 4.2.1(@types/node@18.15.11)(sass@1.60.0)
+ vite: 4.3.4(@types/node@18.16.3)(sass@1.62.1)
transitivePeerDependencies:
- '@types/node'
- less
@@ -19596,12 +19623,12 @@ packages:
- terser
dev: true
- /vite-plugin-turbosnap@1.0.1:
- resolution: {integrity: sha512-isVvISdXZyflIsXYrpTMBnyrtZq92ftohL8/xHi1H0kUwXIFDegqedX1kCKIQ04tjUkphB0cFbGzuvOGVwVTnQ==}
+ /vite-plugin-turbosnap@1.0.2:
+ resolution: {integrity: sha512-irjKcKXRn7v5bPAg4mAbsS6DgibpP1VUFL9tlgxU6lloK6V9yw9qCZkS+s2PtbkZpWNzr3TN3zVJAc6J7gJZmA==}
dev: true
- /vite@4.2.1(@types/node@18.15.11)(sass@1.60.0):
- resolution: {integrity: sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==}
+ /vite@4.3.4(@types/node@18.16.3)(sass@1.62.1):
+ resolution: {integrity: sha512-f90aqGBoxSFxWph2b39ae2uHAxm5jFBBdnfueNxZAT1FTpM13ccFQExCaKbR2xFW5atowjleRniQ7onjJ22QEg==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
@@ -19625,30 +19652,29 @@ packages:
terser:
optional: true
dependencies:
- '@types/node': 18.15.11
- esbuild: 0.17.14
- postcss: 8.4.21
- resolve: 1.22.1
- rollup: 3.20.2
- sass: 1.60.0
+ '@types/node': 18.16.3
+ esbuild: 0.17.18
+ postcss: 8.4.23
+ rollup: 3.21.3
+ sass: 1.62.1
optionalDependencies:
fsevents: 2.3.2
- /vitest-fetch-mock@0.2.2(vitest@0.29.8):
+ /vitest-fetch-mock@0.2.2(vitest@0.30.1):
resolution: {integrity: sha512-XmH6QgTSjCWrqXoPREIdbj40T7i1xnGmAsTAgfckoO75W1IEHKR8hcPCQ7SO16RsdW1t85oUm6pcQRLeBgjVYQ==}
engines: {node: '>=14.14.0'}
peerDependencies:
vitest: '>=0.16.0'
dependencies:
cross-fetch: 3.1.5
- vitest: 0.29.8(happy-dom@8.9.0)(sass@1.60.0)
+ vitest: 0.30.1(happy-dom@9.10.2)(sass@1.62.1)
transitivePeerDependencies:
- encoding
dev: true
- /vitest@0.29.8(happy-dom@8.9.0)(sass@1.60.0):
- resolution: {integrity: sha512-JIAVi2GK5cvA6awGpH0HvH/gEG9PZ0a/WoxdiV3PmqK+3CjQMf8c+J/Vhv4mdZ2nRyXFw66sAg6qz7VNkaHfDQ==}
- engines: {node: '>=v14.16.0'}
+ /vitest@0.30.1(happy-dom@9.10.2)(sass@1.62.1):
+ resolution: {integrity: sha512-y35WTrSTlTxfMLttgQk4rHcaDkbHQwDP++SNwPb+7H8yb13Q3cu2EixrtHzF27iZ8v0XCciSsLg00RkPAzB/aA==}
+ engines: {node: '>=v14.18.0'}
hasBin: true
peerDependencies:
'@edge-runtime/vm': '*'
@@ -19679,18 +19705,21 @@ packages:
dependencies:
'@types/chai': 4.3.4
'@types/chai-subset': 1.3.3
- '@types/node': 18.15.11
- '@vitest/expect': 0.29.8
- '@vitest/runner': 0.29.8
- '@vitest/spy': 0.29.8
- '@vitest/utils': 0.29.8
+ '@types/node': 18.16.3
+ '@vitest/expect': 0.30.1
+ '@vitest/runner': 0.30.1
+ '@vitest/snapshot': 0.30.1
+ '@vitest/spy': 0.30.1
+ '@vitest/utils': 0.30.1
acorn: 8.8.2
acorn-walk: 8.2.0
cac: 6.7.14
chai: 4.3.7
+ concordance: 5.0.4
debug: 4.3.4(supports-color@8.1.1)
- happy-dom: 8.9.0
+ happy-dom: 9.10.2
local-pkg: 0.4.3
+ magic-string: 0.30.0
pathe: 1.1.0
picocolors: 1.0.0
source-map: 0.6.1
@@ -19698,9 +19727,8 @@ packages:
strip-literal: 1.0.1
tinybench: 2.4.0
tinypool: 0.4.0
- tinyspy: 1.1.1
- vite: 4.2.1(@types/node@18.15.11)(sass@1.60.0)
- vite-node: 0.29.8(@types/node@18.15.11)(sass@1.60.0)
+ vite: 4.3.4(@types/node@18.16.3)(sass@1.62.1)
+ vite-node: 0.30.1(@types/node@18.16.3)(sass@1.62.1)
why-is-node-running: 2.2.2
transitivePeerDependencies:
- less
@@ -19718,8 +19746,8 @@ packages:
/vue-docgen-api@4.64.1(vue@3.2.47):
resolution: {integrity: sha512-jbOf7ByE3Zvtuk+429Jorl+eIeh2aB2Fx1GUo3xJd1aByJWE8KDlSEa6b11PB1ze8f0sRUBraRDinICCk0KY7g==}
dependencies:
- '@babel/parser': 7.21.3
- '@babel/types': 7.21.3
+ '@babel/parser': 7.21.4
+ '@babel/types': 7.21.4
'@vue/compiler-dom': 3.2.47
'@vue/compiler-sfc': 3.2.47
ast-types: 0.14.2
@@ -19733,20 +19761,20 @@ packages:
- vue
dev: true
- /vue-eslint-parser@9.1.1(eslint@8.37.0):
+ /vue-eslint-parser@9.1.1(eslint@8.39.0):
resolution: {integrity: sha512-C2aI/r85Q6tYcz4dpgvrs4wH/MqVrRAVIdpYedrxnATDHHkb+TroeRcDpKWGZCx/OcECMWfz7tVwQ8e+Opy6rA==}
engines: {node: ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: '>=6.0.0'
dependencies:
debug: 4.3.4(supports-color@8.1.1)
- eslint: 8.37.0
+ eslint: 8.39.0
eslint-scope: 7.1.1
eslint-visitor-keys: 3.4.0
espree: 9.5.1
esquery: 1.4.2
lodash: 4.17.21
- semver: 7.3.8
+ semver: 7.5.0
transitivePeerDependencies:
- supports-color
dev: true
@@ -19782,15 +19810,16 @@ packages:
he: 1.2.0
dev: true
- /vue-tsc@1.2.0(typescript@5.0.3):
- resolution: {integrity: sha512-rIlzqdrhyPYyLG9zxsVRa+JEseeS9s8F2BbVVVWRRsTZvJO2BbhLEb2HW3MY+DFma0378tnIqs+vfTzbcQtRFw==}
+ /vue-tsc@1.6.3(typescript@5.0.4):
+ resolution: {integrity: sha512-q7l27j0eSJgyGat0khetrvoeaAHieRZFnf8WAJyKvB3eF0AxmLqfs4ahwZhaojBJjZ/lAXZa+Xt8EX54KzQ34w==}
hasBin: true
peerDependencies:
typescript: '*'
dependencies:
- '@volar/vue-language-core': 1.2.0
- '@volar/vue-typescript': 1.2.0
- typescript: 5.0.3
+ '@volar/vue-language-core': 1.6.3
+ '@volar/vue-typescript': 1.6.3(typescript@5.0.4)
+ semver: 7.5.0
+ typescript: 5.0.4
dev: true
/vue@2.7.14:
@@ -19833,8 +19862,8 @@ packages:
axios: 0.27.2(debug@4.3.4)
joi: 17.7.0
lodash: 4.17.21
- minimist: 1.2.7
- rxjs: 7.8.0
+ minimist: 1.2.8
+ rxjs: 7.8.1
transitivePeerDependencies:
- debug
dev: true
@@ -19867,15 +19896,16 @@ packages:
'@zxing/text-encoding': 0.9.0
dev: true
- /web-push@3.5.0:
- resolution: {integrity: sha512-JC0V9hzKTqlDYJ+LTZUXtW7B175qwwaqzbbMSWDxHWxZvd3xY0C2rcotMGDavub2nAAFw+sXTsqR65/KY2A5AQ==}
+ /web-push@3.6.1:
+ resolution: {integrity: sha512-+eN2/4Ybu+nkRgXqlPXUzqeA5BgQ7J4StYJM/e2uJpsSIGhkZJFrTlCx+ow9sD0mn+4jgbo2FkFoMBKftl49ZQ==}
engines: {node: '>= 6'}
+ hasBin: true
dependencies:
asn1.js: 5.4.1
http_ece: 1.1.0
https-proxy-agent: 5.0.1
jws: 4.0.0
- minimist: 1.2.7
+ minimist: 1.2.8
urlsafe-base64: 1.0.0
transitivePeerDependencies:
- supports-color
@@ -19895,12 +19925,15 @@ packages:
/webpack-sources@3.2.3:
resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
engines: {node: '>=10.13.0'}
- dev: true
/webpack-virtual-modules@0.4.6:
resolution: {integrity: sha512-5tyDlKLqPfMqjT3Q9TAqf2YqjwmnUleZwzJi1A5qXnlBCdj2AtOJ6wAWdglTIDOPgOiOrXeBeFcsQ8+aGQ6QbA==}
dev: true
+ /webpack-virtual-modules@0.5.0:
+ resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==}
+ dev: false
+
/websocket@1.0.34:
resolution: {integrity: sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==}
engines: {node: '>=4.0.0'}
@@ -19915,6 +19948,11 @@ packages:
- supports-color
dev: false
+ /well-known-symbols@2.0.0:
+ resolution: {integrity: sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==}
+ engines: {node: '>=6'}
+ dev: true
+
/whatwg-encoding@2.0.0:
resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
engines: {node: '>=12'}
@@ -20021,7 +20059,7 @@ packages:
resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==}
engines: {node: '>= 10.0.0'}
dependencies:
- '@babel/parser': 7.21.3
+ '@babel/parser': 7.21.4
'@babel/types': 7.21.4
assert-never: 1.2.1
babel-walk: 3.0.0-canary-5
@@ -20126,8 +20164,8 @@ packages:
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
engines: {node: '>=12'}
- /xml2js@0.4.23:
- resolution: {integrity: sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==}
+ /xml2js@0.5.0:
+ resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==}
engines: {node: '>=4.0.0'}
dependencies:
sax: 1.2.4
@@ -20329,7 +20367,7 @@ packages:
sharp: 0.31.3
dev: false
- github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.0.2)(@storybook/components@7.0.2)(@storybook/core-events@7.0.2)(@storybook/manager-api@7.0.2)(@storybook/preview-api@7.0.2)(@storybook/theming@7.0.2)(@storybook/types@7.0.2)(react-dom@18.2.0)(react@18.2.0):
+ github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.0.7)(@storybook/components@7.0.7)(@storybook/core-events@7.0.7)(@storybook/manager-api@7.0.7)(@storybook/preview-api@7.0.7)(@storybook/theming@7.0.7)(@storybook/types@7.0.7)(react-dom@18.2.0)(react@18.2.0):
resolution: {tarball: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640}
id: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640
name: storybook-addon-misskey-theme
@@ -20350,21 +20388,21 @@ packages:
react-dom:
optional: true
dependencies:
- '@storybook/blocks': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/components': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/core-events': 7.0.2
- '@storybook/manager-api': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/preview-api': 7.0.2
- '@storybook/theming': 7.0.2(react-dom@18.2.0)(react@18.2.0)
- '@storybook/types': 7.0.2
+ '@storybook/blocks': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/components': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/core-events': 7.0.7
+ '@storybook/manager-api': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/preview-api': 7.0.7
+ '@storybook/theming': 7.0.7(react-dom@18.2.0)(react@18.2.0)
+ '@storybook/types': 7.0.7
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: true
- github.com/misskey-dev/summaly/1bab7afee616429b8bbf7a7cbcbb8ebcef66d992:
- resolution: {tarball: https://codeload.github.com/misskey-dev/summaly/tar.gz/1bab7afee616429b8bbf7a7cbcbb8ebcef66d992}
+ github.com/misskey-dev/summaly/2d63e2a0066f89871e777cc81d43c1ade8c97517:
+ resolution: {tarball: https://codeload.github.com/misskey-dev/summaly/tar.gz/2d63e2a0066f89871e777cc81d43c1ade8c97517}
name: summaly
- version: 4.0.1
+ version: 4.0.2
dependencies:
cheerio: 1.0.0-rc.12
escape-regexp: 0.0.1