summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2022-06-11 19:31:03 +0900
committerGitHub <noreply@github.com>2022-06-11 19:31:03 +0900
commit182a1bf653ecfbcf76e4530b3077c6252b0d4827 (patch)
tree45d1472747d4cac017e96616f844292f5785ccdd
parent12.110.1 (diff)
parent12.111.0 (diff)
downloadsharkey-182a1bf653ecfbcf76e4530b3077c6252b0d4827.tar.gz
sharkey-182a1bf653ecfbcf76e4530b3077c6252b0d4827.tar.bz2
sharkey-182a1bf653ecfbcf76e4530b3077c6252b0d4827.zip
Merge pull request #8783 from misskey-dev/develop
Release: 12.111.0
-rw-r--r--.github/ISSUE_TEMPLATE/01_bug-report.md5
-rw-r--r--.github/labeler.yml12
-rw-r--r--.github/workflows/labeler.yml16
-rw-r--r--.github/workflows/lint.yml64
-rw-r--r--.github/workflows/ok-to-test.yml36
-rw-r--r--.github/workflows/pr-preview-deploy.yml95
-rw-r--r--.github/workflows/pr-preview-destroy.yml21
-rw-r--r--.github/workflows/test.yml6
-rw-r--r--.node-version2
-rw-r--r--.okteto/okteto-pipeline.yml6
-rw-r--r--.vscode/extensions.json3
-rw-r--r--CHANGELOG.md51
-rw-r--r--CONTRIBUTING.md36
-rw-r--r--Dockerfile17
-rw-r--r--README.md169
-rw-r--r--assets/title_float.svg67
-rw-r--r--chart/Chart.yaml3
-rw-r--r--chart/files/default.yml165
-rw-r--r--chart/templates/ConfigMap.yml8
-rw-r--r--chart/templates/Deployment.yml47
-rw-r--r--chart/templates/Service.yml14
-rw-r--r--chart/templates/_helpers.tpl62
-rw-r--r--chart/values.yml3
-rw-r--r--cypress.config.ts12
-rw-r--r--cypress.json3
-rw-r--r--cypress/e2e/basic.cy.js (renamed from cypress/integration/basic.js)55
-rw-r--r--cypress/e2e/widgets.cy.js65
-rw-r--r--cypress/support/commands.js30
-rw-r--r--cypress/support/e2e.js (renamed from cypress/support/index.js)0
-rw-r--r--docker-compose.yml2
-rw-r--r--gulpfile.js1
-rw-r--r--locales/ar-SA.yml27
-rw-r--r--locales/bn-BD.yml16
-rw-r--r--locales/ca-ES.yml160
-rw-r--r--locales/cs-CZ.yml30
-rw-r--r--locales/de-DE.yml14
-rw-r--r--locales/en-US.yml12
-rw-r--r--locales/es-ES.yml78
-rw-r--r--locales/fr-FR.yml6
-rw-r--r--locales/id-ID.yml12
-rw-r--r--locales/it-IT.yml11
-rw-r--r--locales/ja-JP.yml17
-rw-r--r--locales/ja-KS.yml4
-rw-r--r--locales/kab-KAB.yml2
-rw-r--r--locales/kn-IN.yml2
-rw-r--r--locales/ko-KR.yml26
-rw-r--r--locales/nl-NL.yml5
-rw-r--r--locales/pl-PL.yml4
-rw-r--r--locales/pt-PT.yml321
-rw-r--r--locales/ro-RO.yml77
-rw-r--r--locales/ru-RU.yml19
-rw-r--r--locales/sk-SK.yml7
-rw-r--r--locales/sv-SE.yml319
-rw-r--r--locales/uk-UA.yml56
-rw-r--r--locales/vi-VN.yml1613
-rw-r--r--locales/zh-CN.yml43
-rw-r--r--locales/zh-TW.yml173
-rw-r--r--package.json16
-rw-r--r--packages/backend/.eslintrc.cjs23
-rw-r--r--packages/backend/.mocharc.json2
-rw-r--r--packages/backend/.vscode/settings.json4
-rw-r--r--packages/backend/migration/1651224615271-foreign-key.js89
-rw-r--r--packages/backend/migration/1652859567549-uniform-themecolor.js36
-rw-r--r--packages/backend/package.json160
-rw-r--r--packages/backend/src/@types/http-signature.d.ts4
-rw-r--r--packages/backend/src/@types/jsrsasign.d.ts800
-rw-r--r--packages/backend/src/boot/index.ts4
-rw-r--r--packages/backend/src/boot/master.ts36
-rw-r--r--packages/backend/src/config/load.ts2
-rw-r--r--packages/backend/src/config/types.ts1
-rw-r--r--packages/backend/src/daemons/queue-stats.ts4
-rw-r--r--packages/backend/src/daemons/server-stats.ts4
-rw-r--r--packages/backend/src/db/postgre.ts19
-rw-r--r--packages/backend/src/mfm/from-html.ts3
-rw-r--r--packages/backend/src/misc/cache.ts1
-rw-r--r--packages/backend/src/misc/cafy-id.ts33
-rw-r--r--packages/backend/src/misc/create-temp.ts13
-rw-r--r--packages/backend/src/misc/fetch-meta.ts13
-rw-r--r--packages/backend/src/misc/fetch.ts10
-rw-r--r--packages/backend/src/misc/get-ip-hash.ts9
-rw-r--r--packages/backend/src/misc/populate-emojis.ts2
-rw-r--r--packages/backend/src/misc/schema.ts37
-rw-r--r--packages/backend/src/models/entities/access-token.ts6
-rw-r--r--packages/backend/src/models/entities/auth-session.ts2
-rw-r--r--packages/backend/src/models/entities/clip.ts2
-rw-r--r--packages/backend/src/models/entities/drive-file.ts1
-rw-r--r--packages/backend/src/models/entities/emoji.ts1
-rw-r--r--packages/backend/src/models/entities/instance.ts20
-rw-r--r--packages/backend/src/models/entities/meta.ts4
-rw-r--r--packages/backend/src/models/entities/muting.ts1
-rw-r--r--packages/backend/src/models/entities/note.ts6
-rw-r--r--packages/backend/src/models/entities/user-profile.ts1
-rw-r--r--packages/backend/src/models/entities/user.ts2
-rw-r--r--packages/backend/src/models/repositories/drive-file.ts47
-rw-r--r--packages/backend/src/models/repositories/note.ts36
-rw-r--r--packages/backend/src/models/repositories/page.ts6
-rw-r--r--packages/backend/src/models/repositories/user.ts91
-rw-r--r--packages/backend/src/queue/index.ts4
-rw-r--r--packages/backend/src/queue/processors/db/export-blocking.ts105
-rw-r--r--packages/backend/src/queue/processors/db/export-custom-emojis.ts17
-rw-r--r--packages/backend/src/queue/processors/db/export-following.ts105
-rw-r--r--packages/backend/src/queue/processors/db/export-mute.ts107
-rw-r--r--packages/backend/src/queue/processors/db/export-notes.ts117
-rw-r--r--packages/backend/src/queue/processors/db/export-user-lists.ts63
-rw-r--r--packages/backend/src/queue/processors/db/import-custom-emojis.ts10
-rw-r--r--packages/backend/src/queue/processors/inbox.ts2
-rw-r--r--packages/backend/src/queue/types.ts2
-rw-r--r--packages/backend/src/remote/activitypub/db-resolver.ts105
-rw-r--r--packages/backend/src/remote/activitypub/kernel/announce/note.ts3
-rw-r--r--packages/backend/src/remote/activitypub/kernel/delete/index.ts22
-rw-r--r--packages/backend/src/remote/activitypub/kernel/move/index.ts0
-rw-r--r--packages/backend/src/remote/activitypub/kernel/undo/announce.ts1
-rw-r--r--packages/backend/src/remote/activitypub/misc/get-note-html.ts6
-rw-r--r--packages/backend/src/remote/activitypub/models/mention.ts8
-rw-r--r--packages/backend/src/remote/activitypub/models/note.ts15
-rw-r--r--packages/backend/src/remote/activitypub/models/person.ts62
-rw-r--r--packages/backend/src/remote/activitypub/renderer/block.ts24
-rw-r--r--packages/backend/src/remote/activitypub/renderer/flag.ts2
-rw-r--r--packages/backend/src/remote/activitypub/renderer/follow.ts3
-rw-r--r--packages/backend/src/remote/activitypub/renderer/index.ts2
-rw-r--r--packages/backend/src/remote/activitypub/renderer/note.ts26
-rw-r--r--packages/backend/src/remote/activitypub/resolver.ts72
-rw-r--r--packages/backend/src/remote/activitypub/type.ts16
-rw-r--r--packages/backend/src/server/activitypub.ts31
-rw-r--r--packages/backend/src/server/activitypub/followers.ts24
-rw-r--r--packages/backend/src/server/activitypub/following.ts21
-rw-r--r--packages/backend/src/server/activitypub/outbox.ts37
-rw-r--r--packages/backend/src/server/api/2fa.ts4
-rw-r--r--packages/backend/src/server/api/call.ts44
-rw-r--r--packages/backend/src/server/api/common/generate-visibility-query.ts14
-rw-r--r--packages/backend/src/server/api/common/read-messaging-message.ts29
-rw-r--r--packages/backend/src/server/api/common/read-notification.ts26
-rw-r--r--packages/backend/src/server/api/endpoints.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/admin/announcements/list.ts19
-rw-r--r--packages/backend/src/server/api/endpoints/admin/show-user.ts42
-rw-r--r--packages/backend/src/server/api/endpoints/admin/show-users.ts6
-rw-r--r--packages/backend/src/server/api/endpoints/admin/update-meta.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/drive/files/check-existence.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/delete.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/drive/files/find.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/drive/files/show.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/drive/files/update.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/i/2fa/register.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/notes/create.ts36
-rw-r--r--packages/backend/src/server/api/endpoints/notes/reactions.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/notes/translate.ts15
-rw-r--r--packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/notifications/read.ts41
-rw-r--r--packages/backend/src/server/api/endpoints/pages/show.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/request-reset-password.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/reset-db.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/reset-password.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/sw/register.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/sw/unregister.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/test.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/users/clips.ts12
-rw-r--r--packages/backend/src/server/api/endpoints/users/followers.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/following.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/gallery/posts.ts12
-rw-r--r--packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/groups/create.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/groups/delete.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/groups/invite.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/groups/joined.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/groups/leave.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/groups/owned.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/groups/pull.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/groups/show.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/groups/transfer.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/groups/update.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/lists/create.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/lists/delete.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/lists/list.ts2
-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/lists/show.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/lists/update.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/notes.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/pages.ts12
-rw-r--r--packages/backend/src/server/api/endpoints/users/reactions.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/recommendation.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/relation.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/report-abuse.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/search.ts11
-rw-r--r--packages/backend/src/server/api/endpoints/users/show.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/stats.ts176
-rw-r--r--packages/backend/src/server/api/limiter.ts26
-rw-r--r--packages/backend/src/server/api/openapi/gen-spec.ts16
-rw-r--r--packages/backend/src/server/api/private/signin.ts17
-rw-r--r--packages/backend/src/server/api/service/discord.ts30
-rw-r--r--packages/backend/src/server/api/service/github.ts30
-rw-r--r--packages/backend/src/server/api/service/twitter.ts20
-rw-r--r--packages/backend/src/server/api/stream/channels/queue-stats.ts4
-rw-r--r--packages/backend/src/server/api/stream/channels/server-stats.ts4
-rw-r--r--packages/backend/src/server/api/stream/index.ts42
-rw-r--r--packages/backend/src/server/api/streaming.ts2
-rw-r--r--packages/backend/src/server/file/send-drive-file.ts17
-rw-r--r--packages/backend/src/server/index.ts28
-rw-r--r--packages/backend/src/server/proxy/proxy-media.ts8
-rw-r--r--packages/backend/src/server/web/boot.js21
-rw-r--r--packages/backend/src/server/web/index.ts37
-rw-r--r--packages/backend/src/server/web/manifest.ts14
-rw-r--r--packages/backend/src/server/web/style.css30
-rw-r--r--packages/backend/src/server/web/url-preview.ts2
-rw-r--r--packages/backend/src/server/web/views/base.pug46
-rw-r--r--packages/backend/src/server/well-known.ts1
-rw-r--r--packages/backend/src/services/blocking/create.ts30
-rw-r--r--packages/backend/src/services/blocking/delete.ts9
-rw-r--r--packages/backend/src/services/chart/core.ts27
-rw-r--r--packages/backend/src/services/chart/entities.ts12
-rw-r--r--packages/backend/src/services/create-notification.ts4
-rw-r--r--packages/backend/src/services/drive/add-file.ts29
-rw-r--r--packages/backend/src/services/drive/generate-video-thumbnail.ts54
-rw-r--r--packages/backend/src/services/drive/image-processor.ts28
-rw-r--r--packages/backend/src/services/drive/upload-from-url.ts25
-rw-r--r--packages/backend/src/services/fetch-instance-metadata.ts14
-rw-r--r--packages/backend/src/services/following/create.ts6
-rw-r--r--packages/backend/src/services/following/delete.ts11
-rw-r--r--packages/backend/src/services/messages/create.ts2
-rw-r--r--packages/backend/src/services/note/create.ts5
-rw-r--r--packages/backend/src/services/note/delete.ts8
-rw-r--r--packages/backend/src/services/note/reaction/create.ts5
-rw-r--r--packages/backend/src/services/push-notification.ts24
-rw-r--r--packages/backend/src/services/relay.ts6
-rw-r--r--packages/backend/test/.eslintrc7
-rw-r--r--packages/backend/test/.eslintrc.cjs11
-rw-r--r--packages/backend/test/activitypub.ts10
-rw-r--r--packages/backend/test/ap-request.ts8
-rw-r--r--packages/backend/test/api-visibility.ts28
-rw-r--r--packages/backend/test/block.ts2
-rw-r--r--packages/backend/test/chart.ts158
-rw-r--r--packages/backend/test/extract-mentions.ts14
-rw-r--r--packages/backend/test/fetch-resource.ts6
-rw-r--r--packages/backend/test/get-file-info.ts43
-rw-r--r--packages/backend/test/loader.js61
-rw-r--r--packages/backend/test/misc/mock-resolver.ts6
-rw-r--r--packages/backend/test/mute.ts4
-rw-r--r--packages/backend/test/note.ts92
-rw-r--r--packages/backend/test/prelude/url.ts2
-rw-r--r--packages/backend/test/streaming.ts142
-rw-r--r--packages/backend/test/user-notes.ts19
-rw-r--r--packages/backend/test/utils.ts87
-rw-r--r--packages/backend/tsconfig.json7
-rw-r--r--packages/backend/yarn.lock1127
-rw-r--r--packages/client/.eslintrc.js111
-rw-r--r--packages/client/@types/theme.d.ts7
-rw-r--r--packages/client/package.json117
-rw-r--r--packages/client/src/account.ts16
-rw-r--r--packages/client/src/components/abuse-report-window.vue2
-rw-r--r--packages/client/src/components/abuse-report.vue10
-rw-r--r--packages/client/src/components/analog-clock.vue2
-rw-r--r--packages/client/src/components/autocomplete.vue22
-rw-r--r--packages/client/src/components/captcha.vue3
-rw-r--r--packages/client/src/components/channel-follow-button.vue4
-rw-r--r--packages/client/src/components/chart.vue1540
-rw-r--r--packages/client/src/components/cropper-dialog.vue175
-rw-r--r--packages/client/src/components/cw-button.vue2
-rw-r--r--packages/client/src/components/dialog.vue16
-rw-r--r--packages/client/src/components/drive-file-thumbnail.vue2
-rw-r--r--packages/client/src/components/drive-select-dialog.vue4
-rw-r--r--packages/client/src/components/drive-window.vue2
-rw-r--r--packages/client/src/components/drive.file.vue26
-rw-r--r--packages/client/src/components/drive.folder.vue22
-rw-r--r--packages/client/src/components/drive.nav-folder.vue42
-rw-r--r--packages/client/src/components/drive.vue98
-rw-r--r--packages/client/src/components/emoji-picker-window.vue4
-rw-r--r--packages/client/src/components/emoji-picker.section.vue2
-rw-r--r--packages/client/src/components/emoji-picker.vue16
-rw-r--r--packages/client/src/components/follow-button.vue40
-rw-r--r--packages/client/src/components/forgot-password.vue4
-rw-r--r--packages/client/src/components/form-dialog.vue2
-rw-r--r--packages/client/src/components/form/folder.vue2
-rw-r--r--packages/client/src/components/form/radios.vue2
-rw-r--r--packages/client/src/components/form/range.vue4
-rw-r--r--packages/client/src/components/form/switch.vue2
-rw-r--r--packages/client/src/components/global/a.vue25
-rw-r--r--packages/client/src/components/global/avatar.vue2
-rw-r--r--packages/client/src/components/global/emoji.vue2
-rw-r--r--packages/client/src/components/global/header.vue2
-rw-r--r--packages/client/src/components/global/loading.vue76
-rw-r--r--packages/client/src/components/global/misskey-flavored-markdown.vue26
-rw-r--r--packages/client/src/components/global/time.vue5
-rw-r--r--packages/client/src/components/global/url.vue4
-rw-r--r--packages/client/src/components/image-viewer.vue2
-rw-r--r--packages/client/src/components/instance-ticker.vue13
-rw-r--r--packages/client/src/components/link.vue4
-rw-r--r--packages/client/src/components/media-caption.vue16
-rw-r--r--packages/client/src/components/media-video.vue3
-rw-r--r--packages/client/src/components/mention.vue42
-rw-r--r--packages/client/src/components/mfm.ts27
-rw-r--r--packages/client/src/components/modal-page-window.vue16
-rw-r--r--packages/client/src/components/note-detailed.vue20
-rw-r--r--packages/client/src/components/note-simple.vue2
-rw-r--r--packages/client/src/components/note.vue16
-rw-r--r--packages/client/src/components/notification-setting-window.vue15
-rw-r--r--packages/client/src/components/notification.vue15
-rw-r--r--packages/client/src/components/notifications.vue32
-rw-r--r--packages/client/src/components/number-diff.vue4
-rw-r--r--packages/client/src/components/page/page.image.vue28
-rw-r--r--packages/client/src/components/page/page.post.vue12
-rw-r--r--packages/client/src/components/page/page.vue10
-rw-r--r--packages/client/src/components/poll-editor.vue2
-rw-r--r--packages/client/src/components/post-form-attaches.vue14
-rw-r--r--packages/client/src/components/post-form.vue106
-rw-r--r--packages/client/src/components/queue-chart.vue2
-rw-r--r--packages/client/src/components/reactions-viewer.reaction.vue12
-rw-r--r--packages/client/src/components/sample.vue2
-rw-r--r--packages/client/src/components/signin-dialog.vue16
-rw-r--r--packages/client/src/components/signin.vue363
-rw-r--r--packages/client/src/components/signup-dialog.vue4
-rw-r--r--packages/client/src/components/signup.vue42
-rw-r--r--packages/client/src/components/timeline.vue6
-rw-r--r--packages/client/src/components/toast.vue2
-rw-r--r--packages/client/src/components/ui/button.vue24
-rw-r--r--packages/client/src/components/ui/context-menu.vue6
-rw-r--r--packages/client/src/components/ui/folder.vue2
-rw-r--r--packages/client/src/components/ui/menu.vue23
-rw-r--r--packages/client/src/components/ui/modal-window.vue128
-rw-r--r--packages/client/src/components/ui/modal.vue11
-rw-r--r--packages/client/src/components/ui/pagination.vue24
-rw-r--r--packages/client/src/components/ui/popup-menu.vue2
-rw-r--r--packages/client/src/components/ui/tooltip.vue10
-rw-r--r--packages/client/src/components/ui/window.vue54
-rw-r--r--packages/client/src/components/url-preview.vue2
-rw-r--r--packages/client/src/components/user-preview.vue2
-rw-r--r--packages/client/src/components/user-select-dialog.vue6
-rw-r--r--packages/client/src/components/visibility-picker.vue6
-rw-r--r--packages/client/src/components/waiting-dialog.vue4
-rw-r--r--packages/client/src/components/widgets.vue8
-rw-r--r--packages/client/src/directives/adaptive-border.ts2
-rw-r--r--packages/client/src/directives/get-size.ts6
-rw-r--r--packages/client/src/directives/panel.ts2
-rw-r--r--packages/client/src/directives/size.ts4
-rw-r--r--packages/client/src/directives/tooltip.ts4
-rw-r--r--packages/client/src/directives/user-preview.ts4
-rw-r--r--packages/client/src/emojilist.json42
-rw-r--r--packages/client/src/filters/bytes.ts2
-rw-r--r--packages/client/src/init.ts49
-rw-r--r--packages/client/src/instance.ts4
-rw-r--r--packages/client/src/os.ts181
-rw-r--r--packages/client/src/pages/about-misskey.vue1
-rw-r--r--packages/client/src/pages/admin/abuses.vue68
-rw-r--r--packages/client/src/pages/admin/ads.vue144
-rw-r--r--packages/client/src/pages/admin/announcements.vue148
-rw-r--r--packages/client/src/pages/admin/bot-protection.vue89
-rw-r--r--packages/client/src/pages/admin/database.vue31
-rw-r--r--packages/client/src/pages/admin/email-settings.vue161
-rw-r--r--packages/client/src/pages/admin/emoji-edit-dialog.vue116
-rw-r--r--packages/client/src/pages/admin/emojis.vue22
-rw-r--r--packages/client/src/pages/admin/file-dialog.vue86
-rw-r--r--packages/client/src/pages/admin/files.vue4
-rw-r--r--packages/client/src/pages/admin/index.vue539
-rw-r--r--packages/client/src/pages/admin/instance-block.vue58
-rw-r--r--packages/client/src/pages/admin/integrations.discord.vue65
-rw-r--r--packages/client/src/pages/admin/integrations.github.vue65
-rw-r--r--packages/client/src/pages/admin/integrations.twitter.vue63
-rw-r--r--packages/client/src/pages/admin/integrations.vue61
-rw-r--r--packages/client/src/pages/admin/metrics.vue2
-rw-r--r--packages/client/src/pages/admin/object-storage.vue170
-rw-r--r--packages/client/src/pages/admin/other-settings.vue61
-rw-r--r--packages/client/src/pages/admin/overview.vue116
-rw-r--r--packages/client/src/pages/admin/proxy-account.vue83
-rw-r--r--packages/client/src/pages/admin/queue.chart.vue72
-rw-r--r--packages/client/src/pages/admin/queue.vue87
-rw-r--r--packages/client/src/pages/admin/relays.vue117
-rw-r--r--packages/client/src/pages/admin/security.vue71
-rw-r--r--packages/client/src/pages/admin/settings.vue266
-rw-r--r--packages/client/src/pages/api-console.vue98
-rw-r--r--packages/client/src/pages/auth.form.vue2
-rw-r--r--packages/client/src/pages/channel-editor.vue4
-rw-r--r--packages/client/src/pages/emojis.category.vue6
-rw-r--r--packages/client/src/pages/emojis.vue4
-rw-r--r--packages/client/src/pages/federation.vue2
-rw-r--r--packages/client/src/pages/follow.vue2
-rw-r--r--packages/client/src/pages/gallery/edit.vue6
-rw-r--r--packages/client/src/pages/gallery/post.vue4
-rw-r--r--packages/client/src/pages/messaging/index.vue12
-rw-r--r--packages/client/src/pages/messaging/messaging-room.form.vue66
-rw-r--r--packages/client/src/pages/messaging/messaging-room.vue32
-rw-r--r--packages/client/src/pages/mfm-cheat-sheet.vue18
-rw-r--r--packages/client/src/pages/miauth.vue5
-rw-r--r--packages/client/src/pages/my-antennas/edit.vue55
-rw-r--r--packages/client/src/pages/my-antennas/editor.vue187
-rw-r--r--packages/client/src/pages/my-antennas/index.vue39
-rw-r--r--packages/client/src/pages/note.vue8
-rw-r--r--packages/client/src/pages/page-editor/page-editor.vue2
-rw-r--r--packages/client/src/pages/page.vue4
-rw-r--r--packages/client/src/pages/reset-password.vue4
-rw-r--r--packages/client/src/pages/scratchpad.vue163
-rw-r--r--packages/client/src/pages/settings/2fa.vue318
-rw-r--r--packages/client/src/pages/settings/account-info.vue103
-rw-r--r--packages/client/src/pages/settings/accounts.vue139
-rw-r--r--packages/client/src/pages/settings/api.vue67
-rw-r--r--packages/client/src/pages/settings/apps.vue58
-rw-r--r--packages/client/src/pages/settings/custom-css.vue56
-rw-r--r--packages/client/src/pages/settings/deck.vue106
-rw-r--r--packages/client/src/pages/settings/delete-account.vue78
-rw-r--r--packages/client/src/pages/settings/drive.vue127
-rw-r--r--packages/client/src/pages/settings/email.vue121
-rw-r--r--packages/client/src/pages/settings/general.vue263
-rw-r--r--packages/client/src/pages/settings/import-export.vue154
-rw-r--r--packages/client/src/pages/settings/index.vue103
-rw-r--r--packages/client/src/pages/settings/instance-mute.vue76
-rw-r--r--packages/client/src/pages/settings/integration.vue167
-rw-r--r--packages/client/src/pages/settings/menu.vue131
-rw-r--r--packages/client/src/pages/settings/notifications.vue88
-rw-r--r--packages/client/src/pages/settings/other.vue66
-rw-r--r--packages/client/src/pages/settings/plugin.install.vue186
-rw-r--r--packages/client/src/pages/settings/plugin.vue111
-rw-r--r--packages/client/src/pages/settings/profile.vue32
-rw-r--r--packages/client/src/pages/settings/reaction.vue4
-rw-r--r--packages/client/src/pages/settings/security.vue132
-rw-r--r--packages/client/src/pages/settings/sounds.vue162
-rw-r--r--packages/client/src/pages/settings/theme.install.vue4
-rw-r--r--packages/client/src/pages/settings/theme.manage.vue106
-rw-r--r--packages/client/src/pages/settings/theme.vue154
-rw-r--r--packages/client/src/pages/settings/webhook.edit.vue16
-rw-r--r--packages/client/src/pages/settings/word-mute.vue190
-rw-r--r--packages/client/src/pages/share.vue8
-rw-r--r--packages/client/src/pages/theme-editor.vue14
-rw-r--r--packages/client/src/pages/timeline.vue2
-rw-r--r--packages/client/src/pages/user-info.vue7
-rw-r--r--packages/client/src/pages/user/index.vue18
-rw-r--r--packages/client/src/pages/welcome.setup.vue2
-rw-r--r--packages/client/src/pages/welcome.timeline.vue2
-rw-r--r--packages/client/src/router.ts51
-rw-r--r--packages/client/src/scripts/2fa.ts8
-rw-r--r--packages/client/src/scripts/autocomplete.ts28
-rw-r--r--packages/client/src/scripts/contains.ts2
-rw-r--r--packages/client/src/scripts/emojilist.ts2
-rw-r--r--packages/client/src/scripts/extract-avg-color-from-blurhash.ts2
-rw-r--r--packages/client/src/scripts/format-time-string.ts6
-rw-r--r--packages/client/src/scripts/get-account-from-id.ts2
-rw-r--r--packages/client/src/scripts/get-md5.ts10
-rw-r--r--packages/client/src/scripts/get-note-menu.ts20
-rw-r--r--packages/client/src/scripts/get-note-summary.ts2
-rw-r--r--packages/client/src/scripts/get-user-menu.ts11
-rw-r--r--packages/client/src/scripts/get-user-name.ts3
-rw-r--r--packages/client/src/scripts/hotkey.ts24
-rw-r--r--packages/client/src/scripts/hpml/evaluator.ts10
-rw-r--r--packages/client/src/scripts/hpml/lib.ts4
-rw-r--r--packages/client/src/scripts/idb-proxy.ts4
-rw-r--r--packages/client/src/scripts/initialize-sw.ts30
-rw-r--r--packages/client/src/scripts/lookup-user.ts6
-rw-r--r--packages/client/src/scripts/navigate.ts34
-rw-r--r--packages/client/src/scripts/physics.ts6
-rw-r--r--packages/client/src/scripts/please-login.ts19
-rw-r--r--packages/client/src/scripts/popout.ts5
-rw-r--r--packages/client/src/scripts/reaction-picker.ts4
-rw-r--r--packages/client/src/scripts/select-file.ts13
-rw-r--r--packages/client/src/scripts/theme-editor.ts2
-rw-r--r--packages/client/src/scripts/theme.ts51
-rw-r--r--packages/client/src/scripts/upload.ts114
-rw-r--r--packages/client/src/scripts/url.ts2
-rw-r--r--packages/client/src/scripts/use-note-capture.ts4
-rw-r--r--packages/client/src/store.ts7
-rw-r--r--packages/client/src/sw/compose-notification.ts107
-rw-r--r--packages/client/src/sw/sw.ts123
-rw-r--r--packages/client/src/theme-store.ts8
-rw-r--r--packages/client/src/ui/_common_/common.vue11
-rw-r--r--packages/client/src/ui/_common_/sidebar-for-mobile.vue6
-rw-r--r--packages/client/src/ui/_common_/sidebar.vue4
-rw-r--r--packages/client/src/ui/_common_/sw-inject.ts44
-rw-r--r--packages/client/src/ui/_common_/upload.vue2
-rw-r--r--packages/client/src/ui/classic.header.vue4
-rw-r--r--packages/client/src/ui/classic.sidebar.vue6
-rw-r--r--packages/client/src/ui/classic.widgets.vue4
-rw-r--r--packages/client/src/ui/deck.vue2
-rw-r--r--packages/client/src/ui/deck/antenna-column.vue4
-rw-r--r--packages/client/src/ui/deck/column-core.vue2
-rw-r--r--packages/client/src/ui/deck/column.vue34
-rw-r--r--packages/client/src/ui/deck/deck-store.ts22
-rw-r--r--packages/client/src/ui/deck/direct-column.vue2
-rw-r--r--packages/client/src/ui/deck/list-column.vue4
-rw-r--r--packages/client/src/ui/deck/main-column.vue2
-rw-r--r--packages/client/src/ui/deck/mentions-column.vue2
-rw-r--r--packages/client/src/ui/deck/notifications-column.vue6
-rw-r--r--packages/client/src/ui/deck/tl-column.vue4
-rw-r--r--packages/client/src/ui/deck/widgets-column.vue4
-rw-r--r--packages/client/src/ui/universal.widgets.vue8
-rw-r--r--packages/client/src/widgets/activity.calendar.vue68
-rw-r--r--packages/client/src/widgets/activity.chart.vue89
-rw-r--r--packages/client/src/widgets/activity.vue8
-rw-r--r--packages/client/src/widgets/aichan.vue4
-rw-r--r--packages/client/src/widgets/aiscript.vue10
-rw-r--r--packages/client/src/widgets/button.vue8
-rw-r--r--packages/client/src/widgets/calendar.vue2
-rw-r--r--packages/client/src/widgets/clock.vue4
-rw-r--r--packages/client/src/widgets/digital-clock.vue2
-rw-r--r--packages/client/src/widgets/federation.vue4
-rw-r--r--packages/client/src/widgets/job-queue.vue2
-rw-r--r--packages/client/src/widgets/memo.vue4
-rw-r--r--packages/client/src/widgets/notifications.vue7
-rw-r--r--packages/client/src/widgets/online-users.vue2
-rw-r--r--packages/client/src/widgets/photos.vue4
-rw-r--r--packages/client/src/widgets/post-form.vue4
-rw-r--r--packages/client/src/widgets/rss.vue4
-rw-r--r--packages/client/src/widgets/server-metric/cpu-mem.vue125
-rw-r--r--packages/client/src/widgets/server-metric/cpu.vue49
-rw-r--r--packages/client/src/widgets/server-metric/index.vue4
-rw-r--r--packages/client/src/widgets/server-metric/mem.vue62
-rw-r--r--packages/client/src/widgets/server-metric/net.vue122
-rw-r--r--packages/client/src/widgets/slideshow.vue6
-rw-r--r--packages/client/src/widgets/timeline.vue12
-rw-r--r--packages/client/src/widgets/trends.vue4
-rw-r--r--packages/client/src/widgets/widget.ts4
-rw-r--r--packages/client/tsconfig.json10
-rw-r--r--packages/client/vite.config.ts72
-rw-r--r--packages/client/vite.json5.ts38
-rw-r--r--packages/client/webpack.config.js193
-rw-r--r--packages/client/yarn.lock3144
-rw-r--r--packages/shared/.eslintrc.js3
-rw-r--r--packages/sw/.eslintrc.js22
-rw-r--r--packages/sw/.npmrc2
-rw-r--r--packages/sw/.yarnrc1
-rw-r--r--packages/sw/build.js37
-rw-r--r--packages/sw/package.json17
-rw-r--r--packages/sw/src/filters/user.ts14
-rw-r--r--packages/sw/src/scripts/create-notification.ts237
-rw-r--r--packages/sw/src/scripts/get-account-from-id.ts7
-rw-r--r--packages/sw/src/scripts/get-user-name.ts3
-rw-r--r--packages/sw/src/scripts/i18n.ts29
-rw-r--r--packages/sw/src/scripts/lang.ts47
-rw-r--r--packages/sw/src/scripts/login-id.ts11
-rw-r--r--packages/sw/src/scripts/notification-read.ts50
-rw-r--r--packages/sw/src/scripts/operations.ts70
-rw-r--r--packages/sw/src/sw.ts200
-rw-r--r--packages/sw/src/types.ts31
-rw-r--r--packages/sw/tsconfig.json39
-rw-r--r--packages/sw/yarn.lock710
-rw-r--r--scripts/build.js8
-rw-r--r--scripts/clean-all.js3
-rw-r--r--scripts/clean.js1
-rw-r--r--scripts/dev.js6
-rw-r--r--scripts/install-packages.js10
-rw-r--r--scripts/lint.js7
-rw-r--r--yarn.lock175
544 files changed, 14859 insertions, 12469 deletions
diff --git a/.github/ISSUE_TEMPLATE/01_bug-report.md b/.github/ISSUE_TEMPLATE/01_bug-report.md
index 8734fc0c36..0fecce2ee8 100644
--- a/.github/ISSUE_TEMPLATE/01_bug-report.md
+++ b/.github/ISSUE_TEMPLATE/01_bug-report.md
@@ -22,7 +22,10 @@ First, in order to avoid duplicate Issues, please search to see if the problem y
## 🤬 Actual Behavior
-<!--- Tell us what happens instead of the expected behavior -->
+<!--
+Tell us what happens instead of the expected behavior.
+Please include errors from the developer console and/or server log files if you have access to them.
+-->
## 📠Steps to Reproduce
diff --git a/.github/labeler.yml b/.github/labeler.yml
new file mode 100644
index 0000000000..98f1d2e383
--- /dev/null
+++ b/.github/labeler.yml
@@ -0,0 +1,12 @@
+'âš™ï¸Server':
+- packages/backend/**/*
+
+'🖥ï¸Client':
+- packages/client/**/*
+
+'🧪Test':
+- cypress/**/*
+- packages/backend/test/**/*
+
+'â€¼ï¸ wrong locales':
+- any: ['locales/*.yml', '!locales/ja-JP.yml']
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
new file mode 100644
index 0000000000..fa4a58c3a9
--- /dev/null
+++ b/.github/workflows/labeler.yml
@@ -0,0 +1,16 @@
+name: "Pull Request Labeler"
+on:
+ pull_request_target:
+ branches-ignore:
+ - 'l10n_develop'
+
+jobs:
+ triage:
+ permissions:
+ contents: read
+ pull-requests: write
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/labeler@v4
+ with:
+ repo-token: "${{ secrets.GITHUB_TOKEN }}"
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index da2c73a656..4e42fa9314 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -1,25 +1,39 @@
-name: Lint
-
-on:
- push:
- branches:
- - master
- - develop
- pull_request:
-
-jobs:
- lint:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- with:
- submodules: true
- - uses: actions/setup-node@v3
- with:
- node-version: 16.x
- cache: 'yarn'
- cache-dependency-path: |
- packages/backend/yarn.lock
- packages/client/yarn.lock
- - run: yarn install
- - run: yarn lint
+name: Lint
+
+on:
+ push:
+ branches:
+ - master
+ - develop
+ pull_request:
+
+jobs:
+ backend:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ submodules: true
+ - uses: actions/setup-node@v3
+ with:
+ node-version: 18.x
+ cache: 'yarn'
+ cache-dependency-path: |
+ packages/backend/yarn.lock
+ - run: yarn install
+ - run: yarn --cwd ./packages/backend lint
+
+ client:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ submodules: true
+ - uses: actions/setup-node@v3
+ with:
+ node-version: 18.x
+ cache: 'yarn'
+ cache-dependency-path: |
+ packages/client/yarn.lock
+ - run: yarn install
+ - run: yarn --cwd ./packages/client lint
diff --git a/.github/workflows/ok-to-test.yml b/.github/workflows/ok-to-test.yml
new file mode 100644
index 0000000000..87af3a6ba6
--- /dev/null
+++ b/.github/workflows/ok-to-test.yml
@@ -0,0 +1,36 @@
+# If someone with write access comments "/ok-to-test" on a pull request, emit a repository_dispatch event
+name: Ok To Test
+
+on:
+ issue_comment:
+ types: [created]
+
+jobs:
+ ok-to-test:
+ runs-on: ubuntu-latest
+ # Only run for PRs, not issue comments
+ if: ${{ github.event.issue.pull_request }}
+ steps:
+ # Generate a GitHub App installation access token from an App ID and private key
+ # To create a new GitHub App:
+ # https://developer.github.com/apps/building-github-apps/creating-a-github-app/
+ # See app.yml for an example app manifest
+ - name: Generate token
+ id: generate_token
+ uses: tibdex/github-app-token@v1
+ with:
+ app_id: ${{ secrets.DEPLOYBOT_APP_ID }}
+ private_key: ${{ secrets.DEPLOYBOT_PRIVATE_KEY }}
+
+ - name: Slash Command Dispatch
+ uses: peter-evans/slash-command-dispatch@v1
+ env:
+ TOKEN: ${{ steps.generate_token.outputs.token }}
+ with:
+ token: ${{ env.TOKEN }} # GitHub App installation access token
+ # token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} # PAT or OAuth token will also work
+ reaction-token: ${{ secrets.GITHUB_TOKEN }}
+ issue-type: pull-request
+ commands: deploy
+ named-args: true
+ permission: write
diff --git a/.github/workflows/pr-preview-deploy.yml b/.github/workflows/pr-preview-deploy.yml
new file mode 100644
index 0000000000..fd43bce9e6
--- /dev/null
+++ b/.github/workflows/pr-preview-deploy.yml
@@ -0,0 +1,95 @@
+# Run secret-dependent integration tests only after /deploy approval
+on:
+ pull_request:
+ types: [opened, reopened, synchronize]
+ repository_dispatch:
+ types: [deploy-command]
+
+name: Deploy preview environment
+
+jobs:
+ # Repo owner has commented /deploy on a (fork-based) pull request
+ deploy-preview-environment:
+ runs-on: ubuntu-latest
+ if:
+ github.event_name == 'repository_dispatch' &&
+ github.event.client_payload.slash_command.sha != '' &&
+ contains(github.event.client_payload.pull_request.head.sha, github.event.client_payload.slash_command.sha)
+ steps:
+ - uses: actions/github-script@v5
+ id: check-id
+ env:
+ number: ${{ github.event.client_payload.pull_request.number }}
+ job: ${{ github.job }}
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ result-encoding: string
+ script: |
+ const { data: pull } = await github.rest.pulls.get({
+ ...context.repo,
+ pull_number: process.env.number
+ });
+ const ref = pull.head.sha;
+
+ const { data: checks } = await github.rest.checks.listForRef({
+ ...context.repo,
+ ref
+ });
+
+ const check = checks.check_runs.filter(c => c.name === process.env.job);
+
+ return check[0].id;
+
+ - uses: actions/github-script@v5
+ env:
+ check_id: ${{ steps.check-id.outputs.result }}
+ details_url: ${{ github.server_url }}/${{ github.repository }}/runs/${{ github.run_id }}
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ await github.rest.checks.update({
+ ...context.repo,
+ check_run_id: process.env.check_id,
+ status: 'in_progress',
+ details_url: process.env.details_url
+ });
+
+ # Check out merge commit
+ - name: Fork based /deploy checkout
+ uses: actions/checkout@v2
+ with:
+ ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge'
+
+ # <insert integration tests needing secrets>
+ - name: Context
+ uses: okteto/context@latest
+ with:
+ token: ${{ secrets.OKTETO_TOKEN }}
+
+ - name: Deploy preview environment
+ uses: ikuradon/deploy-preview@latest
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ name: pr-${{ github.event.client_payload.pull_request.number }}-syuilo
+ timeout: 15m
+
+ # Update check run called "integration-fork"
+ - uses: actions/github-script@v5
+ id: update-check-run
+ if: ${{ always() }}
+ env:
+ # Conveniently, job.status maps to https://developer.github.com/v3/checks/runs/#update-a-check-run
+ conclusion: ${{ job.status }}
+ check_id: ${{ steps.check-id.outputs.result }}
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const { data: result } = await github.rest.checks.update({
+ ...context.repo,
+ check_run_id: process.env.check_id,
+ status: 'completed',
+ conclusion: process.env.conclusion
+ });
+
+ return result;
diff --git a/.github/workflows/pr-preview-destroy.yml b/.github/workflows/pr-preview-destroy.yml
new file mode 100644
index 0000000000..c14c3db5c5
--- /dev/null
+++ b/.github/workflows/pr-preview-destroy.yml
@@ -0,0 +1,21 @@
+# file: .github/workflows/preview-closed.yaml
+on:
+ pull_request:
+ types:
+ - closed
+
+name: Destroy preview environment
+
+jobs:
+ destroy-preview-environment:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Context
+ uses: okteto/context@latest
+ with:
+ token: ${{ secrets.OKTETO_TOKEN }}
+
+ - name: Destroy preview environment
+ uses: okteto/destroy-preview@latest
+ with:
+ name: pr-${{ github.event.number }}-syuilo
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index d57d85c874..c32c82e2a1 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -13,7 +13,7 @@ jobs:
strategy:
matrix:
- node-version: [16.x]
+ node-version: [18.x]
services:
postgres:
@@ -57,7 +57,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [16.x]
+ node-version: [18.x]
browser: [chrome]
services:
@@ -103,7 +103,7 @@ jobs:
- name: ALSA Env
run: echo -e 'pcm.!default {\n type hw\n card 0\n}\n\nctl.!default {\n type hw\n card 0\n}' > ~/.asoundrc
- name: Cypress run
- uses: cypress-io/github-action@v2
+ uses: cypress-io/github-action@v4
with:
install: false
start: npm run start:test
diff --git a/.node-version b/.node-version
index bf79505bb8..7fd023741b 100644
--- a/.node-version
+++ b/.node-version
@@ -1 +1 @@
-v16.14.0
+v16.15.0
diff --git a/.okteto/okteto-pipeline.yml b/.okteto/okteto-pipeline.yml
new file mode 100644
index 0000000000..e2996fbbc9
--- /dev/null
+++ b/.okteto/okteto-pipeline.yml
@@ -0,0 +1,6 @@
+build:
+ misskey:
+ args:
+ - NODE_ENV=development
+deploy:
+ - helm upgrade --install misskey chart --set image=${OKTETO_BUILD_MISSKEY_IMAGE} --set url="https://misskey-$(kubectl config view --minify -o jsonpath='{..namespace}').cloud.okteto.net" --set environment=development
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 9adb0d0697..42264548ea 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -3,6 +3,7 @@
"editorconfig.editorconfig",
"eg2.vscode-npm-script",
"dbaeumer.vscode-eslint",
- "johnsoncodehk.volar",
+ "Vue.volar",
+ "Vue.vscode-typescript-vue-plugin"
]
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dff67b40dc..eb16a1675d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,6 @@
## 12.x.x (unreleased)
### Improvements
--
### Bugfixes
-
@@ -10,11 +9,59 @@
You should also include the user name that made the change.
-->
+## 12.111.0 (2022/06/11)
+### Improvements
+- Supports Unicode Emoji 14.0 @mei23
+- プッシュ通知を複数アカウント対応㫠#7667 @tamaina
+- プッシュ通知ã«ã‚¯ãƒªãƒƒã‚¯ã‚„actionを設定 #7667 @tamaina
+- ドライブã«ç”»åƒãƒ•ァイルをアップロードã™ã‚‹ã¨ãオリジナル画åƒã‚’破棄ã—ã¦webpublicã®ã¿ä¿æŒã™ã‚‹ã‚ªãƒ—ション @tamaina
+- Server: always remove completed tasks of job queue @Johann150
+- Client: ã‚¢ãƒã‚¿ãƒ¼ã®è¨­å®šã§ç”»åƒã‚’クロップã§ãるよã†ã« @syuilo
+- Client: make emoji stand out more on reaction button @Johann150
+- Client: display URL of QR code for TOTP registration @tamaina
+- Client: render quote renote CWs as MFM @pixeldesu
+- API: notifications/readã¯é…列ã§ã‚‚å—ã‘付ã‘るよã†ã« #7667 @tamaina
+- API: ユーザー検索ã§ã€ã‚¯ã‚¨ãƒªãŒusernameã®æ¡ä»¶ã‚’満ãŸã™å ´åˆã¯usernameã‚‚LIKE検索ã™ã‚‹ã‚ˆã†ã« @tamaina
+- MFM: Allow speed changes in all animated MFMs @Johann150
+- The theme color is now better validated. @Johann150
+ Your own theme color may be unset if it was in an invalid format.
+ Admins should check their instance settings if in doubt.
+- Perform port diagnosis at startup only when Listen fails @mei23
+- Rate limiting is now also usable for non-authenticated users. @Johann150 @mei23
+ Admins should make sure the reverse proxy sets the `X-Forwarded-For` header to the original address.
+
+### Bugfixes
+- Server: keep file order of note attachement @Johann150
+- Server: fix caching @Johann150
+- Server: fix missing foreign key for reports leading to reports page being unusable @Johann150
+- Server: fix internal in-memory caching @Johann150
+- Server: use correct order of attachments on notes @Johann150
+- Server: prevent crash when processing certain PNGs @syuilo
+- Server: Fix unable to generate video thumbnails @mei23
+- Server: Fix `Cannot find module` issue @mei23
+- Federation: Add rel attribute to host-meta @mei23
+- Federation: add id for activitypub follows @Johann150
+- Federation: use `source` instead of `_misskey_content` @Johann150
+- Federation: ensure resolver does not fetch local resources via HTTP(S) @Johann150
+- Federation: correctly render empty note text @Johann150
+- Federation: Fix quote renotes containing no text being federated correctly @Johann150
+- Federation: remove duplicate br tag/newline @Johann150
+- Federation: add missing authorization checks @Johann150
+- Client: fix profile picture height in mentions @tamaina
+- Client: fix abuse reports page to be able to show all reports @Johann150
+- Client: fix settings page @tamaina
+- Client: fix profile tabs @futchitwo
+- Client: fix popout URL @futchitwo
+- Client: correctly handle MiAuth URLs with query string @sn0w
+- Client: ãƒŽãƒ¼ãƒˆè©³ç´°ãƒšãƒ¼ã‚¸ã®æ–°ã—ã„ノートを表示ã™ã‚‹æ©Ÿèƒ½ã®å‹•ä½œãŒæ­£ã—ããªã‚‹ã‚ˆã†ã«ä¿®æ­£ã™ã‚‹ @xianonn
+- MFM: more animated functions support `speed` parameter @futchitwo
+- MFM: limit large MFM @Johann150
+
## 12.110.1 (2022/04/23)
### Bugfixes
- Fix GOP rendering @syuilo
-- Improve performance of antenna, clip, and list @xianon
+- Improve performance of antenna, clip, and list @xianonn
## 12.110.0 (2022/04/11)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a696bc5ceb..a37df3bdee 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,10 +1,11 @@
# Contribution guide
We're glad you're interested in contributing Misskey! In this document you will find the information you need to contribute to the project.
-**â„¹ï¸ Important:** This project uses Japanese as its major language, **but you do not need to translate and write the Issues/PRs in Japanese.**
-Also, you might receive comments on your Issue/PR in Japanese, but you do not need to reply to them in Japanese as well.\
-The accuracy of machine translation into Japanese is not high, so it will be easier for us to understand if you write it in the original language.
-It will also allow the reader to use the translation tool of their preference if necessary.
+> **Note**
+> This project uses Japanese as its major language, **but you do not need to translate and write the Issues/PRs in Japanese.**
+> Also, you might receive comments on your Issue/PR in Japanese, but you do not need to reply to them in Japanese as well.\
+> The accuracy of machine translation into Japanese is not high, so it will be easier for us to understand if you write it in the original language.
+> It will also allow the reader to use the translation tool of their preference if necessary.
## Roadmap
See [ROADMAP.md](./ROADMAP.md)
@@ -16,6 +17,9 @@ Before creating an issue, please check the following:
- Issues should only be used to feature requests, suggestions, and bug tracking.
- Please ask questions or troubleshooting in the [Misskey Forum](https://forum.misskey.io/) or [Discord](https://discord.gg/Wp8gVStHW3).
+> **Warning**
+> Do not close issues that are about to be resolved. It should remain open until a commit that actually resolves it is merged.
+
## Before implementation
When you want to add a feature or fix a bug, **first have the design and policy reviewed in an Issue** (if it is not there, please make one). Without this step, there is a high possibility that the PR will not be merged even if it is implemented.
@@ -62,6 +66,30 @@ Be willing to comment on the good points and not just the things you want fixed
- Are there any omissions or gaps?
- Does it check for anomalies?
+## Deploy
+The `/deploy` command by issue comment can be used to deploy the contents of a PR to the preview environment.
+```
+/deploy sha=<commit hash>
+```
+An actual domain will be assigned so you can test the federation.
+
+## Merge
+For now, basically only @syuilo has the authority to merge PRs into develop because he is most familiar with the codebase.
+However, minor fixes, refactoring, and urgent changes may be merged at the discretion of a contributor.
+
+## Release
+### Release Instructions
+1. Commit version changes in the `develop` branch ([package.json](https://github.com/misskey-dev/misskey/blob/develop/package.json))
+2. Create a release PR.
+ - Into `master` from `develop` branch.
+ - The title must be in the format `Release: x.y.z`.
+ - `x.y.z` is the new version you are trying to release.
+3. Deploy and perform a simple QA check. Also verify that the tests passed.
+4. Merge it.
+5. Create a [release of GitHub](https://github.com/misskey-dev/misskey/releases)
+ - The target branch must be `master`
+ - The tag name must be the version
+
## Localization (l10n)
Misskey uses [Crowdin](https://crowdin.com/project/misskey) for localization management.
You can improve our translations with your Crowdin account.
diff --git a/Dockerfile b/Dockerfile
index e4959756e8..33d5faad12 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,6 @@
-FROM node:16.14.0-alpine3.15 AS base
+FROM node:18.0.0-alpine3.15 AS base
-ENV NODE_ENV=production
+ARG NODE_ENV=production
WORKDIR /misskey
@@ -11,16 +11,16 @@ FROM base AS builder
COPY . ./
RUN apk add --no-cache $BUILD_DEPS && \
- git submodule update --init && \
- yarn install && \
- yarn build && \
- rm -rf .git
+ git submodule update --init && \
+ yarn install && \
+ yarn build && \
+ rm -rf .git
FROM base AS runner
RUN apk add --no-cache \
- ffmpeg \
- tini
+ ffmpeg \
+ tini
ENTRYPOINT ["/sbin/tini", "--"]
@@ -31,5 +31,6 @@ COPY --from=builder /misskey/packages/backend/built ./packages/backend/built
COPY --from=builder /misskey/packages/client/node_modules ./packages/client/node_modules
COPY . ./
+ENV NODE_ENV=production
CMD ["npm", "run", "migrateandstart"]
diff --git a/README.md b/README.md
index c7bc9ef219..c273270644 100644
--- a/README.md
+++ b/README.md
@@ -1,27 +1,29 @@
-[![Misskey](https://github.com/misskey-dev/assets/blob/main/banner.png?raw=true)](https://join.misskey.page/)
-
<div align="center">
+<a href="https://misskey-hub.net">
+ <img src="./assets/title_float.svg" alt="Misskey logo" style="border-radius:50%" width="400"/>
+</a>
+
+**🌎 **[Misskey](https://misskey-hub.net/)** is an open source, decentralized social media platform that's free forever! 🚀**
+
+---
-**🌎 A forever evolving, interplanetary microblogging platform. 🚀**
-
-**Misskey** is a distributed microblogging platform with advanced features such as Reactions and a highly customizable UI.
+<a href="https://misskey-hub.net/instances.html">
+ <img src="https://custom-icon-badges.herokuapp.com/badge/find_an-instance-acea31?logoColor=acea31&style=for-the-badge&logo=misskey&labelColor=363B40" alt="find an instance"/></a>
-[Learn more](https://misskey-hub.net/)
+<a href="https://misskey-hub.net/docs/install.html">
+ <img src="https://custom-icon-badges.herokuapp.com/badge/create_an-instance-FBD53C?logoColor=FBD53C&style=for-the-badge&logo=server&labelColor=363B40" alt="create an instance"/></a>
----
+<a href="./CONTRIBUTING.md">
+ <img src="https://custom-icon-badges.herokuapp.com/badge/become_a-contributor-A371F7?logoColor=A371F7&style=for-the-badge&logo=git-merge&labelColor=363B40" alt="become a contributor"/></a>
-[✨ Find an instance](https://misskey-hub.net/instances.html)
-•
-[📦 Create your own instance](https://misskey-hub.net/docs/install.html)
-•
-[ðŸ› ï¸ Contribute](./CONTRIBUTING.md)
-•
-[🚀 Join the community](https://discord.gg/Wp8gVStHW3)
+<a href="https://discord.gg/Wp8gVStHW3">
+ <img src="https://custom-icon-badges.herokuapp.com/badge/join_the-community-5865F2?logoColor=5865F2&style=for-the-badge&logo=discord&labelColor=363B40" alt="join the community"/></a>
+<a href="https://www.patreon.com/syuilo">
+ <img src="https://custom-icon-badges.herokuapp.com/badge/become_a-patron-F96854?logoColor=F96854&style=for-the-badge&logo=patreon&labelColor=363B40" alt="become a patron"/></a>
+
---
-<a href="https://www.patreon.com/syuilo"><img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" alt="Become a Patron!" width="160" /></a>
-
</div>
<div>
@@ -30,139 +32,26 @@
## ✨ Features
- **ActivityPub support**\
- It is possible to interact with other software.
+Not on Misskey? No problem! Not only can Misskey instances talk to each other, but you can make friends with people on other networks like Mastodon and Pixelfed!
- **Reactions**\
- You can add "reactions" to each post, making it easy for you to express your feelings.
+You can add emoji reactions to any post! No longer are you bound by a like button, show everyone exactly how you feel with the tap of a button.
- **Drive**\
- An interface to manage uploaded files such as images, videos, sounds, etc.
- You can also organize your favorite content into folders, making it easy to share again.
+With Misskey's built in drive, you get cloud storage right in your social media, where you can upload any files, make folders, and find media from posts you've made!
- **Rich Web UI**\
- Misskey has a rich WebUI by default.
- It is highly customizable by flexibly changing the layout and installing various widgets and themes.
- Furthermore, plug-ins can be created using AiScript, a original programming language.
-- and more...
+ Misskey has a rich and easy to use Web UI!
+ It is highly customizable, from changing the layout and adding widgets to making custom themes.
+ Furthermore, plugins can be created using AiScript, an original programming language.
+- And much more...
</div>
<div style="clear: both;"></div>
+## Documentation
+
+Misskey Documentation can be found at [Misskey Hub](https://misskey-hub.net/), some of the links and graphics above also lead to specific portions of it.
+
## Sponsors
<div align="center">
<a class="rss3" title="RSS3" href="https://rss3.io/" target="_blank"><img src="https://rss3.mypinata.cloud/ipfs/QmUG6H3Z7D5P511shn7sB4CPmpjH5uZWu4m5mWX7U3Gqbu" alt="RSS3" height="60"></a>
</div>
-
-## Backers
-<!-- PATREON_START -->
-<table><tr>
-<td><img src="https://c8.patreon.com/2/200/20832595" alt="Roujo " width="100"></td>
-<td><img src="https://c8.patreon.com/2/200/27956229" alt="Oliver Maximilian Seidel" width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weepjp " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19045173/cb91c0f345c24d4ebfd05f19906d5e26/1.png?token-time=2145916800&token-hash=o_zKBytJs_AxHwSYw_5R8eD0eSJe3RoTR3kR3Q0syN0%3D" alt="kiritan " width="100"></td>
-<td><img src="https://c8.patreon.com/2/200/27648259" alt="ã¿ãªã—ã¾ " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/24430516/b1964ac5b9f746d2a12ff53dbc9aa40a/1.jpg?token-time=2145916800&token-hash=bmEiMGYpp3bS7hCCbymjGGsHBZM3AXuBOFO3Kro37PU%3D" alt="Eduardo Quiros" width="100"></td>
-</tr><tr>
-<td><a href="https://www.patreon.com/user?u=20832595">Roujo </a></td>
-<td><a href="https://www.patreon.com/user?u=27956229">Oliver Maximilian Seidel</a></td>
-<td><a href="https://www.patreon.com/weepjp">weepjp </a></td>
-<td><a href="https://www.patreon.com/user?u=19045173">kiritan </a></td>
-<td><a href="https://www.patreon.com/user?u=27648259">ã¿ãªã—ã¾ </a></td>
-<td><a href="https://www.patreon.com/user?u=24430516">Eduardo Quiros</a></td>
-</tr></table>
-<table><tr>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/14215107/1cbe1912c26143919fa0faca16f12ce1/4.jpg?token-time=2145916800&token-hash=BslMqDjTjz8KYANLvxL87agHTugHa0dMPUzT-hwR6Vk%3D" alt="Nesakko" width="100"></td>
-<td><img src="https://c8.patreon.com/2/200/776209" alt="Demogrognard" width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/3075183/c2ae575c604e420297f000ccc396e395/1.jpeg?token-time=2145916800&token-hash=O9qmPtpo6wWb0OuvnkEekhk_1WO2MTdytLr7ZgsAr80%3D" alt="Liaizon Wakest" width="100"></td>
-<td><img src="https://c8.patreon.com/2/200/557245" alt="mkatze " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/23915207/25428766ecd745478e600b3d7f871eb2/1.png?token-time=2145916800&token-hash=urCLLA4KjJZX92Y1CxcBP4d8bVTHGkiaPnQZp-Tqz68%3D" alt="kabo2468y " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/8249688/4aacf36b6b244ab1bc6653591b6640df/2.png?token-time=2145916800&token-hash=1ZEf2w6L34253cZXS_HlVevLEENWS9QqrnxGUAYblPo%3D" alt="AureoleArk " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon " width="100"></td>
-<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当ã‹ãªã¿ " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/36813045/29876ea679d443bcbba3c3f16edab8c2/2.jpeg?token-time=2145916800&token-hash=YCKWnIhrV9rjUCV9KqtJnEqjy_uGYF3WMXftjUdpi7o%3D" alt="Wataru Manji (manji0)" width="100"></td>
-</tr><tr>
-<td><a href="https://www.patreon.com/Nesakko">Nesakko</a></td>
-<td><a href="https://www.patreon.com/user?u=776209">Demogrognard</a></td>
-<td><a href="https://www.patreon.com/wakest">Liaizon Wakest</a></td>
-<td><a href="https://www.patreon.com/user?u=557245">mkatze </a></td>
-<td><a href="https://www.patreon.com/user?u=23915207">kabo2468y </a></td>
-<td><a href="https://www.patreon.com/AureoleArk">AureoleArk </a></td>
-<td><a href="https://www.patreon.com/osapon">osapon </a></td>
-<td><a href="https://www.patreon.com/user?u=16869916">見当ã‹ãªã¿ </a></td>
-<td><a href="https://www.patreon.com/user?u=36813045">Wataru Manji (manji0)</a></td>
-</tr></table>
-<table><tr>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18899730/6a22797f68254034a854d69ea2445fc8/1.png?token-time=2145916800&token-hash=b_uj57yxo5VzkSOUS7oXE_762dyOTB_oxzbO6lFNG3k%3D" alt="YuzuRyo61 " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5788159/af42076ab3354bb49803cfba65f94bee/1.jpg?token-time=2145916800&token-hash=iSaxp_Yr2-ZiU2YVi9rcpZZj9mj3UvNSMrZr4CU4qtA%3D" alt="mewl hayabusa" width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/28779508/3cd4cb7f017f4ee0864341e3464d42f9/1.png?token-time=2145916800&token-hash=eGQtR15be44kgvh8fw2Jx8Db4Bv15YBp2ldxh0EKRxA%3D" alt="S Y" width="100"></td>
-<td><img src="https://c8.patreon.com/2/200/16542964" alt="Takumi Sugita" width="100"></td>
-<td><img src="https://c8.patreon.com/2/200/17866454" alt="sikyosyounin " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3.png?token-time=2145916800&token-hash=KjfQL8nf3AIf6WqzLshBYAyX44piAqOAZiYXgZS_H6A%3D" alt="YUKIMOCHI" width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/38837364/9421361c54c645ac8f5fc442a40c32e9/1.png?token-time=2145916800&token-hash=TUZB48Nem3BeUPLBH6s3P6WyKBnQOy0xKaDSTBBUNzA%3D" alt="xianon" width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/26340354/08834cf767b3449e93098ef73a434e2f/2.png?token-time=2145916800&token-hash=nyM8DnKRL8hR47HQ619mUzsqVRpkWZjgtgBU9RY15Uc%3D" alt="totokoro " width="100"></td>
-</tr><tr>
-<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61 </a></td>
-<td><a href="https://www.patreon.com/hs_sh_net">mewl hayabusa</a></td>
-<td><a href="https://www.patreon.com/user?u=28779508">S Y</a></td>
-<td><a href="https://www.patreon.com/user?u=16542964">Takumi Sugita</a></td>
-<td><a href="https://www.patreon.com/user?u=17866454">sikyosyounin </a></td>
-<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
-<td><a href="https://www.patreon.com/user?u=38837364">xianon</a></td>
-<td><a href="https://www.patreon.com/user?u=26340354">totokoro </a></td>
-</tr></table>
-<table><tr>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/2.jpeg?token-time=2145916800&token-hash=_sTj3dUBOhn9qwiJ7F19Qd-yWWfUqJC_0jG1h0agEqQ%3D" alt="sheeta.s " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5827393/59893c191dda408f9cabd0f20a3a5627/1.jpeg?token-time=2145916800&token-hash=i9N05vOph-eP1LTLb9_npATjYOpntL0ZsHNaZFSsPmE%3D" alt="motcha " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/20494440/540beaf2445f408ea6597bc61e077bb3/1.png?token-time=2145916800&token-hash=UJ0JQge64Bx9XmN_qYA1inMQhrWf4U91fqz7VAKJeSg%3D" alt="axtuki1 " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17880724/311738c8a48f4a6b9443c2445a75adde/1.jpg?token-time=2145916800&token-hash=nVAntpybQrznE0rg05keLrSE6ogPKJXB13rmrJng42c%3D" alt="takimura " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13100201/fc5be4fa90444f09a9c8a06f72385272/1.png?token-time=2145916800&token-hash=i8PjlgfOB2LPEdbtWyx8ZPsBKhGcNZqcw_FQmH71UGU%3D" alt="aqz tamaina" width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/9109588/e3cffc48d20a4e43afe04123e696781d/3.png?token-time=2145916800&token-hash=T_VIUA0IFIbleZv4pIjiszZGnQonwn34sLCYFIhakBo%3D" alt="nafuchoco " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/619ab87cc08448439222631ebb26802f/1.gif?token-time=2145916800&token-hash=o27K7M02s1z-LkDUEO5Oa7cu-GviRXeOXxryi4o_6VU%3D" alt="Atsuko Tominaga" width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/3.png?token-time=2145916800&token-hash=FTm3WVom4dJ9NwWMU4OpCL_8Yc13WiwEbKrDPyTZTPs%3D" alt="natalie" width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/26144593/9514b10a5c1b42a3af58621aee213d1d/1.png?token-time=2145916800&token-hash=v1PYRsjzu4c_mndN4Hvi_dlispZJsuGRCQeNS82pUSM%3D" alt="EBISUME" width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5923936/2a743cbfbff946c2af3f09026047c0da/2.png?token-time=2145916800&token-hash=h6yphW1qnM0n_NOWaf8qtszMRLXEwIxfk5beu4RxdT0%3D" alt="noellabo " width="100"></td>
-</tr><tr>
-<td><a href="https://www.patreon.com/user?u=19356899">sheeta.s </a></td>
-<td><a href="https://www.patreon.com/user?u=5827393">motcha </a></td>
-<td><a href="https://www.patreon.com/user?u=20494440">axtuki1 </a></td>
-<td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td>
-<td><a href="https://www.patreon.com/takimura">takimura </a></td>
-<td><a href="https://www.patreon.com/aqz">aqz tamaina</a></td>
-<td><a href="https://www.patreon.com/user?u=9109588">nafuchoco </a></td>
-<td><a href="https://www.patreon.com/user?u=16900731">Atsuko Tominaga</a></td>
-<td><a href="https://www.patreon.com/user?u=4389829">natalie</a></td>
-<td><a href="https://www.patreon.com/user?u=26144593">EBISUME</a></td>
-<td><a href="https://www.patreon.com/noellabo">noellabo </a></td>
-</tr></table>
-<table><tr>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/2384390/5681180e1efb46a8b28e0e8d4c8b9037/1.jpg?token-time=2145916800&token-hash=SJcMy-Q1BcS940-LFUVOMfR7-5SgrzsEQGhYb3yowFk%3D" alt="CG " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1.jpg?token-time=2145916800&token-hash=7bkMqTwHPRsJPGAq42PYdDXDZBVGLqdgr1ZmBxX8GFQ%3D" alt="Hekovic " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/24641572/b4fd175424814f15b0ca9178d2d2d2e4/1.png?token-time=2145916800&token-hash=e2fyqdbuJbpCckHcwux7rbuW6OPkKdERcus0u2wIEWU%3D" alt="uroco @99" width="100"></td>
-<td><img src="https://c8.patreon.com/2/200/14661394" alt="Chandler " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5731881/4b6038e6cda34c04b83a5fcce3806a93/1.png?token-time=2145916800&token-hash=hBayGfOmQH3kRMdNnDe4oCZD_9fsJWSt29xXR3KRMVk%3D" alt="Nokotaro Takeda" width="100"></td>
-<td><img src="https://c8.patreon.com/2/200/23932002" alt="nenohi " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/9481273/7fa89168e72943859c3d3c96e424ed31/4.jpeg?token-time=2145916800&token-hash=5w1QV1qXe-NdWbdFmp1H7O_-QBsSiV0haumk3XTHIEg%3D" alt="Efertone " width="100"></td>
-<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1.jpeg?token-time=2145916800&token-hash=vGe7wXGqmA8Q7m-kDNb6fyGdwk-Dxk4F-ut8ZZu51RM%3D" alt="Takashi Shibuya" width="100"></td>
-</tr><tr>
-<td><a href="https://www.patreon.com/Corset">CG </a></td>
-<td><a href="https://www.patreon.com/hekovic">Hekovic </a></td>
-<td><a href="https://www.patreon.com/user?u=24641572">uroco @99</a></td>
-<td><a href="https://www.patreon.com/user?u=14661394">Chandler </a></td>
-<td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td>
-<td><a href="https://www.patreon.com/user?u=23932002">nenohi </a></td>
-<td><a href="https://www.patreon.com/efertone">Efertone </a></td>
-<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
-</tr></table>
-
-**Last updated:** Sun, 26 Jul 2020 07:00:10 UTC
-<!-- PATREON_END -->
-
-[backer-url]: #backers
-[backer-badge]: https://opencollective.com/misskey/backers/badge.svg
-[backers-image]: https://opencollective.com/misskey/backers.svg
-[sponsor-url]: #sponsors
-[sponsor-badge]: https://opencollective.com/misskey/sponsors/badge.svg
-[sponsors-image]: https://opencollective.com/misskey/sponsors.svg
-[support-url]: https://opencollective.com/misskey#support
-
-[syuilo-link]: https://syuilo.com
-[syuilo-icon]: https://avatars2.githubusercontent.com/u/4439005?v=3&s=70
diff --git a/assets/title_float.svg b/assets/title_float.svg
new file mode 100644
index 0000000000..43205ac1c4
--- /dev/null
+++ b/assets/title_float.svg
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ id="svg10"
+ version="1.1"
+ viewBox="0 0 162.642 54.261"
+ height="205.08"
+ width="614.71">
+ <metadata
+ id="metadata16">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <style>
+ #g8 {
+ animation-name: floating;
+ animation-duration: 3s;
+ animation-iteration-count: infinite;
+ animation-timing-function: ease-in-out;
+ }
+
+ @keyframes floating {
+ 0% { transform: translate(0, 0px); }
+ 50% { transform: translate(0, -5px); }
+ 100% { transform: translate(0, 0px); }
+ }
+ </style>
+ <linearGradient id="myGradient" gradientTransform="rotate(90)">
+ <stop offset="5%" stop-color="#A1CA03" />
+ <stop offset="95%" stop-color="#91BA03" />
+ </linearGradient>
+ <defs
+ id="defs14" />
+ <g
+ id="g8"
+ fill="url('#myGradient')"
+ word-spacing="0"
+ letter-spacing="0"
+ font-family="OTADESIGN Rounded"
+ font-weight="400">
+ <g
+ id="g4"
+ style="line-height:476.69509888px;-inkscape-font-specification:'OTADESIGN Rounded'">
+ <path
+ id="path2"
+ font-size="141.034"
+ aria-label="Mi"
+ d="m 27.595,34.59 c -1.676,0.006 -3.115,-1.004 -3.793,-2.179 -0.363,-0.513 -1.08,-0.696 -1.09,0 v 3.214 c 0,1.291 -0.47,2.408 -1.412,3.35 -0.915,0.914 -2.031,1.371 -3.35,1.371 -1.29,0 -2.407,-0.457 -3.349,-1.372 -0.914,-0.941 -1.372,-2.058 -1.372,-3.349 V 17.95 c 0,-0.995 0.283,-1.896 0.848,-2.703 0.591,-0.834 1.345,-1.413 2.26,-1.735 0.516591,-0.189385 1.062793,-0.285215 1.613,-0.283 1.453,0 2.664,0.565 3.632,1.695 l 4.832,5.608 c 0.108,0.08 0.424,0.697 1.18,0.697 0.758,0 1.115,-0.617 1.222,-0.698 l 4.791,-5.607 c 0.996,-1.13 2.22,-1.695 3.673,-1.695 0.538,0 1.076,0.094 1.614,0.283 0.914,0.322 1.654,0.9 2.22,1.735 0.591,0.807 0.887,1.708 0.887,2.703 v 17.675 c 0,1.291 -0.47,2.408 -1.412,3.35 -0.915,0.914 -2.032,1.371 -3.35,1.371 -1.291,0 -2.407,-0.457 -3.35,-1.372 -0.914,-0.941 -1.371,-2.058 -1.371,-3.349 v -3.214 c -0.08,-0.877 -0.855,-0.324 -1.13,0 -0.726,1.345 -2.118,2.173 -3.793,2.18 z M 47.806,21.38 c -1.13,0 -2.098333,-0.39 -2.905,-1.17 -0.78,-0.806667 -1.17,-1.775 -1.17,-2.905 0,-1.13 0.39,-2.085 1.17,-2.865 0.806667,-0.806667 1.775,-1.21 2.905,-1.21 1.13,0 2.098667,0.403333 2.906,1.21 0.806667,0.78 1.21,1.735 1.21,2.865 0,1.13 -0.403333,2.098333 -1.21,2.905 -0.807333,0.78 -1.776,1.17 -2.906,1.17 z m 0.04,0.808 c 1.13,0 2.085333,0.403333 2.866,1.21 0.806667,0.806667 1.21,1.775333 1.21,2.906 v 9.967 c 0,1.13 -0.403333,2.098333 -1.21,2.905 -0.78,0.78 -1.735333,1.17 -2.866,1.17 -1.129333,0 -2.097667,-0.39 -2.905,-1.17 -0.806667,-0.806667 -1.21,-1.775 -1.21,-2.905 v -9.967 c 0,-1.13 0.403333,-2.098667 1.21,-2.906 0.806667,-0.806667 1.775,-1.21 2.905,-1.21 z"
+ style="font-size:141.03399658px;-inkscape-font-specification:'OTADESIGN Rounded'" />
+ </g>
+ <path
+ id="path6"
+ d="M60.925 27.24q.968.243 2.42.525 2.42.403 3.792 1.29 2.582 1.695 2.582 5.083 0 2.743-1.815 4.478-2.098 2.017-5.85 2.017-2.742 0-6.13-.767-1.09-.242-1.776-1.089-.645-.847-.645-1.896 0-1.29.887-2.178.928-.928 2.179-.928.363 0 .685.081 1.17.242 4.478.605.444 0 .968-.04.202 0 .202-.242.04-.202-.242-.283-1.372-.242-2.542-.524-1.33-.282-1.896-.484-1.129-.323-1.895-.847-2.582-1.694-2.622-5.083 0-2.702 1.855-4.477 2.26-2.179 6.414-1.977 2.783.121 5.567.726 1.048.242 1.734 1.09.686.846.686 1.936 0 1.25-.928 2.178-.887.887-2.178.887-.323 0-.645-.08-1.17-.242-4.518-.565-.404-.04-.767 0-.323.04-.323.242.04.242.323.323zm17.555 0q.968.243 2.42.525 2.42.403 3.792 1.29 2.581 1.695 2.581 5.083 0 2.743-1.815 4.478-2.098 2.017-5.849 2.017-2.743 0-6.131-.767-1.09-.242-1.775-1.089-.646-.847-.646-1.896 0-1.29.888-2.178.927-.928 2.178-.928.363 0 .686.081 1.17.242 4.477.605.444 0 .968-.04.202 0 .202-.242.04-.202-.242-.283-1.371-.242-2.541-.524-1.331-.282-1.896-.484-1.13-.323-1.896-.847-2.582-1.694-2.622-5.083 0-2.702 1.855-4.477 2.26-2.179 6.414-1.977 2.784.121 5.567.726 1.049.242 1.735 1.09.685.846.685 1.936 0 1.25-.927 2.178-.888.887-2.179.887-.322 0-.645-.08-1.17-.242-4.518-.565-.403-.04-.767 0-.322.04-.322.242.04.242.322.323zm26.075 3.335q.12.08 2.864 2.783 1.25 1.21 1.25 2.945 0 1.613-1.17 2.864-1.17 1.21-2.904 1.21-1.654 0-2.864-1.17l-4.034-3.913q-.161-.12-.323-.12-.322 0-.322 1.21 0 1.694-1.21 2.904-1.21 1.17-2.905 1.17-1.694 0-2.904-1.17-1.17-1.21-1.17-2.905V17.586q0-1.694 1.17-2.864 1.21-1.21 2.904-1.21t2.904 1.21q1.21 1.17 1.21 2.864v6.293q0 .403.283.524.242.121.524-.08.162-.081 4.841-3.188 1.049-.645 2.259-.645 2.219 0 3.429 1.815.645 1.05.645 2.26 0 2.218-1.815 3.428l-2.541 1.614v.04l-.081.04q-.565.363-.04.888zm15.599 10.058q-4.195 0-7.18-2.945-2.945-2.985-2.945-7.18 0-4.155 2.945-7.1 2.985-2.985 7.18-2.985 4.155 0 6.979 2.784.928.927.928 2.259 0 1.33-.928 2.259l-4.68 4.639q-1.008 1.008-2.016 1.008-1.453 0-2.26-.807-.806-.807-.806-2.138 0-1.29.928-2.218l.806-.847q.162-.121.081-.243-.12-.08-.323-.04-.806.202-1.371.807-1.13 1.09-1.13 2.622 0 1.573 1.09 2.703 1.13 1.089 2.702 1.089 1.533 0 2.622-1.13.928-.927 2.26-.927 1.33 0 2.258.927.928.928.928 2.26 0 1.33-.928 2.258-2.985 2.945-7.14 2.945zm29.259-15.786v5.607q0 .564-.08 1.21v7.382q0 4.518-2.744 7.22-2.702 2.703-7.301 2.703-2.662 0-4.8-1.008-2.138-.968-2.138-3.348 0-.807.363-1.533.968-2.179 3.348-2.179.565 0 1.573.323 1.009.323 1.654.323 1.694 0 2.219-.726.201-.283.08-.444-.161-.242-.564-.161-.686.12-1.493.12-4.074 0-6.979-2.904-2.904-2.904-2.904-6.978v-5.607q0-1.695 1.17-2.864 1.21-1.21 2.904-1.21t2.905 1.21q1.21 1.17 1.21 2.864v5.607q0 .685.484 1.21.524.484 1.21.484.726 0 1.21-.484.484-.525.484-1.21v-5.607q0-1.695 1.21-2.864 1.21-1.21 2.905-1.21 1.694 0 2.864 1.21 1.21 1.17 1.21 2.864z"
+ style="line-height:136.34428406px;-inkscape-font-specification:'OTADESIGN Rounded'" />
+ </g>
+</svg>
diff --git a/chart/Chart.yaml b/chart/Chart.yaml
new file mode 100644
index 0000000000..8f31cf7fb4
--- /dev/null
+++ b/chart/Chart.yaml
@@ -0,0 +1,3 @@
+apiVersion: v2
+name: misskey
+version: 0.0.0
diff --git a/chart/files/default.yml b/chart/files/default.yml
new file mode 100644
index 0000000000..a9ef22f424
--- /dev/null
+++ b/chart/files/default.yml
@@ -0,0 +1,165 @@
+#â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”
+# Misskey configuration
+#â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”
+
+# ┌─────â”
+#───┘ URL └─────────────────────────────────────────────────────
+
+# Final accessible URL seen by a user.
+# url: https://example.tld/
+
+# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
+# URL SETTINGS AFTER THAT!
+
+# ┌───────────────────────â”
+#───┘ Port and TLS settings └───────────────────────────────────
+
+#
+# Misskey supports two deployment options for public.
+#
+
+# Option 1: With Reverse Proxy
+#
+# +----- https://example.tld/ ------------+
+# +------+ |+-------------+ +----------------+|
+# | User | ---> || Proxy (443) | ---> | Misskey (3000) ||
+# +------+ |+-------------+ +----------------+|
+# +---------------------------------------+
+#
+# You need to setup reverse proxy. (eg. nginx)
+# You do not define 'https' section.
+
+# Option 2: Standalone
+#
+# +- https://example.tld/ -+
+# +------+ | +---------------+ |
+# | User | ---> | | Misskey (443) | |
+# +------+ | +---------------+ |
+# +------------------------+
+#
+# You need to run Misskey as root.
+# You need to set Certificate in 'https' section.
+
+# To use option 1, uncomment below line.
+port: 3000 # A port that your Misskey server should listen.
+
+# To use option 2, uncomment below lines.
+#port: 443
+
+#https:
+# # path for certification
+# key: /etc/letsencrypt/live/example.tld/privkey.pem
+# cert: /etc/letsencrypt/live/example.tld/fullchain.pem
+
+# ┌──────────────────────────â”
+#───┘ PostgreSQL configuration └────────────────────────────────
+
+db:
+ host: localhost
+ port: 5432
+
+ # Database name
+ db: misskey
+
+ # Auth
+ user: example-misskey-user
+ pass: example-misskey-pass
+
+ # Whether disable Caching queries
+ #disableCache: true
+
+ # Extra Connection options
+ #extra:
+ # ssl: true
+
+# ┌─────────────────────â”
+#───┘ Redis configuration └─────────────────────────────────────
+
+redis:
+ host: localhost
+ port: 6379
+ #pass: example-pass
+ #prefix: example-prefix
+ #db: 1
+
+# ┌─────────────────────────────â”
+#───┘ Elasticsearch configuration └─────────────────────────────
+
+#elasticsearch:
+# host: localhost
+# port: 9200
+# ssl: false
+# user:
+# pass:
+
+# ┌───────────────â”
+#───┘ ID generation └───────────────────────────────────────────
+
+# You can select the ID generation method.
+# You don't usually need to change this setting, but you can
+# change it according to your preferences.
+
+# Available methods:
+# aid ... Short, Millisecond accuracy
+# meid ... Similar to ObjectID, Millisecond accuracy
+# ulid ... Millisecond accuracy
+# objectid ... This is left for backward compatibility
+
+# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
+# ID SETTINGS AFTER THAT!
+
+id: "aid"
+# ┌─────────────────────â”
+#───┘ Other configuration └─────────────────────────────────────
+
+# Whether disable HSTS
+#disableHsts: true
+
+# Number of worker processes
+#clusterLimit: 1
+
+# Job concurrency per worker
+# deliverJobConcurrency: 128
+# inboxJobConcurrency: 16
+
+# Job rate limiter
+# deliverJobPerSec: 128
+# inboxJobPerSec: 16
+
+# Job attempts
+# deliverJobMaxAttempts: 12
+# inboxJobMaxAttempts: 8
+
+# IP address family used for outgoing request (ipv4, ipv6 or dual)
+#outgoingAddressFamily: ipv4
+
+# Syslog option
+#syslog:
+# host: localhost
+# port: 514
+
+# Proxy for HTTP/HTTPS
+#proxy: http://127.0.0.1:3128
+
+#proxyBypassHosts: [
+# 'example.com',
+# '192.0.2.8'
+#]
+
+# Proxy for SMTP/SMTPS
+#proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT
+#proxySmtp: socks4://127.0.0.1:1080 # use SOCKS4
+#proxySmtp: socks5://127.0.0.1:1080 # use SOCKS5
+
+# Media Proxy
+#mediaProxy: https://example.com/proxy
+
+# Sign to ActivityPub GET request (default: false)
+#signToActivityPubGet: true
+
+#allowedPrivateNetworks: [
+# '127.0.0.1/32'
+#]
+
+# Upload or download file size limits (bytes)
+#maxFileSize: 262144000
diff --git a/chart/templates/ConfigMap.yml b/chart/templates/ConfigMap.yml
new file mode 100644
index 0000000000..37c25e0864
--- /dev/null
+++ b/chart/templates/ConfigMap.yml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ include "misskey.fullname" . }}-configuration
+data:
+ default.yml: |-
+ {{ .Files.Get "files/default.yml"|nindent 4 }}
+ url: {{ .Values.url }}
diff --git a/chart/templates/Deployment.yml b/chart/templates/Deployment.yml
new file mode 100644
index 0000000000..d16aece915
--- /dev/null
+++ b/chart/templates/Deployment.yml
@@ -0,0 +1,47 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "misskey.fullname" . }}
+ labels:
+ {{- include "misskey.labels" . | nindent 4 }}
+spec:
+ selector:
+ matchLabels:
+ {{- include "misskey.selectorLabels" . | nindent 6 }}
+ replicas: 1
+ template:
+ metadata:
+ labels:
+ {{- include "misskey.selectorLabels" . | nindent 8 }}
+ spec:
+ containers:
+ - name: misskey
+ image: {{ .Values.image }}
+ env:
+ - name: NODE_ENV
+ value: {{ .Values.environment }}
+ volumeMounts:
+ - name: {{ include "misskey.fullname" . }}-configuration
+ mountPath: /misskey/.config
+ readOnly: true
+ ports:
+ - containerPort: 3000
+ - name: postgres
+ image: postgres:14-alpine
+ env:
+ - name: POSTGRES_USER
+ value: "example-misskey-user"
+ - name: POSTGRES_PASSWORD
+ value: "example-misskey-pass"
+ - name: POSTGRES_DB
+ value: "misskey"
+ ports:
+ - containerPort: 5432
+ - name: redis
+ image: redis:alpine
+ ports:
+ - containerPort: 6379
+ volumes:
+ - name: {{ include "misskey.fullname" . }}-configuration
+ configMap:
+ name: {{ include "misskey.fullname" . }}-configuration
diff --git a/chart/templates/Service.yml b/chart/templates/Service.yml
new file mode 100644
index 0000000000..3209581298
--- /dev/null
+++ b/chart/templates/Service.yml
@@ -0,0 +1,14 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "misskey.fullname" . }}
+ annotations:
+ dev.okteto.com/auto-ingress: "true"
+spec:
+ type: ClusterIP
+ ports:
+ - port: 3000
+ protocol: TCP
+ name: http
+ selector:
+ {{- include "misskey.selectorLabels" . | nindent 4 }}
diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl
new file mode 100644
index 0000000000..a5a2499f3f
--- /dev/null
+++ b/chart/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "misskey.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "misskey.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "misskey.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "misskey.labels" -}}
+helm.sh/chart: {{ include "misskey.chart" . }}
+{{ include "misskey.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "misskey.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "misskey.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "misskey.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "misskey.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/chart/values.yml b/chart/values.yml
new file mode 100644
index 0000000000..a7031538a9
--- /dev/null
+++ b/chart/values.yml
@@ -0,0 +1,3 @@
+url: https://example.tld/
+image: okteto.dev/misskey
+environment: production
diff --git a/cypress.config.ts b/cypress.config.ts
new file mode 100644
index 0000000000..e390c41a54
--- /dev/null
+++ b/cypress.config.ts
@@ -0,0 +1,12 @@
+import { defineConfig } from 'cypress'
+
+export default defineConfig({
+ e2e: {
+ // We've imported your old cypress plugins here.
+ // You may want to clean this up later by importing these.
+ setupNodeEvents(on, config) {
+ return require('./cypress/plugins/index.js')(on, config)
+ },
+ baseUrl: 'http://localhost:61812',
+ },
+})
diff --git a/cypress.json b/cypress.json
deleted file mode 100644
index e858e480b0..0000000000
--- a/cypress.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "baseUrl": "http://localhost:61812"
-}
diff --git a/cypress/integration/basic.js b/cypress/e2e/basic.cy.js
index 7d27b649f4..eb5195c4b2 100644
--- a/cypress/integration/basic.js
+++ b/cypress/e2e/basic.cy.js
@@ -1,8 +1,6 @@
describe('Before setup instance', () => {
beforeEach(() => {
- cy.request('POST', '/api/reset-db').as('reset');
- cy.get('@reset').its('status').should('equal', 204);
- cy.reload(true);
+ cy.resetState();
});
afterEach(() => {
@@ -32,15 +30,10 @@ describe('Before setup instance', () => {
describe('After setup instance', () => {
beforeEach(() => {
- cy.request('POST', '/api/reset-db').as('reset');
- cy.get('@reset').its('status').should('equal', 204);
- cy.reload(true);
+ cy.resetState();
// ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹åˆæœŸã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—
- cy.request('POST', '/api/admin/accounts/create', {
- username: 'admin',
- password: 'pass',
- }).its('body').as('admin');
+ cy.registerUser('admin', 'pass', true);
});
afterEach(() => {
@@ -70,21 +63,13 @@ describe('After setup instance', () => {
describe('After user signup', () => {
beforeEach(() => {
- cy.request('POST', '/api/reset-db').as('reset');
- cy.get('@reset').its('status').should('equal', 204);
- cy.reload(true);
+ cy.resetState();
// ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹åˆæœŸã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—
- cy.request('POST', '/api/admin/accounts/create', {
- username: 'admin',
- password: 'pass',
- }).its('body').as('admin');
+ cy.registerUser('admin', 'pass', true);
// ユーザー作æˆ
- cy.request('POST', '/api/signup', {
- username: 'alice',
- password: 'alice1234',
- }).its('body').as('alice');
+ cy.registerUser('alice', 'alice1234');
});
afterEach(() => {
@@ -129,31 +114,15 @@ describe('After user signup', () => {
describe('After user singed in', () => {
beforeEach(() => {
- cy.request('POST', '/api/reset-db').as('reset');
- cy.get('@reset').its('status').should('equal', 204);
- cy.reload(true);
+ cy.resetState();
// ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹åˆæœŸã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—
- cy.request('POST', '/api/admin/accounts/create', {
- username: 'admin',
- password: 'pass',
- }).its('body').as('admin');
+ cy.registerUser('admin', 'pass', true);
// ユーザー作æˆ
- cy.request('POST', '/api/signup', {
- username: 'alice',
- password: 'alice1234',
- }).its('body').as('alice');
+ cy.registerUser('alice', 'alice1234');
- cy.visit('/');
-
- cy.intercept('POST', '/api/signin').as('signin');
-
- cy.get('[data-cy-signin]').click();
- cy.get('[data-cy-signin-username] input').type('alice');
- cy.get('[data-cy-signin-password] input').type('alice1234{enter}');
-
- cy.wait('@signin').as('signedIn');
+ cy.login('alice', 'alice1234');
});
afterEach(() => {
@@ -163,12 +132,10 @@ describe('After user singed in', () => {
});
it('successfully loads', () => {
- cy.visit('/');
+ cy.get('[data-cy-open-post-form]').should('be.visible');
});
it('note', () => {
- cy.visit('/');
-
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
new file mode 100644
index 0000000000..56ad95ee94
--- /dev/null
+++ b/cypress/e2e/widgets.cy.js
@@ -0,0 +1,65 @@
+describe('After user signed in', () => {
+ beforeEach(() => {
+ cy.resetState();
+ cy.viewport('macbook-16');
+
+ // ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹åˆæœŸã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—
+ cy.registerUser('admin', 'pass', true);
+
+ // ユーザー作æˆ
+ cy.registerUser('alice', 'alice1234');
+
+ cy.login('alice', 'alice1234');
+ });
+
+ afterEach(() => {
+ // テスト終了直å‰ã«ãƒšãƒ¼ã‚¸é·ç§»ã™ã‚‹ã‚ˆã†ãªãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹(例ãˆã°ã‚¢ã‚«ã‚¦ãƒ³ãƒˆä½œæˆ)ã ã¨ã€ãŸã¶ã‚“Cypressã®ãƒã‚°ã§ãƒ–ラウザã®å†…å®¹ãŒæ¬¡ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«å¼•ãç¶™ãŒã‚Œã¦ã—ã¾ã†(例ãˆã°ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒä½œæˆã—終ã‚ã£ãŸæ®µéšŽã‹ã‚‰ãƒ†ã‚¹ãƒˆãŒå§‹ã¾ã‚‹)。
+ // waitを入れるã“ã¨ã§ãれを防止ã§ãã‚‹
+ cy.wait(1000);
+ });
+
+ it('widget edit toggle is visible', () => {
+ cy.get('.mk-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');
+ });
+
+ it('first widget should be removed', () => {
+ cy.get('.mk-widget-edit').click();
+ cy.get('.customize-container:first-child .remove._button').click();
+ cy.get('.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('.bg._modalBg.transparent').click({ multiple: true, force: true });
+ cy.get('.mk-widget-add').click({ force: true });
+ cy.get(`.mkw-${widgetName}`).should('exist');
+ });
+ }
+
+ buildWidgetTest('memo');
+ buildWidgetTest('notifications');
+ buildWidgetTest('timeline');
+ buildWidgetTest('calendar');
+ buildWidgetTest('rss');
+ buildWidgetTest('trends');
+ buildWidgetTest('clock');
+ buildWidgetTest('activity');
+ buildWidgetTest('photos');
+ buildWidgetTest('digitalClock');
+ buildWidgetTest('federation');
+ buildWidgetTest('postForm');
+ buildWidgetTest('slideshow');
+ buildWidgetTest('serverMetric');
+ buildWidgetTest('onlineUsers');
+ buildWidgetTest('jobQueue');
+ buildWidgetTest('button');
+ buildWidgetTest('aiscript');
+ buildWidgetTest('aichan');
+});
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
index 119ab03f7c..95bfcf6855 100644
--- a/cypress/support/commands.js
+++ b/cypress/support/commands.js
@@ -23,3 +23,33 @@
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
+
+Cypress.Commands.add('resetState', () => {
+ cy.window(win => {
+ win.indexedDB.deleteDatabase('keyval-store');
+ });
+ cy.request('POST', '/api/reset-db').as('reset');
+ cy.get('@reset').its('status').should('equal', 204);
+ cy.reload(true);
+});
+
+Cypress.Commands.add('registerUser', (username, password, isAdmin = false) => {
+ const route = isAdmin ? '/api/admin/accounts/create' : '/api/signup';
+
+ cy.request('POST', route, {
+ username: username,
+ password: password,
+ }).its('body').as(username);
+});
+
+Cypress.Commands.add('login', (username, password) => {
+ cy.visit('/');
+
+ cy.intercept('POST', '/api/signin').as('signin');
+
+ cy.get('[data-cy-signin]').click();
+ cy.get('[data-cy-signin-username] input').type(username);
+ cy.get('[data-cy-signin-password] input').type(`${password}{enter}`);
+
+ cy.wait('@signin').as('signedIn');
+});
diff --git a/cypress/support/index.js b/cypress/support/e2e.js
index 9185be344c..9185be344c 100644
--- a/cypress/support/index.js
+++ b/cypress/support/e2e.js
diff --git a/docker-compose.yml b/docker-compose.yml
index e1d51668a7..0bf17a5557 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -9,7 +9,7 @@ services:
- redis
# - es
ports:
- - "127.0.0.1:3000:3000"
+ - "3000:3000"
networks:
- internal_network
- external_network
diff --git a/gulpfile.js b/gulpfile.js
index b7aa4e328e..90f8ebaabe 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -37,7 +37,6 @@ gulp.task('copy:client:locales', cb => {
gulp.task('build:backend:script', () => {
return gulp.src(['./packages/backend/src/server/web/boot.js', './packages/backend/src/server/web/bios.js', './packages/backend/src/server/web/cli.js'])
- .pipe(replace('VERSION', JSON.stringify(meta.version)))
.pipe(replace('LANGS', JSON.stringify(Object.keys(locales))))
.pipe(terser({
toplevel: true
diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml
index f3f8b45777..3bd8f1e506 100644
--- a/locales/ar-SA.yml
+++ b/locales/ar-SA.yml
@@ -141,7 +141,7 @@ flagAsBotDescription: "ÙØ¹Ù‘Ù„ هذا الخيار إذا كان هذا الحØ
flagAsCat: "علّم هذا الحساب كحساب قط"
flagAsCatDescription: "ÙØ¹Ù‘Ù„ هذا الخيار لوضع علامة على الحساب لتوضيح أنه حساب قط."
flagShowTimelineReplies: "أظهر التعليقات ÙÙŠ الخيط الزمني"
-flagShowTimelineRepliesDescription: "يظهر الردود ÙÙŠ الخط الزمني"
+flagShowTimelineRepliesDescription: "يظهر الردود ÙÙŠ الخيط الزمني"
autoAcceptFollowed: "اقبل طلبات المتابعة تلقائيا من الحسابات المتابَعة"
addAccount: "أض٠حساباً"
loginFailed: "ÙØ´Ù„ الولوج"
@@ -312,12 +312,12 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Ø§Ù„ØµÙØ­Ø§Øª"
-integration: "دمج"
+integration: "التكامل"
connectService: "اتصل"
disconnectService: "اقطع الاتصال"
enableLocalTimeline: "ØªÙØ¹ÙŠÙ„ الخيط المحلي"
enableGlobalTimeline: "ØªÙØ¹ÙŠÙ„ الخيط الزمني الشامل"
-disablingTimelinesInfo: "سيتمكن المديرون والمشرÙون من الوصول إلى كل الخطوط الزمنية حتى وإن لم ØªÙØ¹Ù‘Ù„."
+disablingTimelinesInfo: "سيتمكن المديرون والمشرÙون من الوصول إلى كل الخيوط الزمنية حتى وإن لم ØªÙØ¹Ù‘Ù„."
registration: "إنشاء حساب"
enableRegistration: "ØªÙØ¹ÙŠÙ„ إنشاء الحسابات الجديدة"
invite: "دعوة"
@@ -532,6 +532,7 @@ poll: "استطلاع رأي"
useCw: "Ø¥Ø®ÙØ§Ø¡ المحتوى"
enablePlayer: "Ø§ÙØªØ­ مشغل الÙيديو"
disablePlayer: "أغلق مشغل الÙيديو"
+expandTweet: "وسّع التغريدة"
themeEditor: "مصمم القوالب"
description: "الوصÙ"
describeFile: "أض٠تعليقًا توضيحيًا"
@@ -635,6 +636,7 @@ yes: "نعم"
no: "لا"
driveFilesCount: "عدد Ø§Ù„Ù…Ù„ÙØ§Øª ÙÙŠ قرص التخزين"
driveUsage: "المستغل من قرص التخزين"
+noCrawle: "Ø§Ø±ÙØ¶ Ùهرسة زاح٠الويب"
noCrawleDescription: "يطلب من محركات البحث ألّا ÙŠÙÙهرسوا ملÙÙƒ الشخصي وملاحظات ÙˆØµÙØ­Ø§ØªÙƒ وما شابه."
alwaysMarkSensitive: "علّم Ø§ÙØªØ±Ø§Ø¶ÙŠÙ‹Ø§ جميع ملاحظاتي كذات محتوى حساس"
loadRawImages: "حمّل الصور الأصلية بدلًا من المصغرات"
@@ -878,9 +880,11 @@ _mfm:
center: "وسط"
centerDescription: "يمركز المحتوى ÙÙŠ الوَسَط."
quote: "اقتبس"
+ quoteDescription: "يعرض المحتوى كاقتباس"
emoji: "إيموجي مخصص"
emojiDescription: "إحاطة اسم الإيموجي بنقطتي ØªÙØ³ÙŠØ± سيستبدله بصورة الإيموجي."
search: "البحث"
+ searchDescription: "يعرض نصًا ÙÙŠ صندوق البحث"
flip: "اقلب"
flipDescription: "يقلب المحتوى عموديًا أو Ø£Ùقيًا"
jelly: "تأثير (هلام)"
@@ -1003,7 +1007,6 @@ _sfx:
antenna: "الهوائيات"
channel: "إشعارات القنات"
_ago:
- unknown: "مجهول"
future: "المستقبَل"
justNow: "اللحظة"
secondsAgo: "منذ {n} ثوانÙ"
@@ -1030,12 +1033,12 @@ _tutorial:
step3_3: "املأ النموذج وانقر الزرّ الموجود ÙÙŠ أعلى اليمين للإرسال."
step3_4: "ليس لديك ما تقوله؟ إذا اكتب \"بدأت٠استخدم ميسكي\"."
step4_1: "هل نشرت ملاحظتك الأولى؟"
- step4_2: "مرحى! يمكنك الآن رؤية ملاحظتك ÙÙŠ الخط الزمني."
- step5_1: "والآن، لنجعل الخط الزمني أكثر حيوية وذلك بمتابعة بعض المستخدمين."
+ step4_2: "مرحى! يمكنك الآن رؤية ملاحظتك ÙÙŠ الخيط الزمني."
+ step5_1: "والآن، لنجعل الخيط الزمني أكثر حيوية وذلك بمتابعة بعض المستخدمين."
step5_2: "تعرض ØµÙØ­Ø© {features} الملاحظات المتداولة ÙÙŠ هذا المثيل ويتيح لك {Explore} العثور على المستخدمين الرائدين. اعثر على الأشخاص الذين يثيرون إهتمامك وتابعهم!"
step5_3: "لمتابعة مستخدمين ادخل ملÙهم الشخصي بالنقر على صورتهم الشخصية ثم اضغط زر 'تابع'."
step5_4: "إذا كان لدى المستخدم رمز Ù‚ÙÙ„ بجوار اسمه ØŒ وجب عليك انتظاره ليقبل طلب المتابعة يدويًا."
- step6_1: "الآن ستتمكن من رؤية ملاحظات المستخدمين المتابَعين ÙÙŠ الخط الزمني."
+ step6_1: "الآن ستتمكن من رؤية ملاحظات المستخدمين المتابَعين ÙÙŠ الخيط الزمني."
step6_2: "يمكنك Ø§Ù„ØªÙØ§Ø¹Ù„ بسرعة مع الملاحظات عن طريق Ø¥Ø¶Ø§ÙØ© \"ØªÙØ§Ø¹Ù„\"."
step6_3: "Ù„Ø¥Ø¶Ø§ÙØ© ØªÙØ§Ø¹Ù„ لملاحظة ØŒ انقر Ùوق علامة \"+\" أسÙÙ„ للملاحظة واختر الإيموجي المطلوب."
step7_1: "مبارك ! أنهيت الدورة التعليمية الأساسية لاستخدام ميسكي."
@@ -1201,8 +1204,13 @@ _charts:
_instanceCharts:
requests: "الطلبات"
users: "تباين عدد المستخدمين"
+ usersTotal: "تباين عدد المستخدمين"
notes: "تباين عدد الملاحظات"
+ notesTotal: "تباين عدد الملاحظات"
+ ff: "تباين عدد حسابات المتابَعة/Ø§Ù„Ù…ØªØ§Ø¨ÙØ¹Ø©"
+ ffTotal: "تباين عدد حسابات المتابَعة/Ø§Ù„Ù…ØªØ§Ø¨ÙØ¹Ø©"
files: "تباين عدد Ø§Ù„Ù…Ù„ÙØ§Øª"
+ filesTotal: "تباين عدد Ø§Ù„Ù…Ù„ÙØ§Øª"
_timelines:
home: "الرئيسي"
local: "المحلي"
@@ -1321,6 +1329,7 @@ _pages:
random: "عشوائي"
value: "القيم"
fn: "دوال"
+ text: "إجراءات على النصوص"
convert: "تحويل"
list: "القوائم"
blocks:
@@ -1501,6 +1510,10 @@ _notification:
followRequestAccepted: "طلبات المتابعة المقبولة"
groupInvited: "دعوات Ø§Ù„ÙØ±ÙŠÙ‚"
app: "إشعارات التطبيقات المرتبطة"
+ _actions:
+ followBack: "تابعك بالمثل"
+ reply: "رد"
+ renote: "أعد النشر"
_deck:
alwaysShowMainColumn: "أظهر العمود الرئيسي دائمًا"
columnAlign: "حاذ٠الأعمدة"
diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml
index b2ba236fd5..d7753b6dcf 100644
--- a/locales/bn-BD.yml
+++ b/locales/bn-BD.yml
@@ -831,11 +831,18 @@ themeColor: "থিমের রং"
size: "আকার"
numberOfColumn: "কলামের সংখà§à¦¯à¦¾"
searchByGoogle: "গà§à¦—ল"
+instanceDefaultLightTheme: "ইনà§à¦¸à¦Ÿà§à¦¯à¦¾à¦¨à§à¦¸à§‡à¦° ডিফলà§à¦Ÿ লাইট থিম"
+instanceDefaultDarkTheme: "ইনà§à¦¸à¦Ÿà§à¦¯à¦¾à¦¨à§à¦¸à§‡à¦° ডিফলà§à¦Ÿ ডারà§à¦• থিম"
+instanceDefaultThemeDescription: "অবজেকà§à¦Ÿ ফরমà§à¦¯à¦¾à¦Ÿà§‡ থিম কোড লিখà§à¦¨"
+mutePeriod: "মিউটের সময়কাল"
indefinitely: "অনিরà§à¦¦à¦¿à¦·à§à¦Ÿ"
tenMinutes: "১০ মিনিট"
oneHour: "à§§ ঘণà§à¦Ÿà¦¾"
oneDay: "à¦à¦•দিন"
oneWeek: "à¦à¦• সপà§à¦¤à¦¾à¦¹"
+reflectMayTakeTime: "à¦à¦Ÿà¦¿à¦° কাজ দেখা যেতে কিছà§à¦Ÿà¦¾ সময় লাগতে পারে।"
+failedToFetchAccountInformation: "অà§à¦¯à¦¾à¦•াউনà§à¦Ÿà§‡à¦° তথà§à¦¯ উদà§à¦§à¦¾à¦° করা যায়নি"
+rateLimitExceeded: "রেট লিমিট ছাড়িয়ে গেছে "
_emailUnavailable:
used: "à¦à¦‡ ইমেইল ঠিকানাটি ইতোমধà§à¦¯à§‡ বà§à¦¯à¦¬à¦¹à§ƒà¦¤ হয়েছে"
format: "à¦à¦‡ ইমেল ঠিকানাটি সঠিকভাবে লিখা হয়নি"
@@ -1081,7 +1088,6 @@ _sfx:
antenna: "অà§à¦¯à¦¾à¦¨à§à¦Ÿà§‡à¦¨à¦¾à¦—à§à¦²à¦¿"
channel: "চà§à¦¯à¦¾à¦¨à§‡à¦²à§‡à¦° বিজà§à¦žà¦ªà§à¦¤à¦¿"
_ago:
- unknown: "অজানা"
future: "ভবিষà§à¦¯à§Ž"
justNow: "à¦à¦‡à¦®à¦¾à¦¤à§à¦°"
secondsAgo: "{n} সেকেনà§à¦¡ আগে"
@@ -1125,6 +1131,7 @@ _2fa:
registerKey: "সিকিউরিটি কী নিবনà§à¦§à¦¨ করà§à¦¨"
step1: "পà§à¦°à¦¥à¦®à§‡, আপনার ডিভাইসে {a} বা {b} à¦à¦° মতো à¦à¦•টি অথেনটিকেশন অà§à¦¯à¦¾à¦ª ইনসà§à¦Ÿà¦² করà§à¦¨à§·"
step2: "à¦à¦°à¦ªà¦°à§‡, অà§à¦¯à¦¾à¦ªà§‡à¦° সাহাযà§à¦¯à§‡ পà§à¦°à¦¦à¦°à§à¦¶à¦¿à¦¤ QR কোডটি সà§à¦•à§à¦¯à¦¾à¦¨ করà§à¦¨à¥¤"
+ step2Url: "ডেসà§à¦•টপ অà§à¦¯à¦¾à¦ªà§‡, নিমà§à¦¨à¦²à¦¿à¦–িত URL লিখà§à¦¨:"
step3: "অà§à¦¯à¦¾à¦ªà§‡ পà§à¦°à¦¦à¦°à§à¦¶à¦¿à¦¤ টোকেনটি লিখà§à¦¨ à¦à¦¬à¦‚ আপনার কাজ শেষ।"
step4: "আপনাকে à¦à¦–ন থেকে লগ ইন করার সময়, à¦à¦‡à¦­à¦¾à¦¬à§‡ টোকেন লিখতে হবে।"
securityKeyInfo: "আপনি à¦à¦•টি হারà§à¦¡à¦“য়à§à¦¯à¦¾à¦° সিকিউরিটি কী বà§à¦¯à¦¬à¦¹à¦¾à¦° করে লগ ইন করতে পারেন যা FIDO2 বা ডিভাইসের ফিঙà§à¦—ারপà§à¦°à¦¿à¦¨à§à¦Ÿ সেনà§à¦¸à¦° বা পিন সমরà§à¦¥à¦¨ করে৷"
@@ -1608,6 +1615,8 @@ _notification:
youReceivedFollowRequest: "অনà§à¦¸à¦°à¦£ করার জনà§à¦¯ অনà§à¦°à§‹à¦§ পাওয়া গেছে"
yourFollowRequestAccepted: "আপনার অনà§à¦¸à¦°à¦£ করার অনà§à¦°à§‹à¦§ গৃহীত হয়েছে"
youWereInvitedToGroup: "আপনি à¦à¦•টি গà§à¦°à§à¦ªà§‡ আমনà§à¦¤à§à¦°à¦¿à¦¤ হয়েছেন"
+ pollEnded: "পোলের ফলাফল দেখা যাবে"
+ emptyPushNotificationMessage: "আপডেট করা পà§à¦¶ বিজà§à¦žà¦ªà§à¦¤à¦¿"
_types:
all: "সকল"
follow: "অনà§à¦¸à¦°à¦£ করা হচà§à¦›à§‡"
@@ -1617,10 +1626,15 @@ _notification:
quote: "উদà§à¦§à§ƒà¦¤à¦¿"
reaction: "পà§à¦°à¦¤à¦¿à¦•à§à¦°à¦¿à¦¯à¦¼à¦¾"
pollVote: "পোলে ভোট আছে"
+ pollEnded: "পোল শেষ"
receiveFollowRequest: "পà§à¦°à¦¾à¦ªà§à¦¤ অনà§à¦¸à¦°à¦£à§‡à¦° অনà§à¦°à§‹à¦§à¦¸à¦®à§‚হ"
followRequestAccepted: "গৃহীত অনà§à¦¸à¦°à¦£à§‡à¦° অনà§à¦°à§‹à¦§à¦¸à¦®à§‚হ"
groupInvited: "গà§à¦°à§à¦ªà§‡à¦° আমনà§à¦¤à§à¦°à¦¨à¦¸à¦®à§‚হ"
app: "লিঙà§à¦• করা অà§à¦¯à¦¾à¦ª থেকে বিজà§à¦žà¦ªà§à¦¤à¦¿"
+ _actions:
+ followBack: "ফলো বà§à¦¯à¦¾à¦• করেছে"
+ reply: "জবাব"
+ renote: "রিনোট"
_deck:
alwaysShowMainColumn: "সরà§à¦¬à¦¦à¦¾ মেইন কলাম দেখান"
columnAlign: "কলাম সাজান"
diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index 5f74cb6bef..74eab3603b 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -1,6 +1,8 @@
---
_lang_: "Català"
headlineMisskey: "Una xarxa connectada per notes"
+introMisskey: "Benvingut! Misskey és un servei de microblogging descentralitzat de codi obert.\nCrea \"notes\" per compartir els teus pensaments amb tots els que t'envolten. 📡\nAmb \"reaccions\", també pots expressar ràpidament els teus sentiments sobre les notes de tothom. ðŸ‘\nExplorem un món nou! 🚀"
+monthAndDay: "{day}/{month}"
search: "Cercar"
notifications: "Notificacions"
username: "Nom d'usuari"
@@ -10,17 +12,175 @@ fetchingAsApObject: "Cercant en el Fediverse..."
ok: "OK"
gotIt: "Ho he entès!"
cancel: "Cancel·lar"
+enterUsername: "Introdueix el teu nom d'usuari"
+renotedBy: "Resignat per {usuari}"
+noNotes: "Cap nota"
+noNotifications: "Cap notificació"
+instance: "Instàncies"
+settings: "Preferències"
+basicSettings: "Configuració bàsica"
+otherSettings: "Configuració avançada"
+openInWindow: "Obrir en una nova finestra"
+profile: "Perfil"
+timeline: "Línia de temps"
+noAccountDescription: "Aquest usuari encara no ha escrit la seva biografia."
+login: "Iniciar sessió"
+loggingIn: "Identificant-se"
+logout: "Tancar la sessió"
+signup: "Registrar-se"
+uploading: "Pujant..."
+save: "Desar"
+users: "Usuaris"
+addUser: "Afegir un usuari"
+favorite: "Afegir a preferits"
+favorites: "Favorits"
+unfavorite: "Eliminar dels preferits"
+favorited: "Afegit als preferits."
+alreadyFavorited: "Ja s'ha afegit als preferits."
+cantFavorite: "No s'ha pogut afegir als preferits."
+pin: "Fixar al perfil"
+unpin: "Para de fixar del perfil"
+copyContent: "Copiar el contingut"
+copyLink: "Copiar l'enllaç"
+delete: "Eliminar"
+deleteAndEdit: "Esborrar i editar"
+deleteAndEditConfirm: "Estàs segur que vols suprimir aquesta nota i editar-la? Perdràs totes les reaccions, notes i respostes."
+addToList: "Afegir a una llista"
+sendMessage: "Enviar un missatge"
+copyUsername: "Copiar nom d'usuari"
+searchUser: "Cercar usuaris"
+reply: "Respondre"
+loadMore: "Carregar més"
+showMore: "Veure més"
+youGotNewFollower: "t'ha seguit"
+receiveFollowRequest: "Sol·licitud de seguiment rebuda"
+followRequestAccepted: "Sol·licitud de seguiment acceptada"
+mention: "Menció"
+mentions: "Mencions"
+directNotes: "Notes directes"
+importAndExport: "Importar / Exportar"
+import: "Importar"
+export: "Exportar"
+files: "Fitxers"
+download: "Baixar"
+driveFileDeleteConfirm: "Estàs segur que vols suprimir el fitxer \"{name}\"? Les notes associades a aquest fitxer adjunt també se suprimiran."
+unfollowConfirm: "Estàs segur que vols deixar de seguir {name}?"
+exportRequested: "Has sol·licitat una exportació. Això pot trigar una estona. S'afegirà a la teva unitat un cop completat."
+importRequested: "Has sol·licitat una importació. Això pot trigar una estona."
+lists: "Llistes"
+noLists: "No tens cap llista"
+note: "Nota"
+notes: "Notes"
+following: "Seguint"
+followers: "Seguidors"
+followsYou: "Et segueix"
+createList: "Crear llista"
+manageLists: "Gestionar les llistes"
+error: "Error"
+somethingHappened: "S'ha produït un error"
+retry: "Torna-ho a intentar"
+pageLoadError: "S'ha produït un error en carregar la pàgina"
+pageLoadErrorDescription: "Això normalment es deu a errors de xarxa o a la memòria cau del navegador. Prova d'esborrar la memòria cau i torna-ho a provar després d'esperar una estona."
+serverIsDead: "Aquest servidor no respon. Espera una estona i torna-ho a provar."
+youShouldUpgradeClient: "Per veure aquesta pàgina, actualitzeu-la per actualitzar el vostre client."
+enterListName: "Introdueix un nom per a la llista"
+privacy: "Privadesa"
+makeFollowManuallyApprove: "Les sol·licituds de seguiment requereixen aprovació"
+defaultNoteVisibility: "Visibilitat per defecte"
+follow: "Seguint"
+followRequest: "Enviar la sol·licitud de seguiment"
+followRequests: "Sol·licituds de seguiment"
+unfollow: "Deixar de seguir"
+followRequestPending: "Sol·licituds de seguiment pendents"
+enterEmoji: "Introduir un emoji"
+renote: "Renotar"
+unrenote: "Anul·lar renota"
+renoted: "Renotat."
+cantRenote: "Aquesta publicació no pot ser renotada."
+cantReRenote: "Impossible renotar una renota."
+quote: "Citar"
+pinnedNote: "Nota fixada"
+pinned: "Fixar al perfil"
+you: "Tu"
+clickToShow: "Fes clic per mostrar"
+sensitive: "NSFW"
+add: "Afegir"
+reaction: "Reaccions"
+reactionSetting: "Reaccions a mostrar al selector de reaccions"
+reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem \"+\" per afegir."
+rememberNoteVisibility: "Recorda la configuració de visibilitat de les notes"
+attachCancel: "Eliminar el fitxer adjunt"
+markAsSensitive: "Marcar com a NSFW"
+instances: "Instàncies"
+remove: "Eliminar"
+nsfw: "NSFW"
+pinnedNotes: "Nota fixada"
+userList: "Llistes"
smtpUser: "Nom d'usuari"
smtpPass: "Contrasenya"
+user: "Usuaris"
searchByGoogle: "Cercar"
+_email:
+ _follow:
+ title: "t'ha seguit"
_mfm:
+ mention: "Menció"
+ quote: "Citar"
search: "Cercar"
+_theme:
+ keys:
+ mention: "Menció"
+ renote: "Renotar"
_sfx:
+ note: "Notes"
notification: "Notificacions"
+_2fa:
+ step2Url: "També pots inserir aquest enllaç i utilitzes una aplicació d'escriptori:"
_widgets:
notifications: "Notificacions"
+ timeline: "Línia de temps"
+_cw:
+ show: "Carregar més"
+_visibility:
+ followers: "Seguidors"
_profile:
username: "Nom d'usuari"
+_exportOrImport:
+ followingList: "Seguint"
+ userLists: "Llistes"
+_pages:
+ script:
+ categories:
+ list: "Llistes"
+ blocks:
+ _join:
+ arg1: "Llistes"
+ _randomPick:
+ arg1: "Llistes"
+ _dailyRandomPick:
+ arg1: "Llistes"
+ _seedRandomPick:
+ arg2: "Llistes"
+ _pick:
+ arg1: "Llistes"
+ _listLen:
+ arg1: "Llistes"
+ types:
+ array: "Llistes"
+_notification:
+ youWereFollowed: "t'ha seguit"
+ _types:
+ follow: "Seguint"
+ mention: "Menció"
+ renote: "Renotar"
+ quote: "Citar"
+ reaction: "Reaccions"
+ _actions:
+ reply: "Respondre"
+ renote: "Renotar"
_deck:
_columns:
notifications: "Notificacions"
+ tl: "Línia de temps"
+ list: "Llistes"
+ mentions: "Mencions"
diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml
index 2f5e375372..4b20340df1 100644
--- a/locales/cs-CZ.yml
+++ b/locales/cs-CZ.yml
@@ -53,6 +53,8 @@ reply: "Odpovědět"
loadMore: "Zobrazit více"
showMore: "Zobrazit více"
youGotNewFollower: "Máte nového následovníka"
+receiveFollowRequest: "Žádost o sledování přijata"
+followRequestAccepted: "Žádost o sledování přijata"
mention: "Zmínění"
mentions: "Zmínění"
importAndExport: "Import a export"
@@ -60,7 +62,9 @@ import: "Importovat"
export: "Exportovat"
files: "Soubor(ů)"
download: "Stáhnout"
+driveFileDeleteConfirm: "Opravdu chcete smazat soubor \"{name}\"? Poznámky, ke kterým je tento soubor připojen, budou také smazány."
unfollowConfirm: "Jste si jisti že už nechcete sledovat {name}?"
+exportRequested: "Požádali jste o export. To může chvíli trvat. PÅ™idáme ho na váš Disk až bude dokonÄen."
importRequested: "Požádali jste o export. To může chvilku trvat."
lists: "Seznamy"
noLists: "Nemáte žádné seznamy"
@@ -75,13 +79,25 @@ error: "Chyba"
somethingHappened: "Jejda. Něco se nepovedlo."
retry: "Opakovat"
pageLoadError: "NepodaÅ™ilo se naÄíst stránku"
+serverIsDead: "Server neodpovídá. PoÄkejte chvíli a zkuste to znovu."
+youShouldUpgradeClient: "Pro zobrazení této stránky obnovte stránku pro aktualizaci klienta."
enterListName: "Jméno seznamu"
privacy: "Soukromí"
+makeFollowManuallyApprove: "Žádosti o sledování vyžadují potvrzení"
+defaultNoteVisibility: "Výchozí viditelnost"
follow: "Sledovaní"
+followRequest: "Odeslat žádost o sledování"
+followRequests: "Žádosti o sledování"
unfollow: "Přestat sledovat"
+followRequestPending: "Čekající žádosti o sledování"
+enterEmoji: "Vložte emoji"
renote: "Přeposlat"
+unrenote: "Zrušit přeposlání"
+renoted: "Přeposláno"
+cantRenote: "Tento příspěvek nelze přeposlat."
cantReRenote: "OdpovÄ›Ä nemůže být odstranÄ›na."
quote: "Citovat"
+pinnedNote: "Připnutá poznámka"
pinned: "Připnout"
you: "Vy"
clickToShow: "Klikněte pro zobrazení"
@@ -122,6 +138,8 @@ flagAsBot: "Tento úÄet je bot"
flagAsBotDescription: "Pokud je tento úÄet kontrolován programem zaÅ¡krtnÄ›te tuto možnost. To oznaÄí tento úÄet jako bot pro ostatní vývojáře a zabrání tak nekoneÄným interakcím s ostatními boty a upraví Misskey systém aby se choval k tomuhle úÄtu jako bot."
flagAsCat: "Tenhle úÄet je koÄka"
flagAsCatDescription: "Vyberte tuto možnost aby tento úÄet byl oznaÄen jako koÄka."
+flagShowTimelineReplies: "Zobrazovat odpovÄ›di na Äasové ose"
+flagShowTimelineRepliesDescription: "Je-li zapnuto, zobrazí odpovÄ›di uživatelů na poznámky jiných uživatelů na vaší Äasové ose."
autoAcceptFollowed: "Automaticky akceptovat následování od úÄtů které sledujete"
addAccount: "PÅ™idat úÄet"
loginFailed: "Přihlášení se nezdařilo."
@@ -130,13 +148,16 @@ general: "ObecnÄ›"
wallpaper: "Obrázek na pozadí"
setWallpaper: "Nastavení obrázku na pozadí"
removeWallpaper: "Odstranit pozadí"
+searchWith: "Hledat: {q}"
youHaveNoLists: "Nemáte žádné seznamy"
+followConfirm: "Jste si jisti, že chcete sledovat {name}?"
proxyAccount: "Proxy úÄet"
proxyAccountDescription: "Proxy úÄet je úÄet, který za urÄitých podmínek sleduje uživatele na dálku vaším jménem. Například když uživatel zaÅ™adí vzdáleného uživatele do seznamu, pokud nikdo nesleduje uživatele na seznamu, aktivita nebude doruÄena instanci, takže místo toho bude uživatele sledovat úÄet proxy."
host: "Hostitel"
selectUser: "Vyberte uživatele"
recipient: "Pro"
annotation: "Komentáře"
+federation: "Federace"
instances: "Instance"
registeredAt: "Registrován"
latestRequestSentAt: "Poslední požadavek poslán"
@@ -146,6 +167,7 @@ storageUsage: "Využití úložiště"
charts: "Grafy"
perHour: "za hodinu"
perDay: "za den"
+stopActivityDelivery: "Přestat zasílat aktivitu"
blockThisInstance: "Blokovat tuto instanci"
operations: "Operace"
software: "Software"
@@ -283,6 +305,8 @@ iconUrl: "Favicon URL"
bannerUrl: "Baner URL"
backgroundImageUrl: "Adresa URL obrázku pozadí"
basicInfo: "Základní informace"
+pinnedUsers: "Připnutí uživatelé"
+pinnedNotes: "Připnutá poznámka"
hcaptcha: "hCaptcha"
enableHcaptcha: "Aktivovat hCaptchu"
hcaptchaSecretKey: "Tajný KlÃ­Ä (Secret Key)"
@@ -471,6 +495,7 @@ _widgets:
notifications: "Oznámení"
timeline: "Časová osa"
activity: "Aktivita"
+ federation: "Federace"
jobQueue: "Fronta úloh"
_cw:
show: "Zobrazit více"
@@ -485,6 +510,8 @@ _exportOrImport:
muteList: "Ztlumit"
blockingList: "Zablokovat"
userLists: "Seznamy"
+_charts:
+ federation: "Federace"
_timelines:
home: "Domů"
_pages:
@@ -517,6 +544,9 @@ _notification:
renote: "Přeposlat"
quote: "Citovat"
reaction: "Reakce"
+ _actions:
+ reply: "Odpovědět"
+ renote: "Přeposlat"
_deck:
_columns:
notifications: "Oznámení"
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index 1f558787ab..5dfce28002 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -842,6 +842,9 @@ oneDay: "Einen Tag"
oneWeek: "Eine Woche"
reflectMayTakeTime: "Es kann etwas dauern, bis sich dies widerspiegelt."
failedToFetchAccountInformation: "Benutzerkontoinformationen konnten nicht abgefragt werden"
+rateLimitExceeded: "Versuchsanzahl überschritten"
+cropImage: "Bild zuschneiden"
+cropImageAsk: "Möchtest du das Bild zuschneiden?"
_emailUnavailable:
used: "Diese Email-Adresse wird bereits verwendet"
format: "Das Format dieser Email-Adresse ist ungültig"
@@ -1006,7 +1009,7 @@ _instanceMute:
heading: "Liste der stummzuschaltenden Instanzen"
_theme:
explore: "Farbschemata erforschen"
- install: "Farbschmata installieren"
+ install: "Farbschemata installieren"
manage: "Farbschemaverwaltung"
code: "Farbschemencode"
description: "Beschreibung"
@@ -1087,7 +1090,6 @@ _sfx:
antenna: "Antennen"
channel: "Kanalbenachrichtigung"
_ago:
- unknown: "Unbekannt"
future: "Zukunft"
justNow: "Gerade eben"
secondsAgo: "vor {n} Sekunde(n)"
@@ -1131,6 +1133,7 @@ _2fa:
registerKey: "Neuen Sicherheitsschlüssel registrieren"
step1: "Installiere zuerst eine Authentifizierungsapp (z.B. {a} oder {b}) auf deinem Gerät."
step2: "Dann, scanne den angezeigten QR-Code mit deinem Gerät."
+ step2Url: "Nutzt du ein Desktopprogramm kannst du alternativ diese URL eingeben:"
step3: "Gib zum Abschluss den Token ein, der von deiner App angezeigt wird."
step4: "Alle folgenden Anmeldungsversuche werden ab sofort die Eingabe eines solchen Tokens benötigen."
securityKeyInfo: "Du kannst neben Fingerabdruck- oder PIN-Authentifizierung auf deinem Gerät auch Anmeldung mit Hilfe eines FIDO2-kompatiblen Hardware-Sicherheitsschlüssels einrichten."
@@ -1613,8 +1616,9 @@ _notification:
youWereFollowed: "ist dir gefolgt"
youReceivedFollowRequest: "Du hast eine Follow-Anfrage erhalten"
yourFollowRequestAccepted: "Deine Follow-Anfrage wurde akzeptiert"
- youWereInvitedToGroup: "Du wurdest in eine Gruppe eingeladen"
+ youWereInvitedToGroup: "{userName} hat dich in eine Gruppe eingeladen"
pollEnded: "Umfrageergebnisse sind verfügbar"
+ emptyPushNotificationMessage: "Push-Benachrichtigungen wurden aktualisiert"
_types:
all: "Alle"
follow: "Neue Follower"
@@ -1629,6 +1633,10 @@ _notification:
followRequestAccepted: "Akzeptierte Follow-Anfragen"
groupInvited: "Erhaltene Gruppeneinladungen"
app: "Benachrichtigungen von Apps"
+ _actions:
+ followBack: "folgt dir nun auch"
+ reply: "Antworten"
+ renote: "Renote"
_deck:
alwaysShowMainColumn: "Hauptspalte immer zeigen"
columnAlign: "Spaltenausrichtung"
diff --git a/locales/en-US.yml b/locales/en-US.yml
index 99fe05375b..8bfea26b0f 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -842,6 +842,9 @@ oneDay: "One day"
oneWeek: "One week"
reflectMayTakeTime: "It may take some time for this to be reflected."
failedToFetchAccountInformation: "Could not fetch account information"
+rateLimitExceeded: "Rate limit exceeded"
+cropImage: "Crop image"
+cropImageAsk: "Do you want to crop this image?"
_emailUnavailable:
used: "This email address is already being used"
format: "The format of this email address is invalid"
@@ -1087,7 +1090,6 @@ _sfx:
antenna: "Antennas"
channel: "Channel notifications"
_ago:
- unknown: "Unknown"
future: "Future"
justNow: "Just now"
secondsAgo: "{n} second(s) ago"
@@ -1131,6 +1133,7 @@ _2fa:
registerKey: "Register a security key"
step1: "First, install an authentication app (such as {a} or {b}) on your device."
step2: "Then, scan the QR code displayed on this screen."
+ step2Url: "You can also enter this URL if you're using a desktop program:"
step3: "Enter the token provided by your app to finish setup."
step4: "From now on, any future login attempts will ask for such a login token."
securityKeyInfo: "Besides fingerprint or PIN authentication, you can also setup authentication via hardware security keys that support FIDO2 to further secure your account."
@@ -1613,8 +1616,9 @@ _notification:
youWereFollowed: "followed you"
youReceivedFollowRequest: "You've received a follow request"
yourFollowRequestAccepted: "Your follow request was accepted"
- youWereInvitedToGroup: "You've been invited to a group"
+ youWereInvitedToGroup: "{userName} invited you to a group"
pollEnded: "Poll results have become available"
+ emptyPushNotificationMessage: "Push notifications have been updated"
_types:
all: "All"
follow: "New followers"
@@ -1629,6 +1633,10 @@ _notification:
followRequestAccepted: "Accepted follow requests"
groupInvited: "Group invitations"
app: "Notifications from linked apps"
+ _actions:
+ followBack: "followed you back"
+ reply: "Reply"
+ renote: "Renote"
_deck:
alwaysShowMainColumn: "Always show main column"
columnAlign: "Align columns"
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index fd69f62ff5..6c10942b48 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -141,6 +141,8 @@ flagAsBot: "Esta cuenta es un bot"
flagAsBotDescription: "En caso de que esta cuenta fuera usada por un programa, active esta opción. Al hacerlo, esta opción servirá para otros desarrolladores para evitar cadenas infinitas de reacciones, y ajustará los sistemas internos de Misskey para que trate a esta cuenta como un bot."
flagAsCat: "Esta cuenta es un gato"
flagAsCatDescription: "En caso de que declare que esta cuenta es de un gato, active esta opción."
+flagShowTimelineReplies: "Mostrar respuestas a las notas en la biografía"
+flagShowTimelineRepliesDescription: "Cuando se marca, la línea de tiempo muestra respuestas a otras notas además de las notas del usuario"
autoAcceptFollowed: "Aceptar automáticamente las solicitudes de seguimiento de los usuarios que sigues"
addAccount: "Agregar Cuenta"
loginFailed: "Error al iniciar sesión."
@@ -235,6 +237,8 @@ resetAreYouSure: "¿Desea reestablecer?"
saved: "Guardado"
messaging: "Chat"
upload: "Subir"
+keepOriginalUploading: "Mantener la imagen original"
+keepOriginalUploadingDescription: "Mantener la versión original al cargar imágenes. Si está desactivado, el navegador generará imágenes para la publicación web en el momento de recargar la página"
fromDrive: "Desde el drive"
fromUrl: "Desde la URL"
uploadFromUrl: "Subir desde una URL"
@@ -444,6 +448,7 @@ uiLanguage: "Idioma de visualización de la interfaz"
groupInvited: "Invitado al grupo"
aboutX: "Acerca de {x}"
useOsNativeEmojis: "Usa los emojis nativos de la plataforma"
+disableDrawer: "No mostrar los menús en cajones"
youHaveNoGroups: "Sin grupos"
joinOrCreateGroup: "Obtenga una invitación para unirse al grupos o puede crear su propio grupo."
noHistory: "No hay datos en el historial"
@@ -615,6 +620,10 @@ reportAbuse: "Reportar"
reportAbuseOf: "Reportar a {name}"
fillAbuseReportDescription: "Ingrese los detalles del reporte. Si hay una nota en particular, ingrese la URL de esta."
abuseReported: "Se ha enviado el reporte. Muchas gracias."
+reporteeOrigin: "Informar a"
+reporterOrigin: "Origen del informe"
+forwardReport: "Transferir un informe a una instancia remota"
+forwardReportIsAnonymous: "No puede ver su información de la instancia remota y aparecerá como una cuenta anónima del sistema"
send: "Enviar"
abuseMarkAsResolved: "Marcar reporte como resuelto"
openInNewTab: "Abrir en una Nueva Pestaña"
@@ -676,6 +685,7 @@ center: "Centrar"
wide: "Ancho"
narrow: "Estrecho"
reloadToApplySetting: "Esta configuración sólo se aplicará después de recargar la página. ¿Recargar ahora?"
+needReloadToApply: "Se requiere un reinicio para la aplicar los cambios"
showTitlebar: "Mostrar la barra de título"
clearCache: "Limpiar caché"
onlineUsersCount: "{n} usuarios en línea"
@@ -706,19 +716,55 @@ capacity: "Capacidad"
inUse: "Usado"
editCode: "Editar código"
apply: "Aplicar"
+receiveAnnouncementFromInstance: "Recibir notificaciones de la instancia"
+emailNotification: "Notificaciones por correo electrónico"
publish: "Publicar"
inChannelSearch: "Buscar en el canal"
+useReactionPickerForContextMenu: "Haga clic con el botón derecho para abrir el menu de reacciones"
+typingUsers: "{users} está escribiendo"
+jumpToSpecifiedDate: "Saltar a una fecha específica"
+showingPastTimeline: "Mostrar líneas de tiempo antiguas"
+clear: "Limpiar"
markAllAsRead: "Marcar todo como leído"
goBack: "Deseleccionar"
+fullView: "Vista completa"
+quitFullView: "quitar vista completa"
+addDescription: "Agregar descripción"
+userPagePinTip: "Puede mantener sus notas visibles aquí seleccionando Pin en el menú de notas individuales"
+notSpecifiedMentionWarning: "Algunas menciones no están incluidas en el destino"
info: "Información"
+userInfo: "Información del usuario"
+unknown: "Desconocido"
+onlineStatus: "En línea"
+hideOnlineStatus: "mostrarse como desconectado"
+hideOnlineStatusDescription: "Ocultar su estado en línea puede reducir la eficacia de algunas funciones, como la búsqueda"
online: "En línea"
+active: "Activo"
offline: "Sin conexión"
+notRecommended: "obsoleto"
+botProtection: "Protección contra bots"
+instanceBlocking: "Instancias bloqueadas"
+selectAccount: "Elija una cuenta"
+switchAccount: "Cambiar de cuenta"
+enabled: "Activado"
+disabled: "Desactivado"
+quickAction: "Acciones rápidas"
user: "Usuarios"
administration: "Administrar"
+accounts: "Cuentas"
+switch: "Cambiar"
+noMaintainerInformationWarning: "No se ha establecido la información del administrador"
+noBotProtectionWarning: "La protección contra los bots no está configurada"
+configure: "Configurar"
+postToGallery: "Crear una nueva publicación en la galería"
gallery: "Galería"
recentPosts: "Posts recientes"
popularPosts: "Más vistos"
+shareWithNote: "Compartir con una nota"
+ads: "Anuncios"
expiration: "Termina el"
+memo: "Notas"
+priority: "Prioridad"
high: "Alta"
middle: "Mediano"
low: "Baja"
@@ -770,22 +816,50 @@ _accountDelete:
accountDelete: "Eliminar Cuenta"
_ad:
back: "Deseleccionar"
+_forgotPassword:
+ contactAdmin: "Esta instancia no admite el uso de direcciones de correo electrónico, póngase en contacto con el administrador de la instancia para restablecer su contraseña"
_gallery:
my: "Mi galería"
+ liked: "Publicaciones que me gustan"
+ like: "¡Muy bien!"
unlike: "Quitar me gusta"
_email:
_follow:
title: "te ha seguido"
+ _receiveFollowRequest:
+ title: "Has recibido una solicitud de seguimiento"
+_plugin:
+ install: "Instalar plugins"
+ installWarn: "Por favor no instale plugins que no son de confianza"
+ manage: "Gestionar plugins"
_registry:
+ scope: "Alcance"
key: "Clave"
keys: "Clave"
+ domain: "Dominio"
+ createKey: "Crear una llave"
+_aboutMisskey:
+ about: "Misskey es un software de código abierto, desarrollado por syuilo desde el 2014"
+ contributors: "Principales colaboradores"
+ allContributors: "Todos los colaboradores"
+ source: "Código fuente"
+ translation: "Traducir Misskey"
+ donate: "Donar a Misskey"
+ morePatrons: "Muchas más personas nos apoyan. Muchas gracias🥰"
+ patrons: "Patrocinadores"
+_nsfw:
+ respect: "Ocultar medios NSFW"
+ ignore: "No esconder medios NSFW "
+ force: "Ocultar todos los medios"
_mfm:
cheatSheet: "Hoja de referencia de MFM"
intro: "MFM es un lenguaje de marcado dedicado que se puede usar en varios lugares dentro de Misskey. Aquí puede ver una lista de sintaxis disponibles en MFM."
+ dummy: "Misskey expande el mundo de la Fediverso"
mention: "Menciones"
mentionDescription: "El signo @ seguido de un nombre de usuario se puede utilizar para notificar a un usuario en particular."
hashtag: "Hashtag"
url: "URL"
+ urlDescription: "Se pueden mostrar las URL"
link: "Vínculo"
bold: "Negrita"
center: "Centrar"
@@ -915,7 +989,6 @@ _sfx:
antenna: "Antena receptora"
channel: "Notificaciones del canal"
_ago:
- unknown: "Desconocido"
future: "Futuro"
justNow: "Recién ahora"
secondsAgo: "Hace {n} segundos"
@@ -1432,6 +1505,9 @@ _notification:
followRequestAccepted: "El seguimiento fue aceptado"
groupInvited: "Invitado al grupo"
app: "Notificaciones desde aplicaciones"
+ _actions:
+ reply: "Responder"
+ renote: "Renotar"
_deck:
alwaysShowMainColumn: "Siempre mostrar la columna principal"
columnAlign: "Alinear columnas"
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index 1fe74fa9ab..7e225c2992 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -804,7 +804,7 @@ manageAccounts: "Gérer les comptes"
makeReactionsPublic: "Rendre les réactions publiques"
makeReactionsPublicDescription: "Ceci rendra la liste de toutes vos réactions données publique."
classic: "Classique"
-muteThread: "Mettre ce thread en sourdine"
+muteThread: "Masquer cette discussion"
unmuteThread: "Ne plus masquer le fil"
ffVisibility: "Visibilité des abonnés/abonnements"
ffVisibilityDescription: "Permet de configurer qui peut voir les personnes que tu suis et les personnes qui te suivent."
@@ -1075,7 +1075,6 @@ _sfx:
antenna: "Réception de l’antenne"
channel: "Notifications de canal"
_ago:
- unknown: "Inconnu"
future: "Futur"
justNow: "à l’instant"
secondsAgo: "Il y a {n}s"
@@ -1615,6 +1614,9 @@ _notification:
followRequestAccepted: "Demande d'abonnement acceptée"
groupInvited: "Invitation à un groupe"
app: "Notifications provenant des apps"
+ _actions:
+ reply: "Répondre"
+ renote: "Renoter"
_deck:
alwaysShowMainColumn: "Toujours afficher la colonne principale"
columnAlign: "Aligner les colonnes"
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index 11dff184cd..39e2c1f661 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -593,6 +593,7 @@ smtpSecureInfo: "Matikan ini ketika menggunakan STARTTLS"
testEmail: "Tes pengiriman surel"
wordMute: "Bisukan kata"
regexpError: "Kesalahan ekspresi reguler"
+regexpErrorDescription: "Galat terjadi pada baris {line} ekspresi reguler dari {tab} kata yang dibisukan:"
instanceMute: "Bisuka instansi"
userSaysSomething: "{name} mengatakan sesuatu"
makeActive: "Aktifkan"
@@ -839,7 +840,11 @@ tenMinutes: "10 Menit"
oneHour: "1 Jam"
oneDay: "1 Hari"
oneWeek: "1 Bulan"
+reflectMayTakeTime: "Mungkin perlu beberapa saat untuk dicerminkan."
failedToFetchAccountInformation: "Gagal untuk mendapatkan informasi akun"
+rateLimitExceeded: "Batas sudah terlampaui"
+cropImage: "potong gambar"
+cropImageAsk: "Ingin memotong gambar?"
_emailUnavailable:
used: "Alamat surel ini telah digunakan"
format: "Format tidak valid."
@@ -1085,7 +1090,6 @@ _sfx:
antenna: "Penerimaan Antenna"
channel: "Pemberitahuan saluran"
_ago:
- unknown: "Tidak diketahui"
future: "Masa depan"
justNow: "Baru saja"
secondsAgo: "{n} detik lalu"
@@ -1129,6 +1133,7 @@ _2fa:
registerKey: "Daftarkan kunci keamanan baru"
step1: "Pertama, pasang aplikasi otentikasi (seperti {a} atau {b}) di perangkat kamu."
step2: "Lalu, pindai kode QR yang ada di layar."
+ step2Url: "Di aplikasi desktop, masukkan URL berikut:"
step3: "Masukkan token yang telah disediakan oleh aplikasimu untuk menyelesaikan pemasangan."
step4: "Mulai sekarang, upaya login apapun akan meminta token login dari aplikasi otentikasi kamu."
securityKeyInfo: "Kamu dapat memasang otentikasi WebAuthN untuk mengamankan proses login lebih lanjut dengan tidak hanya perangkat keras kunci keamanan yang mendukung FIDO2, namun juga sidik jari atau otentikasi PIN pada perangkatmu."
@@ -1613,6 +1618,7 @@ _notification:
yourFollowRequestAccepted: "Permintaan mengikuti kamu telah diterima"
youWereInvitedToGroup: "Telah diundang ke grup"
pollEnded: "Hasil Kuesioner telah keluar"
+ emptyPushNotificationMessage: "Pembaruan notifikasi dorong"
_types:
all: "Semua"
follow: "Ikuti"
@@ -1627,6 +1633,10 @@ _notification:
followRequestAccepted: "Permintaan mengikuti disetujui"
groupInvited: "Diundang ke grup"
app: "Pemberitahuan dari aplikasi"
+ _actions:
+ followBack: "Ikuti Kembali"
+ reply: "Balas"
+ renote: "Renote"
_deck:
alwaysShowMainColumn: "Selalu tampilkan kolom utama"
columnAlign: "Luruskan kolom"
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index 1eaa78b646..8584ed6a8e 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -10,7 +10,7 @@ password: "Password"
forgotPassword: "Hai dimenticato la tua password?"
fetchingAsApObject: "Recuperando dal Fediverso..."
ok: "OK"
-gotIt: "Capito!"
+gotIt: "Ho capito"
cancel: "Annulla"
enterUsername: "Inserisci un nome utente"
renotedBy: "Rinotato da {user}"
@@ -767,6 +767,7 @@ customCss: "CSS personalizzato"
global: "Federata"
squareAvatars: "Mostra l'immagine del profilo come quadrato"
sent: "Inviare"
+received: "Ricevuto"
searchResult: "Risultati della Ricerca"
hashtags: "Hashtag"
troubleshooting: "Risoluzione problemi"
@@ -804,6 +805,10 @@ welcomeBackWithName: "Bentornato/a, {name}"
clickToFinishEmailVerification: "Fai click su [{ok}] per completare la verifica dell'indirizzo email."
searchByGoogle: "Cerca"
indefinitely: "Non scade"
+tenMinutes: "10 minuti"
+oneHour: "1 ora"
+oneDay: "1 giorno"
+oneWeek: "1 settimana"
_emailUnavailable:
used: "Email già in uso"
format: "Formato email non valido"
@@ -999,7 +1004,6 @@ _sfx:
antenna: "Ricezione dell'antenna"
channel: "Notifiche di canale"
_ago:
- unknown: "Sconosciuto"
future: "Futuro"
justNow: "Ora"
secondsAgo: "{n}s fa"
@@ -1433,6 +1437,9 @@ _notification:
followRequestAccepted: "Richiesta di follow accettata"
groupInvited: "Invito a un gruppo"
app: "Notifiche da applicazioni"
+ _actions:
+ reply: "Rispondi"
+ renote: "Rinota"
_deck:
alwaysShowMainColumn: "Mostra sempre la colonna principale"
columnAlign: "Allineare colonne"
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 6326094dd8..43ab7f2d69 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -356,7 +356,7 @@ antennaExcludeKeywords: "除外キーワード"
antennaKeywordsDescription: "スペースã§åŒºåˆ‡ã‚‹ã¨AND指定ã«ãªã‚Šã€æ”¹è¡Œã§åŒºåˆ‡ã‚‹ã¨OR指定ã«ãªã‚Šã¾ã™"
notifyAntenna: "æ–°ã—ã„ノートを通知ã™ã‚‹"
withFileAntenna: "ãƒ•ã‚¡ã‚¤ãƒ«ãŒæ·»ä»˜ã•れãŸãƒŽãƒ¼ãƒˆã®ã¿"
-enableServiceworker: "ServiceWorkerを有効ã«ã™ã‚‹"
+enableServiceworker: "ブラウザã¸ã®ãƒ—ッシュ通知を有効ã«ã™ã‚‹"
antennaUsersDescription: "ユーザーåを改行ã§åŒºåˆ‡ã£ã¦æŒ‡å®šã—ã¾ã™"
caseSensitive: "å¤§æ–‡å­—å°æ–‡å­—を区別ã™ã‚‹"
withReplies: "返信をå«ã‚€"
@@ -425,7 +425,7 @@ quoteQuestion: "引用ã¨ã—ã¦æ·»ä»˜ã—ã¾ã™ã‹ï¼Ÿ"
noMessagesYet: "ã¾ã ãƒãƒ£ãƒƒãƒˆã¯ã‚りã¾ã›ã‚“"
newMessageExists: "æ–°ã—ã„メッセージãŒã‚りã¾ã™"
onlyOneFileCanBeAttached: "ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã«æ·»ä»˜ã§ãるファイルã¯ã²ã¨ã¤ã§ã™"
-signinRequired: "ログインã—ã¦ãã ã•ã„"
+signinRequired: "続行ã™ã‚‹å‰ã«ã€ã‚µã‚¤ãƒ³ã‚¢ãƒƒãƒ—ã¾ãŸã¯ã‚µã‚¤ãƒ³ã‚¤ãƒ³ãŒå¿…è¦ã§ã™"
invitations: "招待"
invitationCode: "招待コード"
checking: "確èªã—ã¦ã„ã¾ã™"
@@ -842,6 +842,9 @@ oneDay: "1æ—¥"
oneWeek: "1週間"
reflectMayTakeTime: "åæ˜ ã•れるã¾ã§æ™‚é–“ãŒã‹ã‹ã‚‹å ´åˆãŒã‚りã¾ã™ã€‚"
failedToFetchAccountInformation: "アカウント情報ã®å–å¾—ã«å¤±æ•—ã—ã¾ã—ãŸ"
+rateLimitExceeded: "レート制é™ã‚’è¶…ãˆã¾ã—ãŸ"
+cropImage: "ç”»åƒã®ã‚¯ãƒ­ãƒƒãƒ—"
+cropImageAsk: "ç”»åƒã‚’クロップã—ã¾ã™ã‹ï¼Ÿ"
_emailUnavailable:
used: "æ—¢ã«ä½¿ç”¨ã•れã¦ã„ã¾ã™"
@@ -1110,7 +1113,6 @@ _sfx:
channel: "ãƒãƒ£ãƒ³ãƒãƒ«é€šçŸ¥"
_ago:
- unknown: "謎"
future: "未æ¥"
justNow: "ãŸã£ãŸä»Š"
secondsAgo: "{n}ç§’å‰"
@@ -1157,6 +1159,7 @@ _2fa:
registerKey: "キーを登録"
step1: "ã¾ãšã€{a}ã‚„{b}ãªã©ã®èªè¨¼ã‚¢ãƒ—リをãŠä½¿ã„ã®ãƒ‡ãƒã‚¤ã‚¹ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ã¾ã™ã€‚"
step2: "次ã«ã€è¡¨ç¤ºã•れã¦ã„ã‚‹QRコードをアプリã§ã‚¹ã‚­ãƒ£ãƒ³ã—ã¾ã™ã€‚"
+ step2Url: "デスクトップアプリã§ã¯æ¬¡ã®URLを入力ã—ã¾ã™:"
step3: "アプリã«è¡¨ç¤ºã•れã¦ã„るトークンを入力ã—ã¦å®Œäº†ã§ã™ã€‚"
step4: "ã“れã‹ã‚‰ãƒ­ã‚°ã‚¤ãƒ³ã™ã‚‹ã¨ãã‚‚ã€åŒã˜ã‚ˆã†ã«ãƒˆãƒ¼ã‚¯ãƒ³ã‚’入力ã—ã¾ã™ã€‚"
securityKeyInfo: "FIDO2をサãƒãƒ¼ãƒˆã™ã‚‹ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ã‚­ãƒ¼ã‚‚ã—ãã¯ç«¯æœ«ã®æŒ‡ç´‹èªè¨¼ã‚„PINを使用ã—ã¦ãƒ­ã‚°ã‚¤ãƒ³ã™ã‚‹ã‚ˆã†ã«è¨­å®šã§ãã¾ã™ã€‚"
@@ -1668,8 +1671,9 @@ _notification:
youWereFollowed: "フォローã•れã¾ã—ãŸ"
youReceivedFollowRequest: "ãƒ•ã‚©ãƒ­ãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒæ¥ã¾ã—ãŸ"
yourFollowRequestAccepted: "ãƒ•ã‚©ãƒ­ãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒæ‰¿èªã•れã¾ã—ãŸ"
- youWereInvitedToGroup: "ã‚°ãƒ«ãƒ¼ãƒ—ã«æ‹›å¾…ã•れã¾ã—ãŸ"
+ youWereInvitedToGroup: "{userName}ãŒã‚ãªãŸã‚’ã‚°ãƒ«ãƒ¼ãƒ—ã«æ‹›å¾…ã—ã¾ã—ãŸ"
pollEnded: "アンケートã®çµæžœãŒå‡ºã¾ã—ãŸ"
+ emptyPushNotificationMessage: "ãƒ—ãƒƒã‚·ãƒ¥é€šçŸ¥ã®æ›´æ–°ã‚’ã—ã¾ã—ãŸ"
_types:
all: "ã™ã¹ã¦"
@@ -1686,6 +1690,11 @@ _notification:
groupInvited: "ã‚°ãƒ«ãƒ¼ãƒ—ã«æ‹›å¾…ã•れãŸ"
app: "連æºã‚¢ãƒ—リã‹ã‚‰ã®é€šçŸ¥"
+ _actions:
+ followBack: "フォローãƒãƒƒã‚¯"
+ reply: "返信"
+ renote: "Renote"
+
_deck:
alwaysShowMainColumn: "常ã«ãƒ¡ã‚¤ãƒ³ã‚«ãƒ©ãƒ ã‚’表示"
columnAlign: "カラムã®å¯„ã›"
diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml
index 52ecd8c24e..5458152dda 100644
--- a/locales/ja-KS.yml
+++ b/locales/ja-KS.yml
@@ -799,7 +799,6 @@ _sfx:
notification: "通知"
chat: "ãƒãƒ£ãƒƒãƒˆ"
_ago:
- unknown: "ã‚ã‹ã‚‰ã‚“"
future: "未æ¥"
justNow: "ãŸã£ãŸä»Š"
secondsAgo: "{n}ç§’å‰"
@@ -1202,6 +1201,9 @@ _notification:
reaction: "リアクション"
receiveFollowRequest: "フォロー許å¯ã—ã¦ã»ã—ã„ã¿ãŸã„ã‚„ã§"
followRequestAccepted: "フォローãŒå—ç†ã•れãŸã§"
+ _actions:
+ reply: "返事"
+ renote: "Renote"
_deck:
alwaysShowMainColumn: "ã„ã¤ã‚‚メインカラムを表示"
columnAlign: "カラムã®å¯„ã›"
diff --git a/locales/kab-KAB.yml b/locales/kab-KAB.yml
index 6a14cbe1ba..77ca824528 100644
--- a/locales/kab-KAB.yml
+++ b/locales/kab-KAB.yml
@@ -116,6 +116,8 @@ _notification:
_types:
follow: "Ig á¹­á¹­afaá¹›"
mention: "Bder"
+ _actions:
+ reply: "Err"
_deck:
_columns:
notifications: "Ilɣuyen"
diff --git a/locales/kn-IN.yml b/locales/kn-IN.yml
index 3111c90dd5..3682277175 100644
--- a/locales/kn-IN.yml
+++ b/locales/kn-IN.yml
@@ -76,6 +76,8 @@ _profile:
username: "ಬಳಕೆಹೆಸರà³"
_notification:
youWereFollowed: "ಹಿಂಬಾಲಿಸಿದರà³"
+ _actions:
+ reply: "ಉತà³à²¤à²°à²¿à²¸à³"
_deck:
_columns:
notifications: "ಅಧಿಸೂಚನೆಗಳà³"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index e1ad77cbc9..4e7369a5ef 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -592,6 +592,8 @@ smtpSecure: "SMTP ì—°ê²°ì— Implicit SSL/TTS 사용"
smtpSecureInfo: "STARTTLS 사용 시ì—는 해제합니다."
testEmail: "ì´ë©”ì¼ ì „ì†¡ 테스트"
wordMute: "단어 뮤트"
+regexpError: "ì •ê·œ í‘œí˜„ì‹ ì˜¤ë¥˜"
+regexpErrorDescription: "{tab}단어 뮤트 {line}í–‰ì˜ ì •ê·œ 표현ì‹ì— 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤:"
instanceMute: "ì¸ìŠ¤í„´ìŠ¤ 뮤트"
userSaysSomething: "{name}ë‹˜ì´ ë¬´ì–¸ê°€ë¥¼ ë§í–ˆìŠµë‹ˆë‹¤"
makeActive: "활성화"
@@ -825,8 +827,21 @@ overridedDeviceKind: "장치 유형"
smartphone: "스마트í°"
tablet: "태블릿"
auto: "ìžë™"
+themeColor: "테마 컬러"
+size: "í¬ê¸°"
+numberOfColumn: "한 ì¤„ì— ë³´ì¼ ë¦¬ì•¡ì…˜ì˜ ìˆ˜"
searchByGoogle: "검색"
+instanceDefaultLightTheme: "ì¸ìŠ¤í„´ìŠ¤ 기본 ë¼ì´íЏ 테마"
+instanceDefaultDarkTheme: "ì¸ìŠ¤í„´ìŠ¤ 기본 ë‹¤í¬ í…Œë§ˆ"
+instanceDefaultThemeDescription: "ê°ì²´ 형ì‹ì˜ 테마 코드를 입력해 주세요."
+mutePeriod: "뮤트할 기간"
indefinitely: "무기한"
+tenMinutes: "10ë¶„"
+oneHour: "1시간"
+oneDay: "1ì¼"
+oneWeek: "ì¼ì£¼ì¼"
+reflectMayTakeTime: "ë°˜ì˜ë˜ê¸°ê¹Œì§€ ì‹œê°„ì´ ê±¸ë¦´ 수 있습니다."
+failedToFetchAccountInformation: "계정 정보를 가져오지 못했습니다"
_emailUnavailable:
used: "ì´ ë©”ì¼ ì£¼ì†ŒëŠ” 사용중입니다"
format: "형ì‹ì´ 올바르지 않습니다"
@@ -1072,7 +1087,6 @@ _sfx:
antenna: "안테나 수신"
channel: "ì±„ë„ ì•Œë¦¼"
_ago:
- unknown: "알 수 ì—†ìŒ"
future: "미래"
justNow: "방금 전"
secondsAgo: "{n}ì´ˆ ì „"
@@ -1116,6 +1130,7 @@ _2fa:
registerKey: "키를 등ë¡"
step1: "먼저, {a}나 {b}ë“±ì˜ ì¸ì¦ ì•±ì„ ì‚¬ìš© ì¤‘ì¸ ë””ë°”ì´ìŠ¤ì— ì„¤ì¹˜í•©ë‹ˆë‹¤."
step2: "ê·¸ 후, 표시ë˜ì–´ 있는 QR코드를 앱으로 스캔합니다."
+ step2Url: "ë°ìФí¬í†± 앱ì—서는 ë‹¤ìŒ URLì„ ìž…ë ¥í•˜ì„¸ìš”:"
step3: "ì•±ì— í‘œì‹œëœ í† í°ì„ 입력하시면 완료ë©ë‹ˆë‹¤."
step4: "ë‹¤ìŒ ë¡œê·¸ì¸ë¶€í„°ëŠ” 토í°ì„ 입력해야 합니다."
securityKeyInfo: "FIDO2를 ì§€ì›í•˜ëŠ” 하드웨어 보안 키 í˜¹ì€ ë””ë°”ì´ìŠ¤ì˜ ì§€ë¬¸ì¸ì‹ì´ë‚˜ 화면잠금 PINì„ ì´ìš©í•´ì„œ 로그ì¸í•˜ë„ë¡ ì„¤ì •í•  수 있습니다."
@@ -1249,7 +1264,7 @@ _profile:
youCanIncludeHashtags: "해시 태그를 í¬í•¨í•  수 있습니다."
metadata: "추가 정보"
metadataEdit: "추가 정보 편집"
- metadataDescription: "í”„ë¡œí•„ì— ìµœëŒ€ 4ê°œì˜ ì¶”ê°€ 정보를 표시할 수 있어요"
+ metadataDescription: "í”„ë¡œí•„ì— ì¶”ê°€ 정보를 표시할 수 있어요"
metadataLabel: "ë¼ë²¨"
metadataContent: "ë‚´ìš©"
changeAvatar: "아바타 ì´ë¯¸ì§€ 변경"
@@ -1599,6 +1614,8 @@ _notification:
youReceivedFollowRequest: "새로운 팔로우 ìš”ì²­ì´ ìžˆìŠµë‹ˆë‹¤"
yourFollowRequestAccepted: "팔로우 ìš”ì²­ì´ ìˆ˜ë½ë˜ì—ˆìŠµë‹ˆë‹¤"
youWereInvitedToGroup: "ê·¸ë£¹ì— ì´ˆëŒ€ë˜ì—ˆìŠµë‹ˆë‹¤"
+ pollEnded: "투표 결과가 발표ë˜ì—ˆìŠµë‹ˆë‹¤"
+ emptyPushNotificationMessage: "푸시 ì•Œë¦¼ì´ ê°±ì‹ ë˜ì—ˆìŠµë‹ˆë‹¤"
_types:
all: "ì „ë¶€"
follow: "팔로잉"
@@ -1608,10 +1625,15 @@ _notification:
quote: "ì¸ìš©"
reaction: "리액션"
pollVote: "투표 참여"
+ pollEnded: "투표가 종료ë¨"
receiveFollowRequest: "팔로우 ìš”ì²­ì„ ë°›ì•˜ì„ ë•Œ"
followRequestAccepted: "팔로우 ìš”ì²­ì´ ìŠ¹ì¸ë˜ì—ˆì„ 때"
groupInvited: "ê·¸ë£¹ì— ì´ˆëŒ€ë˜ì—ˆì„ 때"
app: "ì—°ë™ëœ ì•±ì„ í†µí•œ 알림"
+ _actions:
+ followBack: "팔로우"
+ reply: "답글"
+ renote: "Renote"
_deck:
alwaysShowMainColumn: "ë©”ì¸ ì¹¼ëŸ¼ í•­ìƒ í‘œì‹œ"
columnAlign: "칼럼 정렬"
diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml
index f4e4a62182..0ded573948 100644
--- a/locales/nl-NL.yml
+++ b/locales/nl-NL.yml
@@ -303,6 +303,8 @@ muteThread: "Discussies dempen "
unmuteThread: "Dempen van discussie ongedaan maken"
hide: "Verbergen"
searchByGoogle: "Zoeken"
+cropImage: "Afbeelding bijsnijden"
+cropImageAsk: "Bijsnijdengevraagd"
_email:
_follow:
title: "volgde jou"
@@ -371,6 +373,9 @@ _notification:
renote: "Herdelen"
quote: "Quote"
reaction: "Reacties"
+ _actions:
+ reply: "Antwoord"
+ renote: "Herdelen"
_deck:
_columns:
notifications: "Meldingen"
diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml
index 78d86dd7e3..fa1dad2173 100644
--- a/locales/pl-PL.yml
+++ b/locales/pl-PL.yml
@@ -946,7 +946,6 @@ _sfx:
chatBg: "Rozmowy (tło)"
channel: "Powiadomienia kanału"
_ago:
- unknown: "Nieznane"
future: "W przyszłości"
justNow: "Przed chwilÄ…"
secondsAgo: "{n} sek. temu"
@@ -1401,6 +1400,9 @@ _notification:
followRequestAccepted: "Przyjęto prośbę o możliwość obserwacji"
groupInvited: "Zaproszono do grup"
app: "Powiadomienia z aplikacji"
+ _actions:
+ reply: "Odpowiedz"
+ renote: "Udostępnij"
_deck:
alwaysShowMainColumn: "Zawsze pokazuj główną kolumnę"
columnAlign: "Wyrównaj kolumny"
diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml
index 104e4ceb7c..0dc15a27bb 100644
--- a/locales/pt-PT.yml
+++ b/locales/pt-PT.yml
@@ -37,26 +37,117 @@ favorites: "Favoritar"
unfavorite: "Remover dos favoritos"
favorited: "Adicionado aos favoritos."
alreadyFavorited: "Já adicionado aos favoritos."
+cantFavorite: "Não foi possível adicionar aos favoritos."
+pin: "Afixar no perfil"
+unpin: "Desafixar do perfil"
+copyContent: "Copiar conteúdos"
+copyLink: "Copiar hiperligação"
+delete: "Eliminar"
+deleteAndEdit: "Eliminar e editar"
+deleteAndEditConfirm: "Tens a certeza que pretendes eliminar esta nota e editá-la? Irás perder todas as suas reações, renotas e respostas."
+addToList: "Adicionar a lista"
+sendMessage: "Enviar uma mensagem"
+copyUsername: "Copiar nome de utilizador"
+searchUser: "Pesquisar utilizador"
+reply: "Responder"
+loadMore: "Carregar mais"
showMore: "Ver mais"
youGotNewFollower: "Você tem um novo seguidor"
+receiveFollowRequest: "Pedido de seguimento recebido"
followRequestAccepted: "Pedido de seguir aceito"
+mention: "Menção"
+mentions: "Menções"
+directNotes: "Notas diretas"
+importAndExport: "Importar/Exportar"
+import: "Importar"
+export: "Exportar"
+files: "Ficheiros"
+download: "Descarregar"
+driveFileDeleteConfirm: "Tens a certeza que pretendes apagar o ficheiro \"{name}\"? As notas que tenham este ficheiro anexado serão também apagadas."
+unfollowConfirm: "Tens a certeza que queres deixar de seguir {name}?"
+exportRequested: "Pediste uma exportação. Este processo pode demorar algum tempo. Será adicionado à tua Drive após a conclusão do processo."
+importRequested: "Pediste uma importação. Este processo pode demorar algum tempo."
+lists: "Listas"
+noLists: "Não tens nenhuma lista"
note: "Post"
notes: "Posts"
+following: "Seguindo"
+followers: "Seguidores"
+followsYou: "Segue-te"
+createList: "Criar lista"
+manageLists: "Gerir listas"
+error: "Erro"
+somethingHappened: "Ocorreu um erro"
+retry: "Tentar novamente"
+pageLoadError: "Ocorreu um erro ao carregar a página."
+pageLoadErrorDescription: "Isto é normalmente causado por erros de rede ou pela cache do browser. Experimenta limpar a cache e tenta novamente após algum tempo."
+serverIsDead: "O servidor não está respondendo. Por favor espere um pouco e tente novamente."
+youShouldUpgradeClient: "Para visualizar essa página, por favor recarregue-a para atualizar seu cliente."
+enterListName: "Insira um nome para a lista"
+privacy: "Privacidade"
+makeFollowManuallyApprove: "Pedidos de seguimento precisam ser aprovados"
+defaultNoteVisibility: "Visibilidade padrão"
+follow: "Seguindo"
+followRequest: "Mandar pedido de seguimento"
+followRequests: "Pedidos de seguimento"
+unfollow: "Deixar de seguir"
+followRequestPending: "Pedido de seguimento pendente"
enterEmoji: "Inserir emoji"
renote: "Repostar"
renoted: "Repostado"
cantRenote: "Não pode repostar"
cantReRenote: "Não pode repostar este repost"
+quote: "Citar"
pinnedNote: "Post fixado"
+pinned: "Afixar no perfil"
+you: "Você"
+clickToShow: "Clique para ver"
sensitive: "Conteúdo sensível"
+add: "Adicionar"
+reaction: "Reações"
+reactionSetting: "Quais reações a mostrar no selecionador de reações"
+rememberNoteVisibility: "Lembrar das configurações de visibilidade de notas"
+attachCancel: "Remover anexo"
+markAsSensitive: "Marcar como sensível"
+unmarkAsSensitive: "Desmarcar como sensível"
+enterFileName: "Digite o nome do ficheiro"
mute: "Silenciar"
unmute: "Dessilenciar"
+block: "Bloquear"
+unblock: "Desbloquear"
+suspend: "Suspender"
+unsuspend: "Cancelar suspensão"
+blockConfirm: "Tem certeza que gostaria de bloquear essa conta?"
+unblockConfirm: "Tem certeza que gostaria de desbloquear essa conta?"
+suspendConfirm: "Tem certeza que gostaria de suspender essa conta?"
+unsuspendConfirm: "Tem certeza que gostaria de cancelar a suspensão dessa conta?"
+selectList: "Escolhe uma lista"
+selectAntenna: "Escolhe uma antena"
+selectWidget: "Escolhe um widget"
+editWidgets: "Editar widgets"
+editWidgetsExit: "Pronto"
+customEmojis: "Emoji personalizado"
+emoji: "Emoji"
+emojis: "Emojis"
+emojiName: "Nome do Emoji"
+emojiUrl: "URL do Emoji"
+addEmoji: "Adicionar um Emoji"
settingGuide: "Guia de configuração"
+flagAsBot: "Marcar conta como robô"
+flagAsCat: "Marcar conta como gato"
+flagAsCatDescription: "Ative essa opção para marcar essa conta como gato."
+flagShowTimelineReplies: "Mostrar respostas na linha de tempo"
+general: "Geral"
+wallpaper: "Papel de parede"
+searchWith: "Buscar: {q}"
+youHaveNoLists: "Não tem nenhuma lista"
+followConfirm: "Tem certeza que quer deixar de seguir {name}?"
instances: "Instância"
registeredAt: "Registrado em"
perHour: "por hora"
perDay: "por dia"
noUsers: "Sem usuários"
+remove: "Eliminar"
messageRead: "Lida"
lightThemes: "Tema claro"
darkThemes: "Tema escuro"
@@ -64,6 +155,9 @@ addFile: "Adicionar arquivo"
nsfw: "Conteúdo sensível"
monthX: "mês de {month}"
pinnedNotes: "Post fixado"
+userList: "Listas"
+none: "Nenhum"
+output: "Resultado"
smtpUser: "Nome de usuário"
smtpPass: "Senha"
user: "Usuários"
@@ -72,9 +166,13 @@ _email:
_follow:
title: "Você tem um novo seguidor"
_mfm:
+ mention: "Menção"
+ quote: "Citar"
+ emoji: "Emoji personalizado"
search: "Pesquisar"
_theme:
keys:
+ mention: "Menção"
renote: "Repostar"
_sfx:
note: "Posts"
@@ -82,15 +180,238 @@ _sfx:
_widgets:
notifications: "Notificações"
timeline: "Timeline"
+_cw:
+ show: "Carregar mais"
+_visibility:
+ followers: "Seguidores"
_profile:
username: "Nome de usuário"
_exportOrImport:
+ followingList: "Seguindo"
muteList: "Silenciar"
+ blockingList: "Bloquear"
+ userLists: "Listas"
+_pages:
+ blocks:
+ _button:
+ _action:
+ _pushEvent:
+ event: "Nome do evento"
+ message: "Mostrar mensagem quando ativado"
+ variable: "Variável a mandar"
+ no-variable: "Nenhum"
+ callAiScript: "Invocar AiScript"
+ _callAiScript:
+ functionName: "Nome da função"
+ radioButton: "Escolha"
+ _radioButton:
+ values: "Lista de escolhas separadas por quebras de texto"
+ script:
+ categories:
+ logical: "Operação lógica"
+ operation: "Cálculos"
+ comparison: "Comparação"
+ list: "Listas"
+ blocks:
+ _strReplace:
+ arg2: "Texto que irá ser substituído"
+ arg3: "Substituir com"
+ strReverse: "Virar texto"
+ join: "Sequência de texto"
+ _join:
+ arg1: "Listas"
+ arg2: "Separador"
+ add: "Somar"
+ _add:
+ arg1: "A"
+ arg2: "B"
+ subtract: "Subtrair"
+ _subtract:
+ arg1: "A"
+ arg2: "B"
+ multiply: "Multiplicar"
+ _multiply:
+ arg1: "A"
+ arg2: "B"
+ divide: "Dividir"
+ _divide:
+ arg1: "A"
+ arg2: "B"
+ mod: "O resto de"
+ _mod:
+ arg1: "A"
+ arg2: "B"
+ round: "Arredondar decimal"
+ _round:
+ arg1: "Numérico"
+ eq: "A e B são iguais"
+ _eq:
+ arg1: "A"
+ arg2: "B"
+ notEq: "A e B são diferentes"
+ _notEq:
+ arg1: "A"
+ arg2: "B"
+ and: "A e B"
+ _and:
+ arg1: "A"
+ arg2: "B"
+ or: "A OU B"
+ _or:
+ arg1: "A"
+ arg2: "B"
+ lt: "< A é menor do que B"
+ _lt:
+ arg1: "A"
+ arg2: "B"
+ gt: "> A é maior do que B"
+ _gt:
+ arg1: "A"
+ arg2: "B"
+ ltEq: "<= A é maior ou igual a B"
+ _ltEq:
+ arg1: "A"
+ arg2: "B"
+ gtEq: ">= A é maior ou igual a B"
+ _gtEq:
+ arg1: "A"
+ arg2: "B"
+ if: "Galho"
+ _if:
+ arg1: "Se"
+ arg2: "Então"
+ arg3: "Se não"
+ not: "NÃO"
+ _not:
+ arg1: "NÃO"
+ random: "Aleatório"
+ _random:
+ arg1: "Probabilidade"
+ rannum: "Numeral aleatório"
+ _rannum:
+ arg1: "Valor mínimo"
+ arg2: "Valor máximo"
+ randomPick: "Escolher aleatoriamente de uma lista"
+ _randomPick:
+ arg1: "Listas"
+ dailyRandom: "Aleatório (Muda uma vez por dia para cada usuário)"
+ _dailyRandom:
+ arg1: "Probabilidade"
+ dailyRannum: "Numeral aleatório (Muda uma vez por dia para cada usuário)"
+ _dailyRannum:
+ arg1: "Valor mínimo"
+ arg2: "Valor máximo"
+ dailyRandomPick: "Escolher aleatoriamente de uma lista (Muda uma vez por dia para cada usuário)"
+ _dailyRandomPick:
+ arg1: "Listas"
+ seedRandom: "Aleatório (com semente)"
+ _seedRandom:
+ arg1: "Semente"
+ arg2: "Probabilidade"
+ seedRannum: "Número aleatório (com semente)"
+ _seedRannum:
+ arg1: "Semente"
+ arg2: "Valor mínimo"
+ arg3: "Valor máximo"
+ seedRandomPick: "Escolher aleatoriamente de uma lista (com uma semente)"
+ _seedRandomPick:
+ arg1: "Semente"
+ arg2: "Listas"
+ DRPWPM: "Escolher aleatoriamente de uma lista ponderada (Muda uma vez por dia para cada usuário)"
+ _DRPWPM:
+ arg1: "Lista de texto"
+ pick: "Escolhe a partir da lista"
+ _pick:
+ arg1: "Listas"
+ arg2: "Posição"
+ listLen: "Pegar comprimento da lista"
+ _listLen:
+ arg1: "Listas"
+ number: "Numérico"
+ stringToNumber: "Texto para numérico"
+ _stringToNumber:
+ arg1: "Texto"
+ numberToString: "Numérico para texto"
+ _numberToString:
+ arg1: "Numérico"
+ splitStrByLine: "Dividir texto por quebras"
+ _splitStrByLine:
+ arg1: "Texto"
+ ref: "Variável"
+ aiScriptVar: "Variável AiScript"
+ fn: "Função"
+ _fn:
+ slots: "Espaços"
+ slots-info: "Separar cada espaço com uma quebra de texto"
+ arg1: "Resultado"
+ for: "Repetição 'for'"
+ _for:
+ arg1: "Número de repetições"
+ arg2: "Ação"
+ typeError: "Espaço {slot} aceita valores de tipo \"{expect}\", mas o valor dado é do tipo \"{actual}\"!"
+ thereIsEmptySlot: "O espaço {slot} está vazio!"
+ types:
+ string: "Texto"
+ number: "Numérico"
+ array: "Listas"
+ stringArray: "Lista de texto"
+ emptySlot: "Espaço vazio"
+ enviromentVariables: "Variáveis de ambiente"
+ pageVariables: "Variáveis de página"
+_relayStatus:
+ requesting: "Pendente"
+ accepted: "Aprovado"
+ rejected: "Recusado"
_notification:
+ fileUploaded: "Carregamento de arquivo efetuado com sucesso"
+ youGotMention: "{name} te mencionou"
+ youGotReply: "{name} te respondeu"
+ youGotQuote: "{name} te citou"
+ youGotPoll: "{name} votou em sua enquete"
+ youGotMessagingMessageFromUser: "{name} te mandou uma mensagem de bate-papo"
+ youGotMessagingMessageFromGroup: "Uma mensagem foi mandada para o grupo {name}"
youWereFollowed: "Você tem um novo seguidor"
+ youReceivedFollowRequest: "Você recebeu um pedido de seguimento"
+ yourFollowRequestAccepted: "Seu pedido de seguimento foi aceito"
+ youWereInvitedToGroup: "{userName} te convidou para um grupo"
+ pollEnded: "Os resultados da enquete agora estão disponíveis"
+ emptyPushNotificationMessage: "As notificações de alerta foram atualizadas"
_types:
+ all: "Todos"
+ follow: "Seguindo"
+ mention: "Menção"
+ reply: "Respostas"
+ renote: "Repostar"
+ quote: "Citar"
+ reaction: "Reações"
+ pollVote: "Votações em enquetes"
+ pollEnded: "Enquetes terminando"
+ receiveFollowRequest: "Recebeu pedidos de seguimento"
+ followRequestAccepted: "Aceitou pedidos de seguimento"
+ groupInvited: "Convites de grupo"
+ app: "Notificações de aplicativos conectados"
+ _actions:
+ followBack: "te seguiu de volta"
+ reply: "Responder"
renote: "Repostar"
_deck:
+ alwaysShowMainColumn: "Sempre mostrar a coluna principal"
+ columnAlign: "Alinhar colunas"
+ columnMargin: "Margem entre colunas"
+ columnHeaderHeight: "Altura do cabeçalho de coluna"
+ addColumn: "Adicionar coluna"
+ swapLeft: "Trocar de posição com a coluna à esquerda"
+ swapRight: "Trocar de posição com a coluna à direita"
+ swapUp: "Trocar de posição com a coluna acima"
+ swapDown: "Trocar de posição com a coluna abaixo"
+ popRight: "Acoplar coluna à direita"
+ profile: "Perfil"
_columns:
+ main: "Principal"
+ widgets: "Widgets"
notifications: "Notificações"
tl: "Timeline"
+ antenna: "Antenas"
+ list: "Listas"
+ mentions: "Menções"
+ direct: "Notas diretas"
diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml
index 6b2ff19e8e..cc74756119 100644
--- a/locales/ro-RO.yml
+++ b/locales/ro-RO.yml
@@ -562,13 +562,87 @@ plugins: "Pluginuri"
deck: "Deck"
undeck: "Părăsește Deck"
useBlurEffectForModal: "Folosește efect de blur pentru modale"
+width: "Lăţime"
+height: "Înălţime"
+large: "Mare"
+medium: "Mediu"
+small: "Mic"
+generateAccessToken: "Generează token de acces"
+permission: "Permisiuni"
+enableAll: "Actevează tot"
+disableAll: "Dezactivează tot"
+tokenRequested: "Acordă acces la cont"
+pluginTokenRequestedDescription: "Acest plugin va putea să folosească permisiunile setate aici."
+notificationType: "Tipul notificării"
+edit: "Editează"
+useStarForReactionFallback: "Folosește ★ ca fallback dacă emoji-ul este necunoscut"
+emailServer: "Server email"
+enableEmail: "Activează distribuția de emailuri"
+emailConfigInfo: "Folosit pentru a confirma emailul tău în timpul logări dacă îți uiți parola"
+email: "Email"
+emailAddress: "Adresă de email"
+smtpConfig: "Configurare Server SMTP"
smtpHost: "Gazdă"
+smtpPort: "Port"
smtpUser: "Nume de utilizator"
smtpPass: "Parolă"
+emptyToDisableSmtpAuth: "Lasă username-ul și parola necompletate pentru a dezactiva verificarea SMTP"
+smtpSecure: "Folosește SSL/TLS implicit pentru conecțiunile SMTP"
+smtpSecureInfo: "Oprește opțiunea asta dacă STARTTLS este folosit"
+testEmail: "Testează livrarea emailurilor"
+wordMute: "Cuvinte pe mut"
+regexpError: "Eroare de Expresie Regulată"
+regexpErrorDescription: "A apărut o eroare în expresia regulată pe linia {line} al cuvintelor {tab} setate pe mut:"
+instanceMute: "Instanțe pe mut"
+userSaysSomething: "{name} a spus ceva"
+makeActive: "Activează"
+display: "Arată"
+copy: "Copiază"
+metrics: "Metrici"
+overview: "Privire de ansamblu"
+logs: "Log-uri"
+delayed: "Întârziate"
+database: "Baza de date"
+channel: "Canale"
+create: "Crează"
+notificationSetting: "Setări notificări"
+notificationSettingDesc: "Selectează tipurile de notificări care să fie arătate"
+useGlobalSetting: "Folosește setările globale"
+useGlobalSettingDesc: "Dacă opțiunea e pornită, notificările contului tău vor fi folosite. Dacă e oprită, configurația va fi individuală."
+other: "Altele"
+regenerateLoginToken: "Regenerează token de login"
+regenerateLoginTokenDescription: "Regenerează token-ul folosit intern în timpul logări. În mod normal asta nu este necesar. Odată regenerat, toate dispozitivele vor fi delogate."
+setMultipleBySeparatingWithSpace: "Separă mai multe intrări cu spații."
+fileIdOrUrl: "Introdu ID sau URL"
+behavior: "Comportament"
+sample: "exemplu"
+abuseReports: "Rapoarte"
+reportAbuse: "Raportează"
+reportAbuseOf: "Raportează {name}"
+fillAbuseReportDescription: "Te rog scrie detaliile legate de acest raport. Dacă este despre o notă specifică, te rog introdu URL-ul ei."
+abuseReported: "Raportul tău a fost trimis. Mulțumim."
+reporter: "Raportorul"
+reporteeOrigin: "Originea raportatului"
+reporterOrigin: "Originea raportorului"
+forwardReport: "Redirecționează raportul către instanța externă"
+forwardReportIsAnonymous: "În locul contului tău, va fi afișat un cont anonim, de sistem, ca raportor către instanța externă."
+send: "Trimite"
+abuseMarkAsResolved: "Marchează raportul ca rezolvat"
+openInNewTab: "Deschide în tab nou"
+openInSideView: "Deschide în vedere laterală"
+defaultNavigationBehaviour: "Comportament de navigare implicit"
+editTheseSettingsMayBreakAccount: "Editarea acestor setări îți pot defecta contul."
+waitingFor: "Așteptând pentru {x}"
+random: "Aleator"
+system: "Sistem"
+switchUi: "Schimbă UI"
+desktop: "Desktop"
clearCache: "Golește cache-ul"
info: "Despre"
user: "Utilizatori"
administration: "Gestionare"
+middle: "Mediu"
+sent: "Trimite"
searchByGoogle: "Caută"
_email:
_follow:
@@ -641,6 +715,9 @@ _notification:
renote: "Re-notează"
quote: "Citează"
reaction: "Reacție"
+ _actions:
+ reply: "Răspunde"
+ renote: "Re-notează"
_deck:
_columns:
notifications: "Notificări"
diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml
index 877e1e185d..c44589a7e5 100644
--- a/locales/ru-RU.yml
+++ b/locales/ru-RU.yml
@@ -141,6 +141,8 @@ flagAsBot: "Ðккаунт бота"
flagAsBotDescription: "Включите, еÑли Ñтот аккаунт управлÑетÑÑ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¼Ð¾Ð¹. Это позволит ÑиÑтеме Misskey учитывать Ñто, а также поможет разработчикам других ботов предотвратить беÑконечные циклы взаимодейÑтвиÑ."
flagAsCat: "Ðккаунт кота"
flagAsCatDescription: "Включите, и Ñтот аккаунт будет помечен как кошачий."
+flagShowTimelineReplies: "Показывать ответы на заметки в ленте"
+flagShowTimelineRepliesDescription: "ЕÑли Ñтот параметр включен, то в ленте, в дополнение к заметкам пользователÑ, отображаютÑÑ Ð¾Ñ‚Ð²ÐµÑ‚Ñ‹ на другие заметки пользователÑ."
autoAcceptFollowed: "Принимать подпиÑчиков автоматичеÑки"
addAccount: "Добавить учётную запиÑÑŒ"
loginFailed: "ÐÐµÑƒÐ´Ð°Ñ‡Ð½Ð°Ñ Ð¿Ð¾Ð¿Ñ‹Ñ‚ÐºÐ° входа"
@@ -236,6 +238,7 @@ saved: "Сохранено"
messaging: "СообщениÑ"
upload: "Загрузить"
keepOriginalUploading: "Сохранить иÑходное изображение"
+keepOriginalUploadingDescription: "СохранÑет иÑходную верÑию при загрузке изображений. ЕÑли выключить, то при загрузке браузер генерирует изображение Ð´Ð»Ñ Ð¿ÑƒÐ±Ð»Ð¸ÐºÐ°Ñ†Ð¸Ð¸."
fromDrive: "С «диÑка»"
fromUrl: "По ÑÑылке"
uploadFromUrl: "Загрузить по ÑÑылке"
@@ -589,6 +592,7 @@ smtpSecure: "ИÑпользовать SSL/TLS Ð´Ð»Ñ SMTP-Ñоединений"
smtpSecureInfo: "Выключите при иÑпользовании STARTTLS."
testEmail: "Проверка доÑтавки Ñлектронной почты"
wordMute: "Скрытие Ñлов"
+regexpError: "Ошибка в регулÑрном выражении"
instanceMute: "Глушение инÑтанÑов"
userSaysSomething: "{name} что-то Ñообщает"
makeActive: "Ðктивировать"
@@ -619,6 +623,8 @@ fillAbuseReportDescription: "Опишите, пожалуйÑта, причинÑ
abuseReported: "Жалоба отправлена. Большое ÑпаÑибо за информацию."
reporteeOrigin: "О ком Ñообщено"
reporterOrigin: "Кто Ñообщил"
+forwardReport: "Перенаправление отчета на инÑтант."
+forwardReportIsAnonymous: "Удаленный инÑтант не Ñможет увидеть вашу информацию и будет отображатьÑÑ ÐºÐ°Ðº Ð°Ð½Ð¾Ð½Ð¸Ð¼Ð½Ð°Ñ ÑиÑÑ‚ÐµÐ¼Ð½Ð°Ñ ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ."
send: "Отправить"
abuseMarkAsResolved: "Отметить жалобу как решённую"
openInNewTab: "Открыть в новой вкладке"
@@ -815,7 +821,16 @@ leaveGroupConfirm: "Покинуть группу «{name}»?"
useDrawerReactionPickerForMobile: "Ð’Ñ‹Ð´Ð²Ð¸Ð¶Ð½Ð°Ñ Ð¿Ð°Ð»Ð¸Ñ‚Ñ€Ð° на мобильном уÑтройÑтве"
welcomeBackWithName: "С возвращением, {name}!"
clickToFinishEmailVerification: "ПожалуйÑта, нажмите [{ok}], чтобы завершить подтверждение адреÑа Ñлектронной почты."
+overridedDeviceKind: "Тип уÑтройÑтва"
+smartphone: "Смартфон"
+tablet: "Планшет"
+auto: "ÐвтоматичеÑки"
+themeColor: "Цвет темы"
+size: "Размер"
+numberOfColumn: "КоличеÑтво Ñтолбцов"
searchByGoogle: "ПоиÑк"
+instanceDefaultLightTheme: "Ð¡Ð²ÐµÑ‚Ð»Ð°Ñ Ñ‚ÐµÐ¼Ð° по умолчанию"
+instanceDefaultDarkTheme: "Ð¢ÐµÐ¼Ð½Ð°Ñ Ñ‚ÐµÐ¼Ð° по умолчанию"
indefinitely: "вечно"
_emailUnavailable:
used: "Уже иÑпользуетÑÑ"
@@ -1059,7 +1074,6 @@ _sfx:
antenna: "Ðнтенна"
channel: "Канал"
_ago:
- unknown: "Когда-то"
future: "Из будущего"
justNow: "Только что"
secondsAgo: "{n} Ñ Ð½Ð°Ð·Ð°Ð´"
@@ -1599,6 +1613,9 @@ _notification:
followRequestAccepted: "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° подпиÑку одобрен"
groupInvited: "Приглашение в группы"
app: "Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¸Ð· приложений"
+ _actions:
+ reply: "Ответить"
+ renote: "РепоÑÑ‚"
_deck:
alwaysShowMainColumn: "Ð’Ñегда показывать главную колонку"
columnAlign: "Выравнивание колонок"
diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml
index c6f2f59bdf..dc1151522e 100644
--- a/locales/sk-SK.yml
+++ b/locales/sk-SK.yml
@@ -841,6 +841,7 @@ oneDay: "1 deň"
oneWeek: "1 týždeň"
reflectMayTakeTime: "Zmeny môžu chvíľu trvať kým sa prejavia."
failedToFetchAccountInformation: "Nepodarilo sa naÄítaÅ¥ informácie o úÄte."
+rateLimitExceeded: "PrekroÄený limit rýchlosti"
_emailUnavailable:
used: "Táto emailová adresa sa už používa"
format: "Formát emailovej adresy je nesprávny"
@@ -1086,7 +1087,6 @@ _sfx:
antenna: "Antény"
channel: "Upozornenia kanála"
_ago:
- unknown: "Neznáme"
future: "Budúcnosť"
justNow: "Teraz"
secondsAgo: "pred {n} sekundami"
@@ -1130,6 +1130,7 @@ _2fa:
registerKey: "RegistrovaÅ¥ bezpeÄnostný kľúÄ"
step1: "Najprv si nainÅ¡talujte autentifikaÄnú aplikáciu (napríklad {a} alebo {b}) na svoje zariadenie."
step2: "Potom, naskenujte QR kód zobrazený na obrazovke."
+ step2Url: "Do aplikácie zadajte nasledujúcu URL adresu:"
step3: "Nastavenie dokonÄíte zadaním tokenu z vaÅ¡ej aplikácie."
step4: "Od teraz, vÅ¡etky ÄalÅ¡ie prihlásenia budú vyžadovaÅ¥ prihlasovací token."
securityKeyInfo: "Okrem odtlaÄku prsta alebo PIN autentifikácie si môžete nastaviÅ¥ autentifikáciu cez hardvérový bezpeÄnostný kÄ¾ÃºÄ podporujúci FIDO2 a tak eÅ¡te viac zabezpeÄiÅ¥ svoj úÄet."
@@ -1628,6 +1629,10 @@ _notification:
followRequestAccepted: "Schválené žiadosti o sledovanie"
groupInvited: "Pozvánky do skupín"
app: "Oznámenia z prepojených aplikácií"
+ _actions:
+ followBack: "Sledovať späť\n"
+ reply: "Odpovedať"
+ renote: "Preposlať"
_deck:
alwaysShowMainColumn: "Vždy zobraziť v hlavnom stĺpci"
columnAlign: "Zarovnať stĺpce"
diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml
new file mode 100644
index 0000000000..42bfa45f25
--- /dev/null
+++ b/locales/sv-SE.yml
@@ -0,0 +1,319 @@
+---
+_lang_: "Svenska"
+headlineMisskey: "Ett nätverk kopplat av noter"
+introMisskey: "Välkommen! Misskey är en öppen och decentraliserad mikrobloggningstjänst.\nSkapa en \"not\" och dela dina tankar med alla runtomkring dig. 📡\nMed \"reaktioner\" kan du snabbt uttrycka dina känslor kring andras noter.ðŸ‘\nLÃ¥t oss utforska en nya värld!🚀"
+monthAndDay: "{day}/{month}"
+search: "Sök"
+notifications: "Notifikationer"
+username: "Användarnamn"
+password: "Lösenord"
+forgotPassword: "Glömt lösenord"
+fetchingAsApObject: "Hämtar från Fediversum..."
+ok: "OK"
+gotIt: "Uppfattat!"
+cancel: "Avbryt"
+enterUsername: "Ange användarnamn"
+renotedBy: "Omnoterad av {user}"
+noNotes: "Inga noteringar"
+noNotifications: "Inga aviseringar"
+instance: "Instanser"
+settings: "Inställningar"
+basicSettings: "Basinställningar"
+otherSettings: "Andra inställningar"
+openInWindow: "Öppna i ett fönster"
+profile: "Profil"
+timeline: "Tidslinje"
+noAccountDescription: "Användaren har inte skrivit en biografi än."
+login: "Logga in"
+loggingIn: "Loggar in"
+logout: "Logga ut"
+signup: "Registrera"
+uploading: "Uppladdning sker..."
+save: "Spara"
+users: "Användare"
+addUser: "Lägg till användare"
+favorite: "Lägg till i favoriter"
+favorites: "Favoriter"
+unfavorite: "Avfavorisera"
+favorited: "Tillagd i favoriter."
+alreadyFavorited: "Redan tillagd i favoriter."
+cantFavorite: "Gick inte att lägga till i favoriter."
+pin: "Fäst till profil"
+unpin: "Lossa från profil"
+copyContent: "Kopiera innehåll"
+copyLink: "Kopiera länk"
+delete: "Radera"
+deleteAndEdit: "Radera och ändra"
+deleteAndEditConfirm: "Är du säker att du vill radera denna not och ändra den? Du kommer förlora alla reaktioner, omnoteringar och svar till den."
+addToList: "Lägg till i lista"
+sendMessage: "Skicka ett meddelande"
+copyUsername: "Kopiera användarnamn"
+searchUser: "Sök användare"
+reply: "Svara"
+loadMore: "Ladda mer"
+showMore: "Visa mer"
+youGotNewFollower: "följde dig"
+receiveFollowRequest: "Följarförfrågan mottagen"
+followRequestAccepted: "Följarförfrågan accepterad"
+mention: "Nämn"
+mentions: "Omnämningar"
+directNotes: "Direktnoter"
+importAndExport: "Importera / Exportera"
+import: "Importera"
+export: "Exportera"
+files: "Filer"
+download: "Nedladdning"
+driveFileDeleteConfirm: "Är du säker att du vill radera filen \"{name}\"? Noter med denna fil bifogad kommer också raderas."
+unfollowConfirm: "Är du säker att du vill avfölja {name}?"
+exportRequested: "Du har begärt en export. Detta kan ta lite tid. Den kommer läggas till i din Drive när den blir klar."
+importRequested: "Du har begärt en import. Detta kan ta lite tid."
+lists: "Listor"
+noLists: "Du har inga listor"
+note: "Not"
+notes: "Noter"
+following: "Följer"
+followers: "Följare"
+followsYou: "Följer dig"
+createList: "Skapa lista"
+manageLists: "Hantera lista"
+error: "Fel!"
+somethingHappened: "Ett fel har uppstått"
+retry: "Försök igen"
+pageLoadError: "Det gick inte att ladda sidan."
+pageLoadErrorDescription: "Detta händer oftast p.g.a. nätverksfel eller din webbläsarcache. Försök tömma din cache och testa sedan igen efter en liten stund."
+serverIsDead: "Servern svarar inte. Vänta ett litet tag och försök igen."
+youShouldUpgradeClient: "För att kunna se denna sida, vänligen ladda om sidan för att uppdatera din klient."
+enterListName: "Skriv ett namn till listan"
+privacy: "Integritet"
+makeFollowManuallyApprove: "Följarförfrågningar kräver manuellt godkännande"
+defaultNoteVisibility: "Standardsynlighet"
+follow: "Följ"
+followRequest: "Skicka följarförfrågan"
+followRequests: "Följarförfrågningar"
+unfollow: "Avfölj"
+followRequestPending: "Följarförfrågning avvaktar för svar"
+enterEmoji: "Skriv en emoji"
+renote: "Omnotera"
+unrenote: "Ta tillbaka omnotering"
+renoted: "Omnoterad."
+cantRenote: "Inlägget kunde inte bli omnoterat."
+cantReRenote: "En omnotering kan inte bli omnoterad."
+quote: "Citat"
+pinnedNote: "Fästad not"
+pinned: "Fäst till profil"
+you: "Du"
+clickToShow: "Klicka för att visa"
+sensitive: "Känsligt innehåll"
+add: "Lägg till"
+reaction: "Reaktioner"
+reactionSetting: "Reaktioner som ska visas i reaktionsväljaren"
+reactionSettingDescription2: "Dra för att omordna, klicka för att radera, tryck \"+\" för att lägga till."
+rememberNoteVisibility: "Komihåg notvisningsinställningar"
+attachCancel: "Ta bort bilaga"
+markAsSensitive: "Markera som känsligt innehåll"
+unmarkAsSensitive: "Avmarkera som känsligt innehåll"
+enterFileName: "Ange filnamn"
+mute: "Tysta"
+unmute: "Avtysta"
+block: "Blockera"
+unblock: "Avblockera"
+suspend: "Suspendera"
+unsuspend: "Ta bort suspenderingen"
+blockConfirm: "Är du säker att du vill blockera kontot?"
+unblockConfirm: "Är du säkert att du vill avblockera kontot?"
+suspendConfirm: "Är du säker att du vill suspendera detta konto?"
+unsuspendConfirm: "Är du säker att du vill avsuspendera detta konto?"
+selectList: "Välj lista"
+selectAntenna: "Välj en antenn"
+selectWidget: "Välj en widget"
+editWidgets: "Redigera widgets"
+editWidgetsExit: "Avsluta redigering"
+customEmojis: "Anpassa emoji"
+emoji: "Emoji"
+emojis: "Emoji"
+emojiName: "Emoji namn"
+emojiUrl: "Emoji länk"
+addEmoji: "Lägg till emoji"
+settingGuide: "Rekommenderade inställningar"
+cacheRemoteFiles: "Spara externa filer till cachen"
+cacheRemoteFilesDescription: "När denna inställning är avstängd kommer externa filer laddas direkt från den externa instansen. Genom att stänga av detta kommer lagringsutrymme minska i användning men kommer öka datatrafiken eftersom miniatyrer inte kommer genereras."
+flagAsBot: "Markera konto som bot"
+flagAsBotDescription: "Aktivera det här alternativet om kontot är kontrollerat av ett program. Om aktiverat kommer den fungera som en flagga för andra utvecklare för att hindra ändlösa kedjor med andra bottar. Det kommer också få Misskeys interna system att hantera kontot som en bot."
+flagAsCat: "Markera konto som katt"
+flagAsCatDescription: "Aktivera denna inställning för att markera kontot som en katt."
+flagShowTimelineReplies: "Visa svar i tidslinje"
+flagShowTimelineRepliesDescription: "Visar användarsvar till andra användares noter i tidslinjen om påslagen."
+autoAcceptFollowed: "Godkänn följarförfrågningar från användare du följer automatiskt"
+addAccount: "Lägg till konto"
+loginFailed: "Inloggningen misslyckades"
+showOnRemote: "Se på extern instans"
+general: "Allmänt"
+wallpaper: "Bakgrundsbild"
+setWallpaper: "Välj bakgrund"
+removeWallpaper: "Ta bort bakgrund"
+searchWith: "Sök: {q}"
+youHaveNoLists: "Du har inga listor"
+followConfirm: "Är du säker att du vill följa {name}?"
+proxyAccount: "Proxykonto"
+proxyAccountDescription: "Ett proxykonto är ett konto som agerar som en extern följare för användare under vissa villkor. Till exempel, när en användare lägger till en extern användare till en lista så kommer den externa användarens aktivitet inte levireras till instansen om ingen lokal användare följer det kontot, så proxykontot används istället."
+host: "Värd"
+selectUser: "Välj användare"
+recipient: "Mottagare"
+annotation: "Kommentarer"
+federation: "Federation"
+instances: "Instanser"
+registeredAt: "Registrerad på"
+latestRequestSentAt: "Senaste förfrågan skickad"
+latestRequestReceivedAt: "Senaste begäran mottagen"
+latestStatus: "Senaste status"
+storageUsage: "Använt lagringsutrymme"
+charts: "Diagram"
+perHour: "Per timme"
+perDay: "Per dag"
+stopActivityDelivery: "Sluta skicka aktiviteter"
+blockThisInstance: "Blockera instans"
+operations: "Operationer"
+software: "Mjukvara"
+version: "Version"
+metadata: "Metadata"
+withNFiles: "{n} fil(er)"
+monitor: "Övervakning"
+jobQueue: "Jobbkö"
+cpuAndMemory: "CPU och minne"
+network: "Nätverk"
+disk: "Disk"
+instanceInfo: "Instansinformation"
+statistics: "Statistik"
+clearQueue: "Rensa kö"
+clearQueueConfirmTitle: "Är du säker att du vill rensa kön?"
+clearQueueConfirmText: "Om någon not är olevererad i kön kommer den inte federeras. Vanligtvis behövs inte denna handling."
+clearCachedFiles: "Rensa cache"
+clearCachedFilesConfirm: "Är du säker att du vill radera alla cachade externa filer?"
+blockedInstances: "Blockerade instanser"
+blockedInstancesDescription: "Lista adressnamn av instanser som du vill blockera. Listade instanser kommer inte längre kommunicera med denna instans."
+muteAndBlock: "Tystningar och blockeringar"
+mutedUsers: "Tystade användare"
+blockedUsers: "Blockerade användare"
+noUsers: "Det finns inga användare"
+editProfile: "Redigera profil"
+noteDeleteConfirm: "Är du säker på att du vill ta bort denna not?"
+pinLimitExceeded: "Du kan inte fästa fler noter"
+intro: "Misskey har installerats! Vänligen skapa en adminanvändare."
+done: "Klar"
+processing: "Bearbetar..."
+preview: "Förhandsvisning"
+default: "Standard"
+noCustomEmojis: "Det finns ingen emoji"
+noJobs: "Det finns inga jobb"
+federating: "Federerar"
+blocked: "Blockerad"
+suspended: "Suspenderad"
+all: "Allt"
+subscribing: "Prenumererar"
+publishing: "Publiceras"
+notResponding: "Svarar inte"
+instanceFollowing: "Följer på instans"
+instanceFollowers: "Följare av instans"
+instanceUsers: "Användare av denna instans"
+changePassword: "Ändra lösenord"
+security: "Säkerhet"
+retypedNotMatch: "Inmatningen matchar inte"
+currentPassword: "Nuvarande lösenord"
+newPassword: "Nytt lösenord"
+newPasswordRetype: "Bekräfta lösenord"
+attachFile: "Bifoga filer"
+more: "Mer!"
+featured: "Utvalda"
+usernameOrUserId: "Användarnamn eller användar-id"
+noSuchUser: "Kan inte hitta användaren"
+lookup: "Sökning"
+announcements: "Nyheter"
+imageUrl: "Bild-URL"
+remove: "Radera"
+removed: "Borttaget"
+removeAreYouSure: "Är du säker att du vill radera \"{x}\"?"
+deleteAreYouSure: "Är du säker att du vill radera \"{x}\"?"
+resetAreYouSure: "Vill du återställa?"
+saved: "Sparad"
+messaging: "Chatt"
+upload: "Ladda upp"
+keepOriginalUploading: "Behåll originalbild"
+nsfw: "Känsligt innehåll"
+pinnedNotes: "Fästad not"
+userList: "Listor"
+smtpHost: "Värd"
+smtpUser: "Användarnamn"
+smtpPass: "Lösenord"
+clearCache: "Rensa cache"
+user: "Användare"
+searchByGoogle: "Sök"
+_email:
+ _follow:
+ title: "följde dig"
+_mfm:
+ mention: "Nämn"
+ quote: "Citat"
+ emoji: "Anpassa emoji"
+ search: "Sök"
+_theme:
+ keys:
+ mention: "Nämn"
+ renote: "Omnotera"
+_sfx:
+ note: "Noter"
+ notification: "Notifikationer"
+ chat: "Chatt"
+_widgets:
+ notifications: "Notifikationer"
+ timeline: "Tidslinje"
+ federation: "Federation"
+ jobQueue: "Jobbkö"
+_cw:
+ show: "Ladda mer"
+_visibility:
+ followers: "Följare"
+_profile:
+ username: "Användarnamn"
+_exportOrImport:
+ followingList: "Följer"
+ muteList: "Tysta"
+ blockingList: "Blockera"
+ userLists: "Listor"
+_charts:
+ federation: "Federation"
+_pages:
+ script:
+ categories:
+ list: "Listor"
+ blocks:
+ _join:
+ arg1: "Listor"
+ _randomPick:
+ arg1: "Listor"
+ _dailyRandomPick:
+ arg1: "Listor"
+ _seedRandomPick:
+ arg2: "Listor"
+ _pick:
+ arg1: "Listor"
+ _listLen:
+ arg1: "Listor"
+ types:
+ array: "Listor"
+_notification:
+ youWereFollowed: "följde dig"
+ _types:
+ follow: "Följer"
+ mention: "Nämn"
+ renote: "Omnotera"
+ quote: "Citat"
+ reaction: "Reaktioner"
+ _actions:
+ reply: "Svara"
+ renote: "Omnotera"
+_deck:
+ _columns:
+ notifications: "Notifikationer"
+ tl: "Tidslinje"
+ list: "Listor"
+ mentions: "Omnämningar"
diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml
index 073b2c310e..7e7ef8685f 100644
--- a/locales/uk-UA.yml
+++ b/locales/uk-UA.yml
@@ -7,6 +7,7 @@ search: "Пошук"
notifications: "СповіщеннÑ"
username: "Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
password: "Пароль"
+forgotPassword: "Я забув пароль"
fetchingAsApObject: "Отримуємо з федіверÑу..."
ok: "OK"
gotIt: "Зрозуміло!"
@@ -80,6 +81,8 @@ somethingHappened: "ЩоÑÑŒ пішло не так"
retry: "Спробувати знову"
pageLoadError: "Помилка при завантаженні Ñторінки"
pageLoadErrorDescription: "Зазвичай це пов’Ñзано з помилками мережі або кешем браузера. ОчиÑтіть кеш або почекайте трохи й Ñпробуйте ще раз."
+serverIsDead: "Відповіді від Ñервера немає. Зачекайте деÑкий Ñ‡Ð°Ñ Ñ– повторіть Ñпробу."
+youShouldUpgradeClient: "Перезавантажте та викориÑтовуйте нову верÑÑ–ÑŽ клієнта, щоб переглÑнути цю Ñторінку."
enterListName: "Введіть назву ÑпиÑку"
privacy: "КонфіденційніÑть"
makeFollowManuallyApprove: "Підтверджувати підпиÑників уручну"
@@ -103,6 +106,7 @@ clickToShow: "ÐатиÑніть Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду"
sensitive: "NSFW"
add: "Додати"
reaction: "Реакції"
+reactionSetting: "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÐ°ÐºÑ†Ñ–Ð¹"
reactionSettingDescription2: "ПереміÑтити щоб змінити порÑдок, Клацнути мишою щоб видалити, ÐатиÑнути \"+\" щоб додати."
rememberNoteVisibility: "Пам’Ñтати параметри видиміÑті"
attachCancel: "Видалити вкладеннÑ"
@@ -137,7 +141,10 @@ flagAsBot: "Ðкаунт бота"
flagAsBotDescription: "Ввімкніть Ñкщо цей обліковий Ð·Ð°Ð¿Ð¸Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑтовуєтьÑÑ Ð±Ð¾Ñ‚Ð¾Ð¼. Ð¦Ñ Ð¾Ð¿Ñ†Ñ–Ñ Ð¿Ð¾Ð·Ð½Ð°Ñ‡Ð¸Ñ‚ÑŒ обліковий Ð·Ð°Ð¿Ð¸Ñ Ñк бота. Це потрібно щоб виключити безкінечну інтеракцію між ботами а також відповідного Ð¿Ñ–Ð´Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Misskey."
flagAsCat: "Ðкаунт кота"
flagAsCatDescription: "Ввімкніть, щоб позначити, що обліковий Ð·Ð°Ð¿Ð¸Ñ Ñ” котиком."
+flagShowTimelineReplies: "Показувати відповіді на нотатки на чаÑовій шкалі"
+flagShowTimelineRepliesDescription: "Показує відповіді кориÑтувачів на нотатки інших кориÑтувачів на чаÑовій шкалі."
autoAcceptFollowed: "Ðвтоматично приймати запити на підпиÑку від кориÑтувачів, на Ñких ви підпиÑані"
+addAccount: "Додати акаунт"
loginFailed: "Ðе вдалоÑÑ ÑƒÐ²Ñ–Ð¹Ñ‚Ð¸"
showOnRemote: "ПереглÑнути в оригіналі"
general: "Загальне"
@@ -148,6 +155,7 @@ searchWith: "Пошук: {q}"
youHaveNoLists: "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” ÑпиÑків"
followConfirm: "ПідпиÑатиÑÑ Ð½Ð° {name}?"
proxyAccount: "ПрокÑÑ–-акаунт"
+proxyAccountDescription: "Обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¿Ñ€Ð¾ÐºÑÑ– – це обліковий запиÑ, Ñкий діє Ñк віддалений підпиÑник Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувачів за певних умов. Ðаприклад, коли кориÑтувач додає віддаленого кориÑтувача до ÑпиÑку, активніÑть віддаленого кориÑтувача не буде доÑтавлена на Ñервер, Ñкщо жоден локальний кориÑтувач не Ñтежить за цим кориÑтувачем, то заміÑть нього буде викориÑтовуватиÑÑ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¸Ð¹ Ð·Ð°Ð¿Ð¸Ñ Ð¿Ñ€Ð¾ÐºÑÑ–-Ñервера."
host: "ХоÑÑ‚"
selectUser: "Виберіть кориÑтувача"
recipient: "Отримувач"
@@ -229,6 +237,8 @@ resetAreYouSure: "Справді Ñкинути?"
saved: "Збережено"
messaging: "Чати"
upload: "Завантажити"
+keepOriginalUploading: "Зберегти оригінальне зображеннÑ"
+keepOriginalUploadingDescription: "Зберігає початково завантажене Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ñк Ñ”. Якщо вимкнено, верÑÑ–Ñ Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð² Інтернеті буде Ñтворена під Ñ‡Ð°Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ."
fromDrive: "З диÑка"
fromUrl: "З поÑиланнÑ"
uploadFromUrl: "Завантажити з поÑиланнÑ"
@@ -275,6 +285,7 @@ emptyDrive: "ДиÑк порожній"
emptyFolder: "Тека порожнÑ"
unableToDelete: "Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð½ÐµÐ¼Ð¾Ð¶Ð»Ð¸Ð²Ðµ"
inputNewFileName: "Введіть ім'Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ файлу"
+inputNewDescription: "Введіть новий заголовок"
inputNewFolderName: "Введіть ім'Ñ Ð½Ð¾Ð²Ð¾Ñ— теки"
circularReferenceFolder: "Ви намагаєтеÑÑŒ переміÑтити папку в Ñ—Ñ— підпапку."
hasChildFilesOrFolders: "Ð¦Ñ Ñ‚ÐµÐºÐ° не Ð¿Ð¾Ñ€Ð¾Ð¶Ð½Ñ Ñ– не може бути видалена"
@@ -306,6 +317,8 @@ monthX: "{month}"
yearX: "{year}"
pages: "Сторінки"
integration: "ІнтеграціÑ"
+connectService: "Під’єднати"
+disconnectService: "ВідключитиÑÑ"
enableLocalTimeline: "Увімкнути локальну Ñтрічку"
enableGlobalTimeline: "Увімкнути глобальну Ñтрічку"
disablingTimelinesInfo: "ÐдмініÑтратори та модератори завжди мають доÑтуп до вÑÑ–Ñ… Ñтрічок, навіть Ñкщо вони вимкнуті."
@@ -317,6 +330,7 @@ driveCapacityPerRemoteAccount: "Об'єм диÑка на одного віддÐ
inMb: "В мегабайтах"
iconUrl: "URL аватара"
bannerUrl: "URL банера"
+backgroundImageUrl: "URL-адреÑа фонового зображеннÑ"
basicInfo: "ОÑновна інформаціÑ"
pinnedUsers: "Закріплені кориÑтувачі"
pinnedUsersDescription: "Впишіть в ÑпиÑок кориÑтувачів, Ñких хочете закріпити на Ñторінці \"Знайти\", ім'Ñ Ð² Ñтовпчик."
@@ -332,6 +346,7 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "Увімкнути reCAPTCHA"
recaptchaSiteKey: "Ключ Ñайту"
recaptchaSecretKey: "Секретний ключ"
+avoidMultiCaptchaConfirm: "ВикориÑÑ‚Ð°Ð½Ð½Ñ ÐºÑ–Ð»ÑŒÐºÐ¾Ñ… ÑиÑтем Captcha може Ñпричинити перешкоди між ними. Бажаєте вимкнути інші активні ÑиÑтеми Captcha? Якщо ви хочете, щоб вони залишалиÑÑ Ð²Ð²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð¸Ð¼Ð¸, натиÑніть «СкаÑувати»."
antennas: "Ðнтени"
manageAntennas: "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð½Ñ‚ÐµÐ½"
name: "Ім'Ñ"
@@ -428,10 +443,12 @@ signinWith: "Увійти за допомогою {x}"
signinFailed: "Ðе вдалоÑÑ ÑƒÐ²Ñ–Ð¹Ñ‚Ð¸. Введені Ñ–Ð¼â€™Ñ ÐºÐ¾Ñ€Ð¸Ñтувача або пароль неправильнi."
tapSecurityKey: "ТоркнітьÑÑ ÐºÐ»ÑŽÑ‡Ð° безпеки"
or: "або"
+language: "Мова"
uiLanguage: "Мова інтерфейÑу"
groupInvited: "Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ групи"
aboutX: "Про {x}"
useOsNativeEmojis: "ВикориÑтовувати емодзі ОС"
+disableDrawer: "Ðе викориÑтовувати виÑувні меню"
youHaveNoGroups: "Ðемає груп"
joinOrCreateGroup: "Отримуйте Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ груп або Ñтворюйте Ñвої влаÑні групи."
noHistory: "ІÑÑ‚Ð¾Ñ€Ñ–Ñ Ð¿Ð¾Ñ€Ð¾Ð¶Ð½Ñ"
@@ -442,6 +459,7 @@ category: "КатегоріÑ"
tags: "Теги"
docSource: "Джерело цього документа"
createAccount: "Створити акаунт"
+existingAccount: "ІÑнуючий обліковий запиÑ"
regenerate: "Оновити"
fontSize: "Розмір шрифту"
noFollowRequests: "Ðемає запитів на підпиÑку"
@@ -463,6 +481,7 @@ showFeaturedNotesInTimeline: "Показувати популÑрні нотат
objectStorage: "Object Storage"
useObjectStorage: "ВикориÑтовувати object storage"
objectStorageBaseUrl: "Base URL"
+objectStorageBaseUrlDesc: "Це початкова чаÑтина адреÑи, що викориÑтовуєтьÑÑ CDN або прокÑÑ–, наприклад Ð´Ð»Ñ S3: https://<bucket>.s3.amazonaws.com, або GCS: 'https://storage.googleapis.com/<bucket>'"
objectStorageBucket: "Bucket"
objectStorageBucketDesc: "Будь лаÑка вкажіть назву відра в налаштованому ÑервіÑÑ–."
objectStoragePrefix: "Prefix"
@@ -513,6 +532,9 @@ removeAllFollowing: "СкаÑувати вÑÑ– підпиÑки"
removeAllFollowingDescription: "СкаÑувати підпиÑку на вÑÑ– акаунти з {host}. Будь лаÑка, робіть це, Ñкщо інÑÑ‚Ð°Ð½Ñ Ð±Ñ–Ð»ÑŒÑˆÐµ не Ñ–Ñнує."
userSuspended: "Обліковий Ð·Ð°Ð¿Ð¸Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¸Ð¹."
userSilenced: "Обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¿Ñ€Ð¸Ð³Ð»ÑƒÑˆÐµÐ½Ð¸Ð¹."
+yourAccountSuspendedTitle: "Цей обліковий Ð·Ð°Ð¿Ð¸Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¾"
+yourAccountSuspendedDescription: "Цей обліковий Ð·Ð°Ð¿Ð¸Ñ Ð±ÑƒÐ»Ð¾ заблоковано через Ð¿Ð¾Ñ€ÑƒÑˆÐµÐ½Ð½Ñ ÑƒÐ¼Ð¾Ð² Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ñлуг Ñервера. Зв'ÑжітьÑÑ Ð· адмініÑтратором, Ñкщо ви хочете дізнатиÑÑ Ð´Ð¾ÐºÐ»Ð°Ð´Ð½Ñ–ÑˆÑƒ причину. Будь лаÑка, не Ñтворюйте новий обліковий запиÑ."
+menu: "Меню"
divider: "Розділювач"
addItem: "Додати елемент"
relays: "РетранÑлÑтори"
@@ -531,6 +553,8 @@ disablePlayer: "Закрити відеоплеєр"
expandTweet: "Розгорнути твіт"
themeEditor: "Редактор тем"
description: "ОпиÑ"
+describeFile: "Додати підпиÑ"
+enterFileDescription: "Введіть підпиÑ"
author: "Ðвтор"
leaveConfirm: "Зміни не збережені. Ви дійÑно хочете ÑкаÑувати зміни?"
manage: "УправліннÑ"
@@ -553,6 +577,7 @@ pluginTokenRequestedDescription: "Цей плагін зможе викориÑÑ
notificationType: "Тип ÑповіщеннÑ"
edit: "Редагувати"
useStarForReactionFallback: "ВикориÑтовувати ★ Ñк запаÑний варіант, Ñкщо емодзі реакції невідомий"
+emailServer: "Сервер електронної пошти"
enableEmail: "Увімкнути функцію доÑтавки пошти"
emailConfigInfo: "ВикориÑтовуєтьÑÑ Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти Ð¿Ñ–Ð´Ñ‡Ð°Ñ Ñ€ÐµÑ”Ñтрації, а також Ð´Ð»Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»ÑŽ."
email: "E-mail"
@@ -567,6 +592,9 @@ smtpSecure: "ВикориÑтовувати безумовне шифруванÐ
smtpSecureInfo: "Вимкніть при викориÑтанні STARTTLS "
testEmail: "ТеÑтовий email"
wordMute: "Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ñлів"
+regexpError: "Помилка регулÑрного виразу"
+regexpErrorDescription: "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° в регулÑрному виразі в Ñ€Ñдку {line} вашого Ñлова {tab} Ñлова що ігноруютьÑÑ:"
+instanceMute: "ÐŸÑ€Ð¸Ð³Ð»ÑƒÑˆÐµÐ½Ð½Ñ Ñ–Ð½ÑтанÑів"
userSaysSomething: "{name} щоÑÑŒ Ñказав(ла)"
makeActive: "Ðктивувати"
display: "ВідображеннÑ"
@@ -594,6 +622,11 @@ reportAbuse: "ПоÑкаржитиÑÑŒ"
reportAbuseOf: "ПоÑкаржитиÑÑŒ на {name}"
fillAbuseReportDescription: "Будь лаÑка вкажіть подробиці Ñкарги. Якщо Ñкарга ÑтоÑуєтьÑÑ Ð·Ð°Ð¿Ð¸Ñу, вкажіть поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° нього."
abuseReported: "ДÑкуємо, вашу Ñкаргу було відправлено. "
+reporter: "Репортер"
+reporteeOrigin: "Про кого повідомлено"
+reporterOrigin: "Хто повідомив"
+forwardReport: "ПереÑлати звіт на віддалений інÑтанÑ"
+forwardReportIsAnonymous: "ЗаміÑть вашого облікового запиÑу анонімний ÑиÑтемний обліковий Ð·Ð°Ð¿Ð¸Ñ Ð±ÑƒÐ´Ðµ відображатиÑÑ Ñк доповідач на віддаленому інÑтанÑÑ–"
send: "Відправити"
abuseMarkAsResolved: "Позначити Ñкаргу Ñк вирішену"
openInNewTab: "Відкрити в новій вкладці"
@@ -655,6 +688,7 @@ center: "Центр"
wide: "Широкий"
narrow: "Вузький"
reloadToApplySetting: "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð²Ñ–Ð¹Ð´Ðµ в дію при перезавантаженні. Перезавантажити?"
+needReloadToApply: "Зміни набудуть чинноÑті піÑÐ»Ñ Ð¿ÐµÑ€ÐµÐ·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñторінки."
showTitlebar: "Показати титульний Ñ€Ñдок"
clearCache: "ОчиÑтити кеш"
onlineUsersCount: "{n} кориÑтувачів онлайн"
@@ -669,12 +703,28 @@ textColor: "ТекÑÑ‚"
saveAs: "Зберегти Ñк…"
advanced: "Розширені"
value: "ЗначеннÑ"
+createdAt: "Створено"
updatedAt: "ОÑтаннє оновленнÑ"
saveConfirm: "Зберегти зміни?"
deleteConfirm: "Ви дійÑно бажаєте це видалити?"
invalidValue: "Ðекоректне значеннÑ."
registry: "РеєÑтр"
closeAccount: "Закрити обліковий запиÑ"
+currentVersion: "ВерÑÑ–Ñ, що викориÑтовуєтьÑÑ"
+latestVersion: "Сама Ñвіжа верÑÑ–Ñ"
+youAreRunningUpToDateClient: "У Ð²Ð°Ñ Ð½Ð°Ð¹Ñвіжіша верÑÑ–Ñ ÐºÐ»Ñ–Ñ”Ð½Ñ‚Ð°."
+newVersionOfClientAvailable: "ДоÑтупніша Ñвіжа верÑÑ–Ñ ÐºÐ»Ñ–Ñ”Ð½Ñ‚Ð°."
+usageAmount: "ВикориÑтане"
+capacity: "ЄмніÑть"
+inUse: "ЗайнÑто"
+editCode: "Редагувати вихідний текÑÑ‚"
+apply: "ЗаÑтоÑувати"
+receiveAnnouncementFromInstance: "Отримувати Ð¾Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð· інÑтанÑу"
+emailNotification: "Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾ÑŽ поштою"
+publish: "Опублікувати"
+inChannelSearch: "Пошук за каналом"
+useReactionPickerForContextMenu: "Відкривати палітру реакцій правою кнопкою"
+typingUsers: "Стук клавіш. Це {users}…"
goBack: "Ðазад"
info: "ІнформаціÑ"
user: "КориÑтувачі"
@@ -687,6 +737,8 @@ hashtags: "Хештеґ"
hide: "Сховати"
searchByGoogle: "Пошук"
indefinitely: "Ðіколи"
+_ffVisibility:
+ public: "Опублікувати"
_ad:
back: "Ðазад"
_gallery:
@@ -867,7 +919,6 @@ _sfx:
antenna: "Прийом антени"
channel: "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐºÐ°Ð½Ð°Ð»Ñƒ"
_ago:
- unknown: "Ðевідомо"
future: "Майбутнє"
justNow: "Щойно"
secondsAgo: "{n}Ñ Ñ‚Ð¾Ð¼Ñƒ"
@@ -1377,6 +1428,9 @@ _notification:
followRequestAccepted: "ПрийнÑті підпиÑки"
groupInvited: "Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ груп"
app: "Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ додатків"
+ _actions:
+ reply: "ВідповіÑти"
+ renote: "Поширити"
_deck:
alwaysShowMainColumn: "Завжди показувати головну колонку"
columnAlign: "ВирівнÑти Ñтовпці"
diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml
index 42f86b3359..9919e0a0a4 100644
--- a/locales/vi-VN.yml
+++ b/locales/vi-VN.yml
@@ -1,35 +1,1093 @@
---
_lang_: "Tiếng Việt"
headlineMisskey: "Mạng xã hội liên hợp"
+introMisskey: "Xin chào! Misskey là má»™t ná»n tảng tiểu blog phi tập trung mã nguồn mở.\nViết \"tút\" để chia sẻ những suy nghÄ© cá»§a bạn 📡\nBằng \"biểu cảm\", bạn có thể bày tá» nhanh chóng cảm xúc cá»§a bạn vá»›i các tút ðŸ‘\nHãy khám phá má»™t thế giá»›i má»›i! 🚀"
monthAndDay: "{day} tháng {month}"
search: "Tìm kiếm"
notifications: "Thông báo"
username: "Tên ngưá»i dùng"
password: "Mật khẩu"
forgotPassword: "Quên mật khẩu"
+fetchingAsApObject: "Äang nạp dữ liệu từ Fediverse..."
ok: "Äồng ý"
+gotIt: "Äã hiểu!"
+cancel: "Há»§y"
+enterUsername: "Nhập tên ngưá»i dùng"
renotedBy: "Chia sẻ bởi {user}"
+noNotes: "Chưa có tút nào."
+noNotifications: "Không có thông báo"
+instance: "Máy chủ"
+settings: "Cài đặt"
+basicSettings: "Thiết lập chung"
+otherSettings: "Thiết lập khác"
+openInWindow: "Mở trong cửa sổ mới"
+profile: "Trang cá nhân"
+timeline: "Bảng tin"
+noAccountDescription: "Ngưá»i này chưa viết mô tả."
+login: "Äăng nhập"
+loggingIn: "Äang đăng nhập..."
+logout: "Äăng xuất"
+signup: "Äăng ký"
+uploading: "Äang tải lên…"
+save: "Lưu"
+users: "Ngưá»i dùng"
+addUser: "Thêm ngưá»i dùng"
+favorite: "Thêm vào yêu thích"
+favorites: "Lượt thích"
+unfavorite: "BỠthích"
+favorited: "Äã thêm vào yêu thích."
+alreadyFavorited: "Äã thêm vào yêu thích rồi."
+cantFavorite: "Không thể thêm vào yêu thích."
+pin: "Ghim"
+unpin: "Bá» ghim"
+copyContent: "Chép nội dung"
+copyLink: "Chép liên kết"
+delete: "Xóa"
+deleteAndEdit: "Sá»­a"
+deleteAndEditConfirm: "Bạn có chắc muốn sá»­a tút này? Những biểu cảm, lượt trả lá»i và đăng lại sẽ bị mất."
+addToList: "Thêm vào danh sách"
+sendMessage: "Gửi tin nhắn"
+copyUsername: "Chép tên ngưá»i dùng"
+searchUser: "Tìm kiếm ngưá»i dùng"
+reply: "Trả lá»i"
+loadMore: "Tải thêm"
+showMore: "Xem thêm"
+youGotNewFollower: "đã theo dõi bạn"
+receiveFollowRequest: "Äã yêu cầu theo dõi"
+followRequestAccepted: "Äã chấp nhận yêu cầu theo dõi"
+mention: "Nhắc đến"
+mentions: "Lượt nhắc"
+directNotes: "Nhắn riêng"
+importAndExport: "Nhập và xuất dữ liệu"
+import: "Nhập dữ liệu"
+export: "Xuất dữ liệu"
+files: "Tập tin"
+download: "Tải xuống"
+driveFileDeleteConfirm: "Bạn có chắc muốn xóa tập tin \"{name}\"? Tút liên quan cũng sẽ bị xóa theo."
+unfollowConfirm: "Bạn có chắc muốn ngưng theo dõi {name}?"
+exportRequested: "Äang chuẩn bị xuất tập tin. Quá trình này có thể mất ít phút. Nó sẽ được tá»± động thêm vào Drive sau khi hoàn thành."
+importRequested: "Bạn vừa yêu cầu nhập dữ liệu. Quá trình này có thể mất ít phút."
+lists: "Danh sách"
+noLists: "Bạn chưa có danh sách nào"
+note: "Tút"
+notes: "Tút"
+following: "Äang theo dõi"
+followers: "Ngưá»i theo dõi"
+followsYou: "Theo dõi bạn"
+createList: "Tạo danh sách"
+manageLists: "Quản lý danh sách"
+error: "Lá»—i"
+somethingHappened: "Xảy ra lỗi"
+retry: "Thử lại"
+pageLoadError: "Xảy ra lỗi khi tải trang."
+pageLoadErrorDescription: "Có thể là do bộ nhớ đệm của trình duyệt. Hãy thử xóa bộ nhớ đệm và thử lại sau ít phút."
+serverIsDead: "Máy chủ không phản hồi. Vui lòng thử lại sau giây lát."
+youShouldUpgradeClient: "Äể xem trang này, hãy làm tươi để cập nhật ứng dụng."
+enterListName: "Äặt tên cho danh sách"
+privacy: "Bảo mật"
+makeFollowManuallyApprove: "Yêu cầu theo dõi cần được duyệt"
+defaultNoteVisibility: "Kiểu tút mặc định"
+follow: "Äang theo dõi"
+followRequest: "Gửi yêu cầu theo dõi"
+followRequests: "Yêu cầu theo dõi"
+unfollow: "Ngưng theo dõi"
+followRequestPending: "Yêu cầu theo dõi Ä‘ang chá»"
+enterEmoji: "Chèn emoji"
+renote: "Äăng lại"
+unrenote: "Hủy đăng lại"
+renoted: "Äã đăng lại."
+cantRenote: "Không thể đăng lại tút này."
+cantReRenote: "Không thể đăng lại một tút đăng lại."
+quote: "Trích dẫn"
+pinnedNote: "Tút ghim"
+pinned: "Ghim"
+you: "Bạn"
+clickToShow: "Nhấn để xem"
+sensitive: "Nhạy cảm"
+add: "Thêm"
+reaction: "Biểu cảm"
+reactionSetting: "Chá»n những biểu cảm hiển thị"
+reactionSettingDescription2: "Kéo để sắp xếp, nhấn để xóa, nhấn \"+\" để thêm."
+rememberNoteVisibility: "Lưu kiểu tút mặc định"
+attachCancel: "Gỡ tập tin đính kèm"
+markAsSensitive: "Äánh dấu là nhạy cảm"
+unmarkAsSensitive: "BỠđánh dấu nhạy cảm"
+enterFileName: "Nhập tên tập tin"
+mute: "Ẩn"
+unmute: "BỠẩn"
+block: "Chặn"
+unblock: "BỠchặn"
+suspend: "Vô hiệu hóa"
+unsuspend: "BỠvô hiệu hóa"
+blockConfirm: "Bạn có chắc muốn chặn ngưá»i này?"
+unblockConfirm: "Bạn có chắc muốn bá» chặn ngưá»i này?"
+suspendConfirm: "Bạn có chắc muốn vô hiệu hóa ngưá»i này?"
+unsuspendConfirm: "Bạn có chắc muốn bá» vô hiệu hóa ngưá»i này?"
+selectList: "Chá»n danh sách"
+selectAntenna: "Chá»n má»™t antenna"
+selectWidget: "Chá»n tiện ích"
+editWidgets: "Sửa tiện ích"
+editWidgetsExit: "Xong"
+customEmojis: "Tùy chỉnh emoji"
+emoji: "Emoji"
+emojis: "Emoji"
+emojiName: "Tên emoji"
+emojiUrl: "URL Emoji"
+addEmoji: "Thêm emoji"
+settingGuide: "Cài đặt đỠxuất"
+cacheRemoteFiles: "Tập tin cache từ xa"
+cacheRemoteFilesDescription: "Khi tùy chá»n này bị tắt, các tập tin từ xa sẽ được tải trá»±c tiếp từ máy chá»§ khác. Äiá»u này sẽ giúp giảm dung lượng lưu trữ nhưng lại tăng lưu lượng truy cập, vì hình thu nhá» sẽ không được tạo."
flagAsBot: "Äánh dấu đây là tài khoản bot"
+flagAsBotDescription: "Bật tùy chá»n này nếu tài khoản này được kiểm soát bởi má»™t chương trình. Nếu được bật, nó sẽ được đánh dấu để các nhà phát triển khác ngăn chặn chuá»—i tương tác vô tận vá»›i các bot khác và Ä‘iá»u chỉnh hệ thống ná»™i bá»™ cá»§a Misskey để coi tài khoản này như má»™t bot."
+flagAsCat: "Tài khoản này là mèo"
+flagAsCatDescription: "Bật tùy chá»n này để đánh dấu tài khoản là má»™t con mèo."
+flagShowTimelineReplies: "Hiện lượt trả lá»i trong bảng tin"
+flagShowTimelineRepliesDescription: "Hiện lượt trả lá»i cá»§a ngưá»i bạn theo dõi trên tút cá»§a những ngưá»i khác."
+autoAcceptFollowed: "Tá»± động phê duyệt theo dõi từ những ngưá»i mà bạn Ä‘ang theo dõi"
+addAccount: "Thêm tài khoản"
+loginFailed: "Äăng nhập không thành công"
+showOnRemote: "Truy cập trang cá»§a ngưá»i này"
+general: "Tổng quan"
+wallpaper: "Ảnh bìa"
+setWallpaper: "Äặt ảnh bìa"
+removeWallpaper: "Xóa ảnh bìa"
searchWith: "Tìm kiếm: {q}"
+youHaveNoLists: "Bạn chưa có danh sách nào"
followConfirm: "Bạn có chắc muốn theo dõi {name}?"
+proxyAccount: "Tài khoản proxy"
+proxyAccountDescription: "Tài khoản proxy là tài khoản hoạt động như má»™t ngưá»i theo dõi từ xa cho ngưá»i dùng trong những Ä‘iá»u kiện nhất định. Ví dụ: khi ngưá»i dùng thêm ngưá»i dùng từ xa vào danh sách, hoạt động cá»§a ngưá»i dùng từ xa sẽ không được chuyển đến phiên bản nếu không có ngưá»i dùng cục bá»™ nào theo dõi ngưá»i dùng đó, vì vậy tài khoản proxy sẽ theo dõi."
+host: "Host"
+selectUser: "Chá»n ngưá»i dùng"
+recipient: "Ngưá»i nhận"
+annotation: "Bình luận"
+federation: "Liên hợp"
+instances: "Máy chủ"
+registeredAt: "Äăng ký vào"
+latestRequestSentAt: "Yêu cầu cuối gửi lúc"
+latestRequestReceivedAt: "Yêu cầu cuối nhận lúc"
+latestStatus: "Trạng thái cuối cùng"
+storageUsage: "Dung lượng lưu trữ"
+charts: "Äồ thị"
+perHour: "Má»—i Giá»"
+perDay: "Mỗi Ngày"
+stopActivityDelivery: "Ngưng gửi hoạt động"
+blockThisInstance: "Chặn máy chủ này"
+operations: "Vận hành"
+software: "Phần má»m"
+version: "Phiên bản"
+metadata: "Metadata"
+withNFiles: "{n} tập tin"
+monitor: "Giám sát"
+jobQueue: "Công việc chỠxử lý"
cpuAndMemory: "CPU và Dung lượng"
+network: "Mạng"
+disk: "Ổ đĩa"
+instanceInfo: "Thông tin máy chủ"
+statistics: "Thống kê"
+clearQueue: "Xóa hàng đợi"
+clearQueueConfirmTitle: "Bạn có chắc muốn xóa hàng đợi?"
+clearQueueConfirmText: "Má»i tút chưa được gá»­i còn lại trong hàng đợi sẽ không được liên hợp. Thông thưá»ng thao tác này không cần thiết."
+clearCachedFiles: "Xóa bộ nhớ đệm"
+clearCachedFilesConfirm: "Bạn có chắc muốn xóa sạch bộ nhớ đệm?"
+blockedInstances: "Máy chủ đã chặn"
+blockedInstancesDescription: "Danh sách những máy chủ bạn muốn chặn. Chúng sẽ không thể giao tiếp với máy chủy này nữa."
+muteAndBlock: "Ẩn và Chặn"
+mutedUsers: "Ngưá»i đã ẩn"
+blockedUsers: "Ngưá»i đã chặn"
+noUsers: "Chưa có ai"
+editProfile: "Sửa hồ sơ"
+noteDeleteConfirm: "Bạn có chắc muốn xóa tút này?"
+pinLimitExceeded: "Bạn đã đạt giới hạn số lượng tút có thể ghim"
+intro: "Äã cài đặt Misskey! Xin hãy tạo tài khoản admin."
+done: "Xong"
+processing: "Äang xá»­ lý"
+preview: "Xem trước"
+default: "Mặc định"
+noCustomEmojis: "Không có emoji"
+noJobs: "Không có công việc"
+federating: "Äang liên hợp"
+blocked: "Äã chặn"
+suspended: "Äã vô hiệu hóa"
+all: "Tất cả"
+subscribing: "Äang đăng ký"
+publishing: "Äang đăng"
+notResponding: "Không có phản hồi"
+instanceFollowing: "Äang theo dõi máy chá»§"
+instanceFollowers: "Ngưá»i theo dõi cá»§a máy chá»§"
+instanceUsers: "Ngưá»i dùng trên máy chá»§ này"
+changePassword: "Äổi mật khẩu"
+security: "Bảo mật"
+retypedNotMatch: "Mật khẩu không trùng khớp."
+currentPassword: "Mật khẩu hiện tại"
+newPassword: "Mật khẩu mới"
+newPasswordRetype: "Nhập lại mật khẩu mới"
+attachFile: "Äính kèm tập tin"
+more: "Thêm nữa!"
+featured: "Nổi bật"
+usernameOrUserId: "Tên ngưá»i dùng hoặc ID"
+noSuchUser: "Không tìm thấy ngưá»i dùng"
+lookup: "Tìm kiếm"
+announcements: "Thông báo"
+imageUrl: "URL ảnh"
+remove: "Xóa"
+removed: "Äã xóa"
+removeAreYouSure: "Bạn có chắc muốn gỡ \"{x}\"?"
+deleteAreYouSure: "Bạn có chắc muốn xóa \"{x}\"?"
+resetAreYouSure: "Bạn có chắc muốn đặt lại?"
+saved: "Äã lưu"
+messaging: "Trò chuyện"
+upload: "Tải lên"
+keepOriginalUploading: "Giữ hình ảnh gốc"
+keepOriginalUploadingDescription: "Giữ nguyên như hình ảnh được tải lên ban đầu. Nếu tắt, một phiên bản để hiển thị trên web sẽ được tạo khi tải lên."
+fromDrive: "Từ ổ đĩa"
+fromUrl: "Từ URL"
+uploadFromUrl: "Tải lên bằng một URL"
+uploadFromUrlDescription: "URL của tập tin bạn muốn tải lên"
+uploadFromUrlRequested: "Äã yêu cầu tải lên"
+uploadFromUrlMayTakeTime: "Sẽ mất má»™t khoảng thá»i gian để tải lên xong."
+explore: "Khám phá"
+messageRead: "Äã Ä‘á»c"
+noMoreHistory: "Không còn gì để Ä‘á»c"
+startMessaging: "Bắt đầu trò chuyện"
+nUsersRead: "Ä‘á»c bởi {n}"
+agreeTo: "Tôi đồng ý {0}"
+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"
+birthday: "Sinh nhật"
+yearsOld: "{age} tuổi"
+registeredDate: "Tham gia"
+location: "Äến từ"
+theme: "Chá»§ Ä‘á»"
+themeForLightMode: "Chủ đỠdùng trong trong chế độ Sáng"
+themeForDarkMode: "Chủ đỠdùng trong chế độ Tối"
+light: "Sáng"
+dark: "Tối"
+lightThemes: "Những chủ đỠsáng"
+darkThemes: "Những chủ đỠtối"
+syncDeviceDarkMode: "Äồng bá»™ vá»›i thiết bị"
+drive: "Ổ đĩa"
+fileName: "Tên tập tin"
+selectFile: "Chá»n tập tin"
+selectFiles: "Chá»n nhiá»u tập tin"
+selectFolder: "Chá»n thư mục"
+selectFolders: "Chá»n nhiá»u thư mục"
+renameFile: "Äổi tên tập tin"
+folderName: "Tên thư mục"
+createFolder: "Tạo thư mục"
+renameFolder: "Äổi tên thư mục"
+deleteFolder: "Xóa thư mục"
+addFile: "Thêm tập tin"
+emptyDrive: "Ổ đĩa của bạn trống trơn"
+emptyFolder: "Thư mục trống"
+unableToDelete: "Không thể xóa"
+inputNewFileName: "Nhập tên mới cho tập tin"
+inputNewDescription: "Nhập mô tả mới"
+inputNewFolderName: "Nhập tên mới cho thư mục"
+circularReferenceFolder: "Thư mục đích là một thư mục con của thư mục bạn muốn di chuyển."
+hasChildFilesOrFolders: "Không thể xóa cho đến khi không còn gì trong thư mục."
+copyUrl: "Sao chép URL"
+rename: "Äổi tên"
+avatar: "Ảnh đại diện"
+banner: "Ảnh bìa"
+nsfw: "Nhạy cảm"
+whenServerDisconnected: "Khi mất kết nối tới máy chủ"
+disconnectedFromServer: "Mất kết nối tới máy chủ"
+reload: "Tải lại"
+doNothing: "Bá» qua"
+reloadConfirm: "Bạn có muốn thử tải lại bảng tin?"
+watch: "Xem"
+unwatch: "Ngừng xem"
+accept: "Äồng ý"
+reject: "Từ chối"
+normal: "Bình thưá»ng"
+instanceName: "Tên máy chủ"
+instanceDescription: "Mô tả máy chủ"
+maintainerName: "Äá»™i ngÅ© vận hành"
+maintainerEmail: "Email đội ngũ"
+tosUrl: "URL Äiá»u khoản dịch vụ"
+thisYear: "Năm"
+thisMonth: "Tháng"
+today: "Hôm nay"
dayX: "{day}"
+monthX: "{month}"
yearX: "{year}"
+pages: "Trang"
+integration: "Tương tác"
+connectService: "Kết nối"
+disconnectService: "Ngắt kết nối"
+enableLocalTimeline: "Bật bảng tin máy chủ"
+enableGlobalTimeline: "Bật bảng tin liên hợp"
+disablingTimelinesInfo: "Quản trị viên và Kiểm duyệt viên luôn có quyá»n truy cập má»i bảng tin, kể cả khi chúng không được bật."
+registration: "Äăng ký"
+enableRegistration: "Cho phép đăng ký mới"
+invite: "Má»i"
+driveCapacityPerLocalAccount: "Dung lượng ổ đĩa tối Ä‘a cho má»—i ngưá»i dùng"
+driveCapacityPerRemoteAccount: "Dung lượng ổ đĩa tối Ä‘a cho má»—i ngưá»i dùng từ xa"
+inMb: "Tính bằng MB"
+iconUrl: "URL Icon"
+bannerUrl: "URL Ảnh bìa"
+backgroundImageUrl: "URL Ảnh ná»n"
+basicInfo: "Thông tin cơ bản"
+pinnedUsers: "Những ngưá»i thú vị"
+pinnedUsersDescription: "Liệt kê má»—i hàng má»™t tên ngưá»i dùng xuống dòng để ghim trên tab \"Khám phá\"."
+pinnedPages: "Trang đã ghim"
+pinnedPagesDescription: "Liệt kê các trang thú vị để ghim trên máy chủ."
+pinnedClipId: "ID của clip muốn ghim"
+pinnedNotes: "Tút ghim"
+hcaptcha: "hCaptcha"
+enableHcaptcha: "Bật hCaptcha"
+hcaptchaSiteKey: "Khóa của trang"
+hcaptchaSecretKey: "Khóa bí mật"
+recaptcha: "reCAPTCHA"
+enableRecaptcha: "Bật reCAPTCHA"
+recaptchaSiteKey: "Khóa của trang"
+recaptchaSecretKey: "Khóa bí mật"
+avoidMultiCaptchaConfirm: "Dùng nhiá»u hệ thống Captcha có thể gây nhiá»…u giữa chúng. Bạn có muốn tắt các hệ thống Captcha khác hiện Ä‘ang hoạt động không? Nếu bạn muốn chúng tiếp tục được bật, hãy nhấn há»§y."
+antennas: "Trạm phát sóng"
+manageAntennas: "Quản lý trạm phát sóng"
+name: "Tên"
+antennaSource: "Nguồn trạm phát sóng"
+antennaKeywords: "Từ khóa để nghe"
+antennaExcludeKeywords: "Từ khóa để lá»c ra"
+antennaKeywordsDescription: "Phân cách bằng dấu cách cho Ä‘iá»u kiện AND hoặc bằng xuống dòng cho Ä‘iá»u kiện OR."
+notifyAntenna: "Thông báo có tút mới"
+withFileAntenna: "Chỉ những tút có media"
+enableServiceworker: "Bật ServiceWorker"
+antennaUsersDescription: "Liệt kê má»—i hàng má»™t tên ngưá»i dùng"
+caseSensitive: "Trưá»ng hợp nhạy cảm"
+withReplies: "Bao gồm lượt trả lá»i"
+connectedTo: "Những tài khoản sau đã kết nối"
+notesAndReplies: "Tút kèm trả lá»i"
+withFiles: "Media"
+silence: "Ẩn"
+silenceConfirm: "Bạn có chắc muốn ẩn ngưá»i này?"
+unsilence: "BỠẩn"
+unsilenceConfirm: "Bạn có chắc muốn bỠẩn ngưá»i này?"
+popularUsers: "Những ngưá»i nổi tiếng"
+recentlyUpdatedUsers: "Hoạt động gần đây"
+recentlyRegisteredUsers: "Má»›i tham gia"
+recentlyDiscoveredUsers: "Mới khám phá"
+exploreUsersCount: "Có {count} ngưá»i"
+exploreFediverse: "Khám phá Fediverse"
+popularTags: "Hashtag thông dụng"
+userList: "Danh sách"
+about: "Giới thiệu"
aboutMisskey: "Vá» Misskey"
+administrator: "Quản trị viên"
+token: "Token"
+twoStepAuthentication: "Xác minh 2 bước"
+moderator: "Kiểm duyệt viên"
+nUsersMentioned: "Dùng bởi {n} ngưá»i"
+securityKey: "Khóa bảo mật"
+securityKeyName: "Tên khoá"
+registerSecurityKey: "Äăng ký khóa bảo mật"
+lastUsed: "Dùng lần cuối"
+unregister: "Hủy đăng ký"
+passwordLessLogin: "Äăng nhập không mật khẩu"
+resetPassword: "Äặt lại mật khẩu"
+newPasswordIs: "Mật khẩu mới là \"{password}\""
+reduceUiAnimation: "Giảm chuyển động UI"
+share: "Chia sẻ"
+notFound: "Không tìm thấy"
+notFoundDescription: "Không tìm thấy trang nào tương ứng với URL này."
+uploadFolder: "Thư mục tải lên mặc định"
+cacheClear: "Xóa bộ nhớ đệm"
+markAsReadAllNotifications: "Äánh dấu tất cả các thông báo là đã Ä‘á»c"
+markAsReadAllUnreadNotes: "Äánh dấu tất cả các tút là đã Ä‘á»c"
+markAsReadAllTalkMessages: "Äánh dấu tất cả các tin nhắn là đã Ä‘á»c"
+help: "Trợ giúp"
+inputMessageHere: "Nhập nội dung tin nhắn"
+close: "Äóng"
+group: "Nhóm"
+groups: "Các nhóm"
+createGroup: "Tạo nhóm"
+ownedGroups: "Nhóm tôi quản lý"
+joinedGroups: "Nhóm tôi tham gia"
+invites: "Má»i"
+groupName: "Tên nhóm"
+members: "Thành viên"
+transfer: "Chuyển giao"
+messagingWithUser: "Nhắn riêng"
+messagingWithGroup: "Chat nhóm"
+title: "Tá»±a Ä‘á»"
+text: "Ná»™i dung"
+enable: "Bật"
+next: "Kế tiếp"
+retype: "Nhập lại"
+noteOf: "Tút của {user}"
+inviteToGroup: "Má»i vào nhóm"
+quoteAttached: "Trích dẫn"
+quoteQuestion: "Trích dẫn lại?"
+noMessagesYet: "Chưa có tin nhắn"
+newMessageExists: "Bạn có tin nhắn mới"
+onlyOneFileCanBeAttached: "Bạn chỉ có thể đính kèm một tập tin"
+signinRequired: "Vui lòng đăng nhập"
+invitations: "Má»i"
+invitationCode: "Mã má»i"
+checking: "Äang kiểm tra..."
+available: "Khả dụng"
+unavailable: "Không khả dụng"
+usernameInvalidFormat: "Bạn có thể dùng viết hoa/viết thưá»ng, chữ số, và dấu gạch dưới."
+tooShort: "Quá ngắn"
+tooLong: "Quá dài"
+weakPassword: "Mật khẩu yếu"
+normalPassword: "Mật khẩu tạm được"
+strongPassword: "Mật khẩu mạnh"
+passwordMatched: "Trùng khớp"
+passwordNotMatched: "Không trùng khớp"
+signinWith: "Äăng nhập bằng {x}"
+signinFailed: "Không thể đăng nhập. Vui lòng kiểm tra tên ngưá»i dùng và mật khẩu cá»§a bạn."
+tapSecurityKey: "Nhấn mã bảo mật của bạn"
+or: "Hoặc"
+language: "Ngôn ngữ"
+uiLanguage: "Ngôn ngữ giao diện"
+groupInvited: "Bạn đã được má»i tham gia nhóm"
+aboutX: "Giới thiệu {x}"
+useOsNativeEmojis: "Dùng emoji hệ thống"
+disableDrawer: "Không dùng menu thanh bên"
+youHaveNoGroups: "Không có nhóm nào"
+joinOrCreateGroup: "Tham gia hoặc tạo một nhóm mới."
+noHistory: "Không có dữ liệu"
+signinHistory: "Lịch sử đăng nhập"
+disableAnimatedMfm: "Tắt MFM với chuyển động"
+doing: "Äang xá»­ lý..."
+category: "Phân loại"
+tags: "Thẻ"
+docSource: "Nguồn tài liệu"
+createAccount: "Tạo tài khoản"
+existingAccount: "Tài khoản hiện có"
+regenerate: "Tạo lại"
+fontSize: "Cỡ chữ"
+noFollowRequests: "Bạn không có yêu cầu theo dõi nào"
+openImageInNewTab: "Mở ảnh trong tab mới"
+dashboard: "Trang chính"
+local: "Máy chủ này"
+remote: "Máy chủ khác"
+total: "Tổng cộng"
+weekOverWeekChanges: "Thay đổi tuần rồi"
+dayOverDayChanges: "Thay đổi hôm qua"
+appearance: "Giao diện"
+clientSettings: "Cài đặt Client"
+accountSettings: "Cài đặt tài khoản"
+promotion: "Quảng cáo"
+promote: "Quảng cáo"
+numberOfDays: "Số ngày"
+hideThisNote: "Ẩn tút này"
+showFeaturedNotesInTimeline: "Hiện tút nổi bật trong bảng tin"
+objectStorage: "Äối tượng lưu trữ"
+useObjectStorage: "Dùng đối tượng lưu trữ"
+objectStorageBaseUrl: "Base URL"
+objectStorageBaseUrlDesc: "URL được sử dụng làm tham khảo. Chỉ định URL của CDN hoặc Proxy của bạn nếu bạn đang sử dụng. Với S3 dùng 'https://<bucket>.s3.amazonaws.com', còn GCS hoặc dịch vụ tương tự dùng 'https://storage.googleapis.com/<bucket>', etc."
+objectStorageBucket: "Bucket"
+objectStorageBucketDesc: "Nhập tên bucket dùng ở nhà cung cấp của bạn."
+objectStoragePrefix: "Tiá»n tố"
+objectStoragePrefixDesc: "Các tập tin sẽ được lưu trữ trong các thư mục có tiá»n tố này."
+objectStorageEndpoint: "Äầu cuối"
+objectStorageEndpointDesc: "Äể trống nếu bạn Ä‘ang dùng AWS S3, nếu không thì chỉ định đầu cuối là '<host>' hoặc '<host>:<port>', tùy thuá»™c vào nhà cung cấp dịch vụ."
+objectStorageRegion: "Khu vá»±c"
+objectStorageRegionDesc: "Nhập một khu vực cụ thể như 'xx-east-1'. Nếu nhà cung cấp dịch vụ của bạn không phân biệt giữa các khu vực, hãy để trống hoặc nhập 'us-east-1'."
+objectStorageUseSSL: "Dùng SSL"
+objectStorageUseSSLDesc: "Tắt nếu bạn không dùng HTTPS để kết nối API"
+objectStorageUseProxy: "Kết nối thông qua Proxy"
+objectStorageUseProxyDesc: "Tắt nếu bạn không dùng Proxy để kết nối API"
+objectStorageSetPublicRead: "Äặt \"public-read\" khi tải lên"
+serverLogs: "Nhật ký máy chủ"
+deleteAll: "Xóa tất cả"
+showFixedPostForm: "Hiện khung soạn tút ở phía trên bảng tin"
+newNoteRecived: "Äã nhận tút má»›i"
+sounds: "Âm thanh"
+listen: "Nghe"
+none: "Không"
+showInPage: "Hiện trong trang"
+popout: "Pop-out"
+volume: "Âm lượng"
+masterVolume: "Âm thanh chung"
+details: "Chi tiết"
+chooseEmoji: "Chá»n emoji"
+unableToProcess: "Không thể hoàn tất hành động"
+recentUsed: "Sử dụng gần đây"
+install: "Cài đặt"
+uninstall: "Gỡ bá»"
+installedApps: "Ứng dụng đã cài đặt"
+nothing: "Không có gì ở đây"
+installedDate: "Cho phép vào"
+lastUsedDate: "Dùng gần nhất"
+state: "Trạng thái"
+sort: "Sắp xếp"
+ascendingOrder: "Tăng dần"
+descendingOrder: "Giảm dần"
+scratchpad: "Scratchpad"
+scratchpadDescription: "Scratchpad cung cấp môi trưá»ng cho các thá»­ nghiệm AiScript. Bạn có thể viết, thá»±c thi và kiểm tra kết quả tương tác vá»›i Misskey trong đó."
+output: "Nguồn ra"
+script: "Kịch bản"
+disablePagesScript: "Tắt AiScript trên Trang"
+updateRemoteUser: "Cập nhật thông tin ngưá»i dùng ở máy chá»§ khác"
+deleteAllFiles: "Xóa toàn bộ tập tin"
+deleteAllFilesConfirm: "Bạn có chắc xóa toàn bộ tập tin?"
+removeAllFollowing: "Ngưng theo dõi tất cả má»i ngưá»i"
+removeAllFollowingDescription: "Thá»±c hiện Ä‘iá»u này sẽ ngưng theo dõi tất cả các tài khoản khá»i {host}. Chỉ thá»±c hiện Ä‘iá»u này nếu máy chá»§ không còn tồn tại."
+userSuspended: "Ngưá»i này đã bị vô hiệu hóa."
+userSilenced: "Ngưá»i này đã bị ẩn"
+yourAccountSuspendedTitle: "Tài khoản bị vô hiệu hóa"
+yourAccountSuspendedDescription: "Tài khoản này đã bị vô hiệu hóa do vi phạm quy tắc máy chá»§ hoặc Ä‘iá»u tương tá»±. Liên hệ vá»›i quản trị viên nếu bạn muốn biết lý do chi tiết hÆ¡n. Vui lòng không tạo tài khoản má»›i."
+menu: "Menu"
+divider: "Phân chia"
+addItem: "Thêm mục"
+relays: "Chuyển tiếp"
+addRelay: "Thêm chuyển tiếp"
+inboxUrl: "URL Hộp thư đến"
+addedRelays: "Äã thêm các chuyển tiếp"
+serviceworkerInfo: "Phải được bật cho thông báo đẩy."
+deletedNote: "Tút đã bị xóa"
+invisibleNote: "Tút ẩn"
+enableInfiniteScroll: "Tự động tải tút mới"
+visibility: "Hiển thị"
+poll: "Bình chá»n"
+useCw: "Ẩn nội dung"
+enablePlayer: "Mở trình phát video"
+disablePlayer: "Äóng trình phát video"
+expandTweet: "Mở rộng tweet"
+themeEditor: "Công cụ thiết kế theme"
+description: "Mô tả"
+describeFile: "Thêm mô tả"
+enterFileDescription: "Nhập mô tả"
+author: "Tác giả"
+leaveConfirm: "Có những thay đổi chưa được lưu. Bạn có muốn bỠchúng không?"
+manage: "Quản lý"
+plugins: "Plugin"
+deck: "Deck"
+undeck: "Bá» Deck"
+useBlurEffectForModal: "Sử dụng hiệu ứng mỠcho các hộp thoại"
+useFullReactionPicker: "Dùng bá»™ chá»n biểu cảm cỡ lá»›n"
+width: "Chiá»u rá»™ng"
+height: "Chiá»u cao"
+large: "Lá»›n"
+medium: "Vừa"
+small: "Nhá»"
+generateAccessToken: "Tạo mã truy cập"
+permission: "Cho phép "
+enableAll: "Bật toàn bộ"
+disableAll: "Tắt toàn bộ"
+tokenRequested: "Cấp quyá»n truy cập vào tài khoản"
+pluginTokenRequestedDescription: "Plugin này sẽ có thể sá»­ dụng các quyá»n được đặt ở đây."
+notificationType: "Loại thông báo"
+edit: "Sá»­a"
+useStarForReactionFallback: "Dùng ★ nếu emoji biểu cảm không có"
+emailServer: "Email máy chủ"
+enableEmail: "Bật phân phối email"
+emailConfigInfo: "ÄÆ°á»£c dùng để xác minh email cá»§a bạn lúc đăng ký hoặc nếu bạn quên mật khẩu cá»§a mình"
+email: "Email"
+emailAddress: "Äịa chỉ email"
+smtpConfig: "Cấu hình máy chủ SMTP"
+smtpHost: "Host"
+smtpPort: "Cổng"
smtpUser: "Tên ngưá»i dùng"
smtpPass: "Mật khẩu"
+emptyToDisableSmtpAuth: "Äể trống tên ngưá»i dùng và mật khẩu để tắt xác thá»±c SMTP"
+smtpSecure: "Dùng SSL/TLS ngầm định cho các kết nối SMTP"
+smtpSecureInfo: "Tắt cái này nếu dùng STARTTLS"
+testEmail: "Kiểm tra vận chuyển email"
+wordMute: "Ẩn chữ"
+regexpError: "Lỗi biểu thức"
+regexpErrorDescription: "Xảy ra lỗi biểu thức ở dòng {line} của {tab} chữ ẩn:"
+instanceMute: "Những máy chủ ẩn"
+userSaysSomething: "{name} nói gì đó"
+makeActive: "Kích hoạt"
+display: "Hiển thị"
+copy: "Sao chép"
+metrics: "Số liệu"
+overview: "Tổng quan"
+logs: "Nhật ký"
+delayed: "Äá»™ trá»…"
+database: "Cơ sở dữ liệu"
+channel: "Kênh"
+create: "Tạo"
+notificationSetting: "Cài đặt thông báo"
+notificationSettingDesc: "Chá»n loại thông báo bạn muốn hiển thị."
+useGlobalSetting: "Dùng thiết lập chung"
+useGlobalSettingDesc: "Nếu được bật, cài đặt thông báo của bạn sẽ được áp dụng. Nếu bị tắt, có thể thực hiện các thiết lập riêng lẻ."
+other: "Khác"
+regenerateLoginToken: "Tạo lại mã đăng nhập"
+regenerateLoginTokenDescription: "Tạo lại mã ná»™i bá»™ có thể dùng để đăng nhập. Thông thưá»ng hành động này là không cần thiết. Nếu được tạo lại, tất cả các thiết bị sẽ bị đăng xuất."
+setMultipleBySeparatingWithSpace: "Tách nhiá»u mục nhập bằng dấu cách."
+fileIdOrUrl: "ID tập tin hoặc URL"
+behavior: "Thao tác"
+sample: "Ví dụ"
+abuseReports: "Lượt báo cáo"
+reportAbuse: "Báo cáo"
reportAbuseOf: "Báo cáo {name}"
+fillAbuseReportDescription: "Vui lòng Ä‘iá»n thông tin chi tiết vá» báo cáo này. Nếu đó là vá» má»™t tút cụ thể, hãy kèm theo URL cá»§a tút."
+abuseReported: "Báo cáo đã được gá»­i. Cảm Æ¡n bạn nhiá»u."
+reporter: "Ngưá»i báo cáo"
+reporteeOrigin: "Bị báo cáo"
+reporterOrigin: "Máy chá»§ ngưá»i báo cáo"
+forwardReport: "Chuyển tiếp báo cáo cho máy chủ từ xa"
+forwardReportIsAnonymous: "Thay vì tài khoản cá»§a bạn, má»™t tài khoản hệ thống ẩn danh sẽ được hiển thị dưới dạng ngưá»i báo cáo ở máy chá»§ từ xa."
+send: "Gá»­i"
+abuseMarkAsResolved: "Äánh dấu đã xá»­ lý"
+openInNewTab: "Mở trong tab mới"
+openInSideView: "Mở trong thanh bên"
+defaultNavigationBehaviour: "Thao tác Ä‘iá»u hướng mặc định"
+editTheseSettingsMayBreakAccount: "Việc chỉnh sá»­a các cài đặt này có thể làm há»ng tài khoản cá»§a bạn."
+instanceTicker: "Thông tin máy chủ của tút"
+waitingFor: "Äang đợi {x}"
+random: "Ngẫu nhiên"
+system: "Hệ thống"
+switchUi: "Chuyển đổi giao diện ngưá»i dùng"
+desktop: "Desktop"
+clip: "Ghim"
+createNew: "Tạo mới"
+optional: "Không bắt buộc"
+createNewClip: "Tạo một ghim mới"
+public: "Công khai"
+i18nInfo: "Misskey Ä‘ang được các tình nguyện viên dịch sang nhiá»u thứ tiếng khác nhau. Bạn có thể há»— trợ tại {link}."
+manageAccessTokens: "Tạo mã truy cập"
+accountInfo: "Thông tin tài khoản"
+notesCount: "Số lượng tút"
+repliesCount: "Số lượt trả lá»i đã gá»­i"
+renotesCount: "Số lượt đăng lại đã gửi"
+repliedCount: "Số lượt trả lá»i đã nhận"
renotedCount: "Lượt chia sẻ"
+followingCount: "Số lượng ngưá»i tôi theo dõi"
+followersCount: "Số lượng ngưá»i theo dõi tôi"
+sentReactionsCount: "Số lượng biểu cảm đã gửi"
+receivedReactionsCount: "Số lượng biểu cảm đã nhận"
+pollVotesCount: "Số lượng bình chá»n đã gá»­i"
+pollVotedCount: "Số lượng bình chá»n đã nhận"
+yes: "Äồng ý"
+no: "Từ chối"
+driveFilesCount: "Số tập tin trong Ổ đĩa"
+driveUsage: "Dung lượng ổ đĩa"
+noCrawle: "Từ chối lập chỉ mục"
+noCrawleDescription: "Không cho công cụ tìm kiếm lập chỉ mục trang hồ sơ, tút, Trang, etc."
+lockedAccountInfo: "Ghi chú cá»§a bạn sẽ hiển thị vá»›i bất kỳ ai, trừ khi bạn đặt chế độ hiển thị tút cá»§a mình thành \"Chỉ ngưá»i theo dõi\"."
+alwaysMarkSensitive: "Luôn đánh dấu NSFW"
+loadRawImages: "Tải ảnh gốc thay vì ảnh thu nhá»"
+disableShowingAnimatedImages: "Không phát ảnh động"
+verificationEmailSent: "Một email xác minh đã được gửi. Vui lòng nhấn vào liên kết đính kèm để hoàn tất xác minh."
+notSet: "Chưa đặt"
+emailVerified: "Email đã được xác minh"
+noteFavoritesCount: "Số lượng tút yêu thích"
+pageLikesCount: "Số lượng trang đã thích"
+pageLikedCount: "Số lượng thích trang đã nhận"
+contact: "Liên hệ"
+useSystemFont: "Dùng phông chữ mặc định của hệ thống"
+clips: "Ghim"
+experimentalFeatures: "Tính năng thử nghiệm"
+developer: "Nhà phát triển"
+makeExplorable: "Không hiện tôi trong \"Khám phá\""
+makeExplorableDescription: "Nếu bạn tắt, tài khoản của bạn sẽ không hiện trong mục \"Khám phá\"."
+showGapBetweenNotesInTimeline: "Hiện dải phân cách giữa các tút trên bảng tin"
+duplicate: "Tạo bản sao"
+left: "Bên traÌi"
+center: "Giữa"
+wide: "Rá»™ng"
+narrow: "Thu hẹp"
+reloadToApplySetting: "Cài đặt này sẽ chỉ áp dụng sau khi tải lại trang. Tải lại ngay bây gi�"
+needReloadToApply: "Cần tải lại để Ä‘iá»u này được áp dụng."
+showTitlebar: "Hiện thanh tá»±a Ä‘á»"
+clearCache: "Xóa bộ nhớ đệm"
+onlineUsersCount: "{n} ngưá»i Ä‘ang online"
+nUsers: "{n} Ngưá»i"
+nNotes: "{n} Tút"
+sendErrorReports: "Báo lỗi"
+sendErrorReportsDescription: "Khi được bật, thông tin chi tiết vá» lá»—i sẽ được chia sẻ vá»›i Misskey khi xảy ra sá»± cố, giúp nâng cao chất lượng cá»§a Misskey.\nBao gồm thông tin như phiên bản hệ Ä‘iá»u hành cá»§a bạn, trình duyệt bạn Ä‘ang sá»­ dụng, hoạt động cá»§a bạn trong Misskey, v.v."
+myTheme: "Theme của tôi"
+backgroundColor: "Màu ná»n"
+accentColor: "Màu phụ"
+textColor: "Màu chữ"
+saveAs: "Lưu thành"
+advanced: "Nâng cao"
+value: "Giá trị"
+createdAt: "Ngày tạo"
+updatedAt: "Cập nhật lúc"
+saveConfirm: "Lưu thay đổi?"
+deleteConfirm: "Bạn có muốn xóa không?"
+invalidValue: "Giá trị không hợp lệ."
+registry: "Registry"
+closeAccount: "Äóng tài khoản"
+currentVersion: "Phiên bản hiện tại"
+latestVersion: "Phiên bản mới nhất"
+youAreRunningUpToDateClient: "Bạn đang sử dụng phiên bản mới nhất."
+newVersionOfClientAvailable: "Có phiên bản mới cho bạn cập nhật."
+usageAmount: "Sử dụng"
+capacity: "Sức chứa"
+inUse: "Äã dùng"
+editCode: "Chỉnh sửa mã"
+apply: "Ãp dụng"
+receiveAnnouncementFromInstance: "Nhận thông báo từ máy chủ này"
+emailNotification: "Thông báo email"
+publish: "Äăng"
+inChannelSearch: "Tìm trong kênh"
+useReactionPickerForContextMenu: "Nhấn chuá»™t phải để mở bá»™ chá»n biểu cảm"
+typingUsers: "{users} đang nhập…"
+jumpToSpecifiedDate: "Äến má»™t ngày cụ thể"
+showingPastTimeline: "Hiện Ä‘ang hiển thị dòng thá»i gian cÅ©"
+clear: "Hoàn lại"
+markAllAsRead: "Äánh dấu tất cả đã Ä‘á»c"
+goBack: "Quay lại"
+unlikeConfirm: "Bạn có chắc muốn bỠthích ?"
+fullView: "Kích thước đầy đủ"
+quitFullView: "Thoát toàn màn hình"
+addDescription: "Thêm mô tả"
+userPagePinTip: "Bạn có thể hiển thị các tút ở đây bằng cách chá»n \"Ghim vào hồ sÆ¡\" từ menu cá»§a má»—i tút."
+notSpecifiedMentionWarning: "Tút này có đỠcập đến những ngưá»i không mong muốn"
+info: "Giới thiệu"
+userInfo: "Thông tin ngưá»i dùng"
+unknown: "Chưa biết"
+onlineStatus: "Trạng thái"
+hideOnlineStatus: "Ẩn trạng thái online"
+hideOnlineStatusDescription: "Ẩn trạng thái online của bạn làm giảm sự tiện lợi của một số tính năng như tìm kiếm."
+online: "Online"
+active: "Hoạt động"
+offline: "Offline"
+notRecommended: "Không đỠxuất"
+botProtection: "Bảo vệ Bot"
+instanceBlocking: "Máy chủ đã chặn"
+selectAccount: "Chá»n má»™t tài khoản"
+switchAccount: "Chuyển tài khoản"
+enabled: "Äã bật"
+disabled: "Äã tắt"
+quickAction: "Thao tác nhanh"
+user: "Ngưá»i dùng"
+administration: "Quản lý"
+accounts: "Tài khoản của bạn"
+switch: "Chuyển đổi"
+noMaintainerInformationWarning: "Chưa thiết lập thông tin vận hành."
+noBotProtectionWarning: "Bảo vệ Bot chưa thiết lập."
+configure: "Thiết lập"
+postToGallery: "Tạo tút có ảnh"
+gallery: "Thư viện ảnh"
+recentPosts: "Tút gần đây"
+popularPosts: "Tút được xem nhiá»u nhất"
+shareWithNote: "Chia sẻ kèm với tút"
+ads: "Quảng cáo"
+expiration: "Thá»i hạn"
+memo: "Lưu ý"
+priority: "Ưu tiên"
+high: "Cao"
+middle: "Vừa"
+low: "Thấp"
+emailNotConfiguredWarning: "Chưa đặt địa chỉ email."
+ratio: "Tỷ lệ"
+previewNoteText: "Hiện xem trước"
+customCss: "Tùy chỉnh CSS"
+customCssWarn: "Chỉ sá»­ dụng những cài đặt này nếu bạn biết rõ vá» nó. Việc nhập các giá trị không đúng có thể khiến máy chá»§ hoạt động không bình thưá»ng."
+global: "Toàn cầu"
+squareAvatars: "Ảnh đại diện vuông"
+sent: "Gá»­i"
+received: "Äã nhận"
+searchResult: "Kết quả tìm kiếm"
+hashtags: "Hashtag"
+troubleshooting: "Khắc phục sự cố"
+useBlurEffect: "Dùng hiệu ứng làm mỠtrong giao diện"
+learnMore: "Tìm hiểu thêm"
+misskeyUpdated: "Misskey vừa được cập nhật!"
+whatIsNew: "Hiện những thay đổi"
+translate: "Dịch"
translatedFrom: "Dịch từ {x}"
+accountDeletionInProgress: "Äang xá»­ lý việc xóa tài khoản"
+usernameInfo: "Bạn có thể sá»­ dụng chữ cái (a ~ z, A ~ Z), chữ số (0 ~ 9) hoặc dấu gạch dưới (_). Tên ngưá»i dùng không thể thay đổi sau này."
+aiChanMode: "Chế độ Ai"
+keepCw: "Giữ cảnh báo nội dung"
+pubSub: "Tài khoản Chính/Phụ"
+lastCommunication: "Lần giao tiếp cuối"
+resolved: "Äã xá»­ lý"
+unresolved: "ChỠxử lý"
+breakFollow: "Xóa ngưá»i theo dõi"
+itsOn: "Äã bật"
+itsOff: "Äã tắt"
+emailRequiredForSignup: "Yêu cầu địa chỉ email khi đăng ký"
+unread: "Chưa Ä‘á»c"
+filter: "Bá»™ lá»c"
+controlPanel: "Bảng Ä‘iá»u khiển"
+manageAccounts: "Quản lý tài khoản"
+makeReactionsPublic: "Äặt lịch sá»­ biểu cảm công khai"
+makeReactionsPublicDescription: "Äiá»u này sẽ hiển thị công khai danh sách tất cả các biểu cảm trước đây cá»§a bạn."
+classic: "Cổ điển"
+muteThread: "Không quan tâm nữa"
+unmuteThread: "Quan tâm tút này"
+ffVisibility: "Hiển thị Theo dõi/Ngưá»i theo dõi"
+ffVisibilityDescription: "Quyết định ai có thể xem những ngưá»i bạn theo dõi và những ngưá»i theo dõi bạn."
+continueThread: "Tiếp tục xem chuỗi tút"
+deleteAccountConfirm: "Äiá»u này sẽ khiến tài khoản bị xóa vÄ©nh viá»…n. Vẫn tiếp tục?"
+incorrectPassword: "Sai mật khẩu."
+voteConfirm: "Xác nhận bình chá»n \"{choice}\"?"
+hide: "Ẩn"
+leaveGroup: "Rá»i khá»i nhóm"
+leaveGroupConfirm: "Bạn có chắc muốn rá»i khá»i nhóm \"{name}\"?"
+useDrawerReactionPickerForMobile: "Hiện bá»™ chá»n biểu cảm dạng xổ ra trên Ä‘iện thoại"
+welcomeBackWithName: "Chào mừng trở lại, {name}"
+clickToFinishEmailVerification: "Vui lòng nhấn [{ok}] để hoàn tất việc đăng ký."
+overridedDeviceKind: "Loại thiết bị"
+smartphone: "Äiện thoại"
+tablet: "Máy tính bảng"
+auto: "Tự động"
+themeColor: "Màu theme"
+size: "Kích thước"
+numberOfColumn: "Số lượng cột"
searchByGoogle: "Google"
+instanceDefaultLightTheme: "Theme máy chủ Sáng-Rộng"
+instanceDefaultDarkTheme: "Theme máy chủ Tối-Rộng"
+instanceDefaultThemeDescription: "Nhập mã theme trong định dạng đối tượng."
+mutePeriod: "Thá»i hạn ẩn"
+indefinitely: "Vĩnh viễn"
+tenMinutes: "10 phút"
+oneHour: "1 giá»"
+oneDay: "1 ngày"
+oneWeek: "1 tuần"
+reflectMayTakeTime: "Có thể mất má»™t thá»i gian để Ä‘iá»u này được áp dụng."
+failedToFetchAccountInformation: "Không thể lấy thông tin tài khoản"
+rateLimitExceeded: "Giới hạn quá mức"
+_emailUnavailable:
+ used: "Äịa chỉ email đã được sá»­ dụng"
+ format: "Äịa chỉ email không hợp lệ"
+ disposable: "Cấm sử dụng địa chỉ email dùng một lần"
+ mx: "Máy chủ email không hợp lệ"
+ smtp: "Máy chủ email không phản hồi"
+_ffVisibility:
+ public: "Äăng"
+ followers: "Chỉ ngưá»i theo dõi má»›i xem được"
+ private: "Riêng tư"
+_signup:
+ almostThere: "Gần xong rồi"
+ emailAddressInfo: "Hãy Ä‘iá»n địa chỉ email cá»§a bạn. Nó sẽ không được công khai."
+ emailSent: "Một email xác minh đã được gửi đến địa chỉ email ({email}) của bạn. Vui lòng nhấn vào liên kết trong đó để hoàn tất việc tạo tài khoản."
+_accountDelete:
+ accountDelete: "Xóa tài khoản"
+ mayTakeTime: "Vì xóa tài khoản là má»™t quá trình tốn nhiá»u tài nguyên nên có thể mất má»™t khoảng thá»i gian để hoàn thành, tùy thuá»™c vào lượng ná»™i dung bạn đã tạo và số lượng tập tin bạn đã tải lên."
+ sendEmail: "Sau khi hoàn tất việc xóa tài khoản, một email sẽ được gửi đến địa chỉ email đã đăng ký tài khoản này."
+ requestAccountDelete: "Yêu cầu xóa tài khoản"
+ started: "Äang bắt đầu xóa tài khoản."
+ inProgress: "Äang xóa dần tài khoản."
+_ad:
+ back: "Quay lại"
+ reduceFrequencyOfThisAd: "Hiện ít lại"
+_forgotPassword:
+ enterEmail: "Nhập địa chỉ email bạn đã sử dụng để đăng ký. Một liên kết mà bạn có thể đặt lại mật khẩu của mình sau đó sẽ được gửi đến nó."
+ ifNoEmail: "Nếu bạn không sử dụng email lúc đăng ký, vui lòng liên hệ với quản trị viên."
+ contactAdmin: "Máy chủ này không hỗ trợ sử dụng địa chỉ email, vui lòng liên hệ với quản trị viên để đặt lại mật khẩu của bạn."
+_gallery:
+ my: "Kho Ảnh"
+ liked: "Tút Äã Thích"
+ like: "Thích"
+ unlike: "BỠthích"
+_email:
+ _follow:
+ title: "đã theo dõi bạn"
+ _receiveFollowRequest:
+ title: "Chấp nhận yêu cầu theo dõi"
+_plugin:
+ install: "Cài đặt tiện ích"
+ installWarn: "Vui lòng không cài đặt những tiện ích đáng ngá»."
+ manage: "Quản lý plugin"
+_registry:
+ scope: "Phạm vi"
+ key: "Mã"
+ keys: "Các mã"
+ domain: "Tên miá»n"
+ createKey: "Tạo mã"
+_aboutMisskey:
+ about: "Misskey là phần má»m mã nguồn mở được phát triển bởi syuilo từ năm 2014."
+ contributors: "Những ngưá»i đóng góp nổi bật"
+ allContributors: "Toàn bá»™ ngưá»i đóng góp"
+ source: "Mã nguồn"
+ translation: "Dịch Misskey"
+ donate: "Ủng hộ Misskey"
+ morePatrons: "Chúng tôi cÅ©ng trân trá»ng sá»± há»— trợ cá»§a nhiá»u ngưá»i đóng góp khác không được liệt kê ở đây. Cảm Æ¡n! 🥰"
+ patrons: "Ngưá»i á»§ng há»™"
+_nsfw:
+ respect: "Ẩn nội dung NSFW"
+ ignore: "Hiện nội dung NSFW"
+ force: "Ẩn má»i media"
_mfm:
+ cheatSheet: "MFM Cheatsheet"
+ intro: "MFM là ngôn ngữ phát triển độc quyá»n cá»§a Misskey có thể được sá»­ dụng ở nhiá»u nÆ¡i. Tại đây bạn có thể xem danh sách tất cả các cú pháp MFM có sẵn."
+ dummy: "Misskey mở rộng thế giới Fediverse"
+ mention: "Nhắc đến"
+ mentionDescription: "Bạn có thể nhắc đến ai đó bằng cách sá»­ dụng @tên ngưá»i dùng."
+ hashtag: "Hashtag"
+ hashtagDescription: "Bạn có thể tạo một hashtag bằng #chữ hoặc #số."
+ url: "URL"
+ urlDescription: "Những URL có thể hiển thị."
+ link: "ÄÆ°á»ng dẫn"
+ linkDescription: "Các phần cụ thể của văn bản có thể được hiển thị dưới dạng URL."
+ bold: "In đậm"
+ boldDescription: "Nổi bật các chữ cái bằng cách làm chúng dày hơn."
+ small: "Nhá»"
+ smallDescription: "Hiển thị ná»™i dung nhá» và má»ng."
+ center: "Giữa"
+ centerDescription: "Hiển thị nội dung căn giữa."
+ inlineCode: "Mã (Trong dòng)"
+ inlineCodeDescription: "Hiển thị tô sáng cú pháp trong dòng cho mã (chương trình)."
+ blockCode: "Mã (Khối)"
+ blockCodeDescription: "Hiển thị tô sáng cú pháp cho mã nhiá»u dòng (chương trình) trong má»™t khối."
+ inlineMath: "Toán há»c (Trong dòng)"
+ inlineMathDescription: "Hiển thị công thức toán (KaTeX) trong dòng"
+ blockMath: "Toán há»c (Khối)"
+ blockMathDescription: "Hiển thị công thức toán há»c nhiá»u dòng (KaTeX) trong má»™t khối"
+ quote: "Trích dẫn"
+ quoteDescription: "Hiển thị ná»™i dung dạng lá»i trích dạng."
+ emoji: "Tùy chỉnh emoji"
+ emojiDescription: "Hiển thị emoji với cú pháp :tên emoji:"
search: "Tìm kiếm"
+ searchDescription: "Hiển thị hộp tìm kiếm với văn bản được nhập trước."
+ flip: "Lật"
+ flipDescription: "Lật ná»™i dung theo chiá»u ngang hoặc chiá»u dá»c."
+ jelly: "Chuyển động (Thạch rau câu)"
+ jellyDescription: "Cho phép nội dung chuyển động giống như thạch rau câu."
+ tada: "Chuyển động (Tada)"
+ tadaDescription: "Cho phép nội dung chuyển động kiểu \"Tada!\"."
+ jump: "Chuyển động (Nhảy múa)"
+ jumpDescription: "Cho phép nội dung chuyển động nhảy nhót."
+ bounce: "Chuyển động (Cà tưng)"
+ bounceDescription: "Cho phép nội dung chuyển động cà tưng."
+ shake: "Chuyển động (Rung)"
+ shakeDescription: "Cho phép nội dung chuyển động rung lắc."
+ twitch: "Chuyển động (Co rút)"
+ twitchDescription: "Cho phép nội dung chuyển động co rút."
+ spin: "Chuyển động (Xoay tít)"
+ spinDescription: "Cho phép nội dung chuyển động xoay tít."
+ x2: "LÆ¡Ìn"
+ x2Description: "Hiển thị nội dung cỡ lớn hơn."
+ x3: "Rất lớn"
+ x3Description: "Hiển thị nội dung cỡ lớn hơn nữa."
+ x4: "Khổng lồ"
+ x4Description: "Hiển thị nội dung cỡ khổng lồ."
+ blur: "Làm má»"
+ blurDescription: "Làm mỠnội dung. Nó sẽ được hiển thị rõ ràng khi di chuột qua."
+ font: "Phông chữ"
+ fontDescription: "Chá»n phông chữ để hiển thị ná»™i dung."
+ rainbow: "Cầu vồng"
+ rainbowDescription: "Làm cho nội dung hiển thị với màu sắc cầu vồng."
+ sparkle: "Lấp lánh"
+ sparkleDescription: "Làm cho nội dung hiệu ứng hạt lấp lánh."
+ rotate: "Xoay"
+ rotateDescription: "Xoay nội dung theo một góc cụ thể."
+_instanceTicker:
+ none: "Không hiển thị"
+ remote: "Hiện cho ngưá»i dùng từ máy chá»§ khác"
+ always: "Luôn hiện"
+_serverDisconnectedBehavior:
+ reload: "Tự động tải lại"
+ dialog: "Hiện hộp thoại cảnh báo"
+ quiet: "Hiển thị cảnh báo không phô trương"
+_channel:
+ create: "Tạo kênh"
+ edit: "Chỉnh sửa kênh"
+ setBanner: "Äặt ảnh bìa"
+ removeBanner: "Xóa ảnh bìa"
+ featured: "Xu hươÌng"
+ owned: "Do tôi quản lý"
+ following: "Äang theo dõi"
+ usersCount: "{n} Thành viên"
+ notesCount: "{n} Tút"
+_menuDisplay:
+ sideFull: "Thanh bên"
+ sideIcon: "Thanh bên (Biểu tượng)"
+ top: "Trên cùng"
+ hide: "Ẩn"
+_wordMute:
+ muteWords: "Ẩn từ ngữ"
+ muteWordsDescription: "Separate with spaces for an AND condition or with line breaks for an OR condition."
+ muteWordsDescription2: "Bao quanh các từ khóa bằng dấu gạch chéo để sử dụng cụm từ thông dụng."
+ softDescription: "Ẩn các tút phù hợp Ä‘iá»u kiện đã đặt khá»i bảng tin."
+ hardDescription: "Ngăn các tút đáp ứng các Ä‘iá»u kiện đã đặt xuất hiện trên bảng tin. Lưu ý, những tút này sẽ không được thêm vào bảng tin ngay cả khi các Ä‘iá»u kiện được thay đổi."
+ soft: "Yếu"
+ hard: "Mạnh"
+ mutedNotes: "Những tút đã ẩn"
+_instanceMute:
+ instanceMuteDescription: "Thao tác này sẽ ẩn má»i tút/lượt đăng lại từ các máy chá»§ được liệt kê, bao gồm cả những tút dạng trả lá»i từ máy chá»§ bị ẩn."
+ instanceMuteDescription2: "Tách bằng cách xuống dòng"
+ title: "Ẩn tút từ những máy chủ đã liệt kê."
+ heading: "Danh sách những máy chủ bị ẩn"
_theme:
+ explore: "Khám phá theme"
+ install: "Cài đặt theme"
+ manage: "Quản lý theme"
+ code: "Mã theme"
+ description: "Mô tả"
installed: "{name} đã được cài đặt"
+ installedThemes: "Theme đã cài đặt"
+ builtinThemes: "Theme tích hợp sẵn"
+ alreadyInstalled: "Theme này đã được cài đặt"
+ invalid: "Äịnh dạng cá»§a theme này không hợp lệ"
+ make: "Tạo theme"
+ base: "Dựa trên có sẵn"
+ addConstant: "Thêm hằng số"
+ constant: "Hằng số"
+ defaultValue: "Giá trị mặc định"
+ color: "Màu sắc"
+ refProp: "Tham chiếu một thuộc tính"
+ refConst: "Tham chiếu một hằng số"
+ key: "Khóa"
+ func: "Hàm"
+ funcKind: "Loại hàm"
+ argument: "Tham số"
+ basedProp: "Thuộc tính tham chiếu"
+ alpha: "Äá»™ trong suốt"
+ darken: "Äá»™ tối"
+ lighten: "Äộ saÌng"
+ inputConstantName: "Nhập tên cho hằng số này"
+ importInfo: "Nếu bạn nhập mã theme ở đây, bạn có thể nhập mã đó vào trình chỉnh sửa theme"
+ deleteConstantConfirm: "Bạn có chắc muốn xóa hằng số {const} không?"
+ keys:
+ accent: "Màu phụ"
+ bg: "Màu ná»n"
+ fg: "Màu chữ"
+ focus: "Trá»ng tâm"
+ indicator: "Chỉ báo"
+ panel: "Thanh bên"
+ shadow: "Bóng má»"
+ header: "Ảnh bìa"
+ navBg: "Ná»n thanh bên"
+ navFg: "Chữ thanh bên"
+ navHoverFg: "Chữ thanh bên (Khi chạm)"
+ navActive: "Chữ thanh bên (Khi chá»n)"
+ navIndicator: "Chỉ báo thanh bên"
+ link: "ÄÆ°á»ng dẫn"
+ hashtag: "Hashtag"
+ mention: "Nhắc đến"
+ mentionMe: "Lượt nhắc (Tôi)"
+ renote: "Äăng lại"
+ modalBg: "Ná»n phương thức"
+ divider: "Phân chia"
+ scrollbarHandle: "Thanh cuộn khi giữ"
+ scrollbarHandleHover: "Thanh cuộn khi chạm"
+ dateLabelFg: "Màu ngày tháng năm"
+ infoBg: "Ná»n thông tin"
+ infoFg: "Chữ thông tin"
+ infoWarnBg: "Ná»n cảnh báo"
+ infoWarnFg: "Chữ cảnh báo"
+ cwBg: "Ná»n nút ná»™i dung ẩn"
+ cwFg: "Chữ nút nội dung ẩn"
+ cwHoverBg: "Ná»n nút ná»™i dung ẩn (Chạm)"
+ toastBg: "Ná»n thông báo"
+ toastFg: "Chữ thông báo"
+ buttonBg: "Ná»n nút"
+ buttonHoverBg: "Ná»n nút (Chạm)"
+ inputBorder: "ÄÆ°á»ng viá»n khung soạn thảo"
+ listItemHoverBg: "Ná»n mục liệt kê (Chạm)"
+ driveFolderBg: "Ná»n thư mục á»” đĩa"
+ wallpaperOverlay: "Lá»›p phá»§ hình ná»n"
+ badge: "Huy hiệu"
+ messageBg: "Ná»n chat"
+ accentDarken: "Màu phụ (Tối)"
+ accentLighten: "Màu phụ (Sáng)"
+ fgHighlighted: "Chữ nổi bật"
_sfx:
+ note: "Tút"
+ noteMy: "Tút của tôi"
notification: "Thông báo"
+ chat: "Trò chuyện"
+ chatBg: "Chat (Ná»n)"
+ antenna: "Trạm phát sóng"
+ channel: "Kênh"
_ago:
- unknown: "Không rõ"
future: "Tương lai"
justNow: "Vừa xong"
secondsAgo: "{n}s trước"
@@ -39,10 +1097,563 @@ _ago:
weeksAgo: "{n} tuần trước"
monthsAgo: "{n} tháng trước"
yearsAgo: "{n} năm trước"
+_time:
+ second: "s"
+ 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! 🚀"
+_2fa:
+ alreadyRegistered: "Bạn đã đăng ký thiết bị xác minh 2 bước."
+ registerDevice: "Äăng ký má»™t thiết bị"
+ registerKey: "Äăng ký má»™t mã bảo vệ"
+ step1: "Trước tiên, hãy cài đặt một ứng dụng xác minh (chẳng hạn như {a} hoặc {b}) trên thiết bị của bạn."
+ step2: "Sau đó, quét mã QR hiển thị trên màn hình này."
+ step2Url: "Bạn cũng có thể nhập URL này nếu sử dụng một chương trình máy tính:"
+ step3: "Nhập mã token do ứng dụng của bạn cung cấp để hoàn tất thiết lập."
+ step4: "Kể từ bây giá», những lần đăng nhập trong tương lai sẽ yêu cầu mã token đăng nhập đó."
+ securityKeyInfo: "Bên cạnh xác minh bằng vân tay hoặc mã PIN, bạn cũng có thể thiết lập xác minh thông qua khóa bảo mật phần cứng hỗ trợ FIDO2 để bảo mật hơn nữa cho tài khoản của mình."
+_permissions:
+ "read:account": "Xem thông tin tài khoản của bạn"
+ "write:account": "Sửa thông tin tài khoản của bạn"
+ "read:blocks": "Xem danh sách ngưá»i bạn chặn"
+ "write:blocks": "Sá»­a danh sách ngưá»i bạn chặn"
+ "read:drive": "Truy cập tập tin, thư mục trong Ổ đĩa"
+ "write:drive": "Sửa và xóa tập tin, thư mục trong Ổ đĩa"
+ "read:favorites": "Xem lượt thích của tôi"
+ "write:favorites": "Sửa lượt thích của tôi"
+ "read:following": "Xem những ngưá»i bạn theo dõi"
+ "write:following": "Theo dõi hoặc ngưng theo dõi ai đó"
+ "read:messaging": "Xem lịch sử chat"
+ "write:messaging": "Soạn hoặc xóa tin nhắn"
+ "read:mutes": "Xem những ngưá»i bạn ẩn"
+ "write:mutes": "Sá»­a những ngưá»i bạn ẩn"
+ "write:notes": "Soạn hoặc xóa tút"
+ "read:notifications": "Xem thông báo của tôi"
+ "write:notifications": "Quản lý thông báo của tôi"
+ "read:reactions": "Xem lượt biểu cảm của tôi"
+ "write:reactions": "Sửa lượt biểu cảm của tôi"
+ "write:votes": "Bình chá»n"
+ "read:pages": "Xem trang của tôi"
+ "write:pages": "Sửa hoặc xóa trang của tôi"
+ "read:page-likes": "Xem lượt thích trên trang của tôi"
+ "write:page-likes": "Sửa lượt thích của tôi trên trang"
+ "read:user-groups": "Xem nhóm của tôi"
+ "write:user-groups": "Sửa hoặc xóa nhóm của tôi"
+ "read:channels": "Xem kênh của tôi"
+ "write:channels": "Sửa kênh của tôi"
+ "read:gallery": "Xem kho ảnh của tôi"
+ "write:gallery": "Sửa kho ảnh của tôi"
+ "read:gallery-likes": "Xem danh sách các tút đã thích trong thư viện của tôi"
+ "write:gallery-likes": "Sửa danh sách các tút đã thích trong thư viện của tôi"
+_auth:
+ shareAccess: "Bạn có muốn cho phép \"{name}\" truy cập vào tài khoản này không?"
+ shareAccessAsk: "Bạn có chắc muốn cho phép ứng dụng này truy cập vào tài khoản của mình không?"
+ permissionAsk: "Ứng dụng này yêu cầu các quyá»n sau"
+ pleaseGoBack: "Vui lòng quay lại ứng dụng"
+ callback: "Quay lại ứng dụng"
+ denied: "Truy cập bị từ chối"
+_antennaSources:
+ all: "Toàn bộ tút"
+ homeTimeline: "Tút từ những ngưá»i đã theo dõi"
+ users: "Tút từ những ngưá»i cụ thể"
+ userList: "Tút từ danh sách ngưá»i dùng cụ thể"
+ userGroup: "Tút từ ngưá»i dùng trong má»™t nhóm cụ thể"
+_weekday:
+ sunday: "Chủ Nhật"
+ monday: "Thứ Hai"
+ tuesday: "Thứ Ba"
+ wednesday: "Thứ Tư"
+ thursday: "Thứ Năm"
+ friday: "Thứ Sáu"
+ saturday: "Thứ Bảy"
_widgets:
+ memo: "Tút đã ghim"
notifications: "Thông báo"
+ timeline: "Bảng tin"
+ calendar: "Lịch"
+ trends: "Xu hươÌng"
+ clock: "Äồng hồ"
+ rss: "Trình Ä‘á»c RSS"
+ activity: "Hoạt động"
+ photos: "Kho ảnh"
+ digitalClock: "Äồng hồ số"
+ federation: "Liên hợp"
+ postForm: "Mẫu đăng"
+ slideshow: "Trình chiếu"
+ button: "Nút"
+ onlineUsers: "Ai đang online"
+ jobQueue: "Công việc chỠxử lý"
+ serverMetric: "Thống kê máy chủ"
+ aiscript: "AiScript console"
+ aichan: "Ai"
+_cw:
+ hide: "Ẩn"
+ show: "Tải thêm"
+ chars: "{count} ký tự"
+ files: "{count} tập tin"
+_poll:
+ noOnlyOneChoice: "Cần ít nhất hai lá»±a chá»n."
+ choiceN: "Lá»±a chá»n {n}"
+ noMore: "Bạn không thể thêm lá»±a chá»n"
+ canMultipleVote: "Cho phép chá»n nhiá»u lá»±a chá»n"
+ expiration: "Thá»i hạn"
+ infinite: "Vĩnh viễn"
+ at: "Kết thúc vào..."
+ after: "Kết thúc sau..."
+ deadlineDate: "Ngày kết thúc"
+ deadlineTime: "giá»"
+ duration: "Thá»i hạn"
+ votesCount: "{n} bình chá»n"
+ totalVotes: "{n} tổng bình chá»n"
+ vote: "Bình chá»n"
+ showResult: "Xem kết quả"
+ voted: "Äã bình chá»n"
+ closed: "Äã kết thúc"
+ remainingDays: "{d} ngày {h} giỠcòn lại"
+ remainingHours: "{h} giỠ{m} phút còn lại"
+ remainingMinutes: "{m} phút {s}s còn lại"
+ remainingSeconds: "{s}s còn lại"
+_visibility:
+ public: "Công khai"
+ publicDescription: "Má»i ngưá»i Ä‘á»u có thể Ä‘á»c tút cá»§a bạn"
+ home: "Trang chính"
+ homeDescription: "Chỉ đăng lên bảng tin nhà"
+ followers: "Ngưá»i theo dõi"
+ followersDescription: "Dành riêng cho ngưá»i theo dõi"
+ specified: "Nhắn riêng"
+ specifiedDescription: "Chỉ ngưá»i được nhắc đến má»›i thấy"
+ localOnly: "Chỉ trên máy chủ"
+ localOnlyDescription: "Không hiển thị vá»›i ngưá»i ở máy chá»§ khác"
+_postForm:
+ replyPlaceholder: "Trả lá»i tút này"
+ quotePlaceholder: "Trích dẫn tút này"
+ channelPlaceholder: "Äăng lên má»™t kênh"
+ _placeholders:
+ a: "Bạn đang định làm gì?"
+ b: "Hôm nay bạn có gì vui?"
+ c: "Bạn đang nghĩ gì?"
+ d: "Bạn muốn nói gì?"
+ e: "Bắt đầu viết..."
+ f: "Äang chá» bạn viết..."
_profile:
+ name: "Tên"
username: "Tên ngưá»i dùng"
+ description: "Tiểu sử"
+ youCanIncludeHashtags: "Bạn có thể dùng hashtag trong tiểu sử."
+ metadata: "Thông tin bổ sung"
+ metadataEdit: "Sửa thông tin bổ sung"
+ metadataDescription: "Sử dụng phần này, bạn có thể hiển thị các mục thông tin bổ sung trong hồ sơ của mình."
+ metadataLabel: "Nhãn"
+ metadataContent: "Ná»™i dung"
+ changeAvatar: "Äổi ảnh đại diện"
+ changeBanner: "Äổi ảnh bìa"
+_exportOrImport:
+ allNotes: "Toàn bộ tút"
+ followingList: "Äang theo dõi"
+ muteList: "Ẩn"
+ blockingList: "Chặn"
+ userLists: "Danh sách"
+ excludeMutingUsers: "Loại trừ những ngưá»i dùng bị ẩn"
+ excludeInactiveUsers: "Loại trừ những ngưá»i dùng không hoạt động"
+_charts:
+ federation: "Liên hợp"
+ apRequest: "Yêu cầu"
+ usersIncDec: "Sá»± khác biệt vá» số lượng ngưá»i dùng"
+ usersTotal: "Tổng số ngưá»i dùng"
+ activeUsers: "Số ngưá»i Ä‘ang hoạt động"
+ notesIncDec: "Sự khác biệt vỠsố lượng tút"
+ localNotesIncDec: "Sự khác biệt vỠsố lượng tút máy chủ này"
+ remoteNotesIncDec: "Sự khác biệt vỠsố lượng tút từ máy chủ khác"
+ notesTotal: "Tổng số sút"
+ filesIncDec: "Sự khác biệt vỠsố lượng tập tin"
+ filesTotal: "Tổng số tập tin"
+ storageUsageIncDec: "Sự khác biệt vỠdung lượng lưu trữ"
+ storageUsageTotal: "Tổng dung lượng lưu trữ"
+_instanceCharts:
+ requests: "Lượt yêu cầu"
+ users: "Sá»± khác biệt vá» số lượng ngưá»i dùng"
+ usersTotal: "Số lượng ngưá»i dùng tích lÅ©y"
+ notes: "Sự khác biệt vỠsố lượng tút"
+ notesTotal: "Số lượng tút tích lũy"
+ ff: "Sá»± khác biệt vá» số lượng ngưá»i dùng được theo dõi/ngưá»i theo dõi"
+ ffTotal: "Số lượng ngưá»i dùng được theo dõi/ngưá»i theo dõi tích lÅ©y"
+ cacheSize: "Sự khác biệt vỠdung lượng bộ nhớ đệm"
+ cacheSizeTotal: "Dung lượng bộ nhớ đệm tích lũy"
+ files: "Sự khác biệt vỠsố lượng tập tin"
+ filesTotal: "Số lượng tập tin tích lũy"
+_timelines:
+ home: "Trang chính"
+ local: "Máy chủ này"
+ social: "Xã hội"
+ global: "Liên hợp"
+_pages:
+ newPage: "Tạo Trang mới"
+ editPage: "Sửa Trang này"
+ readPage: "Xem mã nguồn Trang này"
+ created: "Trang đã được tạo thành công"
+ updated: "Trang đã được cập nhật thành công"
+ deleted: "Trang đã được xóa thành công"
+ pageSetting: "Cài đặt trang"
+ nameAlreadyExists: "URL Trang đã tồn tại"
+ invalidNameTitle: "URL Trang không hợp lệ"
+ invalidNameText: "Không được để trống tựa đỠTrang"
+ editThisPage: "Sửa Trang này"
+ viewSource: "Xem mã nguồn"
+ viewPage: "Xem trang của tôi"
+ like: "Thích"
+ unlike: "BỠthích"
+ my: "Trang của tôi"
+ liked: "Trang đã thích"
+ featured: "Nổi tiếng"
+ inspector: "Thanh tra"
+ contents: "Ná»™i dung"
+ content: "Chặn Trang"
+ variables: "Biến thể"
+ title: "Tá»±a Ä‘á»"
+ url: "URL Trang"
+ summary: "Mô tả Trang"
+ alignCenter: "Căn giữa"
+ hideTitleWhenPinned: "Ẩn tựa đỠTrang khi ghim lên hồ sơ"
+ font: "Phông chữ"
+ fontSerif: "Serif"
+ fontSansSerif: "Sans Serif"
+ eyeCatchingImageSet: "Äặt ảnh thu nhá»"
+ eyeCatchingImageRemove: "Xóa ảnh thu nhá»"
+ chooseBlock: "Thêm khối"
+ selectType: "Chá»n kiểu"
+ enterVariableName: "Nhập tên một biến thể"
+ variableNameIsAlreadyUsed: "Tên biến thể này đã được sử dụng"
+ contentBlocks: "Ná»™i dung"
+ inputBlocks: "Nhập"
+ specialBlocks: "Äặc biệt"
+ blocks:
+ text: "Văn bản"
+ textarea: "Khu vực văn bản"
+ section: "Mục "
+ image: "Hình ảnh"
+ button: "Nút"
+ if: "Nếu"
+ _if:
+ variable: "Biến thể"
+ post: "Mẫu đăng"
+ _post:
+ text: "Ná»™i dung"
+ attachCanvasImage: "Äính kèm hình canva"
+ canvasId: "ID Canva"
+ textInput: "Văn bản đầu vào"
+ _textInput:
+ name: "Tên biến thể"
+ text: "Tá»±a Ä‘á»"
+ default: "Giá trị mặc định"
+ textareaInput: "Văn bản nhiá»u dòng đầu vào"
+ _textareaInput:
+ name: "Tên biến thể"
+ text: "Tá»±a Ä‘á»"
+ default: "Giá trị mặc định"
+ numberInput: "Äầu vào số"
+ _numberInput:
+ name: "Tên biến thể"
+ text: "Tá»±a Ä‘á»"
+ default: "Giá trị mặc định"
+ canvas: "Canva"
+ _canvas:
+ id: "ID Canva"
+ width: "Chiá»u rá»™ng"
+ height: "Chiá»u cao"
+ note: "Tút đã nhúng"
+ _note:
+ id: "ID tút"
+ idDescription: "Ngoài ra, bạn có thể dán URL tút vào đây."
+ detailed: "Xem chi tiết"
+ switch: "Chuyển đổi"
+ _switch:
+ name: "Tên biến thể"
+ text: "Tá»±a Ä‘á»"
+ default: "Giá trị mặc định"
+ counter: "Bộ đếm"
+ _counter:
+ name: "Tên biến thể"
+ text: "Tá»±a Ä‘á»"
+ inc: "Bước"
+ _button:
+ text: "Tá»±a Ä‘á»"
+ colored: "Với màu"
+ action: "Thao tác khi nhấn nút"
+ _action:
+ dialog: "Hiện hộp thoại"
+ _dialog:
+ content: "Ná»™i dung"
+ resetRandom: "Äặt lại seed ngẫu nhiên"
+ pushEvent: "Gửi một sự kiện"
+ _pushEvent:
+ event: "Tên sự kiện"
+ message: "Tin nhắn hiển thị khi kích hoạt"
+ variable: "Biển thể để gửi"
+ no-variable: "Không"
+ callAiScript: "Gá»i AiScript"
+ _callAiScript:
+ functionName: "Tên tính năng"
+ radioButton: "Lá»±a chá»n"
+ _radioButton:
+ name: "Tên biến thể"
+ title: "Tá»±a Ä‘á»"
+ values: "Phân tách các mục bằng cách xuống dòng"
+ default: "Giá trị mặc định"
+ script:
+ categories:
+ flow: "Äiá»u khiển"
+ logical: "Hoạt động logic"
+ operation: "Tính toán"
+ comparison: "So sánh"
+ random: "Ngẫu nhiên"
+ value: "Giá trị"
+ fn: "Tính năng"
+ text: "Tác vụ văn bản"
+ convert: "Chuyển đổi"
+ list: "Danh sách"
+ blocks:
+ text: "Văn bản"
+ multiLineText: "Văn bản (nhiá»u dòng)"
+ textList: "Văn bản liệt kê"
+ _textList:
+ info: "Phân tách mục bằng cách xuống dòng"
+ strLen: "Äá»™ dài văn bản"
+ _strLen:
+ arg1: "Văn bản"
+ strPick: "Trích xuất chuỗi"
+ _strPick:
+ arg1: "Văn bản"
+ arg2: "Vị trí chuỗi"
+ strReplace: "Thay thế chuỗi"
+ _strReplace:
+ arg1: "Ná»™i dung"
+ arg2: "Văn bản thay thế"
+ arg3: "Thay thế bằng"
+ strReverse: "Lật văn bản"
+ _strReverse:
+ arg1: "Văn bản"
+ join: "Nối văn bản"
+ _join:
+ arg1: "Danh sách"
+ arg2: "Phân cách"
+ add: "Cá»™ng"
+ _add:
+ arg1: "A"
+ arg2: "B"
+ subtract: "Trừ"
+ _subtract:
+ arg1: "A"
+ arg2: "B"
+ multiply: "Nhân"
+ _multiply:
+ arg1: "A"
+ arg2: "B"
+ divide: "Chia"
+ _divide:
+ arg1: "A"
+ arg2: "B"
+ mod: "Phần còn lại"
+ _mod:
+ arg1: "A"
+ arg2: "B"
+ round: "Làm tròn thập phân"
+ _round:
+ arg1: "Số"
+ eq: "A và B bằng nhau"
+ _eq:
+ arg1: "A"
+ arg2: "B"
+ notEq: "A và B khác nhau"
+ _notEq:
+ arg1: "A"
+ arg2: "B"
+ and: "A VÀ B"
+ _and:
+ arg1: "A"
+ arg2: "B"
+ or: "A HOẶC B"
+ _or:
+ arg1: "A"
+ arg2: "B"
+ lt: "< A nhỠhơn B"
+ _lt:
+ arg1: "A"
+ arg2: "B"
+ gt: "> A lớn hơn B"
+ _gt:
+ arg1: "A"
+ arg2: "B"
+ ltEq: "<= A nhỠhơn hoặc bằng B"
+ _ltEq:
+ arg1: "A"
+ arg2: "B"
+ gtEq: ">= A lớn hơn hoặc bằng B"
+ _gtEq:
+ arg1: "A"
+ arg2: "B"
+ if: "Nhánh"
+ _if:
+ arg1: "Nếu"
+ arg2: "Sau đó"
+ arg3: "Khác"
+ not: "KHÔNG"
+ _not:
+ arg1: "KHÔNG"
+ random: "Ngẫu nhiên"
+ _random:
+ arg1: "Xác suất"
+ rannum: "Số ngẫu nhiên"
+ _rannum:
+ arg1: "Giá trị tối thiểu"
+ arg2: "Giá trị tối đa"
+ randomPick: "Chá»n ngẫu nhiên từ danh sách"
+ _randomPick:
+ arg1: "Danh sách"
+ dailyRandom: "Ngẫu nhiên (Äổi má»—i ngưá»i má»™t lần má»—i ngày)"
+ _dailyRandom:
+ arg1: "Xác suất"
+ dailyRannum: "Số ngẫu nhiên (Äổi má»—i ngưá»i má»™t lần má»—i ngày)"
+ _dailyRannum:
+ arg1: "Giá trị tối thiểu"
+ arg2: "Giá trị tối đa"
+ dailyRandomPick: "Chá»n ngẫu nhiên từ má»™t danh sách (Äổi má»—i ngưá»i má»™t lần má»—i ngày)"
+ _dailyRandomPick:
+ arg1: "Danh sách"
+ seedRandom: "Ngẫu nhiên (với seed)"
+ _seedRandom:
+ arg1: "Seed"
+ arg2: "Xác suất"
+ seedRannum: "Số ngẫu nhiên (với seed)"
+ _seedRannum:
+ arg1: "Seed"
+ arg2: "Giá trị tối thiểu"
+ arg3: "Giá trị tối đa"
+ seedRandomPick: "Chá»n ngẫu nhiên từ danh sách (vá»›i seed)"
+ _seedRandomPick:
+ arg1: "Seed"
+ arg2: "Danh sách"
+ DRPWPM: "Chá»n ngẫu nhiên từ danh sách nặng (Äổi má»—i ngưá»i má»™t lần má»—i ngày)"
+ _DRPWPM:
+ arg1: "Văn bản liệt kê"
+ pick: "Chá»n từ danh sách"
+ _pick:
+ arg1: "Danh sách"
+ arg2: "Vị trí"
+ listLen: "Lấy độ dài danh sách"
+ _listLen:
+ arg1: "Danh sách"
+ number: "Số"
+ stringToNumber: "Chữ thành số"
+ _stringToNumber:
+ arg1: "Văn bản"
+ numberToString: "Số thành chữ"
+ _numberToString:
+ arg1: "Số"
+ splitStrByLine: "Phân cách văn bản bằng cách xuống dòng"
+ _splitStrByLine:
+ arg1: "Văn bản"
+ ref: "Biến thể"
+ aiScriptVar: "Biển thể AiScript"
+ fn: "Tính năng"
+ _fn:
+ slots: "Chá»—"
+ slots-info: "Phân cách chỗ bằng cách xuống dòng"
+ arg1: "Äầu ra"
+ for: "để-Lặp lại"
+ _for:
+ arg1: "Số lần lặp lại"
+ arg2: "Hành động"
+ typeError: "Chỗ {slot} chấp nhận các giá trị thuộc loại \"{expect}\", nhưng giá trị được cung cấp thuộc loại \"{actual}\"!"
+ thereIsEmptySlot: "Chỗ {slot} đang trống!"
+ types:
+ string: "Văn bản"
+ number: "Số"
+ boolean: "Cá»"
+ array: "Danh sách"
+ stringArray: "Văn bản liệt kê"
+ emptySlot: "Chỗ trống"
+ enviromentVariables: "Biến môi trưá»ng"
+ pageVariables: "Biến trang"
+ argVariables: "Äầu vào chá»—"
+_relayStatus:
+ requesting: "Äang chá»"
+ accepted: "Äã duyệt"
+ rejected: "Äã từ chối"
+_notification:
+ fileUploaded: "Äã tải lên tập tin"
+ youGotMention: "{name} nhắc đến bạn"
+ youGotReply: "{name} trả lá»i bạn"
+ youGotQuote: "{name} trích dẫn tút của bạn"
+ youRenoted: "{name} đăng lại tút của bạn"
+ youGotPoll: "{name} bình chá»n tút cá»§a bạn"
+ youGotMessagingMessageFromUser: "{name} nhắn tin cho bạn"
+ youGotMessagingMessageFromGroup: "Một tin nhắn trong nhóm {name}"
+ youWereFollowed: "đã theo dõi bạn"
+ youReceivedFollowRequest: "Bạn vừa có một yêu cầu theo dõi"
+ yourFollowRequestAccepted: "Yêu cầu theo dõi của bạn đã được chấp nhận"
+ youWereInvitedToGroup: "Bạn đã được má»i tham gia nhóm"
+ pollEnded: "Cuá»™c bình chá»n đã kết thúc"
+ emptyPushNotificationMessage: "Äã cập nhật thông báo đẩy"
+ _types:
+ all: "Toàn bộ"
+ follow: "Äang theo dõi"
+ mention: "Nhắc đến"
+ reply: "Lượt trả lá»i"
+ renote: "Äăng lại"
+ quote: "Trích dẫn"
+ reaction: "Biểu cảm"
+ pollVote: "Lượt bình chá»n"
+ pollEnded: "Bình chá»n kết thúc"
+ receiveFollowRequest: "Yêu cầu theo dõi"
+ followRequestAccepted: "Yêu cầu theo dõi được chấp nhận"
+ groupInvited: "Má»i vào nhóm"
+ app: "Từ app liên kết"
+ _actions:
+ followBack: "đã theo dõi lại bạn"
+ reply: "Trả lá»i"
+ renote: "Äăng lại"
_deck:
+ alwaysShowMainColumn: "Luôn hiện cột chính"
+ columnAlign: "Căn cột"
+ columnMargin: "Căn lỠgiữa các cột"
+ columnHeaderHeight: "Chiá»u rá»™ng cá»™t ảnh bìa"
+ addColumn: "Thêm cột"
+ swapLeft: "Hoán đổi với cột bên trái"
+ swapRight: "Hoán đổi với cột bên phải"
+ swapUp: "Hoán đổi với cột trên"
+ swapDown: "Hoán đổi với cột dưới"
+ stackLeft: "Xếp chồng với cột bên trái"
+ popRight: "Xếp chồng với cột bên trái"
+ profile: "Hồ sơ"
_columns:
+ main: "Chính"
+ widgets: "Tiện ích"
notifications: "Thông báo"
+ tl: "Bảng tin"
+ antenna: "Trạm phát sóng"
+ list: "Danh sách"
+ mentions: "Lượt nhắc"
+ direct: "Nhắn riêng"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index f644585835..4953f55280 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -8,7 +8,7 @@ notifications: "通知"
username: "用户å"
password: "密ç "
forgotPassword: "忘记密ç "
-fetchingAsApObject: "在è”邦宇宙查询中..."
+fetchingAsApObject: "正在è”邦宇宙查询中..."
ok: "OK"
gotIt: "我明白了"
cancel: "å–æ¶ˆ"
@@ -69,7 +69,7 @@ exportRequested: "导出请求已æäº¤ï¼Œè¿™å¯èƒ½éœ€è¦èŠ±ä¸€äº›æ—¶é—´ï¼Œå¯¼å‡
importRequested: "导入请求已æäº¤ï¼Œè¿™å¯èƒ½éœ€è¦èŠ±ä¸€ç‚¹æ—¶é—´ã€‚"
lists: "列表"
noLists: "列表为空"
-note: "å‘帖"
+note: "帖å­"
notes: "帖å­"
following: "关注中"
followers: "关注者"
@@ -96,7 +96,7 @@ enterEmoji: "输入表情符å·"
renote: "转å‘"
unrenote: "å–æ¶ˆè½¬å‘"
renoted: "已转å‘。"
-cantRenote: "è¯¥å¸–å­æ— æ³•转å‘。"
+cantRenote: "该帖无法转å‘。"
cantReRenote: "è½¬å‘æ— æ³•è¢«å†æ¬¡è½¬å‘。"
quote: "引用"
pinnedNote: "已置顶的帖å­"
@@ -155,7 +155,7 @@ searchWith: "æœç´¢:{q}"
youHaveNoLists: "列表为空"
followConfirm: "你确定è¦å…³æ³¨{name}å—?"
proxyAccount: "代ç†è´¦æˆ·"
-proxyAccountDescription: "代ç†å¸æˆ·æ˜¯åœ¨æŸäº›æƒ…å†µä¸‹å……å½“ç”¨æˆ·çš„è¿œç¨‹å…³æ³¨è€…çš„å¸æˆ·ã€‚ 例如,当一个用户列出一个远程用户时,如果没有人跟éšè¯¥åˆ—出的用户,则该活动将ä¸ä¼šä¼ é€’到该实例,因此将代之以代ç†å¸æˆ·ã€‚"
+proxyAccountDescription: "代ç†è´¦æˆ·æ˜¯åœ¨æŸäº›æƒ…况下充当用户的远程关注者的账户。 例如,当一个用户列出一个远程用户时,如果没有人跟éšè¯¥åˆ—出的用户,则该活动将ä¸ä¼šä¼ é€’到该实例,因此将代之以代ç†è´¦æˆ·ã€‚"
host: "主机å"
selectUser: "选择用户"
recipient: "收件人"
@@ -171,7 +171,7 @@ charts: "图表"
perHour: "æ¯å°æ—¶"
perDay: "æ¯å¤©"
stopActivityDelivery: "åœæ­¢å‘逿´»åЍ"
-blockThisInstance: "阻止此实例"
+blockThisInstance: "é˜»æ­¢æ­¤å®žä¾‹å‘æœ¬å®žä¾‹æŽ¨æµ"
operations: "æ“作"
software: "软件"
version: "版本"
@@ -250,7 +250,7 @@ messageRead: "已读"
noMoreHistory: "没有更多的历å²è®°å½•"
startMessaging: "添加èŠå¤©"
nUsersRead: "{n}人已读"
-agreeTo: "{0}äººåŒæ„"
+agreeTo: "{0}å‹¾é€‰åˆ™è¡¨ç¤ºå·²é˜…è¯»å¹¶åŒæ„"
tos: "æœåŠ¡æ¡æ¬¾"
start: "开始"
home: "首页"
@@ -321,7 +321,7 @@ connectService: "连接"
disconnectService: "断开连接"
enableLocalTimeline: "å¯ç”¨æœ¬åœ°æ—¶é—´çº¿åŠŸèƒ½"
enableGlobalTimeline: "å¯ç”¨å…¨å±€æ—¶é—´çº¿"
-disablingTimelinesInfo: "å³ä½¿æ—¶é—´çº¿åŠŸèƒ½è¢«ç¦ç”¨ï¼Œå‡ºäºŽä¾¿åˆ©æ€§çš„原因,管ç†å‘˜å’Œæ•°æ®å›¾è¡¨ä¹Ÿå¯ä»¥ç»§ç»­ä½¿ç”¨ã€‚"
+disablingTimelinesInfo: "å³ä½¿æ—¶é—´çº¿åŠŸèƒ½è¢«ç¦ç”¨ï¼Œå‡ºäºŽæ–¹ä¾¿ï¼Œç®¡ç†å‘˜å’Œæ•°æ®å›¾è¡¨ä¹Ÿå¯ä»¥ç»§ç»­ä½¿ç”¨ã€‚"
registration: "注册"
enableRegistration: "å…许新用户注册"
invite: "邀请"
@@ -440,7 +440,7 @@ strongPassword: "密ç å¼ºåº¦ï¼šå¼º"
passwordMatched: "密ç ä¸€è‡´"
passwordNotMatched: "密ç ä¸ä¸€è‡´"
signinWith: "以{x}登录"
-signinFailed: "无法登录,请检查您的用户å和密ç ã€‚"
+signinFailed: "无法登录,请检查您的用户åå’Œå¯†ç æ˜¯å¦æ­£ç¡®ã€‚"
tapSecurityKey: "轻触硬件安全密钥"
or: "或者"
language: "语言"
@@ -459,7 +459,7 @@ category: "类别"
tags: "标签"
docSource: "æ–‡ä»¶æ¥æº"
createAccount: "注册账户"
-existingAccount: "çŽ°æœ‰çš„å¸æˆ·"
+existingAccount: "现有的账户"
regenerate: "釿–°ç”Ÿæˆ"
fontSize: "字体大å°"
noFollowRequests: "没有关注申请"
@@ -533,7 +533,7 @@ removeAllFollowingDescription: "å–æ¶ˆ{host}的所有关注者。当实例ä¸å­˜
userSuspended: "该用户已被冻结。"
userSilenced: "该用户已被ç¦è¨€ã€‚"
yourAccountSuspendedTitle: "账户已被冻结"
-yourAccountSuspendedDescription: "由于è¿å了æœåŠ¡å™¨çš„æœåŠ¡æ¡æ¬¾æˆ–其他原因,该账户已被冻结。 您å¯ä»¥ä¸Žç®¡ç†å‘˜è”系以了解更多信æ¯ã€‚ 请ä¸è¦åˆ›å»ºä¸€ä¸ªæ–°çš„叿ˆ·ã€‚"
+yourAccountSuspendedDescription: "由于è¿å了æœåŠ¡å™¨çš„æœåŠ¡æ¡æ¬¾æˆ–其他原因,该账户已被冻结。 您å¯ä»¥ä¸Žç®¡ç†å‘˜è”系以了解更多信æ¯ã€‚ 请ä¸è¦åˆ›å»ºä¸€ä¸ªæ–°çš„账户。"
menu: "èœå•"
divider: "分割线"
addItem: "添加项目"
@@ -609,7 +609,7 @@ create: "创建"
notificationSetting: "通知设置"
notificationSettingDesc: "é€‰æ‹©è¦æ˜¾ç¤ºçš„通知类型。"
useGlobalSetting: "使用全局设置"
-useGlobalSettingDesc: "å¯ç”¨æ—¶ï¼Œå°†ä½¿ç”¨å¸æˆ·é€šçŸ¥è®¾ç½®ã€‚关闭时,则å¯ä»¥å•独设置。"
+useGlobalSettingDesc: "å¯ç”¨æ—¶ï¼Œå°†ä½¿ç”¨è´¦æˆ·é€šçŸ¥è®¾ç½®ã€‚关闭时,则å¯ä»¥å•独设置。"
other: "å…¶ä»–"
regenerateLoginToken: "釿–°ç”Ÿæˆç™»å½•令牌"
regenerateLoginTokenDescription: "釿–°ç”Ÿæˆç”¨äºŽç™»å½•的内部令牌。通常您ä¸éœ€è¦è¿™æ ·åšã€‚釿–°ç”ŸæˆåŽï¼Œæ‚¨å°†åœ¨æ‰€æœ‰è®¾å¤‡ä¸Šç™»å‡ºã€‚"
@@ -621,12 +621,12 @@ abuseReports: "举报"
reportAbuse: "举报"
reportAbuseOf: "举报{name}"
fillAbuseReportDescription: "请填写举报的详细原因。如果有对方å‘的帖å­ï¼Œè¯·åŒæ—¶å¡«å†™URL地å€ã€‚"
-abuseReported: "内容已å‘é€ã€‚感谢您的报告。"
-reporter: "报告者"
+abuseReported: "内容已å‘é€ã€‚感谢您æäº¤ä¿¡æ¯ã€‚"
+reporter: "举报者"
reporteeOrigin: "ä¸¾æŠ¥æ¥æº"
reporterOrigin: "ä¸¾æŠ¥è€…æ¥æº"
-forwardReport: "将报告转å‘给远程实例"
-forwardReportIsAnonymous: "在远程实例上显示的报告者是匿å的系统账å·ï¼Œè€Œä¸æ˜¯æ‚¨çš„è´¦å·ã€‚"
+forwardReport: "将该举报信æ¯è½¬å‘给远程实例"
+forwardReportIsAnonymous: "勾选则在远程实例上显示的举报者是匿å的系统账å·ï¼Œè€Œä¸æ˜¯æ‚¨çš„è´¦å·ã€‚"
send: "å‘é€"
abuseMarkAsResolved: "处ç†å®Œæ¯•"
openInNewTab: "在新标签页中打开"
@@ -644,9 +644,9 @@ createNew: "新建"
optional: "å¯é€‰"
createNewClip: "新建书签"
public: "公开"
-i18nInfo: "Misskeyå·²ç»è¢«å¿—愿者们翻译到了å„ç§è¯­è¨€ã€‚如果你也有兴趣,å¯ä»¥é€šè¿‡{link}帮助翻译。"
+i18nInfo: "Misskeyå·²ç»è¢«å¿—愿者们翻译æˆäº†å„ç§è¯­è¨€ã€‚如果你也有兴趣,å¯ä»¥é€šè¿‡{link}帮助翻译。"
manageAccessTokens: "ç®¡ç† Access Tokens"
-accountInfo: "叿ˆ·ä¿¡æ¯"
+accountInfo: "账户信æ¯"
notesCount: "取孿•°é‡"
repliesCount: "å›žå¤æ•°é‡"
renotesCount: "转帖数é‡"
@@ -662,7 +662,7 @@ yes: "是"
no: "å¦"
driveFilesCount: "网盘的文件数"
driveUsage: "网盘的空间用é‡"
-noCrawle: "æ‹’ç»æœç´¢å¼•擎的索引"
+noCrawle: "è¦æ±‚æœç´¢å¼•擎ä¸ç´¢å¼•该站点"
noCrawleDescription: "è¦æ±‚æœç´¢å¼•擎ä¸è¦æ”¶å½•(索引)您的用户页é¢ï¼Œå¸–å­ï¼Œé¡µé¢ç­‰ã€‚"
lockedAccountInfo: "å³ä½¿é€šè¿‡äº†å…³æ³¨è¯·æ±‚,åªè¦æ‚¨ä¸å°†å¸–å­å¯è§èŒƒå›´è®¾ç½®æˆâ€œå…³æ³¨è€…â€ï¼Œä»»ä½•人都å¯ä»¥çœ‹åˆ°æ‚¨çš„帖å­ã€‚"
alwaysMarkSensitive: "é»˜è®¤å°†åª’ä½“æ–‡ä»¶æ ‡è®°ä¸ºæ•æ„Ÿå†…容"
@@ -1087,7 +1087,6 @@ _sfx:
antenna: "天线接收"
channel: "频é“通知"
_ago:
- unknown: "未知"
future: "未æ¥"
justNow: "最近"
secondsAgo: "{n}ç§’å‰"
@@ -1131,6 +1130,7 @@ _2fa:
registerKey: "注册密钥"
step1: "首先,在您的设备上安装验è¯åº”用,例如{a}或{b}。"
step2: "ç„¶åŽï¼Œæ‰«æå±å¹•上显示的二维ç ã€‚"
+ step2Url: "在桌é¢åº”用程åºä¸­è¾“入以下URL:"
step3: "输入您的应用æä¾›çš„动æ€å£ä»¤ä»¥å®Œæˆè®¾ç½®ã€‚"
step4: "从现在开始,任何登录æ“ä½œéƒ½å°†è¦æ±‚您æä¾›åЍæ€å£ä»¤ã€‚"
securityKeyInfo: "您å¯ä»¥è®¾ç½®ä½¿ç”¨æ”¯æŒFIDO2的硬件安全密钥ã€è®¾å¤‡ä¸Šçš„æŒ‡çº¹æˆ–PINæ¥ä¿æŠ¤æ‚¨çš„登录过程。"
@@ -1615,6 +1615,7 @@ _notification:
yourFollowRequestAccepted: "您的关注请求已通过"
youWereInvitedToGroup: "您有新的群组邀请"
pollEnded: "é—®å·è°ƒæŸ¥ç»“果已生æˆã€‚"
+ emptyPushNotificationMessage: "推é€é€šçŸ¥å·²æ›´æ–°"
_types:
all: "全部"
follow: "关注中"
@@ -1629,6 +1630,10 @@ _notification:
followRequestAccepted: "关注请求已通过"
groupInvited: "加入群组邀请"
app: "å…³è”应用的通知"
+ _actions:
+ followBack: "回关"
+ reply: "回å¤"
+ renote: "转å‘"
_deck:
alwaysShowMainColumn: "总是显示主列"
columnAlign: "列对é½"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 18c6f17154..f088fdc0e9 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -135,13 +135,14 @@ emojiName: "表情符號å稱"
emojiUrl: "表情符號URL"
addEmoji: "加入表情符號"
settingGuide: "推薦設定"
-cacheRemoteFiles: "ç·©å­˜éžé ç¨‹æª”案"
+cacheRemoteFiles: "å¿«å–é ç«¯æª”案"
cacheRemoteFilesDescription: "ç¦ç”¨æ­¤è¨­å®šæœƒåœæ­¢é ç«¯æª”案的緩存,從而節çœå„²å­˜ç©ºé–“,但資料會因直接連線從而產生é¡å¤–連接數據。"
flagAsBot: "此使用者是機器人"
flagAsBotDescription: "å¦‚æžœæœ¬å¸³æˆ¶æ˜¯ç”±ç¨‹å¼æŽ§åˆ¶ï¼Œè«‹å•Ÿç”¨æ­¤é¸é …。啟用後,會作為標示幫助其他開發者防止機器人之間產生無é™äº’動的行為,並會調整Misskey內部系統將本帳戶識別為機器人"
flagAsCat: "此使用者是貓"
flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示"
flagShowTimelineReplies: "在時間軸上顯示貼文的回覆"
+flagShowTimelineRepliesDescription: "啟用時,時間線除了顯示用戶的貼文以外,還會顯示用戶å°å…¶ä»–貼文的回覆。"
autoAcceptFollowed: "自動追隨中使用者的追隨請求"
addAccount: "添加帳戶"
loginFailed: "登入失敗"
@@ -153,8 +154,8 @@ removeWallpaper: "移除桌布"
searchWith: "æœå°‹: {q}"
youHaveNoLists: "你沒有任何清單"
followConfirm: "你真的è¦è¿½éš¨{name}嗎?"
-proxyAccount: "代ç†å¸³è™Ÿ"
-proxyAccountDescription: "代ç†å¸³è™Ÿæ˜¯åœ¨æŸäº›æƒ…æ³ä¸‹å……當其他伺æœå™¨ç”¨æˆ¶çš„帳號。例如,當使用者將一個來自其他伺æœå™¨çš„å¸³è™Ÿæ”¾åœ¨åˆ—è¡¨ä¸­æ™‚ï¼Œç”±æ–¼æ²’æœ‰å…¶ä»–ä½¿ç”¨è€…é—œæ³¨è©²å¸³è™Ÿï¼Œè©²æŒ‡ä»¤ä¸æœƒå‚³é€åˆ°è©²ä¼ºæœå™¨ä¸Šï¼Œå› æ­¤æœƒç”±ä»£ç†å¸³æˆ¶é—œæ³¨ã€‚"
+proxyAccount: "代ç†å¸³æˆ¶"
+proxyAccountDescription: "代ç†å¸³æˆ¶æ˜¯åœ¨æŸäº›æƒ…æ³ä¸‹å……當其他伺æœå™¨ç”¨æˆ¶çš„帳戶。例如,當使用者將一個來自其他伺æœå™¨çš„å¸³æˆ¶æ”¾åœ¨åˆ—è¡¨ä¸­æ™‚ï¼Œç”±æ–¼æ²’æœ‰å…¶ä»–ä½¿ç”¨è€…é—œæ³¨è©²å¸³æˆ¶ï¼Œè©²æŒ‡ä»¤ä¸æœƒå‚³é€åˆ°è©²ä¼ºæœå™¨ä¸Šï¼Œå› æ­¤æœƒç”±ä»£ç†å¸³æˆ¶é—œæ³¨ã€‚"
host: "主機"
selectUser: "é¸å–使用者"
recipient: "收件人"
@@ -197,7 +198,7 @@ noUsers: "沒有任何使用者"
editProfile: "編輯個人檔案"
noteDeleteConfirm: "確定刪除此貼文嗎?"
pinLimitExceeded: "ä¸èƒ½ç½®é ‚更多貼文了"
-intro: "Misskey 部署完æˆï¼è«‹å»ºç«‹ç®¡ç†å“¡å¸³è™Ÿï¼"
+intro: "Misskey 部署完æˆï¼è«‹å»ºç«‹ç®¡ç†å“¡å¸³æˆ¶ã€‚"
done: "完æˆ"
processing: "處ç†ä¸­"
preview: "é è¦½"
@@ -236,6 +237,8 @@ resetAreYouSure: "確定è¦é‡è¨­å—Žï¼Ÿ"
saved: "已儲存"
messaging: "傳é€è¨Šæ¯"
upload: "上傳"
+keepOriginalUploading: "ä¿ç•™åŽŸåœ–"
+keepOriginalUploadingDescription: "上傳圖片時ä¿ç•™åŽŸå§‹åœ–ç‰‡ã€‚é—œé–‰æ™‚ï¼Œç€è¦½å™¨æœƒåœ¨ä¸Šå‚³æ™‚生æˆä¸€å¼µç”¨æ–¼web發布的圖片。"
fromDrive: "從雲端空間"
fromUrl: "從URL"
uploadFromUrl: "從網å€ä¸Šå‚³"
@@ -357,7 +360,7 @@ enableServiceworker: "開啟 ServiceWorker"
antennaUsersDescription: "指定用æ›è¡Œç¬¦åˆ†éš”的用戶å"
caseSensitive: "å€åˆ†å¤§å°å¯«"
withReplies: "包å«å›žè¦†"
-connectedTo: "您的帳號已連接到以下社交帳號"
+connectedTo: "您的帳戶已連接到以下社交帳戶"
notesAndReplies: "貼文與回覆"
withFiles: "附件"
silence: "ç¦è¨€"
@@ -445,6 +448,7 @@ uiLanguage: "介é¢èªžè¨€"
groupInvited: "您有新的群組邀請"
aboutX: "關於{x}"
useOsNativeEmojis: "使用OS原生表情符號"
+disableDrawer: "ä¸é¡¯ç¤ºä¸‹æ‹‰å¼é¸å–®"
youHaveNoGroups: "找ä¸åˆ°ç¾¤çµ„"
joinOrCreateGroup: "è«‹åŠ å…¥ç¾æœ‰ç¾¤çµ„,或創建新群組。"
noHistory: "沒有歷å²ç´€éŒ„"
@@ -468,7 +472,7 @@ weekOverWeekChanges: "與上週相比"
dayOverDayChanges: "與å‰ä¸€æ—¥ç›¸æ¯”"
appearance: "外觀"
clientSettings: "用戶端設定"
-accountSettings: "帳號設定"
+accountSettings: "帳戶設定"
promotion: "推廣"
promote: "推廣"
numberOfDays: "有效天數"
@@ -477,6 +481,7 @@ showFeaturedNotesInTimeline: "在時間軸上顯示熱門推薦"
objectStorage: "Object Storage (物件儲存)"
useObjectStorage: "使用Object Storage"
objectStorageBaseUrl: "Base URL"
+objectStorageBaseUrlDesc: "引用時的URL。如果您使用的是CDN或åå‘代ç†ï¼Œè¯·æŒ‡å®šå…¶URL,例如S3:“https://<bucket>.s3.amazonaws.comâ€ï¼ŒGCS:“https://storage.googleapis.com/<bucket>â€"
objectStorageBucket: "儲存空間(Bucket)"
objectStorageBucketDesc: "請指定您正在使用的æœå‹™çš„存儲桶å稱。 "
objectStoragePrefix: "å‰ç¶´"
@@ -484,8 +489,11 @@ objectStoragePrefixDesc: "它存儲在此å‰ç¶´ç›®éŒ„下。"
objectStorageEndpoint: "端點(Endpoint)"
objectStorageEndpointDesc: "如è¦ä½¿ç”¨AWS S3,請留空。å¦å‰‡è«‹ä¾ç…§ä½ ä½¿ç”¨çš„æœå‹™å•†çš„èªªæ˜Žæ›¸é€²è¡Œè¨­å®šï¼Œä»¥'<host>'或 '<host>:<port>'的形å¼è¨­å®šç«¯é»žï¼ˆEndpoint)。"
objectStorageRegion: "地域(Region)"
+objectStorageRegionDesc: "指定一個分å€ï¼Œä¾‹å¦‚“xx-east-1â€ã€‚ 如果您使用的æœå‹™æ²’有分å€çš„æ¦‚念,請留空或填寫“us-east-1â€ã€‚"
objectStorageUseSSL: "使用SSL"
+objectStorageUseSSLDesc: "如果ä¸ä½¿ç”¨https進行API連接,請關閉"
objectStorageUseProxy: "使用網路代ç†"
+objectStorageUseProxyDesc: "如果ä¸ä½¿ç”¨ä»£ç†é€²è¡ŒAPI連接,請關閉"
objectStorageSetPublicRead: "上傳時設定為\"public-read\""
serverLogs: "伺æœå™¨æ—¥èªŒ"
deleteAll: "刪除所有記錄"
@@ -513,6 +521,7 @@ sort: "排åº"
ascendingOrder: "昇冪"
descendingOrder: "é™å†ª"
scratchpad: "暫存記憶體"
+scratchpadDescription: "AiScript控制å°ç‚ºAiScriptæä¾›äº†å¯¦é©—環境。您å¯ä»¥åœ¨æ­¤ç·¨å¯«ã€åŸ·è¡Œå’Œç¢ºèªä»£ç¢¼èˆ‡Misskey互動的结果。"
output: "輸出"
script: "腳本"
disablePagesScript: "åœç”¨é é¢çš„AiScript腳本"
@@ -523,6 +532,9 @@ removeAllFollowing: "解除所有追蹤"
removeAllFollowingDescription: "解除{host}所有的追蹤。在實例ä¸å†å­˜åœ¨æ™‚執行。"
userSuspended: "該使用者已被åœç”¨"
userSilenced: "該用戶已被ç¦è¨€ã€‚"
+yourAccountSuspendedTitle: "帳戶已被å‡çµ"
+yourAccountSuspendedDescription: "由於é•å了伺æœå™¨çš„æœå‹™æ¢æ¬¾æˆ–其他原因,該帳戶已被å‡çµã€‚ 您å¯ä»¥èˆ‡ç®¡ç†å“¡é€£ç¹«ä»¥äº†è§£æ›´å¤šè¨Šæ¯ã€‚ è«‹ä¸è¦å‰µå»ºä¸€å€‹æ–°çš„帳戶。"
+menu: "é¸å–®"
divider: "分割線"
addItem: "新增項目"
relays: "中繼"
@@ -546,7 +558,7 @@ enterFileDescription: "輸入標題 "
author: "作者"
leaveConfirm: "有未ä¿å­˜çš„æ›´æ”¹ã€‚è¦æ”¾æ£„嗎?"
manage: "管ç†"
-plugins: "æ’ä»¶"
+plugins: "外掛"
deck: "多欄模å¼"
undeck: "å–æ¶ˆå¤šæ¬„模å¼"
useBlurEffectForModal: "在模態框使用模糊效果"
@@ -556,10 +568,12 @@ height: "高度"
large: "大"
medium: "中"
small: "å°"
+generateAccessToken: "ç™¼è¡Œå­˜å–æ¬Šæ–"
permission: "權é™"
enableAll: "啟用全部"
disableAll: "åœç”¨å…¨éƒ¨"
-tokenRequested: "å…許存å–帳號"
+tokenRequested: "å…許存å–帳戶"
+pluginTokenRequestedDescription: "æ­¤å¤–æŽ›å°‡æ“æœ‰åœ¨æ­¤è¨­å®šçš„æ¬Šé™ã€‚"
notificationType: "通知形å¼"
edit: "編輯"
useStarForReactionFallback: "以★代替未知的表情符號"
@@ -574,8 +588,13 @@ smtpPort: "埠"
smtpUser: "使用者å稱"
smtpPass: "密碼"
emptyToDisableSmtpAuth: "留空使用者å稱和密碼以關閉SMTP驗證。"
+smtpSecure: "在 SMTP é€£æŽ¥ä¸­ä½¿ç”¨éš±å¼ SSL/TLS"
+smtpSecureInfo: "使用STARTTLS時關閉。"
testEmail: "測試郵件發é€"
-wordMute: "éœéŸ³æ–‡å­—"
+wordMute: "被éœéŸ³çš„æ–‡å­—"
+regexpError: "æ­£è¦è¡¨é”å¼éŒ¯èª¤"
+regexpErrorDescription: "{tab} éœéŸ³æ–‡å­—的第 {line} 行的正è¦è¡¨é”弿œ‰éŒ¯èª¤ï¼š"
+instanceMute: "實例的éœéŸ³"
userSaysSomething: "{name}說了什麼"
makeActive: "啟用"
display: "檢視"
@@ -606,6 +625,8 @@ abuseReported: "回報已é€å‡ºã€‚æ„Ÿè¬æ‚¨çš„報告。"
reporter: "檢舉者"
reporteeOrigin: "檢舉來æº"
reporterOrigin: "檢舉者來æº"
+forwardReport: "將報告轉é€çµ¦é ç«¯å¯¦ä¾‹"
+forwardReportIsAnonymous: "在é ç«¯å¯¦ä¾‹ä¸Šçœ‹ä¸åˆ°æ‚¨çš„資訊,顯示的報告者是匿å的系统帳戶。"
send: "發é€"
abuseMarkAsResolved: "處ç†å®Œç•¢"
openInNewTab: "在新分é ä¸­é–‹å•Ÿ"
@@ -667,6 +688,7 @@ center: "置中"
wide: "寬"
narrow: "窄"
reloadToApplySetting: "設定將會在é é¢é‡æ–°è¼‰å…¥ä¹‹å¾Œç”Ÿæ•ˆã€‚è¦ç¾åœ¨å°±é‡è¼‰é é¢å—Žï¼Ÿ"
+needReloadToApply: "å¿…é ˆé‡æ–°è¼‰å…¥æ‰æœƒç”Ÿæ•ˆã€‚"
showTitlebar: "顯示標題列"
clearCache: "清除快å–資料"
onlineUsersCount: "{n}人正在線上"
@@ -727,6 +749,7 @@ notRecommended: "ä¸æŽ¨è–¦"
botProtection: "Bot防護"
instanceBlocking: "å·²å°éŽ–çš„å¯¦ä¾‹"
selectAccount: "鏿“‡å¸³æˆ¶"
+switchAccount: "切æ›å¸³æˆ¶"
enabled: "已啟用"
disabled: "å·²åœç”¨"
quickAction: "å¿«æ·æ“作"
@@ -753,32 +776,92 @@ emailNotConfiguredWarning: "沒有設定電å­éƒµä»¶åœ°å€"
ratio: "%"
previewNoteText: "é è¦½æ–‡æœ¬"
customCss: "自定義 CSS"
+customCssWarn: "這個設定必須由具備相關知識的人員æ“作,ä¸ç•¶çš„設定å¯èƒ½å¯¼è‡´å®¢æˆ¶ç«¯ç„¡æ³•正常使用。"
global: "公開"
+squareAvatars: "é ­åƒä»¥æ–¹å½¢é¡¯ç¤º"
sent: "發é€"
received: "æ”¶å–"
searchResult: "æœå°‹çµæžœ"
hashtags: "#tag"
troubleshooting: "故障排除"
useBlurEffect: "在 UI 上使用模糊效果"
+learnMore: "更多資訊"
misskeyUpdated: "Misskey 更新完æˆï¼"
+whatIsNew: "顯示更新資訊"
translate: "翻譯"
translatedFrom: "從 {x} 翻譯"
accountDeletionInProgress: "正在刪除帳戶"
+usernameInfo: "在伺æœå™¨ä¸Šæ‚¨çš„帳戶是唯一的識別å稱。您å¯ä»¥ä½¿ç”¨å­—æ¯ (a ~ z, A ~ Z)ã€æ•¸å­— (0 ~ 9) 和下底線 (_)ã€‚ä¹‹å¾Œå¸³æˆ¶åæ˜¯ä¸èƒ½æ›´æ”¹çš„。"
+aiChanMode: "å°è—模å¼"
+keepCw: "ä¿æŒCW"
pubSub: "Pub/Sub 帳戶"
+lastCommunication: "最近的通信"
resolved: "已解決"
unresolved: "未解決"
breakFollow: "移除追蹤者"
+itsOn: "已開啟"
+itsOff: "已關閉"
+emailRequiredForSignup: "註冊帳戶需è¦é›»å­éƒµä»¶åœ°å€"
+unread: "未讀"
+filter: "篩é¸"
+controlPanel: "控制å°"
+manageAccounts: "管ç†å¸³æˆ¶"
+makeReactionsPublic: "將回應設為公開"
+makeReactionsPublicDescription: "將您åšéŽçš„回應設為公開å¯è¦‹ã€‚"
+classic: "ç¶“å…¸"
+muteThread: "將貼文串設為éœéŸ³"
+unmuteThread: "將貼文串的éœéŸ³è§£é™¤"
+ffVisibility: "連接的公開範åœ"
+ffVisibilityDescription: "您å¯ä»¥è¨­å®šæ‚¨çš„關注/關注者資訊的公開範åœ"
+continueThread: "查看更多貼文"
+deleteAccountConfirm: "å°‡è¦åˆªé™¤å¸³æˆ¶ã€‚是å¦ç¢ºå®šï¼Ÿ"
+incorrectPassword: "密碼錯誤。"
+voteConfirm: "確定投給「{choice}ã€ï¼Ÿ"
hide: "éš±è—"
+leaveGroup: "離開群組"
leaveGroupConfirm: "確定離開「{name}ã€ï¼Ÿ"
+useDrawerReactionPickerForMobile: "在移動設備上使用抽屜顯示"
+welcomeBackWithName: "歡迎回來,{name}"
+clickToFinishEmailVerification: "點擊 [{ok}] 完æˆé›»å­éƒµä»¶åœ°å€èªè­‰ã€‚"
+overridedDeviceKind: "è£ç½®é¡žåž‹"
+smartphone: "智慧型手機"
+tablet: "å¹³æ¿"
auto: "自動"
+themeColor: "主題é¡è‰²"
+size: "大å°"
+numberOfColumn: "列數"
searchByGoogle: "æœå°‹"
+instanceDefaultLightTheme: "實例é è¨­çš„æ·ºè‰²ä¸»é¡Œ"
+instanceDefaultDarkTheme: "實例é è¨­çš„æ·±è‰²ä¸»é¡Œ"
+instanceDefaultThemeDescription: "輸入物件形å¼çš„主题代碼"
+mutePeriod: "éœéŸ³çš„æœŸé™"
indefinitely: "無期é™"
+tenMinutes: "10分é˜"
+oneHour: "1å°æ™‚"
+oneDay: "1天"
+oneWeek: "1週"
+reflectMayTakeTime: "å¯èƒ½éœ€è¦ä¸€äº›æ™‚é–“æ‰æœƒå‡ºç¾æ•ˆæžœã€‚"
+failedToFetchAccountInformation: "å–得帳戶資訊失敗"
+_emailUnavailable:
+ used: "已經在使用中"
+ format: "æ ¼å¼ç„¡æ•ˆ"
+ disposable: "䏿˜¯æ°¸ä¹…å¯ç”¨çš„地å€"
+ mx: "郵件伺æœå™¨ä¸æ­£ç¢º"
+ smtp: "郵件伺æœå™¨æ²’有應答"
_ffVisibility:
public: "發佈"
+ followers: "åªæœ‰é—œæ³¨ä½ çš„用戶能看到"
private: "ç§å¯†"
_signup:
almostThere: "å³å°‡å®Œæˆ"
+ emailAddressInfo: "請輸入您所使用的電å­éƒµä»¶åœ°å€ã€‚é›»å­éƒµä»¶åœ°å€ä¸æœƒè¢«å…¬é–‹ã€‚"
+ emailSent: "已將確èªéƒµä»¶ç™¼é€è‡³æ‚¨è¼¸å…¥çš„é›»å­éƒµä»¶åœ°å€ ({email})。請開啟電å­éƒµä»¶ä¸­çš„連çµä»¥å®Œæˆå¸³æˆ¶å‰µå»ºã€‚"
_accountDelete:
+ accountDelete: "刪除帳戶"
+ mayTakeTime: "刪除帳戶的處ç†è² è·è¼ƒå¤§ï¼Œå¦‚果帳戶產生的內容數é‡ä¸Šèˆ¹çš„æª”案數é‡è¼ƒå¤šçš„話,就需è¦èŠ±è´¹ä¸€æ®µæ™‚é–“æ‰èƒ½å®Œæˆã€‚"
+ sendEmail: "帳戶删除完æˆå¾Œï¼Œå°‡å‘註冊地電å­éƒµä»¶åœ°å€ç™¼é€é€šçŸ¥ã€‚"
+ requestAccountDelete: "刪除帳戶請求"
+ started: "已開始刪除作業。"
inProgress: "正在刪除"
_ad:
back: "返回"
@@ -800,7 +883,7 @@ _email:
_plugin:
install: "安è£å¤–掛組件"
installWarn: "è«‹ä¸è¦å®‰è£ä¾†æºä¸æ˜Žçš„外掛組件。"
- manage: "ç®¡ç†æ’ä»¶"
+ manage: "管ç†å¤–掛"
_registry:
scope: "範åœ"
key: "機碼"
@@ -833,14 +916,21 @@ _mfm:
link: "éˆæŽ¥"
linkDescription: "您å¯ä»¥å°‡ç‰¹å®šç¯„åœçš„æ–‡ç« èˆ‡ URL 相關è¯ã€‚ "
bold: "ç²—é«”"
+ boldDescription: "å¯ä»¥å°‡æ–‡å­—顯示为粗體æ¥å¼·èª¿ã€‚"
small: "縮å°"
+ smallDescription: "å¯ä»¥ä½¿å…§å®¹æ–‡å­—變å°ã€è®Šæ·¡ã€‚"
center: "置中"
+ centerDescription: "å¯ä»¥å°‡å…§å®¹ç½®ä¸­é¡¯ç¤ºã€‚"
inlineCode: "程å¼ç¢¼(内嵌)"
+ inlineCodeDescription: "在行內用高亮度顯示,例如程å¼ç¢¼èªžæ³•。"
blockCode: "程å¼ç¢¼(å€å¡Š)"
+ blockCodeDescription: "在å€å¡Šä¸­ç”¨é«˜äº®åº¦é¡¯ç¤ºï¼Œä¾‹å¦‚複數行的程å¼ç¢¼èªžæ³•。"
inlineMath: "數學公å¼(內嵌)"
inlineMathDescription: "顯示內嵌的KaTex數學公å¼ã€‚"
blockMath: "數學公å¼(方塊)"
+ blockMathDescription: "以å€å¡Šé¡¯ç¤ºè¤‡æ•¸è¡Œçš„KaTex數學å¼ã€‚"
quote: "引用"
+ quoteDescription: "å¯ä»¥ç”¨ä¾†è¡¨ç¤ºå¼•用的内容。"
emoji: "自訂表情符號"
emojiDescription: "您å¯ä»¥é€šéŽå°‡è‡ªå®šç¾©è¡¨æƒ…符號å稱括在冒號中來顯示自定義表情符號。 "
search: "æœå°‹"
@@ -849,22 +939,34 @@ _mfm:
flipDescription: "將內容上下或左å³ç¿»è½‰ã€‚"
jelly: "å‹•ç•«(æžœå‡)"
jellyDescription: "顯示果å‡ä¸€æ¨£çš„動畫效果。"
+ tada: "動畫(é˜ï½žï¼‰"
+ tadaDescription: "顯示「é˜ï½žï¼ã€é€™ç¨®æ„Ÿè¦ºçš„動畫效果。"
jump: "動畫(跳動)"
+ jumpDescription: "顯示跳動的動畫效果。"
bounce: "å‹•ç•«(å彈)"
+ bounceDescription: "顯示有彈性的動畫效果。"
shake: "å‹•ç•«(æ–æ™ƒ)"
+ shakeDescription: "顯示顫抖的動畫效果。"
twitch: "動畫(顫抖)"
twitchDescription: "顯示強烈顫抖的動畫效果。"
spin: "動畫(旋轉)"
spinDescription: "顯示旋轉的動畫效果。"
x2: "大"
+ x2Description: "放大顯示內容。"
x3: "較大"
x3Description: "放大顯示內容。"
x4: "最大"
x4Description: "將顯示內容放至最大。"
blur: "模糊"
+ blurDescription: "產生模糊效果。将游標放在上é¢å³å¯å°‡å†…容顯示出來。"
font: "å­—åž‹"
fontDescription: "您å¯ä»¥è¨­å®šé¡¯ç¤ºå…§å®¹çš„å­—åž‹"
+ rainbow: "彩虹"
+ rainbowDescription: "用彩虹色來顯示內容。"
+ sparkle: "閃閃發光"
+ sparkleDescription: "æ·»åŠ é–ƒé–ƒç™¼å…‰çš„ç²’å­æ•ˆæžœã€‚"
rotate: "旋轉"
+ rotateDescription: "以指定的角度旋轉。"
_instanceTicker:
none: "éš±è—"
remote: "å‘é ç«¯ä½¿ç”¨è€…顯示"
@@ -884,11 +986,24 @@ _channel:
usersCount: "有{n}人åƒèˆ‡"
notesCount: "有{n}個貼文"
_menuDisplay:
+ sideFull: "å´å‘"
+ sideIcon: "å´å‘(圖示)"
+ top: "頂部"
hide: "éš±è—"
_wordMute:
muteWords: "加入éœéŸ³æ–‡å­—"
+ muteWordsDescription: "用空格分隔指定AND,用æ›è¡Œåˆ†éš”æŒ‡å®šOR。"
+ muteWordsDescription2: "將關éµå­—用斜線括起來表示正è¦è¡¨é”å¼ã€‚"
softDescription: "éš±è—æ™‚間軸中指定æ¢ä»¶çš„貼文。"
+ hardDescription: "具有指定æ¢ä»¶çš„è²¼æ–‡å°‡ä¸æ·»åŠ åˆ°æ™‚é–“è»¸ã€‚ å³ä½¿æ‚¨æ›´æ”¹æ¢ä»¶ï¼Œæœªè¢«æ·»åŠ çš„è²¼æ–‡ä¹Ÿæœƒè¢«æŽ’é™¤åœ¨å¤–ã€‚"
+ soft: "軟性éœéŸ³"
+ hard: "硬性éœéŸ³"
mutedNotes: "å·²éœéŸ³çš„貼文"
+_instanceMute:
+ instanceMuteDescription: "包括å°è¢«éœéŸ³å¯¦ä¾‹ä¸Šçš„用戶的回覆,被設定的實例上所有貼文åŠè½‰ç™¼éƒ½æœƒè¢«éœéŸ³ã€‚"
+ instanceMuteDescription2: "設定時以æ›è¡Œé€²è¡Œåˆ†éš”"
+ title: "被設定的實例,貼文將被隱è—。"
+ heading: "將實例éœéŸ³"
_theme:
explore: "å–得佈景主題"
install: "安è£ä½ˆæ™¯ä¸»é¡Œ"
@@ -902,10 +1017,12 @@ _theme:
invalid: "主題格å¼éŒ¯èª¤"
make: "製作主題"
base: "基於"
+ addConstant: "添加常數"
constant: "常數"
defaultValue: "é è¨­å€¼"
color: "é¡è‰²"
refProp: "查看屬性 "
+ refConst: "查看常數"
key: "按éµ"
func: "函数"
funcKind: "功能類型"
@@ -914,6 +1031,9 @@ _theme:
alpha: "逿˜Žåº¦"
darken: "暗度"
lighten: "亮度"
+ inputConstantName: "請輸入常數的å稱"
+ importInfo: "您å¯ä»¥åœ¨æ­¤è²¼ä¸Šä¸»é¡Œä»£ç¢¼ï¼Œå°‡å…¶åŒ¯å…¥ç·¨è¼¯å™¨ä¸­"
+ deleteConstantConfirm: "確定è¦åˆ é™¤å¸¸æ•¸{const}嗎?"
keys:
accent: "é‡é»žè‰²å½©"
bg: "背景"
@@ -933,6 +1053,7 @@ _theme:
mention: "æåˆ°"
mentionMe: "æåˆ°äº†æˆ‘"
renote: "轉發貼文"
+ modalBg: "å°è©±æ¡†èƒŒæ™¯"
divider: "分割線"
scrollbarHandle: "æ²å‹•æ¢"
scrollbarHandleHover: "æ²å‹•æ¢ (漂浮)"
@@ -966,7 +1087,6 @@ _sfx:
antenna: "天線接收"
channel: "é »é“通知"
_ago:
- unknown: "未知"
future: "未來"
justNow: "剛剛"
secondsAgo: "{n}ç§’å‰"
@@ -1010,9 +1130,13 @@ _2fa:
registerKey: "註冊éµ"
step1: "首先,在您的設備上安è£äºŒæ­¥é©—證程å¼ï¼Œä¾‹å¦‚{a}或{b}。"
step2: "然後,掃æèž¢å¹•上的QR code。"
+ step2Url: "在桌é¢ç‰ˆæ‡‰ç”¨ä¸­ï¼Œè«‹è¼¸å…¥ä»¥ä¸‹çš„URL:"
+ step3: "輸入您的Appæä¾›çš„æ¬Šæ–以完æˆè¨­å®šã€‚"
+ step4: "從ç¾åœ¨é–‹å§‹ï¼Œä»»ä½•登入æ“ä½œéƒ½å°‡è¦æ±‚您æä¾›æ¬Šæ–。"
+ securityKeyInfo: "您å¯ä»¥è¨­å®šä½¿ç”¨æ”¯æ´FIDO2的硬體安全鎖ã€çµ‚端設備的指纹èªè­‰æˆ–者PIN碼來登入。"
_permissions:
- "read:account": "查看帳戶信æ¯"
- "write:account": "更改帳戶信æ¯"
+ "read:account": "查看我的帳戶資訊"
+ "write:account": "更改我的帳戶資訊"
"read:blocks": "å·²å°éŽ–ç”¨æˆ¶åå–®"
"write:blocks": "編輯已å°éŽ–ç”¨æˆ¶åå–®"
"read:drive": "å­˜å–雲端硬碟"
@@ -1039,6 +1163,10 @@ _permissions:
"write:user-groups": "編輯使用者群組"
"read:channels": "已查看的頻é“"
"write:channels": "編輯頻é“"
+ "read:gallery": "ç€è¦½åœ–庫"
+ "write:gallery": "æ“作圖庫"
+ "read:gallery-likes": "讀å–喜歡的圖片"
+ "write:gallery-likes": "æ“作喜歡的圖片"
_auth:
shareAccess: "è¦æŽˆæ¬Šã€Œâ€œ{name}â€ã€å­˜å–您的帳戶嗎?"
shareAccessAsk: "æ‚¨ç¢ºå®šè¦æŽˆæ¬Šé€™å€‹æ‡‰ç”¨ç¨‹å¼ä½¿ç”¨æ‚¨çš„帳戶嗎?"
@@ -1078,6 +1206,8 @@ _widgets:
onlineUsers: "線上的用戶"
jobQueue: "佇列"
serverMetric: "æœå‹™å™¨æŒ‡æ¨™ "
+ aiscript: "AiScript控制å°"
+ aichan: "å°è—"
_cw:
hide: "éš±è—"
show: "ç€è¦½æ›´å¤š"
@@ -1103,12 +1233,15 @@ _poll:
closed: "å·²çµæŸ"
remainingDays: "{d}天{h}å°æ™‚å¾ŒçµæŸ"
remainingHours: "{h}å°æ™‚{m}åˆ†å¾ŒçµæŸ"
+ remainingMinutes: "{m}分{s}ç§’å¾ŒçµæŸ"
remainingSeconds: "{s}秒後截止"
_visibility:
public: "公開"
publicDescription: "發布給所有用戶 "
home: "首é "
+ homeDescription: "僅發é€è‡³é¦–é çš„æ™‚間軸"
followers: "追隨者"
+ followersDescription: "僅發é€è‡³é—œæ³¨è€…"
specified: "指定使用者"
specifiedDescription: "僅發é€è‡³æŒ‡å®šä½¿ç”¨è€…"
localOnly: "åƒ…é™æœ¬åœ°"
@@ -1131,6 +1264,7 @@ _profile:
youCanIncludeHashtags: "你也å¯ä»¥åœ¨ã€Œé—œæ–¼æˆ‘ã€ä¸­åŠ ä¸Š #tag"
metadata: "進階資訊"
metadataEdit: "編輯進階資訊"
+ metadataDescription: "å¯ä»¥åœ¨å€‹äººè³‡æ–™ä¸­ä»¥è¡¨æ ¼å½¢å¼é¡¯ç¤ºå…¶ä»–資訊。"
metadataLabel: "標籤"
metadataContent: "内容"
changeAvatar: "æ›´æ›å¤§é ­è²¼"
@@ -1141,6 +1275,8 @@ _exportOrImport:
muteList: "éœéŸ³"
blockingList: "å°éŽ–"
userLists: "清單"
+ excludeMutingUsers: "排除被éœéŸ³çš„用戶"
+ excludeInactiveUsers: "æŽ’é™¤ä¸æ´»èºå¸³æˆ¶"
_charts:
federation: "ç«™å°è¯é‚¦"
apRequest: "請求"
@@ -1418,6 +1554,7 @@ _pages:
_seedRandomPick:
arg1: "種å­"
arg2: "清單"
+ DRPWPM: "ä»Žæ©ŸçŽ‡åˆ—è¡¨ä¸­éš¨æ©Ÿé¸æ“‡ï¼ˆæ¯å€‹ç”¨æˆ·æ¯å¤©ï¼‰"
_DRPWPM:
arg1: "字串串列"
pick: "從清單中é¸å–"
@@ -1448,6 +1585,8 @@ _pages:
_for:
arg1: "é‡è¤‡æ¬¡æ•¸"
arg2: "處ç†"
+ typeError: "æ§½åƒæ•¸{slot}需è¦å‚³å…¥â€œ{expect}â€ï¼Œä½†æ˜¯å¯¦éš›å‚³å…¥ç‚ºâ€œ{actual}â€ï¼"
+ thereIsEmptySlot: "åƒæ•¸{slot}是空的ï¼"
types:
string: "字串"
number: "数值"
@@ -1470,10 +1609,13 @@ _notification:
youRenoted: "{name} 轉發了你的貼文"
youGotPoll: "{name}已投票"
youGotMessagingMessageFromUser: "{name}發é€çµ¦æ‚¨çš„訊æ¯"
+ youGotMessagingMessageFromGroup: "{name}發é€çµ¦æ‚¨çš„訊æ¯"
youWereFollowed: "您有新的追隨者"
youReceivedFollowRequest: "您有新的追隨請求"
yourFollowRequestAccepted: "您的追隨請求已通éŽ"
youWereInvitedToGroup: "您有新的群組邀請"
+ pollEnded: "å•å·èª¿æŸ¥å·²ç”¢ç”Ÿçµæžœ"
+ emptyPushNotificationMessage: "推é€é€šçŸ¥å·²æ›´æ–°"
_types:
all: "全部 "
follow: "追隨中"
@@ -1483,10 +1625,15 @@ _notification:
quote: "引用"
reaction: "忇‰"
pollVote: "統計已投票數"
+ pollEnded: "å•å·èª¿æŸ¥çµæŸ"
receiveFollowRequest: "已收到追隨請求"
followRequestAccepted: "追隨請求已接å—"
groupInvited: "加入社群邀請"
app: "應用程å¼é€šçŸ¥"
+ _actions:
+ followBack: "回關"
+ reply: "回覆"
+ renote: "轉發"
_deck:
alwaysShowMainColumn: "總是顯示主欄"
columnAlign: "å°é½Šæ¬„ä½"
diff --git a/package.json b/package.json
index 606e2b7332..e3340005ad 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "misskey",
- "version": "12.110.1",
+ "version": "12.111.0",
"codename": "indigo",
"repository": {
"type": "git",
@@ -19,10 +19,10 @@
"watch": "npm run dev",
"dev": "node ./scripts/dev.js",
"lint": "node ./scripts/lint.js",
- "cy:open": "cypress open",
+ "cy:open": "cypress open --browser --e2e --config-file=cypress.config.ts",
"cy:run": "cypress run",
"e2e": "start-server-and-test start:test http://localhost:61812 cy:run",
- "mocha": "cd packages/backend && cross-env TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" npx mocha",
+ "mocha": "cd packages/backend && cross-env NODE_ENV=test TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" npx mocha",
"test": "npm run mocha",
"format": "gulp format",
"clean": "node ./scripts/clean.js",
@@ -30,8 +30,6 @@
"cleanall": "npm run clean-all"
},
"dependencies": {
- "@types/gulp": "4.0.9",
- "@types/gulp-rename": "2.0.1",
"execa": "5.1.1",
"gulp": "4.0.2",
"gulp-cssnano": "2.1.3",
@@ -41,10 +39,12 @@
"js-yaml": "4.1.0"
},
"devDependencies": {
- "@typescript-eslint/parser": "5.18.0",
+ "@types/gulp": "4.0.9",
+ "@types/gulp-rename": "2.0.1",
+ "@typescript-eslint/parser": "5.27.1",
"cross-env": "7.0.3",
- "cypress": "9.5.3",
+ "cypress": "10.0.3",
"start-server-and-test": "1.14.0",
- "typescript": "4.6.3"
+ "typescript": "4.7.3"
}
}
diff --git a/packages/backend/.eslintrc.cjs b/packages/backend/.eslintrc.cjs
index e2e31e9e33..5a06889dcd 100644
--- a/packages/backend/.eslintrc.cjs
+++ b/packages/backend/.eslintrc.cjs
@@ -6,4 +6,27 @@ module.exports = {
extends: [
'../shared/.eslintrc.js',
],
+ rules: {
+ 'import/order': ['warn', {
+ 'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
+ 'pathGroups': [
+ {
+ 'pattern': '@/**',
+ 'group': 'external',
+ 'position': 'after'
+ }
+ ],
+ }],
+ 'no-restricted-globals': [
+ 'error',
+ {
+ 'name': '__dirname',
+ 'message': 'Not in ESModule. Use `import.meta.url` instead.'
+ },
+ {
+ 'name': '__filename',
+ 'message': 'Not in ESModule. Use `import.meta.url` instead.'
+ }
+ ]
+ },
};
diff --git a/packages/backend/.mocharc.json b/packages/backend/.mocharc.json
index 26628066eb..87c571cfd6 100644
--- a/packages/backend/.mocharc.json
+++ b/packages/backend/.mocharc.json
@@ -5,6 +5,6 @@
"loader=./test/loader.js"
],
"slow": 1000,
- "timeout": 35000,
+ "timeout": 10000,
"exit": true
}
diff --git a/packages/backend/.vscode/settings.json b/packages/backend/.vscode/settings.json
index df3bf05071..9fb3b29d4a 100644
--- a/packages/backend/.vscode/settings.json
+++ b/packages/backend/.vscode/settings.json
@@ -2,5 +2,9 @@
"typescript.tsdk": "node_modules\\typescript\\lib",
"path-intellisense.mappings": {
"@": "${workspaceRoot}/packages/backend/src/"
+ },
+ "editor.formatOnSave": true,
+ "editor.codeActionsOnSave": {
+ "source.fixAll": true
}
}
diff --git a/packages/backend/migration/1651224615271-foreign-key.js b/packages/backend/migration/1651224615271-foreign-key.js
new file mode 100644
index 0000000000..44ba7fb6c4
--- /dev/null
+++ b/packages/backend/migration/1651224615271-foreign-key.js
@@ -0,0 +1,89 @@
+export class foreignKeyReports1651224615271 {
+ name = 'foreignKeyReports1651224615271'
+
+ async up(queryRunner) {
+ await Promise.all([
+ queryRunner.query(`ALTER INDEX "public"."IDX_seoignmeoprigmkpodgrjmkpormg" RENAME TO "IDX_c8cc87bd0f2f4487d17c651fbf"`),
+ queryRunner.query(`DROP INDEX "public"."IDX_note_on_channelId_and_id_desc"`),
+
+ // remove unnecessary default null, see also down
+ queryRunner.query(`ALTER TABLE "user" ALTER COLUMN "followersUri" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "session" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "appId" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "name" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "description" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "iconUrl" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "softwareName" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "softwareVersion" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "name" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "description" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "maintainerName" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "maintainerEmail" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "iconUrl" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "faviconUrl" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "themeColor" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "clip" ALTER COLUMN "description" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "note" ALTER COLUMN "channelId" DROP DEFAULT`),
+ queryRunner.query(`ALTER TABLE "abuse_user_report" ALTER COLUMN "comment" DROP DEFAULT`),
+
+ queryRunner.query(`CREATE INDEX "IDX_315c779174fe8247ab324f036e" ON "drive_file" ("isLink")`),
+ queryRunner.query(`CREATE INDEX "IDX_f22169eb10657bded6d875ac8f" ON "note" ("channelId")`),
+ queryRunner.query(`CREATE INDEX "IDX_a9021cc2e1feb5f72d3db6e9f5" ON "abuse_user_report" ("targetUserId")`),
+
+ queryRunner.query(`DELETE FROM "abuse_user_report" WHERE "targetUserId" NOT IN (SELECT "id" FROM "user")`).then(() => {
+ queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f" FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
+ }),
+
+ queryRunner.query(`ALTER TABLE "poll" ADD CONSTRAINT "UQ_da851e06d0dfe2ef397d8b1bf1b" UNIQUE ("noteId")`),
+ queryRunner.query(`ALTER TABLE "user_keypair" ADD CONSTRAINT "UQ_f4853eb41ab722fe05f81cedeb6" UNIQUE ("userId")`),
+ queryRunner.query(`ALTER TABLE "user_profile" ADD CONSTRAINT "UQ_51cb79b5555effaf7d69ba1cff9" UNIQUE ("userId")`),
+ queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "UQ_10c146e4b39b443ede016f6736d" UNIQUE ("userId")`),
+ queryRunner.query(`ALTER TABLE "promo_note" ADD CONSTRAINT "UQ_e263909ca4fe5d57f8d4230dd5c" UNIQUE ("noteId")`),
+
+ queryRunner.query(`ALTER TABLE "page" RENAME CONSTRAINT "FK_3126dd7c502c9e4d7597ef7ef10" TO "FK_a9ca79ad939bf06066b81c9d3aa"`),
+
+ queryRunner.query(`ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum" ADD VALUE 'pollEnded' AFTER 'pollVote'`),
+ ]);
+ }
+
+ async down(queryRunner) {
+ await Promise.all([
+ // There is no ALTER TYPE REMOVE VALUE query, so the reverse operation is a bit more complex
+ queryRunner.query(`UPDATE "user_profile" SET "mutingNotificationTypes" = array_remove("mutingNotificationTypes", 'pollEnded')`)
+ .then(() =>
+ queryRunner.query(`CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`)
+ ).then(() =>
+ queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" DROP DEFAULT`)
+ ).then(() =>
+ queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" TYPE "public"."user_profile_mutingnotificationtypes_enum_old"[] USING "mutingNotificationTypes"::"text"::"public"."user_profile_mutingnotificationtypes_enum_old"[]`)
+ ).then(() =>
+ queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" SET DEFAULT '{}'`)
+ ).then(() =>
+ queryRunner.query(`DROP TYPE "public"."user_profile_mutingnotificationtypes_enum"`)
+ ).then(() =>
+ queryRunner.query(`ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum_old" RENAME TO "user_profile_mutingnotificationtypes_enum"`)
+ ),
+
+ queryRunner.query(`ALTER TABLE "page" RENAME CONSTRAINT "FK_a9ca79ad939bf06066b81c9d3aa" TO "FK_3126dd7c502c9e4d7597ef7ef10"`),
+
+ queryRunner.query(`ALTER TABLE "promo_note" DROP CONSTRAINT "UQ_e263909ca4fe5d57f8d4230dd5c"`),
+ queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "UQ_10c146e4b39b443ede016f6736d"`),
+ queryRunner.query(`ALTER TABLE "user_profile" DROP CONSTRAINT "UQ_51cb79b5555effaf7d69ba1cff9"`),
+ queryRunner.query(`ALTER TABLE "user_keypair" DROP CONSTRAINT "UQ_f4853eb41ab722fe05f81cedeb6"`),
+ queryRunner.query(`ALTER TABLE "poll" DROP CONSTRAINT "UQ_da851e06d0dfe2ef397d8b1bf1b"`),
+
+ queryRunner.query(`ALTER TABLE "abuse_user_report" ALTER COLUMN "comment" SET DEFAULT '{}'`),
+ queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f"`),
+
+ queryRunner.query(`DROP INDEX "public"."IDX_a9021cc2e1feb5f72d3db6e9f5"`),
+ queryRunner.query(`DROP INDEX "public"."IDX_f22169eb10657bded6d875ac8f"`),
+ queryRunner.query(`DROP INDEX "public"."IDX_315c779174fe8247ab324f036e"`),
+
+ /* DEFAULT's are not set again because if the column can be NULL, then DEFAULT NULL is not necessary.
+ see also https://github.com/typeorm/typeorm/issues/7579#issuecomment-835423615 */
+
+ queryRunner.query(`CREATE INDEX "IDX_note_on_channelId_and_id_desc" ON "note" ("id", "channelId") `),
+ queryRunner.query(`ALTER INDEX "public"."IDX_c8cc87bd0f2f4487d17c651fbf" RENAME TO "IDX_seoignmeoprigmkpodgrjmkpormg"`),
+ ]);
+ }
+}
diff --git a/packages/backend/migration/1652859567549-uniform-themecolor.js b/packages/backend/migration/1652859567549-uniform-themecolor.js
new file mode 100644
index 0000000000..8da1fd7fbb
--- /dev/null
+++ b/packages/backend/migration/1652859567549-uniform-themecolor.js
@@ -0,0 +1,36 @@
+import tinycolor from 'tinycolor2';
+
+export class uniformThemecolor1652859567549 {
+ name = 'uniformThemecolor1652859567549'
+
+ async up(queryRunner) {
+ const formatColor = (color) => {
+ let tc = new tinycolor(color);
+ if (tc.isValid()) {
+ return tc.toHexString();
+ } else {
+ return null;
+ }
+ };
+
+ await queryRunner.query('SELECT "id", "themeColor" FROM "instance" WHERE "themeColor" IS NOT NULL')
+ .then(instances => Promise.all(instances.map(instance => {
+ // update theme color to uniform format, e.g. #00ff00
+ // invalid theme colors get set to null
+ return queryRunner.query('UPDATE "instance" SET "themeColor" = $1 WHERE "id" = $2', [formatColor(instance.themeColor), instance.id]);
+ })));
+
+ // also fix own theme color
+ await queryRunner.query('SELECT "themeColor" FROM "meta" WHERE "themeColor" IS NOT NULL LIMIT 1')
+ .then(metas => {
+ if (metas.length > 0) {
+ return queryRunner.query('UPDATE "meta" SET "themeColor" = $1', [formatColor(metas[0].themeColor)]);
+ }
+ });
+ }
+
+ async down(queryRunner) {
+ // The original representation is not stored, so migrating back is not possible.
+ // The new format also works in older versions so this is not a problem.
+ }
+}
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 314818f80b..2186dcc6a9 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -6,7 +6,7 @@
"build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json",
"watch": "node watch.mjs",
"lint": "eslint --quiet \"src/**/*.ts\"",
- "mocha": "cross-env TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha",
+ "mocha": "cross-env NODE_ENV=test TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha",
"test": "npm run mocha"
},
"resolutions": {
@@ -14,71 +14,25 @@
"lodash": "^4.17.21"
},
"dependencies": {
- "@discordapp/twemoji": "13.1.1",
+ "@bull-board/koa": "3.11.1",
+ "@discordapp/twemoji": "14.0.2",
"@elastic/elasticsearch": "7.11.0",
"@koa/cors": "3.1.0",
"@koa/multer": "3.0.0",
"@koa/router": "9.0.1",
- "@sinonjs/fake-timers": "9.1.1",
+ "@peertube/http-signature": "1.6.0",
+ "@sinonjs/fake-timers": "9.1.2",
"@syuilo/aiscript": "0.11.1",
- "@types/bcryptjs": "2.4.2",
- "@types/bull": "3.15.8",
- "@types/cbor": "6.0.0",
- "@types/escape-regexp": "0.0.1",
- "@types/is-url": "1.2.30",
- "@types/js-yaml": "4.0.5",
- "@types/jsdom": "16.2.14",
- "@types/jsonld": "1.5.6",
- "@types/koa": "2.13.4",
- "@types/koa-bodyparser": "4.3.7",
- "@types/koa-cors": "0.0.2",
- "@types/koa-favicon": "2.0.21",
- "@types/koa-logger": "3.1.2",
- "@types/koa-mount": "4.0.1",
- "@types/koa-send": "4.1.3",
- "@types/koa-views": "7.0.0",
- "@types/koa__cors": "3.1.1",
- "@types/koa__multer": "2.0.4",
- "@types/koa__router": "8.0.11",
- "@types/mocha": "9.1.0",
- "@types/node": "17.0.23",
- "@types/node-fetch": "3.0.3",
- "@types/nodemailer": "6.4.4",
- "@types/oauth": "0.9.1",
- "@types/parse5": "6.0.3",
- "@types/portscanner": "2.1.1",
- "@types/pug": "2.0.6",
- "@types/punycode": "2.1.0",
- "@types/qrcode": "1.4.2",
- "@types/random-seed": "0.3.3",
- "@types/ratelimiter": "3.4.3",
- "@types/redis": "4.0.11",
- "@types/rename": "1.0.4",
- "@types/sanitize-html": "2.6.2",
- "@types/sharp": "0.30.1",
- "@types/sinonjs__fake-timers": "8.1.2",
- "@types/speakeasy": "2.0.7",
- "@types/tinycolor2": "1.4.3",
- "@types/tmp": "0.2.3",
- "@types/uuid": "8.3.4",
- "@types/web-push": "3.3.2",
- "@types/websocket": "1.0.5",
- "@types/ws": "8.5.3",
- "@typescript-eslint/eslint-plugin": "5.18.0",
- "@typescript-eslint/parser": "5.18.0",
- "@bull-board/koa": "3.10.3",
"abort-controller": "3.0.0",
"ajv": "8.11.0",
- "archiver": "5.3.0",
+ "archiver": "5.3.1",
"autobind-decorator": "2.4.0",
"autwh": "0.1.0",
- "aws-sdk": "2.1111.0",
+ "aws-sdk": "2.1152.0",
"bcryptjs": "2.4.3",
"blurhash": "1.1.5",
- "broadcast-channel": "4.10.0",
- "bull": "4.8.1",
+ "bull": "4.8.3",
"cacheable-lookup": "6.0.4",
- "cafy": "15.2.1",
"cbor": "8.1.0",
"chalk": "5.0.1",
"chalk-template": "0.4.0",
@@ -88,22 +42,19 @@
"date-fns": "2.28.0",
"deep-email-validator": "0.1.21",
"escape-regexp": "0.0.1",
- "eslint": "8.13.0",
- "eslint-plugin-import": "2.26.0",
"feed": "4.2.2",
- "file-type": "17.1.1",
+ "file-type": "17.1.2",
"fluent-ffmpeg": "2.1.2",
- "got": "12.0.3",
+ "got": "12.1.0",
"hpagent": "0.1.2",
- "http-signature": "1.3.6",
- "ip-cidr": "3.0.4",
+ "ip-cidr": "3.0.10",
"is-svg": "4.3.2",
"js-yaml": "4.1.0",
"jsdom": "19.0.0",
"json5": "2.2.1",
"json5-loader": "4.0.1",
- "jsonld": "5.2.0",
- "jsrsasign": "8.0.20",
+ "jsonld": "6.0.0",
+ "jsrsasign": "10.5.24",
"koa": "2.13.4",
"koa-bodyparser": "4.3.0",
"koa-favicon": "2.1.0",
@@ -113,19 +64,18 @@
"koa-send": "5.0.1",
"koa-slow": "2.1.0",
"koa-views": "7.0.2",
- "mfm-js": "0.21.0",
+ "mfm-js": "0.22.1",
"mime-types": "2.1.35",
"misskey-js": "0.0.14",
- "mocha": "9.2.2",
+ "mocha": "10.0.0",
"ms": "3.0.0-canary.1",
"multer": "1.4.4",
"nested-property": "4.0.0",
- "node-fetch": "3.2.3",
- "nodemailer": "6.7.3",
+ "node-fetch": "3.2.6",
+ "nodemailer": "6.7.5",
"os-utils": "0.0.14",
"parse5": "6.0.1",
"pg": "8.7.3",
- "portscanner": "2.2.0",
"private-ip": "2.3.3",
"probe-image-size": "7.2.3",
"promise-limit": "2.7.0",
@@ -144,35 +94,83 @@
"rndstr": "1.0.0",
"s-age": "1.1.2",
"sanitize-html": "2.7.0",
- "semver": "7.3.6",
- "sharp": "0.30.3",
+ "semver": "7.3.7",
+ "sharp": "0.29.3",
"speakeasy": "2.0.0",
"strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0",
"style-loader": "3.3.1",
- "summaly": "2.5.0",
+ "summaly": "2.5.1",
"syslog-pro": "1.0.0",
- "systeminformation": "5.11.9",
+ "systeminformation": "5.11.16",
"tinycolor2": "1.4.2",
"tmp": "0.2.1",
- "ts-loader": "9.2.8",
- "ts-node": "10.7.0",
- "tsc-alias": "1.4.1",
- "tsconfig-paths": "3.14.1",
+ "ts-loader": "9.3.0",
+ "ts-node": "10.8.1",
+ "tsc-alias": "1.6.9",
+ "tsconfig-paths": "4.0.0",
"twemoji-parser": "14.0.0",
- "typeorm": "0.3.5",
- "typescript": "4.6.3",
+ "typeorm": "0.3.6",
"ulid": "2.3.0",
"unzipper": "0.10.11",
"uuid": "8.3.2",
- "web-push": "3.4.5",
+ "web-push": "3.5.0",
"websocket": "1.0.34",
- "ws": "8.5.0",
- "xev": "2.0.1"
+ "ws": "8.8.0",
+ "xev": "3.0.2"
},
"devDependencies": {
- "@redocly/openapi-core": "1.0.0-beta.93",
+ "@redocly/openapi-core": "1.0.0-beta.97",
+ "@types/semver": "7.3.9",
+ "@types/bcryptjs": "2.4.2",
+ "@types/bull": "3.15.8",
+ "@types/cbor": "6.0.0",
+ "@types/escape-regexp": "0.0.1",
"@types/fluent-ffmpeg": "2.1.20",
+ "@types/is-url": "1.2.30",
+ "@types/js-yaml": "4.0.5",
+ "@types/jsdom": "16.2.14",
+ "@types/jsonld": "1.5.6",
+ "@types/jsrsasign": "10.5.1",
+ "@types/koa": "2.13.4",
+ "@types/koa-bodyparser": "4.3.7",
+ "@types/koa-cors": "0.0.2",
+ "@types/koa-favicon": "2.0.21",
+ "@types/koa-logger": "3.1.2",
+ "@types/koa-mount": "4.0.1",
+ "@types/koa-send": "4.1.3",
+ "@types/koa-views": "7.0.0",
+ "@types/koa__cors": "3.1.1",
+ "@types/koa__multer": "2.0.4",
+ "@types/koa__router": "8.0.11",
+ "@types/mocha": "9.1.1",
+ "@types/node": "17.0.41",
+ "@types/node-fetch": "3.0.3",
+ "@types/nodemailer": "6.4.4",
+ "@types/oauth": "0.9.1",
+ "@types/parse5": "6.0.3",
+ "@types/pug": "2.0.6",
+ "@types/punycode": "2.1.0",
+ "@types/qrcode": "1.4.2",
+ "@types/random-seed": "0.3.3",
+ "@types/ratelimiter": "3.4.3",
+ "@types/redis": "4.0.11",
+ "@types/rename": "1.0.4",
+ "@types/sanitize-html": "2.6.2",
+ "@types/sharp": "0.30.2",
+ "@types/sinonjs__fake-timers": "8.1.2",
+ "@types/speakeasy": "2.0.7",
+ "@types/tinycolor2": "1.4.3",
+ "@types/tmp": "0.2.3",
+ "@types/uuid": "8.3.4",
+ "@types/web-push": "3.3.2",
+ "@types/websocket": "1.0.5",
+ "@types/ws": "8.5.3",
+ "@typescript-eslint/eslint-plugin": "5.27.1",
+ "@typescript-eslint/parser": "5.27.1",
+ "typescript": "4.7.3",
+ "eslint": "8.17.0",
+ "eslint-plugin-import": "2.26.0",
"cross-env": "7.0.3",
"execa": "6.1.0"
}
diff --git a/packages/backend/src/@types/http-signature.d.ts b/packages/backend/src/@types/http-signature.d.ts
index 8d484312dc..d1f9cd9552 100644
--- a/packages/backend/src/@types/http-signature.d.ts
+++ b/packages/backend/src/@types/http-signature.d.ts
@@ -1,5 +1,5 @@
-declare module 'http-signature' {
- import { IncomingMessage, ClientRequest } from 'http';
+declare module '@peertube/http-signature' {
+ import { IncomingMessage, ClientRequest } from 'node:http';
interface ISignature {
keyId: string;
diff --git a/packages/backend/src/@types/jsrsasign.d.ts b/packages/backend/src/@types/jsrsasign.d.ts
deleted file mode 100644
index bb52f8f64e..0000000000
--- a/packages/backend/src/@types/jsrsasign.d.ts
+++ /dev/null
@@ -1,800 +0,0 @@
-// Attention: Partial Type Definition
-
-declare module 'jsrsasign' {
- //// HELPER TYPES
-
- /**
- * Attention: The value might be changed by the function.
- */
- type Mutable<T> = T;
-
- /**
- * Deprecated: The function might be deleted in future release.
- */
- type Deprecated<T> = T;
-
- //// COMMON TYPES
-
- /**
- * byte number
- */
- type ByteNumber = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255;
-
- /**
- * hexadecimal string /[0-9A-F]/
- */
- type HexString = string;
-
- /**
- * binary string /[01]/
- */
- type BinString = string;
-
- /**
- * base64 string /[A-Za-z0-9+/]=+/
- */
- type Base64String = string;
-
- /**
- * base64 URL encoded string /[A-Za-z0-9_-]/
- */
- type Base64URLString = string;
-
- /**
- * time value (ex. "151231235959Z")
- */
- type TimeValue = string;
-
- /**
- * OID string (ex. '1.2.3.4.567')
- */
- type OID = string;
-
- /**
- * OID name
- */
- type OIDName = string;
-
- /**
- * PEM formatted string
- */
- type PEM = string;
-
- //// ASN1 TYPES
-
- class ASN1Object {
- public isModified: boolean;
-
- public hTLV: ASN1TLV;
-
- public hT: ASN1T;
-
- public hL: ASN1L;
-
- public hV: ASN1V;
-
- public getLengthHexFromValue(): HexString;
-
- public getEncodedHex(): ASN1TLV;
-
- public getValueHex(): ASN1V;
-
- public getFreshValueHex(): ASN1V;
- }
-
- class DERAbstractStructured extends ASN1Object {
- constructor(params?: Partial<Record<'array', ASN1Object[]>>);
-
- public setByASN1ObjectArray(asn1ObjectArray: ASN1Object[]): void;
-
- public appendASN1Object(asn1Object: ASN1Object): void;
- }
-
- class DERSequence extends DERAbstractStructured {
- constructor(params?: Partial<Record<'array', ASN1Object[]>>);
-
- public getFreshValueHex(): ASN1V;
- }
-
- //// ASN1HEX TYPES
-
- /**
- * ASN.1 DER encoded data (hexadecimal string)
- */
- type ASN1S = HexString;
-
- /**
- * index of something
- */
- type Idx<T extends { [idx: string]: unknown } | { [idx: number]: unknown }> = ASN1S extends { [idx: string]: unknown } ? string : ASN1S extends { [idx: number]: unknown } ? number : never;
-
- /**
- * byte length of something
- */
- type ByteLength<T extends { length: unknown }> = T['length'];
-
- /**
- * ASN.1 L(length) (hexadecimal string)
- */
- type ASN1L = HexString;
-
- /**
- * ASN.1 T(tag) (hexadecimal string)
- */
- type ASN1T = HexString;
-
- /**
- * ASN.1 V(value) (hexadecimal string)
- */
- type ASN1V = HexString;
-
- /**
- * ASN.1 TLV (hexadecimal string)
- */
- type ASN1TLV = HexString;
-
- /**
- * ASN.1 object string
- */
- type ASN1ObjectString = string;
-
- /**
- * nth
- */
- type Nth = number;
-
- /**
- * ASN.1 DER encoded OID value (hexadecimal string)
- */
- type ASN1OIDV = HexString;
-
- class ASN1HEX {
- public static getLblen(s: ASN1S, idx: Idx<ASN1S>): ByteLength<ASN1L>;
-
- public static getL(s: ASN1S, idx: Idx<ASN1S>): ASN1L;
-
- public static getVblen(s: ASN1S, idx: Idx<ASN1S>): ByteLength<ASN1V>;
-
- public static getVidx(s: ASN1S, idx: Idx<ASN1S>): Idx<ASN1V>;
-
- public static getV(s: ASN1S, idx: Idx<ASN1S>): ASN1V;
-
- public static getTLV(s: ASN1S, idx: Idx<ASN1S>): ASN1TLV;
-
- public static getNextSiblingIdx(s: ASN1S, idx: Idx<ASN1S>): Idx<ASN1ObjectString>;
-
- public static getChildIdx(h: ASN1S, pos: Idx<ASN1S>): Idx<ASN1ObjectString>[];
-
- public static getNthChildIdx(h: ASN1S, idx: Idx<ASN1S>, nth: Nth): Idx<ASN1ObjectString>;
-
- public static getIdxbyList(h: ASN1S, currentIndex: Idx<ASN1ObjectString>, nthList: Mutable<Nth[]>, checkingTag?: string): Idx<Mutable<Nth[]>>;
-
- public static getTLVbyList(h: ASN1S, currentIndex: Idx<ASN1ObjectString>, nthList: Mutable<Nth[]>, checkingTag?: string): ASN1TLV;
-
- // eslint:disable-next-line:bool-param-default
- public static getVbyList(h: ASN1S, currentIndex: Idx<ASN1ObjectString>, nthList: Mutable<Nth[]>, checkingTag?: string, removeUnusedbits?: boolean): ASN1V;
-
- public static hextooidstr(hex: ASN1OIDV): OID;
-
- public static dump(hexOrObj: ASN1S | ASN1Object, flags?: Record<string, unknown>, idx?: Idx<ASN1S>, indent?: string): string;
-
- public static isASN1HEX(hex: string): hex is HexString;
-
- public static oidname(oidDotOrHex: OID | ASN1OIDV): OIDName;
- }
-
- //// BIG INTEGER TYPES (PARTIAL)
-
- class BigInteger {
- constructor(a: null);
-
- constructor(a: number, b: SecureRandom);
-
- constructor(a: number, b: number, c: SecureRandom);
-
- constructor(a: unknown);
-
- constructor(a: string, b: number);
-
- public am(i: number, x: number, w: number, j: number, c: number, n: number): number;
-
- public DB: number;
-
- public DM: number;
-
- public DV: number;
-
- public FV: number;
-
- public F1: number;
-
- public F2: number;
-
- protected copyTo(r: Mutable<BigInteger>): void;
-
- protected fromInt(x: number): void;
-
- protected fromString(s: string, b: number): void;
-
- protected clamp(): void;
-
- public toString(b: number): string;
-
- public negate(): BigInteger;
-
- public abs(): BigInteger;
-
- public compareTo(a: BigInteger): number;
-
- public bitLength(): number;
-
- protected dlShiftTo(n: number, r: Mutable<BigInteger>): void;
-
- protected drShiftTo(n: number, r: Mutable<BigInteger>): void;
-
- protected lShiftTo(n: number, r: Mutable<BigInteger>): void;
-
- protected rShiftTo(n: number, r: Mutable<BigInteger>): void;
-
- protected subTo(a: BigInteger, r: Mutable<BigInteger>): void;
-
- protected multiplyTo(a: BigInteger, r: Mutable<BigInteger>): void;
-
- protected squareTo(r: Mutable<BigInteger>): void;
-
- protected divRemTo(m: BigInteger, q: Mutable<BigInteger>, r: Mutable<BigInteger>): void;
-
- public mod(a: BigInteger): BigInteger;
-
- protected invDigit(): number;
-
- protected isEven(): boolean;
-
- protected exp(e: number, z: Classic | Montgomery): BigInteger;
-
- public modPowInt(e: number, m: BigInteger): BigInteger;
-
- public static ZERO: BigInteger;
-
- public static ONE: BigInteger;
- }
-
- class Classic {
- constructor(m: BigInteger);
-
- public convert(x: BigInteger): BigInteger;
-
- public revert(x: BigInteger): BigInteger;
-
- public reduce(x: Mutable<BigInteger>): void;
-
- public mulTo(x: BigInteger, r: Mutable<BigInteger>): void;
-
- public sqrTo(x: BigInteger, y: BigInteger, r: Mutable<BigInteger>): void;
- }
-
- class Montgomery {
- constructor(m: BigInteger);
-
- public convert(x: BigInteger): BigInteger;
-
- public revert(x: BigInteger): BigInteger;
-
- public reduce(x: Mutable<BigInteger>): void;
-
- public mulTo(x: BigInteger, r: Mutable<BigInteger>): void;
-
- public sqrTo(x: BigInteger, y: BigInteger, r: Mutable<BigInteger>): void;
- }
-
- //// KEYUTIL TYPES
-
- type DecryptAES = (dataHex: HexString, keyHex: HexString, ivHex: HexString) => HexString;
-
- type Decrypt3DES = (dataHex: HexString, keyHex: HexString, ivHex: HexString) => HexString;
-
- type DecryptDES = (dataHex: HexString, keyHex: HexString, ivHex: HexString) => HexString;
-
- type EncryptAES = (dataHex: HexString, keyHex: HexString, ivHex: HexString) => HexString;
-
- type Encrypt3DES = (dataHex: HexString, keyHex: HexString, ivHex: HexString) => HexString;
-
- type EncryptDES = (dataHex: HexString, keyHex: HexString, ivHex: HexString) => HexString;
-
- type AlgList = {
- 'AES-256-CBC': { 'proc': DecryptAES; 'eproc': EncryptAES; keylen: 32; ivlen: 16; };
- 'AES-192-CBC': { 'proc': DecryptAES; 'eproc': EncryptAES; keylen: 24; ivlen: 16; };
- 'AES-128-CBC': { 'proc': DecryptAES; 'eproc': EncryptAES; keylen: 16; ivlen: 16; };
- 'DES-EDE3-CBC': { 'proc': Decrypt3DES; 'eproc': Encrypt3DES; keylen: 24; ivlen: 8; };
- 'DES-CBC': { 'proc': DecryptDES; 'eproc': EncryptDES; keylen: 8; ivlen: 8; };
- };
-
- type AlgName = keyof AlgList;
-
- type PEMHeadAlgName = 'RSA' | 'EC' | 'DSA';
-
- type GetKeyRSAParam = RSAKey | {
- n: BigInteger;
- e: number;
- } | Record<'n' | 'e', HexString> | Record<'n' | 'e', HexString> & Record<'d' | 'p' | 'q' | 'dp' | 'dq' | 'co', HexString | null> | {
- n: BigInteger;
- e: number;
- d: BigInteger;
- } | {
- kty: 'RSA';
- } & Record<'n' | 'e', Base64URLString> | {
- kty: 'RSA';
- } & Record<'n' | 'e' | 'd' | 'p' | 'q' | 'dp' | 'dq' | 'qi', Base64URLString> | {
- kty: 'RSA';
- } & Record<'n' | 'e' | 'd', Base64URLString>;
-
- type GetKeyECDSAParam = KJUR.crypto.ECDSA | {
- curve: KJUR.crypto.CurveName;
- xy: HexString;
- } | {
- curve: KJUR.crypto.CurveName;
- d: HexString;
- } | {
- kty: 'EC';
- crv: KJUR.crypto.CurveName;
- x: Base64URLString;
- y: Base64URLString;
- } | {
- kty: 'EC';
- crv: KJUR.crypto.CurveName;
- x: Base64URLString;
- y: Base64URLString;
- d: Base64URLString;
- };
-
- type GetKeyDSAParam = KJUR.crypto.DSA | Record<'p' | 'q' | 'g', BigInteger> & Record<'y', BigInteger | null> | Record<'p' | 'q' | 'g' | 'x', BigInteger> & Record<'y', BigInteger | null>;
-
- type GetKeyParam = GetKeyRSAParam | GetKeyECDSAParam | GetKeyDSAParam | string;
-
- class KEYUTIL {
- public version: '1.0.0';
-
- public parsePKCS5PEM(sPKCS5PEM: PEM): Partial<Record<'type' | 's', string>> & (Record<'cipher' | 'ivsalt', string> | Record<'cipher' | 'ivsalt', undefined>);
-
- public getKeyAndUnusedIvByPasscodeAndIvsalt(algName: AlgName, passcode: string, ivsaltHex: HexString): Record<'keyhex' | 'ivhex', HexString>;
-
- public decryptKeyB64(privateKeyB64: Base64String, sharedKeyAlgName: AlgName, sharedKeyHex: HexString, ivsaltHex: HexString): Base64String;
-
- public getDecryptedKeyHex(sEncryptedPEM: PEM, passcode: string): HexString;
-
- public getEncryptedPKCS5PEMFromPrvKeyHex(pemHeadAlg: PEMHeadAlgName, hPrvKey: string, passcode: string, sharedKeyAlgName?: AlgName | null, ivsaltHex?: HexString | null): PEM;
-
- public parseHexOfEncryptedPKCS8(sHEX: HexString): {
- ciphertext: ASN1V;
- encryptionSchemeAlg: 'TripleDES';
- encryptionSchemeIV: ASN1V;
- pbkdf2Salt: ASN1V;
- pbkdf2Iter: number;
- };
-
- public getPBKDF2KeyHexFromParam(info: ReturnType<this['parseHexOfEncryptedPKCS8']>, passcode: string): HexString;
-
- private _getPlainPKCS8HexFromEncryptedPKCS8PEM(pkcs8PEM: PEM, passcode: string): HexString;
-
- public getKeyFromEncryptedPKCS8PEM(prvKeyHex: HexString): ReturnType<this['getKeyFromPlainPrivatePKCS8Hex']>;
-
- public parsePlainPrivatePKCS8Hex(pkcs8PrvHex: HexString): {
- algparam: ASN1V | null;
- algoid: ASN1V;
- keyidx: Idx<ASN1V>;
- };
-
- public getKeyFromPlainPrivatePKCS8PEM(prvKeyHex: HexString): ReturnType<this['getKeyFromPlainPrivatePKCS8Hex']>;
-
- public getKeyFromPlainPrivatePKCS8Hex(prvKeyHex: HexString): RSAKey | KJUR.crypto.DSA | KJUR.crypto.ECDSA;
-
- private _getKeyFromPublicPKCS8Hex(h: HexString): RSAKey | KJUR.crypto.DSA | KJUR.crypto.ECDSA;
-
- public parsePublicRawRSAKeyHex(pubRawRSAHex: HexString): Record<'n' | 'e', ASN1V>;
-
- public parsePublicPKCS8Hex(pkcs8PubHex: HexString): {
- algparam: ASN1V | Record<'p' | 'q' | 'g', ASN1V> | null;
- algoid: ASN1V;
- key: ASN1V;
- };
-
- public static getKey(param: GetKeyRSAParam): RSAKey;
-
- public static getKey(param: GetKeyECDSAParam): KJUR.crypto.ECDSA;
-
- public static getKey(param: GetKeyDSAParam): KJUR.crypto.DSA;
-
- public static getKey(param: string, passcode?: string, hextype?: string): RSAKey | KJUR.crypto.ECDSA | KJUR.crypto.DSA;
-
- public static generateKeypair(alg: 'RSA', keylen: number): Record<'prvKeyObj' | 'pubKeyObj', RSAKey>;
-
- public static generateKeypair(alg: 'EC', curve: KJUR.crypto.CurveName): Record<'prvKeyObj' | 'pubKeyObj', KJUR.crypto.ECDSA>;
-
- public static getPEM(keyObjOrHex: RSAKey | KJUR.crypto.ECDSA | KJUR.crypto.DSA, formatType?: 'PKCS1PRV' | 'PKCS5PRV' | 'PKCS8PRV', passwd?: string, encAlg?: 'DES-CBC' | 'DES-EDE3-CBC' | 'AES-128-CBC' | 'AES-192-CBC' | 'AES-256-CBC', hexType?: string, ivsaltHex?: HexString): object; // To Do
-
- public static getKeyFromCSRPEM(csrPEM: PEM): RSAKey | KJUR.crypto.ECDSA | KJUR.crypto.DSA;
-
- public static getKeyFromCSRHex(csrHex: HexString): RSAKey | KJUR.crypto.ECDSA | KJUR.crypto.DSA;
-
- public static parseCSRHex(csrHex: HexString): Record<'p8pubkeyhex', ASN1TLV>;
-
- public static getJWKFromKey(keyObj: RSAKey): {
- kty: 'RSA';
- } & Record<'n' | 'e' | 'd' | 'p' | 'q' | 'dp' | 'dq' | 'qi', Base64URLString> | {
- kty: 'RSA';
- } & Record<'n' | 'e', Base64URLString>;
-
- public static getJWKFromKey(keyObj: KJUR.crypto.ECDSA): {
- kty: 'EC';
- crv: KJUR.crypto.CurveName;
- x: Base64URLString;
- y: Base64URLString;
- d: Base64URLString;
- } | {
- kty: 'EC';
- crv: KJUR.crypto.CurveName;
- x: Base64URLString;
- y: Base64URLString;
- };
- }
-
- //// KJUR NAMESPACE (PARTIAL)
-
- namespace KJUR {
- namespace crypto {
- type CurveName = 'secp128r1' | 'secp160k1' | 'secp160r1' | 'secp192k1' | 'secp192r1' | 'secp224r1' | 'secp256k1' | 'secp256r1' | 'secp384r1' | 'secp521r1';
-
- class DSA {
- public p: BigInteger | null;
-
- public q: BigInteger | null;
-
- public g: BigInteger | null;
-
- public y: BigInteger | null;
-
- public x: BigInteger | null;
-
- public type: 'DSA';
-
- public isPrivate: boolean;
-
- public isPublic: boolean;
-
- public setPrivate(p: BigInteger, q: BigInteger, g: BigInteger, y: BigInteger | null, x: BigInteger): void;
-
- public setPrivateHex(hP: HexString, hQ: HexString, hG: HexString, hY: HexString | null, hX: HexString): void;
-
- public setPublic(p: BigInteger, q: BigInteger, g: BigInteger, y: BigInteger): void;
-
- public setPublicHex(hP: HexString, hQ: HexString, hG: HexString, hY: HexString): void;
-
- public signWithMessageHash(sHashHex: HexString): HexString;
-
- public verifyWithMessageHash(sHashHex: HexString, hSigVal: HexString): boolean;
-
- public parseASN1Signature(hSigVal: HexString): [BigInteger, BigInteger];
-
- public readPKCS5PrvKeyHex(h: HexString): void;
-
- public readPKCS8PrvKeyHex(h: HexString): void;
-
- public readPKCS8PubKeyHex(h: HexString): void;
-
- public readCertPubKeyHex(h: HexString, nthPKI: number): void;
- }
-
- class ECDSA {
- constructor(params?: {
- curve?: CurveName;
- prv?: HexString;
- pub?: HexString;
- });
-
- public p: BigInteger | null;
-
- public q: BigInteger | null;
-
- public g: BigInteger | null;
-
- public y: BigInteger | null;
-
- public x: BigInteger | null;
-
- public type: 'EC';
-
- public isPrivate: boolean;
-
- public isPublic: boolean;
-
- public getBigRandom(limit: BigInteger): BigInteger;
-
- public setNamedCurve(curveName: CurveName): void;
-
- public setPrivateKeyHex(prvKeyHex: HexString): void;
-
- public setPublicKeyHex(pubKeyHex: HexString): void;
-
- public getPublicKeyXYHex(): Record<'x' | 'y', HexString>;
-
- public getShortNISTPCurveName(): 'P-256' | 'P-384' | null;
-
- public generateKeyPairHex(): Record<'ecprvhex' | 'ecpubhex', HexString>;
-
- public signWithMessageHash(hashHex: HexString): HexString;
-
- public signHex(hashHex: HexString, privHex: HexString): HexString;
-
- public verifyWithMessageHash(sHashHex: HexString, hSigVal: HexString): boolean;
-
- public parseASN1Signature(hSigVal: HexString): [BigInteger, BigInteger];
-
- public readPKCS5PrvKeyHex(h: HexString): void;
-
- public readPKCS8PrvKeyHex(h: HexString): void;
-
- public readPKCS8PubKeyHex(h: HexString): void;
-
- public readCertPubKeyHex(h: HexString, nthPKI: number): void;
-
- public static parseSigHex(sigHex: HexString): Record<'r' | 's', BigInteger>;
-
- public static parseSigHexInHexRS(sigHex: HexString): Record<'r' | 's', ASN1V>;
-
- public static asn1SigToConcatSig(asn1Sig: HexString): HexString;
-
- public static concatSigToASN1Sig(concatSig: HexString): ASN1TLV;
-
- public static hexRSSigToASN1Sig(hR: HexString, hS: HexString): ASN1TLV;
-
- public static biRSSigToASN1Sig(biR: BigInteger, biS: BigInteger): ASN1TLV;
-
- public static getName(s: CurveName | HexString): 'secp256r1' | 'secp256k1' | 'secp384r1' | null;
- }
-
- class Signature {
- constructor(params?: ({
- alg: string;
- prov?: string;
- } | {}) & ({
- psssaltlen: number;
- } | {}) & ({
- prvkeypem: PEM;
- prvkeypas?: never;
- } | {}));
-
- private _setAlgNames(): void;
-
- private _zeroPaddingOfSignature(hex: HexString, bitLength: number): HexString;
-
- public setAlgAndProvider(alg: string, prov: string): void;
-
- public init(key: GetKeyParam, pass?: string): void;
-
- public updateString(str: string): void;
-
- public updateHex(hex: HexString): void;
-
- public sign(): HexString;
-
- public signString(str: string): HexString;
-
- public signHex(hex: HexString): HexString;
-
- public verify(hSigVal: string): boolean | 0;
- }
- }
- }
-
- //// RSAKEY TYPES
-
- class RSAKey {
- public n: BigInteger | null;
-
- public e: number;
-
- public d: BigInteger | null;
-
- public p: BigInteger | null;
-
- public q: BigInteger | null;
-
- public dmp1: BigInteger | null;
-
- public dmq1: BigInteger | null;
-
- public coeff: BigInteger | null;
-
- public type: 'RSA';
-
- public isPrivate?: boolean;
-
- public isPublic?: boolean;
-
- //// RSA PUBLIC
-
- protected doPublic(x: BigInteger): BigInteger;
-
- public setPublic(N: BigInteger, E: number): void;
-
- public setPublic(N: HexString, E: HexString): void;
-
- public encrypt(text: string): HexString | null;
-
- public encryptOAEP(text: string, hash?: string | ((s: string) => string), hashLen?: number): HexString | null;
-
- //// RSA PRIVATE
-
- protected doPrivate(x: BigInteger): BigInteger;
-
- public setPrivate(N: BigInteger, E: number, D: BigInteger): void;
-
- public setPrivate(N: HexString, E: HexString, D: HexString): void;
-
- public setPrivateEx(N: HexString, E: HexString, D?: HexString | null, P?: HexString | null, Q?: HexString | null, DP?: HexString | null, DQ?: HexString | null, C?: HexString | null): void;
-
- public generate(B: number, E: HexString): void;
-
- public decrypt(ctext: HexString): string;
-
- public decryptOAEP(ctext: HexString, hash?: string | ((s: string) => string), hashLen?: number): string | null;
-
- //// RSA PEM
-
- public getPosArrayOfChildrenFromHex(hPrivateKey: PEM): Idx<ASN1ObjectString>[];
-
- public getHexValueArrayOfChildrenFromHex(hPrivateKey: PEM): Idx<ASN1ObjectString>[];
-
- public readPrivateKeyFromPEMString(keyPEM: PEM): void;
-
- public readPKCS5PrvKeyHex(h: HexString): void;
-
- public readPKCS8PrvKeyHex(h: HexString): void;
-
- public readPKCS5PubKeyHex(h: HexString): void;
-
- public readPKCS8PubKeyHex(h: HexString): void;
-
- public readCertPubKeyHex(h: HexString, nthPKI: Nth): void;
-
- //// RSA SIGN
-
- public sign(s: string, hashAlg: string): HexString;
-
- public signWithMessageHash(sHashHex: HexString, hashAlg: string): HexString;
-
- public signPSS(s: string, hashAlg: string, sLen: number): HexString;
-
- public signWithMessageHashPSS(hHash: HexString, hashAlg: string, sLen: number): HexString;
-
- public verify(sMsg: string, hSig: HexString): boolean | 0;
-
- public verifyWithMessageHash(sHashHex: HexString, hSig: HexString): boolean | 0;
-
- public verifyPSS(sMsg: string, hSig: HexString, hashAlg: string, sLen: number): boolean;
-
- public verifyWithMessageHashPSS(hHash: HexString, hSig: HexString, hashAlg: string, sLen: number): boolean;
-
- public static SALT_LEN_HLEN: -1;
-
- public static SALT_LEN_MAX: -2;
-
- public static SALT_LEN_RECOVER: -2;
- }
-
- /// RNG TYPES
- class SecureRandom {
- public nextBytes(ba: Mutable<ByteNumber[]>): void;
- }
-
- //// X509 TYPES
-
- type ExtInfo = {
- critical: boolean;
- oid: OID;
- vidx: Idx<ASN1V>;
- };
-
- type ExtAIAInfo = Record<'ocsp' | 'caissuer', string>;
-
- type ExtCertificatePolicy = {
- id: OIDName;
- } & Partial<{
- cps: string;
- } | {
- unotice: string;
- }>;
-
- class X509 {
- public hex: HexString | null;
-
- public version: number;
-
- public foffset: number;
-
- public aExtInfo: null;
-
- public getVersion(): number;
-
- public getSerialNumberHex(): ASN1V;
-
- public getSignatureAlgorithmField(): OIDName;
-
- public getIssuerHex(): ASN1TLV;
-
- public getIssuerString(): HexString;
-
- public getSubjectHex(): ASN1TLV;
-
- public getSubjectString(): HexString;
-
- public getNotBefore(): TimeValue;
-
- public getNotAfter(): TimeValue;
-
- public getPublicKeyHex(): ASN1TLV;
-
- public getPublicKeyIdx(): Idx<Mutable<Nth[]>>;
-
- public getPublicKeyContentIdx(): Idx<Mutable<Nth[]>>;
-
- public getPublicKey(): RSAKey | KJUR.crypto.ECDSA | KJUR.crypto.DSA;
-
- public getSignatureAlgorithmName(): OIDName;
-
- public getSignatureValueHex(): ASN1V;
-
- public verifySignature(pubKey: GetKeyParam): boolean | 0;
-
- public parseExt(): void;
-
- public getExtInfo(oidOrName: OID | string): ExtInfo | undefined;
-
- public getExtBasicConstraints(): ExtInfo | {} | {
- cA: true;
- pathLen?: number;
- };
-
- public getExtKeyUsageBin(): BinString;
-
- public getExtKeyUsageString(): string;
-
- public getExtSubjectKeyIdentifier(): ASN1V | undefined;
-
- public getExtAuthorityKeyIdentifier(): {
- kid: ASN1V;
- } | undefined;
-
- public getExtExtKeyUsageName(): OIDName[] | undefined;
-
- public getExtSubjectAltName(): Deprecated<string[]>;
-
- public getExtSubjectAltName2(): ['MAIL' | 'DNS' | 'DN' | 'URI' | 'IP', string][] | undefined;
-
- public getExtCRLDistributionPointsURI(): string[] | undefined;
-
- public getExtAIAInfo(): ExtAIAInfo | undefined;
-
- public getExtCertificatePolicies(): ExtCertificatePolicy[] | undefined;
-
- public readCertPEM(sCertPEM: PEM): void;
-
- public readCertHex(sCertHex: HexString): void;
-
- public getInfo(): string;
-
- public static hex2dn(hex: HexString, idx?: Idx<HexString>): string;
-
- public static hex2rdn(hex: HexString, idx?: Idx<HexString>): string;
-
- public static hex2attrTypeValue(hex: HexString, idx?: Idx<HexString>): string;
-
- public static getPublicKeyFromCertPEM(sCertPEM: PEM): RSAKey | KJUR.crypto.ECDSA | KJUR.crypto.DSA;
-
- public static getPublicKeyInfoPropOfCertPEM(sCertPEM: PEM): {
- algparam: ASN1V | null;
- leyhex: ASN1V;
- algoid: ASN1V;
- };
- }
-}
diff --git a/packages/backend/src/boot/index.ts b/packages/backend/src/boot/index.ts
index 5bb20a729f..c3d0592256 100644
--- a/packages/backend/src/boot/index.ts
+++ b/packages/backend/src/boot/index.ts
@@ -1,6 +1,6 @@
import cluster from 'node:cluster';
import chalk from 'chalk';
-import { default as Xev } from 'xev';
+import Xev from 'xev';
import Logger from '@/services/logger.js';
import { envOption } from '../env.js';
@@ -12,7 +12,7 @@ import { workerMain } from './worker.js';
const logger = new Logger('core', 'cyan');
const clusterLogger = logger.createSubLogger('cluster', 'orange', false);
-const ev = new Xev.default();
+const ev = new Xev();
/**
* Init process
diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts
index 09d20f9361..bf51960482 100644
--- a/packages/backend/src/boot/master.ts
+++ b/packages/backend/src/boot/master.ts
@@ -5,7 +5,6 @@ import * as os from 'node:os';
import cluster from 'node:cluster';
import chalk from 'chalk';
import chalkTemplate from 'chalk-template';
-import * as portscanner from 'portscanner';
import semver from 'semver';
import Logger from '@/services/logger.js';
@@ -48,11 +47,6 @@ function greet() {
bootLogger.info(`Misskey v${meta.version}`, null, true);
}
-function isRoot() {
- // maybe process.getuid will be undefined under not POSIX environment (e.g. Windows)
- return process.getuid != null && process.getuid() === 0;
-}
-
/**
* Init master process
*/
@@ -67,7 +61,6 @@ export async function masterMain() {
showNodejsVersion();
config = loadConfigBoot();
await connectDb();
- await validatePort(config);
} catch (e) {
bootLogger.error('Fatal error occurred during initialization', null, true);
process.exit(1);
@@ -97,8 +90,6 @@ function showEnvironment(): void {
logger.warn('The environment is not in production mode.');
logger.warn('DO NOT USE FOR PRODUCTION PURPOSE!', null, true);
}
-
- logger.info(`You ${isRoot() ? '' : 'do not '}have root privileges`);
}
function showNodejsVersion(): void {
@@ -152,29 +143,6 @@ async function connectDb(): Promise<void> {
}
}
-async function validatePort(config: Config): Promise<void> {
- const isWellKnownPort = (port: number) => port < 1024;
-
- async function isPortAvailable(port: number): Promise<boolean> {
- return await portscanner.checkPortStatus(port, '127.0.0.1') === 'closed';
- }
-
- if (config.port == null || Number.isNaN(config.port)) {
- bootLogger.error('The port is not configured. Please configure port.', null, true);
- process.exit(1);
- }
-
- if (process.platform === 'linux' && isWellKnownPort(config.port) && !isRoot()) {
- bootLogger.error('You need root privileges to listen on well-known port on Linux', null, true);
- process.exit(1);
- }
-
- if (!await isPortAvailable(config.port)) {
- bootLogger.error(`Port ${config.port} is already in use`, null, true);
- process.exit(1);
- }
-}
-
async function spawnWorkers(limit: number = 1) {
const workers = Math.min(limit, os.cpus().length);
bootLogger.info(`Starting ${workers} worker${workers === 1 ? '' : 's'}...`);
@@ -186,6 +154,10 @@ function spawnWorker(): Promise<void> {
return new Promise(res => {
const worker = cluster.fork();
worker.on('message', message => {
+ if (message === 'listenFailed') {
+ bootLogger.error(`The server Listen failed due to the previous error.`);
+ process.exit(1);
+ }
if (message !== 'ready') return;
res();
});
diff --git a/packages/backend/src/config/load.ts b/packages/backend/src/config/load.ts
index 7f765463e4..9654a4f3b0 100644
--- a/packages/backend/src/config/load.ts
+++ b/packages/backend/src/config/load.ts
@@ -25,6 +25,7 @@ const path = process.env.NODE_ENV === 'test'
export default function load() {
const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json`, 'utf-8'));
+ const clientManifest = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/_client_dist_/manifest.json`, 'utf-8'));
const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source;
const mixin = {} as Mixin;
@@ -45,6 +46,7 @@ export default function load() {
mixin.authUrl = `${mixin.scheme}://${mixin.host}/auth`;
mixin.driveUrl = `${mixin.scheme}://${mixin.host}/files`;
mixin.userAgent = `Misskey/${meta.version} (${config.url})`;
+ mixin.clientEntry = clientManifest['src/init.ts'];
if (!config.redis.prefix) config.redis.prefix = mixin.host;
diff --git a/packages/backend/src/config/types.ts b/packages/backend/src/config/types.ts
index 58a27803cb..948545db7a 100644
--- a/packages/backend/src/config/types.ts
+++ b/packages/backend/src/config/types.ts
@@ -80,6 +80,7 @@ export type Mixin = {
authUrl: string;
driveUrl: string;
userAgent: string;
+ clientEntry: string;
};
export type Config = Source & Mixin;
diff --git a/packages/backend/src/daemons/queue-stats.ts b/packages/backend/src/daemons/queue-stats.ts
index bfef110545..1535abc6af 100644
--- a/packages/backend/src/daemons/queue-stats.ts
+++ b/packages/backend/src/daemons/queue-stats.ts
@@ -1,7 +1,7 @@
-import { default as Xev } from 'xev';
+import Xev from 'xev';
import { deliverQueue, inboxQueue } from '../queue/queues.js';
-const ev = new Xev.default();
+const ev = new Xev();
const interval = 10000;
diff --git a/packages/backend/src/daemons/server-stats.ts b/packages/backend/src/daemons/server-stats.ts
index 327305ccc5..faf4e6e4a4 100644
--- a/packages/backend/src/daemons/server-stats.ts
+++ b/packages/backend/src/daemons/server-stats.ts
@@ -1,8 +1,8 @@
import si from 'systeminformation';
-import { default as Xev } from 'xev';
+import Xev from 'xev';
import * as osUtils from 'os-utils';
-const ev = new Xev.default();
+const ev = new Xev();
const interval = 2000;
diff --git a/packages/backend/src/db/postgre.ts b/packages/backend/src/db/postgre.ts
index eb5fc2e186..298f6713ea 100644
--- a/packages/backend/src/db/postgre.ts
+++ b/packages/backend/src/db/postgre.ts
@@ -5,9 +5,6 @@ pg.types.setTypeParser(20, Number);
import { Logger, DataSource } from 'typeorm';
import * as highlight from 'cli-highlight';
import config from '@/config/index.js';
-import { envOption } from '../env.js';
-
-import { dbLogger } from './logger.js';
import { User } from '@/models/entities/user.js';
import { DriveFile } from '@/models/entities/drive-file.js';
@@ -74,6 +71,9 @@ import { UserPending } from '@/models/entities/user-pending.js';
import { entities as charts } from '@/services/chart/entities.js';
import { Webhook } from '@/models/entities/webhook.js';
+import { envOption } from '../env.js';
+import { dbLogger } from './logger.js';
+import { redisClient } from './redis.js';
const sqlLogger = dbLogger.createSubLogger('sql', 'gray', false);
@@ -208,16 +208,25 @@ export const db = new DataSource({
migrations: ['../../migration/*.js'],
});
-export async function initDb() {
+export async function initDb(force = false) {
+ if (force) {
+ if (db.isInitialized) {
+ await db.destroy();
+ }
+ await db.initialize();
+ return;
+ }
+
if (db.isInitialized) {
// nop
} else {
- await db.connect();
+ await db.initialize();
}
}
export async function resetDb() {
const reset = async () => {
+ await redisClient.FLUSHDB();
const tables = await db.query(`SELECT relname AS "table"
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE nspname NOT IN ('pg_catalog', 'information_schema')
diff --git a/packages/backend/src/mfm/from-html.ts b/packages/backend/src/mfm/from-html.ts
index 623cb0e71c..15110b6b70 100644
--- a/packages/backend/src/mfm/from-html.ts
+++ b/packages/backend/src/mfm/from-html.ts
@@ -6,6 +6,9 @@ const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/;
const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/;
export function fromHtml(html: string, hashtagNames?: string[]): string {
+ // some AP servers like Pixelfed use br tags as well as newlines
+ html = html.replace(/<br\s?\/?>\r?\n/gi, '\n');
+
const dom = parse5.parseFragment(html);
let text = '';
diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts
index 01bbe98a85..e5b911ed32 100644
--- a/packages/backend/src/misc/cache.ts
+++ b/packages/backend/src/misc/cache.ts
@@ -48,6 +48,7 @@ export class Cache<T> {
// Cache MISS
const value = await fetcher();
+ this.set(key, value);
return value;
}
diff --git a/packages/backend/src/misc/cafy-id.ts b/packages/backend/src/misc/cafy-id.ts
deleted file mode 100644
index dd81c5c4cf..0000000000
--- a/packages/backend/src/misc/cafy-id.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Context } from 'cafy';
-
-// eslint-disable-next-line @typescript-eslint/ban-types
-export class ID<Maybe = string> extends Context<string | (Maybe extends {} ? string : Maybe)> {
- public readonly name = 'ID';
-
- constructor(optional = false, nullable = false) {
- super(optional, nullable);
-
- this.push((v: any) => {
- if (typeof v !== 'string') {
- return new Error('must-be-an-id');
- }
- return true;
- });
- }
-
- public getType() {
- return super.getType('String');
- }
-
- public makeOptional(): ID<undefined> {
- return new ID(true, false);
- }
-
- public makeNullable(): ID<null> {
- return new ID(false, true);
- }
-
- public makeOptionalNullable(): ID<undefined | null> {
- return new ID(true, true);
- }
-}
diff --git a/packages/backend/src/misc/create-temp.ts b/packages/backend/src/misc/create-temp.ts
index 04604cf7d0..f07be634fb 100644
--- a/packages/backend/src/misc/create-temp.ts
+++ b/packages/backend/src/misc/create-temp.ts
@@ -1,10 +1,19 @@
import * as tmp from 'tmp';
-export function createTemp(): Promise<[string, any]> {
- return new Promise<[string, any]>((res, rej) => {
+export function createTemp(): Promise<[string, () => void]> {
+ return new Promise<[string, () => void]>((res, rej) => {
tmp.file((e, path, fd, cleanup) => {
if (e) return rej(e);
res([path, cleanup]);
});
});
}
+
+export function createTempDir(): Promise<[string, () => void]> {
+ return new Promise<[string, () => void]>((res, rej) => {
+ tmp.dir((e, path, cleanup) => {
+ if (e) return rej(e);
+ res([path, cleanup]);
+ });
+ });
+}
diff --git a/packages/backend/src/misc/fetch-meta.ts b/packages/backend/src/misc/fetch-meta.ts
index 5417c10962..e855ac28ee 100644
--- a/packages/backend/src/misc/fetch-meta.ts
+++ b/packages/backend/src/misc/fetch-meta.ts
@@ -20,9 +20,16 @@ export async function fetchMeta(noCache = false): Promise<Meta> {
cache = meta;
return meta;
} else {
- const saved = await transactionalEntityManager.save(Meta, {
- id: 'x',
- }) as Meta;
+ // metaãŒç©ºã®ã¨ãfetchMetaãŒåŒæ™‚ã«å‘¼ã°ã‚Œã‚‹ã¨ã“ã“ãŒåŒæ™‚ã«å‘¼ã°ã‚Œã¦ã—ã¾ã†ã“ã¨ãŒã‚ã‚‹ã®ã§ãƒ•ェイルセーフãªupsertを使ã†
+ const saved = await transactionalEntityManager
+ .upsert(
+ Meta,
+ {
+ id: 'x',
+ },
+ ['id'],
+ )
+ .then((x) => transactionalEntityManager.findOneByOrFail(Meta, x.identifiers[0]));
cache = saved;
return saved;
diff --git a/packages/backend/src/misc/fetch.ts b/packages/backend/src/misc/fetch.ts
index 4b1013c9f5..af6bf2fca7 100644
--- a/packages/backend/src/misc/fetch.ts
+++ b/packages/backend/src/misc/fetch.ts
@@ -1,10 +1,10 @@
-import * as http from 'http';
-import * as https from 'https';
+import * as http from 'node:http';
+import * as https from 'node:https';
+import { URL } from 'node:url';
import CacheableLookup from 'cacheable-lookup';
import fetch from 'node-fetch';
import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent';
import config from '@/config/index.js';
-import { URL } from 'node:url';
export async function getJson(url: string, accept = 'application/json, */*', timeout = 10000, headers?: Record<string, string>) {
const res = await getResponse({
@@ -35,7 +35,7 @@ export async function getHtml(url: string, accept = 'text/html, */*', timeout =
}
export async function getResponse(args: { url: string, method: string, body?: string, headers: Record<string, string>, timeout?: number, size?: number }) {
- const timeout = args?.timeout || 10 * 1000;
+ const timeout = args.timeout || 10 * 1000;
const controller = new AbortController();
setTimeout(() => {
@@ -47,7 +47,7 @@ export async function getResponse(args: { url: string, method: string, body?: st
headers: args.headers,
body: args.body,
timeout,
- size: args?.size || 10 * 1024 * 1024,
+ size: args.size || 10 * 1024 * 1024,
agent: getAgentByUrl,
signal: controller.signal,
});
diff --git a/packages/backend/src/misc/get-ip-hash.ts b/packages/backend/src/misc/get-ip-hash.ts
new file mode 100644
index 0000000000..379325bb13
--- /dev/null
+++ b/packages/backend/src/misc/get-ip-hash.ts
@@ -0,0 +1,9 @@
+import IPCIDR from 'ip-cidr';
+
+export function getIpHash(ip: string) {
+ // because a single person may control many IPv6 addresses,
+ // only a /64 subnet prefix of any IP will be taken into account.
+ // (this means for IPv4 the entire address is used)
+ const prefix = IPCIDR.createAddress(ip).mask(64);
+ return 'ip-' + BigInt('0b' + prefix).toString(36);
+}
diff --git a/packages/backend/src/misc/populate-emojis.ts b/packages/backend/src/misc/populate-emojis.ts
index 86f1356c31..6a185d09f6 100644
--- a/packages/backend/src/misc/populate-emojis.ts
+++ b/packages/backend/src/misc/populate-emojis.ts
@@ -63,7 +63,7 @@ export async function populateEmoji(emojiName: string, noteUserHost: string | nu
const isLocal = emoji.host == null;
const emojiUrl = emoji.publicUrl || emoji.originalUrl; // || emoji.originalUrl ã—ã¦ã‚‹ã®ã¯å¾Œæ–¹äº’æ›æ€§ã®ãŸã‚
- const url = isLocal ? emojiUrl : `${config.url}/proxy/image.png?${query({ url: emojiUrl })}`;
+ const url = isLocal ? emojiUrl : `${config.url}/proxy/${encodeURIComponent((new URL(emojiUrl)).pathname)}?${query({ url: emojiUrl })}`;
return {
name: emojiName,
diff --git a/packages/backend/src/misc/schema.ts b/packages/backend/src/misc/schema.ts
index 5b69812090..fdecc278d4 100644
--- a/packages/backend/src/misc/schema.ts
+++ b/packages/backend/src/misc/schema.ts
@@ -89,7 +89,7 @@ export interface Schema extends OfSchema {
readonly optional?: boolean;
readonly items?: Schema;
readonly properties?: Obj;
- readonly required?: ReadonlyArray<keyof NonNullable<this['properties']>>;
+ readonly required?: ReadonlyArray<Extract<keyof NonNullable<this['properties']>, string>>;
readonly description?: string;
readonly example?: any;
readonly format?: string;
@@ -98,6 +98,9 @@ export interface Schema extends OfSchema {
readonly default?: (this['type'] extends TypeStringef ? StringDefToType<this['type']> : any) | null;
readonly maxLength?: number;
readonly minLength?: number;
+ readonly maximum?: number;
+ readonly minimum?: number;
+ readonly pattern?: string;
}
type RequiredPropertyNames<s extends Obj> = {
@@ -105,24 +108,26 @@ type RequiredPropertyNames<s extends Obj> = {
// K is not optional
s[K]['optional'] extends false ? K :
// K has default value
- s[K]['default'] extends null | string | number | boolean | Record<string, unknown> ? K : never
+ s[K]['default'] extends null | string | number | boolean | Record<string, unknown> ? K :
+ never
}[keyof s];
-export interface Obj { [key: string]: Schema; }
+export type Obj = Record<string, Schema>;
+// https://github.com/misskey-dev/misskey/issues/8535
+// To avoid excessive stack depth error,
+// deceive TypeScript with UnionToIntersection (or more precisely, `infer` expression within it).
export type ObjType<s extends Obj, RequiredProps extends keyof s> =
- { -readonly [P in keyof s]?: SchemaType<s[P]> } &
- { -readonly [P in RequiredProps]: SchemaType<s[P]> } &
- { -readonly [P in RequiredPropertyNames<s>]: SchemaType<s[P]> };
+ UnionToIntersection<
+ { -readonly [R in RequiredPropertyNames<s>]-?: SchemaType<s[R]> } &
+ { -readonly [R in RequiredProps]-?: SchemaType<s[R]> } &
+ { -readonly [P in keyof s]?: SchemaType<s[P]> }
+ >;
type NullOrUndefined<p extends Schema, T> =
- p['nullable'] extends true
- ? p['optional'] extends true
- ? (T | null | undefined)
- : (T | null)
- : p['optional'] extends true
- ? (T | undefined)
- : T;
+ | (p['nullable'] extends true ? null : never)
+ | (p['optional'] extends true ? undefined : never)
+ | T;
// https://stackoverflow.com/questions/54938141/typescript-convert-union-to-intersection
// Get intersection from union
@@ -139,9 +144,9 @@ export type SchemaTypeDef<p extends Schema> =
p['type'] extends 'number' ? number :
p['type'] extends 'string' ? (
p['enum'] extends readonly string[] ?
- p['enum'][number] :
- p['format'] extends 'date-time' ? string : // Dateã«ã™ã‚‹ï¼Ÿï¼Ÿ
- string
+ p['enum'][number] :
+ p['format'] extends 'date-time' ? string : // Dateã«ã™ã‚‹ï¼Ÿï¼Ÿ
+ string
) :
p['type'] extends 'boolean' ? boolean :
p['type'] extends 'object' ? (
diff --git a/packages/backend/src/models/entities/access-token.ts b/packages/backend/src/models/entities/access-token.ts
index 69cdc49cec..c6e2141a46 100644
--- a/packages/backend/src/models/entities/access-token.ts
+++ b/packages/backend/src/models/entities/access-token.ts
@@ -15,7 +15,6 @@ export class AccessToken {
@Column('timestamp with time zone', {
nullable: true,
- default: null,
})
public lastUsedAt: Date | null;
@@ -29,7 +28,6 @@ export class AccessToken {
@Column('varchar', {
length: 128,
nullable: true,
- default: null,
})
public session: string | null;
@@ -52,7 +50,6 @@ export class AccessToken {
@Column({
...id(),
nullable: true,
- default: null,
})
public appId: App['id'] | null;
@@ -65,21 +62,18 @@ export class AccessToken {
@Column('varchar', {
length: 128,
nullable: true,
- default: null,
})
public name: string | null;
@Column('varchar', {
length: 512,
nullable: true,
- default: null,
})
public description: string | null;
@Column('varchar', {
length: 512,
nullable: true,
- default: null,
})
public iconUrl: string | null;
diff --git a/packages/backend/src/models/entities/auth-session.ts b/packages/backend/src/models/entities/auth-session.ts
index b825856201..295d1b486c 100644
--- a/packages/backend/src/models/entities/auth-session.ts
+++ b/packages/backend/src/models/entities/auth-session.ts
@@ -23,7 +23,7 @@ export class AuthSession {
...id(),
nullable: true,
})
- public userId: User['id'];
+ public userId: User['id'] | null;
@ManyToOne(type => User, {
onDelete: 'CASCADE',
diff --git a/packages/backend/src/models/entities/clip.ts b/packages/backend/src/models/entities/clip.ts
index da6b3c7a7f..1386684c32 100644
--- a/packages/backend/src/models/entities/clip.ts
+++ b/packages/backend/src/models/entities/clip.ts
@@ -37,7 +37,7 @@ export class Clip {
public isPublic: boolean;
@Column('varchar', {
- length: 2048, nullable: true, default: null,
+ length: 2048, nullable: true,
comment: 'The description of the Clip.',
})
public description: string | null;
diff --git a/packages/backend/src/models/entities/drive-file.ts b/packages/backend/src/models/entities/drive-file.ts
index 3d375f0e35..a636d1d519 100644
--- a/packages/backend/src/models/entities/drive-file.ts
+++ b/packages/backend/src/models/entities/drive-file.ts
@@ -79,7 +79,6 @@ export class DriveFile {
})
public properties: { width?: number; height?: number; orientation?: number; avgColor?: string };
- @Index()
@Column('boolean')
public storedInternal: boolean;
diff --git a/packages/backend/src/models/entities/emoji.ts b/packages/backend/src/models/entities/emoji.ts
index b72ca72331..7332dd1857 100644
--- a/packages/backend/src/models/entities/emoji.ts
+++ b/packages/backend/src/models/entities/emoji.ts
@@ -36,6 +36,7 @@ export class Emoji {
@Column('varchar', {
length: 512,
+ default: '',
})
public publicUrl: string;
diff --git a/packages/backend/src/models/entities/instance.ts b/packages/backend/src/models/entities/instance.ts
index bb24d6b30f..7ea9234384 100644
--- a/packages/backend/src/models/entities/instance.ts
+++ b/packages/backend/src/models/entities/instance.ts
@@ -107,53 +107,53 @@ export class Instance {
public isSuspended: boolean;
@Column('varchar', {
- length: 64, nullable: true, default: null,
+ length: 64, nullable: true,
comment: 'The software of the Instance.',
})
public softwareName: string | null;
@Column('varchar', {
- length: 64, nullable: true, default: null,
+ length: 64, nullable: true,
})
public softwareVersion: string | null;
@Column('boolean', {
- nullable: true, default: null,
+ nullable: true,
})
public openRegistrations: boolean | null;
@Column('varchar', {
- length: 256, nullable: true, default: null,
+ length: 256, nullable: true,
})
public name: string | null;
@Column('varchar', {
- length: 4096, nullable: true, default: null,
+ length: 4096, nullable: true,
})
public description: string | null;
@Column('varchar', {
- length: 128, nullable: true, default: null,
+ length: 128, nullable: true,
})
public maintainerName: string | null;
@Column('varchar', {
- length: 256, nullable: true, default: null,
+ length: 256, nullable: true,
})
public maintainerEmail: string | null;
@Column('varchar', {
- length: 256, nullable: true, default: null,
+ length: 256, nullable: true,
})
public iconUrl: string | null;
@Column('varchar', {
- length: 256, nullable: true, default: null,
+ length: 256, nullable: true,
})
public faviconUrl: string | null;
@Column('varchar', {
- length: 64, nullable: true, default: null,
+ length: 64, nullable: true,
})
public themeColor: string | null;
diff --git a/packages/backend/src/models/entities/meta.ts b/packages/backend/src/models/entities/meta.ts
index 4d58b5f04f..80b5228bcd 100644
--- a/packages/backend/src/models/entities/meta.ts
+++ b/packages/backend/src/models/entities/meta.ts
@@ -78,7 +78,7 @@ export class Meta {
public blockedHosts: string[];
@Column('varchar', {
- length: 512, array: true, default: '{"/featured", "/channels", "/explore", "/pages", "/about-misskey"}',
+ length: 512, array: true, default: '{/featured,/channels,/explore,/pages,/about-misskey}',
})
public pinnedPages: string[];
@@ -346,14 +346,12 @@ export class Meta {
@Column('varchar', {
length: 8192,
- default: null,
nullable: true,
})
public defaultLightTheme: string | null;
@Column('varchar', {
length: 8192,
- default: null,
nullable: true,
})
public defaultDarkTheme: string | null;
diff --git a/packages/backend/src/models/entities/muting.ts b/packages/backend/src/models/entities/muting.ts
index b3a7e7a671..8f9e69063a 100644
--- a/packages/backend/src/models/entities/muting.ts
+++ b/packages/backend/src/models/entities/muting.ts
@@ -17,7 +17,6 @@ export class Muting {
@Index()
@Column('timestamp with time zone', {
nullable: true,
- default: null,
})
public expiresAt: Date | null;
diff --git a/packages/backend/src/models/entities/note.ts b/packages/backend/src/models/entities/note.ts
index da49d53b69..0ffeb85f69 100644
--- a/packages/backend/src/models/entities/note.ts
+++ b/packages/backend/src/models/entities/note.ts
@@ -53,8 +53,8 @@ export class Note {
})
public threadId: string | null;
- @Column('varchar', {
- length: 8192, nullable: true,
+ @Column('text', {
+ nullable: true,
})
public text: string | null;
@@ -179,7 +179,7 @@ export class Note {
@Index()
@Column({
...id(),
- nullable: true, default: null,
+ nullable: true,
comment: 'The ID of source channel.',
})
public channelId: Channel['id'] | null;
diff --git a/packages/backend/src/models/entities/user-profile.ts b/packages/backend/src/models/entities/user-profile.ts
index f95cb144c5..1778742ea2 100644
--- a/packages/backend/src/models/entities/user-profile.ts
+++ b/packages/backend/src/models/entities/user-profile.ts
@@ -192,6 +192,7 @@ export class UserProfile {
@Column('jsonb', {
default: [],
+ comment: 'List of instances muted by the user.',
})
public mutedInstances: string[];
diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts
index 29d9a0c2ca..df92fb8259 100644
--- a/packages/backend/src/models/entities/user.ts
+++ b/packages/backend/src/models/entities/user.ts
@@ -207,7 +207,7 @@ export class User {
@Column('boolean', {
default: false,
- comment: 'Whether to show users replying to other users in the timeline',
+ comment: 'Whether to show users replying to other users in the timeline.',
})
public showTimelineReplies: boolean;
diff --git a/packages/backend/src/models/repositories/drive-file.ts b/packages/backend/src/models/repositories/drive-file.ts
index 69dc1721c2..0d589d4f11 100644
--- a/packages/backend/src/models/repositories/drive-file.ts
+++ b/packages/backend/src/models/repositories/drive-file.ts
@@ -1,6 +1,5 @@
import { db } from '@/db/postgre.js';
import { DriveFile } from '@/models/entities/drive-file.js';
-import { Users, DriveFolders } from '../index.js';
import { User } from '@/models/entities/user.js';
import { toPuny } from '@/misc/convert-host.js';
import { awaitAll, Promiseable } from '@/prelude/await-all.js';
@@ -9,6 +8,7 @@ import config from '@/config/index.js';
import { query, appendQuery } from '@/prelude/url.js';
import { Meta } from '@/models/entities/meta.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
+import { Users, DriveFolders } from '../index.js';
type PackOptions = {
detail?: boolean,
@@ -29,6 +29,8 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
getPublicProperties(file: DriveFile): DriveFile['properties'] {
if (file.properties.orientation != null) {
+ // TODO
+ //const properties = structuredClone(file.properties);
const properties = JSON.parse(JSON.stringify(file.properties));
if (file.properties.orientation >= 5) {
[properties.width, properties.height] = [properties.height, properties.width];
@@ -111,7 +113,40 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
async pack(
src: DriveFile['id'] | DriveFile,
- options?: PackOptions
+ options?: PackOptions,
+ ): Promise<Packed<'DriveFile'>> {
+ const opts = Object.assign({
+ detail: false,
+ self: false,
+ }, options);
+
+ const file = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
+
+ return await awaitAll<Packed<'DriveFile'>>({
+ id: file.id,
+ createdAt: file.createdAt.toISOString(),
+ name: file.name,
+ type: file.type,
+ md5: file.md5,
+ size: file.size,
+ isSensitive: file.isSensitive,
+ blurhash: file.blurhash,
+ properties: opts.self ? file.properties : this.getPublicProperties(file),
+ url: opts.self ? file.url : this.getPublicUrl(file, false),
+ thumbnailUrl: this.getPublicUrl(file, true),
+ comment: file.comment,
+ folderId: file.folderId,
+ folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, {
+ detail: true,
+ }) : null,
+ userId: opts.withUser ? file.userId : null,
+ user: (opts.withUser && file.userId) ? Users.pack(file.userId) : null,
+ });
+ },
+
+ async packNullable(
+ src: DriveFile['id'] | DriveFile,
+ options?: PackOptions,
): Promise<Packed<'DriveFile'> | null> {
const opts = Object.assign({
detail: false,
@@ -145,9 +180,9 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
async packMany(
files: (DriveFile['id'] | DriveFile)[],
- options?: PackOptions
- ) {
- const items = await Promise.all(files.map(f => this.pack(f, options)));
- return items.filter(x => x != null);
+ options?: PackOptions,
+ ): Promise<Packed<'DriveFile'>[]> {
+ const items = await Promise.all(files.map(f => this.packNullable(f, options)));
+ return items.filter((x): x is Packed<'DriveFile'> => x != null);
},
});
diff --git a/packages/backend/src/models/repositories/note.ts b/packages/backend/src/models/repositories/note.ts
index cf5fcb1787..3fefab0319 100644
--- a/packages/backend/src/models/repositories/note.ts
+++ b/packages/backend/src/models/repositories/note.ts
@@ -136,6 +136,7 @@ async function populateMyReaction(note: Note, meId: User['id'], _hint_?: {
export const NoteRepository = db.getRepository(Note).extend({
async isVisibleForMe(note: Note, meId: User['id'] | null): Promise<boolean> {
+ // This code must always be synchronized with the checks in generateVisibilityQuery.
// visibility ㌠specified ã‹ã¤è‡ªåˆ†ãŒæŒ‡å®šã•れã¦ã„ãªã‹ã£ãŸã‚‰éžè¡¨ç¤º
if (note.visibility === 'specified') {
if (meId == null) {
@@ -144,13 +145,7 @@ export const NoteRepository = db.getRepository(Note).extend({
return true;
} else {
// 指定ã•れã¦ã„ã‚‹ã‹ã©ã†ã‹
- const specified = note.visibleUserIds.some((id: any) => meId === id);
-
- if (specified) {
- return true;
- } else {
- return false;
- }
+ return note.visibleUserIds.some((id: any) => meId === id);
}
}
@@ -168,16 +163,25 @@ export const NoteRepository = db.getRepository(Note).extend({
return true;
} else {
// フォロワーã‹ã©ã†ã‹
- const following = await Followings.findOneBy({
- followeeId: note.userId,
- followerId: meId,
- });
+ const [following, user] = await Promise.all([
+ Followings.count({
+ where: {
+ followeeId: note.userId,
+ followerId: meId,
+ },
+ take: 1,
+ }),
+ Users.findOneByOrFail({ id: meId }),
+ ]);
- if (following == null) {
- return false;
- } else {
- return true;
- }
+ /* If we know the following, everyhting is fine.
+
+ But if we do not know the following, it might be that both the
+ author of the note and the author of the like are remote users,
+ in which case we can never know the following. Instead we have
+ to assume that the users are following each other.
+ */
+ return following > 0 || (note.userHost != null && user.host != null);
}
}
diff --git a/packages/backend/src/models/repositories/page.ts b/packages/backend/src/models/repositories/page.ts
index 1bffb23fa2..092b26b396 100644
--- a/packages/backend/src/models/repositories/page.ts
+++ b/packages/backend/src/models/repositories/page.ts
@@ -1,10 +1,10 @@
import { db } from '@/db/postgre.js';
import { Page } from '@/models/entities/page.js';
import { Packed } from '@/misc/schema.js';
-import { Users, DriveFiles, PageLikes } from '../index.js';
import { awaitAll } from '@/prelude/await-all.js';
import { DriveFile } from '@/models/entities/drive-file.js';
import { User } from '@/models/entities/user.js';
+import { Users, DriveFiles, PageLikes } from '../index.js';
export const PageRepository = db.getRepository(Page).extend({
async pack(
@@ -14,7 +14,7 @@ export const PageRepository = db.getRepository(Page).extend({
const meId = me ? me.id : null;
const page = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
- const attachedFiles: Promise<DriveFile | undefined>[] = [];
+ const attachedFiles: Promise<DriveFile | null>[] = [];
const collectFile = (xs: any[]) => {
for (const x of xs) {
if (x.type === 'image') {
@@ -73,7 +73,7 @@ export const PageRepository = db.getRepository(Page).extend({
script: page.script,
eyeCatchingImageId: page.eyeCatchingImageId,
eyeCatchingImage: page.eyeCatchingImageId ? await DriveFiles.pack(page.eyeCatchingImageId) : null,
- attachedFiles: DriveFiles.packMany(await Promise.all(attachedFiles)),
+ attachedFiles: DriveFiles.packMany((await Promise.all(attachedFiles)).filter((x): x is DriveFile => x != null)),
likedCount: page.likedCount,
isLiked: meId ? await PageLikes.findOneBy({ pageId: page.id, userId: meId }).then(x => x != null) : undefined,
});
diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts
index 541fbaf003..8a4e48efdd 100644
--- a/packages/backend/src/models/repositories/user.ts
+++ b/packages/backend/src/models/repositories/user.ts
@@ -61,47 +61,58 @@ export const UserRepository = db.getRepository(User).extend({
//#endregion
async getRelation(me: User['id'], target: User['id']) {
- const [following1, following2, followReq1, followReq2, toBlocking, fromBlocked, mute] = await Promise.all([
- Followings.findOneBy({
- followerId: me,
- followeeId: target,
- }),
- Followings.findOneBy({
- followerId: target,
- followeeId: me,
- }),
- FollowRequests.findOneBy({
- followerId: me,
- followeeId: target,
- }),
- FollowRequests.findOneBy({
- followerId: target,
- followeeId: me,
- }),
- Blockings.findOneBy({
- blockerId: me,
- blockeeId: target,
- }),
- Blockings.findOneBy({
- blockerId: target,
- blockeeId: me,
- }),
- Mutings.findOneBy({
- muterId: me,
- muteeId: target,
- }),
- ]);
-
- return {
+ return awaitAll({
id: target,
- isFollowing: following1 != null,
- hasPendingFollowRequestFromYou: followReq1 != null,
- hasPendingFollowRequestToYou: followReq2 != null,
- isFollowed: following2 != null,
- isBlocking: toBlocking != null,
- isBlocked: fromBlocked != null,
- isMuted: mute != null,
- };
+ isFollowing: Followings.count({
+ where: {
+ followerId: me,
+ followeeId: target,
+ },
+ take: 1,
+ }).then(n => n > 0),
+ isFollowed: Followings.count({
+ where: {
+ followerId: target,
+ followeeId: me,
+ },
+ take: 1,
+ }).then(n => n > 0),
+ hasPendingFollowRequestFromYou: FollowRequests.count({
+ where: {
+ followerId: me,
+ followeeId: target,
+ },
+ take: 1,
+ }).then(n => n > 0),
+ hasPendingFollowRequestToYou: FollowRequests.count({
+ where: {
+ followerId: target,
+ followeeId: me,
+ },
+ take: 1,
+ }).then(n => n > 0),
+ isBlocking: Blockings.count({
+ where: {
+ blockerId: me,
+ blockeeId: target,
+ },
+ take: 1,
+ }).then(n => n > 0),
+ isBlocked: Blockings.count({
+ where: {
+ blockerId: target,
+ blockeeId: me,
+ },
+ take: 1,
+ }).then(n => n > 0),
+ isMuted: Mutings.count({
+ where: {
+ muterId: me,
+ muteeId: target,
+ },
+ take: 1,
+ }).then(n => n > 0),
+ });
},
async getHasUnreadMessagingMessage(userId: User['id']): Promise<boolean> {
diff --git a/packages/backend/src/queue/index.ts b/packages/backend/src/queue/index.ts
index 2d40290e4c..c5fd7de1cb 100644
--- a/packages/backend/src/queue/index.ts
+++ b/packages/backend/src/queue/index.ts
@@ -1,4 +1,4 @@
-import httpSignature from 'http-signature';
+import httpSignature from '@peertube/http-signature';
import { v4 as uuid } from 'uuid';
import config from '@/config/index.js';
@@ -305,11 +305,13 @@ export default function() {
systemQueue.add('resyncCharts', {
}, {
repeat: { cron: '0 0 * * *' },
+ removeOnComplete: true,
});
systemQueue.add('cleanCharts', {
}, {
repeat: { cron: '0 0 * * *' },
+ removeOnComplete: true,
});
systemQueue.add('checkExpiredMutings', {
diff --git a/packages/backend/src/queue/processors/db/export-blocking.ts b/packages/backend/src/queue/processors/db/export-blocking.ts
index 166c9e4cd3..f5e0424a79 100644
--- a/packages/backend/src/queue/processors/db/export-blocking.ts
+++ b/packages/backend/src/queue/processors/db/export-blocking.ts
@@ -1,11 +1,11 @@
import Bull from 'bull';
-import * as tmp from 'tmp';
import * as fs from 'node:fs';
import { queueLogger } from '../../logger.js';
import { addFile } from '@/services/drive/add-file.js';
import { format as dateFormat } from 'date-fns';
import { getFullApAccount } from '@/misc/convert-host.js';
+import { createTemp } from '@/misc/create-temp.js';
import { Users, Blockings } from '@/models/index.js';
import { MoreThan } from 'typeorm';
import { DbUserJobData } from '@/queue/types.js';
@@ -22,73 +22,72 @@ export async function exportBlocking(job: Bull.Job<DbUserJobData>, done: any): P
}
// Create temp file
- const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
- tmp.file((e, path, fd, cleanup) => {
- if (e) return rej(e);
- res([path, cleanup]);
- });
- });
+ const [path, cleanup] = await createTemp();
logger.info(`Temp file is ${path}`);
- const stream = fs.createWriteStream(path, { flags: 'a' });
+ try {
+ const stream = fs.createWriteStream(path, { flags: 'a' });
- let exportedCount = 0;
- let cursor: any = null;
+ let exportedCount = 0;
+ let cursor: any = null;
- while (true) {
- const blockings = await Blockings.find({
- where: {
- blockerId: user.id,
- ...(cursor ? { id: MoreThan(cursor) } : {}),
- },
- take: 100,
- order: {
- id: 1,
- },
- });
+ while (true) {
+ const blockings = await Blockings.find({
+ where: {
+ blockerId: user.id,
+ ...(cursor ? { id: MoreThan(cursor) } : {}),
+ },
+ take: 100,
+ order: {
+ id: 1,
+ },
+ });
- if (blockings.length === 0) {
- job.progress(100);
- break;
- }
+ if (blockings.length === 0) {
+ job.progress(100);
+ break;
+ }
- cursor = blockings[blockings.length - 1].id;
+ cursor = blockings[blockings.length - 1].id;
- for (const block of blockings) {
- const u = await Users.findOneBy({ id: block.blockeeId });
- if (u == null) {
- exportedCount++; continue;
- }
+ for (const block of blockings) {
+ const u = await Users.findOneBy({ id: block.blockeeId });
+ if (u == null) {
+ exportedCount++; continue;
+ }
- const content = getFullApAccount(u.username, u.host);
- await new Promise<void>((res, rej) => {
- stream.write(content + '\n', err => {
- if (err) {
- logger.error(err);
- rej(err);
- } else {
- res();
- }
+ const content = getFullApAccount(u.username, u.host);
+ await new Promise<void>((res, rej) => {
+ stream.write(content + '\n', err => {
+ if (err) {
+ logger.error(err);
+ rej(err);
+ } else {
+ res();
+ }
+ });
});
+ exportedCount++;
+ }
+
+ const total = await Blockings.countBy({
+ blockerId: user.id,
});
- exportedCount++;
- }
- const total = await Blockings.countBy({
- blockerId: user.id,
- });
+ job.progress(exportedCount / total);
+ }
- job.progress(exportedCount / total);
- }
+ stream.end();
+ logger.succ(`Exported to: ${path}`);
- stream.end();
- logger.succ(`Exported to: ${path}`);
+ const fileName = 'blocking-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
+ const driveFile = await addFile({ user, path, name: fileName, force: true });
- const fileName = 'blocking-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
- const driveFile = await addFile({ user, path, name: fileName, force: true });
+ logger.succ(`Exported to: ${driveFile.id}`);
+ } finally {
+ cleanup();
+ }
- logger.succ(`Exported to: ${driveFile.id}`);
- cleanup();
done();
}
diff --git a/packages/backend/src/queue/processors/db/export-custom-emojis.ts b/packages/backend/src/queue/processors/db/export-custom-emojis.ts
index c2467fb5f0..8ce1d05272 100644
--- a/packages/backend/src/queue/processors/db/export-custom-emojis.ts
+++ b/packages/backend/src/queue/processors/db/export-custom-emojis.ts
@@ -1,5 +1,4 @@
import Bull from 'bull';
-import * as tmp from 'tmp';
import * as fs from 'node:fs';
import { ulid } from 'ulid';
@@ -10,6 +9,7 @@ import { addFile } from '@/services/drive/add-file.js';
import { format as dateFormat } from 'date-fns';
import { Users, Emojis } from '@/models/index.js';
import { } from '@/queue/types.js';
+import { createTemp, createTempDir } from '@/misc/create-temp.js';
import { downloadUrl } from '@/misc/download-url.js';
import config from '@/config/index.js';
import { IsNull } from 'typeorm';
@@ -25,13 +25,7 @@ export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promi
return;
}
- // Create temp dir
- const [path, cleanup] = await new Promise<[string, () => void]>((res, rej) => {
- tmp.dir((e, path, cleanup) => {
- if (e) return rej(e);
- res([path, cleanup]);
- });
- });
+ const [path, cleanup] = await createTempDir();
logger.info(`Temp dir is ${path}`);
@@ -98,12 +92,7 @@ export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promi
metaStream.end();
// Create archive
- const [archivePath, archiveCleanup] = await new Promise<[string, () => void]>((res, rej) => {
- tmp.file((e, path, fd, cleanup) => {
- if (e) return rej(e);
- res([path, cleanup]);
- });
- });
+ const [archivePath, archiveCleanup] = await createTemp();
const archiveStream = fs.createWriteStream(archivePath);
const archive = archiver('zip', {
zlib: { level: 0 },
diff --git a/packages/backend/src/queue/processors/db/export-following.ts b/packages/backend/src/queue/processors/db/export-following.ts
index 965500ac27..4ac165567b 100644
--- a/packages/backend/src/queue/processors/db/export-following.ts
+++ b/packages/backend/src/queue/processors/db/export-following.ts
@@ -1,11 +1,11 @@
import Bull from 'bull';
-import * as tmp from 'tmp';
import * as fs from 'node:fs';
import { queueLogger } from '../../logger.js';
import { addFile } from '@/services/drive/add-file.js';
import { format as dateFormat } from 'date-fns';
import { getFullApAccount } from '@/misc/convert-host.js';
+import { createTemp } from '@/misc/create-temp.js';
import { Users, Followings, Mutings } from '@/models/index.js';
import { In, MoreThan, Not } from 'typeorm';
import { DbUserJobData } from '@/queue/types.js';
@@ -23,73 +23,72 @@ export async function exportFollowing(job: Bull.Job<DbUserJobData>, done: () =>
}
// Create temp file
- const [path, cleanup] = await new Promise<[string, () => void]>((res, rej) => {
- tmp.file((e, path, fd, cleanup) => {
- if (e) return rej(e);
- res([path, cleanup]);
- });
- });
+ const [path, cleanup] = await createTemp();
logger.info(`Temp file is ${path}`);
- const stream = fs.createWriteStream(path, { flags: 'a' });
+ try {
+ const stream = fs.createWriteStream(path, { flags: 'a' });
- let cursor: Following['id'] | null = null;
+ let cursor: Following['id'] | null = null;
- const mutings = job.data.excludeMuting ? await Mutings.findBy({
- muterId: user.id,
- }) : [];
+ const mutings = job.data.excludeMuting ? await Mutings.findBy({
+ muterId: user.id,
+ }) : [];
- while (true) {
- const followings = await Followings.find({
- where: {
- followerId: user.id,
- ...(mutings.length > 0 ? { followeeId: Not(In(mutings.map(x => x.muteeId))) } : {}),
- ...(cursor ? { id: MoreThan(cursor) } : {}),
- },
- take: 100,
- order: {
- id: 1,
- },
- }) as Following[];
+ while (true) {
+ const followings = await Followings.find({
+ where: {
+ followerId: user.id,
+ ...(mutings.length > 0 ? { followeeId: Not(In(mutings.map(x => x.muteeId))) } : {}),
+ ...(cursor ? { id: MoreThan(cursor) } : {}),
+ },
+ take: 100,
+ order: {
+ id: 1,
+ },
+ }) as Following[];
- if (followings.length === 0) {
- break;
- }
+ if (followings.length === 0) {
+ break;
+ }
- cursor = followings[followings.length - 1].id;
+ cursor = followings[followings.length - 1].id;
- for (const following of followings) {
- const u = await Users.findOneBy({ id: following.followeeId });
- if (u == null) {
- continue;
- }
+ for (const following of followings) {
+ const u = await Users.findOneBy({ id: following.followeeId });
+ if (u == null) {
+ continue;
+ }
- if (job.data.excludeInactive && u.updatedAt && (Date.now() - u.updatedAt.getTime() > 1000 * 60 * 60 * 24 * 90)) {
- continue;
- }
+ if (job.data.excludeInactive && u.updatedAt && (Date.now() - u.updatedAt.getTime() > 1000 * 60 * 60 * 24 * 90)) {
+ continue;
+ }
- const content = getFullApAccount(u.username, u.host);
- await new Promise<void>((res, rej) => {
- stream.write(content + '\n', err => {
- if (err) {
- logger.error(err);
- rej(err);
- } else {
- res();
- }
+ const content = getFullApAccount(u.username, u.host);
+ await new Promise<void>((res, rej) => {
+ stream.write(content + '\n', err => {
+ if (err) {
+ logger.error(err);
+ rej(err);
+ } else {
+ res();
+ }
+ });
});
- });
+ }
}
- }
- stream.end();
- logger.succ(`Exported to: ${path}`);
+ stream.end();
+ logger.succ(`Exported to: ${path}`);
- const fileName = 'following-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
- const driveFile = await addFile({ user, path, name: fileName, force: true });
+ const fileName = 'following-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
+ const driveFile = await addFile({ user, path, name: fileName, force: true });
+
+ logger.succ(`Exported to: ${driveFile.id}`);
+ } finally {
+ cleanup();
+ }
- logger.succ(`Exported to: ${driveFile.id}`);
- cleanup();
done();
}
diff --git a/packages/backend/src/queue/processors/db/export-mute.ts b/packages/backend/src/queue/processors/db/export-mute.ts
index 0ef81971f1..6a36cfa072 100644
--- a/packages/backend/src/queue/processors/db/export-mute.ts
+++ b/packages/backend/src/queue/processors/db/export-mute.ts
@@ -1,11 +1,11 @@
import Bull from 'bull';
-import * as tmp from 'tmp';
import * as fs from 'node:fs';
import { queueLogger } from '../../logger.js';
import { addFile } from '@/services/drive/add-file.js';
import { format as dateFormat } from 'date-fns';
import { getFullApAccount } from '@/misc/convert-host.js';
+import { createTemp } from '@/misc/create-temp.js';
import { Users, Mutings } from '@/models/index.js';
import { IsNull, MoreThan } from 'typeorm';
import { DbUserJobData } from '@/queue/types.js';
@@ -22,74 +22,73 @@ export async function exportMute(job: Bull.Job<DbUserJobData>, done: any): Promi
}
// Create temp file
- const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
- tmp.file((e, path, fd, cleanup) => {
- if (e) return rej(e);
- res([path, cleanup]);
- });
- });
+ const [path, cleanup] = await createTemp();
logger.info(`Temp file is ${path}`);
- const stream = fs.createWriteStream(path, { flags: 'a' });
+ try {
+ const stream = fs.createWriteStream(path, { flags: 'a' });
- let exportedCount = 0;
- let cursor: any = null;
+ let exportedCount = 0;
+ let cursor: any = null;
- while (true) {
- const mutes = await Mutings.find({
- where: {
- muterId: user.id,
- expiresAt: IsNull(),
- ...(cursor ? { id: MoreThan(cursor) } : {}),
- },
- take: 100,
- order: {
- id: 1,
- },
- });
+ while (true) {
+ const mutes = await Mutings.find({
+ where: {
+ muterId: user.id,
+ expiresAt: IsNull(),
+ ...(cursor ? { id: MoreThan(cursor) } : {}),
+ },
+ take: 100,
+ order: {
+ id: 1,
+ },
+ });
- if (mutes.length === 0) {
- job.progress(100);
- break;
- }
+ if (mutes.length === 0) {
+ job.progress(100);
+ break;
+ }
- cursor = mutes[mutes.length - 1].id;
+ cursor = mutes[mutes.length - 1].id;
- for (const mute of mutes) {
- const u = await Users.findOneBy({ id: mute.muteeId });
- if (u == null) {
- exportedCount++; continue;
- }
+ for (const mute of mutes) {
+ const u = await Users.findOneBy({ id: mute.muteeId });
+ if (u == null) {
+ exportedCount++; continue;
+ }
- const content = getFullApAccount(u.username, u.host);
- await new Promise<void>((res, rej) => {
- stream.write(content + '\n', err => {
- if (err) {
- logger.error(err);
- rej(err);
- } else {
- res();
- }
+ const content = getFullApAccount(u.username, u.host);
+ await new Promise<void>((res, rej) => {
+ stream.write(content + '\n', err => {
+ if (err) {
+ logger.error(err);
+ rej(err);
+ } else {
+ res();
+ }
+ });
});
+ exportedCount++;
+ }
+
+ const total = await Mutings.countBy({
+ muterId: user.id,
});
- exportedCount++;
- }
- const total = await Mutings.countBy({
- muterId: user.id,
- });
+ job.progress(exportedCount / total);
+ }
- job.progress(exportedCount / total);
- }
+ stream.end();
+ logger.succ(`Exported to: ${path}`);
- stream.end();
- logger.succ(`Exported to: ${path}`);
+ const fileName = 'mute-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
+ const driveFile = await addFile({ user, path, name: fileName, force: true });
- const fileName = 'mute-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
- const driveFile = await addFile({ user, path, name: fileName, force: true });
+ logger.succ(`Exported to: ${driveFile.id}`);
+ } finally {
+ cleanup();
+ }
- logger.succ(`Exported to: ${driveFile.id}`);
- cleanup();
done();
}
diff --git a/packages/backend/src/queue/processors/db/export-notes.ts b/packages/backend/src/queue/processors/db/export-notes.ts
index 7e12a6fac2..051fcdf385 100644
--- a/packages/backend/src/queue/processors/db/export-notes.ts
+++ b/packages/backend/src/queue/processors/db/export-notes.ts
@@ -1,5 +1,4 @@
import Bull from 'bull';
-import * as tmp from 'tmp';
import * as fs from 'node:fs';
import { queueLogger } from '../../logger.js';
@@ -10,6 +9,7 @@ import { MoreThan } from 'typeorm';
import { Note } from '@/models/entities/note.js';
import { Poll } from '@/models/entities/poll.js';
import { DbUserJobData } from '@/queue/types.js';
+import { createTemp } from '@/misc/create-temp.js';
const logger = queueLogger.createSubLogger('export-notes');
@@ -23,82 +23,81 @@ export async function exportNotes(job: Bull.Job<DbUserJobData>, done: any): Prom
}
// Create temp file
- const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
- tmp.file((e, path, fd, cleanup) => {
- if (e) return rej(e);
- res([path, cleanup]);
- });
- });
+ const [path, cleanup] = await createTemp();
logger.info(`Temp file is ${path}`);
- const stream = fs.createWriteStream(path, { flags: 'a' });
+ try {
+ const stream = fs.createWriteStream(path, { flags: 'a' });
- const write = (text: string): Promise<void> => {
- return new Promise<void>((res, rej) => {
- stream.write(text, err => {
- if (err) {
- logger.error(err);
- rej(err);
- } else {
- res();
- }
+ const write = (text: string): Promise<void> => {
+ return new Promise<void>((res, rej) => {
+ stream.write(text, err => {
+ if (err) {
+ logger.error(err);
+ rej(err);
+ } else {
+ res();
+ }
+ });
});
- });
- };
+ };
- await write('[');
+ await write('[');
- let exportedNotesCount = 0;
- let cursor: Note['id'] | null = null;
+ let exportedNotesCount = 0;
+ let cursor: Note['id'] | null = null;
- while (true) {
- const notes = await Notes.find({
- where: {
- userId: user.id,
- ...(cursor ? { id: MoreThan(cursor) } : {}),
- },
- take: 100,
- order: {
- id: 1,
- },
- }) as Note[];
+ while (true) {
+ const notes = await Notes.find({
+ where: {
+ userId: user.id,
+ ...(cursor ? { id: MoreThan(cursor) } : {}),
+ },
+ take: 100,
+ order: {
+ id: 1,
+ },
+ }) as Note[];
- if (notes.length === 0) {
- job.progress(100);
- break;
- }
+ if (notes.length === 0) {
+ job.progress(100);
+ break;
+ }
- cursor = notes[notes.length - 1].id;
+ cursor = notes[notes.length - 1].id;
- for (const note of notes) {
- let poll: Poll | undefined;
- if (note.hasPoll) {
- poll = await Polls.findOneByOrFail({ noteId: note.id });
+ for (const note of notes) {
+ let poll: Poll | undefined;
+ if (note.hasPoll) {
+ poll = await Polls.findOneByOrFail({ noteId: note.id });
+ }
+ const content = JSON.stringify(serialize(note, poll));
+ const isFirst = exportedNotesCount === 0;
+ await write(isFirst ? content : ',\n' + content);
+ exportedNotesCount++;
}
- const content = JSON.stringify(serialize(note, poll));
- const isFirst = exportedNotesCount === 0;
- await write(isFirst ? content : ',\n' + content);
- exportedNotesCount++;
- }
- const total = await Notes.countBy({
- userId: user.id,
- });
+ const total = await Notes.countBy({
+ userId: user.id,
+ });
- job.progress(exportedNotesCount / total);
- }
+ job.progress(exportedNotesCount / total);
+ }
+
+ await write(']');
- await write(']');
+ stream.end();
+ logger.succ(`Exported to: ${path}`);
- stream.end();
- logger.succ(`Exported to: ${path}`);
+ const fileName = 'notes-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json';
+ const driveFile = await addFile({ user, path, name: fileName, force: true });
- const fileName = 'notes-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json';
- const driveFile = await addFile({ user, path, name: fileName, force: true });
+ logger.succ(`Exported to: ${driveFile.id}`);
+ } finally {
+ cleanup();
+ }
- logger.succ(`Exported to: ${driveFile.id}`);
- cleanup();
done();
}
diff --git a/packages/backend/src/queue/processors/db/export-user-lists.ts b/packages/backend/src/queue/processors/db/export-user-lists.ts
index 45852a6038..71dd72df27 100644
--- a/packages/backend/src/queue/processors/db/export-user-lists.ts
+++ b/packages/backend/src/queue/processors/db/export-user-lists.ts
@@ -1,11 +1,11 @@
import Bull from 'bull';
-import * as tmp from 'tmp';
import * as fs from 'node:fs';
import { queueLogger } from '../../logger.js';
import { addFile } from '@/services/drive/add-file.js';
import { format as dateFormat } from 'date-fns';
import { getFullApAccount } from '@/misc/convert-host.js';
+import { createTemp } from '@/misc/create-temp.js';
import { Users, UserLists, UserListJoinings } from '@/models/index.js';
import { In } from 'typeorm';
import { DbUserJobData } from '@/queue/types.js';
@@ -26,46 +26,45 @@ export async function exportUserLists(job: Bull.Job<DbUserJobData>, done: any):
});
// Create temp file
- const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
- tmp.file((e, path, fd, cleanup) => {
- if (e) return rej(e);
- res([path, cleanup]);
- });
- });
+ const [path, cleanup] = await createTemp();
logger.info(`Temp file is ${path}`);
- const stream = fs.createWriteStream(path, { flags: 'a' });
+ try {
+ const stream = fs.createWriteStream(path, { flags: 'a' });
- for (const list of lists) {
- const joinings = await UserListJoinings.findBy({ userListId: list.id });
- const users = await Users.findBy({
- id: In(joinings.map(j => j.userId)),
- });
+ for (const list of lists) {
+ const joinings = await UserListJoinings.findBy({ userListId: list.id });
+ const users = await Users.findBy({
+ id: In(joinings.map(j => j.userId)),
+ });
- for (const u of users) {
- const acct = getFullApAccount(u.username, u.host);
- const content = `${list.name},${acct}`;
- await new Promise<void>((res, rej) => {
- stream.write(content + '\n', err => {
- if (err) {
- logger.error(err);
- rej(err);
- } else {
- res();
- }
+ for (const u of users) {
+ const acct = getFullApAccount(u.username, u.host);
+ const content = `${list.name},${acct}`;
+ await new Promise<void>((res, rej) => {
+ stream.write(content + '\n', err => {
+ if (err) {
+ logger.error(err);
+ rej(err);
+ } else {
+ res();
+ }
+ });
});
- });
+ }
}
- }
- stream.end();
- logger.succ(`Exported to: ${path}`);
+ stream.end();
+ logger.succ(`Exported to: ${path}`);
+
+ const fileName = 'user-lists-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
+ const driveFile = await addFile({ user, path, name: fileName, force: true });
- const fileName = 'user-lists-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
- const driveFile = await addFile({ user, path, name: fileName, force: true });
+ logger.succ(`Exported to: ${driveFile.id}`);
+ } finally {
+ cleanup();
+ }
- logger.succ(`Exported to: ${driveFile.id}`);
- cleanup();
done();
}
diff --git a/packages/backend/src/queue/processors/db/import-custom-emojis.ts b/packages/backend/src/queue/processors/db/import-custom-emojis.ts
index 28e0b867a4..64dfe85374 100644
--- a/packages/backend/src/queue/processors/db/import-custom-emojis.ts
+++ b/packages/backend/src/queue/processors/db/import-custom-emojis.ts
@@ -1,9 +1,9 @@
import Bull from 'bull';
-import * as tmp from 'tmp';
import * as fs from 'node:fs';
import unzipper from 'unzipper';
import { queueLogger } from '../../logger.js';
+import { createTempDir } from '@/misc/create-temp.js';
import { downloadUrl } from '@/misc/download-url.js';
import { DriveFiles, Emojis } from '@/models/index.js';
import { DbUserImportJobData } from '@/queue/types.js';
@@ -25,13 +25,7 @@ export async function importCustomEmojis(job: Bull.Job<DbUserImportJobData>, don
return;
}
- // Create temp dir
- const [path, cleanup] = await new Promise<[string, () => void]>((res, rej) => {
- tmp.dir((e, path, cleanup) => {
- if (e) return rej(e);
- res([path, cleanup]);
- });
- });
+ const [path, cleanup] = await createTempDir();
logger.info(`Temp dir is ${path}`);
diff --git a/packages/backend/src/queue/processors/inbox.ts b/packages/backend/src/queue/processors/inbox.ts
index 4fbfdb234f..198dde6050 100644
--- a/packages/backend/src/queue/processors/inbox.ts
+++ b/packages/backend/src/queue/processors/inbox.ts
@@ -1,6 +1,6 @@
import { URL } from 'node:url';
import Bull from 'bull';
-import httpSignature from 'http-signature';
+import httpSignature from '@peertube/http-signature';
import perform from '@/remote/activitypub/perform.js';
import Logger from '@/services/logger.js';
import { registerOrFetchInstanceDoc } from '@/services/register-or-fetch-instance-doc.js';
diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts
index 6c0b9d9bf6..5ea4725561 100644
--- a/packages/backend/src/queue/types.ts
+++ b/packages/backend/src/queue/types.ts
@@ -3,7 +3,7 @@ import { Note } from '@/models/entities/note';
import { User } from '@/models/entities/user.js';
import { Webhook } from '@/models/entities/webhook';
import { IActivity } from '@/remote/activitypub/type.js';
-import httpSignature from 'http-signature';
+import httpSignature from '@peertube/http-signature';
export type DeliverJobData = {
/** Actor */
diff --git a/packages/backend/src/remote/activitypub/db-resolver.ts b/packages/backend/src/remote/activitypub/db-resolver.ts
index ef07966e42..1a02f675ca 100644
--- a/packages/backend/src/remote/activitypub/db-resolver.ts
+++ b/packages/backend/src/remote/activitypub/db-resolver.ts
@@ -5,14 +5,52 @@ import { User, IRemoteUser, CacheableRemoteUser, CacheableUser } from '@/models/
import { UserPublickey } from '@/models/entities/user-publickey.js';
import { MessagingMessage } from '@/models/entities/messaging-message.js';
import { Notes, Users, UserPublickeys, MessagingMessages } from '@/models/index.js';
-import { IObject, getApId } from './type.js';
-import { resolvePerson } from './models/person.js';
import { Cache } from '@/misc/cache.js';
import { uriPersonCache, userByIdCache } from '@/services/user-cache.js';
+import { IObject, getApId } from './type.js';
+import { resolvePerson } from './models/person.js';
const publicKeyCache = new Cache<UserPublickey | null>(Infinity);
const publicKeyByUserIdCache = new Cache<UserPublickey | null>(Infinity);
+export type UriParseResult = {
+ /** wether the URI was generated by us */
+ local: true;
+ /** id in DB */
+ id: string;
+ /** hint of type, e.g. "notes", "users" */
+ type: string;
+ /** any remaining text after type and id, not including the slash after id. undefined if empty */
+ rest?: string;
+} | {
+ /** wether the URI was generated by us */
+ local: false;
+ /** uri in DB */
+ uri: string;
+};
+
+export function parseUri(value: string | IObject): UriParseResult {
+ const uri = getApId(value);
+
+ // the host part of a URL is case insensitive, so use the 'i' flag.
+ const localRegex = new RegExp('^' + escapeRegexp(config.url) + '/(\\w+)/(\\w+)(?:\/(.+))?', 'i');
+ const matchLocal = uri.match(localRegex);
+
+ if (matchLocal) {
+ return {
+ local: true,
+ type: matchLocal[1],
+ id: matchLocal[2],
+ rest: matchLocal[3],
+ };
+ } else {
+ return {
+ local: false,
+ uri,
+ };
+ }
+}
+
export default class DbResolver {
constructor() {
}
@@ -21,60 +59,54 @@ export default class DbResolver {
* AP Note => Misskey Note in DB
*/
public async getNoteFromApId(value: string | IObject): Promise<Note | null> {
- const parsed = this.parseUri(value);
+ const parsed = parseUri(value);
+
+ if (parsed.local) {
+ if (parsed.type !== 'notes') return null;
- if (parsed.id) {
return await Notes.findOneBy({
id: parsed.id,
});
- }
-
- if (parsed.uri) {
+ } else {
return await Notes.findOneBy({
uri: parsed.uri,
});
}
-
- return null;
}
public async getMessageFromApId(value: string | IObject): Promise<MessagingMessage | null> {
- const parsed = this.parseUri(value);
+ const parsed = parseUri(value);
+
+ if (parsed.local) {
+ if (parsed.type !== 'notes') return null;
- if (parsed.id) {
return await MessagingMessages.findOneBy({
id: parsed.id,
});
- }
-
- if (parsed.uri) {
+ } else {
return await MessagingMessages.findOneBy({
uri: parsed.uri,
});
}
-
- return null;
}
/**
* AP Person => Misskey User in DB
*/
public async getUserFromApId(value: string | IObject): Promise<CacheableUser | null> {
- const parsed = this.parseUri(value);
+ const parsed = parseUri(value);
+
+ if (parsed.local) {
+ if (parsed.type !== 'users') return null;
- if (parsed.id) {
return await userByIdCache.fetchMaybe(parsed.id, () => Users.findOneBy({
id: parsed.id,
}).then(x => x ?? undefined)) ?? null;
- }
-
- if (parsed.uri) {
+ } else {
return await uriPersonCache.fetch(parsed.uri, () => Users.findOneBy({
uri: parsed.uri,
}));
}
-
- return null;
}
/**
@@ -120,31 +152,4 @@ export default class DbResolver {
key,
};
}
-
- public parseUri(value: string | IObject): UriParseResult {
- const uri = getApId(value);
-
- const localRegex = new RegExp('^' + escapeRegexp(config.url) + '/' + '(\\w+)' + '/' + '(\\w+)');
- const matchLocal = uri.match(localRegex);
-
- if (matchLocal) {
- return {
- type: matchLocal[1],
- id: matchLocal[2],
- };
- } else {
- return {
- uri,
- };
- }
- }
}
-
-type UriParseResult = {
- /** id in DB (local object only) */
- id?: string;
- /** uri in DB (remote object only) */
- uri?: string;
- /** hint of type (local object only, ex: notes, users) */
- type?: string
-};
diff --git a/packages/backend/src/remote/activitypub/kernel/announce/note.ts b/packages/backend/src/remote/activitypub/kernel/announce/note.ts
index 680749f4d8..759cb4ae83 100644
--- a/packages/backend/src/remote/activitypub/kernel/announce/note.ts
+++ b/packages/backend/src/remote/activitypub/kernel/announce/note.ts
@@ -9,6 +9,7 @@ import { fetchMeta } from '@/misc/fetch-meta.js';
import { getApLock } from '@/misc/app-lock.js';
import { parseAudience } from '../../audience.js';
import { StatusError } from '@/misc/fetch.js';
+import { Notes } from '@/models/index.js';
const logger = apLogger;
@@ -52,6 +53,8 @@ export default async function(resolver: Resolver, actor: CacheableRemoteUser, ac
throw e;
}
+ if (!await Notes.isVisibleForMe(renote, actor.id)) return 'skip: invalid actor for this activity';
+
logger.info(`Creating the (Re)Note: ${uri}`);
const activityAudience = await parseAudience(actor, activity.to, activity.cc);
diff --git a/packages/backend/src/remote/activitypub/kernel/delete/index.ts b/packages/backend/src/remote/activitypub/kernel/delete/index.ts
index 4c06a9de0b..c7064f553b 100644
--- a/packages/backend/src/remote/activitypub/kernel/delete/index.ts
+++ b/packages/backend/src/remote/activitypub/kernel/delete/index.ts
@@ -13,37 +13,37 @@ export default async (actor: CacheableRemoteUser, activity: IDelete): Promise<st
}
// 削除対象objectã®type
- let formarType: string | undefined;
+ let formerType: string | undefined;
if (typeof activity.object === 'string') {
// typeãŒä¸æ˜Žã ã‘ã©ã€ã©ã†ã›æ¶ˆãˆã¦ã‚‹ã®ã§remote resolveã—ãªã„
- formarType = undefined;
+ formerType = undefined;
} else {
const object = activity.object as IObject;
if (isTombstone(object)) {
- formarType = toSingle(object.formerType);
+ formerType = toSingle(object.formerType);
} else {
- formarType = toSingle(object.type);
+ formerType = toSingle(object.type);
}
}
const uri = getApId(activity.object);
// type䏿˜Žã§ã‚‚actorã¨objectãŒåŒã˜ãªã‚‰ã°ãれã¯Personã«é•ã„ãªã„
- if (!formarType && actor.uri === uri) {
- formarType = 'Person';
+ if (!formerType && actor.uri === uri) {
+ formerType = 'Person';
}
// ãれã§ã‚‚ãªã‹ã£ãŸã‚‰ãŠãらãNote
- if (!formarType) {
- formarType = 'Note';
+ if (!formerType) {
+ formerType = 'Note';
}
- if (validPost.includes(formarType)) {
+ if (validPost.includes(formerType)) {
return await deleteNote(actor, uri);
- } else if (validActor.includes(formarType)) {
+ } else if (validActor.includes(formerType)) {
return await deleteActor(actor, uri);
} else {
- return `Unknown type ${formarType}`;
+ return `Unknown type ${formerType}`;
}
};
diff --git a/packages/backend/src/remote/activitypub/kernel/move/index.ts b/packages/backend/src/remote/activitypub/kernel/move/index.ts
deleted file mode 100644
index e69de29bb2..0000000000
--- a/packages/backend/src/remote/activitypub/kernel/move/index.ts
+++ /dev/null
diff --git a/packages/backend/src/remote/activitypub/kernel/undo/announce.ts b/packages/backend/src/remote/activitypub/kernel/undo/announce.ts
index c2ac31bf8d..417f39722f 100644
--- a/packages/backend/src/remote/activitypub/kernel/undo/announce.ts
+++ b/packages/backend/src/remote/activitypub/kernel/undo/announce.ts
@@ -8,6 +8,7 @@ export const undoAnnounce = async (actor: CacheableRemoteUser, activity: IAnnoun
const note = await Notes.findOneBy({
uri,
+ userId: actor.id,
});
if (!note) return 'skip: no such Announce';
diff --git a/packages/backend/src/remote/activitypub/misc/get-note-html.ts b/packages/backend/src/remote/activitypub/misc/get-note-html.ts
index 3800b40608..389039ebed 100644
--- a/packages/backend/src/remote/activitypub/misc/get-note-html.ts
+++ b/packages/backend/src/remote/activitypub/misc/get-note-html.ts
@@ -3,8 +3,6 @@ import { Note } from '@/models/entities/note.js';
import { toHtml } from '../../../mfm/to-html.js';
export default function(note: Note) {
- let html = note.text ? toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers)) : null;
- if (html == null) html = '<p>.</p>';
-
- return html;
+ if (!note.text) return '';
+ return toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers));
}
diff --git a/packages/backend/src/remote/activitypub/models/mention.ts b/packages/backend/src/remote/activitypub/models/mention.ts
index a160092969..13f77424ec 100644
--- a/packages/backend/src/remote/activitypub/models/mention.ts
+++ b/packages/backend/src/remote/activitypub/models/mention.ts
@@ -1,9 +1,9 @@
+import promiseLimit from 'promise-limit';
import { toArray, unique } from '@/prelude/array.js';
+import { CacheableUser, User } from '@/models/entities/user.js';
import { IObject, isMention, IApMention } from '../type.js';
-import { resolvePerson } from './person.js';
-import promiseLimit from 'promise-limit';
import Resolver from '../resolver.js';
-import { CacheableUser, User } from '@/models/entities/user.js';
+import { resolvePerson } from './person.js';
export async function extractApMentions(tags: IObject | IObject[] | null | undefined) {
const hrefs = unique(extractApMentionObjects(tags).map(x => x.href as string));
@@ -12,7 +12,7 @@ export async function extractApMentions(tags: IObject | IObject[] | null | undef
const limit = promiseLimit<CacheableUser | null>(2);
const mentionedUsers = (await Promise.all(
- hrefs.map(x => limit(() => resolvePerson(x, resolver).catch(() => null)))
+ hrefs.map(x => limit(() => resolvePerson(x, resolver).catch(() => null))),
)).filter((x): x is CacheableUser => x != null);
return mentionedUsers;
diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts
index 097a716614..56c1a483ad 100644
--- a/packages/backend/src/remote/activitypub/models/note.ts
+++ b/packages/backend/src/remote/activitypub/models/note.ts
@@ -3,9 +3,9 @@ import promiseLimit from 'promise-limit';
import config from '@/config/index.js';
import Resolver from '../resolver.js';
import post from '@/services/note/create.js';
-import { resolvePerson, updatePerson } from './person.js';
+import { resolvePerson } from './person.js';
import { resolveImage } from './image.js';
-import { CacheableRemoteUser, IRemoteUser } from '@/models/entities/user.js';
+import { CacheableRemoteUser } from '@/models/entities/user.js';
import { htmlToMfm } from '../misc/html-to-mfm.js';
import { extractApHashtags } from './tag.js';
import { unique, toArray, toSingle } from '@/prelude/array.js';
@@ -15,7 +15,7 @@ import { apLogger } from '../logger.js';
import { DriveFile } from '@/models/entities/drive-file.js';
import { deliverQuestionUpdate } from '@/services/note/polls/update.js';
import { extractDbHost, toPuny } from '@/misc/convert-host.js';
-import { Emojis, Polls, MessagingMessages, Users } from '@/models/index.js';
+import { Emojis, Polls, MessagingMessages } from '@/models/index.js';
import { Note } from '@/models/entities/note.js';
import { IObject, getOneApId, getApId, getOneApHrefNullable, validPost, IPost, isEmoji, getApType } from '../type.js';
import { Emoji } from '@/models/entities/emoji.js';
@@ -197,7 +197,14 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
const cw = note.summary === '' ? null : note.summary;
// テキストã®ãƒ‘ース
- const text = typeof note._misskey_content !== 'undefined' ? note._misskey_content : (note.content ? htmlToMfm(note.content, note.tag) : null);
+ let text: string | null = null;
+ if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source?.content === 'string') {
+ text = note.source.content;
+ } else if (typeof note._misskey_content === 'string') {
+ text = note._misskey_content;
+ } else if (typeof note.content === 'string') {
+ text = htmlToMfm(note.content, note.tag);
+ }
// vote
if (reply && reply.hasPoll) {
diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts
index 88661865da..6097e3b6ed 100644
--- a/packages/backend/src/remote/activitypub/models/person.ts
+++ b/packages/backend/src/remote/activitypub/models/person.ts
@@ -1,17 +1,8 @@
import { URL } from 'node:url';
import promiseLimit from 'promise-limit';
-import $, { Context } from 'cafy';
import config from '@/config/index.js';
-import Resolver from '../resolver.js';
-import { resolveImage } from './image.js';
-import { isCollectionOrOrderedCollection, isCollection, IActor, getApId, getOneApHrefNullable, IObject, isPropertyValue, IApPropertyValue, getApType, isActor } from '../type.js';
-import { fromHtml } from '../../../mfm/from-html.js';
-import { htmlToMfm } from '../misc/html-to-mfm.js';
-import { resolveNote, extractEmojis } from './note.js';
import { registerOrFetchInstanceDoc } from '@/services/register-or-fetch-instance-doc.js';
-import { extractApHashtags } from './tag.js';
-import { apLogger } from '../logger.js';
import { Note } from '@/models/entities/note.js';
import { updateUsertags } from '@/services/update-hashtag.js';
import { Users, Instances, DriveFiles, Followings, UserProfiles, UserPublickeys } from '@/models/index.js';
@@ -32,6 +23,14 @@ import { StatusError } from '@/misc/fetch.js';
import { uriPersonCache } from '@/services/user-cache.js';
import { publishInternalEvent } from '@/services/stream.js';
import { db } from '@/db/postgre.js';
+import { apLogger } from '../logger.js';
+import { htmlToMfm } from '../misc/html-to-mfm.js';
+import { fromHtml } from '../../../mfm/from-html.js';
+import { isCollectionOrOrderedCollection, isCollection, IActor, getApId, getOneApHrefNullable, IObject, isPropertyValue, IApPropertyValue, getApType, isActor } from '../type.js';
+import Resolver from '../resolver.js';
+import { extractApHashtags } from './tag.js';
+import { resolveNote, extractEmojis } from './note.js';
+import { resolveImage } from './image.js';
const logger = apLogger;
@@ -54,20 +53,33 @@ function validateActor(x: IObject, uri: string): IActor {
throw new Error(`invalid Actor type '${x.type}'`);
}
- const validate = (name: string, value: any, validater: Context) => {
- const e = validater.test(value);
- if (e) throw new Error(`invalid Actor: ${name} ${e.message}`);
- };
+ if (!(typeof x.id === 'string' && x.id.length > 0)) {
+ throw new Error('invalid Actor: wrong id');
+ }
- validate('id', x.id, $.default.str.min(1));
- validate('inbox', x.inbox, $.default.str.min(1));
- validate('preferredUsername', x.preferredUsername, $.default.str.min(1).max(128).match(/^\w([\w-.]*\w)?$/));
+ if (!(typeof x.inbox === 'string' && x.inbox.length > 0)) {
+ throw new Error('invalid Actor: wrong inbox');
+ }
+
+ if (!(typeof x.preferredUsername === 'string' && x.preferredUsername.length > 0 && x.preferredUsername.length <= 128 && /^\w([\w-.]*\w)?$/.test(x.preferredUsername))) {
+ throw new Error('invalid Actor: wrong username');
+ }
// These fields are only informational, and some AP software allows these
// fields to be very long. If they are too long, we cut them off. This way
// we can at least see these users and their activities.
- validate('name', truncate(x.name, nameLength), $.default.optional.nullable.str);
- validate('summary', truncate(x.summary, summaryLength), $.default.optional.nullable.str);
+ if (x.name) {
+ if (!(typeof x.name === 'string' && x.name.length > 0)) {
+ throw new Error('invalid Actor: wrong name');
+ }
+ x.name = truncate(x.name, nameLength);
+ }
+ if (x.summary) {
+ if (!(typeof x.summary === 'string' && x.summary.length > 0)) {
+ throw new Error('invalid Actor: wrong summary');
+ }
+ x.summary = truncate(x.summary, summaryLength);
+ }
const idHost = toPuny(new URL(x.id!).hostname);
if (idHost !== expectHost) {
@@ -271,7 +283,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
* @param resolver Resolver
* @param hint Hint of Person object (ã“ã®å€¤ãŒæ­£å½“ãªPersonã®å ´åˆã€Remote resolveã‚’ã›ãšã«æ›´æ–°ã«åˆ©ç”¨ã—ã¾ã™)
*/
-export async function updatePerson(uri: string, resolver?: Resolver | null, hint?: Record<string, unknown>): Promise<void> {
+export async function updatePerson(uri: string, resolver?: Resolver | null, hint?: IObject): Promise<void> {
if (typeof uri !== 'string') throw new Error('uri is not string');
// URIãŒã“ã®ã‚µãƒ¼ãƒãƒ¼ã‚’指ã—ã¦ã„ã‚‹ãªã‚‰ã‚¹ã‚­ãƒƒãƒ—
@@ -289,7 +301,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
if (resolver == null) resolver = new Resolver();
- const object = hint || await resolver.resolve(uri) as any;
+ const object = hint || await resolver.resolve(uri);
const person = validateActor(object, uri);
@@ -400,10 +412,10 @@ export async function resolvePerson(uri: string, resolver?: Resolver): Promise<C
const services: {
[x: string]: (id: string, username: string) => any
} = {
- 'misskey:authentication:twitter': (userId, screenName) => ({ userId, screenName }),
- 'misskey:authentication:github': (id, login) => ({ id, login }),
- 'misskey:authentication:discord': (id, name) => $discord(id, name),
-};
+ 'misskey:authentication:twitter': (userId, screenName) => ({ userId, screenName }),
+ 'misskey:authentication:github': (id, login) => ({ id, login }),
+ 'misskey:authentication:discord': (id, name) => $discord(id, name),
+ };
const $discord = (id: string, name: string) => {
if (typeof name !== 'string') {
@@ -461,7 +473,7 @@ export async function updateFeatured(userId: User['id']) {
// Resolve to (Ordered)Collection Object
const collection = await resolver.resolveCollection(user.featured);
- if (!isCollectionOrOrderedCollection(collection)) throw new Error(`Object is not Collection or OrderedCollection`);
+ if (!isCollectionOrOrderedCollection(collection)) throw new Error('Object is not Collection or OrderedCollection');
// Resolve to Object(may be Note) arrays
const unresolvedItems = isCollection(collection) ? collection.items : collection.orderedItems;
diff --git a/packages/backend/src/remote/activitypub/renderer/block.ts b/packages/backend/src/remote/activitypub/renderer/block.ts
index 10a4fde517..13815fb76f 100644
--- a/packages/backend/src/remote/activitypub/renderer/block.ts
+++ b/packages/backend/src/remote/activitypub/renderer/block.ts
@@ -1,8 +1,20 @@
import config from '@/config/index.js';
-import { ILocalUser, IRemoteUser } from '@/models/entities/user.js';
+import { Blocking } from '@/models/entities/blocking.js';
-export default (blocker: ILocalUser, blockee: IRemoteUser) => ({
- type: 'Block',
- actor: `${config.url}/users/${blocker.id}`,
- object: blockee.uri,
-});
+/**
+ * Renders a block into its ActivityPub representation.
+ *
+ * @param block The block to be rendered. The blockee relation must be loaded.
+ */
+export function renderBlock(block: Blocking) {
+ if (block.blockee?.url == null) {
+ throw new Error('renderBlock: missing blockee uri');
+ }
+
+ return {
+ type: 'Block',
+ id: `${config.url}/blocks/${block.id}`,
+ actor: `${config.url}/users/${block.blockerId}`,
+ object: block.blockee.uri,
+ };
+}
diff --git a/packages/backend/src/remote/activitypub/renderer/flag.ts b/packages/backend/src/remote/activitypub/renderer/flag.ts
index 6fbc11580f..58eadddbaa 100644
--- a/packages/backend/src/remote/activitypub/renderer/flag.ts
+++ b/packages/backend/src/remote/activitypub/renderer/flag.ts
@@ -5,7 +5,7 @@ import { getInstanceActor } from '@/services/instance-actor.js';
// to anonymise reporters, the reporting actor must be a system user
// object has to be a uri or array of uris
-export const renderFlag = (user: ILocalUser, object: [string], content: string): IActivity => {
+export const renderFlag = (user: ILocalUser, object: [string], content: string) => {
return {
type: 'Flag',
actor: `${config.url}/users/${user.id}`,
diff --git a/packages/backend/src/remote/activitypub/renderer/follow.ts b/packages/backend/src/remote/activitypub/renderer/follow.ts
index 9e9692b77a..00fac18ad5 100644
--- a/packages/backend/src/remote/activitypub/renderer/follow.ts
+++ b/packages/backend/src/remote/activitypub/renderer/follow.ts
@@ -4,12 +4,11 @@ import { Users } from '@/models/index.js';
export default (follower: { id: User['id']; host: User['host']; uri: User['host'] }, followee: { id: User['id']; host: User['host']; uri: User['host'] }, requestId?: string) => {
const follow = {
+ id: requestId ?? `${config.url}/follows/${follower.id}/${followee.id}`,
type: 'Follow',
actor: Users.isLocalUser(follower) ? `${config.url}/users/${follower.id}` : follower.uri,
object: Users.isLocalUser(followee) ? `${config.url}/users/${followee.id}` : followee.uri,
} as any;
- if (requestId) follow.id = requestId;
-
return follow;
};
diff --git a/packages/backend/src/remote/activitypub/renderer/index.ts b/packages/backend/src/remote/activitypub/renderer/index.ts
index 5f69332266..f100b77ce5 100644
--- a/packages/backend/src/remote/activitypub/renderer/index.ts
+++ b/packages/backend/src/remote/activitypub/renderer/index.ts
@@ -8,7 +8,7 @@ import { User } from '@/models/entities/user.js';
export const renderActivity = (x: any): IActivity | null => {
if (x == null) return null;
- if (x !== null && typeof x === 'object' && x.id == null) {
+ if (typeof x === 'object' && x.id == null) {
x.id = `${config.url}/${uuid()}`;
}
diff --git a/packages/backend/src/remote/activitypub/renderer/note.ts b/packages/backend/src/remote/activitypub/renderer/note.ts
index 679c8bbfe4..df2ae65205 100644
--- a/packages/backend/src/remote/activitypub/renderer/note.ts
+++ b/packages/backend/src/remote/activitypub/renderer/note.ts
@@ -1,15 +1,15 @@
-import renderDocument from './document.js';
-import renderHashtag from './hashtag.js';
-import renderMention from './mention.js';
-import renderEmoji from './emoji.js';
+import { In, IsNull } from 'typeorm';
import config from '@/config/index.js';
-import toHtml from '../misc/get-note-html.js';
import { Note, IMentionedRemoteUsers } from '@/models/entities/note.js';
import { DriveFile } from '@/models/entities/drive-file.js';
import { DriveFiles, Notes, Users, Emojis, Polls } from '@/models/index.js';
-import { In, IsNull } from 'typeorm';
import { Emoji } from '@/models/entities/emoji.js';
import { Poll } from '@/models/entities/poll.js';
+import toHtml from '../misc/get-note-html.js';
+import renderEmoji from './emoji.js';
+import renderMention from './mention.js';
+import renderHashtag from './hashtag.js';
+import renderDocument from './document.js';
export default async function renderNote(note: Note, dive = true, isTalk = false): Promise<Record<string, unknown>> {
const getPromisedFiles = async (ids: string[]) => {
@@ -82,15 +82,15 @@ export default async function renderNote(note: Note, dive = true, isTalk = false
const files = await getPromisedFiles(note.fileIds);
- const text = note.text;
- let poll: Poll | null;
+ // text should never be undefined
+ const text = note.text ?? null;
+ let poll: Poll | null = null;
if (note.hasPoll) {
poll = await Polls.findOneBy({ noteId: note.id });
}
- let apText = text;
- if (apText == null) apText = '';
+ let apText = text ?? '';
if (quote) {
apText += `\n\nRE: ${quote}`;
@@ -138,6 +138,10 @@ export default async function renderNote(note: Note, dive = true, isTalk = false
summary,
content,
_misskey_content: text,
+ source: {
+ content: text,
+ mediaType: "text/x.misskeymarkdown",
+ },
_misskey_quote: quote,
quoteUrl: quote,
published: note.createdAt.toISOString(),
@@ -159,7 +163,7 @@ export async function getEmojis(names: string[]): Promise<Emoji[]> {
names.map(name => Emojis.findOneBy({
name,
host: IsNull(),
- }))
+ })),
);
return emojis.filter(emoji => emoji != null) as Emoji[];
diff --git a/packages/backend/src/remote/activitypub/resolver.ts b/packages/backend/src/remote/activitypub/resolver.ts
index c1269c75c5..2f9af43c0c 100644
--- a/packages/backend/src/remote/activitypub/resolver.ts
+++ b/packages/backend/src/remote/activitypub/resolver.ts
@@ -2,10 +2,19 @@ import config from '@/config/index.js';
import { getJson } from '@/misc/fetch.js';
import { ILocalUser } from '@/models/entities/user.js';
import { getInstanceActor } from '@/services/instance-actor.js';
+import { fetchMeta } from '@/misc/fetch-meta.js';
+import { extractDbHost, isSelfHost } from '@/misc/convert-host.js';
import { signedGet } from './request.js';
import { IObject, isCollectionOrOrderedCollection, ICollection, IOrderedCollection } from './type.js';
-import { fetchMeta } from '@/misc/fetch-meta.js';
-import { extractDbHost } from '@/misc/convert-host.js';
+import { FollowRequests, Notes, NoteReactions, Polls, Users } from '@/models/index.js';
+import { parseUri } from './db-resolver.js';
+import renderNote from '@/remote/activitypub/renderer/note.js';
+import { renderLike } from '@/remote/activitypub/renderer/like.js';
+import { renderPerson } from '@/remote/activitypub/renderer/person.js';
+import renderQuestion from '@/remote/activitypub/renderer/question.js';
+import renderCreate from '@/remote/activitypub/renderer/create.js';
+import { renderActivity } from '@/remote/activitypub/renderer/index.js';
+import renderFollow from '@/remote/activitypub/renderer/follow.js';
export default class Resolver {
private history: Set<string>;
@@ -40,14 +49,25 @@ export default class Resolver {
return value;
}
+ if (value.includes('#')) {
+ // URLs with fragment parts cannot be resolved correctly because
+ // the fragment part does not get transmitted over HTTP(S).
+ // Avoid strange behaviour by not trying to resolve these at all.
+ throw new Error(`cannot resolve URL with fragment: ${value}`);
+ }
+
if (this.history.has(value)) {
throw new Error('cannot resolve already resolved one');
}
this.history.add(value);
- const meta = await fetchMeta();
const host = extractDbHost(value);
+ if (isSelfHost(host)) {
+ return await this.resolveLocal(value);
+ }
+
+ const meta = await fetchMeta();
if (meta.blockedHosts.includes(host)) {
throw new Error('Instance is blocked');
}
@@ -56,13 +76,13 @@ export default class Resolver {
this.user = await getInstanceActor();
}
- const object = this.user
+ const object = (this.user
? await signedGet(value, this.user)
- : await getJson(value, 'application/activity+json, application/ld+json');
+ : await getJson(value, 'application/activity+json, application/ld+json')) as IObject;
if (object == null || (
Array.isArray(object['@context']) ?
- !object['@context'].includes('https://www.w3.org/ns/activitystreams') :
+ !(object['@context'] as unknown[]).includes('https://www.w3.org/ns/activitystreams') :
object['@context'] !== 'https://www.w3.org/ns/activitystreams'
)) {
throw new Error('invalid response');
@@ -70,4 +90,44 @@ export default class Resolver {
return object;
}
+
+ private resolveLocal(url: string): Promise<IObject> {
+ const parsed = parseUri(url);
+ if (!parsed.local) throw new Error('resolveLocal: not local');
+
+ switch (parsed.type) {
+ case 'notes':
+ return Notes.findOneByOrFail({ id: parsed.id })
+ .then(note => {
+ if (parsed.rest === 'activity') {
+ // this refers to the create activity and not the note itself
+ return renderActivity(renderCreate(renderNote(note)));
+ } else {
+ return renderNote(note);
+ }
+ });
+ case 'users':
+ return Users.findOneByOrFail({ id: parsed.id })
+ .then(user => renderPerson(user as ILocalUser));
+ case 'questions':
+ // Polls are indexed by the note they are attached to.
+ return Promise.all([
+ Notes.findOneByOrFail({ id: parsed.id }),
+ Polls.findOneByOrFail({ noteId: parsed.id }),
+ ])
+ .then(([note, poll]) => renderQuestion({ id: note.userId }, note, poll));
+ case 'likes':
+ return NoteReactions.findOneByOrFail({ id: parsed.id }).then(reaction => renderActivity(renderLike(reaction, { uri: null })));
+ case 'follows':
+ // rest should be <followee id>
+ if (parsed.rest == null || !/^\w+$/.test(parsed.rest)) throw new Error('resolveLocal: invalid follow URI');
+
+ return Promise.all(
+ [parsed.id, parsed.rest].map(id => Users.findOneByOrFail({ id }))
+ )
+ .then(([follower, followee]) => renderActivity(renderFollow(follower, followee, url)));
+ default:
+ throw new Error(`resolveLocal: type ${type} unhandled`);
+ }
+ }
}
diff --git a/packages/backend/src/remote/activitypub/type.ts b/packages/backend/src/remote/activitypub/type.ts
index 2051d2624d..5d00481b75 100644
--- a/packages/backend/src/remote/activitypub/type.ts
+++ b/packages/backend/src/remote/activitypub/type.ts
@@ -2,7 +2,7 @@ export type obj = { [x: string]: any };
export type ApObject = IObject | string | (IObject | string)[];
export interface IObject {
- '@context': string | obj | obj[];
+ '@context': string | string[] | obj | obj[];
type: string | string[];
id?: string;
summary?: string;
@@ -48,7 +48,7 @@ export function getOneApId(value: ApObject): string {
export function getApId(value: string | IObject): string {
if (typeof value === 'string') return value;
if (typeof value.id === 'string') return value.id;
- throw new Error(`cannot detemine id`);
+ throw new Error('cannot detemine id');
}
/**
@@ -57,7 +57,7 @@ export function getApId(value: string | IObject): string {
export function getApType(value: IObject): string {
if (typeof value.type === 'string') return value.type;
if (Array.isArray(value.type) && typeof value.type[0] === 'string') return value.type[0];
- throw new Error(`cannot detect type`);
+ throw new Error('cannot detect type');
}
export function getOneApHrefNullable(value: ApObject | undefined): string | undefined {
@@ -106,7 +106,10 @@ export const isPost = (object: IObject): object is IPost =>
export interface IPost extends IObject {
type: 'Note' | 'Question' | 'Article' | 'Audio' | 'Document' | 'Image' | 'Page' | 'Video' | 'Event';
- _misskey_content?: string;
+ source?: {
+ content: string;
+ mediaType: string;
+ };
_misskey_quote?: string;
quoteUrl?: string;
_misskey_talk: boolean;
@@ -114,7 +117,10 @@ export interface IPost extends IObject {
export interface IQuestion extends IObject {
type: 'Note' | 'Question';
- _misskey_content?: string;
+ source?: {
+ content: string;
+ mediaType: string;
+ };
_misskey_quote?: string;
quoteUrl?: string;
oneOf?: IQuestionChoice[];
diff --git a/packages/backend/src/server/activitypub.ts b/packages/backend/src/server/activitypub.ts
index 133dd36066..cd5f917c40 100644
--- a/packages/backend/src/server/activitypub.ts
+++ b/packages/backend/src/server/activitypub.ts
@@ -1,6 +1,6 @@
import Router from '@koa/router';
import json from 'koa-json-body';
-import httpSignature from 'http-signature';
+import httpSignature from '@peertube/http-signature';
import { renderActivity } from '@/remote/activitypub/renderer/index.js';
import renderNote from '@/remote/activitypub/renderer/note.js';
@@ -15,9 +15,10 @@ import { inbox as processInbox } from '@/queue/index.js';
import { isSelfHost } from '@/misc/convert-host.js';
import { Notes, Users, Emojis, NoteReactions } from '@/models/index.js';
import { ILocalUser, User } from '@/models/entities/user.js';
-import { In, IsNull } from 'typeorm';
+import { In, IsNull, Not } from 'typeorm';
import { renderLike } from '@/remote/activitypub/renderer/like.js';
import { getUserKeypair } from '@/misc/keypair-store.js';
+import renderFollow from '@/remote/activitypub/renderer/follow.js';
// Init router
const router = new Router();
@@ -224,4 +225,30 @@ router.get('/likes/:like', async ctx => {
setResponseType(ctx);
});
+// follow
+router.get('/follows/:follower/:followee', async ctx => {
+ // This may be used before the follow is completed, so we do not
+ // check if the following exists.
+
+ const [follower, followee] = await Promise.all([
+ Users.findOneBy({
+ id: ctx.params.follower,
+ host: IsNull(),
+ }),
+ Users.findOneBy({
+ id: ctx.params.followee,
+ host: Not(IsNull()),
+ }),
+ ]);
+
+ if (follower == null || followee == null) {
+ ctx.status = 404;
+ return;
+ }
+
+ ctx.body = renderActivity(renderFollow(follower, followee));
+ ctx.set('Cache-Control', 'public, max-age=180');
+ setResponseType(ctx);
+});
+
export default router;
diff --git a/packages/backend/src/server/activitypub/followers.ts b/packages/backend/src/server/activitypub/followers.ts
index 4d4f733162..beb48713a6 100644
--- a/packages/backend/src/server/activitypub/followers.ts
+++ b/packages/backend/src/server/activitypub/followers.ts
@@ -1,32 +1,26 @@
import Router from '@koa/router';
+import { FindOptionsWhere, IsNull, LessThan } from 'typeorm';
import config from '@/config/index.js';
-import $ from 'cafy';
-import { ID } from '@/misc/cafy-id.js';
import * as url from '@/prelude/url.js';
import { renderActivity } from '@/remote/activitypub/renderer/index.js';
import renderOrderedCollection from '@/remote/activitypub/renderer/ordered-collection.js';
import renderOrderedCollectionPage from '@/remote/activitypub/renderer/ordered-collection-page.js';
import renderFollowUser from '@/remote/activitypub/renderer/follow-user.js';
-import { setResponseType } from '../activitypub.js';
import { Users, Followings, UserProfiles } from '@/models/index.js';
-import { IsNull, LessThan } from 'typeorm';
+import { Following } from '@/models/entities/following.js';
+import { setResponseType } from '../activitypub.js';
export default async (ctx: Router.RouterContext) => {
const userId = ctx.params.user;
- // Get 'cursor' parameter
- const [cursor, cursorErr] = $.default.optional.type(ID).get(ctx.request.query.cursor);
-
- // Get 'page' parameter
- const pageErr = !$.default.optional.str.or(['true', 'false']).ok(ctx.request.query.page);
- const page: boolean = ctx.request.query.page === 'true';
-
- // Validate parameters
- if (cursorErr || pageErr) {
+ const cursor = ctx.request.query.cursor;
+ if (cursor != null && typeof cursor !== 'string') {
ctx.status = 400;
return;
}
+ const page = ctx.request.query.page === 'true';
+
const user = await Users.findOneBy({
id: userId,
host: IsNull(),
@@ -57,7 +51,7 @@ export default async (ctx: Router.RouterContext) => {
if (page) {
const query = {
followeeId: user.id,
- } as any;
+ } as FindOptionsWhere<Following>;
// ã‚«ãƒ¼ã‚½ãƒ«ãŒæŒ‡å®šã•れã¦ã„ã‚‹å ´åˆ
if (cursor) {
@@ -86,7 +80,7 @@ export default async (ctx: Router.RouterContext) => {
inStock ? `${partOf}?${url.query({
page: 'true',
cursor: followings[followings.length - 1].id,
- })}` : undefined
+ })}` : undefined,
);
ctx.body = renderActivity(rendered);
diff --git a/packages/backend/src/server/activitypub/following.ts b/packages/backend/src/server/activitypub/following.ts
index 0af1f424f9..3a25a6316c 100644
--- a/packages/backend/src/server/activitypub/following.ts
+++ b/packages/backend/src/server/activitypub/following.ts
@@ -1,33 +1,26 @@
import Router from '@koa/router';
+import { LessThan, IsNull, FindOptionsWhere } from 'typeorm';
import config from '@/config/index.js';
-import $ from 'cafy';
-import { ID } from '@/misc/cafy-id.js';
import * as url from '@/prelude/url.js';
import { renderActivity } from '@/remote/activitypub/renderer/index.js';
import renderOrderedCollection from '@/remote/activitypub/renderer/ordered-collection.js';
import renderOrderedCollectionPage from '@/remote/activitypub/renderer/ordered-collection-page.js';
import renderFollowUser from '@/remote/activitypub/renderer/follow-user.js';
-import { setResponseType } from '../activitypub.js';
import { Users, Followings, UserProfiles } from '@/models/index.js';
-import { LessThan, IsNull, FindOptionsWhere } from 'typeorm';
import { Following } from '@/models/entities/following.js';
+import { setResponseType } from '../activitypub.js';
export default async (ctx: Router.RouterContext) => {
const userId = ctx.params.user;
- // Get 'cursor' parameter
- const [cursor, cursorErr] = $.default.optional.type(ID).get(ctx.request.query.cursor);
-
- // Get 'page' parameter
- const pageErr = !$.default.optional.str.or(['true', 'false']).ok(ctx.request.query.page);
- const page: boolean = ctx.request.query.page === 'true';
-
- // Validate parameters
- if (cursorErr || pageErr) {
+ const cursor = ctx.request.query.cursor;
+ if (cursor != null && typeof cursor !== 'string') {
ctx.status = 400;
return;
}
+ const page = ctx.request.query.page === 'true';
+
const user = await Users.findOneBy({
id: userId,
host: IsNull(),
@@ -87,7 +80,7 @@ export default async (ctx: Router.RouterContext) => {
inStock ? `${partOf}?${url.query({
page: 'true',
cursor: followings[followings.length - 1].id,
- })}` : undefined
+ })}` : undefined,
);
ctx.body = renderActivity(rendered);
diff --git a/packages/backend/src/server/activitypub/outbox.ts b/packages/backend/src/server/activitypub/outbox.ts
index 6b9592bcf3..7a2586998a 100644
--- a/packages/backend/src/server/activitypub/outbox.ts
+++ b/packages/backend/src/server/activitypub/outbox.ts
@@ -1,36 +1,37 @@
import Router from '@koa/router';
+import { Brackets, IsNull } from 'typeorm';
import config from '@/config/index.js';
-import $ from 'cafy';
-import { ID } from '@/misc/cafy-id.js';
import { renderActivity } from '@/remote/activitypub/renderer/index.js';
import renderOrderedCollection from '@/remote/activitypub/renderer/ordered-collection.js';
import renderOrderedCollectionPage from '@/remote/activitypub/renderer/ordered-collection-page.js';
-import { setResponseType } from '../activitypub.js';
import renderNote from '@/remote/activitypub/renderer/note.js';
import renderCreate from '@/remote/activitypub/renderer/create.js';
import renderAnnounce from '@/remote/activitypub/renderer/announce.js';
import { countIf } from '@/prelude/array.js';
import * as url from '@/prelude/url.js';
import { Users, Notes } from '@/models/index.js';
-import { makePaginationQuery } from '../api/common/make-pagination-query.js';
-import { Brackets, IsNull } from 'typeorm';
import { Note } from '@/models/entities/note.js';
+import { makePaginationQuery } from '../api/common/make-pagination-query.js';
+import { setResponseType } from '../activitypub.js';
export default async (ctx: Router.RouterContext) => {
const userId = ctx.params.user;
- // Get 'sinceId' parameter
- const [sinceId, sinceIdErr] = $.default.optional.type(ID).get(ctx.request.query.since_id);
+ const sinceId = ctx.request.query.since_id;
+ if (sinceId != null && typeof sinceId !== 'string') {
+ ctx.status = 400;
+ return;
+ }
- // Get 'untilId' parameter
- const [untilId, untilIdErr] = $.default.optional.type(ID).get(ctx.request.query.until_id);
+ const untilId = ctx.request.query.until_id;
+ if (untilId != null && typeof untilId !== 'string') {
+ ctx.status = 400;
+ return;
+ }
- // Get 'page' parameter
- const pageErr = !$.default.optional.str.or(['true', 'false']).ok(ctx.request.query.page);
- const page: boolean = ctx.request.query.page === 'true';
+ const page = ctx.request.query.page === 'true';
- // Validate parameters
- if (sinceIdErr || untilIdErr || pageErr || countIf(x => x != null, [sinceId, untilId]) > 1) {
+ if (countIf(x => x != null, [sinceId, untilId]) > 1) {
ctx.status = 400;
return;
}
@@ -52,8 +53,8 @@ export default async (ctx: Router.RouterContext) => {
const query = makePaginationQuery(Notes.createQueryBuilder('note'), sinceId, untilId)
.andWhere('note.userId = :userId', { userId: user.id })
.andWhere(new Brackets(qb => { qb
- .where(`note.visibility = 'public'`)
- .orWhere(`note.visibility = 'home'`);
+ .where('note.visibility = \'public\'')
+ .orWhere('note.visibility = \'home\'');
}))
.andWhere('note.localOnly = FALSE');
@@ -76,7 +77,7 @@ export default async (ctx: Router.RouterContext) => {
notes.length ? `${partOf}?${url.query({
page: 'true',
until_id: notes[notes.length - 1].id,
- })}` : undefined
+ })}` : undefined,
);
ctx.body = renderActivity(rendered);
@@ -85,7 +86,7 @@ export default async (ctx: Router.RouterContext) => {
// index page
const rendered = renderOrderedCollection(partOf, user.notesCount,
`${partOf}?page=true`,
- `${partOf}?page=true&since_id=000000000000000000000000`
+ `${partOf}?page=true&since_id=000000000000000000000000`,
);
ctx.body = renderActivity(rendered);
ctx.set('Cache-Control', 'public, max-age=180');
diff --git a/packages/backend/src/server/api/2fa.ts b/packages/backend/src/server/api/2fa.ts
index dce8accaac..96b9316e47 100644
--- a/packages/backend/src/server/api/2fa.ts
+++ b/packages/backend/src/server/api/2fa.ts
@@ -1,6 +1,6 @@
import * as crypto from 'node:crypto';
-import config from '@/config/index.js';
import * as jsrsasign from 'jsrsasign';
+import config from '@/config/index.js';
const ECC_PRELUDE = Buffer.from([0x04]);
const NULL_BYTE = Buffer.from([0]);
@@ -145,7 +145,7 @@ export function verifyLogin({
export const procedures = {
none: {
- verify({ publicKey }: {publicKey: Map<number, Buffer>}) {
+ verify({ publicKey }: { publicKey: Map<number, Buffer> }) {
const negTwo = publicKey.get(-2);
if (!negTwo || negTwo.length !== 32) {
diff --git a/packages/backend/src/server/api/call.ts b/packages/backend/src/server/api/call.ts
index 9a85e4565b..cd3e0abc06 100644
--- a/packages/backend/src/server/api/call.ts
+++ b/packages/backend/src/server/api/call.ts
@@ -2,10 +2,11 @@ import Koa from 'koa';
import { performance } from 'perf_hooks';
import { limiter } from './limiter.js';
import { CacheableLocalUser, User } from '@/models/entities/user.js';
-import endpoints, { IEndpoint } from './endpoints.js';
+import endpoints, { IEndpointMeta } from './endpoints.js';
import { ApiError } from './error.js';
import { apiLogger } from './logger.js';
import { AccessToken } from '@/models/entities/access-token.js';
+import { getIpHash } from '@/misc/get-ip-hash.js';
const accessDenied = {
message: 'Access denied.',
@@ -15,6 +16,7 @@ const accessDenied = {
export default async (endpoint: string, user: CacheableLocalUser | null | undefined, token: AccessToken | null | undefined, data: any, ctx?: Koa.Context) => {
const isSecure = user != null && token == null;
+ const isModerator = user != null && (user.isModerator || user.isAdmin);
const ep = endpoints.find(e => e.name === endpoint);
@@ -31,6 +33,32 @@ export default async (endpoint: string, user: CacheableLocalUser | null | undefi
throw new ApiError(accessDenied);
}
+ if (ep.meta.limit && !isModerator) {
+ // koa will automatically load the `X-Forwarded-For` header if `proxy: true` is configured in the app.
+ let limitActor: string;
+ if (user) {
+ limitActor = user.id;
+ } else {
+ limitActor = getIpHash(ctx!.ip);
+ }
+
+ const limit = Object.assign({}, ep.meta.limit);
+
+ if (!limit.key) {
+ limit.key = ep.name;
+ }
+
+ // Rate limit
+ await limiter(limit as IEndpointMeta['limit'] & { key: NonNullable<string> }, limitActor).catch(e => {
+ throw new ApiError({
+ message: 'Rate limit exceeded. Please try again later.',
+ code: 'RATE_LIMIT_EXCEEDED',
+ id: 'd5826d14-3982-4d2e-8011-b9e9f02499ef',
+ httpStatusCode: 429,
+ });
+ });
+ }
+
if (ep.meta.requireCredential && user == null) {
throw new ApiError({
message: 'Credential required.',
@@ -53,7 +81,7 @@ export default async (endpoint: string, user: CacheableLocalUser | null | undefi
throw new ApiError(accessDenied, { reason: 'You are not the admin.' });
}
- if (ep.meta.requireModerator && !user!.isAdmin && !user!.isModerator) {
+ if (ep.meta.requireModerator && !isModerator) {
throw new ApiError(accessDenied, { reason: 'You are not a moderator.' });
}
@@ -65,18 +93,6 @@ export default async (endpoint: string, user: CacheableLocalUser | null | undefi
});
}
- if (ep.meta.requireCredential && ep.meta.limit && !user!.isAdmin && !user!.isModerator) {
- // Rate limit
- await limiter(ep as IEndpoint & { meta: { limit: NonNullable<IEndpoint['meta']['limit']> } }, user!).catch(e => {
- throw new ApiError({
- message: 'Rate limit exceeded. Please try again later.',
- code: 'RATE_LIMIT_EXCEEDED',
- id: 'd5826d14-3982-4d2e-8011-b9e9f02499ef',
- httpStatusCode: 429,
- });
- });
- }
-
// Cast non JSON input
if (ep.meta.requireFile && ep.params.properties) {
for (const k of Object.keys(ep.params.properties)) {
diff --git a/packages/backend/src/server/api/common/generate-visibility-query.ts b/packages/backend/src/server/api/common/generate-visibility-query.ts
index 715982934c..b50b6812f4 100644
--- a/packages/backend/src/server/api/common/generate-visibility-query.ts
+++ b/packages/backend/src/server/api/common/generate-visibility-query.ts
@@ -3,6 +3,7 @@ import { Followings } from '@/models/index.js';
import { Brackets, SelectQueryBuilder } from 'typeorm';
export function generateVisibilityQuery(q: SelectQueryBuilder<any>, me?: { id: User['id'] } | null) {
+ // This code must always be synchronized with the checks in Notes.isVisibleForMe.
if (me == null) {
q.andWhere(new Brackets(qb => { qb
.where(`note.visibility = 'public'`)
@@ -11,7 +12,7 @@ export function generateVisibilityQuery(q: SelectQueryBuilder<any>, me?: { id: U
} else {
const followingQuery = Followings.createQueryBuilder('following')
.select('following.followeeId')
- .where('following.followerId = :followerId', { followerId: me.id });
+ .where('following.followerId = :meId');
q.andWhere(new Brackets(qb => { qb
// 公開投稿ã§ã‚ã‚‹
@@ -20,21 +21,22 @@ export function generateVisibilityQuery(q: SelectQueryBuilder<any>, me?: { id: U
.orWhere(`note.visibility = 'home'`);
}))
// ã¾ãŸã¯ 自分自身
- .orWhere('note.userId = :userId1', { userId1: me.id })
+ .orWhere('note.userId = :meId')
// ã¾ãŸã¯ 自分宛ã¦
- .orWhere(`'{"${me.id}"}' <@ note.visibleUserIds`)
+ .orWhere(':meId = ANY(note.visibleUserIds)')
+ .orWhere(':meId = ANY(note.mentions)')
.orWhere(new Brackets(qb => { qb
// ã¾ãŸã¯ フォロワー宛ã¦ã®æŠ•稿ã§ã‚りã€
- .where('note.visibility = \'followers\'')
+ .where(`note.visibility = 'followers'`)
.andWhere(new Brackets(qb => { qb
// 自分ãŒãƒ•ォロワーã§ã‚ã‚‹
.where(`note.userId IN (${ followingQuery.getQuery() })`)
// ã¾ãŸã¯ è‡ªåˆ†ã®æŠ•ç¨¿ã¸ã®ãƒªãƒ—ライ
- .orWhere('note.replyUserId = :userId3', { userId3: me.id });
+ .orWhere('note.replyUserId = :meId');
}));
}));
}));
- q.setParameters(followingQuery.getParameters());
+ q.setParameters({ meId: me.id });
}
}
diff --git a/packages/backend/src/server/api/common/read-messaging-message.ts b/packages/backend/src/server/api/common/read-messaging-message.ts
index 3638518e67..c4c18ffa06 100644
--- a/packages/backend/src/server/api/common/read-messaging-message.ts
+++ b/packages/backend/src/server/api/common/read-messaging-message.ts
@@ -1,6 +1,7 @@
import { publishMainStream, publishGroupMessagingStream } from '@/services/stream.js';
import { publishMessagingStream } from '@/services/stream.js';
import { publishMessagingIndexStream } from '@/services/stream.js';
+import { pushNotification } from '@/services/push-notification.js';
import { User, IRemoteUser } from '@/models/entities/user.js';
import { MessagingMessage } from '@/models/entities/messaging-message.js';
import { MessagingMessages, UserGroupJoinings, Users } from '@/models/index.js';
@@ -50,6 +51,21 @@ export async function readUserMessagingMessage(
if (!await Users.getHasUnreadMessagingMessage(userId)) {
// å…¨ã¦ã®(ã„ã¾ã¾ã§æœªèª­ã ã£ãŸ)自分宛ã¦ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’(ã“れã§)読ã¿ã¾ã—ãŸã‚ˆã¨ã„ã†ã‚¤ãƒ™ãƒ³ãƒˆã‚’発行
publishMainStream(userId, 'readAllMessagingMessages');
+ pushNotification(userId, 'readAllMessagingMessages', undefined);
+ } else {
+ // ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¨ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã§æœªèª­ãŒãªã‘れã°ã‚¤ãƒ™ãƒ³ãƒˆç™ºè¡Œ
+ const count = await MessagingMessages.count({
+ where: {
+ userId: otherpartyId,
+ recipientId: userId,
+ isRead: false,
+ },
+ take: 1
+ });
+
+ if (!count) {
+ pushNotification(userId, 'readAllMessagingMessagesOfARoom', { userId: otherpartyId });
+ }
}
}
@@ -104,6 +120,19 @@ export async function readGroupMessagingMessage(
if (!await Users.getHasUnreadMessagingMessage(userId)) {
// å…¨ã¦ã®(ã„ã¾ã¾ã§æœªèª­ã ã£ãŸ)自分宛ã¦ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’(ã“れã§)読ã¿ã¾ã—ãŸã‚ˆã¨ã„ã†ã‚¤ãƒ™ãƒ³ãƒˆã‚’発行
publishMainStream(userId, 'readAllMessagingMessages');
+ pushNotification(userId, 'readAllMessagingMessages', undefined);
+ } else {
+ // ãã®ã‚°ãƒ«ãƒ¼ãƒ—ã«ãŠã„ã¦æœªèª­ãŒãªã‘れã°ã‚¤ãƒ™ãƒ³ãƒˆç™ºè¡Œ
+ const unreadExist = await MessagingMessages.createQueryBuilder('message')
+ .where(`message.groupId = :groupId`, { groupId: groupId })
+ .andWhere('message.userId != :userId', { userId: userId })
+ .andWhere('NOT (:userId = ANY(message.reads))', { userId: userId })
+ .andWhere('message.createdAt > :joinedAt', { joinedAt: joining.createdAt }) // 自分ãŒåŠ å…¥ã™ã‚‹å‰ã®ä¼šè©±ã«ã¤ã„ã¦ã¯ã€æœªèª­æ‰±ã„ã—ãªã„
+ .getOne().then(x => x != null);
+
+ if (!unreadExist) {
+ pushNotification(userId, 'readAllMessagingMessagesOfARoom', { groupId });
+ }
}
}
diff --git a/packages/backend/src/server/api/common/read-notification.ts b/packages/backend/src/server/api/common/read-notification.ts
index 1f575042a0..0dad35bcc2 100644
--- a/packages/backend/src/server/api/common/read-notification.ts
+++ b/packages/backend/src/server/api/common/read-notification.ts
@@ -1,4 +1,5 @@
import { publishMainStream } from '@/services/stream.js';
+import { pushNotification } from '@/services/push-notification.js';
import { User } from '@/models/entities/user.js';
import { Notification } from '@/models/entities/notification.js';
import { Notifications, Users } from '@/models/index.js';
@@ -16,28 +17,29 @@ export async function readNotification(
isRead: true,
});
- post(userId);
+ if (!await Users.getHasUnreadNotification(userId)) return postReadAllNotifications(userId);
+ else return postReadNotifications(userId, notificationIds);
}
export async function readNotificationByQuery(
userId: User['id'],
query: Record<string, any>
) {
- // Update documents
- await Notifications.update({
+ const notificationIds = await Notifications.find({
...query,
notifieeId: userId,
isRead: false,
- }, {
- isRead: true,
- });
+ }).then(notifications => notifications.map(notification => notification.id));
+
+ return readNotification(userId, notificationIds);
+}
- post(userId);
+function postReadAllNotifications(userId: User['id']) {
+ publishMainStream(userId, 'readAllNotifications');
+ return pushNotification(userId, 'readAllNotifications', undefined);
}
-async function post(userId: User['id']) {
- if (!await Users.getHasUnreadNotification(userId)) {
- // å…¨ã¦ã®(ã„ã¾ã¾ã§æœªèª­ã ã£ãŸ)通知を(ã“れã§)読ã¿ã¾ã—ãŸã‚ˆã¨ã„ã†ã‚¤ãƒ™ãƒ³ãƒˆã‚’発行
- publishMainStream(userId, 'readAllNotifications');
- }
+function postReadNotifications(userId: User['id'], notificationIds: Notification['id'][]) {
+ publishMainStream(userId, 'readNotifications', notificationIds);
+ return pushNotification(userId, 'readNotifications', { notificationIds });
}
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index e2db03f13a..1e7afd8cdd 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -654,7 +654,6 @@ export interface IEndpointMeta {
/**
* エンドãƒã‚¤ãƒ³ãƒˆã®ãƒªãƒŸãƒ†ãƒ¼ã‚·ãƒ§ãƒ³ã«é–¢ã™ã‚‹ã‚„ã¤
* çœç•¥ã—ãŸå ´åˆã¯ãƒªãƒŸãƒ†ãƒ¼ã‚·ãƒ§ãƒ³ã¯ç„¡ã„ã‚‚ã®ã¨ã—ã¦è§£é‡ˆã•れã¾ã™ã€‚
- * ã¾ãŸã€withCredential ㌠false ã®å ´åˆã¯ãƒªãƒŸãƒ†ãƒ¼ã‚·ãƒ§ãƒ³ã‚’行ã†ã“ã¨ã¯ã§ãã¾ã›ã‚“。
*/
readonly limit?: {
diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
index 1d8eb1d618..7a5758d75b 100644
--- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
@@ -1,5 +1,6 @@
-import define from '../../../define.js';
import { Announcements, AnnouncementReads } from '@/models/index.js';
+import { Announcement } from '@/models/entities/announcement.js';
+import define from '../../../define.js';
import { makePaginationQuery } from '../../../common/make-pagination-query.js';
export const meta = {
@@ -68,11 +69,21 @@ export default define(meta, paramDef, async (ps) => {
const announcements = await query.take(ps.limit).getMany();
+ const reads = new Map<Announcement, number>();
+
for (const announcement of announcements) {
- (announcement as any).reads = await AnnouncementReads.countBy({
+ reads.set(announcement, await AnnouncementReads.countBy({
announcementId: announcement.id,
- });
+ }));
}
- return announcements;
+ return announcements.map(announcement => ({
+ id: announcement.id,
+ createdAt: announcement.createdAt.toISOString(),
+ updatedAt: announcement.updatedAt?.toISOString() ?? null,
+ title: announcement.title,
+ text: announcement.text,
+ imageUrl: announcement.imageUrl,
+ reads: reads.get(announcement)!,
+ }));
});
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 bf6cc16532..78033aed58 100644
--- a/packages/backend/src/server/api/endpoints/admin/show-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts
@@ -1,5 +1,5 @@
+import { Signins, UserProfiles, Users } from '@/models/index.js';
import define from '../../define.js';
-import { Users } from '@/models/index.js';
export const meta = {
tags: ['admin'],
@@ -23,9 +23,12 @@ export const paramDef = {
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async (ps, me) => {
- const user = await Users.findOneBy({ id: ps.userId });
+ const [user, profile] = await Promise.all([
+ Users.findOneBy({ id: ps.userId }),
+ UserProfiles.findOneBy({ userId: ps.userId })
+ ]);
- if (user == null) {
+ if (user == null || profile == null) {
throw new Error('user not found');
}
@@ -34,8 +37,37 @@ export default define(meta, paramDef, async (ps, me) => {
throw new Error('cannot show info of admin');
}
+ if (!_me.isAdmin) {
+ return {
+ isModerator: user.isModerator,
+ isSilenced: user.isSilenced,
+ isSuspended: user.isSuspended,
+ };
+ }
+
+ const maskedKeys = ['accessToken', 'accessTokenSecret', 'refreshToken'];
+ Object.keys(profile.integrations).forEach(integration => {
+ maskedKeys.forEach(key => profile.integrations[integration][key] = '<MASKED>');
+ });
+
+ const signins = await Signins.findBy({ userId: user.id });
+
return {
- ...user,
- token: user.token != null ? '<MASKED>' : user.token,
+ email: profile.email,
+ emailVerified: profile.emailVerified,
+ autoAcceptFollowed: profile.autoAcceptFollowed,
+ noCrawle: profile.noCrawle,
+ alwaysMarkNsfw: profile.alwaysMarkNsfw,
+ carefulBot: profile.carefulBot,
+ injectFeaturedNote: profile.injectFeaturedNote,
+ receiveAnnouncementEmail: profile.receiveAnnouncementEmail,
+ integrations: profile.integrations,
+ mutedWords: profile.mutedWords,
+ mutedInstances: profile.mutedInstances,
+ mutingNotificationTypes: profile.mutingNotificationTypes,
+ isModerator: user.isModerator,
+ isSilenced: user.isSilenced,
+ isSuspended: user.isSuspended,
+ signins,
};
});
diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts
index 2703b4b9db..1575d81d5d 100644
--- a/packages/backend/src/server/api/endpoints/admin/show-users.ts
+++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts
@@ -1,5 +1,5 @@
-import define from '../../define.js';
import { Users } from '@/models/index.js';
+import define from '../../define.js';
export const meta = {
tags: ['admin'],
@@ -24,8 +24,8 @@ export const paramDef = {
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
offset: { type: 'integer', default: 0 },
sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt'] },
- state: { type: 'string', enum: ['all', 'available', 'admin', 'moderator', 'adminOrModerator', 'silenced', 'suspended'], default: "all" },
- origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" },
+ state: { type: 'string', enum: ['all', 'alive', 'available', 'admin', 'moderator', 'adminOrModerator', 'silenced', 'suspended'], default: 'all' },
+ origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'local' },
username: { type: 'string', nullable: true, default: null },
hostname: {
type: 'string',
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 b23ee9e3df..09e43301b7 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -27,7 +27,7 @@ export const paramDef = {
blockedHosts: { type: 'array', nullable: true, items: {
type: 'string',
} },
- themeColor: { type: 'string', nullable: true },
+ themeColor: { type: 'string', nullable: true, pattern: '^#[0-9a-fA-F]{6}$' },
mascotImageUrl: { type: 'string', nullable: true },
bannerUrl: { type: 'string', nullable: true },
errorImageUrl: { type: 'string', nullable: true },
diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts
index 7ffe89a1e5..415a8cc693 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts
@@ -9,6 +9,8 @@ export const meta = {
kind: 'read:drive',
+ description: 'Find the notes to which the given file is attached.',
+
res: {
type: 'array',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts
index 80293df5d9..bbae9bf4e4 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts
@@ -8,6 +8,8 @@ export const meta = {
kind: 'read:drive',
+ description: 'Check if a given file exists.',
+
res: {
type: 'boolean',
optional: false, nullable: false,
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 0939ae3365..7397fd9ce9 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/create.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/create.ts
@@ -20,6 +20,8 @@ export const meta = {
kind: 'write:drive',
+ description: 'Upload a new drive file.',
+
res: {
type: 'object',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/drive/files/delete.ts b/packages/backend/src/server/api/endpoints/drive/files/delete.ts
index 61c56e6314..6108ae7da9 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/delete.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/delete.ts
@@ -11,6 +11,8 @@ export const meta = {
kind: 'write:drive',
+ description: 'Delete an existing drive file.',
+
errors: {
noSuchFile: {
message: 'No such file.',
diff --git a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts
index f9b4ea89ea..f2bc7348c6 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts
@@ -1,5 +1,5 @@
-import define from '../../../define.js';
import { DriveFiles } from '@/models/index.js';
+import define from '../../../define.js';
export const meta = {
tags: ['drive'],
@@ -8,6 +8,8 @@ export const meta = {
kind: 'read:drive',
+ description: 'Search for a drive file by a hash of the contents.',
+
res: {
type: 'array',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/drive/files/find.ts b/packages/backend/src/server/api/endpoints/drive/files/find.ts
index 4938a69d11..245fb45a65 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/find.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/find.ts
@@ -9,6 +9,8 @@ export const meta = {
kind: 'read:drive',
+ description: 'Search for a drive file by the given parameters.',
+
res: {
type: 'array',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/drive/files/show.ts b/packages/backend/src/server/api/endpoints/drive/files/show.ts
index a2bc0c7aa4..2c604c54c8 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/show.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/show.ts
@@ -1,7 +1,7 @@
-import define from '../../../define.js';
-import { ApiError } from '../../../error.js';
import { DriveFile } from '@/models/entities/drive-file.js';
import { DriveFiles, Users } from '@/models/index.js';
+import define from '../../../define.js';
+import { ApiError } from '../../../error.js';
export const meta = {
tags: ['drive'],
@@ -10,6 +10,8 @@ export const meta = {
kind: 'read:drive',
+ description: 'Show the properties of a drive file.',
+
res: {
type: 'object',
optional: false, nullable: false,
@@ -51,7 +53,7 @@ export const paramDef = {
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async (ps, user) => {
- let file: DriveFile | undefined;
+ let file: DriveFile | null = null;
if (ps.fileId) {
file = await DriveFiles.findOneBy({ id: ps.fileId });
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 4b3f5f2dc9..e3debe0b4f 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/update.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts
@@ -11,6 +11,8 @@ export const meta = {
kind: 'write:drive',
+ description: 'Update the properties of a drive file.',
+
errors: {
invalidFileName: {
message: 'Invalid file name.',
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 3bfecac802..53f2298f21 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
@@ -13,6 +13,8 @@ export const meta = {
max: 60,
},
+ description: 'Request the server to download a new drive file from the specified URL.',
+
requireCredential: true,
kind: 'write:drive',
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts
index d5e1b19e54..33f5717728 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts
@@ -2,8 +2,8 @@ import bcrypt from 'bcryptjs';
import * as speakeasy from 'speakeasy';
import * as QRCode from 'qrcode';
import config from '@/config/index.js';
-import define from '../../../define.js';
import { UserProfiles } from '@/models/index.js';
+import define from '../../../define.js';
export const meta = {
requireCredential: true,
@@ -40,15 +40,17 @@ export default define(meta, paramDef, async (ps, user) => {
});
// Get the data URL of the authenticator URL
- const dataUrl = await QRCode.toDataURL(speakeasy.otpauthURL({
+ const url = speakeasy.otpauthURL({
secret: secret.base32,
encoding: 'base32',
label: user.username,
issuer: config.host,
- }));
+ });
+ const dataUrl = await QRCode.toDataURL(url);
return {
qr: dataUrl,
+ url,
secret: secret.base32,
label: user.username,
issuer: config.host,
diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts
index 9de05918c0..a133294169 100644
--- a/packages/backend/src/server/api/endpoints/notes/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/create.ts
@@ -1,15 +1,15 @@
import ms from 'ms';
+import { In } from 'typeorm';
import create from '@/services/note/create.js';
-import define from '../../define.js';
-import { ApiError } from '../../error.js';
import { User } from '@/models/entities/user.js';
import { Users, DriveFiles, Notes, Channels, Blockings } from '@/models/index.js';
import { DriveFile } from '@/models/entities/drive-file.js';
import { Note } from '@/models/entities/note.js';
-import { noteVisibilities } from '../../../../types.js';
import { Channel } from '@/models/entities/channel.js';
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
-import { In } from 'typeorm';
+import { noteVisibilities } from '../../../../types.js';
+import { ApiError } from '../../error.js';
+import define from '../../define.js';
export const meta = {
tags: ['notes'],
@@ -83,7 +83,7 @@ export const meta = {
export const paramDef = {
type: 'object',
properties: {
- visibility: { type: 'string', enum: ['public', 'home', 'followers', 'specified'], default: "public" },
+ visibility: { type: 'string', enum: ['public', 'home', 'followers', 'specified'], default: 'public' },
visibleUserIds: { type: 'array', uniqueItems: true, items: {
type: 'string', format: 'misskey:id',
} },
@@ -134,7 +134,7 @@ export const paramDef = {
{
// (re)note with text, files and poll are optional
properties: {
- text: { type: 'string', maxLength: MAX_NOTE_TEXT_LENGTH, nullable: false },
+ text: { type: 'string', minLength: 1, maxLength: MAX_NOTE_TEXT_LENGTH, nullable: false },
},
required: ['text'],
},
@@ -149,7 +149,7 @@ export const paramDef = {
{
// (re)note with poll, text and files are optional
properties: {
- poll: { type: 'object', nullable: false, },
+ poll: { type: 'object', nullable: false },
},
required: ['poll'],
},
@@ -172,20 +172,24 @@ export default define(meta, paramDef, async (ps, user) => {
let files: DriveFile[] = [];
const fileIds = ps.fileIds != null ? ps.fileIds : ps.mediaIds != null ? ps.mediaIds : null;
if (fileIds != null) {
- files = await DriveFiles.findBy({
- userId: user.id,
- id: In(fileIds),
- });
+ files = await DriveFiles.createQueryBuilder('file')
+ .where('file.userId = :userId AND file.id IN (:...fileIds)', {
+ userId: user.id,
+ fileIds,
+ })
+ .orderBy('array_position(ARRAY[:...fileIds], "id"::text)')
+ .setParameters({ fileIds })
+ .getMany();
}
- let renote: Note | null;
+ let renote: Note | null = null;
if (ps.renoteId != null) {
// Fetch renote to note
renote = await Notes.findOneBy({ id: ps.renoteId });
if (renote == null) {
throw new ApiError(meta.errors.noSuchRenoteTarget);
- } else if (renote.renoteId && !renote.text && !renote.fileIds && !renote.poll) {
+ } else if (renote.renoteId && !renote.text && !renote.fileIds && !renote.hasPoll) {
throw new ApiError(meta.errors.cannotReRenote);
}
@@ -201,14 +205,14 @@ export default define(meta, paramDef, async (ps, user) => {
}
}
- let reply: Note | null;
+ let reply: Note | null = null;
if (ps.replyId != null) {
// Fetch reply
reply = await Notes.findOneBy({ id: ps.replyId });
if (reply == null) {
throw new ApiError(meta.errors.noSuchReplyTarget);
- } else if (reply.renoteId && !reply.text && !reply.fileIds && !renote.poll) {
+ } else if (reply.renoteId && !reply.text && !reply.fileIds && !reply.hasPoll) {
throw new ApiError(meta.errors.cannotReplyToPureRenote);
}
@@ -234,7 +238,7 @@ export default define(meta, paramDef, async (ps, user) => {
}
}
- let channel: Channel | undefined;
+ let channel: Channel | null = null;
if (ps.channelId != null) {
channel = await Channels.findOneBy({ id: ps.channelId });
diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts
index 3555424fa6..fbb065329c 100644
--- a/packages/backend/src/server/api/endpoints/notes/reactions.ts
+++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts
@@ -1,8 +1,8 @@
-import define from '../../define.js';
-import { ApiError } from '../../error.js';
+import { DeepPartial, FindOptionsWhere } from 'typeorm';
import { NoteReactions } from '@/models/index.js';
-import { DeepPartial } from 'typeorm';
import { NoteReaction } from '@/models/entities/note-reaction.js';
+import define from '../../define.js';
+import { ApiError } from '../../error.js';
export const meta = {
tags: ['notes', 'reactions'],
@@ -45,7 +45,7 @@ export const paramDef = {
export default define(meta, paramDef, async (ps, user) => {
const query = {
noteId: ps.noteId,
- } as DeepPartial<NoteReaction>;
+ } as FindOptionsWhere<NoteReaction>;
if (ps.type) {
// ローカルリアクションã¯ãƒ›ã‚¹ãƒˆå㌠. ã¨ã•れã¦ã„ã‚‹ãŒ
diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts
index c602981b30..5e40e7106f 100644
--- a/packages/backend/src/server/api/endpoints/notes/translate.ts
+++ b/packages/backend/src/server/api/endpoints/notes/translate.ts
@@ -1,12 +1,12 @@
-import define from '../../define.js';
-import { getNote } from '../../common/getters.js';
-import { ApiError } from '../../error.js';
+import { URLSearchParams } from 'node:url';
import fetch from 'node-fetch';
import config from '@/config/index.js';
import { getAgentByUrl } from '@/misc/fetch.js';
-import { URLSearchParams } from 'node:url';
import { fetchMeta } from '@/misc/fetch-meta.js';
import { Notes } from '@/models/index.js';
+import { ApiError } from '../../error.js';
+import { getNote } from '../../common/getters.js';
+import define from '../../define.js';
export const meta = {
tags: ['notes'],
@@ -80,7 +80,12 @@ export default define(meta, paramDef, async (ps, user) => {
agent: getAgentByUrl,
});
- const json = await res.json();
+ const json = (await res.json()) as {
+ translations: {
+ detected_source_language: string;
+ text: string;
+ }[];
+ };
return {
sourceLang: json.translations[0].detected_source_language,
diff --git a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts
index abefe07be6..4575cba43f 100644
--- a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts
+++ b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts
@@ -1,4 +1,5 @@
import { publishMainStream } from '@/services/stream.js';
+import { pushNotification } from '@/services/push-notification.js';
import define from '../../define.js';
import { Notifications } from '@/models/index.js';
@@ -28,4 +29,5 @@ export default define(meta, paramDef, async (ps, user) => {
// å…¨ã¦ã®é€šçŸ¥ã‚’読ã¿ã¾ã—ãŸã‚ˆã¨ã„ã†ã‚¤ãƒ™ãƒ³ãƒˆã‚’発行
publishMainStream(user.id, 'readAllNotifications');
+ pushNotification(user.id, 'readAllNotifications', undefined);
});
diff --git a/packages/backend/src/server/api/endpoints/notifications/read.ts b/packages/backend/src/server/api/endpoints/notifications/read.ts
index c7bc5dc0a5..65e96d4862 100644
--- a/packages/backend/src/server/api/endpoints/notifications/read.ts
+++ b/packages/backend/src/server/api/endpoints/notifications/read.ts
@@ -1,10 +1,12 @@
-import { publishMainStream } from '@/services/stream.js';
import define from '../../define.js';
-import { Notifications } from '@/models/index.js';
import { readNotification } from '../../common/read-notification.js';
-import { ApiError } from '../../error.js';
export const meta = {
+ desc: {
+ 'ja-JP': '通知を既読ã«ã—ã¾ã™ã€‚',
+ 'en-US': 'Mark a notification as read.'
+ },
+
tags: ['notifications', 'account'],
requireCredential: true,
@@ -21,23 +23,26 @@ export const meta = {
} as const;
export const paramDef = {
- type: 'object',
- properties: {
- notificationId: { type: 'string', format: 'misskey:id' },
- },
- required: ['notificationId'],
+ oneOf: [
+ {
+ type: 'object',
+ properties: {
+ notificationId: { type: 'string', format: 'misskey:id' },
+ },
+ required: ['notificationId'],
+ },
+ {
+ type: 'object',
+ properties: {
+ notificationIds: { type: 'array', items: { type: 'string', format: 'misskey:id' } },
+ },
+ required: ['notificationIds'],
+ },
+ ],
} as const;
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async (ps, user) => {
- const notification = await Notifications.findOneBy({
- notifieeId: user.id,
- id: ps.notificationId,
- });
-
- if (notification == null) {
- throw new ApiError(meta.errors.noSuchNotification);
- }
-
- readNotification(user.id, [notification.id]);
+ if ('notificationId' in ps) return readNotification(user.id, [ps.notificationId]);
+ return readNotification(user.id, ps.notificationIds);
});
diff --git a/packages/backend/src/server/api/endpoints/pages/show.ts b/packages/backend/src/server/api/endpoints/pages/show.ts
index 3dcce8550f..5d37e86b91 100644
--- a/packages/backend/src/server/api/endpoints/pages/show.ts
+++ b/packages/backend/src/server/api/endpoints/pages/show.ts
@@ -1,8 +1,8 @@
-import define from '../../define.js';
-import { ApiError } from '../../error.js';
+import { IsNull } from 'typeorm';
import { Pages, Users } from '@/models/index.js';
import { Page } from '@/models/entities/page.js';
-import { IsNull } from 'typeorm';
+import define from '../../define.js';
+import { ApiError } from '../../error.js';
export const meta = {
tags: ['pages'],
@@ -45,7 +45,7 @@ export const paramDef = {
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async (ps, user) => {
- let page: Page | undefined;
+ let page: Page | null = null;
if (ps.pageId) {
page = await Pages.findOneBy({ id: ps.pageId });
diff --git a/packages/backend/src/server/api/endpoints/request-reset-password.ts b/packages/backend/src/server/api/endpoints/request-reset-password.ts
index 046337f040..12ce7a9834 100644
--- a/packages/backend/src/server/api/endpoints/request-reset-password.ts
+++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts
@@ -10,8 +10,12 @@ import { genId } from '@/misc/gen-id.js';
import { IsNull } from 'typeorm';
export const meta = {
+ tags: ['reset password'],
+
requireCredential: false,
+ description: 'Request a users password to be reset.',
+
limit: {
duration: ms('1hour'),
max: 3,
diff --git a/packages/backend/src/server/api/endpoints/reset-db.ts b/packages/backend/src/server/api/endpoints/reset-db.ts
index dbe64e9a13..5ff115dab5 100644
--- a/packages/backend/src/server/api/endpoints/reset-db.ts
+++ b/packages/backend/src/server/api/endpoints/reset-db.ts
@@ -3,8 +3,12 @@ import { ApiError } from '../error.js';
import { resetDb } from '@/db/postgre.js';
export const meta = {
+ tags: ['non-productive'],
+
requireCredential: false,
+ description: 'Only available when running with <code>NODE_ENV=testing</code>. Reset the database and flush Redis.',
+
errors: {
},
diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts
index 7acc545c40..3dcb0b9b83 100644
--- a/packages/backend/src/server/api/endpoints/reset-password.ts
+++ b/packages/backend/src/server/api/endpoints/reset-password.ts
@@ -5,8 +5,12 @@ import { Users, UserProfiles, PasswordResetRequests } from '@/models/index.js';
import { ApiError } from '../error.js';
export const meta = {
+ tags: ['reset password'],
+
requireCredential: false,
+ description: 'Complete the password reset that was previously requested.',
+
errors: {
},
diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts
index a48973a0df..5bc3b9b6a1 100644
--- a/packages/backend/src/server/api/endpoints/sw/register.ts
+++ b/packages/backend/src/server/api/endpoints/sw/register.ts
@@ -8,6 +8,8 @@ export const meta = {
requireCredential: true,
+ description: 'Register to receive push notifications.',
+
res: {
type: 'object',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/sw/unregister.ts b/packages/backend/src/server/api/endpoints/sw/unregister.ts
index 9748f2a222..c21856d28f 100644
--- a/packages/backend/src/server/api/endpoints/sw/unregister.ts
+++ b/packages/backend/src/server/api/endpoints/sw/unregister.ts
@@ -5,6 +5,8 @@ export const meta = {
tags: ['account'],
requireCredential: true,
+
+ description: 'Unregister from receiving push notifications.',
} as const;
export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/test.ts b/packages/backend/src/server/api/endpoints/test.ts
index 256da1a66f..9949237a7e 100644
--- a/packages/backend/src/server/api/endpoints/test.ts
+++ b/packages/backend/src/server/api/endpoints/test.ts
@@ -1,6 +1,10 @@
import define from '../define.js';
export const meta = {
+ tags: ['non-productive'],
+
+ description: 'Endpoint for testing input validation.',
+
requireCredential: false,
} as const;
diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts
index 424c594749..37d4153950 100644
--- a/packages/backend/src/server/api/endpoints/users/clips.ts
+++ b/packages/backend/src/server/api/endpoints/users/clips.ts
@@ -4,6 +4,18 @@ import { makePaginationQuery } from '../../common/make-pagination-query.js';
export const meta = {
tags: ['users', 'clips'],
+
+ description: 'Show all clips this user owns.',
+
+ res: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'Clip',
+ },
+ },
} as const;
export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts
index 26b1f20df0..b1fb656208 100644
--- a/packages/backend/src/server/api/endpoints/users/followers.ts
+++ b/packages/backend/src/server/api/endpoints/users/followers.ts
@@ -10,6 +10,8 @@ export const meta = {
requireCredential: false,
+ description: 'Show everyone that follows this user.',
+
res: {
type: 'array',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts
index 42cf5216e8..429a5e80e5 100644
--- a/packages/backend/src/server/api/endpoints/users/following.ts
+++ b/packages/backend/src/server/api/endpoints/users/following.ts
@@ -10,6 +10,8 @@ export const meta = {
requireCredential: false,
+ description: 'Show everyone that this user is following.',
+
res: {
type: 'array',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts
index d7c435256c..35bf2df598 100644
--- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts
+++ b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts
@@ -4,6 +4,18 @@ import { makePaginationQuery } from '../../../common/make-pagination-query.js';
export const meta = {
tags: ['users', 'gallery'],
+
+ description: 'Show all gallery posts by the given user.',
+
+ res: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'GalleryPost',
+ },
+ },
} as const;
export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts
index 73cadc0df7..ab5837b3f3 100644
--- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts
+++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts
@@ -10,6 +10,8 @@ export const meta = {
requireCredential: false,
+ description: 'Get a list of other users that the specified user frequently replies to.',
+
res: {
type: 'array',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/users/groups/create.ts b/packages/backend/src/server/api/endpoints/users/groups/create.ts
index fc775d7cc1..fcaf4af3c3 100644
--- a/packages/backend/src/server/api/endpoints/users/groups/create.ts
+++ b/packages/backend/src/server/api/endpoints/users/groups/create.ts
@@ -11,6 +11,8 @@ export const meta = {
kind: 'write:user-groups',
+ description: 'Create a new group.',
+
res: {
type: 'object',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/users/groups/delete.ts b/packages/backend/src/server/api/endpoints/users/groups/delete.ts
index f68006994c..1bf253ae3f 100644
--- a/packages/backend/src/server/api/endpoints/users/groups/delete.ts
+++ b/packages/backend/src/server/api/endpoints/users/groups/delete.ts
@@ -9,6 +9,8 @@ export const meta = {
kind: 'write:user-groups',
+ description: 'Delete an existing group.',
+
errors: {
noSuchGroup: {
message: 'No such group.',
diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts
index 75c1acc302..eafd7f592c 100644
--- a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts
+++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts
@@ -11,6 +11,8 @@ export const meta = {
kind: 'write:user-groups',
+ description: 'Join a group the authenticated user has been invited to.',
+
errors: {
noSuchInvitation: {
message: 'No such invitation.',
diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts
index 46bc780ab0..08d3a3804b 100644
--- a/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts
+++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts
@@ -9,6 +9,8 @@ export const meta = {
kind: 'write:user-groups',
+ description: 'Delete an existing group invitation for the authenticated user without joining the group.',
+
errors: {
noSuchInvitation: {
message: 'No such invitation.',
diff --git a/packages/backend/src/server/api/endpoints/users/groups/invite.ts b/packages/backend/src/server/api/endpoints/users/groups/invite.ts
index 30a5beb1d9..cc82e43f21 100644
--- a/packages/backend/src/server/api/endpoints/users/groups/invite.ts
+++ b/packages/backend/src/server/api/endpoints/users/groups/invite.ts
@@ -13,6 +13,8 @@ export const meta = {
kind: 'write:user-groups',
+ description: 'Invite a user to an existing group.',
+
errors: {
noSuchGroup: {
message: 'No such group.',
diff --git a/packages/backend/src/server/api/endpoints/users/groups/joined.ts b/packages/backend/src/server/api/endpoints/users/groups/joined.ts
index 77dc59d3e5..6a2862ee5a 100644
--- a/packages/backend/src/server/api/endpoints/users/groups/joined.ts
+++ b/packages/backend/src/server/api/endpoints/users/groups/joined.ts
@@ -9,6 +9,8 @@ export const meta = {
kind: 'read:user-groups',
+ description: 'List the groups that the authenticated user is a member of.',
+
res: {
type: 'array',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/users/groups/leave.ts b/packages/backend/src/server/api/endpoints/users/groups/leave.ts
index 33abd5439f..2343cdf857 100644
--- a/packages/backend/src/server/api/endpoints/users/groups/leave.ts
+++ b/packages/backend/src/server/api/endpoints/users/groups/leave.ts
@@ -9,6 +9,8 @@ export const meta = {
kind: 'write:user-groups',
+ description: 'Leave a group. The owner of a group can not leave. They must transfer ownership or delete the group instead.',
+
errors: {
noSuchGroup: {
message: 'No such group.',
diff --git a/packages/backend/src/server/api/endpoints/users/groups/owned.ts b/packages/backend/src/server/api/endpoints/users/groups/owned.ts
index b1289e601f..de030193cc 100644
--- a/packages/backend/src/server/api/endpoints/users/groups/owned.ts
+++ b/packages/backend/src/server/api/endpoints/users/groups/owned.ts
@@ -8,6 +8,8 @@ export const meta = {
kind: 'read:user-groups',
+ description: 'List the groups that the authenticated user is the owner of.',
+
res: {
type: 'array',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/users/groups/pull.ts b/packages/backend/src/server/api/endpoints/users/groups/pull.ts
index b31990b2e3..703dad6d3b 100644
--- a/packages/backend/src/server/api/endpoints/users/groups/pull.ts
+++ b/packages/backend/src/server/api/endpoints/users/groups/pull.ts
@@ -10,6 +10,8 @@ export const meta = {
kind: 'write:user-groups',
+ description: 'Removes a specified user from a group. The owner can not be removed.',
+
errors: {
noSuchGroup: {
message: 'No such group.',
diff --git a/packages/backend/src/server/api/endpoints/users/groups/show.ts b/packages/backend/src/server/api/endpoints/users/groups/show.ts
index 3ffb0f5ba9..e1cee5fcf7 100644
--- a/packages/backend/src/server/api/endpoints/users/groups/show.ts
+++ b/packages/backend/src/server/api/endpoints/users/groups/show.ts
@@ -9,6 +9,8 @@ export const meta = {
kind: 'read:user-groups',
+ description: 'Show the properties of a group.',
+
res: {
type: 'object',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts
index 41ceee3b2e..1496e766ca 100644
--- a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts
+++ b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts
@@ -10,6 +10,8 @@ export const meta = {
kind: 'write:user-groups',
+ description: 'Transfer ownership of a group from the authenticated user to another user.',
+
res: {
type: 'object',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/users/groups/update.ts b/packages/backend/src/server/api/endpoints/users/groups/update.ts
index 1016aa8926..43cf3e484e 100644
--- a/packages/backend/src/server/api/endpoints/users/groups/update.ts
+++ b/packages/backend/src/server/api/endpoints/users/groups/update.ts
@@ -9,6 +9,8 @@ export const meta = {
kind: 'write:user-groups',
+ description: 'Update the properties of a group.',
+
res: {
type: 'object',
optional: false, nullable: false,
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 d5260256d5..d2941a0af5 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/create.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts
@@ -10,6 +10,8 @@ export const meta = {
kind: 'write:account',
+ description: 'Create a new list of users.',
+
res: {
type: 'object',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/users/lists/delete.ts b/packages/backend/src/server/api/endpoints/users/lists/delete.ts
index b7ad96eef0..8cd02ee02a 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/delete.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/delete.ts
@@ -9,6 +9,8 @@ export const meta = {
kind: 'write:account',
+ description: 'Delete an existing list of users.',
+
errors: {
noSuchList: {
message: 'No such list.',
diff --git a/packages/backend/src/server/api/endpoints/users/lists/list.ts b/packages/backend/src/server/api/endpoints/users/lists/list.ts
index 78311292cb..b337f879b1 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/list.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/list.ts
@@ -8,6 +8,8 @@ export const meta = {
kind: 'read:account',
+ description: 'Show all lists that the authenticated user has created.',
+
res: {
type: 'array',
optional: false, nullable: false,
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 76863f07d1..fa7033b02e 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts
@@ -11,6 +11,8 @@ export const meta = {
kind: 'write:account',
+ description: 'Remove a user from a list.',
+
errors: {
noSuchList: {
message: 'No such 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 260665c63a..1db10afc80 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/push.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts
@@ -11,6 +11,8 @@ export const meta = {
kind: 'write:account',
+ description: 'Add a user to an existing list.',
+
errors: {
noSuchList: {
message: 'No such list.',
diff --git a/packages/backend/src/server/api/endpoints/users/lists/show.ts b/packages/backend/src/server/api/endpoints/users/lists/show.ts
index 5f51980e95..94d24e1274 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/show.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/show.ts
@@ -9,6 +9,8 @@ export const meta = {
kind: 'read:account',
+ description: 'Show the properties of a list.',
+
res: {
type: 'object',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/users/lists/update.ts b/packages/backend/src/server/api/endpoints/users/lists/update.ts
index 52353a14cc..c21cdcf679 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/update.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/update.ts
@@ -9,6 +9,8 @@ export const meta = {
kind: 'write:account',
+ description: 'Update the properties of a list.',
+
res: {
type: 'object',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts
index 16318d2225..57dcdfaa88 100644
--- a/packages/backend/src/server/api/endpoints/users/notes.ts
+++ b/packages/backend/src/server/api/endpoints/users/notes.ts
@@ -12,6 +12,8 @@ import { generateMutedInstanceQuery } from '../../common/generate-muted-instance
export const meta = {
tags: ['users', 'notes'],
+ description: 'Show all notes that this user created.',
+
res: {
type: 'array',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/users/pages.ts b/packages/backend/src/server/api/endpoints/users/pages.ts
index b8b3e8192e..85d122c24f 100644
--- a/packages/backend/src/server/api/endpoints/users/pages.ts
+++ b/packages/backend/src/server/api/endpoints/users/pages.ts
@@ -4,6 +4,18 @@ import { makePaginationQuery } from '../../common/make-pagination-query.js';
export const meta = {
tags: ['users', 'pages'],
+
+ description: 'Show all pages this user created.',
+
+ res: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'Page',
+ },
+ },
} as const;
export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts
index c2d1994343..64994aae49 100644
--- a/packages/backend/src/server/api/endpoints/users/reactions.ts
+++ b/packages/backend/src/server/api/endpoints/users/reactions.ts
@@ -9,6 +9,8 @@ export const meta = {
requireCredential: false,
+ description: 'Show all reactions this user made.',
+
res: {
type: 'array',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts
index a8f18de522..6fff94ddcf 100644
--- a/packages/backend/src/server/api/endpoints/users/recommendation.ts
+++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts
@@ -11,6 +11,8 @@ export const meta = {
kind: 'read:account',
+ description: 'Show users that the authenticated user might be interested to follow.',
+
res: {
type: 'array',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts
index c6262122d4..87cab5fcf1 100644
--- a/packages/backend/src/server/api/endpoints/users/relation.ts
+++ b/packages/backend/src/server/api/endpoints/users/relation.ts
@@ -6,6 +6,8 @@ export const meta = {
requireCredential: true,
+ description: 'Show the different kinds of relations between the authenticated user and the specified user(s).',
+
res: {
optional: false, nullable: false,
oneOf: [
diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts
index 0be385dbbf..c7c7a3f591 100644
--- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts
+++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts
@@ -13,6 +13,8 @@ export const meta = {
requireCredential: true,
+ description: 'File a report.',
+
errors: {
noSuchUser: {
message: 'No such user.',
diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts
index f74d80e2ae..6cbf12b3b5 100644
--- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts
+++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts
@@ -9,6 +9,8 @@ export const meta = {
requireCredential: false,
+ description: 'Search for a user by username and/or host.',
+
res: {
type: 'array',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts
index a72a58a843..19c1a2c690 100644
--- a/packages/backend/src/server/api/endpoints/users/search.ts
+++ b/packages/backend/src/server/api/endpoints/users/search.ts
@@ -8,6 +8,8 @@ export const meta = {
requireCredential: false,
+ description: 'Search for users.',
+
res: {
type: 'array',
optional: false, nullable: false,
@@ -61,7 +63,14 @@ export default define(meta, paramDef, async (ps, me) => {
.getMany();
} else {
const nameQuery = Users.createQueryBuilder('user')
- .where('user.name ILIKE :query', { query: '%' + ps.query + '%' })
+ .where(new Brackets(qb => {
+ qb.where('user.name ILIKE :query', { query: '%' + ps.query + '%' });
+
+ // Also search username if it qualifies as username
+ if (Users.validateLocalUsername(ps.query)) {
+ qb.orWhere('user.usernameLower LIKE :username', { username: '%' + ps.query.toLowerCase() + '%' });
+ }
+ }))
.andWhere(new Brackets(qb => { qb
.where('user.updatedAt IS NULL')
.orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts
index 183ff1b8bb..b31ca30647 100644
--- a/packages/backend/src/server/api/endpoints/users/show.ts
+++ b/packages/backend/src/server/api/endpoints/users/show.ts
@@ -11,6 +11,8 @@ export const meta = {
requireCredential: false,
+ description: 'Show the properties of a user.',
+
res: {
optional: false, nullable: false,
oneOf: [
diff --git a/packages/backend/src/server/api/endpoints/users/stats.ts b/packages/backend/src/server/api/endpoints/users/stats.ts
index d138019a72..d17e8b64b5 100644
--- a/packages/backend/src/server/api/endpoints/users/stats.ts
+++ b/packages/backend/src/server/api/endpoints/users/stats.ts
@@ -1,12 +1,15 @@
import define from '../../define.js';
import { ApiError } from '../../error.js';
import { DriveFiles, Followings, NoteFavorites, NoteReactions, Notes, PageLikes, PollVotes, Users } from '@/models/index.js';
+import { awaitAll } from '@/prelude/await-all.js';
export const meta = {
tags: ['users'],
requireCredential: false,
+ description: 'Show statistics about a user.',
+
errors: {
noSuchUser: {
message: 'No such user.',
@@ -14,6 +17,94 @@ export const meta = {
id: '9e638e45-3b25-4ef7-8f95-07e8498f1819',
},
},
+
+ res: {
+ type: 'object',
+ optional: false, nullable: false,
+ properties: {
+ notesCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ repliesCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ renotesCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ repliedCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ renotedCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ pollVotesCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ pollVotedCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ localFollowingCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ remoteFollowingCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ localFollowersCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ remoteFollowersCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ followingCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ followersCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ sentReactionsCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ receivedReactionsCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ noteFavoritesCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ pageLikesCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ pageLikedCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ driveFilesCount: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ driveUsage: {
+ type: 'integer',
+ optional: false, nullable: false,
+ description: 'Drive usage in bytes',
+ },
+ },
+ },
} as const;
export const paramDef = {
@@ -31,109 +122,72 @@ export default define(meta, paramDef, async (ps, me) => {
throw new ApiError(meta.errors.noSuchUser);
}
- const [
- notesCount,
- repliesCount,
- renotesCount,
- repliedCount,
- renotedCount,
- pollVotesCount,
- pollVotedCount,
- localFollowingCount,
- remoteFollowingCount,
- localFollowersCount,
- remoteFollowersCount,
- sentReactionsCount,
- receivedReactionsCount,
- noteFavoritesCount,
- pageLikesCount,
- pageLikedCount,
- driveFilesCount,
- driveUsage,
- ] = await Promise.all([
- Notes.createQueryBuilder('note')
+ const result = await awaitAll({
+ notesCount: Notes.createQueryBuilder('note')
.where('note.userId = :userId', { userId: user.id })
.getCount(),
- Notes.createQueryBuilder('note')
+ repliesCount: Notes.createQueryBuilder('note')
.where('note.userId = :userId', { userId: user.id })
.andWhere('note.replyId IS NOT NULL')
.getCount(),
- Notes.createQueryBuilder('note')
+ renotesCount: Notes.createQueryBuilder('note')
.where('note.userId = :userId', { userId: user.id })
.andWhere('note.renoteId IS NOT NULL')
.getCount(),
- Notes.createQueryBuilder('note')
+ repliedCount: Notes.createQueryBuilder('note')
.where('note.replyUserId = :userId', { userId: user.id })
.getCount(),
- Notes.createQueryBuilder('note')
+ renotedCount: Notes.createQueryBuilder('note')
.where('note.renoteUserId = :userId', { userId: user.id })
.getCount(),
- PollVotes.createQueryBuilder('vote')
+ pollVotesCount: PollVotes.createQueryBuilder('vote')
.where('vote.userId = :userId', { userId: user.id })
.getCount(),
- PollVotes.createQueryBuilder('vote')
+ pollVotedCount: PollVotes.createQueryBuilder('vote')
.innerJoin('vote.note', 'note')
.where('note.userId = :userId', { userId: user.id })
.getCount(),
- Followings.createQueryBuilder('following')
+ localFollowingCount: Followings.createQueryBuilder('following')
.where('following.followerId = :userId', { userId: user.id })
.andWhere('following.followeeHost IS NULL')
.getCount(),
- Followings.createQueryBuilder('following')
+ remoteFollowingCount: Followings.createQueryBuilder('following')
.where('following.followerId = :userId', { userId: user.id })
.andWhere('following.followeeHost IS NOT NULL')
.getCount(),
- Followings.createQueryBuilder('following')
+ localFollowersCount: Followings.createQueryBuilder('following')
.where('following.followeeId = :userId', { userId: user.id })
.andWhere('following.followerHost IS NULL')
.getCount(),
- Followings.createQueryBuilder('following')
+ remoteFollowersCount: Followings.createQueryBuilder('following')
.where('following.followeeId = :userId', { userId: user.id })
.andWhere('following.followerHost IS NOT NULL')
.getCount(),
- NoteReactions.createQueryBuilder('reaction')
+ sentReactionsCount: NoteReactions.createQueryBuilder('reaction')
.where('reaction.userId = :userId', { userId: user.id })
.getCount(),
- NoteReactions.createQueryBuilder('reaction')
+ receivedReactionsCount: NoteReactions.createQueryBuilder('reaction')
.innerJoin('reaction.note', 'note')
.where('note.userId = :userId', { userId: user.id })
.getCount(),
- NoteFavorites.createQueryBuilder('favorite')
+ noteFavoritesCount: NoteFavorites.createQueryBuilder('favorite')
.where('favorite.userId = :userId', { userId: user.id })
.getCount(),
- PageLikes.createQueryBuilder('like')
+ pageLikesCount: PageLikes.createQueryBuilder('like')
.where('like.userId = :userId', { userId: user.id })
.getCount(),
- PageLikes.createQueryBuilder('like')
+ pageLikedCount: PageLikes.createQueryBuilder('like')
.innerJoin('like.page', 'page')
.where('page.userId = :userId', { userId: user.id })
.getCount(),
- DriveFiles.createQueryBuilder('file')
+ driveFilesCount: DriveFiles.createQueryBuilder('file')
.where('file.userId = :userId', { userId: user.id })
.getCount(),
- DriveFiles.calcDriveUsageOf(user),
- ]);
+ driveUsage: DriveFiles.calcDriveUsageOf(user),
+ });
+
+ result.followingCount = result.localFollowingCount + result.remoteFollowingCount;
+ result.followersCount = result.localFollowersCount + result.remoteFollowersCount;
- return {
- notesCount,
- repliesCount,
- renotesCount,
- repliedCount,
- renotedCount,
- pollVotesCount,
- pollVotedCount,
- localFollowingCount,
- remoteFollowingCount,
- localFollowersCount,
- remoteFollowersCount,
- followingCount: localFollowingCount + remoteFollowingCount,
- followersCount: localFollowersCount + remoteFollowersCount,
- sentReactionsCount,
- receivedReactionsCount,
- noteFavoritesCount,
- pageLikesCount,
- pageLikedCount,
- driveFilesCount,
- driveUsage,
- };
+ return result;
});
diff --git a/packages/backend/src/server/api/limiter.ts b/packages/backend/src/server/api/limiter.ts
index e74db8466e..23430cf8b6 100644
--- a/packages/backend/src/server/api/limiter.ts
+++ b/packages/backend/src/server/api/limiter.ts
@@ -1,25 +1,17 @@
import Limiter from 'ratelimiter';
import { redisClient } from '../../db/redis.js';
-import { IEndpoint } from './endpoints.js';
-import * as Acct from '@/misc/acct.js';
+import { IEndpointMeta } from './endpoints.js';
import { CacheableLocalUser, User } from '@/models/entities/user.js';
import Logger from '@/services/logger.js';
const logger = new Logger('limiter');
-export const limiter = (endpoint: IEndpoint & { meta: { limit: NonNullable<IEndpoint['meta']['limit']> } }, user: CacheableLocalUser) => new Promise<void>((ok, reject) => {
- const limitation = endpoint.meta.limit;
-
- const key = Object.prototype.hasOwnProperty.call(limitation, 'key')
- ? limitation.key
- : endpoint.name;
-
- const hasShortTermLimit =
- Object.prototype.hasOwnProperty.call(limitation, 'minInterval');
+export const limiter = (limitation: IEndpointMeta['limit'] & { key: NonNullable<string> }, actor: string) => new Promise<void>((ok, reject) => {
+ const hasShortTermLimit = typeof limitation.minInterval === 'number';
const hasLongTermLimit =
- Object.prototype.hasOwnProperty.call(limitation, 'duration') &&
- Object.prototype.hasOwnProperty.call(limitation, 'max');
+ typeof limitation.duration === 'number' &&
+ typeof limitation.max === 'number';
if (hasShortTermLimit) {
min();
@@ -32,7 +24,7 @@ export const limiter = (endpoint: IEndpoint & { meta: { limit: NonNullable<IEndp
// Short-term limit
function min(): void {
const minIntervalLimiter = new Limiter({
- id: `${user.id}:${key}:min`,
+ id: `${actor}:${limitation.key}:min`,
duration: limitation.minInterval,
max: 1,
db: redisClient,
@@ -43,7 +35,7 @@ export const limiter = (endpoint: IEndpoint & { meta: { limit: NonNullable<IEndp
return reject('ERR');
}
- logger.debug(`@${Acct.toString(user)} ${endpoint.name} min remaining: ${info.remaining}`);
+ logger.debug(`${actor} ${limitation.key} min remaining: ${info.remaining}`);
if (info.remaining === 0) {
reject('BRIEF_REQUEST_INTERVAL');
@@ -60,7 +52,7 @@ export const limiter = (endpoint: IEndpoint & { meta: { limit: NonNullable<IEndp
// Long term limit
function max(): void {
const limiter = new Limiter({
- id: `${user.id}:${key}`,
+ id: `${actor}:${limitation.key}`,
duration: limitation.duration,
max: limitation.max,
db: redisClient,
@@ -71,7 +63,7 @@ export const limiter = (endpoint: IEndpoint & { meta: { limit: NonNullable<IEndp
return reject('ERR');
}
- logger.debug(`@${Acct.toString(user)} ${endpoint.name} max remaining: ${info.remaining}`);
+ logger.debug(`${actor} ${limitation.key} max remaining: ${info.remaining}`);
if (info.remaining === 0) {
reject('RATE_LIMIT_EXCEEDED');
diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts
index c6e557aefb..3929fff3f7 100644
--- a/packages/backend/src/server/api/openapi/gen-spec.ts
+++ b/packages/backend/src/server/api/openapi/gen-spec.ts
@@ -59,6 +59,18 @@ export function genOpenapiSpec(lang = 'ja-JP') {
desc += ` / **Permission**: *${kind}*`;
}
+ const requestType = endpoint.meta.requireFile ? 'multipart/form-data' : 'application/json';
+ const schema = endpoint.params;
+
+ if (endpoint.meta.requireFile) {
+ schema.properties.file = {
+ type: 'string',
+ format: 'binary',
+ description: 'The file contents.',
+ };
+ schema.required.push('file');
+ }
+
const info = {
operationId: endpoint.name,
summary: endpoint.name,
@@ -78,8 +90,8 @@ export function genOpenapiSpec(lang = 'ja-JP') {
requestBody: {
required: true,
content: {
- 'application/json': {
- schema: endpoint.params,
+ [requestType]: {
+ schema,
},
},
},
diff --git a/packages/backend/src/server/api/private/signin.ts b/packages/backend/src/server/api/private/signin.ts
index 7b66657ad8..79b31764fd 100644
--- a/packages/backend/src/server/api/private/signin.ts
+++ b/packages/backend/src/server/api/private/signin.ts
@@ -9,6 +9,8 @@ import { genId } from '@/misc/gen-id.js';
import { verifyLogin, hash } from '../2fa.js';
import { randomBytes } from 'node:crypto';
import { IsNull } from 'typeorm';
+import { limiter } from '../limiter.js';
+import { getIpHash } from '@/misc/get-ip-hash.js';
export default async (ctx: Koa.Context) => {
ctx.set('Access-Control-Allow-Origin', config.url);
@@ -24,6 +26,21 @@ export default async (ctx: Koa.Context) => {
ctx.body = { error };
}
+ try {
+ // not more than 1 attempt per second and not more than 10 attempts per hour
+ await limiter({ key: 'signin', duration: 60 * 60 * 1000, max: 10, minInterval: 1000 }, getIpHash(ctx.ip));
+ } catch (err) {
+ ctx.status = 429;
+ ctx.body = {
+ error: {
+ message: 'Too many failed attempts to sign in. Try again later.',
+ code: 'TOO_MANY_AUTHENTICATION_FAILURES',
+ id: '22d05606-fbcf-421a-a2db-b32610dcfd1b',
+ },
+ };
+ return;
+ }
+
if (typeof username !== 'string') {
ctx.status = 400;
return;
diff --git a/packages/backend/src/server/api/service/discord.ts b/packages/backend/src/server/api/service/discord.ts
index 04197574c2..97cbcbecdb 100644
--- a/packages/backend/src/server/api/service/discord.ts
+++ b/packages/backend/src/server/api/service/discord.ts
@@ -1,16 +1,16 @@
import Koa from 'koa';
import Router from '@koa/router';
-import { getJson } from '@/misc/fetch.js';
import { OAuth2 } from 'oauth';
+import { v4 as uuid } from 'uuid';
+import { IsNull } from 'typeorm';
+import { getJson } from '@/misc/fetch.js';
import config from '@/config/index.js';
import { publishMainStream } from '@/services/stream.js';
-import { redisClient } from '../../../db/redis.js';
-import { v4 as uuid } from 'uuid';
-import signin from '../common/signin.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
import { Users, UserProfiles } from '@/models/index.js';
import { ILocalUser } from '@/models/entities/user.js';
-import { IsNull } from 'typeorm';
+import { redisClient } from '../../../db/redis.js';
+import signin from '../common/signin.js';
function getUserToken(ctx: Koa.BaseContext): string | null {
return ((ctx.headers['cookie'] || '').match(/igi=(\w+)/) || [null, null])[1];
@@ -54,7 +54,7 @@ router.get('/disconnect/discord', async ctx => {
integrations: profile.integrations,
});
- ctx.body = `Discordã®é€£æºã‚’解除ã—ã¾ã—㟠:v:`;
+ ctx.body = 'Discordã®é€£æºã‚’解除ã—ã¾ã—㟠:v:';
// Publish i updated event
publishMainStream(user.id, 'meUpdated', await Users.pack(user, user, {
@@ -140,7 +140,7 @@ router.get('/dc/cb', async ctx => {
const code = ctx.query.code;
- if (!code) {
+ if (!code || typeof code !== 'string') {
ctx.throw(400, 'invalid session');
return;
}
@@ -174,17 +174,17 @@ router.get('/dc/cb', async ctx => {
}
}));
- const { id, username, discriminator } = await getJson('https://discord.com/api/users/@me', '*/*', 10 * 1000, {
+ const { id, username, discriminator } = (await getJson('https://discord.com/api/users/@me', '*/*', 10 * 1000, {
'Authorization': `Bearer ${accessToken}`,
- });
+ })) as Record<string, unknown>;
- if (!id || !username || !discriminator) {
+ if (typeof id !== 'string' || typeof username !== 'string' || typeof discriminator !== 'string') {
ctx.throw(400, 'invalid session');
return;
}
const profile = await UserProfiles.createQueryBuilder()
- .where(`"integrations"->'discord'->>'id' = :id`, { id: id })
+ .where('"integrations"->\'discord\'->>\'id\' = :id', { id: id })
.andWhere('"userHost" IS NULL')
.getOne();
@@ -211,7 +211,7 @@ router.get('/dc/cb', async ctx => {
} else {
const code = ctx.query.code;
- if (!code) {
+ if (!code || typeof code !== 'string') {
ctx.throw(400, 'invalid session');
return;
}
@@ -245,10 +245,10 @@ router.get('/dc/cb', async ctx => {
}
}));
- const { id, username, discriminator } = await getJson('https://discord.com/api/users/@me', '*/*', 10 * 1000, {
+ const { id, username, discriminator } = (await getJson('https://discord.com/api/users/@me', '*/*', 10 * 1000, {
'Authorization': `Bearer ${accessToken}`,
- });
- if (!id || !username || !discriminator) {
+ })) as Record<string, unknown>;
+ if (typeof id !== 'string' || typeof username !== 'string' || typeof discriminator !== 'string') {
ctx.throw(400, 'invalid session');
return;
}
diff --git a/packages/backend/src/server/api/service/github.ts b/packages/backend/src/server/api/service/github.ts
index 61bb768a63..04dbd1f7ab 100644
--- a/packages/backend/src/server/api/service/github.ts
+++ b/packages/backend/src/server/api/service/github.ts
@@ -1,16 +1,16 @@
import Koa from 'koa';
import Router from '@koa/router';
-import { getJson } from '@/misc/fetch.js';
import { OAuth2 } from 'oauth';
+import { v4 as uuid } from 'uuid';
+import { IsNull } from 'typeorm';
+import { getJson } from '@/misc/fetch.js';
import config from '@/config/index.js';
import { publishMainStream } from '@/services/stream.js';
-import { redisClient } from '../../../db/redis.js';
-import { v4 as uuid } from 'uuid';
-import signin from '../common/signin.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
import { Users, UserProfiles } from '@/models/index.js';
import { ILocalUser } from '@/models/entities/user.js';
-import { IsNull } from 'typeorm';
+import { redisClient } from '../../../db/redis.js';
+import signin from '../common/signin.js';
function getUserToken(ctx: Koa.BaseContext): string | null {
return ((ctx.headers['cookie'] || '').match(/igi=(\w+)/) || [null, null])[1];
@@ -54,7 +54,7 @@ router.get('/disconnect/github', async ctx => {
integrations: profile.integrations,
});
- ctx.body = `GitHubã®é€£æºã‚’解除ã—ã¾ã—㟠:v:`;
+ ctx.body = 'GitHubã®é€£æºã‚’解除ã—ã¾ã—㟠:v:';
// Publish i updated event
publishMainStream(user.id, 'meUpdated', await Users.pack(user, user, {
@@ -138,7 +138,7 @@ router.get('/gh/cb', async ctx => {
const code = ctx.query.code;
- if (!code) {
+ if (!code || typeof code !== 'string') {
ctx.throw(400, 'invalid session');
return;
}
@@ -167,16 +167,16 @@ router.get('/gh/cb', async ctx => {
}
}));
- const { login, id } = await getJson('https://api.github.com/user', 'application/vnd.github.v3+json', 10 * 1000, {
+ const { login, id } = (await getJson('https://api.github.com/user', 'application/vnd.github.v3+json', 10 * 1000, {
'Authorization': `bearer ${accessToken}`,
- });
- if (!login || !id) {
+ })) as Record<string, unknown>;
+ if (typeof login !== 'string' || typeof id !== 'string') {
ctx.throw(400, 'invalid session');
return;
}
const link = await UserProfiles.createQueryBuilder()
- .where(`"integrations"->'github'->>'id' = :id`, { id: id })
+ .where('"integrations"->\'github\'->>\'id\' = :id', { id: id })
.andWhere('"userHost" IS NULL')
.getOne();
@@ -189,7 +189,7 @@ router.get('/gh/cb', async ctx => {
} else {
const code = ctx.query.code;
- if (!code) {
+ if (!code || typeof code !== 'string') {
ctx.throw(400, 'invalid session');
return;
}
@@ -219,11 +219,11 @@ router.get('/gh/cb', async ctx => {
}
}));
- const { login, id } = await getJson('https://api.github.com/user', 'application/vnd.github.v3+json', 10 * 1000, {
+ const { login, id } = (await getJson('https://api.github.com/user', 'application/vnd.github.v3+json', 10 * 1000, {
'Authorization': `bearer ${accessToken}`,
- });
+ })) as Record<string, unknown>;
- if (!login || !id) {
+ if (typeof login !== 'string' || typeof id !== 'string') {
ctx.throw(400, 'invalid session');
return;
}
diff --git a/packages/backend/src/server/api/service/twitter.ts b/packages/backend/src/server/api/service/twitter.ts
index e72b71e2f7..2b4f9f6daa 100644
--- a/packages/backend/src/server/api/service/twitter.ts
+++ b/packages/backend/src/server/api/service/twitter.ts
@@ -2,14 +2,14 @@ import Koa from 'koa';
import Router from '@koa/router';
import { v4 as uuid } from 'uuid';
import autwh from 'autwh';
-import { redisClient } from '../../../db/redis.js';
+import { IsNull } from 'typeorm';
import { publishMainStream } from '@/services/stream.js';
import config from '@/config/index.js';
-import signin from '../common/signin.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
import { Users, UserProfiles } from '@/models/index.js';
import { ILocalUser } from '@/models/entities/user.js';
-import { IsNull } from 'typeorm';
+import signin from '../common/signin.js';
+import { redisClient } from '../../../db/redis.js';
function getUserToken(ctx: Koa.BaseContext): string | null {
return ((ctx.headers['cookie'] || '').match(/igi=(\w+)/) || [null, null])[1];
@@ -53,7 +53,7 @@ router.get('/disconnect/twitter', async ctx => {
integrations: profile.integrations,
});
- ctx.body = `Twitterã®é€£æºã‚’解除ã—ã¾ã—㟠:v:`;
+ ctx.body = 'Twitterã®é€£æºã‚’解除ã—ã¾ã—㟠:v:';
// Publish i updated event
publishMainStream(user.id, 'meUpdated', await Users.pack(user, user, {
@@ -132,10 +132,16 @@ router.get('/tw/cb', async ctx => {
const twCtx = await get;
- const result = await twAuth!.done(JSON.parse(twCtx), ctx.query.oauth_verifier);
+ const verifier = ctx.query.oauth_verifier;
+ if (!verifier || typeof verifier !== 'string') {
+ ctx.throw(400, 'invalid session');
+ return;
+ }
+
+ const result = await twAuth!.done(JSON.parse(twCtx), verifier);
const link = await UserProfiles.createQueryBuilder()
- .where(`"integrations"->'twitter'->>'userId' = :id`, { id: result.userId })
+ .where('"integrations"->\'twitter\'->>\'userId\' = :id', { id: result.userId })
.andWhere('"userHost" IS NULL')
.getOne();
@@ -148,7 +154,7 @@ router.get('/tw/cb', async ctx => {
} else {
const verifier = ctx.query.oauth_verifier;
- if (verifier == null) {
+ if (!verifier || typeof verifier !== 'string') {
ctx.throw(400, 'invalid session');
return;
}
diff --git a/packages/backend/src/server/api/stream/channels/queue-stats.ts b/packages/backend/src/server/api/stream/channels/queue-stats.ts
index 043d03ab8d..b67600474b 100644
--- a/packages/backend/src/server/api/stream/channels/queue-stats.ts
+++ b/packages/backend/src/server/api/stream/channels/queue-stats.ts
@@ -1,7 +1,7 @@
-import { default as Xev } from 'xev';
+import Xev from 'xev';
import Channel from '../channel.js';
-const ev = new Xev.default();
+const ev = new Xev();
export default class extends Channel {
public readonly chName = 'queueStats';
diff --git a/packages/backend/src/server/api/stream/channels/server-stats.ts b/packages/backend/src/server/api/stream/channels/server-stats.ts
index 0da1895767..db75a6fa38 100644
--- a/packages/backend/src/server/api/stream/channels/server-stats.ts
+++ b/packages/backend/src/server/api/stream/channels/server-stats.ts
@@ -1,7 +1,7 @@
-import { default as Xev } from 'xev';
+import Xev from 'xev';
import Channel from '../channel.js';
-const ev = new Xev.default();
+const ev = new Xev();
export default class extends Channel {
public readonly chName = 'serverStats';
diff --git a/packages/backend/src/server/api/stream/index.ts b/packages/backend/src/server/api/stream/index.ts
index b803478281..2d23145f14 100644
--- a/packages/backend/src/server/api/stream/index.ts
+++ b/packages/backend/src/server/api/stream/index.ts
@@ -1,27 +1,25 @@
+import { EventEmitter } from 'events';
import * as websocket from 'websocket';
-import { readNotification } from '../common/read-notification.js';
-import call from '../call.js';
import readNote from '@/services/note/read.js';
-import Channel from './channel.js';
-import channels from './channels/index.js';
-import { EventEmitter } from 'events';
import { User } from '@/models/entities/user.js';
import { Channel as ChannelModel } from '@/models/entities/channel.js';
import { Users, Followings, Mutings, UserProfiles, ChannelFollowings, Blockings } from '@/models/index.js';
-import { ApiError } from '../error.js';
import { AccessToken } from '@/models/entities/access-token.js';
import { UserProfile } from '@/models/entities/user-profile.js';
import { publishChannelStream, publishGroupMessagingStream, publishMessagingStream } from '@/services/stream.js';
import { UserGroup } from '@/models/entities/user-group.js';
-import { StreamEventEmitter, StreamMessages } from './types.js';
import { Packed } from '@/misc/schema.js';
+import { readNotification } from '../common/read-notification.js';
+import channels from './channels/index.js';
+import Channel from './channel.js';
+import { StreamEventEmitter, StreamMessages } from './types.js';
/**
* Main stream connection
*/
export default class Connection {
public user?: User;
- public userProfile?: UserProfile;
+ public userProfile?: UserProfile | null;
public following: Set<User['id']> = new Set();
public muting: Set<User['id']> = new Set();
public blocking: Set<User['id']> = new Set(); // "被"blocking
@@ -84,7 +82,7 @@ export default class Connection {
this.muting.delete(data.body.id);
break;
- // TODO: block events
+ // TODO: block events
case 'followChannel':
this.followingChannels.add(data.body.id);
@@ -126,7 +124,6 @@ export default class Connection {
const { type, body } = obj;
switch (type) {
- case 'api': this.onApiRequest(body); break;
case 'readNotification': this.onReadNotification(body); break;
case 'subNote': this.onSubscribeNote(body); break;
case 's': this.onSubscribeNote(body); break; // alias
@@ -183,31 +180,6 @@ export default class Connection {
}
}
- /**
- * APIãƒªã‚¯ã‚¨ã‚¹ãƒˆè¦æ±‚時
- */
- private async onApiRequest(payload: any) {
- // æ–°é®®ãªãƒ‡ãƒ¼ã‚¿ã‚’利用ã™ã‚‹ãŸã‚ã«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’フェッãƒ
- const user = this.user ? await Users.findOneBy({ id: this.user.id }) : null;
-
- const endpoint = payload.endpoint || payload.ep; // alias
-
- // 呼ã³å‡ºã—
- call(endpoint, user, this.token, payload.data).then(res => {
- this.sendMessageToWs(`api:${payload.id}`, { res });
- }).catch((e: ApiError) => {
- this.sendMessageToWs(`api:${payload.id}`, {
- error: {
- message: e.message,
- code: e.code,
- id: e.id,
- kind: e.kind,
- ...(e.info ? { info: e.info } : {}),
- },
- });
- });
- }
-
private onReadNotification(payload: any) {
if (!payload.id) return;
readNotification(this.user!.id, [payload.id]);
diff --git a/packages/backend/src/server/api/streaming.ts b/packages/backend/src/server/api/streaming.ts
index 2a34edac67..f8e42d27fe 100644
--- a/packages/backend/src/server/api/streaming.ts
+++ b/packages/backend/src/server/api/streaming.ts
@@ -1,4 +1,4 @@
-import * as http from 'http';
+import * as http from 'node:http';
import * as websocket from 'websocket';
import MainStreamConnection from './stream/index.js';
diff --git a/packages/backend/src/server/file/send-drive-file.ts b/packages/backend/src/server/file/send-drive-file.ts
index 6bc220b362..c34e043145 100644
--- a/packages/backend/src/server/file/send-drive-file.ts
+++ b/packages/backend/src/server/file/send-drive-file.ts
@@ -4,14 +4,14 @@ import { dirname } from 'node:path';
import Koa from 'koa';
import send from 'koa-send';
import rename from 'rename';
-import * as tmp from 'tmp';
import { serverLogger } from '../index.js';
import { contentDisposition } from '@/misc/content-disposition.js';
import { DriveFiles } from '@/models/index.js';
import { InternalStorage } from '@/services/drive/internal-storage.js';
+import { createTemp } from '@/misc/create-temp.js';
import { downloadUrl } from '@/misc/download-url.js';
import { detectType } from '@/misc/get-file-info.js';
-import { convertToJpeg, convertToPng, convertToPngOrJpeg } from '@/services/drive/image-processor.js';
+import { convertToWebp, convertToJpeg, convertToPng } from '@/services/drive/image-processor.js';
import { GenerateVideoThumbnail } from '@/services/drive/generate-video-thumbnail.js';
import { StatusError } from '@/misc/fetch.js';
import { FILE_TYPE_BROWSERSAFE } from '@/const.js';
@@ -50,12 +50,7 @@ export default async function(ctx: Koa.Context) {
if (!file.storedInternal) {
if (file.isLink && file.uri) { // 期é™åˆ‡ã‚Œãƒªãƒ¢ãƒ¼ãƒˆãƒ•ァイル
- const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
- tmp.file((e, path, fd, cleanup) => {
- if (e) return rej(e);
- res([path, cleanup]);
- });
- });
+ const [path, cleanup] = await createTemp();
try {
await downloadUrl(file.uri, path);
@@ -64,10 +59,8 @@ export default async function(ctx: Koa.Context) {
const convertFile = async () => {
if (isThumbnail) {
- if (['image/jpeg', 'image/webp'].includes(mime)) {
- return await convertToJpeg(path, 498, 280);
- } else if (['image/png', 'image/svg+xml'].includes(mime)) {
- return await convertToPngOrJpeg(path, 498, 280);
+ if (['image/jpeg', 'image/webp', 'image/png', 'image/svg+xml'].includes(mime)) {
+ return await convertToWebp(path, 498, 280);
} else if (mime.startsWith('video/')) {
return await GenerateVideoThumbnail(path);
}
diff --git a/packages/backend/src/server/index.ts b/packages/backend/src/server/index.ts
index b50e38a63b..f31de2b7f4 100644
--- a/packages/backend/src/server/index.ts
+++ b/packages/backend/src/server/index.ts
@@ -2,8 +2,9 @@
* Core Server
*/
+import cluster from 'node:cluster';
import * as fs from 'node:fs';
-import * as http from 'http';
+import * as http from 'node:http';
import Koa from 'koa';
import Router from '@koa/router';
import mount from 'koa-mount';
@@ -88,10 +89,10 @@ router.get('/avatar/@:acct', async ctx => {
});
router.get('/identicon/:x', async ctx => {
- const [temp] = await createTemp();
+ const [temp, cleanup] = await createTemp();
await genIdenticon(ctx.params.x, fs.createWriteStream(temp));
ctx.set('Content-Type', 'image/png');
- ctx.body = fs.createReadStream(temp);
+ ctx.body = fs.createReadStream(temp).on('close', () => cleanup());
});
router.get('/verify-email/:code', async ctx => {
@@ -142,5 +143,26 @@ export default () => new Promise(resolve => {
initializeStreamingServer(server);
+ server.on('error', e => {
+ switch ((e as any).code) {
+ case 'EACCES':
+ serverLogger.error(`You do not have permission to listen on port ${config.port}.`);
+ break;
+ case 'EADDRINUSE':
+ serverLogger.error(`Port ${config.port} is already in use by another process.`);
+ break;
+ default:
+ serverLogger.error(e);
+ break;
+ }
+
+ if (cluster.isWorker) {
+ process.send!('listenFailed');
+ } else {
+ // disableClustering
+ process.exit(1);
+ }
+ });
+
server.listen(config.port, resolve);
});
diff --git a/packages/backend/src/server/proxy/proxy-media.ts b/packages/backend/src/server/proxy/proxy-media.ts
index 3cc5b827a6..48887bf12f 100644
--- a/packages/backend/src/server/proxy/proxy-media.ts
+++ b/packages/backend/src/server/proxy/proxy-media.ts
@@ -1,7 +1,7 @@
import * as fs from 'node:fs';
import Koa from 'koa';
import { serverLogger } from '../index.js';
-import { IImage, convertToPng, convertToJpeg } from '@/services/drive/image-processor.js';
+import { IImage, convertToWebp } from '@/services/drive/image-processor.js';
import { createTemp } from '@/misc/create-temp.js';
import { downloadUrl } from '@/misc/download-url.js';
import { detectType } from '@/misc/get-file-info.js';
@@ -27,11 +27,11 @@ export async function proxyMedia(ctx: Koa.Context) {
let image: IImage;
if ('static' in ctx.query && ['image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp', 'image/svg+xml'].includes(mime)) {
- image = await convertToPng(path, 498, 280);
+ image = await convertToWebp(path, 498, 280);
} else if ('preview' in ctx.query && ['image/jpeg', 'image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/svg+xml'].includes(mime)) {
- image = await convertToJpeg(path, 200, 200);
+ image = await convertToWebp(path, 200, 200);
} else if (['image/svg+xml'].includes(mime)) {
- image = await convertToPng(path, 2048, 2048);
+ image = await convertToWebp(path, 2048, 2048, 1);
} else if (!mime.startsWith('image/') || !FILE_TYPE_BROWSERSAFE.includes(mime)) {
throw new StatusError('Rejected type', 403, 'Rejected type');
} else {
diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js
index 751e8619bf..94329e11c9 100644
--- a/packages/backend/src/server/web/boot.js
+++ b/packages/backend/src/server/web/boot.js
@@ -54,19 +54,11 @@
//#endregion
//#region Script
- const salt = localStorage.getItem('salt')
- ? `?salt=${localStorage.getItem('salt')}`
- : '';
-
- const script = document.createElement('script');
- script.setAttribute('src', `/assets/app.${v}.js${salt}`);
- script.setAttribute('async', 'true');
- script.setAttribute('defer', 'true');
- script.addEventListener('error', async () => {
- await checkUpdate();
- renderError('APP_FETCH_FAILED');
- });
- document.head.appendChild(script);
+ import(`/assets/${CLIENT_ENTRY}`)
+ .catch(async e => {
+ await checkUpdate();
+ renderError('APP_FETCH_FAILED', JSON.stringify(e));
+ })
//#endregion
//#region Theme
@@ -146,9 +138,6 @@
// eslint-disable-next-line no-inner-declarations
function refresh() {
- // Random
- localStorage.setItem('salt', Math.random().toString().substr(2, 8));
-
// Clear cache (service worker)
try {
navigator.serviceWorker.controller.postMessage('clear');
diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts
index 34d56cfd0c..2feee72be7 100644
--- a/packages/backend/src/server/web/index.ts
+++ b/packages/backend/src/server/web/index.ts
@@ -4,6 +4,7 @@
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
+import { PathOrFileDescriptor, readFileSync } from 'node:fs';
import ms from 'ms';
import Koa from 'koa';
import Router from '@koa/router';
@@ -14,7 +15,7 @@ import { createBullBoard } from '@bull-board/api';
import { BullAdapter } from '@bull-board/api/bullAdapter.js';
import { KoaAdapter } from '@bull-board/koa';
-import { IsNull } from 'typeorm';
+import { In, IsNull } from 'typeorm';
import { fetchMeta } from '@/misc/fetch-meta.js';
import config from '@/config/index.js';
import { Users, Notes, UserProfiles, Pages, Channels, Clips, GalleryPosts } from '@/models/index.js';
@@ -32,6 +33,7 @@ const _dirname = dirname(_filename);
const staticAssets = `${_dirname}/../../../assets/`;
const clientAssets = `${_dirname}/../../../../client/assets/`;
const assets = `${_dirname}/../../../../../built/_client_dist_/`;
+const swAssets = `${_dirname}/../../../../../built/_sw_dist_/`;
// Init app
const app = new Koa();
@@ -72,6 +74,9 @@ app.use(views(_dirname + '/views', {
extension: 'pug',
options: {
version: config.version,
+ getClientEntry: () => process.env.NODE_ENV === 'production' ?
+ config.clientEntry :
+ JSON.parse(readFileSync(`${_dirname}/../../../../../built/_client_dist_/manifest.json`, 'utf-8'))['src/init.ts'],
config,
},
}));
@@ -136,9 +141,10 @@ router.get('/twemoji/(.*)', async ctx => {
});
// ServiceWorker
-router.get('/sw.js', async ctx => {
- await send(ctx as any, `/sw.${config.version}.js`, {
- root: assets,
+router.get(`/sw.js`, async ctx => {
+ await send(ctx as any, `/sw.js`, {
+ root: swAssets,
+ maxage: ms('10 minutes'),
});
});
@@ -241,7 +247,7 @@ router.get(['/@:user', '/@:user/:sub'], async (ctx, next) => {
icon: meta.iconUrl,
themeColor: meta.themeColor,
});
- ctx.set('Cache-Control', 'public, max-age=30');
+ ctx.set('Cache-Control', 'public, max-age=15');
} else {
// リモートユーザーãªã®ã§
// モデレータãŒAPI経由ã§å‚ç…§å¯èƒ½ã«ã™ã‚‹ãŸã‚ã«404ã«ã¯ã—ãªã„
@@ -266,7 +272,10 @@ router.get('/users/:user', async ctx => {
// Note
router.get('/notes/:note', async (ctx, next) => {
- const note = await Notes.findOneBy({ id: ctx.params.note });
+ const note = await Notes.findOneBy({
+ id: ctx.params.note,
+ visibility: In(['public', 'home']),
+ });
if (note) {
const _note = await Notes.pack(note);
@@ -283,11 +292,7 @@ router.get('/notes/:note', async (ctx, next) => {
themeColor: meta.themeColor,
});
- if (['public', 'home'].includes(note.visibility)) {
- ctx.set('Cache-Control', 'public, max-age=180');
- } else {
- ctx.set('Cache-Control', 'private, max-age=0, must-revalidate');
- }
+ ctx.set('Cache-Control', 'public, max-age=15');
return;
}
@@ -324,7 +329,7 @@ router.get('/@:user/pages/:page', async (ctx, next) => {
});
if (['public'].includes(page.visibility)) {
- ctx.set('Cache-Control', 'public, max-age=180');
+ ctx.set('Cache-Control', 'public, max-age=15');
} else {
ctx.set('Cache-Control', 'private, max-age=0, must-revalidate');
}
@@ -355,7 +360,7 @@ router.get('/clips/:clip', async (ctx, next) => {
themeColor: meta.themeColor,
});
- ctx.set('Cache-Control', 'public, max-age=180');
+ ctx.set('Cache-Control', 'public, max-age=15');
return;
}
@@ -380,7 +385,7 @@ router.get('/gallery/:post', async (ctx, next) => {
themeColor: meta.themeColor,
});
- ctx.set('Cache-Control', 'public, max-age=180');
+ ctx.set('Cache-Control', 'public, max-age=15');
return;
}
@@ -404,7 +409,7 @@ router.get('/channels/:channel', async (ctx, next) => {
themeColor: meta.themeColor,
});
- ctx.set('Cache-Control', 'public, max-age=180');
+ ctx.set('Cache-Control', 'public, max-age=15');
return;
}
@@ -463,7 +468,7 @@ router.get('(.*)', async ctx => {
icon: meta.iconUrl,
themeColor: meta.themeColor,
});
- ctx.set('Cache-Control', 'public, max-age=300');
+ ctx.set('Cache-Control', 'public, max-age=15');
});
// Register router
diff --git a/packages/backend/src/server/web/manifest.ts b/packages/backend/src/server/web/manifest.ts
index bcbf9b76a7..ee568b8077 100644
--- a/packages/backend/src/server/web/manifest.ts
+++ b/packages/backend/src/server/web/manifest.ts
@@ -1,16 +1,18 @@
import Koa from 'koa';
-import manifest from './manifest.json' assert { type: 'json' };
import { fetchMeta } from '@/misc/fetch-meta.js';
+import manifest from './manifest.json' assert { type: 'json' };
export const manifestHandler = async (ctx: Koa.Context) => {
- const json = JSON.parse(JSON.stringify(manifest));
+ // TODO
+ //const res = structuredClone(manifest);
+ const res = JSON.parse(JSON.stringify(manifest));
const instance = await fetchMeta(true);
- json.short_name = instance.name || 'Misskey';
- json.name = instance.name || 'Misskey';
- if (instance.themeColor) json.theme_color = instance.themeColor;
+ res.short_name = instance.name || 'Misskey';
+ res.name = instance.name || 'Misskey';
+ if (instance.themeColor) res.theme_color = instance.themeColor;
ctx.set('Cache-Control', 'max-age=300');
- ctx.body = json;
+ ctx.body = res;
};
diff --git a/packages/backend/src/server/web/style.css b/packages/backend/src/server/web/style.css
index 9c4cd4b9bf..d59f00fe16 100644
--- a/packages/backend/src/server/web/style.css
+++ b/packages/backend/src/server/web/style.css
@@ -39,28 +39,24 @@ html {
width: 28px;
height: 28px;
transform: translateY(70px);
+ color: var(--accent);
}
-
-#splashSpinner:before,
-#splashSpinner:after {
- content: " ";
- display: block;
- box-sizing: border-box;
+#splashSpinner > .spinner {
+ position: absolute;
+ top: 0;
+ left: 0;
width: 28px;
height: 28px;
- border-radius: 50%;
- border: solid 4px;
+ fill-rule: evenodd;
+ clip-rule: evenodd;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-miterlimit: 1.5;
}
-
-#splashSpinner:before {
- border-color: currentColor;
- opacity: 0.3;
+#splashSpinner > .spinner.bg {
+ opacity: 0.275;
}
-
-#splashSpinner:after {
- position: absolute;
- top: 0;
- border-color: currentColor transparent transparent transparent;
+#splashSpinner > .spinner.fg {
animation: splashSpinner 0.5s linear infinite;
}
diff --git a/packages/backend/src/server/web/url-preview.ts b/packages/backend/src/server/web/url-preview.ts
index 6bd8ead5b5..1e259649f9 100644
--- a/packages/backend/src/server/web/url-preview.ts
+++ b/packages/backend/src/server/web/url-preview.ts
@@ -56,7 +56,7 @@ export const urlPreviewHandler = async (ctx: Koa.Context) => {
function wrap(url?: string): string | null {
return url != null
? url.match(/^https?:\/\//)
- ? `${config.url}/proxy/preview.jpg?${query({
+ ? `${config.url}/proxy/preview.webp?${query({
url,
preview: '1',
})}`
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index 1513208310..5bb156f0f4 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -1,17 +1,21 @@
block vars
+block loadClientEntry
+ - const clientEntry = getClientEntry();
+
doctype html
-!= '<!--\n'
-!= ' _____ _ _ \n'
-!= ' | |_|___ ___| |_ ___ _ _ \n'
-!= ' | | | | |_ -|_ -| \'_| -_| | |\n'
-!= ' |_|_|_|_|___|___|_,_|___|_ |\n'
-!= ' |___|\n'
-!= ' Thank you for using Misskey!\n'
-!= ' If you are reading this message... how about joining the development?\n'
-!= ' https://github.com/misskey-dev/misskey'
-!= '\n-->\n'
+//
+ -
+ _____ _ _
+ | |_|___ ___| |_ ___ _ _
+ | | | | |_ -|_ -| '_| -_| | |
+ |_|_|_|_|___|___|_,_|___|_ |
+ |___|
+ Thank you for using Misskey!
+ If you are reading this message... how about joining the development?
+ https://github.com/misskey-dev/misskey
+
html
@@ -30,8 +34,14 @@ html
link(rel='prefetch' href='https://xn--931a.moe/assets/info.jpg')
link(rel='prefetch' href='https://xn--931a.moe/assets/not-found.jpg')
link(rel='prefetch' href='https://xn--931a.moe/assets/error.jpg')
- link(rel='preload' href='/assets/fontawesome/css/all.css' as='style')
link(rel='stylesheet' href='/assets/fontawesome/css/all.css')
+ link(rel='modulepreload' href=`/assets/${clientEntry.file}`)
+
+ each href in clientEntry.css
+ link(rel='preload' href=`/assets/${href}` as='style')
+
+ each href in clientEntry.css
+ link(rel='preload' href=`/assets/${href}` as='style')
title
block title
@@ -50,6 +60,10 @@ html
style
include ../style.css
+ script.
+ var VERSION = "#{version}";
+ var CLIENT_ENTRY = "#{clientEntry.file}";
+
script
include ../boot.js
@@ -61,4 +75,14 @@ html
div#splash
img#splashIcon(src= icon || '/static-assets/splash.png')
div#splashSpinner
+ <svg class="spinner bg" viewBox="0 0 152 152" xmlns="http://www.w3.org/2000/svg">
+ <g transform="matrix(1,0,0,1,12,12)">
+ <circle cx="64" cy="64" r="64" style="fill:none;stroke:currentColor;stroke-width:24px;"/>
+ </g>
+ </svg>
+ <svg class="spinner fg" viewBox="0 0 152 152" xmlns="http://www.w3.org/2000/svg">
+ <g transform="matrix(1,0,0,1,12,12)">
+ <path d="M128,64C128,28.654 99.346,0 64,0C99.346,0 128,28.654 128,64Z" style="fill:none;stroke:currentColor;stroke-width:24px;"/>
+ </g>
+ </svg>
block content
diff --git a/packages/backend/src/server/well-known.ts b/packages/backend/src/server/well-known.ts
index 7530b4e0ba..1d094f2edd 100644
--- a/packages/backend/src/server/well-known.ts
+++ b/packages/backend/src/server/well-known.ts
@@ -41,6 +41,7 @@ router.options(allPath, async ctx => {
router.get('/.well-known/host-meta', async ctx => {
ctx.set('Content-Type', xrd);
ctx.body = XRD({ element: 'Link', attributes: {
+ rel: 'lrdd',
type: xrd,
template: `${config.url}${webFingerPath}?resource={uri}`,
} });
diff --git a/packages/backend/src/services/blocking/create.ts b/packages/backend/src/services/blocking/create.ts
index 5e96e5037f..a2c61cca22 100644
--- a/packages/backend/src/services/blocking/create.ts
+++ b/packages/backend/src/services/blocking/create.ts
@@ -2,9 +2,10 @@ import { publishMainStream, publishUserEvent } from '@/services/stream.js';
import { renderActivity } from '@/remote/activitypub/renderer/index.js';
import renderFollow from '@/remote/activitypub/renderer/follow.js';
import renderUndo from '@/remote/activitypub/renderer/undo.js';
-import renderBlock from '@/remote/activitypub/renderer/block.js';
+import { renderBlock } from '@/remote/activitypub/renderer/block.js';
import { deliver } from '@/queue/index.js';
import renderReject from '@/remote/activitypub/renderer/reject.js';
+import { Blocking } from '@/models/entities/blocking.js';
import { User } from '@/models/entities/user.js';
import { Blockings, Users, FollowRequests, Followings, UserListJoinings, UserLists } from '@/models/index.js';
import { perUserFollowingChart } from '@/services/chart/index.js';
@@ -22,15 +23,19 @@ export default async function(blocker: User, blockee: User) {
removeFromList(blockee, blocker),
]);
- await Blockings.insert({
+ const blocking = {
id: genId(),
createdAt: new Date(),
+ blocker,
blockerId: blocker.id,
+ blockee,
blockeeId: blockee.id,
- });
+ } as Blocking;
+
+ await Blockings.insert(blocking);
if (Users.isLocalUser(blocker) && Users.isRemoteUser(blockee)) {
- const content = renderActivity(renderBlock(blocker, blockee));
+ const content = renderActivity(renderBlock(blocking));
deliver(blocker, content, blockee.inbox);
}
}
@@ -95,17 +100,12 @@ async function unFollow(follower: User, followee: User) {
return;
}
- Followings.delete(following.id);
-
- //#region Decrement following count
- Users.decrement({ id: follower.id }, 'followingCount', 1);
- //#endregion
-
- //#region Decrement followers count
- Users.decrement({ id: followee.id }, 'followersCount', 1);
- //#endregion
-
- perUserFollowingChart.update(follower, followee, false);
+ await Promise.all([
+ Followings.delete(following.id),
+ Users.decrement({ id: follower.id }, 'followingCount', 1),
+ Users.decrement({ id: followee.id }, 'followersCount', 1),
+ perUserFollowingChart.update(follower, followee, false),
+ ]);
// Publish unfollow event
if (Users.isLocalUser(follower)) {
diff --git a/packages/backend/src/services/blocking/delete.ts b/packages/backend/src/services/blocking/delete.ts
index d7b5ddd5ff..cb16651bc0 100644
--- a/packages/backend/src/services/blocking/delete.ts
+++ b/packages/backend/src/services/blocking/delete.ts
@@ -1,5 +1,5 @@
import { renderActivity } from '@/remote/activitypub/renderer/index.js';
-import renderBlock from '@/remote/activitypub/renderer/block.js';
+import { renderBlock } from '@/remote/activitypub/renderer/block.js';
import renderUndo from '@/remote/activitypub/renderer/undo.js';
import { deliver } from '@/queue/index.js';
import Logger from '../logger.js';
@@ -19,11 +19,16 @@ export default async function(blocker: CacheableUser, blockee: CacheableUser) {
return;
}
+ // Since we already have the blocker and blockee, we do not need to fetch
+ // them in the query above and can just manually insert them here.
+ blocking.blocker = blocker;
+ blocking.blockee = blockee;
+
Blockings.delete(blocking.id);
// deliver if remote bloking
if (Users.isLocalUser(blocker) && Users.isRemoteUser(blockee)) {
- const content = renderActivity(renderUndo(renderBlock(blocker, blockee), blocker));
+ const content = renderActivity(renderUndo(renderBlock(blocking), blocker));
deliver(blocker, content, blockee.inbox);
}
}
diff --git a/packages/backend/src/services/chart/core.ts b/packages/backend/src/services/chart/core.ts
index cf69e2194d..2960bac8f7 100644
--- a/packages/backend/src/services/chart/core.ts
+++ b/packages/backend/src/services/chart/core.ts
@@ -91,27 +91,20 @@ type ToJsonSchema<S> = {
};
export function getJsonSchema<S extends Schema>(schema: S): ToJsonSchema<Unflatten<ChartResult<S>>> {
- const object = {};
- for (const [k, v] of Object.entries(schema)) {
- nestedProperty.set(object, k, null);
- }
+ const jsonSchema = {
+ type: 'object',
+ properties: {} as Record<string, unknown>,
+ required: [],
+ };
- function f(obj: Record<string, null | Record<string, unknown>>) {
- const jsonSchema = {
- type: 'object',
- properties: {} as Record<string, unknown>,
- required: [],
+ for (const k in schema) {
+ jsonSchema.properties[k] = {
+ type: 'array',
+ items: { type: 'number' },
};
- for (const [k, v] of Object.entries(obj)) {
- jsonSchema.properties[k] = v === null ? {
- type: 'array',
- items: { type: 'number' },
- } : f(v as Record<string, null | Record<string, unknown>>);
- }
- return jsonSchema;
}
- return f(object) as ToJsonSchema<Unflatten<ChartResult<S>>>;
+ return jsonSchema as ToJsonSchema<Unflatten<ChartResult<S>>>;
}
/**
diff --git a/packages/backend/src/services/chart/entities.ts b/packages/backend/src/services/chart/entities.ts
index 13e994cb65..a9eeabd639 100644
--- a/packages/backend/src/services/chart/entities.ts
+++ b/packages/backend/src/services/chart/entities.ts
@@ -11,6 +11,11 @@ import { entity as PerUserFollowingChart } from './charts/entities/per-user-foll
import { entity as PerUserDriveChart } from './charts/entities/per-user-drive.js';
import { entity as ApRequestChart } from './charts/entities/ap-request.js';
+import { entity as TestChart } from './charts/entities/test.js';
+import { entity as TestGroupedChart } from './charts/entities/test-grouped.js';
+import { entity as TestUniqueChart } from './charts/entities/test-unique.js';
+import { entity as TestIntersectionChart } from './charts/entities/test-intersection.js';
+
export const entities = [
FederationChart.hour, FederationChart.day,
NotesChart.hour, NotesChart.day,
@@ -24,4 +29,11 @@ export const entities = [
PerUserFollowingChart.hour, PerUserFollowingChart.day,
PerUserDriveChart.hour, PerUserDriveChart.day,
ApRequestChart.hour, ApRequestChart.day,
+
+ ...(process.env.NODE_ENV === 'test' ? [
+ TestChart.hour, TestChart.day,
+ TestGroupedChart.hour, TestGroupedChart.day,
+ TestUniqueChart.hour, TestUniqueChart.day,
+ TestIntersectionChart.hour, TestIntersectionChart.day,
+ ] : []),
];
diff --git a/packages/backend/src/services/create-notification.ts b/packages/backend/src/services/create-notification.ts
index 9a53db1f38..d53a4235b8 100644
--- a/packages/backend/src/services/create-notification.ts
+++ b/packages/backend/src/services/create-notification.ts
@@ -1,5 +1,5 @@
import { publishMainStream } from '@/services/stream.js';
-import pushSw from './push-notification.js';
+import { pushNotification } from '@/services/push-notification.js';
import { Notifications, Mutings, UserProfiles, Users } from '@/models/index.js';
import { genId } from '@/misc/gen-id.js';
import { User } from '@/models/entities/user.js';
@@ -52,8 +52,8 @@ export async function createNotification(
//#endregion
publishMainStream(notifieeId, 'unreadNotification', packed);
+ pushNotification(notifieeId, 'notification', packed);
- pushSw(notifieeId, 'notification', packed);
if (type === 'follow') sendEmailNotification.follow(notifieeId, await Users.findOneByOrFail({ id: data.notifierId! }));
if (type === 'receiveFollowRequest') sendEmailNotification.receiveFollowRequest(notifieeId, await Users.findOneByOrFail({ id: data.notifierId! }));
}, 2000);
diff --git a/packages/backend/src/services/drive/add-file.ts b/packages/backend/src/services/drive/add-file.ts
index 549b11c9fe..cfbcb60ddf 100644
--- a/packages/backend/src/services/drive/add-file.ts
+++ b/packages/backend/src/services/drive/add-file.ts
@@ -7,7 +7,7 @@ import { deleteFile } from './delete-file.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
import { GenerateVideoThumbnail } from './generate-video-thumbnail.js';
import { driveLogger } from './logger.js';
-import { IImage, convertSharpToJpeg, convertSharpToWebp, convertSharpToPng, convertSharpToPngOrJpeg } from './image-processor.js';
+import { IImage, convertSharpToJpeg, convertSharpToWebp, convertSharpToPng } from './image-processor.js';
import { contentDisposition } from '@/misc/content-disposition.js';
import { getFileInfo } from '@/misc/get-file-info.js';
import { DriveFiles, DriveFolders, Users, Instances, UserProfiles } from '@/models/index.js';
@@ -179,6 +179,7 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
}
let img: sharp.Sharp | null = null;
+ let satisfyWebpublic: boolean;
try {
img = sharp(path);
@@ -192,6 +193,13 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
thumbnail: null,
};
}
+
+ satisfyWebpublic = !!(
+ type !== 'image/svg+xml' && type !== 'image/webp' &&
+ !(metadata.exif || metadata.iptc || metadata.xmp || metadata.tifftagPhotoshop) &&
+ metadata.width && metadata.width <= 2048 &&
+ metadata.height && metadata.height <= 2048
+ );
} catch (err) {
logger.warn(`sharp failed: ${err}`);
return {
@@ -203,15 +211,15 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
// #region webpublic
let webpublic: IImage | null = null;
- if (generateWeb) {
+ if (generateWeb && !satisfyWebpublic) {
logger.info(`creating web image`);
try {
- if (['image/jpeg'].includes(type)) {
+ if (['image/jpeg', 'image/webp'].includes(type)) {
webpublic = await convertSharpToJpeg(img, 2048, 2048);
- } else if (['image/webp'].includes(type)) {
- webpublic = await convertSharpToWebp(img, 2048, 2048);
- } else if (['image/png', 'image/svg+xml'].includes(type)) {
+ } else if (['image/png'].includes(type)) {
+ webpublic = await convertSharpToPng(img, 2048, 2048);
+ } else if (['image/svg+xml'].includes(type)) {
webpublic = await convertSharpToPng(img, 2048, 2048);
} else {
logger.debug(`web image not created (not an required image)`);
@@ -220,7 +228,8 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
logger.warn(`web image not created (an error occured)`, err as Error);
}
} else {
- logger.info(`web image not created (from remote)`);
+ if (satisfyWebpublic) logger.info(`web image not created (original satisfies webpublic)`);
+ else logger.info(`web image not created (from remote)`);
}
// #endregion webpublic
@@ -228,10 +237,8 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
let thumbnail: IImage | null = null;
try {
- if (['image/jpeg', 'image/webp'].includes(type)) {
- thumbnail = await convertSharpToJpeg(img, 498, 280);
- } else if (['image/png', 'image/svg+xml'].includes(type)) {
- thumbnail = await convertSharpToPngOrJpeg(img, 498, 280);
+ if (['image/jpeg', 'image/webp', 'image/png', 'image/svg+xml'].includes(type)) {
+ thumbnail = await convertSharpToWebp(img, 498, 280);
} else {
logger.debug(`thumbnail not created (not an required file)`);
}
diff --git a/packages/backend/src/services/drive/generate-video-thumbnail.ts b/packages/backend/src/services/drive/generate-video-thumbnail.ts
index 04a7a83346..ca12ab8d3d 100644
--- a/packages/backend/src/services/drive/generate-video-thumbnail.ts
+++ b/packages/backend/src/services/drive/generate-video-thumbnail.ts
@@ -1,37 +1,31 @@
import * as fs from 'node:fs';
-import * as tmp from 'tmp';
+import * as path from 'node:path';
+import { createTemp } from '@/misc/create-temp.js';
import { IImage, convertToJpeg } from './image-processor.js';
-import * as FFmpeg from 'fluent-ffmpeg';
+import FFmpeg from 'fluent-ffmpeg';
-export async function GenerateVideoThumbnail(path: string): Promise<IImage> {
- const [outDir, cleanup] = await new Promise<[string, any]>((res, rej) => {
- tmp.dir((e, path, cleanup) => {
- if (e) return rej(e);
- res([path, cleanup]);
- });
- });
+export async function GenerateVideoThumbnail(source: string): Promise<IImage> {
+ const [file, cleanup] = await createTemp();
+ const parsed = path.parse(file);
- await new Promise((res, rej) => {
- FFmpeg({
- source: path,
- })
- .on('end', res)
- .on('error', rej)
- .screenshot({
- folder: outDir,
- filename: 'output.png',
- count: 1,
- timestamps: ['5%'],
+ try {
+ await new Promise((res, rej) => {
+ FFmpeg({
+ source,
+ })
+ .on('end', res)
+ .on('error', rej)
+ .screenshot({
+ folder: parsed.dir,
+ filename: parsed.base,
+ count: 1,
+ timestamps: ['5%'],
+ });
});
- });
-
- const outPath = `${outDir}/output.png`;
-
- const thumbnail = await convertToJpeg(outPath, 498, 280);
-
- // cleanup
- await fs.promises.unlink(outPath);
- cleanup();
- return thumbnail;
+ // JPEGã«å¤‰æ› (Webpã§ã‚‚ã„ã„ãŒã€Mastodonã¯Webpをサãƒãƒ¼ãƒˆã›ãšè¡¨ç¤ºã§ããªããªã‚‹)
+ return await convertToJpeg(498, 280);
+ } finally {
+ cleanup();
+ }
}
diff --git a/packages/backend/src/services/drive/image-processor.ts b/packages/backend/src/services/drive/image-processor.ts
index 146dcfb6ca..2c564ea595 100644
--- a/packages/backend/src/services/drive/image-processor.ts
+++ b/packages/backend/src/services/drive/image-processor.ts
@@ -38,11 +38,11 @@ export async function convertSharpToJpeg(sharp: sharp.Sharp, width: number, heig
* Convert to WebP
* with resize, remove metadata, resolve orientation, stop animation
*/
-export async function convertToWebp(path: string, width: number, height: number): Promise<IImage> {
- return convertSharpToWebp(await sharp(path), width, height);
+export async function convertToWebp(path: string, width: number, height: number, quality: number = 85): Promise<IImage> {
+ return convertSharpToWebp(await sharp(path), width, height, quality);
}
-export async function convertSharpToWebp(sharp: sharp.Sharp, width: number, height: number): Promise<IImage> {
+export async function convertSharpToWebp(sharp: sharp.Sharp, width: number, height: number, quality: number = 85): Promise<IImage> {
const data = await sharp
.resize(width, height, {
fit: 'inside',
@@ -50,7 +50,7 @@ export async function convertSharpToWebp(sharp: sharp.Sharp, width: number, heig
})
.rotate()
.webp({
- quality: 85,
+ quality,
})
.toBuffer();
@@ -85,23 +85,3 @@ export async function convertSharpToPng(sharp: sharp.Sharp, width: number, heigh
type: 'image/png',
};
}
-
-/**
- * Convert to PNG or JPEG
- * with resize, remove metadata, resolve orientation, stop animation
- */
-export async function convertToPngOrJpeg(path: string, width: number, height: number): Promise<IImage> {
- return convertSharpToPngOrJpeg(await sharp(path), width, height);
-}
-
-export async function convertSharpToPngOrJpeg(sharp: sharp.Sharp, width: number, height: number): Promise<IImage> {
- const stats = await sharp.stats();
- const metadata = await sharp.metadata();
-
- // ä¸é€æ˜Žã§300x300pxã®ç¯„囲を超ãˆã¦ã„れã°JPEG
- if (stats.isOpaque && ((metadata.width && metadata.width >= 300) || (metadata.height && metadata!.height >= 300))) {
- return await convertSharpToJpeg(sharp, width, height);
- } else {
- return await convertSharpToPng(sharp, width, height);
- }
-}
diff --git a/packages/backend/src/services/drive/upload-from-url.ts b/packages/backend/src/services/drive/upload-from-url.ts
index 79b1b8c2e1..001fc49ee4 100644
--- a/packages/backend/src/services/drive/upload-from-url.ts
+++ b/packages/backend/src/services/drive/upload-from-url.ts
@@ -45,29 +45,20 @@ export async function uploadFromUrl({
// Create temp file
const [path, cleanup] = await createTemp();
- // write content at URL to temp file
- await downloadUrl(url, path);
-
- let driveFile: DriveFile;
- let error;
-
try {
- driveFile = await addFile({ user, path, name, comment, folderId, force, isLink, url, uri, sensitive });
+ // write content at URL to temp file
+ await downloadUrl(url, path);
+
+ const driveFile = await addFile({ user, path, name, comment, folderId, force, isLink, url, uri, sensitive });
logger.succ(`Got: ${driveFile.id}`);
+ return driveFile!;
} catch (e) {
- error = e;
logger.error(`Failed to create drive file: ${e}`, {
url: url,
e: e,
});
- }
-
- // clean-up
- cleanup();
-
- if (error) {
- throw error;
- } else {
- return driveFile!;
+ throw e;
+ } finally {
+ cleanup();
}
}
diff --git a/packages/backend/src/services/fetch-instance-metadata.ts b/packages/backend/src/services/fetch-instance-metadata.ts
index d5294c5fe8..029c388dc2 100644
--- a/packages/backend/src/services/fetch-instance-metadata.ts
+++ b/packages/backend/src/services/fetch-instance-metadata.ts
@@ -1,5 +1,6 @@
import { DOMWindow, JSDOM } from 'jsdom';
import fetch from 'node-fetch';
+import tinycolor from 'tinycolor2';
import { getJson, getHtml, getAgentByUrl } from '@/misc/fetch.js';
import { Instance } from '@/models/entities/instance.js';
import { Instances } from '@/models/index.js';
@@ -208,16 +209,11 @@ async function fetchIconUrl(instance: Instance, doc: DOMWindow['document'] | nul
}
async function getThemeColor(doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> {
- if (doc) {
- const themeColor = doc.querySelector('meta[name="theme-color"]')?.getAttribute('content');
+ const themeColor = doc?.querySelector('meta[name="theme-color"]')?.getAttribute('content') || manifest?.theme_color;
- if (themeColor) {
- return themeColor;
- }
- }
-
- if (manifest) {
- return manifest.theme_color;
+ if (themeColor) {
+ const color = new tinycolor(themeColor);
+ if (color.isValid()) return color.toHexString();
}
return null;
diff --git a/packages/backend/src/services/following/create.ts b/packages/backend/src/services/following/create.ts
index 7491c44f83..72c24676bb 100644
--- a/packages/backend/src/services/following/create.ts
+++ b/packages/backend/src/services/following/create.ts
@@ -67,8 +67,10 @@ export async function insertFollowingDoc(followee: { id: User['id']; host: User[
if (alreadyFollowed) return;
//#region Increment counts
- Users.increment({ id: follower.id }, 'followingCount', 1);
- Users.increment({ id: followee.id }, 'followersCount', 1);
+ await Promise.all([
+ Users.increment({ id: follower.id }, 'followingCount', 1),
+ Users.increment({ id: followee.id }, 'followersCount', 1),
+ ]);
//#endregion
//#region Update instance stats
diff --git a/packages/backend/src/services/following/delete.ts b/packages/backend/src/services/following/delete.ts
index 241f9606e5..91b5a3d61d 100644
--- a/packages/backend/src/services/following/delete.ts
+++ b/packages/backend/src/services/following/delete.ts
@@ -58,12 +58,11 @@ export default async function(follower: { id: User['id']; host: User['host']; ur
}
export async function decrementFollowing(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }) {
- //#region Decrement following count
- Users.decrement({ id: follower.id }, 'followingCount', 1);
- //#endregion
-
- //#region Decrement followers count
- Users.decrement({ id: followee.id }, 'followersCount', 1);
+ //#region Decrement following / followers counts
+ await Promise.all([
+ Users.decrement({ id: follower.id }, 'followingCount', 1),
+ Users.decrement({ id: followee.id }, 'followersCount', 1),
+ ]);
//#endregion
//#region Update instance stats
diff --git a/packages/backend/src/services/messages/create.ts b/packages/backend/src/services/messages/create.ts
index e5cd5a30d2..e6b3204922 100644
--- a/packages/backend/src/services/messages/create.ts
+++ b/packages/backend/src/services/messages/create.ts
@@ -5,7 +5,7 @@ import { MessagingMessages, UserGroupJoinings, Mutings, Users } from '@/models/i
import { genId } from '@/misc/gen-id.js';
import { MessagingMessage } from '@/models/entities/messaging-message.js';
import { publishMessagingStream, publishMessagingIndexStream, publishMainStream, publishGroupMessagingStream } from '@/services/stream.js';
-import pushNotification from '../push-notification.js';
+import { pushNotification } from '@/services/push-notification.js';
import { Not } from 'typeorm';
import { Note } from '@/models/entities/note.js';
import renderNote from '@/remote/activitypub/renderer/note.js';
diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts
index f14bc2059b..e2bf9d5b59 100644
--- a/packages/backend/src/services/note/create.ts
+++ b/packages/backend/src/services/note/create.ts
@@ -187,6 +187,8 @@ export default async (user: { id: User['id']; username: User['username']; host:
if (data.text) {
data.text = data.text.trim();
+ } else {
+ data.text = null;
}
let tags = data.apHashtags;
@@ -310,7 +312,8 @@ export default async (user: { id: User['id']; username: User['username']; host:
endedPollNotificationQueue.add({
noteId: note.id,
}, {
- delay
+ delay,
+ removeOnComplete: true,
});
}
diff --git a/packages/backend/src/services/note/delete.ts b/packages/backend/src/services/note/delete.ts
index ffd609dd84..4963200161 100644
--- a/packages/backend/src/services/note/delete.ts
+++ b/packages/backend/src/services/note/delete.ts
@@ -1,3 +1,4 @@
+import { Brackets, In } from 'typeorm';
import { publishNoteStream } from '@/services/stream.js';
import renderDelete from '@/remote/activitypub/renderer/delete.js';
import renderAnnounce from '@/remote/activitypub/renderer/announce.js';
@@ -5,15 +6,14 @@ import renderUndo from '@/remote/activitypub/renderer/undo.js';
import { renderActivity } from '@/remote/activitypub/renderer/index.js';
import renderTombstone from '@/remote/activitypub/renderer/tombstone.js';
import config from '@/config/index.js';
-import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js';
import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js';
import { Note, IMentionedRemoteUsers } from '@/models/entities/note.js';
import { Notes, Users, Instances } from '@/models/index.js';
import { notesChart, perUserNotesChart, instanceChart } from '@/services/chart/index.js';
import { deliverToFollowers, deliverToUser } from '@/remote/activitypub/deliver-manager.js';
import { countSameRenotes } from '@/misc/count-same-renotes.js';
+import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js';
import { deliverToRelays } from '../relay.js';
-import { Brackets, In } from 'typeorm';
/**
* 投稿を削除ã—ã¾ã™ã€‚
@@ -40,7 +40,7 @@ export default async function(user: { id: User['id']; uri: User['uri']; host: Us
//#region ãƒ­ãƒ¼ã‚«ãƒ«ã®æŠ•ç¨¿ãªã‚‰å‰Šé™¤ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティをé…é€
if (Users.isLocalUser(user) && !note.localOnly) {
- let renote: Note | null;
+ let renote: Note | null = null;
// if deletd note is renote
if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length === 0)) {
@@ -113,7 +113,7 @@ async function getMentionedRemoteUsers(note: Note) {
const uris = (JSON.parse(note.mentionedRemoteUsers) as IMentionedRemoteUsers).map(x => x.uri);
if (uris.length > 0) {
where.push(
- { uri: In(uris) }
+ { uri: In(uris) },
);
}
diff --git a/packages/backend/src/services/note/reaction/create.ts b/packages/backend/src/services/note/reaction/create.ts
index 5a0948bca9..83d302826a 100644
--- a/packages/backend/src/services/note/reaction/create.ts
+++ b/packages/backend/src/services/note/reaction/create.ts
@@ -27,6 +27,11 @@ export default async (user: { id: User['id']; host: User['host']; }, note: Note,
}
}
+ // check visibility
+ if (!await Notes.isVisibleForMe(note, user.id)) {
+ throw new IdentifiableError('68e9d2d1-48bf-42c2-b90a-b20e09fd3d48', 'Note not accessible for you.');
+ }
+
// TODO: cache
reaction = await toDbReaction(reaction, user.host);
diff --git a/packages/backend/src/services/push-notification.ts b/packages/backend/src/services/push-notification.ts
index 41122c92e8..5c3bafbb34 100644
--- a/packages/backend/src/services/push-notification.ts
+++ b/packages/backend/src/services/push-notification.ts
@@ -5,8 +5,15 @@ import { fetchMeta } from '@/misc/fetch-meta.js';
import { Packed } from '@/misc/schema.js';
import { getNoteSummary } from '@/misc/get-note-summary.js';
-type notificationType = 'notification' | 'unreadMessagingMessage';
-type notificationBody = Packed<'Notification'> | Packed<'MessagingMessage'>;
+// Defined also packages/sw/types.ts#L14-L21
+type pushNotificationsTypes = {
+ 'notification': Packed<'Notification'>;
+ 'unreadMessagingMessage': Packed<'MessagingMessage'>;
+ 'readNotifications': { notificationIds: string[] };
+ 'readAllNotifications': undefined;
+ 'readAllMessagingMessages': undefined;
+ 'readAllMessagingMessagesOfARoom': { userId: string } | { groupId: string };
+};
// プッシュメッセージサーãƒãƒ¼ã«ã¯æ–‡å­—数制é™ãŒã‚ã‚‹ãŸã‚ã€å†…容を削減ã—ã¾ã™
function truncateNotification(notification: Packed<'Notification'>): any {
@@ -17,12 +24,11 @@ function truncateNotification(notification: Packed<'Notification'>): any {
...notification.note,
// textã‚’getNoteSummaryã—ãŸã‚‚ã®ã«ç½®ãæ›ãˆã‚‹
text: getNoteSummary(notification.type === 'renote' ? notification.note.renote as Packed<'Note'> : notification.note),
- ...{
- cw: undefined,
- reply: undefined,
- renote: undefined,
- user: undefined as any, // 通知をå—ã‘å–ã£ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã§ã‚ã‚‹å ´åˆãŒå¤šã„ã®ã§ã“れもæ¨ã¦ã‚‹
- }
+
+ cw: undefined,
+ reply: undefined,
+ renote: undefined,
+ user: undefined as any, // 通知をå—ã‘å–ã£ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã§ã‚ã‚‹å ´åˆãŒå¤šã„ã®ã§ã“れもæ¨ã¦ã‚‹
}
};
}
@@ -30,7 +36,7 @@ function truncateNotification(notification: Packed<'Notification'>): any {
return notification;
}
-export default async function(userId: string, type: notificationType, body: notificationBody) {
+export async function pushNotification<T extends keyof pushNotificationsTypes>(userId: string, type: T, body: pushNotificationsTypes[T]) {
const meta = await fetchMeta();
if (!meta.enableServiceWorker || meta.swPublicKey == null || meta.swPrivateKey == null) return;
diff --git a/packages/backend/src/services/relay.ts b/packages/backend/src/services/relay.ts
index 1ab45588da..6bc4304436 100644
--- a/packages/backend/src/services/relay.ts
+++ b/packages/backend/src/services/relay.ts
@@ -1,4 +1,4 @@
-import { createSystemUser } from './create-system-user.js';
+import { IsNull } from 'typeorm';
import { renderFollowRelay } from '@/remote/activitypub/renderer/follow-relay.js';
import { renderActivity, attachLdSignature } from '@/remote/activitypub/renderer/index.js';
import renderUndo from '@/remote/activitypub/renderer/undo.js';
@@ -8,7 +8,7 @@ import { Users, Relays } from '@/models/index.js';
import { genId } from '@/misc/gen-id.js';
import { Cache } from '@/misc/cache.js';
import { Relay } from '@/models/entities/relay.js';
-import { IsNull } from 'typeorm';
+import { createSystemUser } from './create-system-user.js';
const ACTOR_USERNAME = 'relay.actor' as const;
@@ -88,6 +88,8 @@ export async function deliverToRelays(user: { id: User['id']; host: null; }, act
}));
if (relays.length === 0) return;
+ // TODO
+ //const copy = structuredClone(activity);
const copy = JSON.parse(JSON.stringify(activity));
if (!copy.to) copy.to = ['https://www.w3.org/ns/activitystreams#Public'];
diff --git a/packages/backend/test/.eslintrc b/packages/backend/test/.eslintrc
deleted file mode 100644
index cea1b11388..0000000000
--- a/packages/backend/test/.eslintrc
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "env": {
- "node": true,
- "mocha": true,
- "commonjs": true
- }
-}
diff --git a/packages/backend/test/.eslintrc.cjs b/packages/backend/test/.eslintrc.cjs
new file mode 100644
index 0000000000..d83dc37d2f
--- /dev/null
+++ b/packages/backend/test/.eslintrc.cjs
@@ -0,0 +1,11 @@
+module.exports = {
+ parserOptions: {
+ tsconfigRootDir: __dirname,
+ project: ['./tsconfig.json'],
+ },
+ extends: ['../.eslintrc.cjs'],
+ env: {
+ node: true,
+ mocha: true,
+ },
+};
diff --git a/packages/backend/test/activitypub.ts b/packages/backend/test/activitypub.ts
index 70f35cafd8..f4ae27e5ec 100644
--- a/packages/backend/test/activitypub.ts
+++ b/packages/backend/test/activitypub.ts
@@ -1,12 +1,14 @@
process.env.NODE_ENV = 'test';
-import rndstr from 'rndstr';
import * as assert from 'assert';
+import rndstr from 'rndstr';
+import { initDb } from '../src/db/postgre.js';
import { initTestDb } from './utils.js';
describe('ActivityPub', () => {
before(async () => {
- await initTestDb();
+ //await initTestDb();
+ await initDb();
});
describe('Parse minimum object', () => {
@@ -57,8 +59,8 @@ describe('ActivityPub', () => {
const note = await createNote(post.id, resolver, true);
assert.deepStrictEqual(note?.uri, post.id);
- assert.deepStrictEqual(note?.visibility, 'public');
- assert.deepStrictEqual(note?.text, post.content);
+ assert.deepStrictEqual(note.visibility, 'public');
+ assert.deepStrictEqual(note.text, post.content);
});
});
diff --git a/packages/backend/test/ap-request.ts b/packages/backend/test/ap-request.ts
index 48f4fceb51..da95c421f3 100644
--- a/packages/backend/test/ap-request.ts
+++ b/packages/backend/test/ap-request.ts
@@ -1,7 +1,7 @@
import * as assert from 'assert';
+import httpSignature from 'http-signature';
import { genRsaKeyPair } from '../src/misc/gen-key-pair.js';
import { createSignedPost, createSignedGet } from '../src/remote/activitypub/ap-request.js';
-import httpSignature from 'http-signature';
export const buildParsedSignature = (signingString: string, signature: string, algorithm: string) => {
return {
@@ -13,7 +13,7 @@ export const buildParsedSignature = (signingString: string, signature: string, a
signature: signature,
},
signingString: signingString,
- algorithm: algorithm?.toUpperCase(),
+ algorithm: algorithm.toUpperCase(),
keyId: 'KeyID', // dummy, not used for verify
};
};
@@ -26,7 +26,7 @@ describe('ap-request', () => {
const activity = { a: 1 };
const body = JSON.stringify(activity);
const headers = {
- 'User-Agent': 'UA'
+ 'User-Agent': 'UA',
};
const req = createSignedPost({ key, url, body, additionalHeaders: headers });
@@ -42,7 +42,7 @@ describe('ap-request', () => {
const key = { keyId: 'x', 'privateKeyPem': keypair.privateKey };
const url = 'https://example.com/outbox';
const headers = {
- 'User-Agent': 'UA'
+ 'User-Agent': 'UA',
};
const req = createSignedGet({ key, url, additionalHeaders: headers });
diff --git a/packages/backend/test/api-visibility.ts b/packages/backend/test/api-visibility.ts
index d946191be8..b155549f98 100644
--- a/packages/backend/test/api-visibility.ts
+++ b/packages/backend/test/api-visibility.ts
@@ -61,40 +61,40 @@ describe('API visibility', () => {
const show = async (noteId: any, by: any) => {
return await request('/notes/show', {
- noteId
+ noteId,
}, by);
};
before(async () => {
//#region prepare
// signup
- alice = await signup({ username: 'alice' });
+ alice = await signup({ username: 'alice' });
follower = await signup({ username: 'follower' });
- other = await signup({ username: 'other' });
- target = await signup({ username: 'target' });
- target2 = await signup({ username: 'target2' });
+ other = await signup({ username: 'other' });
+ target = await signup({ username: 'target' });
+ target2 = await signup({ username: 'target2' });
// follow alice <= follower
await request('/following/create', { userId: alice.id }, follower);
// normal posts
- pub = await post(alice, { text: 'x', visibility: 'public' });
+ pub = await post(alice, { text: 'x', visibility: 'public' });
home = await post(alice, { text: 'x', visibility: 'home' });
- fol = await post(alice, { text: 'x', visibility: 'followers' });
- spe = await post(alice, { text: 'x', visibility: 'specified', visibleUserIds: [target.id] });
+ fol = await post(alice, { text: 'x', visibility: 'followers' });
+ spe = await post(alice, { text: 'x', visibility: 'specified', visibleUserIds: [target.id] });
// replies
tgt = await post(target, { text: 'y', visibility: 'public' });
- pubR = await post(alice, { text: 'x', replyId: tgt.id, visibility: 'public' });
+ pubR = await post(alice, { text: 'x', replyId: tgt.id, visibility: 'public' });
homeR = await post(alice, { text: 'x', replyId: tgt.id, visibility: 'home' });
- folR = await post(alice, { text: 'x', replyId: tgt.id, visibility: 'followers' });
- speR = await post(alice, { text: 'x', replyId: tgt.id, visibility: 'specified' });
+ folR = await post(alice, { text: 'x', replyId: tgt.id, visibility: 'followers' });
+ speR = await post(alice, { text: 'x', replyId: tgt.id, visibility: 'specified' });
// mentions
- pubM = await post(alice, { text: '@target x', replyId: tgt.id, visibility: 'public' });
+ pubM = await post(alice, { text: '@target x', replyId: tgt.id, visibility: 'public' });
homeM = await post(alice, { text: '@target x', replyId: tgt.id, visibility: 'home' });
- folM = await post(alice, { text: '@target x', replyId: tgt.id, visibility: 'followers' });
- speM = await post(alice, { text: '@target2 x', replyId: tgt.id, visibility: 'specified' });
+ folM = await post(alice, { text: '@target x', replyId: tgt.id, visibility: 'followers' });
+ speM = await post(alice, { text: '@target2 x', replyId: tgt.id, visibility: 'specified' });
//#endregion
});
diff --git a/packages/backend/test/block.ts b/packages/backend/test/block.ts
index 103eec991d..b3343813cd 100644
--- a/packages/backend/test/block.ts
+++ b/packages/backend/test/block.ts
@@ -25,7 +25,7 @@ describe('Block', () => {
it('Block作æˆ', async(async () => {
const res = await request('/blocking/create', {
- userId: bob.id
+ userId: bob.id,
}, alice);
assert.strictEqual(res.status, 200);
diff --git a/packages/backend/test/chart.ts b/packages/backend/test/chart.ts
index c8cea874f0..ac0844679f 100644
--- a/packages/backend/test/chart.ts
+++ b/packages/backend/test/chart.ts
@@ -2,30 +2,21 @@ process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as lolex from '@sinonjs/fake-timers';
-import { async, initTestDb } from './utils.js';
import TestChart from '../src/services/chart/charts/test.js';
import TestGroupedChart from '../src/services/chart/charts/test-grouped.js';
import TestUniqueChart from '../src/services/chart/charts/test-unique.js';
import TestIntersectionChart from '../src/services/chart/charts/test-intersection.js';
-import * as _TestChart from '../src/services/chart/charts/entities/test.js';
-import * as _TestGroupedChart from '../src/services/chart/charts/entities/test-grouped.js';
-import * as _TestUniqueChart from '../src/services/chart/charts/entities/test-unique.js';
-import * as _TestIntersectionChart from '../src/services/chart/charts/entities/test-intersection.js';
+import { initDb } from '../src/db/postgre.js';
describe('Chart', () => {
let testChart: TestChart;
let testGroupedChart: TestGroupedChart;
let testUniqueChart: TestUniqueChart;
let testIntersectionChart: TestIntersectionChart;
- let clock: lolex.Clock;
+ let clock: lolex.InstalledClock;
- beforeEach(async(async () => {
- await initTestDb(false, [
- _TestChart.entity.hour, _TestChart.entity.day,
- _TestGroupedChart.entity.hour, _TestGroupedChart.entity.day,
- _TestUniqueChart.entity.hour, _TestUniqueChart.entity.day,
- _TestIntersectionChart.entity.hour, _TestIntersectionChart.entity.day,
- ]);
+ beforeEach(async () => {
+ await initDb(true);
testChart = new TestChart();
testGroupedChart = new TestGroupedChart();
@@ -33,15 +24,16 @@ describe('Chart', () => {
testIntersectionChart = new TestIntersectionChart();
clock = lolex.install({
- now: new Date(Date.UTC(2000, 0, 1, 0, 0, 0))
+ now: new Date(Date.UTC(2000, 0, 1, 0, 0, 0)),
+ shouldClearNativeTimers: true,
});
- }));
+ });
- afterEach(async(async () => {
+ afterEach(() => {
clock.uninstall();
- }));
+ });
- it('Can updates', async(async () => {
+ it('Can updates', async () => {
await testChart.increment();
await testChart.save();
@@ -52,7 +44,7 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
- total: [1, 0, 0]
+ total: [1, 0, 0],
},
});
@@ -60,12 +52,12 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
- total: [1, 0, 0]
+ total: [1, 0, 0],
},
});
- }));
+ });
- it('Can updates (dec)', async(async () => {
+ it('Can updates (dec)', async () => {
await testChart.decrement();
await testChart.save();
@@ -76,7 +68,7 @@ describe('Chart', () => {
foo: {
dec: [1, 0, 0],
inc: [0, 0, 0],
- total: [-1, 0, 0]
+ total: [-1, 0, 0],
},
});
@@ -84,12 +76,12 @@ describe('Chart', () => {
foo: {
dec: [1, 0, 0],
inc: [0, 0, 0],
- total: [-1, 0, 0]
+ total: [-1, 0, 0],
},
});
- }));
+ });
- it('Empty chart', async(async () => {
+ it('Empty chart', async () => {
const chartHours = await testChart.getChart('hour', 3, null);
const chartDays = await testChart.getChart('day', 3, null);
@@ -97,7 +89,7 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [0, 0, 0],
- total: [0, 0, 0]
+ total: [0, 0, 0],
},
});
@@ -105,12 +97,12 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [0, 0, 0],
- total: [0, 0, 0]
+ total: [0, 0, 0],
},
});
- }));
+ });
- it('Can updates at multiple times at same time', async(async () => {
+ it('Can updates at multiple times at same time', async () => {
await testChart.increment();
await testChart.increment();
await testChart.increment();
@@ -123,7 +115,7 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [3, 0, 0],
- total: [3, 0, 0]
+ total: [3, 0, 0],
},
});
@@ -131,12 +123,12 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [3, 0, 0],
- total: [3, 0, 0]
+ total: [3, 0, 0],
},
});
- }));
+ });
- it('複数回saveã•れã¦ã‚‚ãƒ‡ãƒ¼ã‚¿ã®æ›´æ–°ã¯ä¸€åº¦ã ã‘', async(async () => {
+ it('複数回saveã•れã¦ã‚‚ãƒ‡ãƒ¼ã‚¿ã®æ›´æ–°ã¯ä¸€åº¦ã ã‘', async () => {
await testChart.increment();
await testChart.save();
await testChart.save();
@@ -149,7 +141,7 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
- total: [1, 0, 0]
+ total: [1, 0, 0],
},
});
@@ -157,12 +149,12 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
- total: [1, 0, 0]
+ total: [1, 0, 0],
},
});
- }));
+ });
- it('Can updates at different times', async(async () => {
+ it('Can updates at different times', async () => {
await testChart.increment();
await testChart.save();
@@ -178,7 +170,7 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [1, 1, 0],
- total: [2, 1, 0]
+ total: [2, 1, 0],
},
});
@@ -186,14 +178,14 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [2, 0, 0],
- total: [2, 0, 0]
+ total: [2, 0, 0],
},
});
- }));
+ });
// 仕様上ã¯ã“ã†ãªã£ã¦ã»ã—ã„ã‘ã©ã€å®Ÿè£…ã¯é›£ã—ãã†ãªã®ã§skip
/*
- it('Can updates at different times without save', async(async () => {
+ it('Can updates at different times without save', async () => {
await testChart.increment();
clock.tick('01:00:00');
@@ -219,10 +211,10 @@ describe('Chart', () => {
total: [2, 0, 0]
},
});
- }));
+ });
*/
- it('Can padding', async(async () => {
+ it('Can padding', async () => {
await testChart.increment();
await testChart.save();
@@ -238,7 +230,7 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 1],
- total: [2, 1, 1]
+ total: [2, 1, 1],
},
});
@@ -246,13 +238,13 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [2, 0, 0],
- total: [2, 0, 0]
+ total: [2, 0, 0],
},
});
- }));
+ });
// è¦æ±‚ã•れãŸç¯„囲ã«ãƒ­ã‚°ãŒã²ã¨ã¤ã‚‚ãªã„å ´åˆã§ã‚‚パディングã§ãã‚‹
- it('Can padding from past range', async(async () => {
+ it('Can padding from past range', async () => {
await testChart.increment();
await testChart.save();
@@ -265,7 +257,7 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [0, 0, 0],
- total: [1, 1, 1]
+ total: [1, 1, 1],
},
});
@@ -273,14 +265,14 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
- total: [1, 0, 0]
+ total: [1, 0, 0],
},
});
- }));
+ });
// è¦æ±‚ã•れãŸç¯„å›²ã®æœ€ã‚‚å¤ã„箇所ã«ä½ç½®ã™ã‚‹ãƒ­ã‚°ãŒå­˜åœ¨ã—ãªã„å ´åˆã§ã‚‚パディングã§ãã‚‹
// Issue #3190
- it('Can padding from past range 2', async(async () => {
+ it('Can padding from past range 2', async () => {
await testChart.increment();
await testChart.save();
@@ -296,7 +288,7 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
- total: [2, 1, 1]
+ total: [2, 1, 1],
},
});
@@ -304,12 +296,12 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [2, 0, 0],
- total: [2, 0, 0]
+ total: [2, 0, 0],
},
});
- }));
+ });
- it('Can specify offset', async(async () => {
+ it('Can specify offset', async () => {
await testChart.increment();
await testChart.save();
@@ -325,7 +317,7 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
- total: [1, 0, 0]
+ total: [1, 0, 0],
},
});
@@ -333,12 +325,12 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [2, 0, 0],
- total: [2, 0, 0]
+ total: [2, 0, 0],
},
});
- }));
+ });
- it('Can specify offset (floor time)', async(async () => {
+ it('Can specify offset (floor time)', async () => {
clock.tick('00:30:00');
await testChart.increment();
@@ -356,7 +348,7 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
- total: [1, 0, 0]
+ total: [1, 0, 0],
},
});
@@ -364,13 +356,13 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [2, 0, 0],
- total: [2, 0, 0]
+ total: [2, 0, 0],
},
});
- }));
+ });
describe('Grouped', () => {
- it('Can updates', async(async () => {
+ it('Can updates', async () => {
await testGroupedChart.increment('alice');
await testGroupedChart.save();
@@ -383,7 +375,7 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
- total: [1, 0, 0]
+ total: [1, 0, 0],
},
});
@@ -391,7 +383,7 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
- total: [1, 0, 0]
+ total: [1, 0, 0],
},
});
@@ -399,7 +391,7 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [0, 0, 0],
- total: [0, 0, 0]
+ total: [0, 0, 0],
},
});
@@ -407,14 +399,14 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [0, 0, 0],
- total: [0, 0, 0]
+ total: [0, 0, 0],
},
});
- }));
+ });
});
describe('Unique increment', () => {
- it('Can updates', async(async () => {
+ it('Can updates', async () => {
await testUniqueChart.uniqueIncrement('alice');
await testUniqueChart.uniqueIncrement('alice');
await testUniqueChart.uniqueIncrement('bob');
@@ -430,10 +422,10 @@ describe('Chart', () => {
assert.deepStrictEqual(chartDays, {
foo: [2, 0, 0],
});
- }));
+ });
describe('Intersection', () => {
- it('æ¡ä»¶ãŒæº€ãŸã•れã¦ã„ãªã„å ´åˆã¯ã‚«ã‚¦ãƒ³ãƒˆã•れãªã„', async(async () => {
+ it('æ¡ä»¶ãŒæº€ãŸã•れã¦ã„ãªã„å ´åˆã¯ã‚«ã‚¦ãƒ³ãƒˆã•れãªã„', async () => {
await testIntersectionChart.addA('alice');
await testIntersectionChart.addA('bob');
await testIntersectionChart.addB('carol');
@@ -453,9 +445,9 @@ describe('Chart', () => {
b: [1, 0, 0],
aAndB: [0, 0, 0],
});
- }));
+ });
- it('æ¡ä»¶ãŒæº€ãŸã•れã¦ã„ã‚‹å ´åˆã«ã‚«ã‚¦ãƒ³ãƒˆã•れる', async(async () => {
+ it('æ¡ä»¶ãŒæº€ãŸã•れã¦ã„ã‚‹å ´åˆã«ã‚«ã‚¦ãƒ³ãƒˆã•れる', async () => {
await testIntersectionChart.addA('alice');
await testIntersectionChart.addA('bob');
await testIntersectionChart.addB('carol');
@@ -476,12 +468,12 @@ describe('Chart', () => {
b: [2, 0, 0],
aAndB: [1, 0, 0],
});
- }));
+ });
});
});
describe('Resync', () => {
- it('Can resync', async(async () => {
+ it('Can resync', async () => {
testChart.total = 1;
await testChart.resync();
@@ -493,7 +485,7 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [0, 0, 0],
- total: [1, 0, 0]
+ total: [1, 0, 0],
},
});
@@ -501,12 +493,12 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [0, 0, 0],
- total: [1, 0, 0]
+ total: [1, 0, 0],
},
});
- }));
+ });
- it('Can resync (2)', async(async () => {
+ it('Can resync (2)', async () => {
await testChart.increment();
await testChart.save();
@@ -523,7 +515,7 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [0, 1, 0],
- total: [100, 1, 0]
+ total: [100, 1, 0],
},
});
@@ -531,9 +523,9 @@ describe('Chart', () => {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
- total: [100, 0, 0]
+ total: [100, 0, 0],
},
});
- }));
+ });
});
});
diff --git a/packages/backend/test/extract-mentions.ts b/packages/backend/test/extract-mentions.ts
index 9bfbc4192a..85afb098d8 100644
--- a/packages/backend/test/extract-mentions.ts
+++ b/packages/backend/test/extract-mentions.ts
@@ -1,7 +1,7 @@
import * as assert from 'assert';
-import { extractMentions } from '../src/misc/extract-mentions.js';
import { parse } from 'mfm-js';
+import { extractMentions } from '../src/misc/extract-mentions.js';
describe('Extract mentions', () => {
it('simple', () => {
@@ -10,15 +10,15 @@ describe('Extract mentions', () => {
assert.deepStrictEqual(mentions, [{
username: 'foo',
acct: '@foo',
- host: null
+ host: null,
}, {
username: 'bar',
acct: '@bar',
- host: null
+ host: null,
}, {
username: 'baz',
acct: '@baz',
- host: null
+ host: null,
}]);
});
@@ -28,15 +28,15 @@ describe('Extract mentions', () => {
assert.deepStrictEqual(mentions, [{
username: 'foo',
acct: '@foo',
- host: null
+ host: null,
}, {
username: 'bar',
acct: '@bar',
- host: null
+ host: null,
}, {
username: 'baz',
acct: '@baz',
- host: null
+ host: null,
}]);
});
});
diff --git a/packages/backend/test/fetch-resource.ts b/packages/backend/test/fetch-resource.ts
index 4cb4b42562..ddb0e94b86 100644
--- a/packages/backend/test/fetch-resource.ts
+++ b/packages/backend/test/fetch-resource.ts
@@ -2,8 +2,8 @@ process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as childProcess from 'child_process';
-import { async, startServer, signup, post, request, simpleGet, port, shutdownServer } from './utils.js';
import * as openapi from '@redocly/openapi-core';
+import { async, startServer, signup, post, request, simpleGet, port, shutdownServer } from './utils.js';
// Request Accept
const ONLY_AP = 'application/activity+json';
@@ -26,7 +26,7 @@ describe('Fetch resource', () => {
p = await startServer();
alice = await signup({ username: 'alice' });
alicesPost = await post(alice, {
- text: 'test'
+ text: 'test',
});
});
@@ -70,7 +70,7 @@ describe('Fetch resource', () => {
const config = await openapi.loadConfig();
const result = await openapi.bundle({
config,
- ref: `http://localhost:${port}/api.json`
+ ref: `http://localhost:${port}/api.json`,
});
for (const problem of result.problems) {
diff --git a/packages/backend/test/get-file-info.ts b/packages/backend/test/get-file-info.ts
index 20061b8708..7ce98db50f 100644
--- a/packages/backend/test/get-file-info.ts
+++ b/packages/backend/test/get-file-info.ts
@@ -1,10 +1,15 @@
import * as assert from 'assert';
-import { async } from './utils.js';
+import { fileURLToPath } from 'node:url';
+import { dirname } from 'node:path';
import { getFileInfo } from '../src/misc/get-file-info.js';
+import { async } from './utils.js';
+
+const _filename = fileURLToPath(import.meta.url);
+const _dirname = dirname(_filename);
describe('Get file info', () => {
it('Empty file', async (async () => {
- const path = `${__dirname}/resources/emptyfile`;
+ const path = `${_dirname}/resources/emptyfile`;
const info = await getFileInfo(path) as any;
delete info.warnings;
delete info.blurhash;
@@ -13,7 +18,7 @@ describe('Get file info', () => {
md5: 'd41d8cd98f00b204e9800998ecf8427e',
type: {
mime: 'application/octet-stream',
- ext: null
+ ext: null,
},
width: undefined,
height: undefined,
@@ -22,7 +27,7 @@ describe('Get file info', () => {
}));
it('Generic JPEG', async (async () => {
- const path = `${__dirname}/resources/Lenna.jpg`;
+ const path = `${_dirname}/resources/Lenna.jpg`;
const info = await getFileInfo(path) as any;
delete info.warnings;
delete info.blurhash;
@@ -31,7 +36,7 @@ describe('Get file info', () => {
md5: '091b3f259662aa31e2ffef4519951168',
type: {
mime: 'image/jpeg',
- ext: 'jpg'
+ ext: 'jpg',
},
width: 512,
height: 512,
@@ -40,7 +45,7 @@ describe('Get file info', () => {
}));
it('Generic APNG', async (async () => {
- const path = `${__dirname}/resources/anime.png`;
+ const path = `${_dirname}/resources/anime.png`;
const info = await getFileInfo(path) as any;
delete info.warnings;
delete info.blurhash;
@@ -49,7 +54,7 @@ describe('Get file info', () => {
md5: '08189c607bea3b952704676bb3c979e0',
type: {
mime: 'image/apng',
- ext: 'apng'
+ ext: 'apng',
},
width: 256,
height: 256,
@@ -58,7 +63,7 @@ describe('Get file info', () => {
}));
it('Generic AGIF', async (async () => {
- const path = `${__dirname}/resources/anime.gif`;
+ const path = `${_dirname}/resources/anime.gif`;
const info = await getFileInfo(path) as any;
delete info.warnings;
delete info.blurhash;
@@ -67,7 +72,7 @@ describe('Get file info', () => {
md5: '32c47a11555675d9267aee1a86571e7e',
type: {
mime: 'image/gif',
- ext: 'gif'
+ ext: 'gif',
},
width: 256,
height: 256,
@@ -76,7 +81,7 @@ describe('Get file info', () => {
}));
it('PNG with alpha', async (async () => {
- const path = `${__dirname}/resources/with-alpha.png`;
+ const path = `${_dirname}/resources/with-alpha.png`;
const info = await getFileInfo(path) as any;
delete info.warnings;
delete info.blurhash;
@@ -85,7 +90,7 @@ describe('Get file info', () => {
md5: 'f73535c3e1e27508885b69b10cf6e991',
type: {
mime: 'image/png',
- ext: 'png'
+ ext: 'png',
},
width: 256,
height: 256,
@@ -94,7 +99,7 @@ describe('Get file info', () => {
}));
it('Generic SVG', async (async () => {
- const path = `${__dirname}/resources/image.svg`;
+ const path = `${_dirname}/resources/image.svg`;
const info = await getFileInfo(path) as any;
delete info.warnings;
delete info.blurhash;
@@ -103,7 +108,7 @@ describe('Get file info', () => {
md5: 'b6f52b4b021e7b92cdd04509c7267965',
type: {
mime: 'image/svg+xml',
- ext: 'svg'
+ ext: 'svg',
},
width: 256,
height: 256,
@@ -113,7 +118,7 @@ describe('Get file info', () => {
it('SVG with XML definition', async (async () => {
// https://github.com/misskey-dev/misskey/issues/4413
- const path = `${__dirname}/resources/with-xml-def.svg`;
+ const path = `${_dirname}/resources/with-xml-def.svg`;
const info = await getFileInfo(path) as any;
delete info.warnings;
delete info.blurhash;
@@ -122,7 +127,7 @@ describe('Get file info', () => {
md5: '4b7a346cde9ccbeb267e812567e33397',
type: {
mime: 'image/svg+xml',
- ext: 'svg'
+ ext: 'svg',
},
width: 256,
height: 256,
@@ -131,7 +136,7 @@ describe('Get file info', () => {
}));
it('Dimension limit', async (async () => {
- const path = `${__dirname}/resources/25000x25000.png`;
+ const path = `${_dirname}/resources/25000x25000.png`;
const info = await getFileInfo(path) as any;
delete info.warnings;
delete info.blurhash;
@@ -140,7 +145,7 @@ describe('Get file info', () => {
md5: '268c5dde99e17cf8fe09f1ab3f97df56',
type: {
mime: 'application/octet-stream', // do not treat as image
- ext: null
+ ext: null,
},
width: 25000,
height: 25000,
@@ -149,7 +154,7 @@ describe('Get file info', () => {
}));
it('Rotate JPEG', async (async () => {
- const path = `${__dirname}/resources/rotate.jpg`;
+ const path = `${_dirname}/resources/rotate.jpg`;
const info = await getFileInfo(path) as any;
delete info.warnings;
delete info.blurhash;
@@ -158,7 +163,7 @@ describe('Get file info', () => {
md5: '68d5b2d8d1d1acbbce99203e3ec3857e',
type: {
mime: 'image/jpeg',
- ext: 'jpg'
+ ext: 'jpg',
},
width: 512,
height: 256,
diff --git a/packages/backend/test/loader.js b/packages/backend/test/loader.js
index 016f32f1a8..6b21587e32 100644
--- a/packages/backend/test/loader.js
+++ b/packages/backend/test/loader.js
@@ -1,37 +1,34 @@
-import path from 'path'
-import typescript from 'typescript'
-import { createMatchPath } from 'tsconfig-paths'
-import { resolve as BaseResolve, getFormat, transformSource } from 'ts-node/esm'
+/**
+ * ts-node/esmãƒ­ãƒ¼ãƒ€ãƒ¼ã«æŠ•ã’ã‚‹å‰ã«path mappingを解決ã™ã‚‹
+ * å‚考
+ * - https://github.com/TypeStrong/ts-node/discussions/1450#discussioncomment-1806115
+ * - https://nodejs.org/api/esm.html#loaders
+ * ※ https://github.com/TypeStrong/ts-node/pull/1585 ãŒå–り込ã¾ã‚ŒãŸã‚‰ã“ã®ã‚«ã‚¹ã‚¿ãƒ ãƒ­ãƒ¼ãƒ€ãƒ¼ã¯å¿…è¦ãªããªã‚‹
+ */
-const { readConfigFile, parseJsonConfigFileContent, sys } = typescript
+import { resolve as resolveTs, load } from 'ts-node/esm';
+import { loadConfig, createMatchPath } from 'tsconfig-paths';
+import { pathToFileURL } from 'url';
-const __dirname = path.dirname(new URL(import.meta.url).pathname)
+const tsconfig = loadConfig();
+const matchPath = createMatchPath(tsconfig.absoluteBaseUrl, tsconfig.paths);
-const configFile = readConfigFile('./test/tsconfig.json', sys.readFile)
-if (typeof configFile.error !== 'undefined') {
- throw new Error(`Failed to load tsconfig: ${configFile.error}`)
+export function resolve(specifier, ctx, defaultResolve) {
+ let resolvedSpecifier;
+ if (specifier.endsWith('.js')) {
+ // maybe transpiled
+ const specifierWithoutExtension = specifier.substring(0, specifier.length - '.js'.length);
+ const matchedSpecifier = matchPath(specifierWithoutExtension);
+ if (matchedSpecifier) {
+ resolvedSpecifier = pathToFileURL(`${matchedSpecifier}.js`).href;
+ }
+ } else {
+ const matchedSpecifier = matchPath(specifier);
+ if (matchedSpecifier) {
+ resolvedSpecifier = pathToFileURL(matchedSpecifier).href;
+ }
+ }
+ return resolveTs(resolvedSpecifier ?? specifier, ctx, defaultResolve);
}
-const { options } = parseJsonConfigFileContent(
- configFile.config,
- {
- fileExists: sys.fileExists,
- readFile: sys.readFile,
- readDirectory: sys.readDirectory,
- useCaseSensitiveFileNames: true,
- },
- __dirname
-)
-
-export { getFormat, transformSource } // ã“ã„ã¤ã‚‰ã¯ãã®ã¾ã¾ä½¿ã£ã¦ã»ã—ã„ã®ã§ re-export ã™ã‚‹
-
-const matchPath = createMatchPath(options.baseUrl, options.paths)
-
-export async function resolve(specifier, context, defaultResolve) {
- const matchedSpecifier = matchPath(specifier.replace('.js', '.ts'))
- return BaseResolve( // ts-node/esm ã® resolve ã« tsconfig-paths ã§è§£æ±ºã—ãŸãƒ‘スを渡ã™
- matchedSpecifier ? `${matchedSpecifier}.ts` : specifier,
- context,
- defaultResolve
- )
-}
+export { load };
diff --git a/packages/backend/test/misc/mock-resolver.ts b/packages/backend/test/misc/mock-resolver.ts
index 5a46daf49f..ba89ac329a 100644
--- a/packages/backend/test/misc/mock-resolver.ts
+++ b/packages/backend/test/misc/mock-resolver.ts
@@ -11,7 +11,7 @@ export class MockResolver extends Resolver {
public async _register(uri: string, content: string | Record<string, any>, type = 'application/activity+json') {
this._rs.set(uri, {
type,
- content: typeof content === 'string' ? content : JSON.stringify(content)
+ content: typeof content === 'string' ? content : JSON.stringify(content),
});
}
@@ -22,9 +22,9 @@ export class MockResolver extends Resolver {
if (!r) {
throw {
- name: `StatusError`,
+ name: 'StatusError',
statusCode: 404,
- message: `Not registed for mock`
+ message: 'Not registed for mock',
};
}
diff --git a/packages/backend/test/mute.ts b/packages/backend/test/mute.ts
index 288e8a8055..2be70f2b65 100644
--- a/packages/backend/test/mute.ts
+++ b/packages/backend/test/mute.ts
@@ -25,7 +25,7 @@ describe('Mute', () => {
it('ミュート作æˆ', async(async () => {
const res = await request('/mute/create', {
- userId: carol.id
+ userId: carol.id,
}, alice);
assert.strictEqual(res.status, 204);
@@ -117,7 +117,7 @@ describe('Mute', () => {
const aliceNote = await post(alice);
const carolNote = await post(carol);
const bobNote = await post(bob, {
- renoteId: carolNote.id
+ renoteId: carolNote.id,
});
const res = await request('/notes/local-timeline', {}, alice);
diff --git a/packages/backend/test/note.ts b/packages/backend/test/note.ts
index 942b2709df..1183e9e4f1 100644
--- a/packages/backend/test/note.ts
+++ b/packages/backend/test/note.ts
@@ -2,8 +2,8 @@ process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as childProcess from 'child_process';
-import { async, signup, request, post, uploadFile, startServer, shutdownServer, initTestDb } from './utils.js';
import { Note } from '../src/models/entities/note.js';
+import { async, signup, request, post, uploadFile, startServer, shutdownServer, initTestDb } from './utils.js';
describe('Note', () => {
let p: childProcess.ChildProcess;
@@ -26,7 +26,7 @@ describe('Note', () => {
it('投稿ã§ãã‚‹', async(async () => {
const post = {
- text: 'test'
+ text: 'test',
};
const res = await request('/notes/create', post, alice);
@@ -40,7 +40,7 @@ describe('Note', () => {
const file = await uploadFile(alice);
const res = await request('/notes/create', {
- fileIds: [file.id]
+ fileIds: [file.id],
}, alice);
assert.strictEqual(res.status, 200);
@@ -53,7 +53,7 @@ describe('Note', () => {
const res = await request('/notes/create', {
text: 'test',
- fileIds: [file.id]
+ fileIds: [file.id],
}, alice);
assert.strictEqual(res.status, 200);
@@ -64,7 +64,7 @@ describe('Note', () => {
it('存在ã—ãªã„ファイルã¯ç„¡è¦–', async(async () => {
const res = await request('/notes/create', {
text: 'test',
- fileIds: ['000000000000000000000000']
+ fileIds: ['000000000000000000000000'],
}, alice);
assert.strictEqual(res.status, 200);
@@ -74,19 +74,19 @@ describe('Note', () => {
it('䏿­£ãªãƒ•ァイルIDã§æ€’られる', async(async () => {
const res = await request('/notes/create', {
- fileIds: ['kyoppie']
+ fileIds: ['kyoppie'],
}, alice);
assert.strictEqual(res.status, 400);
}));
it('返信ã§ãã‚‹', async(async () => {
const bobPost = await post(bob, {
- text: 'foo'
+ text: 'foo',
});
const alicePost = {
text: 'bar',
- replyId: bobPost.id
+ replyId: bobPost.id,
};
const res = await request('/notes/create', alicePost, alice);
@@ -100,11 +100,11 @@ describe('Note', () => {
it('renoteã§ãã‚‹', async(async () => {
const bobPost = await post(bob, {
- text: 'test'
+ text: 'test',
});
const alicePost = {
- renoteId: bobPost.id
+ renoteId: bobPost.id,
};
const res = await request('/notes/create', alicePost, alice);
@@ -117,12 +117,12 @@ describe('Note', () => {
it('引用renoteã§ãã‚‹', async(async () => {
const bobPost = await post(bob, {
- text: 'test'
+ text: 'test',
});
const alicePost = {
text: 'test',
- renoteId: bobPost.id
+ renoteId: bobPost.id,
};
const res = await request('/notes/create', alicePost, alice);
@@ -136,7 +136,7 @@ describe('Note', () => {
it('文字数ãŽã‚ŠãŽã‚Šã§æ€’られãªã„', async(async () => {
const post = {
- text: '!'.repeat(500)
+ text: '!'.repeat(500),
};
const res = await request('/notes/create', post, alice);
assert.strictEqual(res.status, 200);
@@ -144,7 +144,7 @@ describe('Note', () => {
it('文字数オーãƒãƒ¼ã§æ€’られる', async(async () => {
const post = {
- text: '!'.repeat(501)
+ text: '!'.repeat(501),
};
const res = await request('/notes/create', post, alice);
assert.strictEqual(res.status, 400);
@@ -153,7 +153,7 @@ describe('Note', () => {
it('存在ã—ãªã„ãƒªãƒ—ãƒ©ã‚¤å…ˆã§æ€’られる', async(async () => {
const post = {
text: 'test',
- replyId: '000000000000000000000000'
+ replyId: '000000000000000000000000',
};
const res = await request('/notes/create', post, alice);
assert.strictEqual(res.status, 400);
@@ -161,7 +161,7 @@ describe('Note', () => {
it('存在ã—ãªã„renoteå¯¾è±¡ã§æ€’られる', async(async () => {
const post = {
- renoteId: '000000000000000000000000'
+ renoteId: '000000000000000000000000',
};
const res = await request('/notes/create', post, alice);
assert.strictEqual(res.status, 400);
@@ -170,7 +170,7 @@ describe('Note', () => {
it('䏿­£ãªãƒªãƒ—ライ先IDã§æ€’られる', async(async () => {
const post = {
text: 'test',
- replyId: 'foo'
+ replyId: 'foo',
};
const res = await request('/notes/create', post, alice);
assert.strictEqual(res.status, 400);
@@ -178,7 +178,7 @@ describe('Note', () => {
it('䏿­£ãªrenote対象IDã§æ€’られる', async(async () => {
const post = {
- renoteId: 'foo'
+ renoteId: 'foo',
};
const res = await request('/notes/create', post, alice);
assert.strictEqual(res.status, 400);
@@ -186,7 +186,7 @@ describe('Note', () => {
it('存在ã—ãªã„ユーザーã«ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ã§ãã‚‹', async(async () => {
const post = {
- text: '@ghost yo'
+ text: '@ghost yo',
};
const res = await request('/notes/create', post, alice);
@@ -198,7 +198,7 @@ describe('Note', () => {
it('åŒã˜ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«è¤‡æ•°ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ã—ã¦ã‚‚内部的ã«ã¾ã¨ã‚られる', async(async () => {
const post = {
- text: '@bob @bob @bob yo'
+ text: '@bob @bob @bob yo',
};
const res = await request('/notes/create', post, alice);
@@ -216,8 +216,8 @@ describe('Note', () => {
const res = await request('/notes/create', {
text: 'test',
poll: {
- choices: ['foo', 'bar']
- }
+ choices: ['foo', 'bar'],
+ },
}, alice);
assert.strictEqual(res.status, 200);
@@ -227,7 +227,7 @@ describe('Note', () => {
it('投票ã®é¸æŠžè‚¢ãŒç„¡ãã¦æ€’られる', async(async () => {
const res = await request('/notes/create', {
- poll: {}
+ poll: {},
}, alice);
assert.strictEqual(res.status, 400);
}));
@@ -235,8 +235,8 @@ describe('Note', () => {
it('投票ã®é¸æŠžè‚¢ãŒç„¡ãã¦æ€’られる (空ã®é…列)', async(async () => {
const res = await request('/notes/create', {
poll: {
- choices: []
- }
+ choices: [],
+ },
}, alice);
assert.strictEqual(res.status, 400);
}));
@@ -244,8 +244,8 @@ describe('Note', () => {
it('投票ã®é¸æŠžè‚¢ãŒ1ã¤ã§æ€’られる', async(async () => {
const res = await request('/notes/create', {
poll: {
- choices: ['Strawberry Pasta']
- }
+ choices: ['Strawberry Pasta'],
+ },
}, alice);
assert.strictEqual(res.status, 400);
}));
@@ -254,13 +254,13 @@ describe('Note', () => {
const { body } = await request('/notes/create', {
text: 'test',
poll: {
- choices: ['sakura', 'izumi', 'ako']
- }
+ choices: ['sakura', 'izumi', 'ako'],
+ },
}, alice);
const res = await request('/notes/polls/vote', {
noteId: body.createdNote.id,
- choice: 1
+ choice: 1,
}, alice);
assert.strictEqual(res.status, 204);
@@ -270,18 +270,18 @@ describe('Note', () => {
const { body } = await request('/notes/create', {
text: 'test',
poll: {
- choices: ['sakura', 'izumi', 'ako']
- }
+ choices: ['sakura', 'izumi', 'ako'],
+ },
}, alice);
await request('/notes/polls/vote', {
noteId: body.createdNote.id,
- choice: 0
+ choice: 0,
}, alice);
const res = await request('/notes/polls/vote', {
noteId: body.createdNote.id,
- choice: 2
+ choice: 2,
}, alice);
assert.strictEqual(res.status, 400);
@@ -292,23 +292,23 @@ describe('Note', () => {
text: 'test',
poll: {
choices: ['sakura', 'izumi', 'ako'],
- multiple: true
- }
+ multiple: true,
+ },
}, alice);
await request('/notes/polls/vote', {
noteId: body.createdNote.id,
- choice: 0
+ choice: 0,
}, alice);
await request('/notes/polls/vote', {
noteId: body.createdNote.id,
- choice: 1
+ choice: 1,
}, alice);
const res = await request('/notes/polls/vote', {
noteId: body.createdNote.id,
- choice: 2
+ choice: 2,
}, alice);
assert.strictEqual(res.status, 204);
@@ -319,15 +319,15 @@ describe('Note', () => {
text: 'test',
poll: {
choices: ['sakura', 'izumi', 'ako'],
- expiredAfter: 1
- }
+ expiredAfter: 1,
+ },
}, alice);
await new Promise(x => setTimeout(x, 2));
const res = await request('/notes/polls/vote', {
noteId: body.createdNote.id,
- choice: 1
+ choice: 1,
}, alice);
assert.strictEqual(res.status, 400);
@@ -341,11 +341,11 @@ describe('Note', () => {
}, alice);
const replyOneRes = await request('/notes/create', {
text: 'reply one',
- replyId: mainNoteRes.body.createdNote.id
+ replyId: mainNoteRes.body.createdNote.id,
}, alice);
const replyTwoRes = await request('/notes/create', {
text: 'reply two',
- replyId: mainNoteRes.body.createdNote.id
+ replyId: mainNoteRes.body.createdNote.id,
}, alice);
const deleteOneRes = await request('/notes/delete', {
@@ -353,7 +353,7 @@ describe('Note', () => {
}, alice);
assert.strictEqual(deleteOneRes.status, 204);
- let mainNote = await Notes.findOne({id: mainNoteRes.body.createdNote.id});
+ let mainNote = await Notes.findOne({ id: mainNoteRes.body.createdNote.id });
assert.strictEqual(mainNote.repliesCount, 1);
const deleteTwoRes = await request('/notes/delete', {
@@ -361,7 +361,7 @@ describe('Note', () => {
}, alice);
assert.strictEqual(deleteTwoRes.status, 204);
- mainNote = await Notes.findOne({id: mainNoteRes.body.createdNote.id});
+ mainNote = await Notes.findOne({ id: mainNoteRes.body.createdNote.id });
assert.strictEqual(mainNote.repliesCount, 0);
}));
});
diff --git a/packages/backend/test/prelude/url.ts b/packages/backend/test/prelude/url.ts
index 84e43d26c2..df102c8dfe 100644
--- a/packages/backend/test/prelude/url.ts
+++ b/packages/backend/test/prelude/url.ts
@@ -6,7 +6,7 @@ describe('url', () => {
const s = query({
foo: 'ãµã…',
bar: 'b a r',
- baz: undefined
+ baz: undefined,
});
assert.deepStrictEqual(s, 'foo=%E3%81%B5%E3%81%85&bar=b%20a%20r');
});
diff --git a/packages/backend/test/streaming.ts b/packages/backend/test/streaming.ts
index 8d22b6d3d3..f080b71dd4 100644
--- a/packages/backend/test/streaming.ts
+++ b/packages/backend/test/streaming.ts
@@ -2,8 +2,8 @@ process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as childProcess from 'child_process';
-import { connectStream, signup, request, post, startServer, shutdownServer, initTestDb } from './utils.js';
import { Following } from '../src/models/entities/following.js';
+import { connectStream, signup, request, post, startServer, shutdownServer, initTestDb } from './utils.js';
describe('Streaming', () => {
let p: childProcess.ChildProcess;
@@ -30,7 +30,7 @@ describe('Streaming', () => {
followerSharedInbox: null,
followeeHost: followee.host,
followeeInbox: null,
- followeeSharedInbox: null
+ followeeSharedInbox: null,
});
};
@@ -47,7 +47,7 @@ describe('Streaming', () => {
});
post(alice, {
- text: 'foo @bob bar'
+ text: 'foo @bob bar',
});
}));
@@ -55,7 +55,7 @@ describe('Streaming', () => {
const alice = await signup({ username: 'alice' });
const bob = await signup({ username: 'bob' });
const bobNote = await post(bob, {
- text: 'foo'
+ text: 'foo',
});
const ws = await connectStream(bob, 'main', ({ type, body }) => {
@@ -67,14 +67,14 @@ describe('Streaming', () => {
});
post(alice, {
- renoteId: bobNote.id
+ renoteId: bobNote.id,
});
}));
describe('Home Timeline', () => {
it('è‡ªåˆ†ã®æŠ•ç¨¿ãŒæµã‚Œã‚‹', () => new Promise(async done => {
const post = {
- text: 'foo'
+ text: 'foo',
};
const me = await signup();
@@ -96,7 +96,7 @@ describe('Streaming', () => {
// Alice ㌠Bob をフォロー
await request('/following/create', {
- userId: bob.id
+ userId: bob.id,
}, alice);
const ws = await connectStream(alice, 'homeTimeline', ({ type, body }) => {
@@ -108,7 +108,7 @@ describe('Streaming', () => {
});
post(bob, {
- text: 'foo'
+ text: 'foo',
});
}));
@@ -125,7 +125,7 @@ describe('Streaming', () => {
});
post(bob, {
- text: 'foo'
+ text: 'foo',
});
setTimeout(() => {
@@ -141,7 +141,7 @@ describe('Streaming', () => {
// Alice ㌠Bob をフォロー
await request('/following/create', {
- userId: bob.id
+ userId: bob.id,
}, alice);
const ws = await connectStream(alice, 'homeTimeline', ({ type, body }) => {
@@ -157,7 +157,7 @@ describe('Streaming', () => {
post(bob, {
text: 'foo',
visibility: 'specified',
- visibleUserIds: [alice.id]
+ visibleUserIds: [alice.id],
});
}));
@@ -168,7 +168,7 @@ describe('Streaming', () => {
// Alice ㌠Bob をフォロー
await request('/following/create', {
- userId: bob.id
+ userId: bob.id,
}, alice);
let fired = false;
@@ -183,7 +183,7 @@ describe('Streaming', () => {
post(bob, {
text: 'foo',
visibility: 'specified',
- visibleUserIds: [carol.id]
+ visibleUserIds: [carol.id],
});
setTimeout(() => {
@@ -207,7 +207,7 @@ describe('Streaming', () => {
});
post(me, {
- text: 'foo'
+ text: 'foo',
});
}));
@@ -224,7 +224,7 @@ describe('Streaming', () => {
});
post(bob, {
- text: 'foo'
+ text: 'foo',
});
}));
@@ -241,7 +241,7 @@ describe('Streaming', () => {
});
post(bob, {
- text: 'foo'
+ text: 'foo',
});
setTimeout(() => {
@@ -257,7 +257,7 @@ describe('Streaming', () => {
// Alice ㌠Bob をフォロー
await request('/following/create', {
- userId: bob.id
+ userId: bob.id,
}, alice);
let fired = false;
@@ -269,7 +269,7 @@ describe('Streaming', () => {
});
post(bob, {
- text: 'foo'
+ text: 'foo',
});
setTimeout(() => {
@@ -294,7 +294,7 @@ describe('Streaming', () => {
// ホーム指定
post(bob, {
text: 'foo',
- visibility: 'home'
+ visibility: 'home',
});
setTimeout(() => {
@@ -310,7 +310,7 @@ describe('Streaming', () => {
// Alice ㌠Bob をフォロー
await request('/following/create', {
- userId: bob.id
+ userId: bob.id,
}, alice);
let fired = false;
@@ -325,7 +325,7 @@ describe('Streaming', () => {
post(bob, {
text: 'foo',
visibility: 'specified',
- visibleUserIds: [alice.id]
+ visibleUserIds: [alice.id],
});
setTimeout(() => {
@@ -350,7 +350,7 @@ describe('Streaming', () => {
// ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼å®›ã¦æŠ•ç¨¿
post(bob, {
text: 'foo',
- visibility: 'followers'
+ visibility: 'followers',
});
setTimeout(() => {
@@ -374,7 +374,7 @@ describe('Streaming', () => {
});
post(me, {
- text: 'foo'
+ text: 'foo',
});
}));
@@ -391,7 +391,7 @@ describe('Streaming', () => {
});
post(bob, {
- text: 'foo'
+ text: 'foo',
});
}));
@@ -411,7 +411,7 @@ describe('Streaming', () => {
});
post(bob, {
- text: 'foo'
+ text: 'foo',
});
}));
@@ -428,7 +428,7 @@ describe('Streaming', () => {
});
post(bob, {
- text: 'foo'
+ text: 'foo',
});
setTimeout(() => {
@@ -444,7 +444,7 @@ describe('Streaming', () => {
// Alice ㌠Bob をフォロー
await request('/following/create', {
- userId: bob.id
+ userId: bob.id,
}, alice);
const ws = await connectStream(alice, 'hybridTimeline', ({ type, body }) => {
@@ -460,7 +460,7 @@ describe('Streaming', () => {
post(bob, {
text: 'foo',
visibility: 'specified',
- visibleUserIds: [alice.id]
+ visibleUserIds: [alice.id],
});
}));
@@ -470,7 +470,7 @@ describe('Streaming', () => {
// Alice ㌠Bob をフォロー
await request('/following/create', {
- userId: bob.id
+ userId: bob.id,
}, alice);
const ws = await connectStream(alice, 'hybridTimeline', ({ type, body }) => {
@@ -485,7 +485,7 @@ describe('Streaming', () => {
// ホーム投稿
post(bob, {
text: 'foo',
- visibility: 'home'
+ visibility: 'home',
});
}));
@@ -504,7 +504,7 @@ describe('Streaming', () => {
// ホーム投稿
post(bob, {
text: 'foo',
- visibility: 'home'
+ visibility: 'home',
});
setTimeout(() => {
@@ -529,7 +529,7 @@ describe('Streaming', () => {
// ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼å®›ã¦æŠ•ç¨¿
post(bob, {
text: 'foo',
- visibility: 'followers'
+ visibility: 'followers',
});
setTimeout(() => {
@@ -554,7 +554,7 @@ describe('Streaming', () => {
});
post(bob, {
- text: 'foo'
+ text: 'foo',
});
}));
@@ -571,7 +571,7 @@ describe('Streaming', () => {
});
post(bob, {
- text: 'foo'
+ text: 'foo',
});
}));
@@ -590,7 +590,7 @@ describe('Streaming', () => {
// ホーム投稿
post(bob, {
text: 'foo',
- visibility: 'home'
+ visibility: 'home',
});
setTimeout(() => {
@@ -608,13 +608,13 @@ describe('Streaming', () => {
// リスト作æˆ
const list = await request('/users/lists/create', {
- name: 'my list'
+ name: 'my list',
}, alice).then(x => x.body);
// Alice ㌠Bob をリスイン
await request('/users/lists/push', {
listId: list.id,
- userId: bob.id
+ userId: bob.id,
}, alice);
const ws = await connectStream(alice, 'userList', ({ type, body }) => {
@@ -624,11 +624,11 @@ describe('Streaming', () => {
done();
}
}, {
- listId: list.id
+ listId: list.id,
});
post(bob, {
- text: 'foo'
+ text: 'foo',
});
}));
@@ -638,7 +638,7 @@ describe('Streaming', () => {
// リスト作æˆ
const list = await request('/users/lists/create', {
- name: 'my list'
+ name: 'my list',
}, alice).then(x => x.body);
let fired = false;
@@ -648,11 +648,11 @@ describe('Streaming', () => {
fired = true;
}
}, {
- listId: list.id
+ listId: list.id,
});
post(bob, {
- text: 'foo'
+ text: 'foo',
});
setTimeout(() => {
@@ -669,13 +669,13 @@ describe('Streaming', () => {
// リスト作æˆ
const list = await request('/users/lists/create', {
- name: 'my list'
+ name: 'my list',
}, alice).then(x => x.body);
// Alice ㌠Bob をリスイン
await request('/users/lists/push', {
listId: list.id,
- userId: bob.id
+ userId: bob.id,
}, alice);
const ws = await connectStream(alice, 'userList', ({ type, body }) => {
@@ -686,14 +686,14 @@ describe('Streaming', () => {
done();
}
}, {
- listId: list.id
+ listId: list.id,
});
// Bob ㌠Alice å®›ã¦ã®ãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆæŠ•稿
post(bob, {
text: 'foo',
visibility: 'specified',
- visibleUserIds: [alice.id]
+ visibleUserIds: [alice.id],
});
}));
@@ -704,13 +704,13 @@ describe('Streaming', () => {
// リスト作æˆ
const list = await request('/users/lists/create', {
- name: 'my list'
+ name: 'my list',
}, alice).then(x => x.body);
// Alice ㌠Bob をリスイン
await request('/users/lists/push', {
listId: list.id,
- userId: bob.id
+ userId: bob.id,
}, alice);
let fired = false;
@@ -720,13 +720,13 @@ describe('Streaming', () => {
fired = true;
}
}, {
- listId: list.id
+ listId: list.id,
});
// ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼å®›ã¦æŠ•ç¨¿
post(bob, {
text: 'foo',
- visibility: 'followers'
+ visibility: 'followers',
});
setTimeout(() => {
@@ -749,12 +749,12 @@ describe('Streaming', () => {
}
}, {
q: [
- ['foo']
- ]
+ ['foo'],
+ ],
});
post(me, {
- text: '#foo'
+ text: '#foo',
});
}));
@@ -773,20 +773,20 @@ describe('Streaming', () => {
}
}, {
q: [
- ['foo', 'bar']
- ]
+ ['foo', 'bar'],
+ ],
});
post(me, {
- text: '#foo'
+ text: '#foo',
});
post(me, {
- text: '#bar'
+ text: '#bar',
});
post(me, {
- text: '#foo #bar'
+ text: '#foo #bar',
});
setTimeout(() => {
@@ -816,24 +816,24 @@ describe('Streaming', () => {
}, {
q: [
['foo'],
- ['bar']
- ]
+ ['bar'],
+ ],
});
post(me, {
- text: '#foo'
+ text: '#foo',
});
post(me, {
- text: '#bar'
+ text: '#bar',
});
post(me, {
- text: '#foo #bar'
+ text: '#foo #bar',
});
post(me, {
- text: '#piyo'
+ text: '#piyo',
});
setTimeout(() => {
@@ -866,28 +866,28 @@ describe('Streaming', () => {
}, {
q: [
['foo', 'bar'],
- ['piyo']
- ]
+ ['piyo'],
+ ],
});
post(me, {
- text: '#foo'
+ text: '#foo',
});
post(me, {
- text: '#bar'
+ text: '#bar',
});
post(me, {
- text: '#foo #bar'
+ text: '#foo #bar',
});
post(me, {
- text: '#piyo'
+ text: '#piyo',
});
post(me, {
- text: '#waaa'
+ text: '#waaa',
});
setTimeout(() => {
diff --git a/packages/backend/test/user-notes.ts b/packages/backend/test/user-notes.ts
index 25ffe04756..5b7933da67 100644
--- a/packages/backend/test/user-notes.ts
+++ b/packages/backend/test/user-notes.ts
@@ -2,8 +2,13 @@ process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as childProcess from 'child_process';
+import { dirname } from 'node:path';
+import { fileURLToPath } from 'node:url';
import { async, signup, request, post, uploadFile, startServer, shutdownServer } from './utils.js';
+const _filename = fileURLToPath(import.meta.url);
+const _dirname = dirname(_filename);
+
describe('users/notes', () => {
let p: childProcess.ChildProcess;
@@ -15,16 +20,16 @@ describe('users/notes', () => {
before(async () => {
p = await startServer();
alice = await signup({ username: 'alice' });
- const jpg = await uploadFile(alice, __dirname + '/resources/Lenna.jpg');
- const png = await uploadFile(alice, __dirname + '/resources/Lenna.png');
+ const jpg = await uploadFile(alice, _dirname + '/resources/Lenna.jpg');
+ const png = await uploadFile(alice, _dirname + '/resources/Lenna.png');
jpgNote = await post(alice, {
- fileIds: [jpg.id]
+ fileIds: [jpg.id],
});
pngNote = await post(alice, {
- fileIds: [png.id]
+ fileIds: [png.id],
});
jpgPngNote = await post(alice, {
- fileIds: [jpg.id, png.id]
+ fileIds: [jpg.id, png.id],
});
});
@@ -35,7 +40,7 @@ describe('users/notes', () => {
it('ファイルタイプ指定 (jpg)', async(async () => {
const res = await request('/users/notes', {
userId: alice.id,
- fileType: ['image/jpeg']
+ fileType: ['image/jpeg'],
}, alice);
assert.strictEqual(res.status, 200);
@@ -48,7 +53,7 @@ describe('users/notes', () => {
it('ファイルタイプ指定 (jpg or png)', async(async () => {
const res = await request('/users/notes', {
userId: alice.id,
- fileType: ['image/jpeg', 'image/png']
+ fileType: ['image/jpeg', 'image/png'],
}, alice);
assert.strictEqual(res.status, 200);
diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts
index 32a030f933..5eb4ed3b01 100644
--- a/packages/backend/test/utils.ts
+++ b/packages/backend/test/utils.ts
@@ -1,14 +1,20 @@
import * as fs from 'node:fs';
+import { fileURLToPath } from 'node:url';
+import { dirname } from 'node:path';
+import * as childProcess from 'child_process';
+import * as http from 'node:http';
+import { SIGKILL } from 'constants';
import * as WebSocket from 'ws';
import * as misskey from 'misskey-js';
import fetch from 'node-fetch';
import FormData from 'form-data';
-import * as childProcess from 'child_process';
-import * as http from 'http';
+import { DataSource } from 'typeorm';
import loadConfig from '../src/config/load.js';
-import { SIGKILL } from 'constants';
import { entities } from '../src/db/postgre.js';
+const _filename = fileURLToPath(import.meta.url);
+const _dirname = dirname(_filename);
+
const config = loadConfig();
export const port = config.port;
@@ -22,29 +28,29 @@ export const async = (fn: Function) => (done: Function) => {
export const request = async (endpoint: string, params: any, me?: any): Promise<{ body: any, status: number }> => {
const auth = me ? {
- i: me.token
+ i: me.token,
} : {};
const res = await fetch(`http://localhost:${port}/api${endpoint}`, {
method: 'POST',
headers: {
- 'Content-Type': 'application/json'
+ 'Content-Type': 'application/json',
},
- body: JSON.stringify(Object.assign(auth, params))
+ body: JSON.stringify(Object.assign(auth, params)),
});
const status = res.status;
const body = res.status !== 204 ? await res.json().catch() : null;
return {
- body, status
+ body, status,
};
};
export const signup = async (params?: any): Promise<any> => {
const q = Object.assign({
username: 'test',
- password: 'test'
+ password: 'test',
}, params);
const res = await request('/signup', q);
@@ -54,7 +60,7 @@ export const signup = async (params?: any): Promise<any> => {
export const post = async (user: any, params?: misskey.Endpoints['notes/create']['req']): Promise<misskey.entities.Note> => {
const q = Object.assign({
- text: 'test'
+ text: 'test',
}, params);
const res = await request('/notes/create', q, user);
@@ -65,26 +71,26 @@ export const post = async (user: any, params?: misskey.Endpoints['notes/create']
export const react = async (user: any, note: any, reaction: string): Promise<any> => {
await request('/notes/reactions/create', {
noteId: note.id,
- reaction: reaction
+ reaction: reaction,
}, user);
};
export const uploadFile = (user: any, path?: string): Promise<any> => {
- const formData = new FormData();
- formData.append('i', user.token);
- formData.append('file', fs.createReadStream(path || __dirname + '/resources/Lenna.png'));
+ const formData = new FormData();
+ formData.append('i', user.token);
+ formData.append('file', fs.createReadStream(path || _dirname + '/resources/Lenna.png'));
- return fetch(`http://localhost:${port}/api/drive/files/create`, {
- method: 'post',
- body: formData,
- timeout: 30 * 1000,
- }).then(res => {
- if (!res.ok) {
- throw `${res.status} ${res.statusText}`;
- } else {
- return res.json();
- }
- });
+ return fetch(`http://localhost:${port}/api/drive/files/create`, {
+ method: 'post',
+ body: formData,
+ timeout: 30 * 1000,
+ }).then(res => {
+ if (!res.ok) {
+ throw `${res.status} ${res.statusText}`;
+ } else {
+ return res.json();
+ }
+ });
};
export function connectStream(user: any, channel: string, listener: (message: Record<string, any>) => any, params?: any): Promise<WebSocket> {
@@ -94,9 +100,9 @@ export function connectStream(user: any, channel: string, listener: (message: Re
ws.on('open', () => {
ws.on('message', data => {
const msg = JSON.parse(data.toString());
- if (msg.type == 'channel' && msg.body.id == 'a') {
+ if (msg.type === 'channel' && msg.body.id === 'a') {
listener(msg.body);
- } else if (msg.type == 'connected' && msg.body.id == 'a') {
+ } else if (msg.type === 'connected' && msg.body.id === 'a') {
res(ws);
}
});
@@ -107,8 +113,8 @@ export function connectStream(user: any, channel: string, listener: (message: Re
channel: channel,
id: 'a',
pong: true,
- params: params
- }
+ params: params,
+ },
}));
});
});
@@ -119,8 +125,8 @@ export const simpleGet = async (path: string, accept = '*/*'): Promise<{ status?
return await new Promise((resolve, reject) => {
const req = http.request(`http://localhost:${port}${path}`, {
headers: {
- Accept: accept
- }
+ Accept: accept,
+ },
}, res => {
if (res.statusCode! >= 400) {
reject(res);
@@ -139,9 +145,9 @@ export const simpleGet = async (path: string, accept = '*/*'): Promise<{ status?
export function launchServer(callbackSpawnedProcess: (p: childProcess.ChildProcess) => void, moreProcess: () => Promise<void> = async () => {}) {
return (done: (err?: Error) => any) => {
- const p = childProcess.spawn('node', [__dirname + '/../index.js'], {
+ const p = childProcess.spawn('node', [_dirname + '/../index.js'], {
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
- env: { NODE_ENV: 'test', PATH: process.env.PATH }
+ env: { NODE_ENV: 'test', PATH: process.env.PATH },
});
callbackSpawnedProcess(p);
p.on('message', message => {
@@ -153,12 +159,7 @@ export function launchServer(callbackSpawnedProcess: (p: childProcess.ChildProce
export async function initTestDb(justBorrow = false, initEntities?: any[]) {
if (process.env.NODE_ENV !== 'test') throw 'NODE_ENV is not a test';
- try {
- const conn = await getConnection();
- await conn.close();
- } catch (e) {}
-
- return await createConnection({
+ const db = new DataSource({
type: 'postgres',
host: config.db.host,
port: config.db.port,
@@ -167,8 +168,12 @@ export async function initTestDb(justBorrow = false, initEntities?: any[]) {
database: config.db.db,
synchronize: true && !justBorrow,
dropSchema: true && !justBorrow,
- entities: initEntities || entities
+ entities: initEntities || entities,
});
+
+ await db.initialize();
+
+ return db;
}
export function startServer(timeout = 30 * 1000): Promise<childProcess.ChildProcess> {
@@ -178,9 +183,9 @@ export function startServer(timeout = 30 * 1000): Promise<childProcess.ChildProc
rej('timeout to start');
}, timeout);
- const p = childProcess.spawn('node', [__dirname + '/../built/index.js'], {
+ const p = childProcess.spawn('node', [_dirname + '/../built/index.js'], {
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
- env: { NODE_ENV: 'test', PATH: process.env.PATH }
+ env: { NODE_ENV: 'test', PATH: process.env.PATH },
});
p.on('error', e => rej(e));
diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json
index 3120851aae..22338a4976 100644
--- a/packages/backend/tsconfig.json
+++ b/packages/backend/tsconfig.json
@@ -25,9 +25,14 @@
"rootDir": "./src",
"baseUrl": "./",
"paths": {
- "@/*": ["./src/*"]
+ "@/*": [
+ "./src/*"
+ ]
},
"outDir": "./built",
+ "types": [
+ "node"
+ ],
"typeRoots": [
"./node_modules/@types",
"./src/@types"
diff --git a/packages/backend/yarn.lock b/packages/backend/yarn.lock
index 5cd71acf9c..5ce87c34eb 100644
--- a/packages/backend/yarn.lock
+++ b/packages/backend/yarn.lock
@@ -12,20 +12,6 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.9.tgz#ca34cb95e1c2dd126863a84465ae8ef66114be99"
integrity sha512-nEUfRiARCcaVo3ny3ZQjURjHQZUo/JkEw7rLlSZy/psWGnvwXFtPcr6jb7Yb41DVW5LTe6KRq9LGleRNsg1Frw==
-"@babel/runtime@^7.16.0":
- version "7.16.3"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5"
- integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==
- dependencies:
- regenerator-runtime "^0.13.4"
-
-"@babel/runtime@^7.6.2":
- version "7.12.13"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.13.tgz#0a21452352b02542db0ffb928ac2d3ca7cb6d66d"
- integrity sha512-8+3UMPBrjFa/6TtKi/7sehPKqfAm4g6K+YQjyyFOLUTxzOngcRZTlAVY8sc2CORJYqdHQY8gRPHmn+qo15rCBw==
- dependencies:
- regenerator-runtime "^0.13.4"
-
"@babel/types@^7.6.1", "@babel/types@^7.9.6":
version "7.13.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80"
@@ -35,68 +21,63 @@
lodash "^4.17.19"
to-fast-properties "^2.0.0"
-"@bull-board/api@3.10.3":
- version "3.10.3"
- resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-3.10.3.tgz#c6aad9f5cfb3acbe02c57e823ee81c1ae575849d"
- integrity sha512-kV6EPwi9j71qBmozvDmtT01j986r4cFqNmBgq7HApYXW0G2U8Brmv0Ut0iMQZRc/X7aA5KYL3qXcEsriFnq+jw==
+"@bull-board/api@3.11.1":
+ version "3.11.1"
+ resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-3.11.1.tgz#98b2c9556f643718bb5bde4a1306e6706af8192e"
+ integrity sha512-ElwX7sM+Ng4ZL9KUsbDubRE+r2hu/gss85OsROeE9bmyfkW14jOJkgr5MKUyjTTgPEeMs1Mw55TgQs2vxoWBiA==
dependencies:
redis-info "^3.0.8"
-"@bull-board/koa@3.10.3":
- version "3.10.3"
- resolved "https://registry.yarnpkg.com/@bull-board/koa/-/koa-3.10.3.tgz#b9f02629f96f056d6a038c3c58fc339d58e55abb"
- integrity sha512-DK8m09MwcRwUR3tz3xI0iSK/Ih2huQ2MAWm8krYjO5deswP2yBaCWE4OtpiULLfVpf8z4zB3Oqa0xNJrKRHTOQ==
+"@bull-board/koa@3.11.1":
+ version "3.11.1"
+ resolved "https://registry.yarnpkg.com/@bull-board/koa/-/koa-3.11.1.tgz#1872aba2c65d116d1183b3003e4a2cb2c1e2fbbf"
+ integrity sha512-F/thrTuC1JWpdBO7DPdKD/wr8c+d7MJGu0sr5ARsT1WXhng7sU7OqBEP/5Y7HhByurjDFXDxcgk/mc78Tmeb/Q==
dependencies:
- "@bull-board/api" "3.10.3"
- "@bull-board/ui" "3.10.3"
- ejs "^3.1.6"
+ "@bull-board/api" "3.11.1"
+ "@bull-board/ui" "3.11.1"
+ ejs "^3.1.7"
koa "^2.13.1"
koa-mount "^4.0.0"
koa-router "^10.0.0"
koa-static "^5.0.0"
koa-views "^7.0.1"
-"@bull-board/ui@3.10.3":
- version "3.10.3"
- resolved "https://registry.yarnpkg.com/@bull-board/ui/-/ui-3.10.3.tgz#b921199d42b32d8ddd9bbf0e35c25be0d64403e9"
- integrity sha512-6zYW3FqySg+4IKEeM1jt/5ixNVBKQjtZLG9W81ADVcHk8YceQ++7URWzDb8nQEct3rEW4bjR6nicVWNXMSN7Lw==
+"@bull-board/ui@3.11.1":
+ version "3.11.1"
+ resolved "https://registry.yarnpkg.com/@bull-board/ui/-/ui-3.11.1.tgz#17a2af5573f31811a543105b9a96249c95e93ce7"
+ integrity sha512-SRrfvxHF/WaBICiAFuWAoAlTvoBYUBmX94oRbSKzVILRFZMe3gs0hN071BFohrn4yOTFHAkWPN7cjMbaqHwCag==
dependencies:
- "@bull-board/api" "3.10.3"
+ "@bull-board/api" "3.11.1"
-"@cspotcode/source-map-consumer@0.8.0":
- version "0.8.0"
- resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b"
- integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==
-
-"@cspotcode/source-map-support@0.7.0":
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5"
- integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==
+"@cspotcode/source-map-support@^0.8.0":
+ version "0.8.1"
+ resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
+ integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
dependencies:
- "@cspotcode/source-map-consumer" "0.8.0"
+ "@jridgewell/trace-mapping" "0.3.9"
"@cto.af/textdecoder@^0.0.0":
version "0.0.0"
resolved "https://registry.yarnpkg.com/@cto.af/textdecoder/-/textdecoder-0.0.0.tgz#e1e8d84c936c30a0f4619971f19ca41941af9fdc"
integrity sha512-sJpx3F5xcVV/9jNYJQtvimo4Vfld/nD3ph+ZWtQzZ03Zo8rJC7QKQTRcIGS13Rcz80DwFNthCWMrd58vpY4ZAQ==
-"@digitalbazaar/http-client@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@digitalbazaar/http-client/-/http-client-1.1.0.tgz#cac383b24ace04b18b919deab773462b03d3d7b0"
- integrity sha512-ks7hqa6hm9NyULdbm9qL6TRS8rADzBw8R0lETvUgvdNXu9H62XG2YqoKRDThtfgWzWxLwRJ3Z2o4ev81dZZbyQ==
+"@digitalbazaar/http-client@^3.2.0":
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/@digitalbazaar/http-client/-/http-client-3.2.0.tgz#b85ea09028c7d0f288f976c852d0a8f3875f0fcf"
+ integrity sha512-NhYXcWE/JDE7AnJikNX7q0S6zNuUPA2NuIoRdUpmvHlarjmRqyr6hIO3Awu2FxlUzbdiI1uzuWrZyB9mD1tTvw==
dependencies:
- esm "^3.2.22"
- ky "^0.25.1"
- ky-universal "^0.8.2"
+ ky "^0.30.0"
+ ky-universal "^0.10.1"
+ undici "^5.2.0"
-"@discordapp/twemoji@13.1.1":
- version "13.1.1"
- resolved "https://registry.yarnpkg.com/@discordapp/twemoji/-/twemoji-13.1.1.tgz#f750d491ffb740eca619fac0c63650c1de7fff91"
- integrity sha512-WDnPjWq/trfCcZk7dzQ2cYH5v5XaIfPzyixJ//O9XKilYYZRVS3p61vFvax5qMwanMMbnNG1iOzeqHKtivO32A==
+"@discordapp/twemoji@14.0.2":
+ version "14.0.2"
+ resolved "https://registry.yarnpkg.com/@discordapp/twemoji/-/twemoji-14.0.2.tgz#50cc19f6f3769dc6b36eb251421b5f5d4629e837"
+ integrity sha512-eYJpFsjViDTYwq3f6v+tRu8iRc+yLAeGrlh6kmNRvvC6rroUE2bMlBfEQ/WNh+2Q1FtSEFXpxzuQPOHzRzbAyA==
dependencies:
fs-extra "^8.0.1"
jsonfile "^5.0.0"
- twemoji-parser "13.1.0"
+ twemoji-parser "14.0.0"
universalify "^0.1.2"
"@elastic/elasticsearch@7.11.0":
@@ -110,19 +91,19 @@
pump "^3.0.0"
secure-json-parse "^2.1.0"
-"@eslint/eslintrc@^1.2.1":
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6"
- integrity sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==
+"@eslint/eslintrc@^1.3.0":
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
+ integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==
dependencies:
ajv "^6.12.4"
debug "^4.3.2"
- espree "^9.3.1"
- globals "^13.9.0"
+ espree "^9.3.2"
+ globals "^13.15.0"
ignore "^5.2.0"
import-fresh "^3.2.1"
js-yaml "^4.1.0"
- minimatch "^3.0.4"
+ minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@gar/promisify@^1.0.1":
@@ -144,6 +125,24 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
+"@jridgewell/resolve-uri@^3.0.3":
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe"
+ integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==
+
+"@jridgewell/sourcemap-codec@^1.4.10":
+ version "1.4.13"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c"
+ integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==
+
+"@jridgewell/trace-mapping@0.3.9":
+ version "0.3.9"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
+ integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.0.3"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+
"@koa/cors@3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@koa/cors/-/cors-3.1.0.tgz#618bb073438cfdbd3ebd0e648a76e33b84f3a3b2"
@@ -234,6 +233,15 @@
mkdirp "^1.0.4"
rimraf "^3.0.2"
+"@peertube/http-signature@1.6.0":
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/@peertube/http-signature/-/http-signature-1.6.0.tgz#22bef028384e6437e8dbd94052ba7b8bd7f7f1ae"
+ integrity sha512-Bx780c7FPYtkV4LgCoaJcXYcKQqaMef2iQR2V2r5klkYkIQWFxbTOpyhKxvVXYIBIFpj5Cb8DGVDAmhkm7aavg==
+ dependencies:
+ assert-plus "^1.0.0"
+ jsprim "^1.2.2"
+ sshpk "^1.14.1"
+
"@redocly/ajv@^8.6.4":
version "8.6.4"
resolved "https://registry.yarnpkg.com/@redocly/ajv/-/ajv-8.6.4.tgz#94053e7a9d4146d1a4feacd3813892873f229a85"
@@ -244,10 +252,10 @@
require-from-string "^2.0.2"
uri-js "^4.2.2"
-"@redocly/openapi-core@1.0.0-beta.93":
- version "1.0.0-beta.93"
- resolved "https://registry.yarnpkg.com/@redocly/openapi-core/-/openapi-core-1.0.0-beta.93.tgz#882db8684598217f621adc7349288229253a0038"
- integrity sha512-xQj7UnjPj3mKtkyRrm+bjzEoyo0CVNjGP4pV6BzQ0vgKf0Jqq7apFC703psyBH+JscYr7NKK1hPQU76ylhFDdg==
+"@redocly/openapi-core@1.0.0-beta.97":
+ version "1.0.0-beta.97"
+ resolved "https://registry.yarnpkg.com/@redocly/openapi-core/-/openapi-core-1.0.0-beta.97.tgz#324ed46e9a9aee4c615be22ee348c53f7bb5f180"
+ integrity sha512-3WW9/6flosJuRtU3GI0Vw39OYFZqqXMDCp5TLa3EjXOb7Nm6AZTWRb3Y+I/+UdNJ/NTszVJkQczoa1t476ekiQ==
dependencies:
"@redocly/ajv" "^8.6.4"
"@types/node" "^14.11.8"
@@ -255,7 +263,7 @@
js-levenshtein "^1.1.6"
js-yaml "^4.1.0"
lodash.isequal "^4.5.0"
- minimatch "^3.0.4"
+ minimatch "^5.0.1"
node-fetch "^2.6.1"
pluralize "^8.0.0"
yaml-ast-parser "0.0.43"
@@ -277,10 +285,10 @@
dependencies:
type-detect "4.0.8"
-"@sinonjs/fake-timers@9.1.1":
- version "9.1.1"
- resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.1.tgz#7b698e0b9d12d93611f06ee143c30ced848e2840"
- integrity sha512-Wp5vwlZ0lOqpSYGKqr53INws9HLkt6JDc/pDZcPf7bchQnrXJMXPns8CXx0hFikMSGSWfvtvvpb2gtMVfkWagA==
+"@sinonjs/fake-timers@9.1.2":
+ version "9.1.2"
+ resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c"
+ integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==
dependencies:
"@sinonjs/commons" "^1.7.0"
@@ -527,6 +535,11 @@
resolved "https://registry.yarnpkg.com/@types/jsonld/-/jsonld-1.5.6.tgz#4396c0b17128abf5773bb68b5453b88fc565b0d4"
integrity sha512-OUcfMjRie5IOrJulUQwVNvV57SOdKcTfBj3pjXNxzXqeOIrY2aGDNGW/Tlp83EQPkz4tCE6YWVrGuc/ZeaAQGg==
+"@types/jsrsasign@10.5.1":
+ version "10.5.1"
+ resolved "https://registry.yarnpkg.com/@types/jsrsasign/-/jsrsasign-10.5.1.tgz#6f9defd46dfcf324b1cff08a06be639858deee3b"
+ integrity sha512-QqM03IXHY6SX835mWdx7Vp8ZOxw/hcnMjGjapUQf+pgFPRyGdjg3jxFsr4p+rolKcdRhptm3mtVQNk4OMhCQcA==
+
"@types/keygrip@*":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72"
@@ -649,10 +662,10 @@
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d"
integrity sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==
-"@types/mocha@9.1.0":
- version "9.1.0"
- resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.0.tgz#baf17ab2cca3fcce2d322ebc30454bff487efad5"
- integrity sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==
+"@types/mocha@9.1.1":
+ version "9.1.1"
+ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4"
+ integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==
"@types/node-fetch@3.0.3":
version "3.0.3"
@@ -666,10 +679,10 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.6.2.tgz#331b7b9f8621c638284787c5559423822fdffc50"
integrity sha512-LSw8TZt12ZudbpHc6EkIyDM3nHVWKYrAvGy6EAJfNfjusbwnThqjqxUKKRwuV3iWYeW/LYMzNgaq3MaLffQ2xA==
-"@types/node@17.0.23":
- version "17.0.23"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.23.tgz#3b41a6e643589ac6442bdbd7a4a3ded62f33f7da"
- integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==
+"@types/node@17.0.41":
+ version "17.0.41"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.41.tgz#1607b2fd3da014ae5d4d1b31bc792a39348dfb9b"
+ integrity sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw==
"@types/node@^14.11.8":
version "14.17.9"
@@ -700,11 +713,6 @@
resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.3.tgz#705bb349e789efa06f43f128cef51240753424cb"
integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==
-"@types/portscanner@2.1.1":
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/@types/portscanner/-/portscanner-2.1.1.tgz#89d5094e16f3d941f20f3889dfa5d3a164b3dd3b"
- integrity sha512-1NsVIbgBKvrqxwtMN0V6CLji1ERwKSI/RWz0J3y++CzSwYNGBStCfpIFgxV3ZwxsDR5PoZqoUWhwraDm+Ztn0Q==
-
"@types/pug@2.0.6":
version "2.0.6"
resolved "https://registry.yarnpkg.com/@types/pug/-/pug-2.0.6.tgz#f830323c88172e66826d0bde413498b61054b5a6"
@@ -777,6 +785,11 @@
dependencies:
htmlparser2 "^6.0.0"
+"@types/semver@7.3.9":
+ version "7.3.9"
+ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.9.tgz#152c6c20a7688c30b967ec1841d31ace569863fc"
+ integrity sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==
+
"@types/serve-static@*":
version "1.13.3"
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.3.tgz#eb7e1c41c4468272557e897e9171ded5e2ded9d1"
@@ -785,10 +798,10 @@
"@types/express-serve-static-core" "*"
"@types/mime" "*"
-"@types/sharp@0.30.1":
- version "0.30.1"
- resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.30.1.tgz#31bd128f2437e8fc31424eb23d8284aa127bfa8d"
- integrity sha512-LxzQsKo2YtvA2DlqACNXmlbLGMVJCSU/HhV4N9RrStClUEf02iN+AakD/zUOpZkbo1OG+lHk2LeqoHedLwln2w==
+"@types/sharp@0.30.2":
+ version "0.30.2"
+ resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.30.2.tgz#df5ff34140b3bad165482e6f3d26b08e42a0503a"
+ integrity sha512-uLCBwjDg/BTcQit0dpNGvkIjvH3wsb8zpaJePCjvONBBSfaKHoxXBIuq1MT8DMQEfk2fKYnpC9QExCgFhkGkMQ==
dependencies:
"@types/node" "*"
@@ -845,85 +858,85 @@
dependencies:
"@types/node" "*"
-"@typescript-eslint/eslint-plugin@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.18.0.tgz#950df411cec65f90d75d6320a03b2c98f6c3af7d"
- integrity sha512-tzrmdGMJI/uii9/V6lurMo4/o+dMTKDH82LkNjhJ3adCW22YQydoRs5MwTiqxGF9CSYxPxQ7EYb4jLNlIs+E+A==
+"@typescript-eslint/eslint-plugin@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.27.1.tgz#fdf59c905354139046b41b3ed95d1609913d0758"
+ integrity sha512-6dM5NKT57ZduNnJfpY81Phe9nc9wolnMCnknb1im6brWi1RYv84nbMS3olJa27B6+irUVV1X/Wb+Am0FjJdGFw==
dependencies:
- "@typescript-eslint/scope-manager" "5.18.0"
- "@typescript-eslint/type-utils" "5.18.0"
- "@typescript-eslint/utils" "5.18.0"
- debug "^4.3.2"
+ "@typescript-eslint/scope-manager" "5.27.1"
+ "@typescript-eslint/type-utils" "5.27.1"
+ "@typescript-eslint/utils" "5.27.1"
+ debug "^4.3.4"
functional-red-black-tree "^1.0.1"
- ignore "^5.1.8"
+ ignore "^5.2.0"
regexpp "^3.2.0"
- semver "^7.3.5"
+ semver "^7.3.7"
tsutils "^3.21.0"
-"@typescript-eslint/parser@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.18.0.tgz#2bcd4ff21df33621df33e942ccb21cb897f004c6"
- integrity sha512-+08nYfurBzSSPndngnHvFw/fniWYJ5ymOrn/63oMIbgomVQOvIDhBoJmYZ9lwQOCnQV9xHGvf88ze3jFGUYooQ==
+"@typescript-eslint/parser@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.27.1.tgz#3a4dcaa67e45e0427b6ca7bb7165122c8b569639"
+ integrity sha512-7Va2ZOkHi5NP+AZwb5ReLgNF6nWLGTeUJfxdkVUAPPSaAdbWNnFZzLZ4EGGmmiCTg+AwlbE1KyUYTBglosSLHQ==
dependencies:
- "@typescript-eslint/scope-manager" "5.18.0"
- "@typescript-eslint/types" "5.18.0"
- "@typescript-eslint/typescript-estree" "5.18.0"
- debug "^4.3.2"
+ "@typescript-eslint/scope-manager" "5.27.1"
+ "@typescript-eslint/types" "5.27.1"
+ "@typescript-eslint/typescript-estree" "5.27.1"
+ debug "^4.3.4"
-"@typescript-eslint/scope-manager@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.18.0.tgz#a7d7b49b973ba8cebf2a3710eefd457ef2fb5505"
- integrity sha512-C0CZML6NyRDj+ZbMqh9FnPscg2PrzSaVQg3IpTmpe0NURMVBXlghGZgMYqBw07YW73i0MCqSDqv2SbywnCS8jQ==
+"@typescript-eslint/scope-manager@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.27.1.tgz#4d1504392d01fe5f76f4a5825991ec78b7b7894d"
+ integrity sha512-fQEOSa/QroWE6fAEg+bJxtRZJTH8NTskggybogHt4H9Da8zd4cJji76gA5SBlR0MgtwF7rebxTbDKB49YUCpAg==
dependencies:
- "@typescript-eslint/types" "5.18.0"
- "@typescript-eslint/visitor-keys" "5.18.0"
+ "@typescript-eslint/types" "5.27.1"
+ "@typescript-eslint/visitor-keys" "5.27.1"
-"@typescript-eslint/type-utils@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.18.0.tgz#62dbfc8478abf36ba94a90ddf10be3cc8e471c74"
- integrity sha512-vcn9/6J5D6jtHxpEJrgK8FhaM8r6J1/ZiNu70ZUJN554Y3D9t3iovi6u7JF8l/e7FcBIxeuTEidZDR70UuCIfA==
+"@typescript-eslint/type-utils@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.27.1.tgz#369f695199f74c1876e395ebea202582eb1d4166"
+ integrity sha512-+UC1vVUWaDHRnC2cQrCJ4QtVjpjjCgjNFpg8b03nERmkHv9JV9X5M19D7UFMd+/G7T/sgFwX2pGmWK38rqyvXw==
dependencies:
- "@typescript-eslint/utils" "5.18.0"
- debug "^4.3.2"
+ "@typescript-eslint/utils" "5.27.1"
+ debug "^4.3.4"
tsutils "^3.21.0"
-"@typescript-eslint/types@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.18.0.tgz#4f0425d85fdb863071680983853c59a62ce9566e"
- integrity sha512-bhV1+XjM+9bHMTmXi46p1Led5NP6iqQcsOxgx7fvk6gGiV48c6IynY0apQb7693twJDsXiVzNXTflhplmaiJaw==
+"@typescript-eslint/types@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.27.1.tgz#34e3e629501349d38be6ae97841298c03a6ffbf1"
+ integrity sha512-LgogNVkBhCTZU/m8XgEYIWICD6m4dmEDbKXESCbqOXfKZxRKeqpiJXQIErv66sdopRKZPo5l32ymNqibYEH/xg==
-"@typescript-eslint/typescript-estree@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.18.0.tgz#6498e5ee69a32e82b6e18689e2f72e4060986474"
- integrity sha512-wa+2VAhOPpZs1bVij9e5gyVu60ReMi/KuOx4LKjGx2Y3XTNUDJgQ+5f77D49pHtqef/klglf+mibuHs9TrPxdQ==
+"@typescript-eslint/typescript-estree@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.27.1.tgz#7621ee78607331821c16fffc21fc7a452d7bc808"
+ integrity sha512-DnZvvq3TAJ5ke+hk0LklvxwYsnXpRdqUY5gaVS0D4raKtbznPz71UJGnPTHEFo0GDxqLOLdMkkmVZjSpET1hFw==
dependencies:
- "@typescript-eslint/types" "5.18.0"
- "@typescript-eslint/visitor-keys" "5.18.0"
- debug "^4.3.2"
- globby "^11.0.4"
+ "@typescript-eslint/types" "5.27.1"
+ "@typescript-eslint/visitor-keys" "5.27.1"
+ debug "^4.3.4"
+ globby "^11.1.0"
is-glob "^4.0.3"
- semver "^7.3.5"
+ semver "^7.3.7"
tsutils "^3.21.0"
-"@typescript-eslint/utils@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.18.0.tgz#27fc84cf95c1a96def0aae31684cb43a37e76855"
- integrity sha512-+hFGWUMMri7OFY26TsOlGa+zgjEy1ssEipxpLjtl4wSll8zy85x0GrUSju/FHdKfVorZPYJLkF3I4XPtnCTewA==
+"@typescript-eslint/utils@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.27.1.tgz#b4678b68a94bc3b85bf08f243812a6868ac5128f"
+ integrity sha512-mZ9WEn1ZLDaVrhRaYgzbkXBkTPghPFsup8zDbbsYTxC5OmqrFE7skkKS/sraVsLP3TcT3Ki5CSyEFBRkLH/H/w==
dependencies:
"@types/json-schema" "^7.0.9"
- "@typescript-eslint/scope-manager" "5.18.0"
- "@typescript-eslint/types" "5.18.0"
- "@typescript-eslint/typescript-estree" "5.18.0"
+ "@typescript-eslint/scope-manager" "5.27.1"
+ "@typescript-eslint/types" "5.27.1"
+ "@typescript-eslint/typescript-estree" "5.27.1"
eslint-scope "^5.1.1"
eslint-utils "^3.0.0"
-"@typescript-eslint/visitor-keys@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.18.0.tgz#c7c07709823804171d569017f3b031ced7253e60"
- integrity sha512-Hf+t+dJsjAKpKSkg3EHvbtEpFFb/1CiOHnvI8bjHgOD4/wAw3gKrA0i94LrbekypiZVanJu3McWJg7rWDMzRTg==
+"@typescript-eslint/visitor-keys@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.27.1.tgz#05a62666f2a89769dac2e6baa48f74e8472983af"
+ integrity sha512-xYs6ffo01nhdJgPieyk7HAOpjhTsx7r/oB9LWEhwAXgwn33tkr+W8DI2ChboqhZlC4q3TC6geDYPoiX8ROqyOQ==
dependencies:
- "@typescript-eslint/types" "5.18.0"
- eslint-visitor-keys "^3.0.0"
+ "@typescript-eslint/types" "5.27.1"
+ eslint-visitor-keys "^3.3.0"
"@ungap/promise-all-settled@1.1.2":
version "1.1.2"
@@ -963,10 +976,10 @@ acorn-globals@^6.0.0:
acorn "^7.1.1"
acorn-walk "^7.1.1"
-acorn-jsx@^5.3.1:
- version "5.3.1"
- resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
- integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==
+acorn-jsx@^5.3.2:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+ integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
acorn-walk@^7.1.1:
version "7.1.1"
@@ -988,11 +1001,16 @@ acorn@^8.4.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c"
integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==
-acorn@^8.5.0, acorn@^8.7.0:
+acorn@^8.5.0:
version "8.7.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
+acorn@^8.7.1:
+ version "8.7.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
+ integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
+
agent-base@6, agent-base@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
@@ -1074,7 +1092,7 @@ ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
-ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+ansi-styles@^4.0.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
@@ -1082,6 +1100,13 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
"@types/color-name" "^1.1.1"
color-convert "^2.0.1"
+ansi-styles@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ dependencies:
+ color-convert "^2.0.1"
+
any-promise@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
@@ -1131,13 +1156,13 @@ archiver-utils@^2.1.0:
normalize-path "^3.0.0"
readable-stream "^2.0.0"
-archiver@5.3.0:
- version "5.3.0"
- resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.0.tgz#dd3e097624481741df626267564f7dd8640a45ba"
- integrity sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg==
+archiver@5.3.1:
+ version "5.3.1"
+ resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.1.tgz#21e92811d6f09ecfce649fbefefe8c79e57cbbb6"
+ integrity sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==
dependencies:
archiver-utils "^2.1.0"
- async "^3.2.0"
+ async "^3.2.3"
buffer-crc32 "^0.2.1"
readable-stream "^3.6.0"
readdir-glob "^1.0.0"
@@ -1227,27 +1252,10 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
-async@0.9.x:
- version "0.9.2"
- resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
- integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=
-
-async@>=0.2.9:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
- integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
-
-async@^2.6.0:
- version "2.6.3"
- resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
- integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
- dependencies:
- lodash "^4.17.14"
-
-async@^3.2.0:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/async/-/async-3.2.1.tgz#d3274ec66d107a47476a4c49136aacdb00665fc8"
- integrity sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg==
+async@>=0.2.9, async@^3.2.3:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9"
+ integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==
asynckit@^0.4.0:
version "0.4.0"
@@ -1266,10 +1274,10 @@ autwh@0.1.0:
dependencies:
oauth "0.9.15"
-aws-sdk@2.1111.0:
- version "2.1111.0"
- resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1111.0.tgz#02b1e5c530ef8140235ee7c48c710bb2dbd7dc84"
- integrity sha512-WRyNcCckzmu1djTAWfR2r+BuI/PbuLrhG3oa+oH39v4NZ4EecYWFL1CoCPlC2kRUML4maSba5T4zlxjcNl7ELQ==
+aws-sdk@2.1152.0:
+ version "2.1152.0"
+ resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1152.0.tgz#73e4fb81b3a9c289234b5d6848bcdb854f169bdf"
+ integrity sha512-Lqwk0bDhm3vzpYb3AAM9VgGHeDpbB8+o7UJnP9R+CO23kJfi/XRpKihAcbyKDD/AUQ+O1LJaUVpvaJYLS9Am7w==
dependencies:
buffer "4.9.2"
events "1.1.1"
@@ -1278,7 +1286,7 @@ aws-sdk@2.1111.0:
querystring "0.2.0"
sax "1.2.1"
url "0.10.3"
- uuid "3.3.2"
+ uuid "8.0.0"
xml2js "0.4.19"
axios@^0.24.0:
@@ -1296,9 +1304,9 @@ babel-walk@3.0.0-canary-5:
"@babel/types" "^7.9.6"
balanced-match@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
- integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+ integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base32.js@0.0.1:
version "0.0.1"
@@ -1327,11 +1335,6 @@ bcryptjs@2.4.3:
resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb"
integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=
-big-integer@^1.6.16:
- version "1.6.48"
- resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
- integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==
-
big-integer@^1.6.17:
version "1.6.51"
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686"
@@ -1397,27 +1400,20 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
-braces@^3.0.1, braces@~3.0.2:
+brace-expansion@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+ integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+ dependencies:
+ balanced-match "^1.0.0"
+
+braces@^3.0.1, braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
-broadcast-channel@4.10.0:
- version "4.10.0"
- resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-4.10.0.tgz#d19fb902df227df40b1b580351713d30c302d198"
- integrity sha512-hOUh312XyHk6JTVyX9cyXaH1UYs+2gHVtnW16oQAu9FL7ALcXGXc/YoJWqlkV8vUn14URQPMmRi4A9q4UrwVEQ==
- dependencies:
- "@babel/runtime" "^7.16.0"
- detect-node "^2.1.0"
- microseconds "0.2.0"
- nano-time "1.0.0"
- oblivious-set "1.0.0"
- p-queue "6.6.2"
- rimraf "3.0.2"
- unload "2.3.1"
-
browser-process-hrtime@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
@@ -1490,10 +1486,10 @@ bufferutil@^4.0.1:
dependencies:
node-gyp-build "~3.7.0"
-bull@4.8.1:
- version "4.8.1"
- resolved "https://registry.yarnpkg.com/bull/-/bull-4.8.1.tgz#83daaefc3118876450b21d7a02bc11ea28a2440e"
- integrity sha512-ojH5AfOchKQsQwwE+thViS1pMpvREGC+Ov1+3HXsQqn5Q27ZSGkgMriMqc6c9J9rvQ/+D732pZE+TN1+2LRWVg==
+bull@4.8.3:
+ version "4.8.3"
+ resolved "https://registry.yarnpkg.com/bull/-/bull-4.8.3.tgz#4ab67029fee1183dcb7185895b20dc08c02d6bf2"
+ integrity sha512-oOHr+KTLu3JM5V9TXsg18/1xyVQceoYCFiGrXZOpu9abZn3W3vXJtMBrwB6Yvl/RxSKVVBpoa25RF/ya3750qg==
dependencies:
cron-parser "^4.2.1"
debuglog "^1.0.0"
@@ -1586,11 +1582,6 @@ cacheable-request@^7.0.2:
normalize-url "^6.0.1"
responselike "^2.0.0"
-cafy@15.2.1:
- version "15.2.1"
- resolved "https://registry.yarnpkg.com/cafy/-/cafy-15.2.1.tgz#5a55eaeb721c604c7dca652f3d555c392e5f995a"
- integrity sha512-g2zOmFb63p6XcZ/zeMWKYP8YKQYNWnhJmi6K71Ql4EAFTAay31xF0PBPtdBCCfQ0fiETgWTMxKtySAVI/Od6aQ==
-
call-bind@^1.0.0, call-bind@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
@@ -1678,7 +1669,7 @@ chalk@^4.0.0, chalk@^4.1.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
-chalk@^4.1.2:
+chalk@^4.0.2, chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@@ -1720,7 +1711,7 @@ cheerio@0.22.0:
lodash.reject "^4.4.0"
lodash.some "^4.4.0"
-chokidar@3.5.3, chokidar@^3.3.1, chokidar@^3.5.2:
+chokidar@3.5.3, chokidar@^3.3.1, chokidar@^3.5.3:
version "3.3.1"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.1.tgz#c84e5b3d18d9a4d77558fef466b1bf16bbeb3450"
integrity sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==
@@ -1859,10 +1850,10 @@ color-support@^1.1.2:
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
-color@^4.2.1:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/color/-/color-4.2.1.tgz#498aee5fce7fc982606c8875cab080ac0547c884"
- integrity sha512-MFJr0uY4RvTQUKvPq7dh9grVOTYSFeXja2mBXioCGjnjJoXrAp9jJ1NQTDR73c9nwBSAQiNKloKl5zq9WB9UPw==
+color@^4.0.1:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a"
+ integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==
dependencies:
color-convert "^2.0.1"
color-string "^1.9.0"
@@ -1884,10 +1875,10 @@ commander@^2.19.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
-commander@^8.2.0:
- version "8.3.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
- integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
+commander@^9.0.0:
+ version "9.2.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-9.2.0.tgz#6e21014b2ed90d8b7c9647230d8b7a94a4a419a9"
+ integrity sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==
compress-commons@^4.1.0:
version "4.1.1"
@@ -2079,11 +2070,6 @@ dashdash@^1.12.0:
dependencies:
assert-plus "^1.0.0"
-data-uri-to-buffer@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636"
- integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==
-
data-uri-to-buffer@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz#b5db46aea50f6176428ac05b73be39a57701a64b"
@@ -2124,6 +2110,13 @@ debug@4.3.3:
dependencies:
ms "2.1.2"
+debug@4.3.4, debug@^4.3.3, debug@^4.3.4:
+ version "4.3.4"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+ integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+ dependencies:
+ ms "2.1.2"
+
debug@^3.1.0, debug@^3.2.7:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
@@ -2145,13 +2138,6 @@ debug@^4.3.2:
dependencies:
ms "2.1.2"
-debug@^4.3.3:
- version "4.3.4"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
- integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
- dependencies:
- ms "2.1.2"
-
debuglog@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
@@ -2261,26 +2247,16 @@ destroy@^1.0.4:
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
-detect-file@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
- integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=
+detect-libc@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
+ integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
detect-libc@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.0.tgz#c528bc09bc6d1aa30149228240917c225448f204"
integrity sha512-S55LzUl8HUav8l9E2PBTlC5PAJrHK7tkM+XXFGD+fbsbkTzhCpG6K05LxJcUOEWzMa4v6ptcMZ9s3fOdJDu0Zw==
-detect-libc@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd"
- integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==
-
-detect-node@2.1.0, detect-node@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
- integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
-
dicer@0.2.5:
version "0.2.5"
resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f"
@@ -2470,12 +2446,12 @@ ee-first@1.1.1:
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
-ejs@^3.1.6:
- version "3.1.6"
- resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.6.tgz#5bfd0a0689743bb5268b3550cceeebbc1702822a"
- integrity sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==
+ejs@^3.1.7:
+ version "3.1.8"
+ resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b"
+ integrity sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==
dependencies:
- jake "^10.6.1"
+ jake "^10.8.5"
emoji-regex@^8.0.0:
version "8.0.0"
@@ -2700,22 +2676,17 @@ eslint-visitor-keys@^2.0.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
-eslint-visitor-keys@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz#e32e99c6cdc2eb063f204eda5db67bfe58bb4186"
- integrity sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q==
-
eslint-visitor-keys@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
-eslint@8.13.0:
- version "8.13.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.13.0.tgz#6fcea43b6811e655410f5626cfcf328016badcd7"
- integrity sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==
+eslint@8.17.0:
+ version "8.17.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.17.0.tgz#1cfc4b6b6912f77d24b874ca1506b0fe09328c21"
+ integrity sha512-gq0m0BTJfci60Fz4nczYxNAlED+sMcihltndR8t9t1evnU/azx53x3t2UHXC/uRjcbvRw/XctpaNygSTcQD+Iw==
dependencies:
- "@eslint/eslintrc" "^1.2.1"
+ "@eslint/eslintrc" "^1.3.0"
"@humanwhocodes/config-array" "^0.9.2"
ajv "^6.10.0"
chalk "^4.0.0"
@@ -2726,14 +2697,14 @@ eslint@8.13.0:
eslint-scope "^7.1.1"
eslint-utils "^3.0.0"
eslint-visitor-keys "^3.3.0"
- espree "^9.3.1"
+ espree "^9.3.2"
esquery "^1.4.0"
esutils "^2.0.2"
fast-deep-equal "^3.1.3"
file-entry-cache "^6.0.1"
functional-red-black-tree "^1.0.1"
glob-parent "^6.0.1"
- globals "^13.6.0"
+ globals "^13.15.0"
ignore "^5.2.0"
import-fresh "^3.0.0"
imurmurhash "^0.1.4"
@@ -2742,7 +2713,7 @@ eslint@8.13.0:
json-stable-stringify-without-jsonify "^1.0.1"
levn "^0.4.1"
lodash.merge "^4.6.2"
- minimatch "^3.0.4"
+ minimatch "^3.1.2"
natural-compare "^1.4.0"
optionator "^0.9.1"
regexpp "^3.2.0"
@@ -2751,18 +2722,13 @@ eslint@8.13.0:
text-table "^0.2.0"
v8-compile-cache "^2.0.3"
-esm@^3.2.22:
- version "3.2.25"
- resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
- integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
-
-espree@^9.3.1:
- version "9.3.1"
- resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd"
- integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==
+espree@^9.3.2:
+ version "9.3.2"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.2.tgz#f58f77bd334731182801ced3380a8cc859091596"
+ integrity sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==
dependencies:
- acorn "^8.7.0"
- acorn-jsx "^5.3.1"
+ acorn "^8.7.1"
+ acorn-jsx "^5.3.2"
eslint-visitor-keys "^3.3.0"
esprima@^4.0.1:
@@ -2809,7 +2775,7 @@ event-target-shim@^5.0.0:
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
-eventemitter3@^4.0.4, eventemitter3@^4.0.7:
+eventemitter3@^4.0.7:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
@@ -2844,13 +2810,6 @@ expand-template@^2.0.3:
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
-expand-tilde@^2.0.0, expand-tilde@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502"
- integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=
- dependencies:
- homedir-polyfill "^1.0.1"
-
ext@^1.1.2:
version "1.4.0"
resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244"
@@ -2897,6 +2856,17 @@ fast-glob@^3.1.1:
micromatch "^4.0.2"
picomatch "^2.2.1"
+fast-glob@^3.2.9:
+ version "3.2.11"
+ resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
+ integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
+ dependencies:
+ "@nodelib/fs.stat" "^2.0.2"
+ "@nodelib/fs.walk" "^1.2.3"
+ glob-parent "^5.1.2"
+ merge2 "^1.3.0"
+ micromatch "^4.0.4"
+
fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
@@ -2926,11 +2896,6 @@ feed@4.2.2:
dependencies:
xml-js "^1.6.11"
-fetch-blob@^2.1.1:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c"
- integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow==
-
fetch-blob@^3.1.2, fetch-blob@^3.1.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.1.4.tgz#e8c6567f80ad7fc22fd302e7dcb72bafde9c1717"
@@ -2946,21 +2911,21 @@ file-entry-cache@^6.0.1:
dependencies:
flat-cache "^3.0.4"
-file-type@17.1.1:
- version "17.1.1"
- resolved "https://registry.yarnpkg.com/file-type/-/file-type-17.1.1.tgz#24c59bc663df0c0c181b31dfacde25e06431afbe"
- integrity sha512-heRUMZHby2Qj6wZAA3YHeMlRmZNQTcb6VxctkGmM+mcM6ROQKvHpr7SS6EgdfEhH+s25LDshBjvPx/Ecm+bOVQ==
+file-type@17.1.2:
+ version "17.1.2"
+ resolved "https://registry.yarnpkg.com/file-type/-/file-type-17.1.2.tgz#9257437a64e0c3623f70d9f27430522d978b1384"
+ integrity sha512-3thBUSfa9YEUEGO/NAAiQGvjujZxZiJTF6xNwyDn6kB0NcEtwMn5ttkGG9jGwm/Nt/t8U1bpBNqyBNZCz4F4ig==
dependencies:
readable-web-to-node-stream "^3.0.2"
strtok3 "^7.0.0-alpha.7"
token-types "^5.0.0-alpha.2"
filelist@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b"
- integrity sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ==
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.3.tgz#448607750376484932f67ef1b9ff07386b036c83"
+ integrity sha512-LwjCsruLWQULGYKy7TX0OPtrL9kLpojOFKc5VCTxdFTV7w5zbsgqVKfnkKG7Qgjtq50gKfO56hJv88OfcGb70Q==
dependencies:
- minimatch "^3.0.4"
+ minimatch "^5.0.1"
fill-range@^7.0.1:
version "7.0.1"
@@ -2969,14 +2934,6 @@ fill-range@^7.0.1:
dependencies:
to-regex-range "^5.0.1"
-find-node-modules@^2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/find-node-modules/-/find-node-modules-2.1.2.tgz#57565a3455baf671b835bc6b2134a9b938b9c53c"
- integrity sha512-x+3P4mbtRPlSiVE1Qco0Z4YLU8WFiFcuWTf3m75OV9Uzcfs2Bg+O9N+r/K0AnmINBW06KpfqKwYJbFlFq4qNug==
- dependencies:
- findup-sync "^4.0.0"
- merge "^2.1.0"
-
find-up@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
@@ -3000,16 +2957,6 @@ find-up@^4.1.0:
locate-path "^5.0.0"
path-exists "^4.0.0"
-findup-sync@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-4.0.0.tgz#956c9cdde804052b881b428512905c4a5f2cdef0"
- integrity sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==
- dependencies:
- detect-file "^1.0.0"
- is-glob "^4.0.0"
- micromatch "^4.0.2"
- resolve-dir "^1.0.1"
-
flat-cache@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
@@ -3210,7 +3157,7 @@ github-from-package@0.0.0:
resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=
-glob-parent@^5.1.0, glob-parent@~5.1.0:
+glob-parent@^5.1.0, glob-parent@^5.1.2, glob-parent@~5.1.0:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
@@ -3248,37 +3195,10 @@ glob@^7.1.3, glob@^7.1.4:
once "^1.3.0"
path-is-absolute "^1.0.0"
-global-modules@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea"
- integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==
- dependencies:
- global-prefix "^1.0.1"
- is-windows "^1.0.1"
- resolve-dir "^1.0.0"
-
-global-prefix@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe"
- integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=
- dependencies:
- expand-tilde "^2.0.2"
- homedir-polyfill "^1.0.1"
- ini "^1.3.4"
- is-windows "^1.0.1"
- which "^1.2.14"
-
-globals@^13.6.0:
- version "13.7.0"
- resolved "https://registry.yarnpkg.com/globals/-/globals-13.7.0.tgz#aed3bcefd80ad3ec0f0be2cf0c895110c0591795"
- integrity sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA==
- dependencies:
- type-fest "^0.20.2"
-
-globals@^13.9.0:
- version "13.9.0"
- resolved "https://registry.yarnpkg.com/globals/-/globals-13.9.0.tgz#4bf2bf635b334a173fb1daf7c5e6b218ecdc06cb"
- integrity sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==
+globals@^13.15.0:
+ version "13.15.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-13.15.0.tgz#38113218c907d2f7e98658af246cef8b77e90bac"
+ integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==
dependencies:
type-fest "^0.20.2"
@@ -3294,6 +3214,18 @@ globby@^11.0.4:
merge2 "^1.3.0"
slash "^3.0.0"
+globby@^11.1.0:
+ version "11.1.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
+ integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
+ dependencies:
+ array-union "^2.1.0"
+ dir-glob "^3.0.1"
+ fast-glob "^3.2.9"
+ ignore "^5.2.0"
+ merge2 "^1.4.1"
+ slash "^3.0.0"
+
got@11.5.1:
version "11.5.1"
resolved "https://registry.yarnpkg.com/got/-/got-11.5.1.tgz#bf098a270fe80b3fb88ffd5a043a59ebb0a391db"
@@ -3311,10 +3243,10 @@ got@11.5.1:
p-cancelable "^2.0.0"
responselike "^2.0.0"
-got@12.0.3:
- version "12.0.3"
- resolved "https://registry.yarnpkg.com/got/-/got-12.0.3.tgz#c7314daab26d42039e624adbf98f6d442e5de749"
- integrity sha512-hmdcXi/S0gcAtDg4P8j/rM7+j3o1Aq6bXhjxkDhRY2ipe7PHpvx/14DgTY2czHOLaGeU8VRvRecidwfu9qdFug==
+got@12.1.0:
+ version "12.1.0"
+ resolved "https://registry.yarnpkg.com/got/-/got-12.1.0.tgz#099f3815305c682be4fd6b0ee0726d8e4c6b0af4"
+ integrity sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==
dependencies:
"@sindresorhus/is" "^4.6.0"
"@szmarczak/http-timer" "^5.0.1"
@@ -3345,11 +3277,6 @@ graceful-fs@^4.2.6:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
-growl@1.10.5:
- version "1.10.5"
- resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
- integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
-
has-bigints@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
@@ -3404,13 +3331,6 @@ highlight.js@^10.7.1:
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.2.tgz#89319b861edc66c48854ed1e6da21ea89f847360"
integrity sha512-oFLl873u4usRM9K63j4ME9u3etNF0PLiJhSQ8rdfuL51Wn3zkD6drf9ZW0dOzjnZI22YYG24z30JcmfCZjMgYg==
-homedir-polyfill@^1.0.1:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8"
- integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==
- dependencies:
- parse-passwd "^1.0.0"
-
hpagent@0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/hpagent/-/hpagent-0.1.2.tgz#cab39c66d4df2d4377dbd212295d878deb9bdaa9"
@@ -3507,15 +3427,6 @@ http-proxy-agent@^5.0.0:
agent-base "6"
debug "4"
-http-signature@1.3.6:
- version "1.3.6"
- resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9"
- integrity sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==
- dependencies:
- assert-plus "^1.0.0"
- jsprim "^2.0.2"
- sshpk "^1.14.1"
-
http2-wrapper@^1.0.0-beta.5.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d"
@@ -3600,11 +3511,6 @@ ignore@^5.1.4:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
-ignore@^5.1.8:
- version "5.1.9"
- resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.9.tgz#9ec1a5cbe8e1446ec60d4420060d43aa6e7382fb"
- integrity sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==
-
ignore@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
@@ -3700,10 +3606,10 @@ ip-address@^7.1.0:
jsbn "1.1.0"
sprintf-js "1.1.2"
-ip-cidr@3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/ip-cidr/-/ip-cidr-3.0.4.tgz#a915c47e00f47ea8d5f8ed662ea6161471c44375"
- integrity sha512-pKNiqmBlTvEkhaLAa3+FOmYSY0/jjADVxxjA3NbujZZTT8mjLI90Q+6mwg6kd0fNm0RuAOkWJ1u1a/ETmlrPNQ==
+ip-cidr@3.0.10:
+ version "3.0.10"
+ resolved "https://registry.yarnpkg.com/ip-cidr/-/ip-cidr-3.0.10.tgz#e1a039705196d84b43858f81a243fd70def9cefc"
+ integrity sha512-PXSsrRYirsuaCI1qBVyVXRLUIpNzxm76eHd3UvN5NXTMUG85GWGZpr6P+70mimc5e7Nfh/tShmjk0oSywErMWg==
dependencies:
ip-address "^7.1.0"
jsbn "^1.1.0"
@@ -3848,13 +3754,6 @@ is-negative-zero@^2.0.1:
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==
-is-number-like@^1.0.3:
- version "1.0.8"
- resolved "https://registry.yarnpkg.com/is-number-like/-/is-number-like-1.0.8.tgz#2e129620b50891042e44e9bbbb30593e75cfbbe3"
- integrity sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==
- dependencies:
- lodash.isfinite "^3.3.2"
-
is-number-object@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0"
@@ -3962,11 +3861,6 @@ is-whitespace@^0.3.0:
resolved "https://registry.yarnpkg.com/is-whitespace/-/is-whitespace-0.3.0.tgz#1639ecb1be036aec69a54cbb401cfbed7114ab7f"
integrity sha1-Fjnssb4DauxppUy7QBz77XEUq38=
-is-windows@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
- integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
-
isarray@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
@@ -3982,13 +3876,13 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
-jake@^10.6.1:
- version "10.8.2"
- resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.2.tgz#ebc9de8558160a66d82d0eadc6a2e58fbc500a7b"
- integrity sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==
+jake@^10.8.5:
+ version "10.8.5"
+ resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46"
+ integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==
dependencies:
- async "0.9.x"
- chalk "^2.4.2"
+ async "^3.2.3"
+ chalk "^4.0.2"
filelist "^1.0.1"
minimatch "^3.0.4"
@@ -4117,7 +4011,7 @@ json5-loader@4.0.1:
loader-utils "^2.0.0"
schema-utils "^3.0.0"
-json5@2.2.1:
+json5@2.2.1, json5@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
@@ -4152,30 +4046,30 @@ jsonfile@^5.0.0:
optionalDependencies:
graceful-fs "^4.1.6"
-jsonld@5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/jsonld/-/jsonld-5.2.0.tgz#d1e8af38a334cb95edf0f2ae4e2b58baf8d2b5a9"
- integrity sha512-JymgT6Xzk5CHEmHuEyvoTNviEPxv6ihLWSPu1gFdtjSAyM6cFqNrv02yS/SIur3BBIkCf0HjizRc24d8/FfQKw==
+jsonld@6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/jsonld/-/jsonld-6.0.0.tgz#560a8a871dce72aba5d4c6b08356438d863d62fb"
+ integrity sha512-1SkN2RXhMCTCSkX+bzHvr9ycM2HTmjWyV41hn2xG7k6BqlCgRjw0zHmuqfphjBRPqi1gKMIqgBCe/0RZMcWrAA==
dependencies:
- "@digitalbazaar/http-client" "^1.1.0"
+ "@digitalbazaar/http-client" "^3.2.0"
canonicalize "^1.0.1"
lru-cache "^6.0.0"
rdf-canonize "^3.0.0"
-jsprim@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d"
- integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==
+jsprim@^1.2.2:
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb"
+ integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==
dependencies:
assert-plus "1.0.0"
extsprintf "1.3.0"
json-schema "0.4.0"
verror "1.10.0"
-jsrsasign@8.0.20:
- version "8.0.20"
- resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-8.0.20.tgz#37d8029c9d8f794d8ac8d8998bce319921491f11"
- integrity sha512-JTXt9+nqdynIB8wFsS6e8ffHhIjilhywXwdaEVHSj9OVmwldG2H0EoCqkQ+KXkm2tVqREfH/HEmklY4k1/6Rcg==
+jsrsasign@10.5.24:
+ version "10.5.24"
+ resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-10.5.24.tgz#2d159e1756b2268682c6eb5e147184e33e946b1c"
+ integrity sha512-0i/UHRgJZifp/YmoXHyNQXUY4eKWiSd7YxuD7oKEw9mlqgr51hg9lZQw2nlEDvwHDh7pyj6ZjYlxldlW27xb/Q==
jstransformer@1.0.0:
version "1.0.0"
@@ -4368,18 +4262,18 @@ koa@2.13.4, koa@^2.13.1:
type-is "^1.6.16"
vary "^1.1.2"
-ky-universal@^0.8.2:
- version "0.8.2"
- resolved "https://registry.yarnpkg.com/ky-universal/-/ky-universal-0.8.2.tgz#edc398d54cf495d7d6830aa1ab69559a3cc7f824"
- integrity sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ==
+ky-universal@^0.10.1:
+ version "0.10.1"
+ resolved "https://registry.yarnpkg.com/ky-universal/-/ky-universal-0.10.1.tgz#778881e098f6e3c52a87b382d9acca54d22bb0d3"
+ integrity sha512-r8909k+ELKZAxhVA5c440x22hqw5XcMRwLRbgpPQk4JHy3/ddJnvzcnSo5Ww3HdKdNeS3Y8dBgcIYyVahMa46g==
dependencies:
abort-controller "^3.0.0"
- node-fetch "3.0.0-beta.9"
+ node-fetch "^3.2.2"
-ky@^0.25.1:
- version "0.25.1"
- resolved "https://registry.yarnpkg.com/ky/-/ky-0.25.1.tgz#0df0bd872a9cc57e31acd5dbc1443547c881bfbc"
- integrity sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA==
+ky@^0.30.0:
+ version "0.30.0"
+ resolved "https://registry.yarnpkg.com/ky/-/ky-0.30.0.tgz#a3d293e4f6c4604a9a4694eceb6ce30e73d27d64"
+ integrity sha512-X/u76z4JtDVq10u1JA5UQfatPxgPaVDMYTrgHyiTpGN2z4TMEJkIHsoSBBSg9SWZEIXTKsi9kHgiQ9o3Y/4yog==
lazystream@^1.0.0:
version "1.0.1"
@@ -4485,11 +4379,6 @@ lodash.isequal@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
-lodash.isfinite@^3.3.2:
- version "3.3.2"
- resolved "https://registry.yarnpkg.com/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz#fb89b65a9a80281833f0b7478b3a5104f898ebb3"
- integrity sha1-+4m2WpqAKBgz8LdHizpRBPiY67M=
-
lodash.isplainobject@^4.0.6:
version "4.0.6"
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
@@ -4535,7 +4424,7 @@ lodash.union@^4.6.0:
resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=
-lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.21:
+lodash@^4.17.11, lodash@^4.17.19, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -4573,11 +4462,6 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
-lru-cache@^7.4.0:
- version "7.8.1"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.8.1.tgz#68ee3f4807a57d2ba185b7fd90827d5c21ce82bb"
- integrity sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg==
-
luxon@^1.28.0:
version "1.28.0"
resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf"
@@ -4625,27 +4509,22 @@ merge-stream@^2.0.0:
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
-merge2@^1.3.0:
+merge2@^1.3.0, merge2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
-merge@^2.1.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/merge/-/merge-2.1.1.tgz#59ef4bf7e0b3e879186436e8481c06a6c162ca98"
- integrity sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==
-
methods@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
-mfm-js@0.21.0:
- version "0.21.0"
- resolved "https://registry.yarnpkg.com/mfm-js/-/mfm-js-0.21.0.tgz#954cc6e7071700b0b1872c78a90bada10be7f772"
- integrity sha512-nyQXaipa7rmAw9ER9uYigMvGcdCwhSv93abZBwccnSnPOc1W3S/WW0+sN28g3YSmlHDCA0i2q9aAFc9EgOi5KA==
+mfm-js@0.22.1:
+ version "0.22.1"
+ resolved "https://registry.yarnpkg.com/mfm-js/-/mfm-js-0.22.1.tgz#ad5f0b95cc903ca5a5e414e2edf64ac4648dc8c2"
+ integrity sha512-UV5zvDKlWPpBFeABhyCzuOTJ3RwrNrmVpJ+zz/dFX6D/ntEywljgxkfsLamcy0ZSwUAr0O+WQxGHvAwyxUgsAQ==
dependencies:
- twemoji-parser "13.1.x"
+ twemoji-parser "14.0.x"
micromatch@^4.0.0, micromatch@^4.0.2:
version "4.0.2"
@@ -4655,10 +4534,13 @@ micromatch@^4.0.0, micromatch@^4.0.2:
braces "^3.0.1"
picomatch "^2.0.5"
-microseconds@0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39"
- integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==
+micromatch@^4.0.4:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+ integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+ dependencies:
+ braces "^3.0.2"
+ picomatch "^2.3.1"
mime-db@1.44.0:
version "1.44.0"
@@ -4704,21 +4586,14 @@ minimalistic-assert@^1.0.0:
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
-minimatch@4.2.1:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4"
- integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==
- dependencies:
- brace-expansion "^1.1.7"
-
-minimatch@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
- integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+minimatch@5.0.1, minimatch@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b"
+ integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==
dependencies:
- brace-expansion "^1.1.7"
+ brace-expansion "^2.0.1"
-minimatch@^3.1.2:
+minimatch@^3.0.4, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
@@ -4815,40 +4690,38 @@ mkdirp@^1.0.3, mkdirp@^1.0.4, mkdirp@~1.0.3:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
-mocha@9.2.2:
- version "9.2.2"
- resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9"
- integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==
+mocha@10.0.0:
+ version "10.0.0"
+ resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.0.0.tgz#205447d8993ec755335c4b13deba3d3a13c4def9"
+ integrity sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==
dependencies:
"@ungap/promise-all-settled" "1.1.2"
ansi-colors "4.1.1"
browser-stdout "1.3.1"
chokidar "3.5.3"
- debug "4.3.3"
+ debug "4.3.4"
diff "5.0.0"
escape-string-regexp "4.0.0"
find-up "5.0.0"
glob "7.2.0"
- growl "1.10.5"
he "1.2.0"
js-yaml "4.1.0"
log-symbols "4.1.0"
- minimatch "4.2.1"
+ minimatch "5.0.1"
ms "2.1.3"
- nanoid "3.3.1"
+ nanoid "3.3.3"
serialize-javascript "6.0.0"
strip-json-comments "3.1.1"
supports-color "8.1.1"
- which "2.0.2"
- workerpool "6.2.0"
+ workerpool "6.2.1"
yargs "16.2.0"
yargs-parser "20.2.4"
yargs-unparser "2.0.0"
moment@^2.22.2:
- version "2.24.0"
- resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
- integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
+ version "2.29.3"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.3.tgz#edd47411c322413999f7a5940d526de183c031f3"
+ integrity sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==
ms@2.0.0:
version "2.0.0"
@@ -4899,10 +4772,10 @@ multer@1.4.4:
type-is "^1.6.4"
xtend "^4.0.0"
-mylas@^2.1.4:
- version "2.1.5"
- resolved "https://registry.yarnpkg.com/mylas/-/mylas-2.1.5.tgz#7ccf41ec5a93ab2d63fc3678abf1942c0e7bdeb1"
- integrity sha512-7ZyrJux1lipSR45IxDvWz7zJOXWTazTFCqD4/p8XBF4O+mtJwf7QpMWTH+jE4lV9O2I38xcpS0KTIp7GwhUTmA==
+mylas@^2.1.9:
+ version "2.1.9"
+ resolved "https://registry.yarnpkg.com/mylas/-/mylas-2.1.9.tgz#8329626f95c0ce522ca7d3c192eca6221d172cdc"
+ integrity sha512-pa+cQvmhoM8zzgitPYZErmDt9EdTNVnXsH1XFjMeM4TyG4FFcgxrvK1+jwabVFwUOEDaSWuXBMjg43kqt/Ydlg==
mz@^2.4.0, mz@^2.7.0:
version "2.7.0"
@@ -4918,14 +4791,12 @@ nan@^2.14.2, nan@^2.15.0:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee"
integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==
-nano-time@1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef"
- integrity sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8=
- dependencies:
- big-integer "^1.6.16"
+nanoid@3.3.3:
+ version "3.3.3"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25"
+ integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==
-nanoid@3.3.1, nanoid@^3.1.30:
+nanoid@^3.1.30:
version "3.3.1"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35"
integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==
@@ -4976,7 +4847,7 @@ node-abi@^3.3.0:
dependencies:
semver "^7.3.5"
-node-addon-api@^4.3.0:
+node-addon-api@^4.2.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f"
integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==
@@ -4995,18 +4866,10 @@ node-fetch@*:
fetch-blob "^3.1.4"
formdata-polyfill "^4.0.10"
-node-fetch@3.0.0-beta.9:
- version "3.0.0-beta.9"
- resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.0.0-beta.9.tgz#0a7554cfb824380dd6812864389923c783c80d9b"
- integrity sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg==
- dependencies:
- data-uri-to-buffer "^3.0.1"
- fetch-blob "^2.1.1"
-
-node-fetch@3.2.3:
- version "3.2.3"
- resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.2.3.tgz#a03c9cc2044d21d1a021566bd52f080f333719a6"
- integrity sha512-AXP18u4pidSZ1xYXRDPY/8jdv3RAozIt/WLNR/MBGZAz+xjtlr90RvCnsvHQRiXyWliZF/CpytExp32UU67/SA==
+node-fetch@3.2.6, node-fetch@^3.2.2:
+ version "3.2.6"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.2.6.tgz#6d4627181697a9d9674aae0d61548e0d629b31b9"
+ integrity sha512-LAy/HZnLADOVkVPubaxHDft29booGglPFDr2Hw0J1AercRh01UiVFm++KMDnJeH9sHgNB4hsXPii7Sgym/sTbw==
dependencies:
data-uri-to-buffer "^4.0.0"
fetch-blob "^3.1.4"
@@ -5045,10 +4908,10 @@ node-gyp@^8.4.1:
tar "^6.1.2"
which "^2.0.2"
-nodemailer@6.7.3:
- version "6.7.3"
- resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.7.3.tgz#b73f9a81b9c8fa8acb4ea14b608f5e725ea8e018"
- integrity sha512-KUdDsspqx89sD4UUyUKzdlUOper3hRkDVkrKh/89G+d9WKsU5ox51NWS4tB1XR5dPUdR4SP0E3molyEfOvSa3g==
+nodemailer@6.7.5:
+ version "6.7.5"
+ resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.7.5.tgz#b30b1566f5fa2249f7bd49ced4c58bec6b25915e"
+ integrity sha512-6VtMpwhsrixq1HDYSBBHvW0GwiWawE75dS3oal48VqRhUvKJNnKnJo2RI/bCVQubj1vgrgscMNW4DHaD6xtMCg==
nofilter@^2.0.3:
version "2.0.3"
@@ -5175,11 +5038,6 @@ object.values@^1.1.5:
define-properties "^1.1.3"
es-abstract "^1.19.1"
-oblivious-set@1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566"
- integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==
-
on-finished@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
@@ -5327,14 +5185,6 @@ p-map@^4.0.0:
dependencies:
aggregate-error "^3.0.0"
-p-queue@6.6.2:
- version "6.6.2"
- resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426"
- integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==
- dependencies:
- eventemitter3 "^4.0.4"
- p-timeout "^3.2.0"
-
p-timeout@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe"
@@ -5364,11 +5214,6 @@ parent-module@^1.0.0:
dependencies:
callsites "^3.0.0"
-parse-passwd@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
- integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=
-
parse-srcset@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1"
@@ -5507,11 +5352,23 @@ picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.0.7, picomatch@^2.2.1:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
+picomatch@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+ integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
pify@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
+plimit-lit@^1.2.6:
+ version "1.2.6"
+ resolved "https://registry.yarnpkg.com/plimit-lit/-/plimit-lit-1.2.6.tgz#8c1336f26a042b6e9f1acc665be5eee4c2a55fb3"
+ integrity sha512-EuVnKyDeFgr58aidKf2G7DI41r23bxphlvBKAZ8e8dT9of0Ez2g9w6JbJGUP1YBNC2yG9+ZCCbjLj4yS1P5Gzw==
+ dependencies:
+ queue-lit "^1.2.7"
+
pluralize@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1"
@@ -5527,14 +5384,6 @@ pngjs@^5.0.0:
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb"
integrity sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==
-portscanner@2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/portscanner/-/portscanner-2.2.0.tgz#6059189b3efa0965c9d96a56b958eb9508411cf1"
- integrity sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==
- dependencies:
- async "^2.6.0"
- is-number-like "^1.0.3"
-
postcss@^8.3.11:
version "8.3.11"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.11.tgz#c3beca7ea811cd5e1c4a3ec6d2e7599ef1f8f858"
@@ -5566,10 +5415,10 @@ postgres-interval@^1.1.0:
dependencies:
xtend "^4.0.0"
-prebuild-install@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.1.tgz#c10075727c318efe72412f333e0ef625beaf3870"
- integrity sha512-QBSab31WqkyxpnMWQxubYAHR5S9B2+r81ucocew34Fkl98FhvKIF50jIJnNOBmAZfyNV7vE5T6gd3hTVWgY6tg==
+prebuild-install@^7.0.0:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.0.tgz#991b6ac16c81591ba40a6d5de93fb33673ac1370"
+ integrity sha512-CNcMgI1xBypOyGqjp3wOc8AAo1nMhZS3Cwd3iHIxOdAUbb+YxdNuM4Z5iIrZ8RLvOsf3F3bl7b7xGq6DjQoNYA==
dependencies:
detect-libc "^2.0.0"
expand-template "^2.0.3"
@@ -5828,6 +5677,11 @@ querystring@0.2.0:
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
+queue-lit@^1.2.7:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/queue-lit/-/queue-lit-1.2.7.tgz#69081656c9e7b81f09770bb2de6aa007f1a90763"
+ integrity sha512-K/rTdggORRcmf3+c89ijPlgJ/ldGP4oBj6Sm7VcTup4B2clf03Jo8QaXTnMst4EEQwkUbOZFN4frKocq2I85gw==
+
quick-lru@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
@@ -6006,11 +5860,6 @@ reflect-metadata@0.1.13, reflect-metadata@^0.1.13:
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08"
integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
-regenerator-runtime@^0.13.4:
- version "0.13.7"
- resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
- integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
-
regexpp@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
@@ -6053,14 +5902,6 @@ resolve-alpn@^1.2.0:
resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9"
integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==
-resolve-dir@^1.0.0, resolve-dir@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43"
- integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=
- dependencies:
- expand-tilde "^2.0.0"
- global-modules "^1.0.0"
-
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
@@ -6115,7 +5956,7 @@ rimraf@2:
dependencies:
glob "^7.1.3"
-rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2:
+rimraf@^3.0.0, rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
@@ -6213,12 +6054,12 @@ seedrandom@3.0.5:
resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7"
integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==
-semver@7.3.6:
- version "7.3.6"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.6.tgz#5d73886fb9c0c6602e79440b97165c29581cbb2b"
- integrity sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==
+semver@7.3.7, semver@^7.3.7:
+ version "7.3.7"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
+ integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
dependencies:
- lru-cache "^7.4.0"
+ lru-cache "^6.0.0"
semver@^5.6.0:
version "5.7.1"
@@ -6274,17 +6115,17 @@ sha.js@^2.4.11:
inherits "^2.0.1"
safe-buffer "^5.0.1"
-sharp@0.30.3:
- version "0.30.3"
- resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.30.3.tgz#315a1817423a4d1cde5119a21c99c234a7a6fb37"
- integrity sha512-rjpfJFK58ZOFSG8sxYSo3/JQb4ej095HjXp9X7gVu7gEn1aqSG8TCW29h/Rr31+PXrFADo1H/vKfw0uhMQWFtg==
+sharp@0.29.3:
+ version "0.29.3"
+ resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.29.3.tgz#0da183d626094c974516a48fab9b3e4ba92eb5c2"
+ integrity sha512-fKWUuOw77E4nhpyzCCJR1ayrttHoFHBT2U/kR/qEMRhvPEcluG4BKj324+SCO1e84+knXHwhJ1HHJGnUt4ElGA==
dependencies:
- color "^4.2.1"
- detect-libc "^2.0.1"
- node-addon-api "^4.3.0"
- prebuild-install "^7.0.1"
+ color "^4.0.1"
+ detect-libc "^1.0.3"
+ node-addon-api "^4.2.0"
+ prebuild-install "^7.0.0"
semver "^7.3.5"
- simple-get "^4.0.1"
+ simple-get "^4.0.0"
tar-fs "^2.1.1"
tunnel-agent "^0.6.0"
@@ -6329,7 +6170,7 @@ simple-concat@^1.0.0:
resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
-simple-get@^4.0.0, simple-get@^4.0.1:
+simple-get@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543"
integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==
@@ -6588,16 +6429,17 @@ style-loader@3.3.1:
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575"
integrity sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==
-summaly@2.5.0:
- version "2.5.0"
- resolved "https://registry.yarnpkg.com/summaly/-/summaly-2.5.0.tgz#ec5af6e84857efcb6c844d896e83569e64a923ea"
- integrity sha512-IzvO2s7yj/PUyH42qWjVjSPpIiPlgTRWGh33t4cIZKOqPQJ2INo7e83hXhHFr4hXTb3JRcIdCuM1ELjlrujiUQ==
+summaly@2.5.1:
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/summaly/-/summaly-2.5.1.tgz#742fe6631987f84ad2e95d2b0f7902ec57e0f6b3"
+ integrity sha512-WWvl7rLs3wm61Xc2JqgTbSuqtIOmGqKte+rkbnxe6ISy4089lQ+7F2ajooQNee6PWHl9kZ27SDd1ZMoL3/6R4A==
dependencies:
cheerio "0.22.0"
debug "4.3.3"
escape-regexp "0.0.1"
got "11.5.1"
html-entities "2.3.2"
+ iconv-lite "0.6.3"
jschardet "3.0.0"
koa "2.13.4"
private-ip "2.3.3"
@@ -6642,10 +6484,10 @@ syslog-pro@1.0.0:
dependencies:
moment "^2.22.2"
-systeminformation@5.11.9:
- version "5.11.9"
- resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.11.9.tgz#95f2334e739dd224178948a2afaced7d9abfdf9d"
- integrity sha512-eeMtL9UJFR/LYG+2rpeAgZ0Va4ojlNQTkYiQH/xbbPwDjDMsaetj3Pkc+C1aH5G8mav6HvDY8kI4Vl4noksSkA==
+systeminformation@5.11.16:
+ version "5.11.16"
+ resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.11.16.tgz#5f6fda2447fafe204bd2ab543475f1ffa8c14a85"
+ integrity sha512-/a1VfP9WELKLT330yhAHJ4lWCXRYynel1kMMHKc/qdzCgDt3BIcMlo+3tKcTiRHFefjV3fz4AvqMx7dGO/72zw==
tapable@^2.2.0:
version "2.2.0"
@@ -6803,22 +6645,22 @@ trace-redirect@1.0.6:
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9"
integrity sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=
-ts-loader@9.2.8:
- version "9.2.8"
- resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.2.8.tgz#e89aa32fa829c5cad0a1d023d6b3adecd51d5a48"
- integrity sha512-gxSak7IHUuRtwKf3FIPSW1VpZcqF9+MBrHOvBp9cjHh+525SjtCIJKVGjRKIAfxBwDGDGCFF00rTfzB1quxdSw==
+ts-loader@9.3.0:
+ version "9.3.0"
+ resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.3.0.tgz#980f4dbfb60e517179e15e10ed98e454b132159f"
+ integrity sha512-2kLLAdAD+FCKijvGKi9sS0OzoqxLCF3CxHpok7rVgCZ5UldRzH0TkbwG9XECKjBzHsAewntC5oDaI/FwKzEUog==
dependencies:
chalk "^4.1.0"
enhanced-resolve "^5.0.0"
micromatch "^4.0.0"
semver "^7.3.4"
-ts-node@10.7.0:
- version "10.7.0"
- resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5"
- integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==
+ts-node@10.8.1:
+ version "10.8.1"
+ resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.8.1.tgz#ea2bd3459011b52699d7e88daa55a45a1af4f066"
+ integrity sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g==
dependencies:
- "@cspotcode/source-map-support" "0.7.0"
+ "@cspotcode/source-map-support" "^0.8.0"
"@tsconfig/node10" "^1.0.7"
"@tsconfig/node12" "^1.0.7"
"@tsconfig/node14" "^1.0.0"
@@ -6829,22 +6671,31 @@ ts-node@10.7.0:
create-require "^1.1.0"
diff "^4.0.1"
make-error "^1.1.1"
- v8-compile-cache-lib "^3.0.0"
+ v8-compile-cache-lib "^3.0.1"
yn "3.1.1"
-tsc-alias@1.4.1:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/tsc-alias/-/tsc-alias-1.4.1.tgz#6a6075dd94267d9befdad1431f410bd0b8819805"
- integrity sha512-nHTR8qvM/LiYI8Fx6UrzAQXRngAuE2PEK+n9uXmQY6fN+oLZhweNFkCLbyxKDmlLfYnclSuaR+dSuvRd7FUu8Q==
+tsc-alias@1.6.9:
+ version "1.6.9"
+ resolved "https://registry.yarnpkg.com/tsc-alias/-/tsc-alias-1.6.9.tgz#d04d95124b95ad8eea55e52d45cf65a744c26baa"
+ integrity sha512-5lv5uAHn0cgxY1XfpXIdquUSz2xXq3ryQyNtxC6DYH7YT5rt/W+9Gsft2uyLFTh+ozk4qU8iCSP3VemjT69xlQ==
dependencies:
- chokidar "^3.5.2"
- commander "^8.2.0"
- find-node-modules "^2.1.2"
+ chokidar "^3.5.3"
+ commander "^9.0.0"
globby "^11.0.4"
- mylas "^2.1.4"
+ mylas "^2.1.9"
normalize-path "^3.0.0"
+ plimit-lit "^1.2.6"
+
+tsconfig-paths@4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.0.0.tgz#1082f5d99fd127b72397eef4809e4dd06d229b64"
+ integrity sha512-SLBg2GBKlR6bVtMgJJlud/o3waplKtL7skmLkExomIiaAtLGtVsoXIqP3SYdjbcH9lq/KVv7pMZeCBpLYOit6Q==
+ dependencies:
+ json5 "^2.2.1"
+ minimist "^1.2.6"
+ strip-bom "^3.0.0"
-tsconfig-paths@3.14.1, tsconfig-paths@^3.14.1:
+tsconfig-paths@^3.14.1:
version "3.14.1"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a"
integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==
@@ -6888,12 +6739,7 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
-twemoji-parser@13.1.0, twemoji-parser@13.1.x:
- version "13.1.0"
- resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-13.1.0.tgz#65e7e449c59258791b22ac0b37077349127e3ea4"
- integrity sha512-AQOzLJpYlpWMy8n+0ATyKKZzWlZBJN+G0C+5lhX7Ftc2PeEVdUU/7ns2Pn2vVje26AIZ/OHwFoUbdv6YYD/wGg==
-
-twemoji-parser@14.0.0:
+twemoji-parser@14.0.0, twemoji-parser@14.0.x:
version "14.0.0"
resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-14.0.0.tgz#13dabcb6d3a261d9efbf58a1666b182033bf2b62"
integrity sha512-9DUOTGLOWs0pFWnh1p6NF+C3CkQ96PWmEFwhOVmT3WbecRC+68AIqpsnJXygfkFcp4aXbOp8Dwbhh/HQgvoRxA==
@@ -6952,10 +6798,10 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
-typeorm@0.3.5:
- version "0.3.5"
- resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.5.tgz#8fe50d517de5ec6f4b38856ea0f180e4a60cf7e4"
- integrity sha512-KL4c8nQqouHaXs4m1J3xh7oXWqX4+A9poExbceLxBRtlavpJQYqiSnqt3JYGpy7Tl9vD5DG5DrmZrSslTkkW5Q==
+typeorm@0.3.6:
+ version "0.3.6"
+ resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.6.tgz#65203443a1b684bb746785913fe2b0877aa991c0"
+ integrity sha512-DRqgfqcelMiGgWSMbBmVoJNFN2nPNA3EeY2gC324ndr2DZoGRTb9ILtp2oGVGnlA+cu5zgQ6it5oqKFNkte7Aw==
dependencies:
"@sqltools/formatter" "^1.2.2"
app-root-path "^3.0.0"
@@ -6975,10 +6821,10 @@ typeorm@0.3.5:
xml2js "^0.4.23"
yargs "^17.3.1"
-typescript@4.6.3:
- version "4.6.3"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c"
- integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==
+typescript@4.7.3:
+ version "4.7.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.3.tgz#8364b502d5257b540f9de4c40be84c98e23a129d"
+ integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==
ulid@2.3.0:
version "2.3.0"
@@ -6995,6 +6841,11 @@ unbox-primitive@^1.0.1:
has-symbols "^1.0.2"
which-boxed-primitive "^1.0.2"
+undici@^5.2.0:
+ version "5.4.0"
+ resolved "https://registry.yarnpkg.com/undici/-/undici-5.4.0.tgz#c474fae02743d4788b96118d46008a24195024d2"
+ integrity sha512-A1SRXysDg7J+mVP46jF+9cKANw0kptqSFZ8tGyL+HBiv0K1spjxPX8Z4EGu+Eu6pjClJUBdnUPlxrOafR668/g==
+
unique-filename@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230"
@@ -7014,14 +6865,6 @@ universalify@^0.1.0, universalify@^0.1.2:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
-unload@2.3.1:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/unload/-/unload-2.3.1.tgz#9d16862d372a5ce5cb630ad1309c2fd6e35dacfe"
- integrity sha512-MUZEiDqvAN9AIDRbbBnVYVvfcR6DrjCqeU2YQMmliFZl9uaBUjTkhuDQkBiyAy8ad5bx1TXVbqZ3gg7namsWjA==
- dependencies:
- "@babel/runtime" "^7.6.2"
- detect-node "2.1.0"
-
unpipe@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
@@ -7075,25 +6918,25 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
-uuid@3.3.2:
- version "3.3.2"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
- integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
-
uuid@7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==
+uuid@8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c"
+ integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==
+
uuid@8.3.2, uuid@^8.3.0, uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
-v8-compile-cache-lib@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz#0582bcb1c74f3a2ee46487ceecf372e46bce53e8"
- integrity sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==
+v8-compile-cache-lib@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
+ integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
v8-compile-cache@^2.0.3:
version "2.2.0"
@@ -7133,10 +6976,10 @@ w3c-xmlserializer@^3.0.0:
dependencies:
xml-name-validator "^4.0.0"
-web-push@3.4.5:
- version "3.4.5"
- resolved "https://registry.yarnpkg.com/web-push/-/web-push-3.4.5.tgz#f94074ff150538872c7183e4d8881c8305920cf1"
- integrity sha512-2njbTqZ6Q7ZqqK14YpK1GGmaZs3NmuGYF5b7abCXulUIWFSlSYcZ3NBJQRFcMiQDceD7vQknb8FUuvI1F7Qe/g==
+web-push@3.5.0:
+ version "3.5.0"
+ resolved "https://registry.yarnpkg.com/web-push/-/web-push-3.5.0.tgz#4576533746052eda3bd50414b54a1b0a21eeaeae"
+ integrity sha512-JC0V9hzKTqlDYJ+LTZUXtW7B175qwwaqzbbMSWDxHWxZvd3xY0C2rcotMGDavub2nAAFw+sXTsqR65/KY2A5AQ==
dependencies:
asn1.js "^5.3.0"
http_ece "1.1.0"
@@ -7216,20 +7059,20 @@ which-module@^2.0.0:
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
-which@2.0.2, which@^2.0.1, which@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
- integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
- dependencies:
- isexe "^2.0.0"
-
-which@^1.1.1, which@^1.2.14:
+which@^1.1.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
dependencies:
isexe "^2.0.0"
+which@^2.0.1, which@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+ integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+ dependencies:
+ isexe "^2.0.0"
+
wide-align@^1.1.0:
version "1.1.3"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
@@ -7259,10 +7102,10 @@ word-wrap@^1.2.3, word-wrap@~1.2.3:
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
-workerpool@6.2.0:
- version "6.2.0"
- resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b"
- integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==
+workerpool@6.2.1:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
+ integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
wrap-ansi@^6.2.0:
version "6.2.0"
@@ -7287,20 +7130,20 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
-ws@8.5.0:
- version "8.5.0"
- resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f"
- integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==
+ws@8.8.0:
+ version "8.8.0"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769"
+ integrity sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==
ws@^8.2.3:
version "8.4.2"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.4.2.tgz#18e749868d8439f2268368829042894b6907aa0b"
integrity sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==
-xev@2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/xev/-/xev-2.0.1.tgz#24484173a22115bc8a990ef5d4d5129695b827a7"
- integrity sha512-icDf9M67bDge0F2qf02WKZq+s7mMO/SbPv67ZQPym6JThLEOdlWWLdB7VTVgRJp3ekgaiVItCAyH6aoKCPvfIA==
+xev@3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/xev/-/xev-3.0.2.tgz#3f4080bd8bed0d3479c674050e3696da98d22a4d"
+ integrity sha512-8kxuH95iMXzHZj+fwqfA4UrPcYOy6bGIgfWzo9Ji23JoEc30ge/Z++Ubkiuy8c0+M64nXmmxrmJ7C8wnuBhluw==
xml-js@^1.6.11:
version "1.6.11"
diff --git a/packages/client/.eslintrc.js b/packages/client/.eslintrc.js
index a6e23e5171..10f0e5a9cb 100644
--- a/packages/client/.eslintrc.js
+++ b/packages/client/.eslintrc.js
@@ -1,68 +1,85 @@
module.exports = {
root: true,
env: {
- "node": false
+ 'node': false,
},
- parser: "vue-eslint-parser",
+ parser: 'vue-eslint-parser',
parserOptions: {
- "parser": "@typescript-eslint/parser",
+ 'parser': '@typescript-eslint/parser',
tsconfigRootDir: __dirname,
- //project: ['./tsconfig.json'],
+ project: ['./tsconfig.json'],
+ extraFileExtensions: ['.vue'],
},
extends: [
- //"../shared/.eslintrc.js",
- "plugin:vue/vue3-recommended"
+ '../shared/.eslintrc.js',
+ 'plugin:vue/vue3-recommended',
],
rules: {
+ '@typescript-eslint/no-empty-interface': [
+ 'error',
+ {
+ 'allowSingleExtends': true,
+ },
+ ],
// window ã®ç¦æ­¢ç†ç”±: グローãƒãƒ«ã‚¹ã‚³ãƒ¼ãƒ—ã¨è¡çªã—ã€äºˆæœŸã›ã¬çµæžœã‚’æ‹›ããŸã‚
// data ã®ç¦æ­¢ç†ç”±: 抽象的ã™ãŽã‚‹ãŸã‚
// e ã®ç¦æ­¢ç†ç”±: error ã‚„ event ãªã©ã€è¤‡æ•°ã®ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã®é ­æ–‡å­—ã§ã‚り分ã‹ã‚Šã«ãã„ãŸã‚
- "id-denylist": ["error", "window", "data", "e"],
+ 'id-denylist': ['error', 'window', 'data', 'e'],
'eqeqeq': ['error', 'always', { 'null': 'ignore' }],
- "no-shadow": ["warn"],
- "vue/attributes-order": ["error", {
- "alphabetical": false
+ 'no-shadow': ['warn'],
+ 'vue/attributes-order': ['error', {
+ 'alphabetical': false,
}],
- "vue/no-use-v-if-with-v-for": ["error", {
- "allowUsingIterationVar": false
+ 'vue/no-use-v-if-with-v-for': ['error', {
+ 'allowUsingIterationVar': false,
}],
- "vue/no-ref-as-operand": "error",
- "vue/no-multi-spaces": ["error", {
- "ignoreProperties": false
+ 'vue/no-ref-as-operand': 'error',
+ 'vue/no-multi-spaces': ['error', {
+ 'ignoreProperties': false,
}],
- "vue/no-v-html": "error",
- "vue/order-in-components": "error",
- "vue/html-indent": ["warn", "tab", {
- "attribute": 1,
- "baseIndent": 0,
- "closeBracket": 0,
- "alignAttributesVertically": true,
- "ignores": []
+ 'vue/no-v-html': 'error',
+ 'vue/order-in-components': 'error',
+ 'vue/html-indent': ['warn', 'tab', {
+ 'attribute': 1,
+ 'baseIndent': 0,
+ 'closeBracket': 0,
+ 'alignAttributesVertically': true,
+ 'ignores': [],
}],
- "vue/html-closing-bracket-spacing": ["warn", {
- "startTag": "never",
- "endTag": "never",
- "selfClosingTag": "never"
+ 'vue/html-closing-bracket-spacing': ['warn', {
+ 'startTag': 'never',
+ 'endTag': 'never',
+ 'selfClosingTag': 'never',
}],
- "vue/multi-word-component-names": "warn",
- "vue/require-v-for-key": "warn",
- "vue/no-unused-components": "warn",
- "vue/valid-v-for": "warn",
- "vue/return-in-computed-property": "warn",
- "vue/no-setup-props-destructure": "warn",
- "vue/max-attributes-per-line": "off",
- "vue/html-self-closing": "off",
- "vue/singleline-html-element-content-newline": "off",
+ 'vue/multi-word-component-names': 'warn',
+ 'vue/require-v-for-key': 'warn',
+ 'vue/no-unused-components': 'warn',
+ 'vue/valid-v-for': 'warn',
+ 'vue/return-in-computed-property': 'warn',
+ 'vue/no-setup-props-destructure': 'warn',
+ 'vue/max-attributes-per-line': 'off',
+ 'vue/html-self-closing': 'off',
+ 'vue/singleline-html-element-content-newline': 'off',
},
globals: {
- "require": false,
- "_DEV_": false,
- "_LANGS_": false,
- "_VERSION_": false,
- "_ENV_": false,
- "_PERF_PREFIX_": false,
- "_DATA_TRANSFER_DRIVE_FILE_": false,
- "_DATA_TRANSFER_DRIVE_FOLDER_": false,
- "_DATA_TRANSFER_DECK_COLUMN_": false
- }
-}
+ // Node.js
+ 'module': false,
+ 'require': false,
+ '__dirname': false,
+
+ // Vue
+ '$$': false,
+ '$ref': false,
+ '$computed': false,
+
+ // Misskey
+ '_DEV_': false,
+ '_LANGS_': false,
+ '_VERSION_': false,
+ '_ENV_': false,
+ '_PERF_PREFIX_': false,
+ '_DATA_TRANSFER_DRIVE_FILE_': false,
+ '_DATA_TRANSFER_DRIVE_FOLDER_': false,
+ '_DATA_TRANSFER_DECK_COLUMN_': false,
+ },
+};
diff --git a/packages/client/@types/theme.d.ts b/packages/client/@types/theme.d.ts
new file mode 100644
index 0000000000..67f724a9aa
--- /dev/null
+++ b/packages/client/@types/theme.d.ts
@@ -0,0 +1,7 @@
+declare module '@/themes/*.json5' {
+ import { Theme } from "@/scripts/theme";
+
+ const theme: Theme;
+
+ export default theme;
+}
diff --git a/packages/client/package.json b/packages/client/package.json
index 9de500f3ab..83c8086e23 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -1,80 +1,52 @@
{
"private": true,
"scripts": {
- "watch": "webpack --watch",
- "build": "webpack",
- "lint": "eslint --quiet 'src/**/*.{ts,vue}'"
+ "watch": "vite build --watch --mode development",
+ "build": "vite build",
+ "lint": "eslint --quiet \"src/**/*.{ts,vue}\""
},
"resolutions": {
"chokidar": "^3.3.1",
"lodash": "^4.17.21"
},
"dependencies": {
- "@discordapp/twemoji": "13.1.1",
+ "@discordapp/twemoji": "14.0.2",
"@fortawesome/fontawesome-free": "6.1.1",
+ "@rollup/plugin-alias": "3.1.9",
+ "@rollup/plugin-json": "4.1.0",
"@syuilo/aiscript": "0.11.1",
- "@types/escape-regexp": "0.0.1",
- "@types/glob": "7.2.0",
- "@types/gulp": "4.0.9",
- "@types/gulp-rename": "2.0.1",
- "@types/is-url": "1.2.30",
- "@types/katex": "0.14.0",
- "@types/matter-js": "0.17.7",
- "@types/mocha": "9.1.0",
- "@types/oauth": "0.9.1",
- "@types/parse5": "6.0.3",
- "@types/punycode": "2.1.0",
- "@types/qrcode": "1.4.2",
- "@types/random-seed": "0.3.3",
- "@types/seedrandom": "3.0.2",
- "@types/throttle-debounce": "2.1.0",
- "@types/tinycolor2": "1.4.3",
- "@types/uuid": "8.3.4",
- "@types/webpack": "5.28.0",
- "@types/webpack-stream": "3.2.12",
- "@types/websocket": "1.0.5",
- "@types/ws": "8.5.3",
- "@typescript-eslint/parser": "5.18.0",
- "@vue/compiler-sfc": "3.2.31",
+ "@vitejs/plugin-vue": "2.3.3",
+ "@vue/compiler-sfc": "3.2.37",
"abort-controller": "3.0.0",
"autobind-decorator": "2.4.0",
"autosize": "5.0.1",
"autwh": "0.1.0",
"blurhash": "1.1.5",
- "broadcast-channel": "4.10.0",
- "chart.js": "3.7.1",
+ "broadcast-channel": "4.13.0",
+ "browser-image-resizer": "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.2",
+ "chart.js": "3.8.0",
"chartjs-adapter-date-fns": "2.0.0",
- "chartjs-plugin-gradient": "0.2.2",
+ "chartjs-plugin-gradient": "0.5.0",
"chartjs-plugin-zoom": "1.2.1",
"compare-versions": "4.1.3",
"content-disposition": "0.5.4",
- "css-loader": "6.7.1",
- "cssnano": "5.1.7",
+ "cropperjs": "2.0.0-beta",
"date-fns": "2.28.0",
"escape-regexp": "0.0.1",
- "eslint": "8.13.0",
- "eslint-plugin-vue": "8.6.0",
"eventemitter3": "4.0.7",
"feed": "4.2.2",
- "glob": "7.2.0",
"idb-keyval": "6.1.0",
"insert-text-at-cursor": "0.3.0",
- "ip-cidr": "3.0.4",
"json5": "2.2.1",
- "json5-loader": "4.0.1",
- "katex": "0.15.3",
+ "katex": "0.15.6",
"matter-js": "0.18.0",
- "mfm-js": "0.21.0",
+ "mfm-js": "0.22.1",
"misskey-js": "0.0.14",
- "mocha": "9.2.2",
+ "mocha": "10.0.0",
"ms": "2.1.3",
"nested-property": "4.0.0",
- "parse5": "6.0.1",
- "photoswipe": "5.2.4",
- "portscanner": "2.2.0",
- "postcss": "8.4.12",
- "postcss-loader": "6.2.1",
- "prismjs": "1.27.0",
+ "photoswipe": "5.2.7",
+ "prismjs": "1.28.0",
"private-ip": "2.3.3",
"promise-limit": "2.7.0",
"pug": "3.0.2",
@@ -84,43 +56,58 @@
"random-seed": "0.3.0",
"reflect-metadata": "0.1.13",
"rndstr": "1.0.0",
+ "rollup": "2.75.6",
"s-age": "1.1.2",
- "sass": "1.50.0",
- "sass-loader": "12.6.0",
+ "sass": "1.52.3",
"seedrandom": "3.0.5",
"strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0",
- "style-loader": "3.3.1",
"syuilo-password-strength": "0.0.1",
"textarea-caret": "3.1.0",
- "three": "0.139.2",
- "throttle-debounce": "4.0.0",
+ "three": "0.141.0",
+ "throttle-debounce": "5.0.0",
"tinycolor2": "1.4.2",
- "ts-loader": "9.2.8",
- "tsc-alias": "1.5.0",
- "tsconfig-paths": "3.14.1",
+ "tsc-alias": "1.6.9",
+ "tsconfig-paths": "4.0.0",
"twemoji-parser": "14.0.0",
- "typescript": "4.6.3",
+ "typescript": "4.7.3",
"uuid": "8.3.2",
"v-debounce": "0.1.2",
"vanilla-tilt": "1.7.2",
- "vue": "3.2.31",
- "vue-loader": "17.0.0",
+ "vite": "2.9.10",
+ "vue": "3.2.37",
"vue-prism-editor": "2.0.0-alpha.2",
- "vue-router": "4.0.14",
- "vue-style-loader": "4.1.3",
- "vue-svg-loader": "0.17.0-beta.2",
+ "vue-router": "4.0.16",
"vuedraggable": "4.0.1",
- "webpack": "5.72.0",
- "webpack-cli": "4.9.2",
"websocket": "1.0.34",
- "ws": "8.5.0"
+ "ws": "8.8.0"
},
"devDependencies": {
- "@typescript-eslint/eslint-plugin": "5.18.0",
+ "@types/escape-regexp": "0.0.1",
+ "@types/glob": "7.2.0",
+ "@types/gulp": "4.0.9",
+ "@types/gulp-rename": "2.0.1",
+ "@types/is-url": "1.2.30",
+ "@types/katex": "0.14.0",
+ "@types/matter-js": "0.17.7",
+ "@types/mocha": "9.1.1",
+ "@types/oauth": "0.9.1",
+ "@types/punycode": "2.1.0",
+ "@types/qrcode": "1.4.2",
+ "@types/random-seed": "0.3.3",
+ "@types/seedrandom": "3.0.2",
+ "@types/throttle-debounce": "5.0.0",
+ "@types/tinycolor2": "1.4.3",
+ "@types/uuid": "8.3.4",
+ "@types/websocket": "1.0.5",
+ "@types/ws": "8.5.3",
+ "@typescript-eslint/eslint-plugin": "5.27.1",
+ "@typescript-eslint/parser": "5.27.1",
"cross-env": "7.0.3",
- "cypress": "9.5.3",
+ "cypress": "10.0.3",
+ "eslint": "8.17.0",
"eslint-plugin-import": "2.26.0",
+ "eslint-plugin-vue": "9.1.0",
"start-server-and-test": "1.14.0"
}
}
diff --git a/packages/client/src/account.ts b/packages/client/src/account.ts
index f4dcab319c..ce4af61f18 100644
--- a/packages/client/src/account.ts
+++ b/packages/client/src/account.ts
@@ -1,5 +1,5 @@
import { del, get, set } from '@/scripts/idb-proxy';
-import { reactive } from 'vue';
+import { defineAsyncComponent, reactive } from 'vue';
import * as misskey from 'misskey-js';
import { apiUrl } from '@/config';
import { waiting, api, popup, popupMenu, success, alert } from '@/os';
@@ -11,10 +11,10 @@ import { i18n } from './i18n';
type Account = misskey.entities.MeDetailed;
-const data = localStorage.getItem('account');
+const accountData = localStorage.getItem('account');
// TODO: 外部ã‹ã‚‰ã¯readonlyã«
-export const $i = data ? reactive(JSON.parse(data) as Account) : null;
+export const $i = accountData ? reactive(JSON.parse(accountData) as Account) : null;
export const iAmModerator = $i != null && ($i.isAdmin || $i.isModerator);
@@ -52,7 +52,7 @@ export async function signout() {
return Promise.all(registrations.map(registration => registration.unregister()));
});
}
- } catch (e) {}
+ } catch (err) {}
//#endregion
document.cookie = `igi=; path=/`;
@@ -104,8 +104,8 @@ function fetchAccount(token: string): Promise<Account> {
});
}
-export function updateAccount(data) {
- for (const [key, value] of Object.entries(data)) {
+export function updateAccount(accountData) {
+ for (const [key, value] of Object.entries(accountData)) {
$i[key] = value;
}
localStorage.setItem('account', JSON.stringify($i));
@@ -141,7 +141,7 @@ export async function openAccountMenu(opts: {
onChoose?: (account: misskey.entities.UserDetailed) => void;
}, ev: MouseEvent) {
function showSigninDialog() {
- popup(import('@/components/signin-dialog.vue'), {}, {
+ popup(defineAsyncComponent(() => import('@/components/signin-dialog.vue')), {}, {
done: res => {
addAccount(res.id, res.i);
success();
@@ -150,7 +150,7 @@ export async function openAccountMenu(opts: {
}
function createAccount() {
- popup(import('@/components/signup-dialog.vue'), {}, {
+ popup(defineAsyncComponent(() => import('@/components/signup-dialog.vue')), {}, {
done: res => {
addAccount(res.id, res.i);
switchAccountWithToken(res.i);
diff --git a/packages/client/src/components/abuse-report-window.vue b/packages/client/src/components/abuse-report-window.vue
index f2cb369802..5114349620 100644
--- a/packages/client/src/components/abuse-report-window.vue
+++ b/packages/client/src/components/abuse-report-window.vue
@@ -37,7 +37,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'closed'): void;
+ (ev: 'closed'): void;
}>();
const window = ref<InstanceType<typeof XWindow>>();
diff --git a/packages/client/src/components/abuse-report.vue b/packages/client/src/components/abuse-report.vue
index b67cef209b..a947406f88 100644
--- a/packages/client/src/components/abuse-report.vue
+++ b/packages/client/src/components/abuse-report.vue
@@ -2,7 +2,7 @@
<div class="bcekxzvu _card _gap">
<div class="_content target">
<MkAvatar class="avatar" :user="report.targetUser" :show-indicator="true"/>
- <MkA class="info" :to="userPage(report.targetUser)" v-user-preview="report.targetUserId">
+ <MkA v-user-preview="report.targetUserId" class="info" :to="userPage(report.targetUser)">
<MkUserName class="name" :user="report.targetUser"/>
<MkAcct class="acct" :user="report.targetUser" style="display: block;"/>
</MkA>
@@ -43,20 +43,20 @@ export default defineComponent({
MkSwitch,
},
- emits: ['resolved'],
-
props: {
report: {
type: Object,
required: true,
}
- }
+ },
+
+ emits: ['resolved'],
data() {
return {
forward: this.report.forwarded,
};
- }
+ },
methods: {
acct,
diff --git a/packages/client/src/components/analog-clock.vue b/packages/client/src/components/analog-clock.vue
index 59b8e97304..18dd1e3f41 100644
--- a/packages/client/src/components/analog-clock.vue
+++ b/packages/client/src/components/analog-clock.vue
@@ -42,7 +42,7 @@
<script lang="ts" setup>
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
-import * as tinycolor from 'tinycolor2';
+import tinycolor from 'tinycolor2';
withDefaults(defineProps<{
thickness: number;
diff --git a/packages/client/src/components/autocomplete.vue b/packages/client/src/components/autocomplete.vue
index adeac4e050..1e4a4506f7 100644
--- a/packages/client/src/components/autocomplete.vue
+++ b/packages/client/src/components/autocomplete.vue
@@ -131,8 +131,8 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'done', v: { type: string; value: any }): void;
- (e: 'closed'): void;
+ (event: 'done', value: { type: string; value: any }): void;
+ (event: 'closed'): void;
}>();
const suggests = ref<Element>();
@@ -152,7 +152,7 @@ function complete(type: string, value: any) {
emit('closed');
if (type === 'emoji') {
let recents = defaultStore.state.recentlyUsedEmojis;
- recents = recents.filter((e: any) => e !== value);
+ recents = recents.filter((emoji: any) => emoji !== value);
recents.unshift(value);
defaultStore.set('recentlyUsedEmojis', recents.splice(0, 32));
}
@@ -232,7 +232,7 @@ function exec() {
} else if (props.type === 'emoji') {
if (!props.q || props.q === '') {
// 最近使ã£ãŸçµµæ–‡å­—をサジェスト
- emojis.value = defaultStore.state.recentlyUsedEmojis.map(emoji => emojiDb.find(e => e.emoji === emoji)).filter(x => x) as EmojiDef[];
+ emojis.value = defaultStore.state.recentlyUsedEmojis.map(emoji => emojiDb.find(dbEmoji => dbEmoji.emoji === emoji)).filter(x => x) as EmojiDef[];
return;
}
@@ -269,17 +269,17 @@ function exec() {
}
}
-function onMousedown(e: Event) {
- if (!contains(rootEl.value, e.target) && (rootEl.value !== e.target)) props.close();
+function onMousedown(event: Event) {
+ if (!contains(rootEl.value, event.target) && (rootEl.value !== event.target)) props.close();
}
-function onKeydown(e: KeyboardEvent) {
+function onKeydown(event: KeyboardEvent) {
const cancel = () => {
- e.preventDefault();
- e.stopPropagation();
+ event.preventDefault();
+ event.stopPropagation();
};
- switch (e.key) {
+ switch (event.key) {
case 'Enter':
if (select.value !== -1) {
cancel();
@@ -310,7 +310,7 @@ function onKeydown(e: KeyboardEvent) {
break;
default:
- e.stopPropagation();
+ event.stopPropagation();
props.textarea.focus();
}
}
diff --git a/packages/client/src/components/captcha.vue b/packages/client/src/components/captcha.vue
index ccd8880df8..183658471b 100644
--- a/packages/client/src/components/captcha.vue
+++ b/packages/client/src/components/captcha.vue
@@ -27,8 +27,7 @@ type CaptchaContainer = {
};
declare global {
- interface Window extends CaptchaContainer {
- }
+ interface Window extends CaptchaContainer { }
}
const props = defineProps<{
diff --git a/packages/client/src/components/channel-follow-button.vue b/packages/client/src/components/channel-follow-button.vue
index 7bbf5ae663..dff02beec0 100644
--- a/packages/client/src/components/channel-follow-button.vue
+++ b/packages/client/src/components/channel-follow-button.vue
@@ -48,8 +48,8 @@ async function onClick() {
});
isFollowing.value = true;
}
- } catch (e) {
- console.error(e);
+ } catch (err) {
+ console.error(err);
} finally {
wait.value = false;
}
diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue
index cc1aa9c20a..4e9c4e587a 100644
--- a/packages/client/src/components/chart.vue
+++ b/packages/client/src/components/chart.vue
@@ -7,8 +7,13 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent, onMounted, ref, watch, PropType, onUnmounted, shallowRef } from 'vue';
+<script lang="ts" setup>
+/* eslint-disable id-denylist --
+ Chart.js has a `data` attribute in most chart definitions, which triggers the
+ id-denylist violation when setting it. This is causing about 60+ lint issues.
+ As this is part of Chart.js's API it makes sense to disable the check here.
+*/
+import { defineProps, onMounted, ref, watch, PropType, onUnmounted } from 'vue';
import {
Chart,
ArcElement,
@@ -29,11 +34,53 @@ import {
import 'chartjs-adapter-date-fns';
import { enUS } from 'date-fns/locale';
import zoomPlugin from 'chartjs-plugin-zoom';
-import gradient from 'chartjs-plugin-gradient';
+// https://github.com/misskey-dev/misskey/pull/8575#issuecomment-1114242002
+// We can't use gradient because Vite throws a error.
+//import gradient from 'chartjs-plugin-gradient';
import * as os from '@/os';
import { defaultStore } from '@/store';
import MkChartTooltip from '@/components/chart-tooltip.vue';
+const props = defineProps({
+ src: {
+ type: String,
+ required: true,
+ },
+ args: {
+ type: Object,
+ required: false,
+ },
+ limit: {
+ type: Number,
+ required: false,
+ default: 90
+ },
+ span: {
+ type: String as PropType<'hour' | 'day'>,
+ required: true,
+ },
+ detailed: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ stacked: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ bar: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ aspectRatio: {
+ type: Number,
+ required: false,
+ default: null
+ },
+});
+
Chart.register(
ArcElement,
LineElement,
@@ -50,7 +97,7 @@ Chart.register(
SubTitle,
Filler,
zoomPlugin,
- gradient,
+ //gradient,
);
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
@@ -78,826 +125,777 @@ const getColor = (i) => {
return colorSets[i % colorSets.length];
};
-export default defineComponent({
- props: {
- src: {
- type: String,
- required: true,
- },
- args: {
- type: Object,
- required: false,
- },
- limit: {
- type: Number,
- required: false,
- default: 90
- },
- span: {
- type: String as PropType<'hour' | 'day'>,
- required: true,
- },
- detailed: {
- type: Boolean,
- required: false,
- default: false
- },
- stacked: {
- type: Boolean,
- required: false,
- default: false
- },
- bar: {
- type: Boolean,
- required: false,
- default: false
- },
- aspectRatio: {
- type: Number,
- required: false,
- default: null
- },
- },
+const now = new Date();
+let chartInstance: Chart = null;
+let chartData: {
+ series: {
+ name: string;
+ type: 'line' | 'area';
+ color?: string;
+ dashed?: boolean;
+ hidden?: boolean;
+ data: {
+ x: number;
+ y: number;
+ }[];
+ }[];
+} = null;
- setup(props) {
- const now = new Date();
- let chartInstance: Chart = null;
- let data: {
- series: {
- name: string;
- type: 'line' | 'area';
- color?: string;
- dashed?: boolean;
- hidden?: boolean;
- data: {
- x: number;
- y: number;
- }[];
- }[];
- } = null;
+const chartEl = ref<HTMLCanvasElement>(null);
+const fetching = ref(true);
- const chartEl = ref<HTMLCanvasElement>(null);
- const fetching = ref(true);
+const getDate = (ago: number) => {
+ const y = now.getFullYear();
+ const m = now.getMonth();
+ const d = now.getDate();
+ const h = now.getHours();
- const getDate = (ago: number) => {
- const y = now.getFullYear();
- const m = now.getMonth();
- const d = now.getDate();
- const h = now.getHours();
-
- return props.span === 'day' ? new Date(y, m, d - ago) : new Date(y, m, d, h - ago);
- };
+ return props.span === 'day' ? new Date(y, m, d - ago) : new Date(y, m, d, h - ago);
+};
- const format = (arr) => {
- return arr.map((v, i) => ({
- x: getDate(i).getTime(),
- y: v
- }));
- };
+const format = (arr) => {
+ return arr.map((v, i) => ({
+ x: getDate(i).getTime(),
+ y: v
+ }));
+};
- const tooltipShowing = ref(false);
- const tooltipX = ref(0);
- const tooltipY = ref(0);
- const tooltipTitle = ref(null);
- const tooltipSeries = ref(null);
- let disposeTooltipComponent;
+const tooltipShowing = ref(false);
+const tooltipX = ref(0);
+const tooltipY = ref(0);
+const tooltipTitle = ref(null);
+const tooltipSeries = ref(null);
+let disposeTooltipComponent;
- os.popup(MkChartTooltip, {
- showing: tooltipShowing,
- x: tooltipX,
- y: tooltipY,
- title: tooltipTitle,
- series: tooltipSeries,
- }, {}).then(({ dispose }) => {
- disposeTooltipComponent = dispose;
- });
+os.popup(MkChartTooltip, {
+ showing: tooltipShowing,
+ x: tooltipX,
+ y: tooltipY,
+ title: tooltipTitle,
+ series: tooltipSeries,
+}, {}).then(({ dispose }) => {
+ disposeTooltipComponent = dispose;
+});
- function externalTooltipHandler(context) {
- if (context.tooltip.opacity === 0) {
- tooltipShowing.value = false;
- return;
- }
+function externalTooltipHandler(context) {
+ if (context.tooltip.opacity === 0) {
+ tooltipShowing.value = false;
+ return;
+ }
- tooltipTitle.value = context.tooltip.title[0];
- tooltipSeries.value = context.tooltip.body.map((b, i) => ({
- backgroundColor: context.tooltip.labelColors[i].backgroundColor,
- borderColor: context.tooltip.labelColors[i].borderColor,
- text: b.lines[0],
- }));
+ tooltipTitle.value = context.tooltip.title[0];
+ tooltipSeries.value = context.tooltip.body.map((b, i) => ({
+ backgroundColor: context.tooltip.labelColors[i].backgroundColor,
+ borderColor: context.tooltip.labelColors[i].borderColor,
+ text: b.lines[0],
+ }));
- const rect = context.chart.canvas.getBoundingClientRect();
+ const rect = context.chart.canvas.getBoundingClientRect();
- tooltipShowing.value = true;
- tooltipX.value = rect.left + window.pageXOffset + context.tooltip.caretX;
- tooltipY.value = rect.top + window.pageYOffset + context.tooltip.caretY;
- }
+ tooltipShowing.value = true;
+ tooltipX.value = rect.left + window.pageXOffset + context.tooltip.caretX;
+ tooltipY.value = rect.top + window.pageYOffset + context.tooltip.caretY;
+}
- const render = () => {
- if (chartInstance) {
- chartInstance.destroy();
- }
+const render = () => {
+ if (chartInstance) {
+ chartInstance.destroy();
+ }
- const gridColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)';
- const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
+ const gridColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)';
+ const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
- // フォントカラー
- Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--fg');
+ // フォントカラー
+ Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--fg');
- const maxes = data.series.map((x, i) => Math.max(...x.data.map(d => d.y)));
+ const maxes = chartData.series.map((x, i) => Math.max(...x.data.map(d => d.y)));
- chartInstance = new Chart(chartEl.value, {
- type: props.bar ? 'bar' : 'line',
- data: {
- labels: new Array(props.limit).fill(0).map((_, i) => getDate(i).toLocaleString()).slice().reverse(),
- datasets: data.series.map((x, i) => ({
- parsing: false,
- label: x.name,
- data: x.data.slice().reverse(),
- tension: 0.3,
- pointRadius: 0,
- borderWidth: props.bar ? 0 : 2,
- borderColor: x.color ? x.color : getColor(i),
- borderDash: x.dashed ? [5, 5] : [],
- borderJoinStyle: 'round',
- borderRadius: props.bar ? 3 : undefined,
- backgroundColor: props.bar ? (x.color ? x.color : getColor(i)) : alpha(x.color ? x.color : getColor(i), 0.1),
- gradient: props.bar ? undefined : {
- backgroundColor: {
- axis: 'y',
- colors: {
- 0: alpha(x.color ? x.color : getColor(i), 0),
- [maxes[i]]: alpha(x.color ? x.color : getColor(i), 0.2),
- },
- },
+ chartInstance = new Chart(chartEl.value, {
+ type: props.bar ? 'bar' : 'line',
+ data: {
+ labels: new Array(props.limit).fill(0).map((_, i) => getDate(i).toLocaleString()).slice().reverse(),
+ datasets: chartData.series.map((x, i) => ({
+ parsing: false,
+ label: x.name,
+ data: x.data.slice().reverse(),
+ tension: 0.3,
+ pointRadius: 0,
+ borderWidth: props.bar ? 0 : 2,
+ borderColor: x.color ? x.color : getColor(i),
+ borderDash: x.dashed ? [5, 5] : [],
+ borderJoinStyle: 'round',
+ borderRadius: props.bar ? 3 : undefined,
+ backgroundColor: props.bar ? (x.color ? x.color : getColor(i)) : alpha(x.color ? x.color : getColor(i), 0.1),
+ /*gradient: props.bar ? undefined : {
+ backgroundColor: {
+ axis: 'y',
+ colors: {
+ 0: alpha(x.color ? x.color : getColor(i), 0),
+ [maxes[i]]: alpha(x.color ? x.color : getColor(i), 0.2),
},
- barPercentage: 0.9,
- categoryPercentage: 0.9,
- fill: x.type === 'area',
- clip: 8,
- hidden: !!x.hidden,
- })),
+ },
+ },*/
+ barPercentage: 0.9,
+ categoryPercentage: 0.9,
+ fill: x.type === 'area',
+ clip: 8,
+ hidden: !!x.hidden,
+ })),
+ },
+ options: {
+ aspectRatio: props.aspectRatio || 2.5,
+ layout: {
+ padding: {
+ left: 0,
+ right: 8,
+ top: 0,
+ bottom: 0,
},
- options: {
- aspectRatio: props.aspectRatio || 2.5,
- layout: {
- padding: {
- left: 0,
- right: 8,
- top: 0,
- bottom: 0,
- },
+ },
+ scales: {
+ x: {
+ type: 'time',
+ stacked: props.stacked,
+ offset: false,
+ time: {
+ stepSize: 1,
+ unit: props.span === 'day' ? 'month' : 'day',
},
- scales: {
- x: {
- type: 'time',
- stacked: props.stacked,
- offset: false,
- time: {
- stepSize: 1,
- unit: props.span === 'day' ? 'month' : 'day',
- },
- grid: {
- color: gridColor,
- borderColor: 'rgb(0, 0, 0, 0)',
- },
- ticks: {
- display: props.detailed,
- maxRotation: 0,
- autoSkipPadding: 16,
- },
- adapters: {
- date: {
- locale: enUS,
- },
- },
- min: getDate(props.limit).getTime(),
- },
- y: {
- position: 'left',
- stacked: props.stacked,
- suggestedMax: 50,
- grid: {
- color: gridColor,
- borderColor: 'rgb(0, 0, 0, 0)',
- },
- ticks: {
- display: props.detailed,
- //mirror: true,
- },
- },
+ grid: {
+ color: gridColor,
+ borderColor: 'rgb(0, 0, 0, 0)',
},
- interaction: {
- intersect: false,
- mode: 'index',
+ ticks: {
+ display: props.detailed,
+ maxRotation: 0,
+ autoSkipPadding: 16,
},
- elements: {
- point: {
- hoverRadius: 5,
- hoverBorderWidth: 2,
+ adapters: {
+ date: {
+ locale: enUS,
},
},
- animation: false,
- plugins: {
- legend: {
- display: props.detailed,
- position: 'bottom',
- labels: {
- boxWidth: 16,
- },
+ min: getDate(props.limit).getTime(),
+ },
+ y: {
+ position: 'left',
+ stacked: props.stacked,
+ suggestedMax: 50,
+ grid: {
+ color: gridColor,
+ borderColor: 'rgb(0, 0, 0, 0)',
+ },
+ ticks: {
+ display: props.detailed,
+ //mirror: true,
+ },
+ },
+ },
+ interaction: {
+ intersect: false,
+ mode: 'index',
+ },
+ elements: {
+ point: {
+ hoverRadius: 5,
+ hoverBorderWidth: 2,
+ },
+ },
+ animation: false,
+ plugins: {
+ legend: {
+ display: props.detailed,
+ position: 'bottom',
+ labels: {
+ boxWidth: 16,
+ },
+ },
+ tooltip: {
+ enabled: false,
+ mode: 'index',
+ animation: {
+ duration: 0,
+ },
+ external: externalTooltipHandler,
+ },
+ zoom: props.detailed ? {
+ pan: {
+ enabled: true,
+ },
+ zoom: {
+ wheel: {
+ enabled: true,
},
- tooltip: {
+ pinch: {
+ enabled: true,
+ },
+ drag: {
enabled: false,
- mode: 'index',
- animation: {
- duration: 0,
- },
- external: externalTooltipHandler,
},
- zoom: props.detailed ? {
- pan: {
- enabled: true,
- },
- zoom: {
- wheel: {
- enabled: true,
- },
- pinch: {
- enabled: true,
- },
- drag: {
- enabled: false,
- },
- mode: 'x',
- },
- limits: {
- x: {
- min: 'original',
- max: 'original',
- },
- y: {
- min: 'original',
- max: 'original',
- },
- }
- } : undefined,
- gradient,
+ mode: 'x',
},
- },
- plugins: [{
- id: 'vLine',
- beforeDraw(chart, args, options) {
- if (chart.tooltip._active && chart.tooltip._active.length) {
- const activePoint = chart.tooltip._active[0];
- const ctx = chart.ctx;
- const x = activePoint.element.x;
- const topY = chart.scales.y.top;
- const bottomY = chart.scales.y.bottom;
-
- ctx.save();
- ctx.beginPath();
- ctx.moveTo(x, bottomY);
- ctx.lineTo(x, topY);
- ctx.lineWidth = 1;
- ctx.strokeStyle = vLineColor;
- ctx.stroke();
- ctx.restore();
- }
+ limits: {
+ x: {
+ min: 'original',
+ max: 'original',
+ },
+ y: {
+ min: 'original',
+ max: 'original',
+ },
}
- }]
- });
- };
+ } : undefined,
+ //gradient,
+ },
+ },
+ plugins: [{
+ id: 'vLine',
+ beforeDraw(chart, args, options) {
+ if (chart.tooltip._active && chart.tooltip._active.length) {
+ const activePoint = chart.tooltip._active[0];
+ const ctx = chart.ctx;
+ const x = activePoint.element.x;
+ const topY = chart.scales.y.top;
+ const bottomY = chart.scales.y.bottom;
- const exportData = () => {
- // TODO
- };
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(x, bottomY);
+ ctx.lineTo(x, topY);
+ ctx.lineWidth = 1;
+ ctx.strokeStyle = vLineColor;
+ ctx.stroke();
+ ctx.restore();
+ }
+ }
+ }]
+ });
+};
- const fetchFederationChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/federation', { limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Received',
- type: 'area',
- data: format(raw.inboxInstances),
- color: colors.blue,
- }, {
- name: 'Delivered',
- type: 'area',
- data: format(raw.deliveredInstances),
- color: colors.green,
- }, {
- name: 'Stalled',
- type: 'area',
- data: format(raw.stalled),
- color: colors.red,
- }, {
- name: 'Pub Active',
- type: 'line',
- data: format(raw.pubActive),
- color: colors.purple,
- }, {
- name: 'Sub Active',
- type: 'line',
- data: format(raw.subActive),
- color: colors.orange,
- }, {
- name: 'Pub & Sub',
- type: 'line',
- data: format(raw.pubsub),
- dashed: true,
- color: colors.cyan,
- }, {
- name: 'Pub',
- type: 'line',
- data: format(raw.pub),
- dashed: true,
- color: colors.purple,
- }, {
- name: 'Sub',
- type: 'line',
- data: format(raw.sub),
- dashed: true,
- color: colors.orange,
- }],
- };
- };
+const exportData = () => {
+ // TODO
+};
- const fetchApRequestChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/ap-request', { limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'In',
- type: 'area',
- color: '#008FFB',
- data: format(raw.inboxReceived)
- }, {
- name: 'Out (succ)',
- type: 'area',
- color: '#00E396',
- data: format(raw.deliverSucceeded)
- }, {
- name: 'Out (fail)',
- type: 'area',
- color: '#FEB019',
- data: format(raw.deliverFailed)
- }]
- };
- };
+const fetchFederationChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/federation', { limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Received',
+ type: 'area',
+ data: format(raw.inboxInstances),
+ color: colors.blue,
+ }, {
+ name: 'Delivered',
+ type: 'area',
+ data: format(raw.deliveredInstances),
+ color: colors.green,
+ }, {
+ name: 'Stalled',
+ type: 'area',
+ data: format(raw.stalled),
+ color: colors.red,
+ }, {
+ name: 'Pub Active',
+ type: 'line',
+ data: format(raw.pubActive),
+ color: colors.purple,
+ }, {
+ name: 'Sub Active',
+ type: 'line',
+ data: format(raw.subActive),
+ color: colors.orange,
+ }, {
+ name: 'Pub & Sub',
+ type: 'line',
+ data: format(raw.pubsub),
+ dashed: true,
+ color: colors.cyan,
+ }, {
+ name: 'Pub',
+ type: 'line',
+ data: format(raw.pub),
+ dashed: true,
+ color: colors.purple,
+ }, {
+ name: 'Sub',
+ type: 'line',
+ data: format(raw.sub),
+ dashed: true,
+ color: colors.orange,
+ }],
+ };
+};
- const fetchNotesChart = async (type: string): Promise<typeof data> => {
- const raw = await os.api('charts/notes', { limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'All',
- type: 'line',
- data: format(type == 'combined'
- ? sum(raw.local.inc, negate(raw.local.dec), raw.remote.inc, negate(raw.remote.dec))
- : sum(raw[type].inc, negate(raw[type].dec))
- ),
- color: '#888888',
- }, {
- name: 'Renotes',
- type: 'area',
- data: format(type == 'combined'
- ? sum(raw.local.diffs.renote, raw.remote.diffs.renote)
- : raw[type].diffs.renote
- ),
- color: colors.green,
- }, {
- name: 'Replies',
- type: 'area',
- data: format(type == 'combined'
- ? sum(raw.local.diffs.reply, raw.remote.diffs.reply)
- : raw[type].diffs.reply
- ),
- color: colors.yellow,
- }, {
- name: 'Normal',
- type: 'area',
- data: format(type == 'combined'
- ? sum(raw.local.diffs.normal, raw.remote.diffs.normal)
- : raw[type].diffs.normal
- ),
- color: colors.blue,
- }, {
- name: 'With file',
- type: 'area',
- data: format(type == 'combined'
- ? sum(raw.local.diffs.withFile, raw.remote.diffs.withFile)
- : raw[type].diffs.withFile
- ),
- color: colors.purple,
- }],
- };
- };
+const fetchApRequestChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/ap-request', { limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'In',
+ type: 'area',
+ color: '#008FFB',
+ data: format(raw.inboxReceived)
+ }, {
+ name: 'Out (succ)',
+ type: 'area',
+ color: '#00E396',
+ data: format(raw.deliverSucceeded)
+ }, {
+ name: 'Out (fail)',
+ type: 'area',
+ color: '#FEB019',
+ data: format(raw.deliverFailed)
+ }]
+ };
+};
- const fetchNotesTotalChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/notes', { limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Combined',
- type: 'line',
- data: format(sum(raw.local.total, raw.remote.total)),
- }, {
- name: 'Local',
- type: 'area',
- data: format(raw.local.total),
- }, {
- name: 'Remote',
- type: 'area',
- data: format(raw.remote.total),
- }],
- };
- };
+const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
+ const raw = await os.api('charts/notes', { limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'All',
+ type: 'line',
+ data: format(type === 'combined'
+ ? sum(raw.local.inc, negate(raw.local.dec), raw.remote.inc, negate(raw.remote.dec))
+ : sum(raw[type].inc, negate(raw[type].dec))
+ ),
+ color: '#888888',
+ }, {
+ name: 'Renotes',
+ type: 'area',
+ data: format(type === 'combined'
+ ? sum(raw.local.diffs.renote, raw.remote.diffs.renote)
+ : raw[type].diffs.renote
+ ),
+ color: colors.green,
+ }, {
+ name: 'Replies',
+ type: 'area',
+ data: format(type === 'combined'
+ ? sum(raw.local.diffs.reply, raw.remote.diffs.reply)
+ : raw[type].diffs.reply
+ ),
+ color: colors.yellow,
+ }, {
+ name: 'Normal',
+ type: 'area',
+ data: format(type === 'combined'
+ ? sum(raw.local.diffs.normal, raw.remote.diffs.normal)
+ : raw[type].diffs.normal
+ ),
+ color: colors.blue,
+ }, {
+ name: 'With file',
+ type: 'area',
+ data: format(type === 'combined'
+ ? sum(raw.local.diffs.withFile, raw.remote.diffs.withFile)
+ : raw[type].diffs.withFile
+ ),
+ color: colors.purple,
+ }],
+ };
+};
- const fetchUsersChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/users', { limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Combined',
- type: 'line',
- data: format(total
- ? sum(raw.local.total, raw.remote.total)
- : sum(raw.local.inc, negate(raw.local.dec), raw.remote.inc, negate(raw.remote.dec))
- ),
- }, {
- name: 'Local',
- type: 'area',
- data: format(total
- ? raw.local.total
- : sum(raw.local.inc, negate(raw.local.dec))
- ),
- }, {
- name: 'Remote',
- type: 'area',
- data: format(total
- ? raw.remote.total
- : sum(raw.remote.inc, negate(raw.remote.dec))
- ),
- }],
- };
- };
+const fetchNotesTotalChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/notes', { limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Combined',
+ type: 'line',
+ data: format(sum(raw.local.total, raw.remote.total)),
+ }, {
+ name: 'Local',
+ type: 'area',
+ data: format(raw.local.total),
+ }, {
+ name: 'Remote',
+ type: 'area',
+ data: format(raw.remote.total),
+ }],
+ };
+};
- const fetchActiveUsersChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/active-users', { limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Read & Write',
- type: 'area',
- data: format(raw.readWrite),
- color: colors.orange,
- }, {
- name: 'Write',
- type: 'area',
- data: format(raw.write),
- color: colors.lime,
- }, {
- name: 'Read',
- type: 'area',
- data: format(raw.read),
- color: colors.blue,
- }, {
- name: '< Week',
- type: 'area',
- data: format(raw.registeredWithinWeek),
- color: colors.green,
- }, {
- name: '< Month',
- type: 'area',
- data: format(raw.registeredWithinMonth),
- color: colors.yellow,
- }, {
- name: '< Year',
- type: 'area',
- data: format(raw.registeredWithinYear),
- color: colors.red,
- }, {
- name: '> Week',
- type: 'area',
- data: format(raw.registeredOutsideWeek),
- color: colors.yellow,
- }, {
- name: '> Month',
- type: 'area',
- data: format(raw.registeredOutsideMonth),
- color: colors.red,
- }, {
- name: '> Year',
- type: 'area',
- data: format(raw.registeredOutsideYear),
- color: colors.purple,
- }],
- };
- };
+const fetchUsersChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/users', { limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Combined',
+ type: 'line',
+ data: format(total
+ ? sum(raw.local.total, raw.remote.total)
+ : sum(raw.local.inc, negate(raw.local.dec), raw.remote.inc, negate(raw.remote.dec))
+ ),
+ }, {
+ name: 'Local',
+ type: 'area',
+ data: format(total
+ ? raw.local.total
+ : sum(raw.local.inc, negate(raw.local.dec))
+ ),
+ }, {
+ name: 'Remote',
+ type: 'area',
+ data: format(total
+ ? raw.remote.total
+ : sum(raw.remote.inc, negate(raw.remote.dec))
+ ),
+ }],
+ };
+};
- const fetchDriveChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/drive', { limit: props.limit, span: props.span });
- return {
- bytes: true,
- series: [{
- name: 'All',
- type: 'line',
- dashed: true,
- data: format(
- sum(
- raw.local.incSize,
- negate(raw.local.decSize),
- raw.remote.incSize,
- negate(raw.remote.decSize)
- )
- ),
- }, {
- name: 'Local +',
- type: 'area',
- data: format(raw.local.incSize),
- }, {
- name: 'Local -',
- type: 'area',
- data: format(negate(raw.local.decSize)),
- }, {
- name: 'Remote +',
- type: 'area',
- data: format(raw.remote.incSize),
- }, {
- name: 'Remote -',
- type: 'area',
- data: format(negate(raw.remote.decSize)),
- }],
- };
- };
+const fetchActiveUsersChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/active-users', { limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Read & Write',
+ type: 'area',
+ data: format(raw.readWrite),
+ color: colors.orange,
+ }, {
+ name: 'Write',
+ type: 'area',
+ data: format(raw.write),
+ color: colors.lime,
+ }, {
+ name: 'Read',
+ type: 'area',
+ data: format(raw.read),
+ color: colors.blue,
+ }, {
+ name: '< Week',
+ type: 'area',
+ data: format(raw.registeredWithinWeek),
+ color: colors.green,
+ }, {
+ name: '< Month',
+ type: 'area',
+ data: format(raw.registeredWithinMonth),
+ color: colors.yellow,
+ }, {
+ name: '< Year',
+ type: 'area',
+ data: format(raw.registeredWithinYear),
+ color: colors.red,
+ }, {
+ name: '> Week',
+ type: 'area',
+ data: format(raw.registeredOutsideWeek),
+ color: colors.yellow,
+ }, {
+ name: '> Month',
+ type: 'area',
+ data: format(raw.registeredOutsideMonth),
+ color: colors.red,
+ }, {
+ name: '> Year',
+ type: 'area',
+ data: format(raw.registeredOutsideYear),
+ color: colors.purple,
+ }],
+ };
+};
- const fetchDriveFilesChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/drive', { limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'All',
- type: 'line',
- dashed: true,
- data: format(
- sum(
- raw.local.incCount,
- negate(raw.local.decCount),
- raw.remote.incCount,
- negate(raw.remote.decCount)
- )
- ),
- }, {
- name: 'Local +',
- type: 'area',
- data: format(raw.local.incCount),
- }, {
- name: 'Local -',
- type: 'area',
- data: format(negate(raw.local.decCount)),
- }, {
- name: 'Remote +',
- type: 'area',
- data: format(raw.remote.incCount),
- }, {
- name: 'Remote -',
- type: 'area',
- data: format(negate(raw.remote.decCount)),
- }],
- };
- };
+const fetchDriveChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/drive', { limit: props.limit, span: props.span });
+ return {
+ bytes: true,
+ series: [{
+ name: 'All',
+ type: 'line',
+ dashed: true,
+ data: format(
+ sum(
+ raw.local.incSize,
+ negate(raw.local.decSize),
+ raw.remote.incSize,
+ negate(raw.remote.decSize)
+ )
+ ),
+ }, {
+ name: 'Local +',
+ type: 'area',
+ data: format(raw.local.incSize),
+ }, {
+ name: 'Local -',
+ type: 'area',
+ data: format(negate(raw.local.decSize)),
+ }, {
+ name: 'Remote +',
+ type: 'area',
+ data: format(raw.remote.incSize),
+ }, {
+ name: 'Remote -',
+ type: 'area',
+ data: format(negate(raw.remote.decSize)),
+ }],
+ };
+};
- const fetchInstanceRequestsChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'In',
- type: 'area',
- color: '#008FFB',
- data: format(raw.requests.received)
- }, {
- name: 'Out (succ)',
- type: 'area',
- color: '#00E396',
- data: format(raw.requests.succeeded)
- }, {
- name: 'Out (fail)',
- type: 'area',
- color: '#FEB019',
- data: format(raw.requests.failed)
- }]
- };
- };
+const fetchDriveFilesChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/drive', { limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'All',
+ type: 'line',
+ dashed: true,
+ data: format(
+ sum(
+ raw.local.incCount,
+ negate(raw.local.decCount),
+ raw.remote.incCount,
+ negate(raw.remote.decCount)
+ )
+ ),
+ }, {
+ name: 'Local +',
+ type: 'area',
+ data: format(raw.local.incCount),
+ }, {
+ name: 'Local -',
+ type: 'area',
+ data: format(negate(raw.local.decCount)),
+ }, {
+ name: 'Remote +',
+ type: 'area',
+ data: format(raw.remote.incCount),
+ }, {
+ name: 'Remote -',
+ type: 'area',
+ data: format(negate(raw.remote.decCount)),
+ }],
+ };
+};
- const fetchInstanceUsersChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Users',
- type: 'area',
- color: '#008FFB',
- data: format(total
- ? raw.users.total
- : sum(raw.users.inc, negate(raw.users.dec))
- )
- }]
- };
- };
+const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'In',
+ type: 'area',
+ color: '#008FFB',
+ data: format(raw.requests.received)
+ }, {
+ name: 'Out (succ)',
+ type: 'area',
+ color: '#00E396',
+ data: format(raw.requests.succeeded)
+ }, {
+ name: 'Out (fail)',
+ type: 'area',
+ color: '#FEB019',
+ data: format(raw.requests.failed)
+ }]
+ };
+};
- const fetchInstanceNotesChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Notes',
- type: 'area',
- color: '#008FFB',
- data: format(total
- ? raw.notes.total
- : sum(raw.notes.inc, negate(raw.notes.dec))
- )
- }]
- };
- };
+const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Users',
+ type: 'area',
+ color: '#008FFB',
+ data: format(total
+ ? raw.users.total
+ : sum(raw.users.inc, negate(raw.users.dec))
+ )
+ }]
+ };
+};
- const fetchInstanceFfChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Following',
- type: 'area',
- color: '#008FFB',
- data: format(total
- ? raw.following.total
- : sum(raw.following.inc, negate(raw.following.dec))
- )
- }, {
- name: 'Followers',
- type: 'area',
- color: '#00E396',
- data: format(total
- ? raw.followers.total
- : sum(raw.followers.inc, negate(raw.followers.dec))
- )
- }]
- };
- };
+const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Notes',
+ type: 'area',
+ color: '#008FFB',
+ data: format(total
+ ? raw.notes.total
+ : sum(raw.notes.inc, negate(raw.notes.dec))
+ )
+ }]
+ };
+};
- const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- bytes: true,
- series: [{
- name: 'Drive usage',
- type: 'area',
- color: '#008FFB',
- data: format(total
- ? raw.drive.totalUsage
- : sum(raw.drive.incUsage, negate(raw.drive.decUsage))
- )
- }]
- };
- };
+const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Following',
+ type: 'area',
+ color: '#008FFB',
+ data: format(total
+ ? raw.following.total
+ : sum(raw.following.inc, negate(raw.following.dec))
+ )
+ }, {
+ name: 'Followers',
+ type: 'area',
+ color: '#00E396',
+ data: format(total
+ ? raw.followers.total
+ : sum(raw.followers.inc, negate(raw.followers.dec))
+ )
+ }]
+ };
+};
- const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Drive files',
- type: 'area',
- color: '#008FFB',
- data: format(total
- ? raw.drive.totalFiles
- : sum(raw.drive.incFiles, negate(raw.drive.decFiles))
- )
- }]
- };
- };
+const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ bytes: true,
+ series: [{
+ name: 'Drive usage',
+ type: 'area',
+ color: '#008FFB',
+ data: format(total
+ ? raw.drive.totalUsage
+ : sum(raw.drive.incUsage, negate(raw.drive.decUsage))
+ )
+ }]
+ };
+};
- const fetchPerUserNotesChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/user/notes', { userId: props.args.user.id, limit: props.limit, span: props.span });
- return {
- series: [...(props.args.withoutAll ? [] : [{
- name: 'All',
- type: 'line',
- data: format(sum(raw.inc, negate(raw.dec))),
- color: '#888888',
- }]), {
- name: 'With file',
- type: 'area',
- data: format(raw.diffs.withFile),
- color: colors.purple,
- }, {
- name: 'Renotes',
- type: 'area',
- data: format(raw.diffs.renote),
- color: colors.green,
- }, {
- name: 'Replies',
- type: 'area',
- data: format(raw.diffs.reply),
- color: colors.yellow,
- }, {
- name: 'Normal',
- type: 'area',
- data: format(raw.diffs.normal),
- color: colors.blue,
- }],
- };
- };
+const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Drive files',
+ type: 'area',
+ color: '#008FFB',
+ data: format(total
+ ? raw.drive.totalFiles
+ : sum(raw.drive.incFiles, negate(raw.drive.decFiles))
+ )
+ }]
+ };
+};
- const fetchPerUserFollowingChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Local',
- type: 'area',
- data: format(raw.local.followings.total),
- }, {
- name: 'Remote',
- type: 'area',
- data: format(raw.remote.followings.total),
- }],
- };
- };
+const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/user/notes', { userId: props.args.user.id, limit: props.limit, span: props.span });
+ return {
+ series: [...(props.args.withoutAll ? [] : [{
+ name: 'All',
+ type: 'line',
+ data: format(sum(raw.inc, negate(raw.dec))),
+ color: '#888888',
+ }]), {
+ name: 'With file',
+ type: 'area',
+ data: format(raw.diffs.withFile),
+ color: colors.purple,
+ }, {
+ name: 'Renotes',
+ type: 'area',
+ data: format(raw.diffs.renote),
+ color: colors.green,
+ }, {
+ name: 'Replies',
+ type: 'area',
+ data: format(raw.diffs.reply),
+ color: colors.yellow,
+ }, {
+ name: 'Normal',
+ type: 'area',
+ data: format(raw.diffs.normal),
+ color: colors.blue,
+ }],
+ };
+};
- const fetchPerUserFollowersChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Local',
- type: 'area',
- data: format(raw.local.followers.total),
- }, {
- name: 'Remote',
- type: 'area',
- data: format(raw.remote.followers.total),
- }],
- };
- };
+const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Local',
+ type: 'area',
+ data: format(raw.local.followings.total),
+ }, {
+ name: 'Remote',
+ type: 'area',
+ data: format(raw.remote.followings.total),
+ }],
+ };
+};
- const fetchPerUserDriveChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/user/drive', { userId: props.args.user.id, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Inc',
- type: 'area',
- data: format(raw.incSize),
- }, {
- name: 'Dec',
- type: 'area',
- data: format(raw.decSize),
- }],
- };
- };
+const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Local',
+ type: 'area',
+ data: format(raw.local.followers.total),
+ }, {
+ name: 'Remote',
+ type: 'area',
+ data: format(raw.remote.followers.total),
+ }],
+ };
+};
- const fetchAndRender = async () => {
- const fetchData = () => {
- switch (props.src) {
- case 'federation': return fetchFederationChart();
- case 'ap-request': return fetchApRequestChart();
- case 'users': return fetchUsersChart(false);
- case 'users-total': return fetchUsersChart(true);
- case 'active-users': return fetchActiveUsersChart();
- case 'notes': return fetchNotesChart('combined');
- case 'local-notes': return fetchNotesChart('local');
- case 'remote-notes': return fetchNotesChart('remote');
- case 'notes-total': return fetchNotesTotalChart();
- case 'drive': return fetchDriveChart();
- case 'drive-files': return fetchDriveFilesChart();
-
- case 'instance-requests': return fetchInstanceRequestsChart();
- case 'instance-users': return fetchInstanceUsersChart(false);
- case 'instance-users-total': return fetchInstanceUsersChart(true);
- case 'instance-notes': return fetchInstanceNotesChart(false);
- case 'instance-notes-total': return fetchInstanceNotesChart(true);
- case 'instance-ff': return fetchInstanceFfChart(false);
- case 'instance-ff-total': return fetchInstanceFfChart(true);
- case 'instance-drive-usage': return fetchInstanceDriveUsageChart(false);
- case 'instance-drive-usage-total': return fetchInstanceDriveUsageChart(true);
- case 'instance-drive-files': return fetchInstanceDriveFilesChart(false);
- case 'instance-drive-files-total': return fetchInstanceDriveFilesChart(true);
+const fetchPerUserDriveChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/user/drive', { userId: props.args.user.id, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Inc',
+ type: 'area',
+ data: format(raw.incSize),
+ }, {
+ name: 'Dec',
+ type: 'area',
+ data: format(raw.decSize),
+ }],
+ };
+};
- case 'per-user-notes': return fetchPerUserNotesChart();
- case 'per-user-following': return fetchPerUserFollowingChart();
- case 'per-user-followers': return fetchPerUserFollowersChart();
- case 'per-user-drive': return fetchPerUserDriveChart();
- }
- };
- fetching.value = true;
- data = await fetchData();
- fetching.value = false;
- render();
- };
+const fetchAndRender = async () => {
+ const fetchData = () => {
+ switch (props.src) {
+ case 'federation': return fetchFederationChart();
+ case 'ap-request': return fetchApRequestChart();
+ case 'users': return fetchUsersChart(false);
+ case 'users-total': return fetchUsersChart(true);
+ case 'active-users': return fetchActiveUsersChart();
+ case 'notes': return fetchNotesChart('combined');
+ case 'local-notes': return fetchNotesChart('local');
+ case 'remote-notes': return fetchNotesChart('remote');
+ case 'notes-total': return fetchNotesTotalChart();
+ case 'drive': return fetchDriveChart();
+ case 'drive-files': return fetchDriveFilesChart();
+ case 'instance-requests': return fetchInstanceRequestsChart();
+ case 'instance-users': return fetchInstanceUsersChart(false);
+ case 'instance-users-total': return fetchInstanceUsersChart(true);
+ case 'instance-notes': return fetchInstanceNotesChart(false);
+ case 'instance-notes-total': return fetchInstanceNotesChart(true);
+ case 'instance-ff': return fetchInstanceFfChart(false);
+ case 'instance-ff-total': return fetchInstanceFfChart(true);
+ case 'instance-drive-usage': return fetchInstanceDriveUsageChart(false);
+ case 'instance-drive-usage-total': return fetchInstanceDriveUsageChart(true);
+ case 'instance-drive-files': return fetchInstanceDriveFilesChart(false);
+ case 'instance-drive-files-total': return fetchInstanceDriveFilesChart(true);
- watch(() => [props.src, props.span], fetchAndRender);
+ case 'per-user-notes': return fetchPerUserNotesChart();
+ case 'per-user-following': return fetchPerUserFollowingChart();
+ case 'per-user-followers': return fetchPerUserFollowersChart();
+ case 'per-user-drive': return fetchPerUserDriveChart();
+ }
+ };
+ fetching.value = true;
+ chartData = await fetchData();
+ fetching.value = false;
+ render();
+};
- onMounted(() => {
- fetchAndRender();
- });
+watch(() => [props.src, props.span], fetchAndRender);
- onUnmounted(() => {
- if (disposeTooltipComponent) disposeTooltipComponent();
- });
+onMounted(() => {
+ fetchAndRender();
+});
- return {
- chartEl,
- fetching,
- };
- },
+onUnmounted(() => {
+ if (disposeTooltipComponent) disposeTooltipComponent();
});
+/* eslint-enable id-denylist */
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/components/cropper-dialog.vue b/packages/client/src/components/cropper-dialog.vue
new file mode 100644
index 0000000000..a8bde6ea05
--- /dev/null
+++ b/packages/client/src/components/cropper-dialog.vue
@@ -0,0 +1,175 @@
+<template>
+<XModalWindow
+ ref="dialogEl"
+ :width="800"
+ :height="500"
+ :scroll="false"
+ :with-ok-button="true"
+ @close="cancel()"
+ @ok="ok()"
+ @closed="$emit('closed')"
+>
+ <template #header>{{ $ts.cropImage }}</template>
+ <template #default="{ width, height }">
+ <div class="mk-cropper-dialog" :style="`--vw: ${width}px; --vh: ${height}px;`">
+ <Transition name="fade">
+ <div v-if="loading" class="loading">
+ <MkLoading/>
+ </div>
+ </Transition>
+ <div class="container">
+ <img ref="imgEl" :src="imgUrl" style="display: none;" @load="onImageLoad">
+ </div>
+ </div>
+ </template>
+</XModalWindow>
+</template>
+
+<script lang="ts" setup>
+import { nextTick, onMounted } from 'vue';
+import * as misskey from 'misskey-js';
+import Cropper from 'cropperjs';
+import tinycolor from 'tinycolor2';
+import XModalWindow from '@/components/ui/modal-window.vue';
+import * as os from '@/os';
+import { $i } from '@/account';
+import { defaultStore } from '@/store';
+import { apiUrl, url } from '@/config';
+import { query } from '@/scripts/url';
+
+const emit = defineEmits<{
+ (ev: 'ok', cropped: misskey.entities.DriveFile): void;
+ (ev: 'cancel'): void;
+ (ev: 'closed'): void;
+}>();
+
+const props = defineProps<{
+ file: misskey.entities.DriveFile;
+ aspectRatio: number;
+}>();
+
+const imgUrl = `${url}/proxy/image.webp?${query({
+ url: props.file.url,
+})}`;
+let dialogEl = $ref<InstanceType<typeof XModalWindow>>();
+let imgEl = $ref<HTMLImageElement>();
+let cropper: Cropper | null = null;
+let loading = $ref(true);
+
+const ok = async () => {
+ const promise = new Promise<misskey.entities.DriveFile>(async (res) => {
+ const croppedCanvas = await cropper?.getCropperSelection()?.$toCanvas();
+ croppedCanvas.toBlob(blob => {
+ const formData = new FormData();
+ formData.append('file', blob);
+ formData.append('i', $i.token);
+ if (defaultStore.state.uploadFolder) {
+ formData.append('folderId', defaultStore.state.uploadFolder);
+ }
+
+ fetch(apiUrl + '/drive/files/create', {
+ method: 'POST',
+ body: formData,
+ })
+ .then(response => response.json())
+ .then(f => {
+ res(f);
+ });
+ });
+ });
+
+ os.promiseDialog(promise);
+
+ const f = await promise;
+
+ emit('ok', f);
+ dialogEl.close();
+};
+
+const cancel = () => {
+ emit('cancel');
+ dialogEl.close();
+};
+
+const onImageLoad = () => {
+ loading = false;
+
+ if (cropper) {
+ cropper.getCropperImage()!.$center('contain');
+ cropper.getCropperSelection()!.$center();
+ }
+};
+
+onMounted(() => {
+ cropper = new Cropper(imgEl, {
+ });
+
+ const computedStyle = getComputedStyle(document.documentElement);
+
+ const selection = cropper.getCropperSelection()!;
+ selection.themeColor = tinycolor(computedStyle.getPropertyValue('--accent')).toHexString();
+ selection.aspectRatio = props.aspectRatio;
+ selection.initialAspectRatio = props.aspectRatio;
+ selection.outlined = true;
+
+ window.setTimeout(() => {
+ cropper.getCropperImage()!.$center('contain');
+ selection.$center();
+ }, 100);
+
+ // モーダルオープンアニメーションãŒçµ‚ã‚ã£ãŸã‚ã¨ã§å†åº¦èª¿æ•´
+ window.setTimeout(() => {
+ cropper.getCropperImage()!.$center('contain');
+ selection.$center();
+ }, 500);
+});
+</script>
+
+<style lang="scss" scoped>
+.fade-enter-active,
+.fade-leave-active {
+ transition: opacity 0.5s ease 0.5s;
+}
+.fade-enter-from,
+.fade-leave-to {
+ opacity: 0;
+}
+
+.mk-cropper-dialog {
+ display: flex;
+ flex-direction: column;
+ width: var(--vw);
+ height: var(--vh);
+ position: relative;
+
+ > .loading {
+ position: absolute;
+ z-index: 10;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ -webkit-backdrop-filter: var(--blur, blur(10px));
+ backdrop-filter: var(--blur, blur(10px));
+ background: rgba(0, 0, 0, 0.5);
+ }
+
+ > .container {
+ flex: 1;
+ width: 100%;
+ height: 100%;
+
+ > ::v-deep(cropper-canvas) {
+ width: 100%;
+ height: 100%;
+
+ > cropper-selection > cropper-handle[action="move"] {
+ background: transparent;
+ }
+ }
+ }
+}
+</style>
diff --git a/packages/client/src/components/cw-button.vue b/packages/client/src/components/cw-button.vue
index e7c9aabe4e..dd906f9bf3 100644
--- a/packages/client/src/components/cw-button.vue
+++ b/packages/client/src/components/cw-button.vue
@@ -18,7 +18,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'update:modelValue', v: boolean): void;
+ (ev: 'update:modelValue', v: boolean): void;
}>();
const label = computed(() => {
diff --git a/packages/client/src/components/dialog.vue b/packages/client/src/components/dialog.vue
index 3e106a4f0c..b090f3cb4e 100644
--- a/packages/client/src/components/dialog.vue
+++ b/packages/client/src/components/dialog.vue
@@ -90,8 +90,8 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'done', v: { canceled: boolean; result: any }): void;
- (e: 'closed'): void;
+ (ev: 'done', v: { canceled: boolean; result: any }): void;
+ (ev: 'closed'): void;
}>();
const modal = ref<InstanceType<typeof MkModal>>();
@@ -122,14 +122,14 @@ function onBgClick() {
if (props.cancelableByBgClick) cancel();
}
*/
-function onKeydown(e: KeyboardEvent) {
- if (e.key === 'Escape') cancel();
+function onKeydown(evt: KeyboardEvent) {
+ if (evt.key === 'Escape') cancel();
}
-function onInputKeydown(e: KeyboardEvent) {
- if (e.key === 'Enter') {
- e.preventDefault();
- e.stopPropagation();
+function onInputKeydown(evt: KeyboardEvent) {
+ if (evt.key === 'Enter') {
+ evt.preventDefault();
+ evt.stopPropagation();
ok();
}
}
diff --git a/packages/client/src/components/drive-file-thumbnail.vue b/packages/client/src/components/drive-file-thumbnail.vue
index 81b80e7e8e..dd24440e82 100644
--- a/packages/client/src/components/drive-file-thumbnail.vue
+++ b/packages/client/src/components/drive-file-thumbnail.vue
@@ -42,7 +42,7 @@ const is = computed(() => {
"application/x-tar",
"application/gzip",
"application/x-7z-compressed"
- ].some(e => e === props.file.type)) return 'archive';
+ ].some(archiveType => archiveType === props.file.type)) return 'archive';
return 'unknown';
});
diff --git a/packages/client/src/components/drive-select-dialog.vue b/packages/client/src/components/drive-select-dialog.vue
index f6c59457d1..03974559d2 100644
--- a/packages/client/src/components/drive-select-dialog.vue
+++ b/packages/client/src/components/drive-select-dialog.vue
@@ -33,8 +33,8 @@ withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'done', r?: Misskey.entities.DriveFile[]): void;
- (e: 'closed'): void;
+ (ev: 'done', r?: Misskey.entities.DriveFile[]): void;
+ (ev: 'closed'): void;
}>();
const dialog = ref<InstanceType<typeof XModalWindow>>();
diff --git a/packages/client/src/components/drive-window.vue b/packages/client/src/components/drive-window.vue
index d08c5fb674..5bbfca83c9 100644
--- a/packages/client/src/components/drive-window.vue
+++ b/packages/client/src/components/drive-window.vue
@@ -24,6 +24,6 @@ defineProps<{
}>();
const emit = defineEmits<{
- (e: 'closed'): void;
+ (ev: 'closed'): void;
}>();
</script>
diff --git a/packages/client/src/components/drive.file.vue b/packages/client/src/components/drive.file.vue
index 262eae0de1..aaf7ca3ca3 100644
--- a/packages/client/src/components/drive.file.vue
+++ b/packages/client/src/components/drive.file.vue
@@ -31,7 +31,7 @@
</template>
<script lang="ts" setup>
-import { computed, ref } from 'vue';
+import { computed, defineAsyncComponent, ref } from 'vue';
import * as Misskey from 'misskey-js';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import MkDriveFileThumbnail from './drive-file-thumbnail.vue';
@@ -50,9 +50,9 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'chosen', r: Misskey.entities.DriveFile): void;
- (e: 'dragstart'): void;
- (e: 'dragend'): void;
+ (ev: 'chosen', r: Misskey.entities.DriveFile): void;
+ (ev: 'dragstart'): void;
+ (ev: 'dragend'): void;
}>();
const isDragging = ref(false);
@@ -99,14 +99,14 @@ function onClick(ev: MouseEvent) {
}
}
-function onContextmenu(e: MouseEvent) {
- os.contextMenu(getMenu(), e);
+function onContextmenu(ev: MouseEvent) {
+ os.contextMenu(getMenu(), ev);
}
-function onDragstart(e: DragEvent) {
- if (e.dataTransfer) {
- e.dataTransfer.effectAllowed = 'move';
- e.dataTransfer.setData(_DATA_TRANSFER_DRIVE_FILE_, JSON.stringify(props.file));
+function onDragstart(ev: DragEvent) {
+ if (ev.dataTransfer) {
+ ev.dataTransfer.effectAllowed = 'move';
+ ev.dataTransfer.setData(_DATA_TRANSFER_DRIVE_FILE_, JSON.stringify(props.file));
}
isDragging.value = true;
@@ -133,11 +133,11 @@ function rename() {
}
function describe() {
- os.popup(import('@/components/media-caption.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/media-caption.vue')), {
title: i18n.ts.describeFile,
input: {
placeholder: i18n.ts.inputNewDescription,
- default: props.file.comment !== null ? props.file.comment : '',
+ default: props.file.comment != null ? props.file.comment : '',
},
image: props.file
}, {
@@ -146,7 +146,7 @@ function describe() {
let comment = result.result;
os.api('drive/files/update', {
fileId: props.file.id,
- comment: comment.length == 0 ? null : comment
+ comment: comment.length === 0 ? null : comment
});
}
}, 'closed');
diff --git a/packages/client/src/components/drive.folder.vue b/packages/client/src/components/drive.folder.vue
index 57621bf097..3ccb5d6219 100644
--- a/packages/client/src/components/drive.folder.vue
+++ b/packages/client/src/components/drive.folder.vue
@@ -27,7 +27,7 @@
</template>
<script lang="ts" setup>
-import { computed, ref } from 'vue';
+import { computed, defineAsyncComponent, ref } from 'vue';
import * as Misskey from 'misskey-js';
import * as os from '@/os';
import { i18n } from '@/i18n';
@@ -71,7 +71,7 @@ function onMouseover() {
}
function onMouseout() {
- hover.value = false
+ hover.value = false;
}
function onDragover(ev: DragEvent) {
@@ -84,12 +84,12 @@ function onDragover(ev: DragEvent) {
return;
}
- const isFile = ev.dataTransfer.items[0].kind == 'file';
- const isDriveFile = ev.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FILE_;
- const isDriveFolder = ev.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FOLDER_;
+ const isFile = ev.dataTransfer.items[0].kind === 'file';
+ const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
+ const isDriveFolder = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FOLDER_;
if (isFile || isDriveFile || isDriveFolder) {
- ev.dataTransfer.dropEffect = ev.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
+ ev.dataTransfer.dropEffect = ev.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move';
} else {
ev.dataTransfer.dropEffect = 'none';
}
@@ -118,7 +118,7 @@ function onDrop(ev: DragEvent) {
//#region ドライブã®ãƒ•ァイル
const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
- if (driveFile != null && driveFile != '') {
+ if (driveFile != null && driveFile !== '') {
const file = JSON.parse(driveFile);
emit('removeFile', file.id);
os.api('drive/files/update', {
@@ -130,11 +130,11 @@ function onDrop(ev: DragEvent) {
//#region ドライブã®ãƒ•ォルダ
const driveFolder = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FOLDER_);
- if (driveFolder != null && driveFolder != '') {
+ if (driveFolder != null && driveFolder !== '') {
const folder = JSON.parse(driveFolder);
// 移動先ãŒè‡ªåˆ†è‡ªèº«ãªã‚‰reject
- if (folder.id == props.folder.id) return;
+ if (folder.id === props.folder.id) return;
emit('removeFolder', folder.id);
os.api('drive/folders/update', {
@@ -204,7 +204,7 @@ function deleteFolder() {
defaultStore.set('uploadFolder', null);
}
}).catch(err => {
- switch(err.id) {
+ switch (err.id) {
case 'b0fc8a17-963c-405d-bfbc-859a487295e1':
os.alert({
type: 'error',
@@ -230,7 +230,7 @@ function onContextmenu(ev: MouseEvent) {
text: i18n.ts.openInWindow,
icon: 'fas fa-window-restore',
action: () => {
- os.popup(import('./drive-window.vue'), {
+ os.popup(defineAsyncComponent(() => import('./drive-window.vue')), {
initialFolder: props.folder
}, {
}, 'closed');
diff --git a/packages/client/src/components/drive.nav-folder.vue b/packages/client/src/components/drive.nav-folder.vue
index 67223267c1..5482703317 100644
--- a/packages/client/src/components/drive.nav-folder.vue
+++ b/packages/client/src/components/drive.nav-folder.vue
@@ -24,10 +24,10 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'move', v?: Misskey.entities.DriveFolder): void;
- (e: 'upload', file: File, folder?: Misskey.entities.DriveFolder | null): void;
- (e: 'removeFile', v: Misskey.entities.DriveFile['id']): void;
- (e: 'removeFolder', v: Misskey.entities.DriveFolder['id']): void;
+ (ev: 'move', v?: Misskey.entities.DriveFolder): void;
+ (ev: 'upload', file: File, folder?: Misskey.entities.DriveFolder | null): void;
+ (ev: 'removeFile', v: Misskey.entities.DriveFile['id']): void;
+ (ev: 'removeFolder', v: Misskey.entities.DriveFolder['id']): void;
}>();
const hover = ref(false);
@@ -45,22 +45,22 @@ function onMouseout() {
hover.value = false;
}
-function onDragover(e: DragEvent) {
- if (!e.dataTransfer) return;
+function onDragover(ev: DragEvent) {
+ if (!ev.dataTransfer) return;
// ã“ã®ãƒ•ォルダãŒãƒ«ãƒ¼ãƒˆã‹ã¤ã‚«ãƒ¬ãƒ³ãƒˆãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªãªã‚‰ãƒ‰ãƒ­ãƒƒãƒ—ç¦æ­¢
if (props.folder == null && props.parentFolder == null) {
- e.dataTransfer.dropEffect = 'none';
+ ev.dataTransfer.dropEffect = 'none';
}
- const isFile = e.dataTransfer.items[0].kind == 'file';
- const isDriveFile = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FILE_;
- const isDriveFolder = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FOLDER_;
+ const isFile = ev.dataTransfer.items[0].kind === 'file';
+ const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
+ const isDriveFolder = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FOLDER_;
if (isFile || isDriveFile || isDriveFolder) {
- e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
+ ev.dataTransfer.dropEffect = ev.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move';
} else {
- e.dataTransfer.dropEffect = 'none';
+ ev.dataTransfer.dropEffect = 'none';
}
return false;
@@ -74,22 +74,22 @@ function onDragleave() {
if (props.folder || props.parentFolder) draghover.value = false;
}
-function onDrop(e: DragEvent) {
+function onDrop(ev: DragEvent) {
draghover.value = false;
- if (!e.dataTransfer) return;
+ if (!ev.dataTransfer) return;
// ファイルã ã£ãŸã‚‰
- if (e.dataTransfer.files.length > 0) {
- for (const file of Array.from(e.dataTransfer.files)) {
+ if (ev.dataTransfer.files.length > 0) {
+ for (const file of Array.from(ev.dataTransfer.files)) {
emit('upload', file, props.folder);
}
return;
}
//#region ドライブã®ãƒ•ァイル
- const driveFile = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
- if (driveFile != null && driveFile != '') {
+ const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
+ if (driveFile != null && driveFile !== '') {
const file = JSON.parse(driveFile);
emit('removeFile', file.id);
os.api('drive/files/update', {
@@ -100,11 +100,11 @@ function onDrop(e: DragEvent) {
//#endregion
//#region ドライブã®ãƒ•ォルダ
- const driveFolder = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FOLDER_);
- if (driveFolder != null && driveFolder != '') {
+ const driveFolder = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FOLDER_);
+ if (driveFolder != null && driveFolder !== '') {
const folder = JSON.parse(driveFolder);
// 移動先ãŒè‡ªåˆ†è‡ªèº«ãªã‚‰reject
- if (props.folder && folder.id == props.folder.id) return;
+ if (props.folder && folder.id === props.folder.id) return;
emit('removeFolder', folder.id);
os.api('drive/folders/update', {
folderId: folder.id,
diff --git a/packages/client/src/components/drive.vue b/packages/client/src/components/drive.vue
index e044c67523..6c2c8acad0 100644
--- a/packages/client/src/components/drive.vue
+++ b/packages/client/src/components/drive.vue
@@ -97,6 +97,7 @@ import * as os from '@/os';
import { stream } from '@/stream';
import { defaultStore } from '@/store';
import { i18n } from '@/i18n';
+import { uploadFile, uploads } from '@/scripts/upload';
const props = withDefaults(defineProps<{
initialFolder?: Misskey.entities.DriveFolder;
@@ -109,11 +110,11 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'selected', v: Misskey.entities.DriveFile | Misskey.entities.DriveFolder): void;
- (e: 'change-selection', v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]): void;
- (e: 'move-root'): void;
- (e: 'cd', v: Misskey.entities.DriveFolder | null): void;
- (e: 'open-folder', v: Misskey.entities.DriveFolder): void;
+ (ev: 'selected', v: Misskey.entities.DriveFile | Misskey.entities.DriveFolder): void;
+ (ev: 'change-selection', v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]): void;
+ (ev: 'move-root'): void;
+ (ev: 'cd', v: Misskey.entities.DriveFolder | null): void;
+ (ev: 'open-folder', v: Misskey.entities.DriveFolder): void;
}>();
const loadMoreFiles = ref<InstanceType<typeof MkButton>>();
@@ -127,8 +128,9 @@ const moreFolders = ref(false);
const hierarchyFolders = ref<Misskey.entities.DriveFolder[]>([]);
const selectedFiles = ref<Misskey.entities.DriveFile[]>([]);
const selectedFolders = ref<Misskey.entities.DriveFolder[]>([]);
-const uploadings = os.uploads;
+const uploadings = uploads;
const connection = stream.useChannel('drive');
+const keepOriginal = ref<boolean>(defaultStore.state.keepOriginalUploading); // 外部渡ã—ãŒå¤šã„ã®ã§$refã¯ä½¿ã‚ãªã„ã»ã†ãŒã‚ˆã„
// ドロップã•れよã†ã¨ã—ã¦ã„ã‚‹ã‹
const draghover = ref(false);
@@ -141,7 +143,7 @@ const fetching = ref(true);
const ilFilesObserver = new IntersectionObserver(
(entries) => entries.some((entry) => entry.isIntersecting) && !fetching.value && moreFiles.value && fetchMoreFiles()
-)
+);
watch(folder, () => emit('cd', folder.value));
@@ -151,7 +153,7 @@ function onStreamDriveFileCreated(file: Misskey.entities.DriveFile) {
function onStreamDriveFileUpdated(file: Misskey.entities.DriveFile) {
const current = folder.value ? folder.value.id : null;
- if (current != file.folderId) {
+ if (current !== file.folderId) {
removeFile(file);
} else {
addFile(file, true);
@@ -168,7 +170,7 @@ function onStreamDriveFolderCreated(createdFolder: Misskey.entities.DriveFolder)
function onStreamDriveFolderUpdated(updatedFolder: Misskey.entities.DriveFolder) {
const current = folder.value ? folder.value.id : null;
- if (current != updatedFolder.parentId) {
+ if (current !== updatedFolder.parentId) {
removeFolder(updatedFolder);
} else {
addFolder(updatedFolder, true);
@@ -179,23 +181,23 @@ function onStreamDriveFolderDeleted(folderId: string) {
removeFolder(folderId);
}
-function onDragover(e: DragEvent): any {
- if (!e.dataTransfer) return;
+function onDragover(ev: DragEvent): any {
+ if (!ev.dataTransfer) return;
// ドラッグ元ãŒè‡ªåˆ†è‡ªèº«ã®æ‰€æœ‰ã™ã‚‹ã‚¢ã‚¤ãƒ†ãƒ ã ã£ãŸã‚‰
if (isDragSource.value) {
// 自分自身ã«ã¯ãƒ‰ãƒ­ãƒƒãƒ—ã•ã›ãªã„
- e.dataTransfer.dropEffect = 'none';
+ ev.dataTransfer.dropEffect = 'none';
return;
}
- const isFile = e.dataTransfer.items[0].kind == 'file';
- const isDriveFile = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FILE_;
- const isDriveFolder = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FOLDER_;
+ const isFile = ev.dataTransfer.items[0].kind === 'file';
+ const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
+ const isDriveFolder = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FOLDER_;
if (isFile || isDriveFile || isDriveFolder) {
- e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
+ ev.dataTransfer.dropEffect = ev.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move';
} else {
- e.dataTransfer.dropEffect = 'none';
+ ev.dataTransfer.dropEffect = 'none';
}
return false;
@@ -209,24 +211,24 @@ function onDragleave() {
draghover.value = false;
}
-function onDrop(e: DragEvent): any {
+function onDrop(ev: DragEvent): any {
draghover.value = false;
- if (!e.dataTransfer) return;
+ if (!ev.dataTransfer) return;
// ドロップã•れã¦ããŸã‚‚ã®ãŒãƒ•ァイルã ã£ãŸã‚‰
- if (e.dataTransfer.files.length > 0) {
- for (const file of Array.from(e.dataTransfer.files)) {
+ if (ev.dataTransfer.files.length > 0) {
+ for (const file of Array.from(ev.dataTransfer.files)) {
upload(file, folder.value);
}
return;
}
//#region ドライブã®ãƒ•ァイル
- const driveFile = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
- if (driveFile != null && driveFile != '') {
+ const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
+ if (driveFile != null && driveFile !== '') {
const file = JSON.parse(driveFile);
- if (files.value.some(f => f.id == file.id)) return;
+ if (files.value.some(f => f.id === file.id)) return;
removeFile(file.id);
os.api('drive/files/update', {
fileId: file.id,
@@ -236,13 +238,13 @@ function onDrop(e: DragEvent): any {
//#endregion
//#region ドライブã®ãƒ•ォルダ
- const driveFolder = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FOLDER_);
- if (driveFolder != null && driveFolder != '') {
+ const driveFolder = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FOLDER_);
+ if (driveFolder != null && driveFolder !== '') {
const droppedFolder = JSON.parse(driveFolder);
// 移動先ãŒè‡ªåˆ†è‡ªèº«ãªã‚‰reject
- if (folder.value && droppedFolder.id == folder.value.id) return false;
- if (folders.value.some(f => f.id == droppedFolder.id)) return false;
+ if (folder.value && droppedFolder.id === folder.value.id) return false;
+ if (folders.value.some(f => f.id === droppedFolder.id)) return false;
removeFolder(droppedFolder.id);
os.api('drive/folders/update', {
folderId: droppedFolder.id,
@@ -330,7 +332,7 @@ function deleteFolder(folderToDelete: Misskey.entities.DriveFolder) {
// 削除時ã«è¦ªãƒ•ォルダã«ç§»å‹•
move(folderToDelete.parentId);
}).catch(err => {
- switch(err.id) {
+ switch (err.id) {
case 'b0fc8a17-963c-405d-bfbc-859a487295e1':
os.alert({
type: 'error',
@@ -355,16 +357,16 @@ function onChangeFileInput() {
}
function upload(file: File, folderToUpload?: Misskey.entities.DriveFolder | null) {
- os.upload(file, (folderToUpload && typeof folderToUpload == 'object') ? folderToUpload.id : null).then(res => {
+ uploadFile(file, (folderToUpload && typeof folderToUpload === 'object') ? folderToUpload.id : null, undefined, keepOriginal.value).then(res => {
addFile(res, true);
});
}
function chooseFile(file: Misskey.entities.DriveFile) {
- const isAlreadySelected = selectedFiles.value.some(f => f.id == file.id);
+ const isAlreadySelected = selectedFiles.value.some(f => f.id === file.id);
if (props.multiple) {
if (isAlreadySelected) {
- selectedFiles.value = selectedFiles.value.filter(f => f.id != file.id);
+ selectedFiles.value = selectedFiles.value.filter(f => f.id !== file.id);
} else {
selectedFiles.value.push(file);
}
@@ -380,10 +382,10 @@ function chooseFile(file: Misskey.entities.DriveFile) {
}
function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) {
- const isAlreadySelected = selectedFolders.value.some(f => f.id == folderToChoose.id);
+ const isAlreadySelected = selectedFolders.value.some(f => f.id === folderToChoose.id);
if (props.multiple) {
if (isAlreadySelected) {
- selectedFolders.value = selectedFolders.value.filter(f => f.id != folderToChoose.id);
+ selectedFolders.value = selectedFolders.value.filter(f => f.id !== folderToChoose.id);
} else {
selectedFolders.value.push(folderToChoose);
}
@@ -402,7 +404,7 @@ function move(target?: Misskey.entities.DriveFolder) {
if (!target) {
goRoot();
return;
- } else if (typeof target == 'object') {
+ } else if (typeof target === 'object') {
target = target.id;
}
@@ -428,9 +430,9 @@ function move(target?: Misskey.entities.DriveFolder) {
function addFolder(folderToAdd: Misskey.entities.DriveFolder, unshift = false) {
const current = folder.value ? folder.value.id : null;
- if (current != folderToAdd.parentId) return;
+ if (current !== folderToAdd.parentId) return;
- if (folders.value.some(f => f.id == folderToAdd.id)) {
+ if (folders.value.some(f => f.id === folderToAdd.id)) {
const exist = folders.value.map(f => f.id).indexOf(folderToAdd.id);
folders.value[exist] = folderToAdd;
return;
@@ -445,9 +447,9 @@ function addFolder(folderToAdd: Misskey.entities.DriveFolder, unshift = false) {
function addFile(fileToAdd: Misskey.entities.DriveFile, unshift = false) {
const current = folder.value ? folder.value.id : null;
- if (current != fileToAdd.folderId) return;
+ if (current !== fileToAdd.folderId) return;
- if (files.value.some(f => f.id == fileToAdd.id)) {
+ if (files.value.some(f => f.id === fileToAdd.id)) {
const exist = files.value.map(f => f.id).indexOf(fileToAdd.id);
files.value[exist] = fileToAdd;
return;
@@ -462,12 +464,12 @@ function addFile(fileToAdd: Misskey.entities.DriveFile, unshift = false) {
function removeFolder(folderToRemove: Misskey.entities.DriveFolder | string) {
const folderIdToRemove = typeof folderToRemove === 'object' ? folderToRemove.id : folderToRemove;
- folders.value = folders.value.filter(f => f.id != folderIdToRemove);
+ folders.value = folders.value.filter(f => f.id !== folderIdToRemove);
}
function removeFile(file: Misskey.entities.DriveFile | string) {
const fileId = typeof file === 'object' ? file.id : file;
- files.value = files.value.filter(f => f.id != fileId);
+ files.value = files.value.filter(f => f.id !== fileId);
}
function appendFile(file: Misskey.entities.DriveFile) {
@@ -510,7 +512,7 @@ async function fetch() {
folderId: folder.value ? folder.value.id : null,
limit: foldersMax + 1
}).then(fetchedFolders => {
- if (fetchedFolders.length == foldersMax + 1) {
+ if (fetchedFolders.length === foldersMax + 1) {
moreFolders.value = true;
fetchedFolders.pop();
}
@@ -522,7 +524,7 @@ async function fetch() {
type: props.type,
limit: filesMax + 1
}).then(fetchedFiles => {
- if (fetchedFiles.length == filesMax + 1) {
+ if (fetchedFiles.length === filesMax + 1) {
moreFiles.value = true;
fetchedFiles.pop();
}
@@ -549,7 +551,7 @@ function fetchMoreFiles() {
untilId: files.value[files.value.length - 1].id,
limit: max + 1
}).then(files => {
- if (files.length == max + 1) {
+ if (files.length === max + 1) {
moreFiles.value = true;
files.pop();
} else {
@@ -562,6 +564,10 @@ function fetchMoreFiles() {
function getMenu() {
return [{
+ type: 'switch',
+ text: i18n.ts.keepOriginalUploading,
+ ref: keepOriginal,
+ }, null, {
text: i18n.ts.addFile,
type: 'label'
}, {
@@ -601,7 +607,7 @@ function onContextmenu(ev: MouseEvent) {
onMounted(() => {
if (defaultStore.state.enableInfiniteScroll && loadMoreFiles.value) {
nextTick(() => {
- ilFilesObserver.observe(loadMoreFiles.value?.$el)
+ ilFilesObserver.observe(loadMoreFiles.value?.$el);
});
}
@@ -622,7 +628,7 @@ onMounted(() => {
onActivated(() => {
if (defaultStore.state.enableInfiniteScroll) {
nextTick(() => {
- ilFilesObserver.observe(loadMoreFiles.value?.$el)
+ ilFilesObserver.observe(loadMoreFiles.value?.$el);
});
}
});
diff --git a/packages/client/src/components/emoji-picker-window.vue b/packages/client/src/components/emoji-picker-window.vue
index 4d27fb48ba..610690d701 100644
--- a/packages/client/src/components/emoji-picker-window.vue
+++ b/packages/client/src/components/emoji-picker-window.vue
@@ -25,8 +25,8 @@ withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'chosen', v: any): void;
- (e: 'closed'): void;
+ (ev: 'chosen', v: any): void;
+ (ev: 'closed'): void;
}>();
function chosen(emoji: any) {
diff --git a/packages/client/src/components/emoji-picker.section.vue b/packages/client/src/components/emoji-picker.section.vue
index 1026e894d1..52f7047487 100644
--- a/packages/client/src/components/emoji-picker.section.vue
+++ b/packages/client/src/components/emoji-picker.section.vue
@@ -24,7 +24,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'chosen', v: string, ev: MouseEvent): void;
+ (ev: 'chosen', v: string, event: MouseEvent): void;
}>();
const shown = ref(!!props.initialShown);
diff --git a/packages/client/src/components/emoji-picker.vue b/packages/client/src/components/emoji-picker.vue
index 8601ea121c..64732e7033 100644
--- a/packages/client/src/components/emoji-picker.vue
+++ b/packages/client/src/components/emoji-picker.vue
@@ -1,6 +1,6 @@
<template>
<div class="omfetrab" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }">
- <input ref="search" v-model.trim="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" @paste.stop="paste" @keyup.enter="done()">
+ <input ref="search" v-model.trim="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" @paste.stop="paste" @keyup.enter="done()">
<div ref="emojis" class="emojis">
<section class="result">
<div v-if="searchResultCustom.length > 0">
@@ -61,7 +61,7 @@
</div>
<div>
<header class="_acrylic">{{ i18n.ts.emoji }}</header>
- <XSection v-for="category in categories" :emojis="emojilist.filter(e => e.category === category).map(e => e.char)" @chosen="chosen">{{ category }}</XSection>
+ <XSection v-for="category in categories" :key="category" :emojis="emojilist.filter(e => e.category === category).map(e => e.char)" @chosen="chosen">{{ category }}</XSection>
</div>
</div>
<div class="tabs">
@@ -97,7 +97,7 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'chosen', v: string): void;
+ (ev: 'chosen', v: string): void;
}>();
const search = ref<HTMLInputElement>();
@@ -138,7 +138,7 @@ watch(q, () => {
const emojis = customEmojis;
const matches = new Set<Misskey.entities.CustomEmoji>();
- const exactMatch = emojis.find(e => e.name === newQ);
+ const exactMatch = emojis.find(emoji => emoji.name === newQ);
if (exactMatch) matches.add(exactMatch);
if (newQ.includes(' ')) { // AND検索
@@ -201,7 +201,7 @@ watch(q, () => {
const emojis = emojilist;
const matches = new Set<UnicodeEmojiDef>();
- const exactMatch = emojis.find(e => e.name === newQ);
+ const exactMatch = emojis.find(emoji => emoji.name === newQ);
if (exactMatch) matches.add(exactMatch);
if (newQ.includes(' ')) { // AND検索
@@ -295,7 +295,7 @@ function chosen(emoji: any, ev?: MouseEvent) {
// 最近使ã£ãŸçµµæ–‡å­—æ›´æ–°
if (!pinned.value.includes(key)) {
let recents = defaultStore.state.recentlyUsedEmojis;
- recents = recents.filter((e: any) => e !== key);
+ recents = recents.filter((emoji: any) => emoji !== key);
recents.unshift(key);
defaultStore.set('recentlyUsedEmojis', recents.splice(0, 32));
}
@@ -313,12 +313,12 @@ function done(query?: any): boolean | void {
if (query == null || typeof query !== 'string') return;
const q2 = query.replace(/:/g, '');
- const exactMatchCustom = customEmojis.find(e => e.name === q2);
+ const exactMatchCustom = customEmojis.find(emoji => emoji.name === q2);
if (exactMatchCustom) {
chosen(exactMatchCustom);
return true;
}
- const exactMatchUnicode = emojilist.find(e => e.char === q2 || e.name === q2);
+ const exactMatchUnicode = emojilist.find(emoji => emoji.char === q2 || emoji.name === q2);
if (exactMatchUnicode) {
chosen(exactMatchUnicode);
return true;
diff --git a/packages/client/src/components/follow-button.vue b/packages/client/src/components/follow-button.vue
index 93c9e891c1..efee795e43 100644
--- a/packages/client/src/components/follow-button.vue
+++ b/packages/client/src/components/follow-button.vue
@@ -28,7 +28,7 @@
</template>
<script lang="ts" setup>
-import { onBeforeUnmount, onMounted, ref } from 'vue';
+import { onBeforeUnmount, onMounted } from 'vue';
import * as Misskey from 'misskey-js';
import * as os from '@/os';
import { stream } from '@/stream';
@@ -43,32 +43,30 @@ const props = withDefaults(defineProps<{
large: false,
});
-const isFollowing = ref(props.user.isFollowing);
-const hasPendingFollowRequestFromYou = ref(props.user.hasPendingFollowRequestFromYou);
-const wait = ref(false);
+let isFollowing = $ref(props.user.isFollowing);
+let hasPendingFollowRequestFromYou = $ref(props.user.hasPendingFollowRequestFromYou);
+let wait = $ref(false);
const connection = stream.useChannel('main');
if (props.user.isFollowing == null) {
os.api('users/show', {
userId: props.user.id
- }).then(u => {
- isFollowing.value = u.isFollowing;
- hasPendingFollowRequestFromYou.value = u.hasPendingFollowRequestFromYou;
- });
+ })
+ .then(onFollowChange);
}
function onFollowChange(user: Misskey.entities.UserDetailed) {
- if (user.id == props.user.id) {
- isFollowing.value = user.isFollowing;
- hasPendingFollowRequestFromYou.value = user.hasPendingFollowRequestFromYou;
+ if (user.id === props.user.id) {
+ isFollowing = user.isFollowing;
+ hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
}
}
async function onClick() {
- wait.value = true;
+ wait = true;
try {
- if (isFollowing.value) {
+ if (isFollowing) {
const { canceled } = await os.confirm({
type: 'warning',
text: i18n.t('unfollowConfirm', { name: props.user.name || props.user.username }),
@@ -80,26 +78,22 @@ async function onClick() {
userId: props.user.id
});
} else {
- if (hasPendingFollowRequestFromYou.value) {
+ if (hasPendingFollowRequestFromYou) {
await os.api('following/requests/cancel', {
userId: props.user.id
});
- } else if (props.user.isLocked) {
- await os.api('following/create', {
- userId: props.user.id
- });
- hasPendingFollowRequestFromYou.value = true;
+ hasPendingFollowRequestFromYou = false;
} else {
await os.api('following/create', {
userId: props.user.id
});
- hasPendingFollowRequestFromYou.value = true;
+ hasPendingFollowRequestFromYou = true;
}
}
- } catch (e) {
- console.error(e);
+ } catch (err) {
+ console.error(err);
} finally {
- wait.value = false;
+ wait = false;
}
}
diff --git a/packages/client/src/components/forgot-password.vue b/packages/client/src/components/forgot-password.vue
index 46cbf6bd70..19c1f23c85 100644
--- a/packages/client/src/components/forgot-password.vue
+++ b/packages/client/src/components/forgot-password.vue
@@ -41,8 +41,8 @@ import { instance } from '@/instance';
import { i18n } from '@/i18n';
const emit = defineEmits<{
- (e: 'done'): void;
- (e: 'closed'): void;
+ (ev: 'done'): void;
+ (ev: 'closed'): void;
}>();
let dialog: InstanceType<typeof XModalWindow> = $ref();
diff --git a/packages/client/src/components/form-dialog.vue b/packages/client/src/components/form-dialog.vue
index efd0da443d..11459f5937 100644
--- a/packages/client/src/components/form-dialog.vue
+++ b/packages/client/src/components/form-dialog.vue
@@ -44,7 +44,7 @@
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template>
<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
</FormRange>
- <MkButton v-else-if="form[item].type === 'button'" @click="form[item].action($event, values)" class="_formBlock">
+ <MkButton v-else-if="form[item].type === 'button'" class="_formBlock" @click="form[item].action($event, values)">
<span v-text="form[item].content || item"></span>
</MkButton>
</template>
diff --git a/packages/client/src/components/form/folder.vue b/packages/client/src/components/form/folder.vue
index 571afe50c0..1b960657d7 100644
--- a/packages/client/src/components/form/folder.vue
+++ b/packages/client/src/components/form/folder.vue
@@ -24,7 +24,7 @@ const props = withDefaults(defineProps<{
defaultOpen: boolean;
}>(), {
defaultOpen: false,
-})
+});
let opened = $ref(props.defaultOpen);
let openedAtLeastOnce = $ref(props.defaultOpen);
diff --git a/packages/client/src/components/form/radios.vue b/packages/client/src/components/form/radios.vue
index ff5d51f9c7..a52acae9e1 100644
--- a/packages/client/src/components/form/radios.vue
+++ b/packages/client/src/components/form/radios.vue
@@ -14,7 +14,7 @@ export default defineComponent({
data() {
return {
value: this.modelValue,
- }
+ };
},
watch: {
value() {
diff --git a/packages/client/src/components/form/range.vue b/packages/client/src/components/form/range.vue
index a82348d317..07f2c23124 100644
--- a/packages/client/src/components/form/range.vue
+++ b/packages/client/src/components/form/range.vue
@@ -16,7 +16,7 @@
</template>
<script lang="ts">
-import { computed, defineComponent, onMounted, onUnmounted, ref, watch } from 'vue';
+import { computed, defineAsyncComponent, defineComponent, onMounted, onUnmounted, ref, watch } from 'vue';
import * as os from '@/os';
export default defineComponent({
@@ -112,7 +112,7 @@ export default defineComponent({
ev.preventDefault();
const tooltipShowing = ref(true);
- os.popup(import('@/components/ui/tooltip.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/ui/tooltip.vue')), {
showing: tooltipShowing,
text: computed(() => {
return props.textConverter(finalValue.value);
diff --git a/packages/client/src/components/form/switch.vue b/packages/client/src/components/form/switch.vue
index b5a30d635c..fadb770aee 100644
--- a/packages/client/src/components/form/switch.vue
+++ b/packages/client/src/components/form/switch.vue
@@ -31,7 +31,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'update:modelValue', v: boolean): void;
+ (ev: 'update:modelValue', v: boolean): void;
}>();
let button = $ref<HTMLElement>();
diff --git a/packages/client/src/components/global/a.vue b/packages/client/src/components/global/a.vue
index 52fef50f9b..5287d59b3e 100644
--- a/packages/client/src/components/global/a.vue
+++ b/packages/client/src/components/global/a.vue
@@ -5,14 +5,13 @@
</template>
<script lang="ts" setup>
-import { inject } from 'vue';
import * as os from '@/os';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import { router } from '@/router';
import { url } from '@/config';
import { popout as popout_ } from '@/scripts/popout';
import { i18n } from '@/i18n';
-import { defaultStore } from '@/store';
+import { MisskeyNavigator } from '@/scripts/navigate';
const props = withDefaults(defineProps<{
to: string;
@@ -23,9 +22,7 @@ const props = withDefaults(defineProps<{
behavior: null,
});
-type Navigate = (path: string, record?: boolean) => void;
-const navHook = inject<null | Navigate>('navHook', null);
-const sideViewHook = inject<null | Navigate>('sideViewHook', null);
+const mkNav = new MisskeyNavigator();
const active = $computed(() => {
if (props.activeClass == null) return false;
@@ -48,11 +45,11 @@ function onContextmenu(ev) {
action: () => {
os.pageWindow(props.to);
}
- }, sideViewHook ? {
+ }, mkNav.sideViewHook ? {
icon: 'fas fa-columns',
text: i18n.ts.openInSideView,
action: () => {
- sideViewHook(props.to);
+ if (mkNav.sideViewHook) mkNav.sideViewHook(props.to);
}
} : undefined, {
icon: 'fas fa-expand-alt',
@@ -101,18 +98,6 @@ function nav() {
}
}
- if (navHook) {
- navHook(props.to);
- } else {
- if (defaultStore.state.defaultSideView && sideViewHook && props.to !== '/') {
- return sideViewHook(props.to);
- }
-
- if (router.currentRoute.value.path === props.to) {
- window.scroll({ top: 0, behavior: 'smooth' });
- } else {
- router.push(props.to);
- }
- }
+ mkNav.push(props.to);
}
</script>
diff --git a/packages/client/src/components/global/avatar.vue b/packages/client/src/components/global/avatar.vue
index 27cfb6e4d4..4868896c99 100644
--- a/packages/client/src/components/global/avatar.vue
+++ b/packages/client/src/components/global/avatar.vue
@@ -32,7 +32,7 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'click', ev: MouseEvent): void;
+ (ev: 'click', v: MouseEvent): void;
}>();
const url = $computed(() => defaultStore.state.disableShowingAnimatedImages
diff --git a/packages/client/src/components/global/emoji.vue b/packages/client/src/components/global/emoji.vue
index 92edb1caf9..0075e0867d 100644
--- a/packages/client/src/components/global/emoji.vue
+++ b/packages/client/src/components/global/emoji.vue
@@ -46,7 +46,7 @@ export default defineComponent({
const url = computed(() => {
if (char.value) {
let codes = Array.from(char.value).map(x => x.codePointAt(0).toString(16));
- if (!codes.includes('200d')) codes = codes.filter(x => x != 'fe0f');
+ if (!codes.includes('200d')) codes = codes.filter(x => x !== 'fe0f');
codes = codes.filter(x => x && x.length);
return `${twemojiSvgBase}/${codes.join('-')}.svg`;
} else {
diff --git a/packages/client/src/components/global/header.vue b/packages/client/src/components/global/header.vue
index e558614c12..63db19a520 100644
--- a/packages/client/src/components/global/header.vue
+++ b/packages/client/src/components/global/header.vue
@@ -38,7 +38,7 @@
<script lang="ts">
import { computed, defineComponent, onMounted, onUnmounted, PropType, ref, inject } from 'vue';
-import * as tinycolor from 'tinycolor2';
+import tinycolor from 'tinycolor2';
import { popupMenu } from '@/os';
import { url } from '@/config';
import { scrollToTop } from '@/scripts/scroll';
diff --git a/packages/client/src/components/global/loading.vue b/packages/client/src/components/global/loading.vue
index 43ea1395ed..5a7e362fcf 100644
--- a/packages/client/src/components/global/loading.vue
+++ b/packages/client/src/components/global/loading.vue
@@ -1,11 +1,24 @@
<template>
-<div class="yxspomdl" :class="{ inline, colored, mini }">
- <div class="ring"></div>
+<div :class="[$style.root, { [$style.inline]: inline, [$style.colored]: colored, [$style.mini]: mini }]">
+ <div :class="$style.container">
+ <svg :class="[$style.spinner, $style.bg]" viewBox="0 0 168 168" xmlns="http://www.w3.org/2000/svg">
+ <g transform="matrix(1.125,0,0,1.125,12,12)">
+ <circle cx="64" cy="64" r="64" style="fill:none;stroke:currentColor;stroke-width:21.33px;"/>
+ </g>
+ </svg>
+ <svg :class="[$style.spinner, $style.fg]" viewBox="0 0 168 168" xmlns="http://www.w3.org/2000/svg">
+ <g transform="matrix(1.125,0,0,1.125,12,12)">
+ <path d="M128,64C128,28.654 99.346,0 64,0C99.346,0 128,28.654 128,64Z" style="fill:none;stroke:currentColor;stroke-width:21.33px;"/>
+ </g>
+ </svg>
+ </div>
</div>
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { useCssModule } from 'vue';
+
+useCssModule();
const props = withDefaults(defineProps<{
inline?: boolean;
@@ -18,8 +31,8 @@ const props = withDefaults(defineProps<{
});
</script>
-<style lang="scss" scoped>
-@keyframes ring {
+<style lang="scss" module>
+@keyframes spinner {
0% {
transform: rotate(0deg);
}
@@ -28,12 +41,12 @@ const props = withDefaults(defineProps<{
}
}
-.yxspomdl {
+.root {
padding: 32px;
text-align: center;
cursor: wait;
- --size: 48px;
+ --size: 40px;
&.colored {
color: var(--accent);
@@ -49,34 +62,33 @@ const props = withDefaults(defineProps<{
padding: 16px;
--size: 32px;
}
+}
- > .ring {
- position: relative;
- display: inline-block;
- vertical-align: middle;
+.container {
+ position: relative;
+ width: var(--size);
+ height: var(--size);
+ margin: 0 auto;
+}
- &:before,
- &:after {
- content: " ";
- display: block;
- box-sizing: border-box;
- width: var(--size);
- height: var(--size);
- border-radius: 50%;
- border: solid 4px;
- }
+.spinner {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: var(--size);
+ height: var(--size);
+ fill-rule: evenodd;
+ clip-rule: evenodd;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-miterlimit: 1.5;
+}
- &:before {
- border-color: currentColor;
- opacity: 0.3;
- }
+.bg {
+ opacity: 0.275;
+}
- &:after {
- position: absolute;
- top: 0;
- border-color: currentColor transparent transparent transparent;
- animation: ring 0.5s linear infinite;
- }
- }
+.fg {
+ animation: spinner 0.5s linear infinite;
}
</style>
diff --git a/packages/client/src/components/global/misskey-flavored-markdown.vue b/packages/client/src/components/global/misskey-flavored-markdown.vue
index 243d8614ba..70d0108e9f 100644
--- a/packages/client/src/components/global/misskey-flavored-markdown.vue
+++ b/packages/client/src/components/global/misskey-flavored-markdown.vue
@@ -31,6 +31,32 @@ const props = withDefaults(defineProps<{
}
}
+.mfm-x2 {
+ --mfm-zoom-size: 200%;
+}
+
+.mfm-x3 {
+ --mfm-zoom-size: 400%;
+}
+
+.mfm-x4 {
+ --mfm-zoom-size: 600%;
+}
+
+.mfm-x2, .mfm-x3, .mfm-x4 {
+ font-size: var(--mfm-zoom-size);
+
+ .mfm-x2, .mfm-x3, .mfm-x4 {
+ /* only half effective */
+ font-size: calc(var(--mfm-zoom-size) / 2 + 50%);
+
+ .mfm-x2, .mfm-x3, .mfm-x4 {
+ /* disabled */
+ font-size: 100%;
+ }
+ }
+}
+
@keyframes mfm-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
diff --git a/packages/client/src/components/global/time.vue b/packages/client/src/components/global/time.vue
index 5748d9de61..a7f142f961 100644
--- a/packages/client/src/components/global/time.vue
+++ b/packages/client/src/components/global/time.vue
@@ -17,7 +17,7 @@ const props = withDefaults(defineProps<{
mode: 'relative',
});
-const _time = typeof props.time == 'string' ? new Date(props.time) : props.time;
+const _time = typeof props.time === 'string' ? new Date(props.time) : props.time;
const absolute = _time.toLocaleString();
let now = $ref(new Date());
@@ -32,8 +32,7 @@ const relative = $computed(() => {
ago >= 60 ? i18n.t('_ago.minutesAgo', { n: (~~(ago / 60)).toString() }) :
ago >= 10 ? i18n.t('_ago.secondsAgo', { n: (~~(ago % 60)).toString() }) :
ago >= -1 ? i18n.ts._ago.justNow :
- ago < -1 ? i18n.ts._ago.future :
- i18n.ts._ago.unknown);
+ i18n.ts._ago.future);
});
function tick() {
diff --git a/packages/client/src/components/global/url.vue b/packages/client/src/components/global/url.vue
index 55f6c5d5f9..34ba9024cc 100644
--- a/packages/client/src/components/global/url.vue
+++ b/packages/client/src/components/global/url.vue
@@ -18,7 +18,7 @@
</template>
<script lang="ts">
-import { defineComponent, ref } from 'vue';
+import { defineAsyncComponent, defineComponent, ref } from 'vue';
import { toUnicode as decodePunycode } from 'punycode/';
import { url as local } from '@/config';
import * as os from '@/os';
@@ -50,7 +50,7 @@ export default defineComponent({
const el = ref();
useTooltip(el, (showing) => {
- os.popup(import('@/components/url-preview-popup.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/url-preview-popup.vue')), {
showing,
url: props.url,
source: el.value,
diff --git a/packages/client/src/components/image-viewer.vue b/packages/client/src/components/image-viewer.vue
index c39076df16..7bc88399ef 100644
--- a/packages/client/src/components/image-viewer.vue
+++ b/packages/client/src/components/image-viewer.vue
@@ -25,7 +25,7 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'closed'): void;
+ (ev: 'closed'): void;
}>();
const modal = $ref<InstanceType<typeof MkModal>>();
diff --git a/packages/client/src/components/instance-ticker.vue b/packages/client/src/components/instance-ticker.vue
index 9b0a18ec90..c32409ecf4 100644
--- a/packages/client/src/components/instance-ticker.vue
+++ b/packages/client/src/components/instance-ticker.vue
@@ -39,6 +39,19 @@ const bg = {
border-radius: 4px 0 0 4px;
overflow: hidden;
color: #fff;
+ text-shadow: /* .866 ≈ sin(60deg) */
+ 1px 0 1px #000,
+ .866px .5px 1px #000,
+ .5px .866px 1px #000,
+ 0 1px 1px #000,
+ -.5px .866px 1px #000,
+ -.866px .5px 1px #000,
+ -1px 0 1px #000,
+ -.866px -.5px 1px #000,
+ -.5px -.866px 1px #000,
+ 0 -1px 1px #000,
+ .5px -.866px 1px #000,
+ .866px -.5px 1px #000;
> .icon {
height: 100%;
diff --git a/packages/client/src/components/link.vue b/packages/client/src/components/link.vue
index 317c931cec..846a9a3a76 100644
--- a/packages/client/src/components/link.vue
+++ b/packages/client/src/components/link.vue
@@ -8,7 +8,7 @@
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { defineAsyncComponent } from 'vue';
import { url as local } from '@/config';
import { useTooltip } from '@/scripts/use-tooltip';
import * as os from '@/os';
@@ -26,7 +26,7 @@ const target = self ? null : '_blank';
const el = $ref();
useTooltip($$(el), (showing) => {
- os.popup(import('@/components/url-preview-popup.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/url-preview-popup.vue')), {
showing,
url: props.url,
source: el,
diff --git a/packages/client/src/components/media-caption.vue b/packages/client/src/components/media-caption.vue
index ef546f3f70..feed3854f9 100644
--- a/packages/client/src/components/media-caption.vue
+++ b/packages/client/src/components/media-caption.vue
@@ -77,7 +77,7 @@ export default defineComponent({
computed: {
remainingLength(): number {
- if (typeof this.inputValue != "string") return 512;
+ if (typeof this.inputValue !== "string") return 512;
return 512 - length(this.inputValue);
}
},
@@ -116,17 +116,17 @@ export default defineComponent({
}
},
- onKeydown(e) {
- if (e.which === 27) { // ESC
+ onKeydown(evt) {
+ if (evt.which === 27) { // ESC
this.cancel();
}
},
- onInputKeydown(e) {
- if (e.which === 13) { // Enter
- if (e.ctrlKey) {
- e.preventDefault();
- e.stopPropagation();
+ onInputKeydown(evt) {
+ if (evt.which === 13) { // Enter
+ if (evt.ctrlKey) {
+ evt.preventDefault();
+ evt.stopPropagation();
this.ok();
}
}
diff --git a/packages/client/src/components/media-video.vue b/packages/client/src/components/media-video.vue
index 680eb27e64..5c38691e69 100644
--- a/packages/client/src/components/media-video.vue
+++ b/packages/client/src/components/media-video.vue
@@ -8,7 +8,8 @@
<div v-else class="kkjnbbplepmiyuadieoenjgutgcmtsvu">
<video
:poster="video.thumbnailUrl"
- :title="video.name"
+ :title="video.comment"
+ :alt="video.comment"
preload="none"
controls
@contextmenu.stop
diff --git a/packages/client/src/components/mention.vue b/packages/client/src/components/mention.vue
index 479acfbc8f..70c2f49afa 100644
--- a/packages/client/src/components/mention.vue
+++ b/packages/client/src/components/mention.vue
@@ -1,22 +1,22 @@
<template>
-<MkA v-if="url.startsWith('/')" v-user-preview="canonical" class="ldlomzub" :class="{ isMe }" :to="url" :style="{ background: bg }">
- <img class="icon" :src="`/avatar/@${username}@${host}`" alt="">
+<MkA v-if="url.startsWith('/')" v-user-preview="canonical" :class="[$style.root, { isMe }]" :to="url" :style="{ background: bg }">
+ <img :class="$style.icon" :src="`/avatar/@${username}@${host}`" alt="">
<span class="main">
<span class="username">@{{ username }}</span>
- <span v-if="(host != localHost) || $store.state.showFullAcct" class="host">@{{ toUnicode(host) }}</span>
+ <span v-if="(host != localHost) || $store.state.showFullAcct" :class="$style.mainHost">@{{ toUnicode(host) }}</span>
</span>
</MkA>
-<a v-else class="ldlomzub" :href="url" target="_blank" rel="noopener" :style="{ background: bg }">
+<a v-else :class="$style.root" :href="url" target="_blank" rel="noopener" :style="{ background: bg }">
<span class="main">
<span class="username">@{{ username }}</span>
- <span class="host">@{{ toUnicode(host) }}</span>
+ <span :class="$style.mainHost">@{{ toUnicode(host) }}</span>
</span>
</a>
</template>
<script lang="ts">
-import { defineComponent } from 'vue';
-import * as tinycolor from 'tinycolor2';
+import { defineComponent, useCssModule } from 'vue';
+import tinycolor from 'tinycolor2';
import { toUnicode } from 'punycode';
import { host as localHost } from '@/config';
import { $i } from '@/account';
@@ -45,6 +45,8 @@ export default defineComponent({
const bg = tinycolor(getComputedStyle(document.documentElement).getPropertyValue(isMe ? '--mentionMe' : '--mention'));
bg.setAlpha(0.1);
+ useCssModule();
+
return {
localHost,
isMe,
@@ -57,8 +59,8 @@ export default defineComponent({
});
</script>
-<style lang="scss" scoped>
-.ldlomzub {
+<style lang="scss" module>
+.root {
display: inline-block;
padding: 4px 8px 4px 4px;
border-radius: 999px;
@@ -67,18 +69,18 @@ export default defineComponent({
&.isMe {
color: var(--mentionMe);
}
+}
- > .icon {
- width: 1.5em;
- margin: 0 0.2em 0 0;
- vertical-align: bottom;
- border-radius: 100%;
- }
+.icon {
+ width: 1.5em;
+ height: 1.5em;
+ object-fit: cover;
+ margin: 0 0.2em 0 0;
+ vertical-align: bottom;
+ border-radius: 100%;
+}
- > .main {
- > .host {
- opacity: 0.5;
- }
- }
+.mainHost {
+ opacity: 0.5;
}
</style>
diff --git a/packages/client/src/components/mfm.ts b/packages/client/src/components/mfm.ts
index 37076652fd..4556a82d55 100644
--- a/packages/client/src/components/mfm.ts
+++ b/packages/client/src/components/mfm.ts
@@ -91,7 +91,8 @@ export default defineComponent({
let style;
switch (token.props.name) {
case 'tada': {
- style = `font-size: 150%;` + (this.$store.state.animatedMfm ? 'animation: tada 1s linear infinite both;' : '');
+ const speed = validTime(token.props.args.speed) || '1s';
+ style = 'font-size: 150%;' + (this.$store.state.animatedMfm ? `animation: tada ${speed} linear infinite both;` : '');
break;
}
case 'jelly': {
@@ -123,11 +124,13 @@ export default defineComponent({
break;
}
case 'jump': {
- style = this.$store.state.animatedMfm ? 'animation: mfm-jump 0.75s linear infinite;' : '';
+ const speed = validTime(token.props.args.speed) || '0.75s';
+ style = this.$store.state.animatedMfm ? `animation: mfm-jump ${speed} linear infinite;` : '';
break;
}
case 'bounce': {
- style = this.$store.state.animatedMfm ? 'animation: mfm-bounce 0.75s linear infinite; transform-origin: center bottom;' : '';
+ const speed = validTime(token.props.args.speed) || '0.75s';
+ style = this.$store.state.animatedMfm ? `animation: mfm-bounce ${speed} linear infinite; transform-origin: center bottom;` : '';
break;
}
case 'flip': {
@@ -139,16 +142,19 @@ export default defineComponent({
break;
}
case 'x2': {
- style = `font-size: 200%;`;
- break;
+ return h('span', {
+ class: 'mfm-x2',
+ }, genEl(token.children));
}
case 'x3': {
- style = `font-size: 400%;`;
- break;
+ return h('span', {
+ class: 'mfm-x3',
+ }, genEl(token.children));
}
case 'x4': {
- style = `font-size: 600%;`;
- break;
+ return h('span', {
+ class: 'mfm-x4',
+ }, genEl(token.children));
}
case 'font': {
const family =
@@ -168,7 +174,8 @@ export default defineComponent({
}, genEl(token.children));
}
case 'rainbow': {
- style = this.$store.state.animatedMfm ? 'animation: mfm-rainbow 1s linear infinite;' : '';
+ const speed = validTime(token.props.args.speed) || '1s';
+ style = this.$store.state.animatedMfm ? `animation: mfm-rainbow ${speed} linear infinite;` : '';
break;
}
case 'sparkle': {
diff --git a/packages/client/src/components/modal-page-window.vue b/packages/client/src/components/modal-page-window.vue
index 2e17d5d030..21bdb657b7 100644
--- a/packages/client/src/components/modal-page-window.vue
+++ b/packages/client/src/components/modal-page-window.vue
@@ -39,8 +39,8 @@ export default defineComponent({
inject: {
sideViewHook: {
- default: null
- }
+ default: null,
+ },
},
provide() {
@@ -94,31 +94,31 @@ export default defineComponent({
}, {
icon: 'fas fa-expand-alt',
text: this.$ts.showInPage,
- action: this.expand
+ action: this.expand,
}, this.sideViewHook ? {
icon: 'fas fa-columns',
text: this.$ts.openInSideView,
action: () => {
this.sideViewHook(this.path);
this.$refs.window.close();
- }
+ },
} : undefined, {
icon: 'fas fa-external-link-alt',
text: this.$ts.popout,
- action: this.popout
+ action: this.popout,
}, null, {
icon: 'fas fa-external-link-alt',
text: this.$ts.openInNewTab,
action: () => {
window.open(this.url, '_blank');
this.$refs.window.close();
- }
+ },
}, {
icon: 'fas fa-link',
text: this.$ts.copyLink,
action: () => {
copyToClipboard(this.url);
- }
+ },
}];
},
},
@@ -155,7 +155,7 @@ export default defineComponent({
onContextmenu(ev: MouseEvent) {
os.contextMenu(this.contextmenu, ev);
- }
+ },
},
});
</script>
diff --git a/packages/client/src/components/note-detailed.vue b/packages/client/src/components/note-detailed.vue
index d30284ca5f..6234b710d2 100644
--- a/packages/client/src/components/note-detailed.vue
+++ b/packages/client/src/components/note-detailed.vue
@@ -2,9 +2,9 @@
<div
v-if="!muted"
v-show="!isDeleted"
+ ref="el"
v-hotkey="keymap"
v-size="{ max: [500, 450, 350, 300] }"
- ref="el"
class="lxwezrsl _block"
:tabindex="!isDeleted ? '-1' : null"
:class="{ renote: isRenote }"
@@ -197,7 +197,7 @@ const keymap = {
'q': () => renoteButton.value.renote(true),
'esc': blur,
'm|o': () => menu(true),
- 's': () => showContent.value != showContent.value,
+ 's': () => showContent.value !== showContent.value,
};
useNoteCapture({
@@ -222,7 +222,7 @@ function react(viaKeyboard = false): void {
reactionPicker.show(reactButton.value, reaction => {
os.api('notes/reactions/create', {
noteId: appearNote.id,
- reaction: reaction
+ reaction: reaction,
});
}, () => {
focus();
@@ -233,7 +233,7 @@ function undoReact(note): void {
const oldReaction = note.myReaction;
if (!oldReaction) return;
os.api('notes/reactions/delete', {
- noteId: note.id
+ noteId: note.id,
});
}
@@ -257,7 +257,7 @@ function onContextmenu(ev: MouseEvent): void {
function menu(viaKeyboard = false): void {
os.popupMenu(getNoteMenu({ note: note, translating, translation, menuButton }), menuButton.value, {
- viaKeyboard
+ viaKeyboard,
}).then(focus);
}
@@ -269,12 +269,12 @@ function showRenoteMenu(viaKeyboard = false): void {
danger: true,
action: () => {
os.api('notes/delete', {
- noteId: note.id
+ noteId: note.id,
});
isDeleted.value = true;
- }
+ },
}], renoteTime.value, {
- viaKeyboard: viaKeyboard
+ viaKeyboard: viaKeyboard,
});
}
@@ -288,14 +288,14 @@ function blur() {
os.api('notes/children', {
noteId: appearNote.id,
- limit: 30
+ limit: 30,
}).then(res => {
replies.value = res;
});
if (appearNote.replyId) {
os.api('notes/conversation', {
- noteId: appearNote.replyId
+ noteId: appearNote.replyId,
}).then(res => {
conversation.value = res.reverse();
});
diff --git a/packages/client/src/components/note-simple.vue b/packages/client/src/components/note-simple.vue
index c6907787b5..b813b9a2b9 100644
--- a/packages/client/src/components/note-simple.vue
+++ b/packages/client/src/components/note-simple.vue
@@ -5,7 +5,7 @@
<XNoteHeader class="header" :note="note" :mini="true"/>
<div class="body">
<p v-if="note.cw != null" class="cw">
- <span v-if="note.cw != ''" class="text">{{ note.cw }}</span>
+ <Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$i" :custom-emojis="note.emojis"/>
<XCwButton v-model="showContent" :note="note"/>
</p>
<div v-show="note.cw == null || showContent" class="content">
diff --git a/packages/client/src/components/note.vue b/packages/client/src/components/note.vue
index 3cd7a819d4..e5744d1ce9 100644
--- a/packages/client/src/components/note.vue
+++ b/packages/client/src/components/note.vue
@@ -185,7 +185,7 @@ const keymap = {
'down|j|tab': focusAfter,
'esc': blur,
'm|o': () => menu(true),
- 's': () => showContent.value != showContent.value,
+ 's': () => showContent.value !== showContent.value,
};
useNoteCapture({
@@ -210,7 +210,7 @@ function react(viaKeyboard = false): void {
reactionPicker.show(reactButton.value, reaction => {
os.api('notes/reactions/create', {
noteId: appearNote.id,
- reaction: reaction
+ reaction: reaction,
});
}, () => {
focus();
@@ -221,7 +221,7 @@ function undoReact(note): void {
const oldReaction = note.myReaction;
if (!oldReaction) return;
os.api('notes/reactions/delete', {
- noteId: note.id
+ noteId: note.id,
});
}
@@ -245,7 +245,7 @@ function onContextmenu(ev: MouseEvent): void {
function menu(viaKeyboard = false): void {
os.popupMenu(getNoteMenu({ note: note, translating, translation, menuButton }), menuButton.value, {
- viaKeyboard
+ viaKeyboard,
}).then(focus);
}
@@ -257,12 +257,12 @@ function showRenoteMenu(viaKeyboard = false): void {
danger: true,
action: () => {
os.api('notes/delete', {
- noteId: note.id
+ noteId: note.id,
});
isDeleted.value = true;
- }
+ },
}], renoteTime.value, {
- viaKeyboard: viaKeyboard
+ viaKeyboard: viaKeyboard,
});
}
@@ -284,7 +284,7 @@ function focusAfter() {
function readPromo() {
os.api('promo/read', {
- noteId: appearNote.id
+ noteId: appearNote.id,
});
isDeleted.value = true;
}
diff --git a/packages/client/src/components/notification-setting-window.vue b/packages/client/src/components/notification-setting-window.vue
index ec1efec261..64d828394b 100644
--- a/packages/client/src/components/notification-setting-window.vue
+++ b/packages/client/src/components/notification-setting-window.vue
@@ -1,5 +1,6 @@
<template>
-<XModalWindow ref="dialog"
+<XModalWindow
+ ref="dialog"
:width="400"
:height="450"
:with-ok-button="true"
@@ -28,18 +29,18 @@
<script lang="ts">
import { defineComponent, PropType } from 'vue';
-import XModalWindow from '@/components/ui/modal-window.vue';
+import { notificationTypes } from 'misskey-js';
import MkSwitch from './form/switch.vue';
import MkInfo from './ui/info.vue';
import MkButton from './ui/button.vue';
-import { notificationTypes } from 'misskey-js';
+import XModalWindow from '@/components/ui/modal-window.vue';
export default defineComponent({
components: {
XModalWindow,
MkSwitch,
MkInfo,
- MkButton
+ MkButton,
},
props: {
@@ -53,7 +54,7 @@ export default defineComponent({
type: Boolean,
required: false,
default: true,
- }
+ },
},
emits: ['done', 'closed'],
@@ -93,7 +94,7 @@ export default defineComponent({
for (const type in this.typesMap) {
this.typesMap[type as typeof notificationTypes[number]] = true;
}
- }
- }
+ },
+ },
});
</script>
diff --git a/packages/client/src/components/notification.vue b/packages/client/src/components/notification.vue
index 1a360f9905..cbfd809f37 100644
--- a/packages/client/src/components/notification.vue
+++ b/packages/client/src/components/notification.vue
@@ -16,7 +16,8 @@
<i v-else-if="notification.type === 'pollVote'" class="fas fa-poll-h"></i>
<i v-else-if="notification.type === 'pollEnded'" class="fas fa-poll-h"></i>
<!-- notification.reaction ㌠null ã«ãªã‚‹ã“ã¨ã¯ã¾ãšãªã„ãŒã€ã“ã“ã§optional chaining使ã†ã¨ä¸€éƒ¨ãƒ–ラウザã§åˆºã•ã‚‹ã®ã§å¿µã®ç‚º -->
- <XReactionIcon v-else-if="notification.type === 'reaction'"
+ <XReactionIcon
+ v-else-if="notification.type === 'reaction'"
ref="reactionRef"
:reaction="notification.reaction ? notification.reaction.replace(/^:(\w+):$/, ':$1@.:') : notification.reaction"
:custom-emojis="notification.note.emojis"
@@ -72,12 +73,12 @@
</template>
<script lang="ts">
-import { defineComponent, ref, onMounted, onUnmounted } from 'vue';
+import { defineComponent, ref, onMounted, onUnmounted, watch } from 'vue';
import * as misskey from 'misskey-js';
-import { getNoteSummary } from '@/scripts/get-note-summary';
import XReactionIcon from './reaction-icon.vue';
import MkFollowButton from './follow-button.vue';
import XReactionTooltip from './reaction-tooltip.vue';
+import { getNoteSummary } from '@/scripts/get-note-summary';
import { notePage } from '@/filters/note';
import { userPage } from '@/filters/user';
import { i18n } from '@/i18n';
@@ -87,7 +88,7 @@ import { useTooltip } from '@/scripts/use-tooltip';
export default defineComponent({
components: {
- XReactionIcon, MkFollowButton
+ XReactionIcon, MkFollowButton,
},
props: {
@@ -116,7 +117,7 @@ export default defineComponent({
const readObserver = new IntersectionObserver((entries, observer) => {
if (!entries.some(entry => entry.isIntersecting)) return;
stream.send('readNotification', {
- id: props.notification.id
+ id: props.notification.id,
});
observer.disconnect();
});
@@ -126,6 +127,10 @@ export default defineComponent({
const connection = stream.useChannel('main');
connection.on('readAllNotifications', () => readObserver.disconnect());
+ watch(props.notification.isRead, () => {
+ readObserver.disconnect();
+ });
+
onUnmounted(() => {
readObserver.disconnect();
connection.dispose();
diff --git a/packages/client/src/components/notifications.vue b/packages/client/src/components/notifications.vue
index d522503a14..8eb569c369 100644
--- a/packages/client/src/components/notifications.vue
+++ b/packages/client/src/components/notifications.vue
@@ -19,8 +19,7 @@
<script lang="ts" setup>
import { defineComponent, markRaw, onUnmounted, onMounted, computed, ref } from 'vue';
import { notificationTypes } from 'misskey-js';
-import MkPagination from '@/components/ui/pagination.vue';
-import { Paging } from '@/components/ui/pagination.vue';
+import MkPagination, { Paging } from '@/components/ui/pagination.vue';
import XNotification from '@/components/notification.vue';
import XList from '@/components/date-separated-list.vue';
import XNote from '@/components/note.vue';
@@ -49,14 +48,14 @@ const onNotification = (notification) => {
const isMuted = props.includeTypes ? !props.includeTypes.includes(notification.type) : $i.mutingNotificationTypes.includes(notification.type);
if (isMuted || document.visibilityState === 'visible') {
stream.send('readNotification', {
- id: notification.id
+ id: notification.id,
});
}
if (!isMuted) {
pagingComponent.value.prepend({
...notification,
- isRead: document.visibilityState === 'visible'
+ isRead: document.visibilityState === 'visible',
});
}
};
@@ -64,6 +63,31 @@ const onNotification = (notification) => {
onMounted(() => {
const connection = stream.useChannel('main');
connection.on('notification', onNotification);
+ connection.on('readAllNotifications', () => {
+ if (pagingComponent.value) {
+ for (const item of pagingComponent.value.queue) {
+ item.isRead = true;
+ }
+ for (const item of pagingComponent.value.items) {
+ item.isRead = true;
+ }
+ }
+ });
+ connection.on('readNotifications', notificationIds => {
+ if (pagingComponent.value) {
+ for (let i = 0; i < pagingComponent.value.queue.length; i++) {
+ if (notificationIds.includes(pagingComponent.value.queue[i].id)) {
+ pagingComponent.value.queue[i].isRead = true;
+ }
+ }
+ for (let i = 0; i < (pagingComponent.value.items || []).length; i++) {
+ if (notificationIds.includes(pagingComponent.value.items[i].id)) {
+ pagingComponent.value.items[i].isRead = true;
+ }
+ }
+ }
+ });
+
onUnmounted(() => {
connection.dispose();
});
diff --git a/packages/client/src/components/number-diff.vue b/packages/client/src/components/number-diff.vue
index 9889c97ec3..e7d4a5472a 100644
--- a/packages/client/src/components/number-diff.vue
+++ b/packages/client/src/components/number-diff.vue
@@ -12,7 +12,7 @@ export default defineComponent({
props: {
value: {
type: Number,
- required: true
+ required: true,
},
},
@@ -26,7 +26,7 @@ export default defineComponent({
isZero,
number,
};
- }
+ },
});
</script>
diff --git a/packages/client/src/components/page/page.image.vue b/packages/client/src/components/page/page.image.vue
index 04ce74bd7c..6e38a9f424 100644
--- a/packages/client/src/components/page/page.image.vue
+++ b/packages/client/src/components/page/page.image.vue
@@ -1,34 +1,22 @@
<template>
<div class="lzyxtsnt">
- <img v-if="image" :src="image.url"/>
+ <ImgWithBlurhash v-if="image" :hash="image.blurhash" :src="image.url" :alt="image.comment" :title="image.comment" :cover="false"/>
</div>
</template>
-<script lang="ts">
+<script lang="ts" setup>
import { defineComponent, PropType } from 'vue';
+import ImgWithBlurhash from '@/components/img-with-blurhash.vue';
import * as os from '@/os';
import { ImageBlock } from '@/scripts/hpml/block';
import { Hpml } from '@/scripts/hpml/evaluator';
-export default defineComponent({
- props: {
- block: {
- type: Object as PropType<ImageBlock>,
- required: true
- },
- hpml: {
- type: Object as PropType<Hpml>,
- required: true
- }
- },
- setup(props, ctx) {
- const image = props.hpml.page.attachedFiles.find(x => x.id === props.block.fileId);
+const props = defineProps<{
+ block: PropType<ImageBlock>,
+ hpml: PropType<Hpml>,
+}>();
- return {
- image
- };
- }
-});
+const image = props.hpml.page.attachedFiles.find(x => x.id === props.block.fileId);
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/components/page/page.post.vue b/packages/client/src/components/page/page.post.vue
index 847da37c51..3401f945bd 100644
--- a/packages/client/src/components/page/page.post.vue
+++ b/packages/client/src/components/page/page.post.vue
@@ -52,21 +52,21 @@ export default defineComponent({
const promise = new Promise((ok) => {
const canvas = this.hpml.canvases[this.block.canvasId];
canvas.toBlob(blob => {
- const data = new FormData();
- data.append('file', blob);
- data.append('i', this.$i.token);
+ const formData = new FormData();
+ formData.append('file', blob);
+ formData.append('i', this.$i.token);
if (this.$store.state.uploadFolder) {
- data.append('folderId', this.$store.state.uploadFolder);
+ formData.append('folderId', this.$store.state.uploadFolder);
}
fetch(apiUrl + '/drive/files/create', {
method: 'POST',
- body: data
+ body: formData,
})
.then(response => response.json())
.then(f => {
ok(f);
- })
+ });
});
});
os.promiseDialog(promise);
diff --git a/packages/client/src/components/page/page.vue b/packages/client/src/components/page/page.vue
index e54147bbd0..a067762372 100644
--- a/packages/client/src/components/page/page.vue
+++ b/packages/client/src/components/page/page.vue
@@ -38,8 +38,8 @@ export default defineComponent({
let ast;
try {
ast = parse(props.page.script);
- } catch (e) {
- console.error(e);
+ } catch (err) {
+ console.error(err);
/*os.alert({
type: 'error',
text: 'Syntax error :('
@@ -48,11 +48,11 @@ export default defineComponent({
}
hpml.aiscript.exec(ast).then(() => {
hpml.eval();
- }).catch(e => {
- console.error(e);
+ }).catch(err => {
+ console.error(err);
/*os.alert({
type: 'error',
- text: e
+ text: err
});*/
});
} else {
diff --git a/packages/client/src/components/poll-editor.vue b/packages/client/src/components/poll-editor.vue
index 6f3f23a2d3..9aa5510c7f 100644
--- a/packages/client/src/components/poll-editor.vue
+++ b/packages/client/src/components/poll-editor.vue
@@ -104,7 +104,7 @@ function add() {
}
function remove(i) {
- choices.value = choices.value.filter((_, _i) => _i != i);
+ choices.value = choices.value.filter((_, _i) => _i !== i);
}
function get() {
diff --git a/packages/client/src/components/post-form-attaches.vue b/packages/client/src/components/post-form-attaches.vue
index 9dd69a0ee5..6b9827407b 100644
--- a/packages/client/src/components/post-form-attaches.vue
+++ b/packages/client/src/components/post-form-attaches.vue
@@ -16,7 +16,7 @@
<script lang="ts">
import { defineComponent, defineAsyncComponent } from 'vue';
-import MkDriveFileThumbnail from './drive-file-thumbnail.vue'
+import MkDriveFileThumbnail from './drive-file-thumbnail.vue';
import * as os from '@/os';
export default defineComponent({
@@ -88,7 +88,7 @@ export default defineComponent({
},
async describe(file) {
- os.popup(import("@/components/media-caption.vue"), {
+ os.popup(defineAsyncComponent(() => import("@/components/media-caption.vue")), {
title: this.$ts.describeFile,
input: {
placeholder: this.$ts.inputNewDescription,
@@ -98,7 +98,7 @@ export default defineComponent({
}, {
done: result => {
if (!result || result.canceled) return;
- let comment = result.result.length == 0 ? null : result.result;
+ let comment = result.result.length === 0 ? null : result.result;
os.api('drive/files/update', {
fileId: file.id,
comment: comment,
@@ -114,19 +114,19 @@ export default defineComponent({
this.menu = os.popupMenu([{
text: this.$ts.renameFile,
icon: 'fas fa-i-cursor',
- action: () => { this.rename(file) }
+ action: () => { this.rename(file); }
}, {
text: file.isSensitive ? this.$ts.unmarkAsSensitive : this.$ts.markAsSensitive,
icon: file.isSensitive ? 'fas fa-eye-slash' : 'fas fa-eye',
- action: () => { this.toggleSensitive(file) }
+ action: () => { this.toggleSensitive(file); }
}, {
text: this.$ts.describeFile,
icon: 'fas fa-i-cursor',
- action: () => { this.describe(file) }
+ action: () => { this.describe(file); }
}, {
text: this.$ts.attachCancel,
icon: 'fas fa-times-circle',
- action: () => { this.detachMedia(file.id) }
+ action: () => { this.detachMedia(file.id); }
}], ev.currentTarget ?? ev.target).then(() => this.menu = null);
}
}
diff --git a/packages/client/src/components/post-form.vue b/packages/client/src/components/post-form.vue
index 656689ddcb..0197313e0e 100644
--- a/packages/client/src/components/post-form.vue
+++ b/packages/client/src/components/post-form.vue
@@ -62,7 +62,7 @@
</template>
<script lang="ts" setup>
-import { inject, watch, nextTick, onMounted } from 'vue';
+import { inject, watch, nextTick, onMounted, defineAsyncComponent } from 'vue';
import * as mfm from 'mfm-js';
import * as misskey from 'misskey-js';
import insertTextAtCursor from 'insert-text-at-cursor';
@@ -87,6 +87,7 @@ import MkInfo from '@/components/ui/info.vue';
import { i18n } from '@/i18n';
import { instance } from '@/instance';
import { $i, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account';
+import { uploadFile } from '@/scripts/upload';
const modal = inject('modal');
@@ -106,7 +107,7 @@ const props = withDefaults(defineProps<{
fixed?: boolean;
autofocus?: boolean;
}>(), {
- initialVisibleUsers: [],
+ initialVisibleUsers: () => [],
autofocus: true,
});
@@ -227,7 +228,7 @@ if (props.mention) {
text += ' ';
}
-if (props.reply && (props.reply.user.username != $i.username || (props.reply.user.host != null && props.reply.user.host != host))) {
+if (props.reply && (props.reply.user.username !== $i.username || (props.reply.user.host != null && props.reply.user.host !== host))) {
text = `@${props.reply.user.username}${props.reply.user.host != null ? '@' + toASCII(props.reply.user.host) : ''} `;
}
@@ -238,16 +239,15 @@ if (props.reply && props.reply.text != null) {
for (const x of extractMentions(ast)) {
const mention = x.host ?
`@${x.username}@${toASCII(x.host)}` :
- (otherHost == null || otherHost == host) ?
+ (otherHost == null || otherHost === host) ?
`@${x.username}` :
`@${x.username}@${toASCII(otherHost)}`;
// 自分ã¯é™¤å¤–
- if ($i.username == x.username && x.host == null) continue;
- if ($i.username == x.username && x.host == host) continue;
+ if ($i.username === x.username && (x.host == null || x.host === host)) continue;
// é‡è¤‡ã¯é™¤å¤–
- if (text.indexOf(`${mention} `) != -1) continue;
+ if (text.includes(`${mention} `)) continue;
text += `${mention} `;
}
@@ -302,7 +302,7 @@ function checkMissingMention() {
const ast = mfm.parse(text);
for (const x of extractMentions(ast)) {
- if (!visibleUsers.some(u => (u.username === x.username) && (u.host == x.host))) {
+ if (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) {
hasNotSpecifiedMentions = true;
return;
}
@@ -315,7 +315,7 @@ function addMissingMention() {
const ast = mfm.parse(text);
for (const x of extractMentions(ast)) {
- if (!visibleUsers.some(u => (u.username === x.username) && (u.host == x.host))) {
+ if (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) {
os.api('users/show', { username: x.username, host: x.host }).then(user => {
visibleUsers.push(user);
});
@@ -356,7 +356,7 @@ function chooseFileFrom(ev) {
}
function detachFile(id) {
- files = files.filter(x => x.id != id);
+ files = files.filter(x => x.id !== id);
}
function updateFiles(_files) {
@@ -372,7 +372,7 @@ function updateFileName(file, name) {
}
function upload(file: File, name?: string) {
- os.upload(file, defaultStore.state.uploadFolder, name).then(res => {
+ uploadFile(file, defaultStore.state.uploadFolder, name).then(res => {
files.push(res);
});
}
@@ -383,7 +383,7 @@ function setVisibility() {
return;
}
- os.popup(import('./visibility-picker.vue'), {
+ os.popup(defineAsyncComponent(() => import('./visibility-picker.vue')), {
currentVisibility: visibility,
currentLocalOnly: localOnly,
src: visibilityButton,
@@ -426,24 +426,24 @@ function clear() {
quoteId = null;
}
-function onKeydown(e: KeyboardEvent) {
- if ((e.which === 10 || e.which === 13) && (e.ctrlKey || e.metaKey) && canPost) post();
- if (e.which === 27) emit('esc');
+function onKeydown(ev: KeyboardEvent) {
+ if ((ev.which === 10 || ev.which === 13) && (ev.ctrlKey || ev.metaKey) && canPost) post();
+ if (ev.which === 27) emit('esc');
typing();
}
-function onCompositionUpdate(e: CompositionEvent) {
- imeText = e.data;
+function onCompositionUpdate(ev: CompositionEvent) {
+ imeText = ev.data;
typing();
}
-function onCompositionEnd(e: CompositionEvent) {
+function onCompositionEnd(ev: CompositionEvent) {
imeText = '';
}
-async function onPaste(e: ClipboardEvent) {
- for (const { item, i } of Array.from(e.clipboardData.items).map((item, i) => ({item, i}))) {
- if (item.kind == 'file') {
+async function onPaste(ev: ClipboardEvent) {
+ for (const { item, i } of Array.from(ev.clipboardData.items).map((item, i) => ({ item, i }))) {
+ if (item.kind === 'file') {
const file = item.getAsFile();
const lio = file.name.lastIndexOf('.');
const ext = lio >= 0 ? file.name.slice(lio) : '';
@@ -452,10 +452,10 @@ async function onPaste(e: ClipboardEvent) {
}
}
- const paste = e.clipboardData.getData('text');
+ const paste = ev.clipboardData.getData('text');
if (!props.renote && !quoteId && paste.startsWith(url + '/notes/')) {
- e.preventDefault();
+ ev.preventDefault();
os.confirm({
type: 'info',
@@ -471,49 +471,49 @@ async function onPaste(e: ClipboardEvent) {
}
}
-function onDragover(e) {
- if (!e.dataTransfer.items[0]) return;
- const isFile = e.dataTransfer.items[0].kind == 'file';
- const isDriveFile = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FILE_;
+function onDragover(ev) {
+ if (!ev.dataTransfer.items[0]) return;
+ const isFile = ev.dataTransfer.items[0].kind === 'file';
+ const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
if (isFile || isDriveFile) {
- e.preventDefault();
+ ev.preventDefault();
draghover = true;
- e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
+ ev.dataTransfer.dropEffect = ev.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move';
}
}
-function onDragenter(e) {
+function onDragenter(ev) {
draghover = true;
}
-function onDragleave(e) {
+function onDragleave(ev) {
draghover = false;
}
-function onDrop(e): void {
+function onDrop(ev): void {
draghover = false;
// ファイルã ã£ãŸã‚‰
- if (e.dataTransfer.files.length > 0) {
- e.preventDefault();
- for (const x of Array.from(e.dataTransfer.files)) upload(x);
+ if (ev.dataTransfer.files.length > 0) {
+ ev.preventDefault();
+ for (const x of Array.from(ev.dataTransfer.files)) upload(x);
return;
}
//#region ドライブã®ãƒ•ァイル
- const driveFile = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
- if (driveFile != null && driveFile != '') {
+ const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
+ if (driveFile != null && driveFile !== '') {
const file = JSON.parse(driveFile);
files.push(file);
- e.preventDefault();
+ ev.preventDefault();
}
//#endregion
}
function saveDraft() {
- const data = JSON.parse(localStorage.getItem('drafts') || '{}');
+ const draftData = JSON.parse(localStorage.getItem('drafts') || '{}');
- data[draftKey] = {
+ draftData[draftKey] = {
updatedAt: new Date(),
data: {
text: text,
@@ -526,20 +526,20 @@ function saveDraft() {
}
};
- localStorage.setItem('drafts', JSON.stringify(data));
+ localStorage.setItem('drafts', JSON.stringify(draftData));
}
function deleteDraft() {
- const data = JSON.parse(localStorage.getItem('drafts') || '{}');
+ const draftData = JSON.parse(localStorage.getItem('drafts') || '{}');
- delete data[draftKey];
+ delete draftData[draftKey];
- localStorage.setItem('drafts', JSON.stringify(data));
+ localStorage.setItem('drafts', JSON.stringify(draftData));
}
async function post() {
- let data = {
- text: text == '' ? undefined : text,
+ let postData = {
+ text: text === '' ? undefined : text,
fileIds: files.length > 0 ? files.map(f => f.id) : undefined,
replyId: props.reply ? props.reply.id : undefined,
renoteId: props.renote ? props.renote.id : quoteId ? quoteId : undefined,
@@ -548,18 +548,18 @@ async function post() {
cw: useCw ? cw || '' : undefined,
localOnly: localOnly,
visibility: visibility,
- visibleUserIds: visibility == 'specified' ? visibleUsers.map(u => u.id) : undefined,
+ visibleUserIds: visibility === 'specified' ? visibleUsers.map(u => u.id) : undefined,
};
if (withHashtags && hashtags && hashtags.trim() !== '') {
const hashtags_ = hashtags.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' ');
- data.text = data.text ? `${data.text} ${hashtags_}` : hashtags_;
+ postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_;
}
// plugin
if (notePostInterruptors.length > 0) {
for (const interruptor of notePostInterruptors) {
- data = await interruptor.handler(JSON.parse(JSON.stringify(data)));
+ postData = await interruptor.handler(JSON.parse(JSON.stringify(postData)));
}
}
@@ -571,13 +571,13 @@ async function post() {
}
posting = true;
- os.api('notes/create', data, token).then(() => {
+ os.api('notes/create', postData, token).then(() => {
clear();
nextTick(() => {
deleteDraft();
emit('posted');
- if (data.text && data.text != '') {
- const hashtags_ = mfm.parse(data.text).filter(x => x.type === 'hashtag').map(x => x.props.hashtag);
+ if (postData.text && postData.text !== '') {
+ const hashtags_ = mfm.parse(postData.text).filter(x => x.type === 'hashtag').map(x => x.props.hashtag);
const history = JSON.parse(localStorage.getItem('hashtags') || '[]') as string[];
localStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history))));
}
@@ -661,7 +661,7 @@ onMounted(() => {
cw = draft.data.cw;
visibility = draft.data.visibility;
localOnly = draft.data.localOnly;
- files = (draft.data.files || []).filter(e => e);
+ files = (draft.data.files || []).filter(draftFile => draftFile);
if (draft.data.poll) {
poll = draft.data.poll;
}
diff --git a/packages/client/src/components/queue-chart.vue b/packages/client/src/components/queue-chart.vue
index 7e0ed58cbd..7bb548cf06 100644
--- a/packages/client/src/components/queue-chart.vue
+++ b/packages/client/src/components/queue-chart.vue
@@ -222,7 +222,7 @@ export default defineComponent({
return {
chartEl,
- }
+ };
},
});
</script>
diff --git a/packages/client/src/components/reactions-viewer.reaction.vue b/packages/client/src/components/reactions-viewer.reaction.vue
index 7dc079fde6..91a90a6996 100644
--- a/packages/client/src/components/reactions-viewer.reaction.vue
+++ b/packages/client/src/components/reactions-viewer.reaction.vue
@@ -7,8 +7,8 @@
:class="{ reacted: note.myReaction == reaction, canToggle }"
@click="toggleReaction()"
>
- <XReactionIcon :reaction="reaction" :custom-emojis="note.emojis"/>
- <span>{{ count }}</span>
+ <XReactionIcon class="icon" :reaction="reaction" :custom-emojis="note.emojis"/>
+ <span class="count">{{ count }}</span>
</button>
</template>
@@ -141,12 +141,16 @@ export default defineComponent({
background: var(--accent);
}
- > span {
+ > .count {
color: var(--fgOnAccent);
}
+
+ > .icon {
+ filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.5));
+ }
}
- > span {
+ > .count {
font-size: 0.9em;
line-height: 32px;
margin: 0 0 0 4px;
diff --git a/packages/client/src/components/sample.vue b/packages/client/src/components/sample.vue
index 65249ff7e9..f80b9c96b7 100644
--- a/packages/client/src/components/sample.vue
+++ b/packages/client/src/components/sample.vue
@@ -52,7 +52,7 @@ export default defineComponent({
flag: true,
radio: 'misskey',
mfm: `Hello world! This is an @example mention. BTW you are @${this.$i ? this.$i.username : 'guest'}.\nAlso, here is ${config.url} and [example link](${config.url}). for more details, see https://example.com.\nAs you know #misskey is open-source software.`
- }
+ };
},
methods: {
diff --git a/packages/client/src/components/signin-dialog.vue b/packages/client/src/components/signin-dialog.vue
index 5c2048e7b0..848b11fada 100644
--- a/packages/client/src/components/signin-dialog.vue
+++ b/packages/client/src/components/signin-dialog.vue
@@ -2,12 +2,12 @@
<XModalWindow ref="dialog"
:width="370"
:height="400"
- @close="dialog.close()"
+ @close="onClose"
@closed="emit('closed')"
>
<template #header>{{ $ts.login }}</template>
- <MkSignin :auto-set="autoSet" @login="onLogin"/>
+ <MkSignin :auto-set="autoSet" :message="message" @login="onLogin"/>
</XModalWindow>
</template>
@@ -18,17 +18,25 @@ import MkSignin from './signin.vue';
const props = withDefaults(defineProps<{
autoSet?: boolean;
+ message?: string,
}>(), {
autoSet: false,
+ message: ''
});
const emit = defineEmits<{
- (e: 'done'): void;
- (e: 'closed'): void;
+ (ev: 'done'): void;
+ (ev: 'closed'): void;
+ (ev: 'cancelled'): void;
}>();
const dialog = $ref<InstanceType<typeof XModalWindow>>();
+function onClose() {
+ emit('cancelled');
+ dialog.close();
+}
+
function onLogin(res) {
emit('done', res);
dialog.close();
diff --git a/packages/client/src/components/signin.vue b/packages/client/src/components/signin.vue
index f640e948ad..b772d1479b 100644
--- a/packages/client/src/components/signin.vue
+++ b/packages/client/src/components/signin.vue
@@ -1,39 +1,42 @@
<template>
<form class="eppvobhk _monolithic_" :class="{ signing, totpLogin }" @submit.prevent="onSubmit">
<div class="auth _section _formRoot">
- <div v-show="withAvatar" class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }"></div>
+ <div v-show="withAvatar" class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null, marginBottom: message ? '1.5em' : null }"></div>
+ <MkInfo v-if="message">
+ {{ message }}
+ </MkInfo>
<div v-if="!totpLogin" class="normal-signin">
- <MkInput v-model="username" class="_formBlock" :placeholder="$ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required data-cy-signin-username @update:modelValue="onUsernameChange">
+ <MkInput v-model="username" class="_formBlock" :placeholder="i18n.ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required data-cy-signin-username @update:modelValue="onUsernameChange">
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
</MkInput>
- <MkInput v-if="!user || user && !user.usePasswordLessLogin" v-model="password" class="_formBlock" :placeholder="$ts.password" type="password" :with-password-toggle="true" required data-cy-signin-password>
+ <MkInput v-if="!user || user && !user.usePasswordLessLogin" v-model="password" class="_formBlock" :placeholder="i18n.ts.password" type="password" :with-password-toggle="true" required data-cy-signin-password>
<template #prefix><i class="fas fa-lock"></i></template>
- <template #caption><button class="_textButton" type="button" @click="resetPassword">{{ $ts.forgotPassword }}</button></template>
+ <template #caption><button class="_textButton" type="button" @click="resetPassword">{{ i18n.ts.forgotPassword }}</button></template>
</MkInput>
- <MkButton class="_formBlock" type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $ts.loggingIn : $ts.login }}</MkButton>
+ <MkButton class="_formBlock" type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? i18n.ts.loggingIn : i18n.ts.login }}</MkButton>
</div>
<div v-if="totpLogin" class="2fa-signin" :class="{ securityKeys: user && user.securityKeys }">
<div v-if="user && user.securityKeys" class="twofa-group tap-group">
- <p>{{ $ts.tapSecurityKey }}</p>
+ <p>{{ i18n.ts.tapSecurityKey }}</p>
<MkButton v-if="!queryingKey" @click="queryKey">
- {{ $ts.retry }}
+ {{ i18n.ts.retry }}
</MkButton>
</div>
<div v-if="user && user.securityKeys" class="or-hr">
- <p class="or-msg">{{ $ts.or }}</p>
+ <p class="or-msg">{{ i18n.ts.or }}</p>
</div>
<div class="twofa-group totp-group">
- <p style="margin-bottom:0;">{{ $ts.twoStepAuthentication }}</p>
+ <p style="margin-bottom:0;">{{ i18n.ts.twoStepAuthentication }}</p>
<MkInput v-if="user && user.usePasswordLessLogin" v-model="password" type="password" :with-password-toggle="true" required>
- <template #label>{{ $ts.password }}</template>
+ <template #label>{{ i18n.ts.password }}</template>
<template #prefix><i class="fas fa-lock"></i></template>
</MkInput>
<MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false" required>
- <template #label>{{ $ts.token }}</template>
+ <template #label>{{ i18n.ts.token }}</template>
<template #prefix><i class="fas fa-gavel"></i></template>
</MkInput>
- <MkButton type="submit" :disabled="signing" primary style="margin: 0 auto;">{{ signing ? $ts.loggingIn : $ts.login }}</MkButton>
+ <MkButton type="submit" :disabled="signing" primary style="margin: 0 auto;">{{ signing ? i18n.ts.loggingIn : i18n.ts.login }}</MkButton>
</div>
</div>
</div>
@@ -45,190 +48,198 @@
</form>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineAsyncComponent } from 'vue';
import { toUnicode } from 'punycode/';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
-import { apiUrl, host } from '@/config';
+import MkInfo from '@/components/ui/info.vue';
+import { apiUrl, host as configHost } from '@/config';
import { byteify, hexify } from '@/scripts/2fa';
import * as os from '@/os';
import { login } from '@/account';
import { showSuspendedDialog } from '../scripts/show-suspended-dialog';
+import { instance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- MkInput,
- },
+let signing = $ref(false);
+let user = $ref(null);
+let username = $ref('');
+let password = $ref('');
+let token = $ref('');
+let host = $ref(toUnicode(configHost));
+let totpLogin = $ref(false);
+let credential = $ref(null);
+let challengeData = $ref(null);
+let queryingKey = $ref(false);
+let hCaptchaResponse = $ref(null);
+let reCaptchaResponse = $ref(null);
- props: {
- withAvatar: {
- type: Boolean,
- required: false,
- default: true
- },
- autoSet: {
- type: Boolean,
- required: false,
- default: false,
- }
- },
+const meta = $computed(() => instance);
- emits: ['login'],
+const emit = defineEmits<{
+ (ev: 'login', v: any): void;
+}>();
- data() {
- return {
- signing: false,
- user: null,
- username: '',
- password: '',
- token: '',
- apiUrl,
- host: toUnicode(host),
- totpLogin: false,
- credential: null,
- challengeData: null,
- queryingKey: false,
- };
+const props = defineProps({
+ withAvatar: {
+ type: Boolean,
+ required: false,
+ default: true
},
-
- computed: {
- meta() {
- return this.$instance;
- },
+ autoSet: {
+ type: Boolean,
+ required: false,
+ default: false,
},
+ message: {
+ type: String,
+ required: false,
+ default: ''
+ }
+});
- methods: {
- onUsernameChange() {
- os.api('users/show', {
- username: this.username
- }).then(user => {
- this.user = user;
- }, () => {
- this.user = null;
- });
- },
-
- onLogin(res) {
- if (this.autoSet) {
- return login(res.i);
- } else {
- return;
- }
- },
-
- queryKey() {
- this.queryingKey = true;
- return navigator.credentials.get({
- publicKey: {
- challenge: byteify(this.challengeData.challenge, 'base64'),
- allowCredentials: this.challengeData.securityKeys.map(key => ({
- id: byteify(key.id, 'hex'),
- type: 'public-key',
- transports: ['usb', 'nfc', 'ble', 'internal']
- })),
- timeout: 60 * 1000
- }
- }).catch(() => {
- this.queryingKey = false;
- return Promise.reject(null);
- }).then(credential => {
- this.queryingKey = false;
- this.signing = true;
- return os.api('signin', {
- username: this.username,
- password: this.password,
- signature: hexify(credential.response.signature),
- authenticatorData: hexify(credential.response.authenticatorData),
- clientDataJSON: hexify(credential.response.clientDataJSON),
- credentialId: credential.id,
- challengeId: this.challengeData.challengeId
- });
- }).then(res => {
- this.$emit('login', res);
- return this.onLogin(res);
- }).catch(err => {
- if (err === null) return;
- os.alert({
- type: 'error',
- text: this.$ts.signinFailed
- });
- this.signing = false;
- });
- },
+function onUsernameChange() {
+ os.api('users/show', {
+ username: username
+ }).then(userResponse => {
+ user = userResponse;
+ }, () => {
+ user = null;
+ });
+}
- onSubmit() {
- this.signing = true;
- if (!this.totpLogin && this.user && this.user.twoFactorEnabled) {
- if (window.PublicKeyCredential && this.user.securityKeys) {
- os.api('signin', {
- username: this.username,
- password: this.password
- }).then(res => {
- this.totpLogin = true;
- this.signing = false;
- this.challengeData = res;
- return this.queryKey();
- }).catch(this.loginFailed);
- } else {
- this.totpLogin = true;
- this.signing = false;
- }
- } else {
- os.api('signin', {
- username: this.username,
- password: this.password,
- token: this.user && this.user.twoFactorEnabled ? this.token : undefined
- }).then(res => {
- this.$emit('login', res);
- this.onLogin(res);
- }).catch(this.loginFailed);
- }
- },
+function onLogin(res) {
+ if (props.autoSet) {
+ return login(res.i);
+ }
+}
- loginFailed(err) {
- switch (err.id) {
- case '6cc579cc-885d-43d8-95c2-b8c7fc963280': {
- os.alert({
- type: 'error',
- title: this.$ts.loginFailed,
- text: this.$ts.noSuchUser
- });
- break;
- }
- case '932c904e-9460-45b7-9ce6-7ed33be7eb2c': {
- os.alert({
- type: 'error',
- title: this.$ts.loginFailed,
- text: this.$ts.incorrectPassword,
- });
- break;
- }
- case 'e03a5f46-d309-4865-9b69-56282d94e1eb': {
- showSuspendedDialog();
- break;
- }
- default: {
- os.alert({
- type: 'error',
- title: this.$ts.loginFailed,
- text: JSON.stringify(err)
- });
- }
- }
+function queryKey() {
+ queryingKey = true;
+ return navigator.credentials.get({
+ publicKey: {
+ challenge: byteify(challengeData.challenge, 'base64'),
+ allowCredentials: challengeData.securityKeys.map(key => ({
+ id: byteify(key.id, 'hex'),
+ type: 'public-key',
+ transports: ['usb', 'nfc', 'ble', 'internal']
+ })),
+ timeout: 60 * 1000
+ }
+ }).catch(() => {
+ queryingKey = false;
+ return Promise.reject(null);
+ }).then(credential => {
+ queryingKey = false;
+ signing = true;
+ return os.api('signin', {
+ username,
+ password,
+ signature: hexify(credential.response.signature),
+ authenticatorData: hexify(credential.response.authenticatorData),
+ clientDataJSON: hexify(credential.response.clientDataJSON),
+ credentialId: credential.id,
+ challengeId: challengeData.challengeId,
+ 'hcaptcha-response': hCaptchaResponse,
+ 'g-recaptcha-response': reCaptchaResponse,
+ });
+ }).then(res => {
+ emit('login', res);
+ return onLogin(res);
+ }).catch(err => {
+ if (err === null) return;
+ os.alert({
+ type: 'error',
+ text: i18n.ts.signinFailed
+ });
+ signing = false;
+ });
+}
- this.challengeData = null;
- this.totpLogin = false;
- this.signing = false;
- },
+function onSubmit() {
+ signing = true;
+ console.log('submit');
+ if (!totpLogin && user && user.twoFactorEnabled) {
+ if (window.PublicKeyCredential && user.securityKeys) {
+ os.api('signin', {
+ username,
+ password,
+ 'hcaptcha-response': hCaptchaResponse,
+ 'g-recaptcha-response': reCaptchaResponse,
+ }).then(res => {
+ totpLogin = true;
+ signing = false;
+ challengeData = res;
+ return queryKey();
+ }).catch(loginFailed);
+ } else {
+ totpLogin = true;
+ signing = false;
+ }
+ } else {
+ os.api('signin', {
+ username,
+ password,
+ 'hcaptcha-response': hCaptchaResponse,
+ 'g-recaptcha-response': reCaptchaResponse,
+ token: user && user.twoFactorEnabled ? token : undefined
+ }).then(res => {
+ emit('login', res);
+ onLogin(res);
+ }).catch(loginFailed);
+ }
+}
- resetPassword() {
- os.popup(import('@/components/forgot-password.vue'), {}, {
- }, 'closed');
+function loginFailed(err) {
+ switch (err.id) {
+ case '6cc579cc-885d-43d8-95c2-b8c7fc963280': {
+ os.alert({
+ type: 'error',
+ title: i18n.ts.loginFailed,
+ text: i18n.ts.noSuchUser
+ });
+ break;
+ }
+ case '932c904e-9460-45b7-9ce6-7ed33be7eb2c': {
+ os.alert({
+ type: 'error',
+ title: i18n.ts.loginFailed,
+ text: i18n.ts.incorrectPassword,
+ });
+ break;
+ }
+ case 'e03a5f46-d309-4865-9b69-56282d94e1eb': {
+ showSuspendedDialog();
+ break;
+ }
+ case '22d05606-fbcf-421a-a2db-b32610dcfd1b': {
+ os.alert({
+ type: 'error',
+ title: i18n.ts.loginFailed,
+ text: i18n.ts.rateLimitExceeded,
+ });
+ break;
+ }
+ default: {
+ console.log(err);
+ os.alert({
+ type: 'error',
+ title: i18n.ts.loginFailed,
+ text: JSON.stringify(err)
+ });
}
}
-});
+
+ challengeData = null;
+ totpLogin = false;
+ signing = false;
+}
+
+function resetPassword() {
+ os.popup(defineAsyncComponent(() => import('@/components/forgot-password.vue')), {}, {
+ }, 'closed');
+}
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/components/signup-dialog.vue b/packages/client/src/components/signup-dialog.vue
index bda2495ba7..6dad9257a4 100644
--- a/packages/client/src/components/signup-dialog.vue
+++ b/packages/client/src/components/signup-dialog.vue
@@ -27,8 +27,8 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'done'): void;
- (e: 'closed'): void;
+ (ev: 'done'): void;
+ (ev: 'closed'): void;
}>();
const dialog = $ref<InstanceType<typeof XModalWindow>>();
diff --git a/packages/client/src/components/signup.vue b/packages/client/src/components/signup.vue
index 38a9fd55f1..3f2af306e5 100644
--- a/packages/client/src/components/signup.vue
+++ b/packages/client/src/components/signup.vue
@@ -1,11 +1,11 @@
<template>
-<form class="qlvuhzng _formRoot" :autocomplete="Math.random()" @submit.prevent="onSubmit">
+<form class="qlvuhzng _formRoot" autocomplete="new-password" @submit.prevent="onSubmit">
<template v-if="meta">
- <MkInput v-if="meta.disableRegistration" v-model="invitationCode" class="_formBlock" type="text" :autocomplete="Math.random()" spellcheck="false" required>
+ <MkInput v-if="meta.disableRegistration" v-model="invitationCode" class="_formBlock" type="text" spellcheck="false" required>
<template #label>{{ $ts.invitationCode }}</template>
<template #prefix><i class="fas fa-key"></i></template>
</MkInput>
- <MkInput v-model="username" class="_formBlock" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required data-cy-signup-username @update:modelValue="onChangeUsername">
+ <MkInput v-model="username" class="_formBlock" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" spellcheck="false" required data-cy-signup-username @update:modelValue="onChangeUsername">
<template #label>{{ $ts.username }} <div v-tooltip:dialog="$ts.usernameInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template>
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
@@ -19,7 +19,7 @@
<span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooLong }}</span>
</template>
</MkInput>
- <MkInput v-if="meta.emailRequiredForSignup" v-model="email" class="_formBlock" :debounce="true" type="email" :autocomplete="Math.random()" spellcheck="false" required data-cy-signup-email @update:modelValue="onChangeEmail">
+ <MkInput v-if="meta.emailRequiredForSignup" v-model="email" class="_formBlock" :debounce="true" type="email" spellcheck="false" required data-cy-signup-email @update:modelValue="onChangeEmail">
<template #label>{{ $ts.emailAddress }} <div v-tooltip:dialog="$ts._signup.emailAddressInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template>
<template #prefix><i class="fas fa-envelope"></i></template>
<template #caption>
@@ -34,7 +34,7 @@
<span v-else-if="emailState === 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.error }}</span>
</template>
</MkInput>
- <MkInput v-model="password" class="_formBlock" type="password" :autocomplete="Math.random()" required data-cy-signup-password @update:modelValue="onChangePassword">
+ <MkInput v-model="password" class="_formBlock" type="password" autocomplete="new-password" required data-cy-signup-password @update:modelValue="onChangePassword">
<template #label>{{ $ts.password }}</template>
<template #prefix><i class="fas fa-lock"></i></template>
<template #caption>
@@ -43,7 +43,7 @@
<span v-if="passwordStrength == 'high'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.strongPassword }}</span>
</template>
</MkInput>
- <MkInput v-model="retypedPassword" class="_formBlock" type="password" :autocomplete="Math.random()" required data-cy-signup-password-retype @update:modelValue="onChangePasswordRetype">
+ <MkInput v-model="retypedPassword" class="_formBlock" type="password" autocomplete="new-password" required data-cy-signup-password-retype @update:modelValue="onChangePasswordRetype">
<template #label>{{ $ts.password }} ({{ $ts.retype }})</template>
<template #prefix><i class="fas fa-lock"></i></template>
<template #caption>
@@ -58,8 +58,8 @@
</template>
</I18n>
</MkSwitch>
- <captcha v-if="meta.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="_formBlock captcha" provider="hcaptcha" :sitekey="meta.hcaptchaSiteKey"/>
- <captcha v-if="meta.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="_formBlock captcha" provider="recaptcha" :sitekey="meta.recaptchaSiteKey"/>
+ <MkCaptcha v-if="meta.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="_formBlock captcha" provider="hcaptcha" :sitekey="meta.hcaptchaSiteKey"/>
+ <MkCaptcha v-if="meta.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="_formBlock captcha" provider="recaptcha" :sitekey="meta.recaptchaSiteKey"/>
<MkButton class="_formBlock" type="submit" :disabled="shouldDisableSubmitting" gradate data-cy-signup-submit>{{ $ts.start }}</MkButton>
</template>
</form>
@@ -67,7 +67,7 @@
<script lang="ts">
import { defineComponent, defineAsyncComponent } from 'vue';
-const getPasswordStrength = require('syuilo-password-strength');
+const getPasswordStrength = await import('syuilo-password-strength');
import { toUnicode } from 'punycode/';
import { host, url } from '@/config';
import MkButton from './ui/button.vue';
@@ -81,7 +81,7 @@ export default defineComponent({
MkButton,
MkInput,
MkSwitch,
- captcha: defineAsyncComponent(() => import('./captcha.vue')),
+ MkCaptcha: defineAsyncComponent(() => import('./captcha.vue')),
},
props: {
@@ -111,7 +111,7 @@ export default defineComponent({
ToSAgreement: false,
hCaptchaResponse: null,
reCaptchaResponse: null,
- }
+ };
},
computed: {
@@ -124,20 +124,20 @@ export default defineComponent({
this.meta.tosUrl && !this.ToSAgreement ||
this.meta.enableHcaptcha && !this.hCaptchaResponse ||
this.meta.enableRecaptcha && !this.reCaptchaResponse ||
- this.passwordRetypeState == 'not-match';
+ this.passwordRetypeState === 'not-match';
},
shouldShowProfileUrl(): boolean {
- return (this.username != '' &&
- this.usernameState != 'invalid-format' &&
- this.usernameState != 'min-range' &&
- this.usernameState != 'max-range');
+ return (this.username !== '' &&
+ this.usernameState !== 'invalid-format' &&
+ this.usernameState !== 'min-range' &&
+ this.usernameState !== 'max-range');
}
},
methods: {
onChangeUsername() {
- if (this.username == '') {
+ if (this.username === '') {
this.usernameState = null;
return;
}
@@ -165,7 +165,7 @@ export default defineComponent({
},
onChangeEmail() {
- if (this.email == '') {
+ if (this.email === '') {
this.emailState = null;
return;
}
@@ -188,7 +188,7 @@ export default defineComponent({
},
onChangePassword() {
- if (this.password == '') {
+ if (this.password === '') {
this.passwordStrength = '';
return;
}
@@ -198,12 +198,12 @@ export default defineComponent({
},
onChangePasswordRetype() {
- if (this.retypedPassword == '') {
+ if (this.retypedPassword === '') {
this.passwordRetypeState = null;
return;
}
- this.passwordRetypeState = this.password == this.retypedPassword ? 'match' : 'not-match';
+ this.passwordRetypeState = this.password === this.retypedPassword ? 'match' : 'not-match';
},
onSubmit() {
diff --git a/packages/client/src/components/timeline.vue b/packages/client/src/components/timeline.vue
index 59956b9526..a3fa27ab78 100644
--- a/packages/client/src/components/timeline.vue
+++ b/packages/client/src/components/timeline.vue
@@ -19,8 +19,8 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'note'): void;
- (e: 'queue', count: number): void;
+ (ev: 'note'): void;
+ (ev: 'queue', count: number): void;
}>();
provide('inChannel', computed(() => props.src === 'channel'));
@@ -95,7 +95,7 @@ if (props.src === 'antenna') {
visibility: 'specified'
};
const onNote = note => {
- if (note.visibility == 'specified') {
+ if (note.visibility === 'specified') {
prepend(note);
}
};
diff --git a/packages/client/src/components/toast.vue b/packages/client/src/components/toast.vue
index 99933f3846..c9fad64eb6 100644
--- a/packages/client/src/components/toast.vue
+++ b/packages/client/src/components/toast.vue
@@ -19,7 +19,7 @@ defineProps<{
}>();
const emit = defineEmits<{
- (e: 'closed'): void;
+ (ev: 'closed'): void;
}>();
const zIndex = os.claimZIndex('high');
diff --git a/packages/client/src/components/ui/button.vue b/packages/client/src/components/ui/button.vue
index c7b6c8ba96..e6b20d9881 100644
--- a/packages/client/src/components/ui/button.vue
+++ b/packages/client/src/components/ui/button.vue
@@ -90,32 +90,32 @@ export default defineComponent({
}
},
methods: {
- onMousedown(e: MouseEvent) {
+ onMousedown(evt: MouseEvent) {
function distance(p, q) {
return Math.hypot(p.x - q.x, p.y - q.y);
}
function calcCircleScale(boxW, boxH, circleCenterX, circleCenterY) {
- const origin = {x: circleCenterX, y: circleCenterY};
- const dist1 = distance({x: 0, y: 0}, origin);
- const dist2 = distance({x: boxW, y: 0}, origin);
- const dist3 = distance({x: 0, y: boxH}, origin);
- const dist4 = distance({x: boxW, y: boxH }, origin);
+ const origin = { x: circleCenterX, y: circleCenterY };
+ const dist1 = distance({ x: 0, y: 0 }, origin);
+ const dist2 = distance({ x: boxW, y: 0 }, origin);
+ const dist3 = distance({ x: 0, y: boxH }, origin);
+ const dist4 = distance({ x: boxW, y: boxH }, origin);
return Math.max(dist1, dist2, dist3, dist4) * 2;
}
- const rect = e.target.getBoundingClientRect();
+ const rect = evt.target.getBoundingClientRect();
const ripple = document.createElement('div');
- ripple.style.top = (e.clientY - rect.top - 1).toString() + 'px';
- ripple.style.left = (e.clientX - rect.left - 1).toString() + 'px';
+ ripple.style.top = (evt.clientY - rect.top - 1).toString() + 'px';
+ ripple.style.left = (evt.clientX - rect.left - 1).toString() + 'px';
this.$refs.ripples.appendChild(ripple);
- const circleCenterX = e.clientX - rect.left;
- const circleCenterY = e.clientY - rect.top;
+ const circleCenterX = evt.clientX - rect.left;
+ const circleCenterY = evt.clientY - rect.top;
- const scale = calcCircleScale(e.target.clientWidth, e.target.clientHeight, circleCenterX, circleCenterY);
+ const scale = calcCircleScale(evt.target.clientWidth, evt.target.clientHeight, circleCenterX, circleCenterY);
window.setTimeout(() => {
ripple.style.transform = 'scale(' + (scale / 2) + ')';
diff --git a/packages/client/src/components/ui/context-menu.vue b/packages/client/src/components/ui/context-menu.vue
index f491b43b46..e637d361cf 100644
--- a/packages/client/src/components/ui/context-menu.vue
+++ b/packages/client/src/components/ui/context-menu.vue
@@ -19,7 +19,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'closed'): void;
+ (ev: 'closed'): void;
}>();
let rootEl = $ref<HTMLDivElement>();
@@ -63,8 +63,8 @@ onBeforeUnmount(() => {
}
});
-function onMousedown(e: Event) {
- if (!contains(rootEl, e.target) && (rootEl != e.target)) emit('closed');
+function onMousedown(evt: Event) {
+ if (!contains(rootEl, evt.target) && (rootEl !== evt.target)) emit('closed');
}
</script>
diff --git a/packages/client/src/components/ui/folder.vue b/packages/client/src/components/ui/folder.vue
index fe1602b2bb..7daa82cbd3 100644
--- a/packages/client/src/components/ui/folder.vue
+++ b/packages/client/src/components/ui/folder.vue
@@ -23,7 +23,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import * as tinycolor from 'tinycolor2';
+import tinycolor from 'tinycolor2';
const localStoragePrefix = 'ui:folder:';
diff --git a/packages/client/src/components/ui/menu.vue b/packages/client/src/components/ui/menu.vue
index a93cc8cda8..dad5dfa8b0 100644
--- a/packages/client/src/components/ui/menu.vue
+++ b/packages/client/src/components/ui/menu.vue
@@ -1,5 +1,6 @@
<template>
-<div ref="itemsEl" v-hotkey="keymap"
+<div
+ ref="itemsEl" v-hotkey="keymap"
class="rrevdjwt"
:class="{ center: align === 'center', asDrawer }"
:style="{ width: (width && !asDrawer) ? width + 'px' : '', maxHeight: maxHeight ? maxHeight + 'px' : '' }"
@@ -60,7 +61,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'close'): void;
+ (ev: 'close'): void;
}>();
let itemsEl = $ref<HTMLDivElement>();
@@ -162,6 +163,15 @@ function focusDown() {
position: relative;
}
+ &:not(:disabled):hover {
+ color: var(--accent);
+ text-decoration: none;
+
+ &:before {
+ background: var(--accentedBg);
+ }
+ }
+
&.danger {
color: #ff2a2a;
@@ -191,15 +201,6 @@ function focusDown() {
}
}
- &:not(:disabled):hover {
- color: var(--accent);
- text-decoration: none;
-
- &:before {
- background: var(--accentedBg);
- }
- }
-
&:not(:active):focus-visible {
box-shadow: 0 0 0 2px var(--focus) inset;
}
diff --git a/packages/client/src/components/ui/modal-window.vue b/packages/client/src/components/ui/modal-window.vue
index b4b8c2b965..d2b2ccff7a 100644
--- a/packages/client/src/components/ui/modal-window.vue
+++ b/packages/client/src/components/ui/modal-window.vue
@@ -1,7 +1,7 @@
<template>
-<MkModal ref="modal" :prefer-type="'dialog'" @click="$emit('click')" @closed="$emit('closed')">
- <div class="ebkgoccj _window _narrow_" :style="{ width: `${width}px`, height: scroll ? (height ? `${height}px` : null) : (height ? `min(${height}px, 100%)` : '100%') }" @keydown="onKeydown">
- <div class="header">
+<MkModal ref="modal" :prefer-type="'dialog'" @click="onBgClick" @closed="$emit('closed')">
+ <div ref="rootEl" class="ebkgoccj _window _narrow_" :style="{ width: `${width}px`, height: scroll ? (height ? `${height}px` : null) : (height ? `min(${height}px, 100%)` : '100%') }" @keydown="onKeydown">
+ <div ref="headerEl" class="header">
<button v-if="withOkButton" class="_button" @click="$emit('close')"><i class="fas fa-times"></i></button>
<span class="title">
<slot name="header"></slot>
@@ -11,82 +11,82 @@
</div>
<div v-if="padding" class="body">
<div class="_section">
- <slot></slot>
+ <slot :width="bodyWidth" :height="bodyHeight"></slot>
</div>
</div>
<div v-else class="body">
- <slot></slot>
+ <slot :width="bodyWidth" :height="bodyHeight"></slot>
</div>
</div>
</MkModal>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { onMounted, onUnmounted } from 'vue';
import MkModal from './modal.vue';
-export default defineComponent({
- components: {
- MkModal
- },
- props: {
- withOkButton: {
- type: Boolean,
- required: false,
- default: false
- },
- okButtonDisabled: {
- type: Boolean,
- required: false,
- default: false
- },
- padding: {
- type: Boolean,
- required: false,
- default: false
- },
- width: {
- type: Number,
- required: false,
- default: 400
- },
- height: {
- type: Number,
- required: false,
- default: null
- },
- canClose: {
- type: Boolean,
- required: false,
- default: true,
- },
- scroll: {
- type: Boolean,
- required: false,
- default: true,
- },
- },
+const props = withDefaults(defineProps<{
+ withOkButton: boolean;
+ okButtonDisabled: boolean;
+ padding: boolean;
+ width: number;
+ height: number | null;
+ scroll: boolean;
+}>(), {
+ withOkButton: false,
+ okButtonDisabled: false,
+ padding: false,
+ width: 400,
+ height: null,
+ scroll: true,
+});
- emits: ['click', 'close', 'closed', 'ok'],
+const emit = defineEmits<{
+ (event: 'click'): void;
+ (event: 'close'): void;
+ (event: 'closed'): void;
+ (event: 'ok'): void;
+}>();
- data() {
- return {
- };
- },
+let modal = $ref<InstanceType<typeof MkModal>>();
+let rootEl = $ref<HTMLElement>();
+let headerEl = $ref<HTMLElement>();
+let bodyWidth = $ref(0);
+let bodyHeight = $ref(0);
- methods: {
- close() {
- this.$refs.modal.close();
- },
+const close = () => {
+ modal.close();
+};
- onKeydown(e) {
- if (e.which === 27) { // Esc
- e.preventDefault();
- e.stopPropagation();
- this.close();
- }
- },
+const onBgClick = () => {
+ emit('click');
+};
+
+const onKeydown = (evt) => {
+ if (evt.which === 27) { // Esc
+ evt.preventDefault();
+ evt.stopPropagation();
+ close();
}
+};
+
+const ro = new ResizeObserver((entries, observer) => {
+ bodyWidth = rootEl.offsetWidth;
+ bodyHeight = rootEl.offsetHeight - headerEl.offsetHeight;
+});
+
+onMounted(() => {
+ bodyWidth = rootEl.offsetWidth;
+ bodyHeight = rootEl.offsetHeight - headerEl.offsetHeight;
+ ro.observe(rootEl);
+});
+
+onUnmounted(() => {
+ ro.disconnect();
+});
+
+defineExpose({
+ close,
});
</script>
diff --git a/packages/client/src/components/ui/modal.vue b/packages/client/src/components/ui/modal.vue
index 1e4159055e..d6a29ec4b7 100644
--- a/packages/client/src/components/ui/modal.vue
+++ b/packages/client/src/components/ui/modal.vue
@@ -1,5 +1,5 @@
<template>
-<transition :name="$store.state.animation ? (type === 'drawer') ? 'modal-drawer' : (type === 'popup') ? 'modal-popup' : 'modal' : ''" :duration="$store.state.animation ? 200 : 0" appear @after-leave="emit('closed')" @enter="emit('opening')" @after-enter="childRendered">
+<transition :name="$store.state.animation ? (type === 'drawer') ? 'modal-drawer' : (type === 'popup') ? 'modal-popup' : 'modal' : ''" :duration="$store.state.animation ? 200 : 0" appear @after-leave="emit('closed')" @enter="emit('opening')" @after-enter="onOpened">
<div v-show="manualShowing != null ? manualShowing : showing" v-hotkey.global="keymap" class="qzhlnise" :class="{ drawer: type === 'drawer', dialog: type === 'dialog' || type === 'dialog:top', popup: type === 'popup' }" :style="{ zIndex, pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
<div class="bg _modalBg" :class="{ transparent: transparentBg && (type === 'popup') }" :style="{ zIndex }" @click="onBgClick" @contextmenu.prevent.stop="() => {}"></div>
<div ref="content" class="content" :class="{ fixed, top: type === 'dialog:top' }" :style="{ zIndex }" @click.self="onBgClick">
@@ -48,6 +48,7 @@ const props = withDefaults(defineProps<{
const emit = defineEmits<{
(ev: 'opening'): void;
+ (ev: 'opened'): void;
(ev: 'click'): void;
(ev: 'esc'): void;
(ev: 'close'): void;
@@ -212,7 +213,9 @@ const align = () => {
popover.style.top = top + 'px';
};
-const childRendered = () => {
+const onOpened = () => {
+ emit('opened');
+
// モーダルコンテンツã«ãƒžã‚¦ã‚¹ãƒœã‚¿ãƒ³ãŒæŠ¼ã•れã€ã‚³ãƒ³ãƒ†ãƒ³ãƒ„外ã§ãƒžã‚¦ã‚¹ãƒœã‚¿ãƒ³ãŒé›¢ã•れãŸã¨ãã«ãƒ¢ãƒ¼ãƒ€ãƒ«ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰ã‚¯ãƒªãƒƒã‚¯ã¨åˆ¤å®šã•ã›ãªã„ãŸã‚ã«ãƒžã‚¦ã‚¹ã‚¤ãƒ™ãƒ³ãƒˆã‚’監視ã—フラグ管ç†ã™ã‚‹
const el = content.value!.children[0];
el.addEventListener('mousedown', ev => {
@@ -234,10 +237,10 @@ onMounted(() => {
}
fixed.value = (type.value === 'drawer') || (getFixedContainer(props.src) != null);
- await nextTick()
+ await nextTick();
align();
- }, { immediate: true, });
+ }, { immediate: true });
nextTick(() => {
const popover = content.value;
diff --git a/packages/client/src/components/ui/pagination.vue b/packages/client/src/components/ui/pagination.vue
index 13f3215671..c081e06acd 100644
--- a/packages/client/src/components/ui/pagination.vue
+++ b/packages/client/src/components/ui/pagination.vue
@@ -14,8 +14,14 @@
</div>
<div v-else ref="rootEl">
+ <div v-show="pagination.reversed && more" key="_more_" class="cxiknjgy _gap">
+ <MkButton v-if="!moreFetching" class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary @click="fetchMoreAhead">
+ {{ $ts.loadMore }}
+ </MkButton>
+ <MkLoading v-else class="loading"/>
+ </div>
<slot :items="items"></slot>
- <div v-show="more" key="_more_" class="cxiknjgy _gap">
+ <div v-show="!pagination.reversed && more" key="_more_" class="cxiknjgy _gap">
<MkButton v-if="!moreFetching" v-appear="($store.state.enableInfiniteScroll && !disableAutoLoad) ? fetchMore : null" class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary @click="fetchMore">
{{ $ts.loadMore }}
</MkButton>
@@ -62,7 +68,7 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'queue', count: number): void;
+ (ev: 'queue', count: number): void;
}>();
type Item = { id: string; [another: string]: unknown; };
@@ -106,7 +112,7 @@ const init = async (): Promise<void> => {
offset.value = res.length;
error.value = false;
fetching.value = false;
- }, e => {
+ }, err => {
error.value = true;
fetching.value = false;
});
@@ -149,7 +155,7 @@ const fetchMore = async (): Promise<void> => {
}
offset.value += res.length;
moreFetching.value = false;
- }, e => {
+ }, err => {
moreFetching.value = false;
});
};
@@ -177,7 +183,7 @@ const fetchMoreAhead = async (): Promise<void> => {
}
offset.value += res.length;
moreFetching.value = false;
- }, e => {
+ }, err => {
moreFetching.value = false;
});
};
@@ -244,6 +250,11 @@ const append = (item: Item): void => {
items.value.push(item);
};
+const removeItem = (finder: (item: Item) => boolean) => {
+ const i = items.value.findIndex(finder);
+ items.value.splice(i, 1);
+};
+
const updateItem = (id: Item['id'], replacer: (old: Item) => Item): void => {
const i = items.value.findIndex(item => item.id === id);
items.value[i] = replacer(items.value[i]);
@@ -270,11 +281,12 @@ onDeactivated(() => {
defineExpose({
items,
+ queue,
backed,
reload,
- fetchMoreAhead,
prepend,
append,
+ removeItem,
updateItem,
});
</script>
diff --git a/packages/client/src/components/ui/popup-menu.vue b/packages/client/src/components/ui/popup-menu.vue
index 8d6c1b5695..2bc7030d77 100644
--- a/packages/client/src/components/ui/popup-menu.vue
+++ b/packages/client/src/components/ui/popup-menu.vue
@@ -19,7 +19,7 @@ defineProps<{
}>();
const emit = defineEmits<{
- (e: 'closed'): void;
+ (ev: 'closed'): void;
}>();
let modal = $ref<InstanceType<typeof MkModal>>();
diff --git a/packages/client/src/components/ui/tooltip.vue b/packages/client/src/components/ui/tooltip.vue
index ee1909554e..571d11ba3b 100644
--- a/packages/client/src/components/ui/tooltip.vue
+++ b/packages/client/src/components/ui/tooltip.vue
@@ -63,7 +63,7 @@ const setPosition = () => {
}
return [left, top];
- }
+ };
const calcPosWhenBottom = () => {
let left: number;
@@ -84,7 +84,7 @@ const setPosition = () => {
}
return [left, top];
- }
+ };
const calcPosWhenLeft = () => {
let left: number;
@@ -105,7 +105,7 @@ const setPosition = () => {
}
return [left, top];
- }
+ };
const calcPosWhenRight = () => {
let left: number;
@@ -126,7 +126,7 @@ const setPosition = () => {
}
return [left, top];
- }
+ };
const calc = (): {
left: number;
@@ -172,7 +172,7 @@ const setPosition = () => {
}
return null as never;
- }
+ };
const { left, top, transformOrigin } = calc();
el.value.style.transformOrigin = transformOrigin;
diff --git a/packages/client/src/components/ui/window.vue b/packages/client/src/components/ui/window.vue
index fa32ecfdef..2066cf579d 100644
--- a/packages/client/src/components/ui/window.vue
+++ b/packages/client/src/components/ui/window.vue
@@ -139,10 +139,10 @@ export default defineComponent({
this.showing = false;
},
- onKeydown(e) {
- if (e.which === 27) { // Esc
- e.preventDefault();
- e.stopPropagation();
+ onKeydown(evt) {
+ if (evt.which === 27) { // Esc
+ evt.preventDefault();
+ evt.stopPropagation();
this.close();
}
},
@@ -162,15 +162,15 @@ export default defineComponent({
this.top();
},
- onHeaderMousedown(e) {
+ onHeaderMousedown(evt) {
const main = this.$el as any;
if (!contains(main, document.activeElement)) main.focus();
const position = main.getBoundingClientRect();
- const clickX = e.touches && e.touches.length > 0 ? e.touches[0].clientX : e.clientX;
- const clickY = e.touches && e.touches.length > 0 ? e.touches[0].clientY : e.clientY;
+ const clickX = evt.touches && evt.touches.length > 0 ? evt.touches[0].clientX : evt.clientX;
+ const clickY = evt.touches && evt.touches.length > 0 ? evt.touches[0].clientY : evt.clientY;
const moveBaseX = clickX - position.left;
const moveBaseY = clickY - position.top;
const browserWidth = window.innerWidth;
@@ -204,10 +204,10 @@ export default defineComponent({
},
// 上ãƒãƒ³ãƒ‰ãƒ«æŽ´ã¿æ™‚
- onTopHandleMousedown(e) {
+ onTopHandleMousedown(evt) {
const main = this.$el as any;
- const base = e.clientY;
+ const base = evt.clientY;
const height = parseInt(getComputedStyle(main, '').height, 10);
const top = parseInt(getComputedStyle(main, '').top, 10);
@@ -230,10 +230,10 @@ export default defineComponent({
},
// å³ãƒãƒ³ãƒ‰ãƒ«æŽ´ã¿æ™‚
- onRightHandleMousedown(e) {
+ onRightHandleMousedown(evt) {
const main = this.$el as any;
- const base = e.clientX;
+ const base = evt.clientX;
const width = parseInt(getComputedStyle(main, '').width, 10);
const left = parseInt(getComputedStyle(main, '').left, 10);
const browserWidth = window.innerWidth;
@@ -254,10 +254,10 @@ export default defineComponent({
},
// 下ãƒãƒ³ãƒ‰ãƒ«æŽ´ã¿æ™‚
- onBottomHandleMousedown(e) {
+ onBottomHandleMousedown(evt) {
const main = this.$el as any;
- const base = e.clientY;
+ const base = evt.clientY;
const height = parseInt(getComputedStyle(main, '').height, 10);
const top = parseInt(getComputedStyle(main, '').top, 10);
const browserHeight = window.innerHeight;
@@ -278,10 +278,10 @@ export default defineComponent({
},
// å·¦ãƒãƒ³ãƒ‰ãƒ«æŽ´ã¿æ™‚
- onLeftHandleMousedown(e) {
+ onLeftHandleMousedown(evt) {
const main = this.$el as any;
- const base = e.clientX;
+ const base = evt.clientX;
const width = parseInt(getComputedStyle(main, '').width, 10);
const left = parseInt(getComputedStyle(main, '').left, 10);
@@ -304,27 +304,27 @@ export default defineComponent({
},
// 左上ãƒãƒ³ãƒ‰ãƒ«æŽ´ã¿æ™‚
- onTopLeftHandleMousedown(e) {
- this.onTopHandleMousedown(e);
- this.onLeftHandleMousedown(e);
+ onTopLeftHandleMousedown(evt) {
+ this.onTopHandleMousedown(evt);
+ this.onLeftHandleMousedown(evt);
},
// å³ä¸Šãƒãƒ³ãƒ‰ãƒ«æŽ´ã¿æ™‚
- onTopRightHandleMousedown(e) {
- this.onTopHandleMousedown(e);
- this.onRightHandleMousedown(e);
+ onTopRightHandleMousedown(evt) {
+ this.onTopHandleMousedown(evt);
+ this.onRightHandleMousedown(evt);
},
// å³ä¸‹ãƒãƒ³ãƒ‰ãƒ«æŽ´ã¿æ™‚
- onBottomRightHandleMousedown(e) {
- this.onBottomHandleMousedown(e);
- this.onRightHandleMousedown(e);
+ onBottomRightHandleMousedown(evt) {
+ this.onBottomHandleMousedown(evt);
+ this.onRightHandleMousedown(evt);
},
// 左下ãƒãƒ³ãƒ‰ãƒ«æŽ´ã¿æ™‚
- onBottomLeftHandleMousedown(e) {
- this.onBottomHandleMousedown(e);
- this.onLeftHandleMousedown(e);
+ onBottomLeftHandleMousedown(evt) {
+ this.onBottomHandleMousedown(evt);
+ this.onLeftHandleMousedown(evt);
},
// 高ã•ã‚’é©ç”¨
diff --git a/packages/client/src/components/url-preview.vue b/packages/client/src/components/url-preview.vue
index c7bbd1fbd1..6c593c7b41 100644
--- a/packages/client/src/components/url-preview.vue
+++ b/packages/client/src/components/url-preview.vue
@@ -90,7 +90,7 @@ fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${requestLang}`).the
sitename = info.sitename;
fetching = false;
player = info.player;
- })
+ });
});
function adjustTweetHeight(message: any) {
diff --git a/packages/client/src/components/user-preview.vue b/packages/client/src/components/user-preview.vue
index 51c5330564..f80947f75a 100644
--- a/packages/client/src/components/user-preview.vue
+++ b/packages/client/src/components/user-preview.vue
@@ -70,7 +70,7 @@ export default defineComponent({
},
mounted() {
- if (typeof this.q == 'object') {
+ if (typeof this.q === 'object') {
this.user = this.q;
this.fetched = true;
} else {
diff --git a/packages/client/src/components/user-select-dialog.vue b/packages/client/src/components/user-select-dialog.vue
index dbef34d547..b34d21af07 100644
--- a/packages/client/src/components/user-select-dialog.vue
+++ b/packages/client/src/components/user-select-dialog.vue
@@ -60,9 +60,9 @@ import * as os from '@/os';
import { defaultStore } from '@/store';
const emit = defineEmits<{
- (e: 'ok', selected: misskey.entities.UserDetailed): void;
- (e: 'cancel'): void;
- (e: 'closed'): void;
+ (ev: 'ok', selected: misskey.entities.UserDetailed): void;
+ (ev: 'cancel'): void;
+ (ev: 'closed'): void;
}>();
let username = $ref('');
diff --git a/packages/client/src/components/visibility-picker.vue b/packages/client/src/components/visibility-picker.vue
index 4b20063a51..c717c3a461 100644
--- a/packages/client/src/components/visibility-picker.vue
+++ b/packages/client/src/components/visibility-picker.vue
@@ -57,9 +57,9 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'changeVisibility', v: typeof misskey.noteVisibilities[number]): void;
- (e: 'changeLocalOnly', v: boolean): void;
- (e: 'closed'): void;
+ (ev: 'changeVisibility', v: typeof misskey.noteVisibilities[number]): void;
+ (ev: 'changeLocalOnly', v: boolean): void;
+ (ev: 'closed'): void;
}>();
let v = $ref(props.currentVisibility);
diff --git a/packages/client/src/components/waiting-dialog.vue b/packages/client/src/components/waiting-dialog.vue
index 7dfcc55695..9e631b55b1 100644
--- a/packages/client/src/components/waiting-dialog.vue
+++ b/packages/client/src/components/waiting-dialog.vue
@@ -21,8 +21,8 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'done');
- (e: 'closed');
+ (ev: 'done');
+ (ev: 'closed');
}>();
function done() {
diff --git a/packages/client/src/components/widgets.vue b/packages/client/src/components/widgets.vue
index da9d935281..74dd79f733 100644
--- a/packages/client/src/components/widgets.vue
+++ b/packages/client/src/components/widgets.vue
@@ -2,11 +2,11 @@
<div class="vjoppmmu">
<template v-if="edit">
<header>
- <MkSelect v-model="widgetAdderSelected" style="margin-bottom: var(--margin)">
+ <MkSelect v-model="widgetAdderSelected" style="margin-bottom: var(--margin)" class="mk-widget-select">
<template #label>{{ $ts.selectWidget }}</template>
<option v-for="widget in widgetDefs" :key="widget" :value="widget">{{ $t(`_widgets.${widget}`) }}</option>
</MkSelect>
- <MkButton inline primary @click="addWidget"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton>
+ <MkButton inline primary class="mk-widget-add" @click="addWidget"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton>
<MkButton inline @click="$emit('exit')">{{ $ts.close }}</MkButton>
</header>
<XDraggable
@@ -19,7 +19,7 @@
<div class="customize-container">
<button class="config _button" @click.prevent.stop="configWidget(element.id)"><i class="fas fa-cog"></i></button>
<button class="remove _button" @click.prevent.stop="removeWidget(element)"><i class="fas fa-times"></i></button>
- <component class="handle" :ref="el => widgetRefs[element.id] = el" :is="`mkw-${element.name}`" :widget="element" @updateProps="updateWidget(element.id, $event)"/>
+ <component :is="`mkw-${element.name}`" :ref="el => widgetRefs[element.id] = el" class="handle" :widget="element" @updateProps="updateWidget(element.id, $event)"/>
</div>
</template>
</XDraggable>
@@ -37,7 +37,7 @@ import { widgets as widgetDefs } from '@/widgets';
export default defineComponent({
components: {
- XDraggable: defineAsyncComponent(() => import('vuedraggable').then(x => x.default)),
+ XDraggable: defineAsyncComponent(() => import('vuedraggable')),
MkSelect,
MkButton,
},
diff --git a/packages/client/src/directives/adaptive-border.ts b/packages/client/src/directives/adaptive-border.ts
index fc426ca2cc..619c9f0b6d 100644
--- a/packages/client/src/directives/adaptive-border.ts
+++ b/packages/client/src/directives/adaptive-border.ts
@@ -9,7 +9,7 @@ export default {
} else {
return el.parentElement ? getBgColor(el.parentElement) : 'transparent';
}
- }
+ };
const parentBg = getBgColor(src.parentElement);
diff --git a/packages/client/src/directives/get-size.ts b/packages/client/src/directives/get-size.ts
index 1fcd0718dc..2c4e9c188d 100644
--- a/packages/client/src/directives/get-size.ts
+++ b/packages/client/src/directives/get-size.ts
@@ -25,12 +25,12 @@ function calc(src: Element) {
return;
}
if (info.intersection) {
- info.intersection.disconnect()
+ info.intersection.disconnect();
delete info.intersection;
- };
+ }
info.fn(width, height);
-};
+}
export default {
mounted(src, binding, vn) {
diff --git a/packages/client/src/directives/panel.ts b/packages/client/src/directives/panel.ts
index 5f9158db2e..d31dc41ed4 100644
--- a/packages/client/src/directives/panel.ts
+++ b/packages/client/src/directives/panel.ts
@@ -9,7 +9,7 @@ export default {
} else {
return el.parentElement ? getBgColor(el.parentElement) : 'transparent';
}
- }
+ };
const parentBg = getBgColor(src.parentElement);
diff --git a/packages/client/src/directives/size.ts b/packages/client/src/directives/size.ts
index 36f649f180..51855e0de5 100644
--- a/packages/client/src/directives/size.ts
+++ b/packages/client/src/directives/size.ts
@@ -60,9 +60,9 @@ function calc(el: Element) {
return;
}
if (info.intersection) {
- info.intersection.disconnect()
+ info.intersection.disconnect();
delete info.intersection;
- };
+ }
mountings.set(el, Object.assign(info, { previousWidth: width }));
diff --git a/packages/client/src/directives/tooltip.ts b/packages/client/src/directives/tooltip.ts
index dd715227a4..0e69da954e 100644
--- a/packages/client/src/directives/tooltip.ts
+++ b/packages/client/src/directives/tooltip.ts
@@ -1,7 +1,7 @@
// TODO: useTooltip関数使ã†ã‚ˆã†ã«ã—ãŸã„
// ãŸã ãƒ‡ã‚£ãƒ¬ã‚¯ãƒ†ã‚£ãƒ–内ã§onUnmountedãªã©ã®composition api使ãˆã‚‹ã®ã‹ä¸æ˜Ž
-import { Directive, ref } from 'vue';
+import { defineAsyncComponent, Directive, ref } from 'vue';
import { isTouchUsing } from '@/scripts/touch';
import { popup, alert } from '@/os';
@@ -45,7 +45,7 @@ export default {
if (self.text == null) return;
const showing = ref(true);
- popup(import('@/components/ui/tooltip.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/ui/tooltip.vue')), {
showing,
text: self.text,
targetElement: el,
diff --git a/packages/client/src/directives/user-preview.ts b/packages/client/src/directives/user-preview.ts
index cdd2afa194..9d18a69877 100644
--- a/packages/client/src/directives/user-preview.ts
+++ b/packages/client/src/directives/user-preview.ts
@@ -1,4 +1,4 @@
-import { Directive, ref } from 'vue';
+import { defineAsyncComponent, Directive, ref } from 'vue';
import autobind from 'autobind-decorator';
import { popup } from '@/os';
@@ -24,7 +24,7 @@ export class UserPreview {
const showing = ref(true);
- popup(import('@/components/user-preview.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/user-preview.vue')), {
showing,
q: this.user,
source: this.el
diff --git a/packages/client/src/emojilist.json b/packages/client/src/emojilist.json
index 75c424ab4b..402e82e33b 100644
--- a/packages/client/src/emojilist.json
+++ b/packages/client/src/emojilist.json
@@ -96,6 +96,13 @@
{ "category": "face", "char": "\uD83D\uDE36\u200D\uD83C\uDF2B\uFE0F", "name": "face_in_clouds", "keywords": [] },
{ "category": "face", "char": "\uD83D\uDE2E\u200D\uD83D\uDCA8", "name": "face_exhaling", "keywords": [] },
{ "category": "face", "char": "\uD83D\uDE35\u200D\uD83D\uDCAB", "name": "face_with_spiral_eyes", "keywords": [] },
+ { "category": "face", "char": "\uD83E\uDEE0", "name": "melting_face", "keywords": ["disappear", "dissolve", "liquid", "melt", "toketa"] },
+ { "category": "face", "char": "\uD83E\uDEE2", "name": "face_with_open_eyes_and_hand_over_mouth", "keywords": ["amazement", "awe", "disbelief", "embarrass", "scared", "surprise", "ohoho"] },
+ { "category": "face", "char": "\uD83E\uDEE3", "name": "face_with_peeking_eye", "keywords": ["captivated", "peep", "stare", "chunibyo"] },
+ { "category": "face", "char": "\uD83E\uDEE1", "name": "saluting_face", "keywords": ["ok", "salute", "sunny", "troops", "yes", "raja"] },
+ { "category": "face", "char": "\uD83E\uDEE5", "name": "dotted_line_face", "keywords": ["depressed", "disappear", "hide", "introvert", "invisible", "tensen"] },
+ { "category": "face", "char": "\uD83E\uDEE4", "name": "face_with_diagonal_mouth", "keywords": ["disappointed", "meh", "skeptical", "unsure"] },
+ { "category": "face", "char": "\uD83E\uDD79", "name": "face_holding_back_tears", "keywords": ["angry", "cry", "proud", "resist", "sad"] },
{ "category": "face", "char": "💩", "name": "poop", "keywords": ["hankey", "shitface", "fail", "turd", "shit"] },
{ "category": "face", "char": "😈", "name": "smiling_imp", "keywords": ["devil", "horns"] },
{ "category": "face", "char": "👿", "name": "imp", "keywords": ["devil", "angry", "horns"] },
@@ -149,11 +156,19 @@
{ "category": "people", "char": "🤞", "name": "crossed_fingers", "keywords": ["good", "lucky"] },
{ "category": "people", "char": "🖖", "name": "vulcan_salute", "keywords": ["hand", "fingers", "spock", "star trek"] },
{ "category": "people", "char": "âœ", "name": "writing_hand", "keywords": ["lower_left_ballpoint_pen", "stationery", "write", "compose"] },
+ { "category": "people", "char": "\uD83E\uDEF0", "name": "hand_with_index_finger_and_thumb_crossed", "keywords": [] },
+ { "category": "people", "char": "\uD83E\uDEF1", "name": "rightwards_hand", "keywords": [] },
+ { "category": "people", "char": "\uD83E\uDEF2", "name": "leftwards_hand", "keywords": [] },
+ { "category": "people", "char": "\uD83E\uDEF3", "name": "palm_down_hand", "keywords": [] },
+ { "category": "people", "char": "\uD83E\uDEF4", "name": "palm_up_hand", "keywords": [] },
+ { "category": "people", "char": "\uD83E\uDEF5", "name": "index_pointing_at_the_viewer", "keywords": [] },
+ { "category": "people", "char": "\uD83E\uDEF6", "name": "heart_hands", "keywords": ["moemoekyun"] },
{ "category": "people", "char": "ðŸ¤", "name": "pinching_hand", "keywords": ["hand", "fingers"] },
{ "category": "people", "char": "🤌", "name": "pinched_fingers", "keywords": ["hand", "fingers"] },
{ "category": "people", "char": "🤳", "name": "selfie", "keywords": ["camera", "phone"] },
{ "category": "people", "char": "💅", "name": "nail_care", "keywords": ["beauty", "manicure", "finger", "fashion", "nail"] },
{ "category": "people", "char": "👄", "name": "lips", "keywords": ["mouth", "kiss"] },
+ { "category": "people", "char": "\uD83E\uDEE6", "name": "biting_lip", "keywords": [] },
{ "category": "people", "char": "🦷", "name": "tooth", "keywords": ["teeth", "dentist"] },
{ "category": "people", "char": "👅", "name": "tongue", "keywords": ["mouth", "playful"] },
{ "category": "people", "char": "👂", "name": "ear", "keywords": ["face", "hear", "sound", "listen"] },
@@ -275,7 +290,11 @@
{ "category": "people", "char": "🧚â€â™€ï¸", "name": "woman_fairy", "keywords": ["woman", "female"] },
{ "category": "people", "char": "🧚â€â™‚ï¸", "name": "man_fairy", "keywords": ["man", "male"] },
{ "category": "people", "char": "👼", "name": "angel", "keywords": ["heaven", "wings", "halo"] },
+ { "category": "people", "char": "\uD83E\uDDCC", "name": "troll", "keywords": [] },
{ "category": "people", "char": "🤰", "name": "pregnant_woman", "keywords": ["baby"] },
+ { "category": "people", "char": "\uD83E\uDEC3", "name": "pregnant_man", "keywords": [] },
+ { "category": "people", "char": "\uD83E\uDEC4", "name": "pregnant_person", "keywords": [] },
+ { "category": "people", "char": "\uD83E\uDEC5", "name": "person_with_crown", "keywords": [] },
{ "category": "people", "char": "🤱", "name": "breastfeeding", "keywords": ["nursing", "baby"] },
{ "category": "people", "char": "\uD83D\uDC69\u200D\uD83C\uDF7C", "name": "woman_feeding_baby", "keywords": [] },
{ "category": "people", "char": "\uD83D\uDC68\u200D\uD83C\uDF7C", "name": "man_feeding_baby", "keywords": [] },
@@ -459,7 +478,7 @@
{ "category": "animals_and_nature", "char": "ðŸ›", "name": "bug", "keywords": ["animal", "insect", "nature", "worm"] },
{ "category": "animals_and_nature", "char": "🦋", "name": "butterfly", "keywords": ["animal", "insect", "nature", "caterpillar"] },
{ "category": "animals_and_nature", "char": "ðŸŒ", "name": "snail", "keywords": ["slow", "animal", "shell"] },
- { "category": "animals_and_nature", "char": "ðŸž", "name": "beetle", "keywords": ["animal", "insect", "nature", "ladybug"] },
+ { "category": "animals_and_nature", "char": "ðŸž", "name": "lady_beetle", "keywords": ["animal", "insect", "nature", "ladybug"] },
{ "category": "animals_and_nature", "char": "ðŸœ", "name": "ant", "keywords": ["animal", "insect", "nature", "bug"] },
{ "category": "animals_and_nature", "char": "🦗", "name": "grasshopper", "keywords": ["animal", "cricket", "chirp"] },
{ "category": "animals_and_nature", "char": "🕷", "name": "spider", "keywords": ["animal", "arachnid"] },
@@ -615,6 +634,10 @@
{ "category": "animals_and_nature", "char": "💧", "name": "droplet", "keywords": ["water", "drip", "faucet", "spring"] },
{ "category": "animals_and_nature", "char": "💦", "name": "sweat_drops", "keywords": ["water", "drip", "oops"] },
{ "category": "animals_and_nature", "char": "🌊", "name": "ocean", "keywords": ["sea", "water", "wave", "nature", "tsunami", "disaster"] },
+ { "category": "animals_and_nature", "char": "\uD83E\uDEB7", "name": "lotus", "keywords": [] },
+ { "category": "animals_and_nature", "char": "\uD83E\uDEB8", "name": "coral", "keywords": [] },
+ { "category": "animals_and_nature", "char": "\uD83E\uDEB9", "name": "empty_nest", "keywords": [] },
+ { "category": "animals_and_nature", "char": "\uD83E\uDEBA", "name": "nest_with_eggs", "keywords": [] },
{ "category": "food_and_drink", "char": "ðŸ", "name": "green_apple", "keywords": ["fruit", "nature"] },
{ "category": "food_and_drink", "char": "ðŸŽ", "name": "apple", "keywords": ["fruit", "mac", "school"] },
{ "category": "food_and_drink", "char": "ðŸ", "name": "pear", "keywords": ["fruit", "nature", "food"] },
@@ -737,6 +760,9 @@
{ "category": "food_and_drink", "char": "🥣", "name": "bowl_with_spoon", "keywords": ["food", "breakfast", "cereal", "oatmeal", "porridge"] },
{ "category": "food_and_drink", "char": "🥡", "name": "takeout_box", "keywords": ["food", "leftovers"] },
{ "category": "food_and_drink", "char": "🥢", "name": "chopsticks", "keywords": ["food"] },
+ { "category": "food_and_drink", "char": "\uD83E\uDED7", "name": "pouring_liquid", "keywords": [] },
+ { "category": "food_and_drink", "char": "\uD83E\uDED8", "name": "beans", "keywords": [] },
+ { "category": "food_and_drink", "char": "\uD83E\uDED9", "name": "jar", "keywords": [] },
{ "category": "activity", "char": "âš½", "name": "soccer", "keywords": ["sports", "football"] },
{ "category": "activity", "char": "ðŸ€", "name": "basketball", "keywords": ["sports", "balls", "NBA"] },
{ "category": "activity", "char": "ðŸˆ", "name": "football", "keywords": ["sports", "balls", "NFL"] },
@@ -844,6 +870,8 @@
{ "category": "activity", "char": "🪄", "name": "magic_wand", "keywords": [] },
{ "category": "activity", "char": "🪅", "name": "pinata", "keywords": [] },
{ "category": "activity", "char": "🪆", "name": "nesting_dolls", "keywords": [] },
+ { "category": "activity", "char": "\uD83E\uDEAC", "name": "hamsa", "keywords": [] },
+ { "category": "activity", "char": "\uD83E\uDEA9", "name": "mirror_ball", "keywords": [] },
{ "category": "travel_and_places", "char": "🚗", "name": "red_car", "keywords": ["red", "transportation", "vehicle"] },
{ "category": "travel_and_places", "char": "🚕", "name": "taxi", "keywords": ["uber", "vehicle", "cars", "transportation"] },
{ "category": "travel_and_places", "char": "🚙", "name": "blue_car", "keywords": ["transportation", "vehicle"] },
@@ -971,11 +999,12 @@
{ "category": "travel_and_places", "char": "🕋", "name": "kaaba", "keywords": ["mecca", "mosque", "islam"] },
{ "category": "travel_and_places", "char": "⛩", "name": "shinto_shrine", "keywords": ["temple", "japan", "kyoto"] },
{ "category": "travel_and_places", "char": "🛕", "name": "hindu_temple", "keywords": ["temple"] },
-
{ "category": "travel_and_places", "char": "🪨", "name": "rock", "keywords": [] },
{ "category": "travel_and_places", "char": "🪵", "name": "wood", "keywords": [] },
{ "category": "travel_and_places", "char": "🛖", "name": "hut", "keywords": [] },
-
+ { "category": "travel_and_places", "char": "\uD83D\uDEDD", "name": "playground_slide", "keywords": [] },
+ { "category": "travel_and_places", "char": "\uD83D\uDEDE", "name": "wheel", "keywords": [] },
+ { "category": "travel_and_places", "char": "\uD83D\uDEDF", "name": "ring_buoy", "keywords": [] },
{ "category": "objects", "char": "⌚", "name": "watch", "keywords": ["time", "accessories"] },
{ "category": "objects", "char": "📱", "name": "iphone", "keywords": ["technology", "apple", "gadgets", "dial"] },
{ "category": "objects", "char": "📲", "name": "calling", "keywords": ["iphone", "incoming"] },
@@ -1016,6 +1045,7 @@
{ "category": "objects", "char": "⌛", "name": "hourglass", "keywords": ["time", "clock", "oldschool", "limit", "exam", "quiz", "test"] },
{ "category": "objects", "char": "📡", "name": "satellite", "keywords": ["communication", "future", "radio", "space"] },
{ "category": "objects", "char": "🔋", "name": "battery", "keywords": ["power", "energy", "sustain"] },
+ { "category": "objects", "char": "\uD83E\uDEAB", "name": "battery", "keywords": [] },
{ "category": "objects", "char": "🔌", "name": "electric_plug", "keywords": ["charger", "power"] },
{ "category": "objects", "char": "💡", "name": "bulb", "keywords": ["light", "electricity", "idea"] },
{ "category": "objects", "char": "🔦", "name": "flashlight", "keywords": ["dark", "camping", "sight", "night"] },
@@ -1031,6 +1061,7 @@
{ "category": "objects", "char": "💰", "name": "moneybag", "keywords": ["dollar", "payment", "coins", "sale"] },
{ "category": "objects", "char": "🪙", "name": "coin", "keywords": ["dollar", "payment", "coins", "sale"] },
{ "category": "objects", "char": "💳", "name": "credit_card", "keywords": ["money", "sales", "dollar", "bill", "payment", "shopping"] },
+ { "category": "objects", "char": "\uD83E\uDEAB", "name": "identification_card", "keywords": [] },
{ "category": "objects", "char": "💎", "name": "gem", "keywords": ["blue", "ruby", "diamond", "jewelry"] },
{ "category": "objects", "char": "âš–", "name": "balance_scale", "keywords": ["law", "fairness", "weight"] },
{ "category": "objects", "char": "🧰", "name": "toolbox", "keywords": ["tools", "diy", "fix", "maintainer", "mechanic"] },
@@ -1077,6 +1108,8 @@
{ "category": "objects", "char": "🩹", "name": "adhesive_bandage", "keywords": ["health", "hospital", "medicine", "needle", "doctor", "nurse"] },
{ "category": "objects", "char": "🩺", "name": "stethoscope", "keywords": ["health", "hospital", "medicine", "needle", "doctor", "nurse"] },
{ "category": "objects", "char": "🪒", "name": "razor", "keywords": ["health"] },
+ { "category": "objects", "char": "\uD83E\uDE7B", "name": "xray", "keywords": [] },
+ { "category": "objects", "char": "\uD83E\uDE7C", "name": "crutch", "keywords": [] },
{ "category": "objects", "char": "🧬", "name": "dna", "keywords": ["biologist", "genetics", "life"] },
{ "category": "objects", "char": "🧫", "name": "petri_dish", "keywords": ["bacteria", "biology", "culture", "lab"] },
{ "category": "objects", "char": "🧪", "name": "test_tube", "keywords": ["chemistry", "experiment", "lab", "science"] },
@@ -1111,6 +1144,7 @@
{ "category": "objects", "char": "🪤", "name": "mouse_trap", "keywords": ["household"] },
{ "category": "objects", "char": "🪣", "name": "bucket", "keywords": ["household"] },
{ "category": "objects", "char": "🪥", "name": "toothbrush", "keywords": ["household"] },
+ { "category": "objects", "char": "\uD83E\uDEE7", "name": "bubbles", "keywords": [] },
{ "category": "objects", "char": "â›±", "name": "parasol_on_ground", "keywords": ["weather", "summer"] },
{ "category": "objects", "char": "🗿", "name": "moyai", "keywords": ["rock", "easter island", "moai"] },
{ "category": "objects", "char": "ðŸ›", "name": "shopping", "keywords": ["mall", "buy", "purchase"] },
@@ -1404,6 +1438,7 @@
{ "category": "symbols", "char": "âž–", "name": "heavy_minus_sign", "keywords": ["math", "calculation", "subtract", "less"] },
{ "category": "symbols", "char": "âž—", "name": "heavy_division_sign", "keywords": ["divide", "math", "calculation"] },
{ "category": "symbols", "char": "✖ï¸", "name": "heavy_multiplication_x", "keywords": ["math", "calculation"] },
+ { "category": "symbols", "char": "\uD83D\uDFF0", "name": "heavy_equals_sign", "keywords": [] },
{ "category": "symbols", "char": "♾", "name": "infinity", "keywords": ["forever"] },
{ "category": "symbols", "char": "💲", "name": "heavy_dollar_sign", "keywords": ["money", "sales", "payment", "currency", "buck"] },
{ "category": "symbols", "char": "💱", "name": "currency_exchange", "keywords": ["money", "sales", "dollar", "travel"] },
@@ -1747,3 +1782,4 @@
{ "category": "flags", "char": "🇺🇳", "name": "united_nations", "keywords": ["un", "flag", "banner"] },
{ "category": "flags", "char": "ðŸ´â€â˜ ï¸", "name": "pirate_flag", "keywords": ["skull", "crossbones", "flag", "banner"] }
]
+
diff --git a/packages/client/src/filters/bytes.ts b/packages/client/src/filters/bytes.ts
index 50e63534b6..c80f2f0ed2 100644
--- a/packages/client/src/filters/bytes.ts
+++ b/packages/client/src/filters/bytes.ts
@@ -1,7 +1,7 @@
export default (v, digits = 0) => {
if (v == null) return '?';
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
- if (v == 0) return '0';
+ if (v === 0) return '0';
const isMinus = v < 0;
if (isMinus) v = -v;
const i = Math.floor(Math.log(v) / Math.log(1024));
diff --git a/packages/client/src/init.ts b/packages/client/src/init.ts
index ab3299d22b..bb6176e409 100644
--- a/packages/client/src/init.ts
+++ b/packages/client/src/init.ts
@@ -13,9 +13,9 @@ if (localStorage.getItem('accounts') != null) {
}
//#endregion
-import { computed, createApp, watch, markRaw, version as vueVersion } from 'vue';
+import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent } from 'vue';
import compareVersions from 'compare-versions';
-import * as JSON5 from 'json5';
+import JSON5 from 'json5';
import widgets from '@/widgets';
import directives from '@/directives';
@@ -146,8 +146,7 @@ if ($i && $i.token) {
try {
document.body.innerHTML = '<div>Please wait...</div>';
await login(i);
- location.reload();
- } catch (e) {
+ } catch (err) {
// Render the error screen
// TODO: ã¡ã‚ƒã‚“ã¨ã—ãŸã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆã‚’レンダリングã™ã‚‹(v10ã¨ã‹ã®ãƒˆãƒ©ãƒ–ルシューティングゲーム付ãã®ã‚„ã¤ã¿ãŸã„ãª)
document.body.innerHTML = '<div id="err">Oops!</div>';
@@ -169,14 +168,14 @@ fetchInstanceMetaPromise.then(() => {
initializeSw();
});
-const app = createApp(await (
- window.location.search === '?zen' ? import('@/ui/zen.vue') :
- !$i ? import('@/ui/visitor.vue') :
- ui === 'deck' ? import('@/ui/deck.vue') :
- ui === 'desktop' ? import('@/ui/desktop.vue') :
- ui === 'classic' ? import('@/ui/classic.vue') :
- import('@/ui/universal.vue')
-).then(x => x.default));
+const app = createApp(
+ window.location.search === '?zen' ? defineAsyncComponent(() => import('@/ui/zen.vue')) :
+ !$i ? defineAsyncComponent(() => import('@/ui/visitor.vue')) :
+ ui === 'deck' ? defineAsyncComponent(() => import('@/ui/deck.vue')) :
+ ui === 'desktop' ? defineAsyncComponent(() => import('@/ui/desktop.vue')) :
+ ui === 'classic' ? defineAsyncComponent(() => import('@/ui/classic.vue')) :
+ defineAsyncComponent(() => import('@/ui/universal.vue'))
+);
if (_DEV_) {
app.config.performance = true;
@@ -204,8 +203,24 @@ if (splash) splash.addEventListener('transitionend', () => {
splash.remove();
});
-const rootEl = document.createElement('div');
-document.body.appendChild(rootEl);
+// https://github.com/misskey-dev/misskey/pull/8575#issuecomment-1114239210
+// ãªãœã‹init.tsã®å†…容ãŒ2回実行ã•れるã“ã¨ãŒã‚ã‚‹ãŸã‚ã€mountã™ã‚‹divã‚’1ã¤ã«åˆ¶é™ã™ã‚‹
+const rootEl = (() => {
+ const MISSKEY_MOUNT_DIV_ID = 'misskey_app';
+
+ const currentEl = document.getElementById(MISSKEY_MOUNT_DIV_ID);
+
+ if (currentEl) {
+ console.warn('multiple import detected');
+ return currentEl;
+ }
+
+ const rootEl = document.createElement('div');
+ rootEl.id = MISSKEY_MOUNT_DIV_ID;
+ document.body.appendChild(rootEl);
+ return rootEl;
+})();
+
app.mount(rootEl);
// boot.jsã®ã‚„ã¤ã‚’解除
@@ -231,10 +246,10 @@ if (lastVersion !== version) {
if (lastVersion != null && compareVersions(version, lastVersion) === 1) {
// ログインã—ã¦ã‚‹å ´åˆã ã‘
if ($i) {
- popup(import('@/components/updated.vue'), {}, {}, 'closed');
+ popup(defineAsyncComponent(() => import('@/components/updated.vue')), {}, {}, 'closed');
}
}
- } catch (e) {
+ } catch (err) {
}
}
@@ -319,7 +334,7 @@ stream.on('_disconnected_', async () => {
}
});
-stream.on('emojiAdded', data => {
+stream.on('emojiAdded', emojiData => {
// TODO
//store.commit('instance/set', );
});
diff --git a/packages/client/src/instance.ts b/packages/client/src/instance.ts
index 6e912aa2e5..d24eb2419a 100644
--- a/packages/client/src/instance.ts
+++ b/packages/client/src/instance.ts
@@ -4,11 +4,11 @@ import { api } from './os';
// TODO: ä»–ã®ã‚¿ãƒ–ã¨æ°¸ç¶šåŒ–ã•れãŸstateã‚’åŒæœŸ
-const data = localStorage.getItem('instance');
+const instanceData = localStorage.getItem('instance');
// TODO: instanceをリアクティブã«ã™ã‚‹ã‹ã¯å†è€ƒã®ä½™åœ°ã‚り
-export const instance: Misskey.entities.InstanceMetadata = reactive(data ? JSON.parse(data) : {
+export const instance: Misskey.entities.InstanceMetadata = reactive(instanceData ? JSON.parse(instanceData) : {
// TODO: set default values
});
diff --git a/packages/client/src/os.ts b/packages/client/src/os.ts
index 43c110555f..14860465fa 100644
--- a/packages/client/src/os.ts
+++ b/packages/client/src/os.ts
@@ -1,6 +1,6 @@
// TODO: ãªã‚“ã§ã‚‚ã‹ã‚“ã§ã‚‚os.tsã«çªã£è¾¼ã‚€ã®ã‚„ã‚ãŸã„ã®ã§ã‚ˆã—ãªã«åˆ†å‰²ã™ã‚‹
-import { Component, defineAsyncComponent, markRaw, reactive, Ref, ref } from 'vue';
+import { Component, markRaw, Ref, ref, defineAsyncComponent } from 'vue';
import { EventEmitter } from 'eventemitter3';
import insertTextAtCursor from 'insert-text-at-cursor';
import * as Misskey from 'misskey-js';
@@ -10,7 +10,6 @@ import MkWaitingDialog from '@/components/waiting-dialog.vue';
import { MenuItem } from '@/types/menu';
import { resolve } from '@/router';
import { $i } from '@/account';
-import { defaultStore } from '@/store';
export const pendingApiRequestsCount = ref(0);
@@ -35,7 +34,7 @@ export const api = ((endpoint: string, data: Record<string, any> = {}, token?: s
method: 'POST',
body: JSON.stringify(data),
credentials: 'omit',
- cache: 'no-cache'
+ cache: 'no-cache',
}).then(async (res) => {
const body = res.status === 204 ? null : await res.json();
@@ -60,10 +59,10 @@ export const apiWithDialog = ((
token?: string | null | undefined,
) => {
const promise = api(endpoint, data, token);
- promiseDialog(promise, null, (e) => {
+ promiseDialog(promise, null, (err) => {
alert({
type: 'error',
- text: e.message + '\n' + (e as any).id,
+ text: err.message + '\n' + (err as any).id,
});
});
@@ -73,7 +72,7 @@ export const apiWithDialog = ((
export function promiseDialog<T extends Promise<any>>(
promise: T,
onSuccess?: ((res: any) => void) | null,
- onFailure?: ((e: Error) => void) | null,
+ onFailure?: ((err: Error) => void) | null,
text?: string,
): T {
const showing = ref(true);
@@ -89,14 +88,14 @@ export function promiseDialog<T extends Promise<any>>(
showing.value = false;
}, 1000);
}
- }).catch(e => {
+ }).catch(err => {
showing.value = false;
if (onFailure) {
- onFailure(e);
+ onFailure(err);
} else {
alert({
type: 'error',
- text: e
+ text: err,
});
}
});
@@ -111,10 +110,6 @@ export function promiseDialog<T extends Promise<any>>(
return promise;
}
-function isModule(x: any): x is typeof import('*.vue') {
- return x.default != null;
-}
-
let popupIdCount = 0;
export const popups = ref([]) as Ref<{
id: any;
@@ -132,10 +127,7 @@ export function claimZIndex(priority: 'low' | 'middle' | 'high' = 'low'): number
return zIndexes[priority];
}
-export async function popup(component: Component | typeof import('*.vue') | Promise<Component | typeof import('*.vue')>, props: Record<string, any>, events = {}, disposeEvent?: string) {
- if (component.then) component = await component;
-
- if (isModule(component)) component = component.default;
+export async function popup(component: Component, props: Record<string, any>, events = {}, disposeEvent?: string) {
markRaw(component);
const id = ++popupIdCount;
@@ -150,7 +142,7 @@ export async function popup(component: Component | typeof import('*.vue') | Prom
props,
events: disposeEvent ? {
...events,
- [disposeEvent]: dispose
+ [disposeEvent]: dispose,
} : events,
id,
};
@@ -164,7 +156,7 @@ export async function popup(component: Component | typeof import('*.vue') | Prom
export function pageWindow(path: string) {
const { component, props } = resolve(path);
- popup(import('@/components/page-window.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/page-window.vue')), {
initialPath: path,
initialComponent: markRaw(component),
initialProps: props,
@@ -173,7 +165,7 @@ export function pageWindow(path: string) {
export function modalPageWindow(path: string) {
const { component, props } = resolve(path);
- popup(import('@/components/modal-page-window.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/modal-page-window.vue')), {
initialPath: path,
initialComponent: markRaw(component),
initialProps: props,
@@ -181,8 +173,8 @@ export function modalPageWindow(path: string) {
}
export function toast(message: string) {
- popup(import('@/components/toast.vue'), {
- message
+ popup(defineAsyncComponent(() => import('@/components/toast.vue')), {
+ message,
}, {}, 'closed');
}
@@ -192,7 +184,7 @@ export function alert(props: {
text?: string | null;
}): Promise<void> {
return new Promise((resolve, reject) => {
- popup(import('@/components/dialog.vue'), props, {
+ popup(defineAsyncComponent(() => import('@/components/dialog.vue')), props, {
done: result => {
resolve();
},
@@ -206,7 +198,7 @@ export function confirm(props: {
text?: string | null;
}): Promise<{ canceled: boolean }> {
return new Promise((resolve, reject) => {
- popup(import('@/components/dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/dialog.vue')), {
...props,
showCancelButton: true,
}, {
@@ -227,14 +219,14 @@ export function inputText(props: {
canceled: false; result: string;
}> {
return new Promise((resolve, reject) => {
- popup(import('@/components/dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/dialog.vue')), {
title: props.title,
text: props.text,
input: {
type: props.type,
placeholder: props.placeholder,
default: props.default,
- }
+ },
}, {
done: result => {
resolve(result ? result : { canceled: true });
@@ -252,14 +244,14 @@ export function inputNumber(props: {
canceled: false; result: number;
}> {
return new Promise((resolve, reject) => {
- popup(import('@/components/dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/dialog.vue')), {
title: props.title,
text: props.text,
input: {
type: 'number',
placeholder: props.placeholder,
default: props.default,
- }
+ },
}, {
done: result => {
resolve(result ? result : { canceled: true });
@@ -277,14 +269,14 @@ export function inputDate(props: {
canceled: false; result: Date;
}> {
return new Promise((resolve, reject) => {
- popup(import('@/components/dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/dialog.vue')), {
title: props.title,
text: props.text,
input: {
type: 'date',
placeholder: props.placeholder,
default: props.default,
- }
+ },
}, {
done: result => {
resolve(result ? { result: new Date(result.result), canceled: false } : { canceled: true });
@@ -293,7 +285,7 @@ export function inputDate(props: {
});
}
-export function select<C extends any = any>(props: {
+export function select<C = any>(props: {
title?: string | null;
text?: string | null;
default?: string | null;
@@ -314,14 +306,14 @@ export function select<C extends any = any>(props: {
canceled: false; result: C;
}> {
return new Promise((resolve, reject) => {
- popup(import('@/components/dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/dialog.vue')), {
title: props.title,
text: props.text,
select: {
items: props.items,
groupedItems: props.groupedItems,
default: props.default,
- }
+ },
}, {
done: result => {
resolve(result ? result : { canceled: true });
@@ -336,9 +328,9 @@ export function success() {
window.setTimeout(() => {
showing.value = false;
}, 1000);
- popup(import('@/components/waiting-dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/waiting-dialog.vue')), {
success: true,
- showing: showing
+ showing: showing,
}, {
done: () => resolve(),
}, 'closed');
@@ -348,9 +340,9 @@ export function success() {
export function waiting() {
return new Promise((resolve, reject) => {
const showing = ref(true);
- popup(import('@/components/waiting-dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/waiting-dialog.vue')), {
success: false,
- showing: showing
+ showing: showing,
}, {
done: () => resolve(),
}, 'closed');
@@ -359,7 +351,7 @@ export function waiting() {
export function form(title, form) {
return new Promise((resolve, reject) => {
- popup(import('@/components/form-dialog.vue'), { title, form }, {
+ popup(defineAsyncComponent(() => import('@/components/form-dialog.vue')), { title, form }, {
done: result => {
resolve(result);
},
@@ -369,7 +361,7 @@ export function form(title, form) {
export async function selectUser() {
return new Promise((resolve, reject) => {
- popup(import('@/components/user-select-dialog.vue'), {}, {
+ popup(defineAsyncComponent(() => import('@/components/user-select-dialog.vue')), {}, {
ok: user => {
resolve(user);
},
@@ -379,9 +371,9 @@ export async function selectUser() {
export async function selectDriveFile(multiple: boolean) {
return new Promise((resolve, reject) => {
- popup(import('@/components/drive-select-dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/drive-select-dialog.vue')), {
type: 'file',
- multiple
+ multiple,
}, {
done: files => {
if (files) {
@@ -394,9 +386,9 @@ export async function selectDriveFile(multiple: boolean) {
export async function selectDriveFolder(multiple: boolean) {
return new Promise((resolve, reject) => {
- popup(import('@/components/drive-select-dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/drive-select-dialog.vue')), {
type: 'folder',
- multiple
+ multiple,
}, {
done: folders => {
if (folders) {
@@ -409,9 +401,9 @@ export async function selectDriveFolder(multiple: boolean) {
export async function pickEmoji(src: HTMLElement | null, opts) {
return new Promise((resolve, reject) => {
- popup(import('@/components/emoji-picker-dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/emoji-picker-dialog.vue')), {
src,
- ...opts
+ ...opts,
}, {
done: emoji => {
resolve(emoji);
@@ -420,6 +412,21 @@ export async function pickEmoji(src: HTMLElement | null, opts) {
});
}
+export async function cropImage(image: Misskey.entities.DriveFile, options: {
+ aspectRatio: number;
+}): Promise<Misskey.entities.DriveFile> {
+ return new Promise((resolve, reject) => {
+ popup(defineAsyncComponent(() => import('@/components/cropper-dialog.vue')), {
+ file: image,
+ aspectRatio: options.aspectRatio,
+ }, {
+ ok: x => {
+ resolve(x);
+ },
+ }, 'closed');
+ });
+}
+
type AwaitType<T> =
T extends Promise<infer U> ? U :
T extends (...args: any[]) => Promise<infer V> ? V :
@@ -459,9 +466,9 @@ export async function openEmojiPicker(src?: HTMLElement, opts, initialTextarea:
characterData: false,
});
- openingEmojiPicker = await popup(import('@/components/emoji-picker-window.vue'), {
+ openingEmojiPicker = await popup(defineAsyncComponent(() => import('@/components/emoji-picker-window.vue')), {
src,
- ...opts
+ ...opts,
}, {
chosen: emoji => {
insertTextAtCursor(activeTextarea, emoji);
@@ -470,7 +477,7 @@ export async function openEmojiPicker(src?: HTMLElement, opts, initialTextarea:
openingEmojiPicker!.dispose();
openingEmojiPicker = null;
observer.disconnect();
- }
+ },
});
}
@@ -481,12 +488,12 @@ export function popupMenu(items: MenuItem[] | Ref<MenuItem[]>, src?: HTMLElement
}) {
return new Promise((resolve, reject) => {
let dispose;
- popup(import('@/components/ui/popup-menu.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/ui/popup-menu.vue')), {
items,
src,
width: options?.width,
align: options?.align,
- viaKeyboard: options?.viaKeyboard
+ viaKeyboard: options?.viaKeyboard,
}, {
closed: () => {
resolve();
@@ -502,7 +509,7 @@ export function contextMenu(items: MenuItem[] | Ref<MenuItem[]>, ev: MouseEvent)
ev.preventDefault();
return new Promise((resolve, reject) => {
let dispose;
- popup(import('@/components/ui/context-menu.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/ui/context-menu.vue')), {
items,
ev,
}, {
@@ -537,78 +544,6 @@ export function post(props: Record<string, any> = {}) {
export const deckGlobalEvents = new EventEmitter();
-export const uploads = ref<{
- id: string;
- name: string;
- progressMax: number | undefined;
- progressValue: number | undefined;
- img: string;
-}[]>([]);
-
-export function upload(file: File, folder?: any, name?: string, keepOriginal: boolean = defaultStore.state.keepOriginalUploading): Promise<Misskey.entities.DriveFile> {
- if (folder && typeof folder === 'object') folder = folder.id;
-
- return new Promise((resolve, reject) => {
- const id = Math.random().toString();
-
- const reader = new FileReader();
- reader.onload = (e) => {
- const ctx = reactive({
- id: id,
- name: name || file.name || 'untitled',
- progressMax: undefined,
- progressValue: undefined,
- img: window.URL.createObjectURL(file)
- });
-
- uploads.value.push(ctx);
-
- console.log(keepOriginal);
-
- const data = new FormData();
- data.append('i', $i.token);
- data.append('force', 'true');
- data.append('file', file);
-
- if (folder) data.append('folderId', folder);
- if (name) data.append('name', name);
-
- const xhr = new XMLHttpRequest();
- xhr.open('POST', apiUrl + '/drive/files/create', true);
- xhr.onload = (ev) => {
- if (xhr.status !== 200 || ev.target == null || ev.target.response == null) {
- // TODO: 消ã™ã®ã§ã¯ãªãã¦å†é€ã§ãるよã†ã«ã—ãŸã„
- uploads.value = uploads.value.filter(x => x.id != id);
-
- alert({
- type: 'error',
- text: 'upload failed'
- });
-
- reject();
- return;
- }
-
- const driveFile = JSON.parse(ev.target.response);
-
- resolve(driveFile);
-
- uploads.value = uploads.value.filter(x => x.id != id);
- };
-
- xhr.upload.onprogress = e => {
- if (e.lengthComputable) {
- ctx.progressMax = e.total;
- ctx.progressValue = e.loaded;
- }
- };
-
- xhr.send(data);
- };
- reader.readAsArrayBuffer(file);
- });
-}
-
/*
export function checkExistence(fileData: ArrayBuffer): Promise<any> {
return new Promise((resolve, reject) => {
diff --git a/packages/client/src/pages/about-misskey.vue b/packages/client/src/pages/about-misskey.vue
index ff04ed84f2..691bc4f07b 100644
--- a/packages/client/src/pages/about-misskey.vue
+++ b/packages/client/src/pages/about-misskey.vue
@@ -150,6 +150,7 @@ const patrons = [
'Weeble',
'è‰æš®ã›ã›ã›',
'ThatOneCalculator',
+ 'pixeldesu',
];
let easterEggReady = false;
diff --git a/packages/client/src/pages/admin/abuses.vue b/packages/client/src/pages/admin/abuses.vue
index 92f93797ce..e1d0361c0b 100644
--- a/packages/client/src/pages/admin/abuses.vue
+++ b/packages/client/src/pages/admin/abuses.vue
@@ -24,10 +24,10 @@
</div>
<!-- TODO
<div class="inputs" style="display: flex; padding-top: 1.2em;">
- <MkInput v-model="searchUsername" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.reports.reload()">
+ <MkInput v-model="searchUsername" style="margin: 0; flex: 1;" type="text" spellcheck="false">
<span>{{ $ts.username }}</span>
</MkInput>
- <MkInput v-model="searchHost" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.reports.reload()" :disabled="pagination.params().origin === 'local'">
+ <MkInput v-model="searchHost" style="margin: 0; flex: 1;" type="text" spellcheck="false" :disabled="pagination.params().origin === 'local'">
<span>{{ $ts.host }}</span>
</MkInput>
</div>
@@ -41,8 +41,8 @@
</div>
</template>
-<script lang="ts">
-import { computed, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { computed } from 'vue';
import MkInput from '@/components/form/input.vue';
import MkSelect from '@/components/form/select.vue';
@@ -50,45 +50,35 @@ import MkPagination from '@/components/ui/pagination.vue';
import XAbuseReport from '@/components/abuse-report.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkInput,
- MkSelect,
- MkPagination,
- XAbuseReport,
- },
+let reports = $ref<InstanceType<typeof MkPagination>>();
- emits: ['info'],
+let state = $ref('unresolved');
+let reporterOrigin = $ref('combined');
+let targetUserOrigin = $ref('combined');
+let searchUsername = $ref('');
+let searchHost = $ref('');
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.abuseReports,
- icon: 'fas fa-exclamation-circle',
- bg: 'var(--bg)',
- },
- searchUsername: '',
- searchHost: '',
- state: 'unresolved',
- reporterOrigin: 'combined',
- targetUserOrigin: 'combined',
- pagination: {
- endpoint: 'admin/abuse-user-reports' as const,
- limit: 10,
- params: computed(() => ({
- state: this.state,
- reporterOrigin: this.reporterOrigin,
- targetUserOrigin: this.targetUserOrigin,
- })),
- },
- }
- },
+const pagination = {
+ endpoint: 'admin/abuse-user-reports' as const,
+ limit: 10,
+ params: computed(() => ({
+ state,
+ reporterOrigin,
+ targetUserOrigin,
+ })),
+};
- methods: {
- resolved(reportId) {
- this.$refs.reports.removeItem(item => item.id === reportId);
- },
+function resolved(reportId) {
+ reports.removeItem(item => item.id === reportId);
+}
+
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.abuseReports,
+ icon: 'fas fa-exclamation-circle',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/admin/ads.vue b/packages/client/src/pages/admin/ads.vue
index 8f164caa99..b18e08db96 100644
--- a/packages/client/src/pages/admin/ads.vue
+++ b/packages/client/src/pages/admin/ads.vue
@@ -7,7 +7,7 @@
<template #label>URL</template>
</MkInput>
<MkInput v-model="ad.imageUrl" class="_formBlock">
- <template #label>{{ $ts.imageUrl }}</template>
+ <template #label>{{ i18n.ts.imageUrl }}</template>
</MkInput>
<FormRadios v-model="ad.place" class="_formBlock">
<template #label>Form</template>
@@ -17,34 +17,34 @@
</FormRadios>
<!--
<div style="margin: 32px 0;">
- {{ $ts.priority }}
- <MkRadio v-model="ad.priority" value="high">{{ $ts.high }}</MkRadio>
- <MkRadio v-model="ad.priority" value="middle">{{ $ts.middle }}</MkRadio>
- <MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio>
+ {{ i18n.ts.priority }}
+ <MkRadio v-model="ad.priority" value="high">{{ i18n.ts.high }}</MkRadio>
+ <MkRadio v-model="ad.priority" value="middle">{{ i18n.ts.middle }}</MkRadio>
+ <MkRadio v-model="ad.priority" value="low">{{ i18n.ts.low }}</MkRadio>
</div>
-->
<FormSplit>
<MkInput v-model="ad.ratio" type="number">
- <template #label>{{ $ts.ratio }}</template>
+ <template #label>{{ i18n.ts.ratio }}</template>
</MkInput>
<MkInput v-model="ad.expiresAt" type="date">
- <template #label>{{ $ts.expiration }}</template>
+ <template #label>{{ i18n.ts.expiration }}</template>
</MkInput>
</FormSplit>
<MkTextarea v-model="ad.memo" class="_formBlock">
- <template #label>{{ $ts.memo }}</template>
+ <template #label>{{ i18n.ts.memo }}</template>
</MkTextarea>
<div class="buttons _formBlock">
- <MkButton class="button" inline primary style="margin-right: 12px;" @click="save(ad)"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
- <MkButton class="button" inline danger @click="remove(ad)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
+ <MkButton class="button" inline primary style="margin-right: 12px;" @click="save(ad)"><i class="fas fa-save"></i> {{ i18n.ts.save }}</MkButton>
+ <MkButton class="button" inline danger @click="remove(ad)"><i class="fas fa-trash-alt"></i> {{ i18n.ts.remove }}</MkButton>
</div>
</div>
</div>
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkTextarea from '@/components/form/textarea.vue';
@@ -52,81 +52,65 @@ import FormRadios from '@/components/form/radios.vue';
import FormSplit from '@/components/form/split.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- MkInput,
- MkTextarea,
- FormRadios,
- FormSplit,
- },
+let ads: any[] = $ref([]);
- emits: ['info'],
+os.api('admin/ad/list').then(adsResponse => {
+ ads = adsResponse;
+});
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.ads,
- icon: 'fas fa-audio-description',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- icon: 'fas fa-plus',
- text: this.$ts.add,
- handler: this.add,
- }],
- },
- ads: [],
- }
- },
+function add() {
+ ads.unshift({
+ id: null,
+ memo: '',
+ place: 'square',
+ priority: 'middle',
+ ratio: 1,
+ url: '',
+ imageUrl: null,
+ expiresAt: null,
+ });
+}
- created() {
- os.api('admin/ad/list').then(ads => {
- this.ads = ads;
+function remove(ad) {
+ os.confirm({
+ type: 'warning',
+ text: i18n.t('removeAreYouSure', { x: ad.url }),
+ }).then(({ canceled }) => {
+ if (canceled) return;
+ ads = ads.filter(x => x !== ad);
+ os.apiWithDialog('admin/ad/delete', {
+ id: ad.id
});
- },
-
- methods: {
- add() {
- this.ads.unshift({
- id: null,
- memo: '',
- place: 'square',
- priority: 'middle',
- ratio: 1,
- url: '',
- imageUrl: null,
- expiresAt: null,
- });
- },
+ });
+}
- remove(ad) {
- os.confirm({
- type: 'warning',
- text: this.$t('removeAreYouSure', { x: ad.url }),
- }).then(({ canceled }) => {
- if (canceled) return;
- this.ads = this.ads.filter(x => x != ad);
- os.apiWithDialog('admin/ad/delete', {
- id: ad.id
- });
- });
- },
+function save(ad) {
+ if (ad.id == null) {
+ os.apiWithDialog('admin/ad/create', {
+ ...ad,
+ expiresAt: new Date(ad.expiresAt).getTime()
+ });
+ } else {
+ os.apiWithDialog('admin/ad/update', {
+ ...ad,
+ expiresAt: new Date(ad.expiresAt).getTime()
+ });
+ }
+}
- save(ad) {
- if (ad.id == null) {
- os.apiWithDialog('admin/ad/create', {
- ...ad,
- expiresAt: new Date(ad.expiresAt).getTime()
- });
- } else {
- os.apiWithDialog('admin/ad/update', {
- ...ad,
- expiresAt: new Date(ad.expiresAt).getTime()
- });
- }
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.ads,
+ icon: 'fas fa-audio-description',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-plus',
+ text: i18n.ts.add,
+ handler: add,
+ }],
}
});
</script>
diff --git a/packages/client/src/pages/admin/announcements.vue b/packages/client/src/pages/admin/announcements.vue
index a0d720bb29..97774975de 100644
--- a/packages/client/src/pages/admin/announcements.vue
+++ b/packages/client/src/pages/admin/announcements.vue
@@ -3,112 +3,98 @@
<section v-for="announcement in announcements" class="_card _gap announcements">
<div class="_content announcement">
<MkInput v-model="announcement.title">
- <template #label>{{ $ts.title }}</template>
+ <template #label>{{ i18n.ts.title }}</template>
</MkInput>
<MkTextarea v-model="announcement.text">
- <template #label>{{ $ts.text }}</template>
+ <template #label>{{ i18n.ts.text }}</template>
</MkTextarea>
<MkInput v-model="announcement.imageUrl">
- <template #label>{{ $ts.imageUrl }}</template>
+ <template #label>{{ i18n.ts.imageUrl }}</template>
</MkInput>
- <p v-if="announcement.reads">{{ $t('nUsersRead', { n: announcement.reads }) }}</p>
+ <p v-if="announcement.reads">{{ i18n.t('nUsersRead', { n: announcement.reads }) }}</p>
<div class="buttons">
- <MkButton class="button" inline primary @click="save(announcement)"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
- <MkButton class="button" inline @click="remove(announcement)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
+ <MkButton class="button" inline primary @click="save(announcement)"><i class="fas fa-save"></i> {{ i18n.ts.save }}</MkButton>
+ <MkButton class="button" inline @click="remove(announcement)"><i class="fas fa-trash-alt"></i> {{ i18n.ts.remove }}</MkButton>
</div>
</div>
</section>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkTextarea from '@/components/form/textarea.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- MkInput,
- MkTextarea,
- },
+let announcements: any[] = $ref([]);
- emits: ['info'],
+os.api('admin/announcements/list').then(announcementResponse => {
+ announcements = announcementResponse;
+});
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.announcements,
- icon: 'fas fa-broadcast-tower',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- icon: 'fas fa-plus',
- text: this.$ts.add,
- handler: this.add,
- }],
- },
- announcements: [],
- }
- },
+function add() {
+ announcements.unshift({
+ id: null,
+ title: '',
+ text: '',
+ imageUrl: null
+ });
+}
- created() {
- os.api('admin/announcements/list').then(announcements => {
- this.announcements = announcements;
- });
- },
+function remove(announcement) {
+ os.confirm({
+ type: 'warning',
+ text: i18n.t('removeAreYouSure', { x: announcement.title }),
+ }).then(({ canceled }) => {
+ if (canceled) return;
+ announcements = announcements.filter(x => x !== announcement);
+ os.api('admin/announcements/delete', announcement);
+ });
+}
- methods: {
- add() {
- this.announcements.unshift({
- id: null,
- title: '',
- text: '',
- imageUrl: null
+function save(announcement) {
+ if (announcement.id == null) {
+ os.api('admin/announcements/create', announcement).then(() => {
+ os.alert({
+ type: 'success',
+ text: i18n.ts.saved
});
- },
-
- remove(announcement) {
- os.confirm({
- type: 'warning',
- text: this.$t('removeAreYouSure', { x: announcement.title }),
- }).then(({ canceled }) => {
- if (canceled) return;
- this.announcements = this.announcements.filter(x => x != announcement);
- os.api('admin/announcements/delete', announcement);
+ }).catch(err => {
+ os.alert({
+ type: 'error',
+ text: err
});
- },
+ });
+ } else {
+ os.api('admin/announcements/update', announcement).then(() => {
+ os.alert({
+ type: 'success',
+ text: i18n.ts.saved
+ });
+ }).catch(err => {
+ os.alert({
+ type: 'error',
+ text: err
+ });
+ });
+ }
+}
- save(announcement) {
- if (announcement.id == null) {
- os.api('admin/announcements/create', announcement).then(() => {
- os.alert({
- type: 'success',
- text: this.$ts.saved
- });
- }).catch(e => {
- os.alert({
- type: 'error',
- text: e
- });
- });
- } else {
- os.api('admin/announcements/update', announcement).then(() => {
- os.alert({
- type: 'success',
- text: this.$ts.saved
- });
- }).catch(e => {
- os.alert({
- type: 'error',
- text: e
- });
- });
- }
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.announcements,
+ icon: 'fas fa-broadcast-tower',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-plus',
+ text: i18n.ts.add,
+ handler: add,
+ }],
}
});
</script>
diff --git a/packages/client/src/pages/admin/bot-protection.vue b/packages/client/src/pages/admin/bot-protection.vue
index 5e0cdd96a5..30fee5015a 100644
--- a/packages/client/src/pages/admin/bot-protection.vue
+++ b/packages/client/src/pages/admin/bot-protection.vue
@@ -43,8 +43,8 @@
</div>
</template>
-<script lang="ts">
-import { defineAsyncComponent, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineAsyncComponent } from 'vue';
import FormRadios from '@/components/form/radios.vue';
import FormInput from '@/components/form/input.vue';
import FormButton from '@/components/ui/button.vue';
@@ -54,64 +54,39 @@ import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
-export default defineComponent({
- components: {
- FormRadios,
- FormInput,
- FormButton,
- FormSuspense,
- FormSlot,
- MkCaptcha: defineAsyncComponent(() => import('@/components/captcha.vue')),
- },
+const MkCaptcha = defineAsyncComponent(() => import('@/components/captcha.vue'));
- emits: ['info'],
+let provider = $ref(null);
+let hcaptchaSiteKey: string | null = $ref(null);
+let hcaptchaSecretKey: string | null = $ref(null);
+let recaptchaSiteKey: string | null = $ref(null);
+let recaptchaSecretKey: string | null = $ref(null);
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.botProtection,
- icon: 'fas fa-shield-alt'
- },
- provider: null,
- enableHcaptcha: false,
- hcaptchaSiteKey: null,
- hcaptchaSecretKey: null,
- enableRecaptcha: false,
- recaptchaSiteKey: null,
- recaptchaSecretKey: null,
- }
- },
+const enableHcaptcha = $computed(() => provider === 'hcaptcha');
+const enableRecaptcha = $computed(() => provider === 'recaptcha');
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.enableHcaptcha = meta.enableHcaptcha;
- this.hcaptchaSiteKey = meta.hcaptchaSiteKey;
- this.hcaptchaSecretKey = meta.hcaptchaSecretKey;
- this.enableRecaptcha = meta.enableRecaptcha;
- this.recaptchaSiteKey = meta.recaptchaSiteKey;
- this.recaptchaSecretKey = meta.recaptchaSecretKey;
+async function init() {
+ const meta = await os.api('admin/meta');
+ enableHcaptcha = meta.enableHcaptcha;
+ hcaptchaSiteKey = meta.hcaptchaSiteKey;
+ hcaptchaSecretKey = meta.hcaptchaSecretKey;
+ enableRecaptcha = meta.enableRecaptcha;
+ recaptchaSiteKey = meta.recaptchaSiteKey;
+ recaptchaSecretKey = meta.recaptchaSecretKey;
- this.provider = this.enableHcaptcha ? 'hcaptcha' : this.enableRecaptcha ? 'recaptcha' : null;
+ provider = enableHcaptcha ? 'hcaptcha' : enableRecaptcha ? 'recaptcha' : null;
+}
- this.$watch(() => this.provider, () => {
- this.enableHcaptcha = this.provider === 'hcaptcha';
- this.enableRecaptcha = this.provider === 'recaptcha';
- });
- },
-
- save() {
- os.apiWithDialog('admin/update-meta', {
- enableHcaptcha: this.enableHcaptcha,
- hcaptchaSiteKey: this.hcaptchaSiteKey,
- hcaptchaSecretKey: this.hcaptchaSecretKey,
- enableRecaptcha: this.enableRecaptcha,
- recaptchaSiteKey: this.recaptchaSiteKey,
- recaptchaSecretKey: this.recaptchaSecretKey,
- }).then(() => {
- fetchInstance();
- });
- }
- }
-});
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ enableHcaptcha,
+ hcaptchaSiteKey,
+ hcaptchaSecretKey,
+ enableRecaptcha,
+ recaptchaSiteKey,
+ recaptchaSecretKey,
+ }).then(() => {
+ fetchInstance();
+ });
+}
</script>
diff --git a/packages/client/src/pages/admin/database.vue b/packages/client/src/pages/admin/database.vue
index 3a835eeafa..d3519922b1 100644
--- a/packages/client/src/pages/admin/database.vue
+++ b/packages/client/src/pages/admin/database.vue
@@ -9,36 +9,23 @@
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormSuspense from '@/components/form/suspense.vue';
import MkKeyValue from '@/components/key-value.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import bytes from '@/filters/bytes';
import number from '@/filters/number';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSuspense,
- MkKeyValue,
- },
+const databasePromiseFactory = () => os.api('admin/get-table-stats').then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size));
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.database,
- icon: 'fas fa-database',
- bg: 'var(--bg)',
- },
- databasePromiseFactory: () => os.api('admin/get-table-stats', {}).then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size)),
- }
- },
-
- methods: {
- bytes, number,
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.database,
+ icon: 'fas fa-database',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/admin/email-settings.vue b/packages/client/src/pages/admin/email-settings.vue
index 7df0b6db1c..aa13043193 100644
--- a/packages/client/src/pages/admin/email-settings.vue
+++ b/packages/client/src/pages/admin/email-settings.vue
@@ -3,37 +3,37 @@
<FormSuspense :p="init">
<div class="_formRoot">
<FormSwitch v-model="enableEmail" class="_formBlock">
- <template #label>{{ $ts.enableEmail }}</template>
- <template #caption>{{ $ts.emailConfigInfo }}</template>
+ <template #label>{{ i18n.ts.enableEmail }}</template>
+ <template #caption>{{ i18n.ts.emailConfigInfo }}</template>
</FormSwitch>
<template v-if="enableEmail">
<FormInput v-model="email" type="email" class="_formBlock">
- <template #label>{{ $ts.emailAddress }}</template>
+ <template #label>{{ i18n.ts.emailAddress }}</template>
</FormInput>
<FormSection>
- <template #label>{{ $ts.smtpConfig }}</template>
+ <template #label>{{ i18n.ts.smtpConfig }}</template>
<FormSplit :min-width="280">
<FormInput v-model="smtpHost" class="_formBlock">
- <template #label>{{ $ts.smtpHost }}</template>
+ <template #label>{{ i18n.ts.smtpHost }}</template>
</FormInput>
<FormInput v-model="smtpPort" type="number" class="_formBlock">
- <template #label>{{ $ts.smtpPort }}</template>
+ <template #label>{{ i18n.ts.smtpPort }}</template>
</FormInput>
</FormSplit>
<FormSplit :min-width="280">
<FormInput v-model="smtpUser" class="_formBlock">
- <template #label>{{ $ts.smtpUser }}</template>
+ <template #label>{{ i18n.ts.smtpUser }}</template>
</FormInput>
<FormInput v-model="smtpPass" type="password" class="_formBlock">
- <template #label>{{ $ts.smtpPass }}</template>
+ <template #label>{{ i18n.ts.smtpPass }}</template>
</FormInput>
</FormSplit>
- <FormInfo class="_formBlock">{{ $ts.emptyToDisableSmtpAuth }}</FormInfo>
+ <FormInfo class="_formBlock">{{ i18n.ts.emptyToDisableSmtpAuth }}</FormInfo>
<FormSwitch v-model="smtpSecure" class="_formBlock">
- <template #label>{{ $ts.smtpSecure }}</template>
- <template #caption>{{ $ts.smtpSecureInfo }}</template>
+ <template #label>{{ i18n.ts.smtpSecure }}</template>
+ <template #caption>{{ i18n.ts.smtpSecureInfo }}</template>
</FormSwitch>
</FormSection>
</template>
@@ -42,8 +42,8 @@
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue';
import FormInfo from '@/components/ui/info.vue';
@@ -52,86 +52,71 @@ import FormSplit from '@/components/form/split.vue';
import FormSection from '@/components/form/section.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
-import { fetchInstance } from '@/instance';
+import { fetchInstance, instance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSwitch,
- FormInput,
- FormSplit,
- FormSection,
- FormInfo,
- FormSuspense,
- },
+let enableEmail: boolean = $ref(false);
+let email: any = $ref(null);
+let smtpSecure: boolean = $ref(false);
+let smtpHost: string = $ref('');
+let smtpPort: number = $ref(0);
+let smtpUser: string = $ref('');
+let smtpPass: string = $ref('');
- emits: ['info'],
+async function init() {
+ const meta = await os.api('admin/meta');
+ enableEmail = meta.enableEmail;
+ email = meta.email;
+ smtpSecure = meta.smtpSecure;
+ smtpHost = meta.smtpHost;
+ smtpPort = meta.smtpPort;
+ smtpUser = meta.smtpUser;
+ smtpPass = meta.smtpPass;
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.emailServer,
- icon: 'fas fa-envelope',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- text: this.$ts.testEmail,
- handler: this.testEmail,
- }, {
- asFullButton: true,
- icon: 'fas fa-check',
- text: this.$ts.save,
- handler: this.save,
- }],
- },
- enableEmail: false,
- email: null,
- smtpSecure: false,
- smtpHost: '',
- smtpPort: 0,
- smtpUser: '',
- smtpPass: '',
- }
- },
+async function testEmail() {
+ const { canceled, result: destination } = await os.inputText({
+ title: i18n.ts.destination,
+ type: 'email',
+ placeholder: instance.maintainerEmail
+ });
+ if (canceled) return;
+ os.apiWithDialog('admin/send-email', {
+ to: destination,
+ subject: 'Test email',
+ text: 'Yo'
+ });
+}
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.enableEmail = meta.enableEmail;
- this.email = meta.email;
- this.smtpSecure = meta.smtpSecure;
- this.smtpHost = meta.smtpHost;
- this.smtpPort = meta.smtpPort;
- this.smtpUser = meta.smtpUser;
- this.smtpPass = meta.smtpPass;
- },
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ enableEmail,
+ email,
+ smtpSecure,
+ smtpHost,
+ smtpPort,
+ smtpUser,
+ smtpPass,
+ }).then(() => {
+ fetchInstance();
+ });
+}
- async testEmail() {
- const { canceled, result: destination } = await os.inputText({
- title: this.$ts.destination,
- type: 'email',
- placeholder: this.$instance.maintainerEmail
- });
- if (canceled) return;
- os.apiWithDialog('admin/send-email', {
- to: destination,
- subject: 'Test email',
- text: 'Yo'
- });
- },
-
- save() {
- os.apiWithDialog('admin/update-meta', {
- enableEmail: this.enableEmail,
- email: this.email,
- smtpSecure: this.smtpSecure,
- smtpHost: this.smtpHost,
- smtpPort: this.smtpPort,
- smtpUser: this.smtpUser,
- smtpPass: this.smtpPass,
- }).then(() => {
- fetchInstance();
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.emailServer,
+ icon: 'fas fa-envelope',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ text: i18n.ts.testEmail,
+ handler: testEmail,
+ }, {
+ asFullButton: true,
+ icon: 'fas fa-check',
+ text: i18n.ts.save,
+ handler: save,
+ }],
}
});
</script>
diff --git a/packages/client/src/pages/admin/emoji-edit-dialog.vue b/packages/client/src/pages/admin/emoji-edit-dialog.vue
index 2e3903426e..d482fa49e6 100644
--- a/packages/client/src/pages/admin/emoji-edit-dialog.vue
+++ b/packages/client/src/pages/admin/emoji-edit-dialog.vue
@@ -27,85 +27,71 @@
</XModalWindow>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import XModalWindow from '@/components/ui/modal-window.vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import * as os from '@/os';
import { unique } from '@/scripts/array';
+import { i18n } from '@/i18n';
+import { emojiCategories } from '@/instance';
-export default defineComponent({
- components: {
- XModalWindow,
- MkButton,
- MkInput,
- },
+const props = defineProps<{
+ emoji: any,
+}>();
- props: {
- emoji: {
- required: true,
- }
- },
-
- emits: ['done', 'closed'],
+let dialog = $ref(null);
+let name: string = $ref(props.emoji.name);
+let category: string = $ref(props.emoji.category);
+let aliases: string = $ref(props.emoji.aliases.join(' '));
+let categories: string[] = $ref(emojiCategories);
- data() {
- return {
- name: this.emoji.name,
- category: this.emoji.category,
- aliases: this.emoji.aliases?.join(' '),
- categories: [],
- }
- },
+const emit = defineEmits<{
+ (ev: 'done', v: { deleted?: boolean, updated?: any }): void,
+ (ev: 'closed'): void
+}>();
- created() {
- os.api('meta', { detail: false }).then(({ emojis }) => {
- this.categories = unique(emojis.map((x: any) => x.category || '').filter((x: string) => x !== ''));
- });
- },
+function ok() {
+ update();
+}
- methods: {
- ok() {
- this.update();
- },
+async function update() {
+ await os.apiWithDialog('admin/emoji/update', {
+ id: props.emoji.id,
+ name,
+ category,
+ aliases: aliases.split(' '),
+ });
- async update() {
- await os.apiWithDialog('admin/emoji/update', {
- id: this.emoji.id,
- name: this.name,
- category: this.category,
- aliases: this.aliases.split(' '),
- });
+ emit('done', {
+ updated: {
+ id: props.emoji.id,
+ name,
+ category,
+ aliases: aliases.split(' '),
+ }
+ });
- this.$emit('done', {
- updated: {
- name: this.name,
- category: this.category,
- aliases: this.aliases.split(' '),
- }
- });
- this.$refs.dialog.close();
- },
+ dialog.close();
+}
- async del() {
- const { canceled } = await os.confirm({
- type: 'warning',
- text: this.$t('removeAreYouSure', { x: this.emoji.name }),
- });
- if (canceled) return;
+async function del() {
+ const { canceled } = await os.confirm({
+ type: 'warning',
+ text: i18n.t('removeAreYouSure', { x: name }),
+ });
+ if (canceled) return;
- os.api('admin/emoji/delete', {
- id: this.emoji.id
- }).then(() => {
- this.$emit('done', {
- deleted: true
- });
- this.$refs.dialog.close();
- });
- },
- }
-});
+ os.api('admin/emoji/delete', {
+ id: props.emoji.id
+ }).then(() => {
+ emit('done', {
+ deleted: true
+ });
+ dialog.close();
+ });
+}
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/pages/admin/emojis.vue b/packages/client/src/pages/admin/emojis.vue
index a080ee9c23..8ca5b3d65c 100644
--- a/packages/client/src/pages/admin/emojis.vue
+++ b/packages/client/src/pages/admin/emojis.vue
@@ -63,7 +63,7 @@
</template>
<script lang="ts" setup>
-import { computed, defineComponent, ref, toRef } from 'vue';
+import { computed, defineAsyncComponent, defineComponent, ref, toRef } from 'vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkPagination from '@/components/ui/pagination.vue';
@@ -130,17 +130,17 @@ const add = async (ev: MouseEvent) => {
};
const edit = (emoji) => {
- os.popup(import('./emoji-edit-dialog.vue'), {
+ os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), {
emoji: emoji
}, {
done: result => {
if (result.updated) {
- emojisPaginationComponent.value.replaceItem(item => item.id === emoji.id, {
- ...emoji,
+ emojisPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({
+ ...oldEmoji,
...result.updated
- });
+ }));
} else if (result.deleted) {
- emojisPaginationComponent.value.removeItem(item => item.id === emoji.id);
+ emojisPaginationComponent.value.removeItem((item) => item.id === emoji.id);
}
},
}, 'closed');
@@ -159,7 +159,7 @@ const remoteMenu = (emoji, ev: MouseEvent) => {
}, {
text: i18n.ts.import,
icon: 'fas fa-plus',
- action: () => { im(emoji) }
+ action: () => { im(emoji); }
}], ev.currentTarget ?? ev.target);
};
@@ -175,10 +175,10 @@ const menu = (ev: MouseEvent) => {
type: 'info',
text: i18n.ts.exportRequested,
});
- }).catch((e) => {
+ }).catch((err) => {
os.alert({
type: 'error',
- text: e.message,
+ text: err.message,
});
});
}
@@ -195,10 +195,10 @@ const menu = (ev: MouseEvent) => {
type: 'info',
text: i18n.ts.importRequested,
});
- }).catch((e) => {
+ }).catch((err) => {
os.alert({
type: 'error',
- text: e.message,
+ text: err.message,
});
});
}
diff --git a/packages/client/src/pages/admin/file-dialog.vue b/packages/client/src/pages/admin/file-dialog.vue
index 4c33f62399..0765548aab 100644
--- a/packages/client/src/pages/admin/file-dialog.vue
+++ b/packages/client/src/pages/admin/file-dialog.vue
@@ -34,74 +34,52 @@
</XModalWindow>
</template>
-<script lang="ts">
-import { computed, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import MkButton from '@/components/ui/button.vue';
import MkSwitch from '@/components/form/switch.vue';
import XModalWindow from '@/components/ui/modal-window.vue';
import MkDriveFileThumbnail from '@/components/drive-file-thumbnail.vue';
import bytes from '@/filters/bytes';
import * as os from '@/os';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- MkSwitch,
- XModalWindow,
- MkDriveFileThumbnail,
- },
+let file: any = $ref(null);
+let info: any = $ref(null);
+let isSensitive: boolean = $ref(false);
- props: {
- fileId: {
- required: true,
- }
- },
-
- emits: ['closed'],
-
- data() {
- return {
- file: null,
- info: null,
- isSensitive: false,
- };
- },
+const props = defineProps<{
+ fileId: string,
+}>();
- created() {
- this.fetch();
- },
-
- methods: {
- async fetch() {
- this.file = await os.api('drive/files/show', { fileId: this.fileId });
- this.info = await os.api('admin/drive/show-file', { fileId: this.fileId });
- this.isSensitive = this.file.isSensitive;
- },
+async function fetch() {
+ file = await os.api('drive/files/show', { fileId: props.fileId });
+ info = await os.api('admin/drive/show-file', { fileId: props.fileId });
+ isSensitive = file.isSensitive;
+}
- showUser() {
- os.pageWindow(`/user-info/${this.file.userId}`);
- },
+fetch();
- async del() {
- const { canceled } = await os.confirm({
- type: 'warning',
- text: this.$t('removeAreYouSure', { x: this.file.name }),
- });
- if (canceled) return;
+function showUser() {
+ os.pageWindow(`/user-info/${file.userId}`);
+}
- os.apiWithDialog('drive/files/delete', {
- fileId: this.file.id
- });
- },
+async function del() {
+ const { canceled } = await os.confirm({
+ type: 'warning',
+ text: i18n.t('removeAreYouSure', { x: file.name }),
+ });
+ if (canceled) return;
- async toggleIsSensitive(v) {
- await os.api('drive/files/update', { fileId: this.fileId, isSensitive: v });
- this.isSensitive = v;
- },
+ os.apiWithDialog('drive/files/delete', {
+ fileId: file.id
+ });
+}
- bytes
- }
-});
+async function toggleIsSensitive(v) {
+ await os.api('drive/files/update', { fileId: props.fileId, isSensitive: v });
+ isSensitive = v;
+}
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/pages/admin/files.vue b/packages/client/src/pages/admin/files.vue
index c62f053092..3cda688698 100644
--- a/packages/client/src/pages/admin/files.vue
+++ b/packages/client/src/pages/admin/files.vue
@@ -55,7 +55,7 @@
</template>
<script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, defineAsyncComponent } from 'vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkSelect from '@/components/form/select.vue';
@@ -93,7 +93,7 @@ function clear() {
}
function show(file) {
- os.popup(import('./file-dialog.vue'), {
+ os.popup(defineAsyncComponent(() => import('./file-dialog.vue')), {
fileId: file.id
}, {}, 'closed');
}
diff --git a/packages/client/src/pages/admin/index.vue b/packages/client/src/pages/admin/index.vue
index 6b11650f48..9b7fa5678e 100644
--- a/packages/client/src/pages/admin/index.vue
+++ b/packages/client/src/pages/admin/index.vue
@@ -1,6 +1,6 @@
<template>
<div ref="el" class="hiyeyicy" :class="{ wide: !narrow }">
- <div v-if="!narrow || page == null" class="nav">
+ <div v-if="!narrow || initialPage == null" class="nav">
<MkHeader :info="header"></MkHeader>
<MkSpacer :content-max="700" :margin-min="16">
@@ -12,21 +12,21 @@
<MkInfo v-if="noMaintainerInformation" warn class="info">{{ $ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ $ts.configure }}</MkA></MkInfo>
<MkInfo v-if="noBotProtection" warn class="info">{{ $ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ $ts.configure }}</MkA></MkInfo>
- <MkSuperMenu :def="menuDef" :grid="page == null"></MkSuperMenu>
+ <MkSuperMenu :def="menuDef" :grid="initialPage == null"></MkSuperMenu>
</div>
</MkSpacer>
</div>
- <div class="main">
+ <div v-if="!(narrow && initialPage == null)" class="main">
<MkStickyContainer>
<template #header><MkHeader v-if="childInfo && !childInfo.hideHeader" :info="childInfo"/></template>
- <component :is="component" :ref="el => pageChanged(el)" :key="page" v-bind="pageProps"/>
+ <component :is="component" :ref="el => pageChanged(el)" :key="initialPage" v-bind="pageProps"/>
</MkStickyContainer>
</div>
</div>
</template>
-<script lang="ts">
-import { computed, defineAsyncComponent, defineComponent, isRef, nextTick, onMounted, reactive, ref, watch } from 'vue';
+<script lang="ts" setup>
+import { defineAsyncComponent, nextTick, onMounted, onUnmounted, provide, watch } from 'vue';
import { i18n } from '@/i18n';
import MkSuperMenu from '@/components/ui/super-menu.vue';
import MkInfo from '@/components/ui/info.vue';
@@ -35,292 +35,277 @@ import { instance } from '@/instance';
import * as symbols from '@/symbols';
import * as os from '@/os';
import { lookupUser } from '@/scripts/lookup-user';
+import { MisskeyNavigator } from '@/scripts/navigate';
-export default defineComponent({
- components: {
- MkSuperMenu,
- MkInfo,
- },
+const isEmpty = (x: string | null) => x == null || x === '';
- provide: {
- shouldOmitHeaderTitle: false,
- },
+const nav = new MisskeyNavigator();
- props: {
- initialPage: {
- type: String,
- required: false
- }
- },
+const indexInfo = {
+ title: i18n.ts.controlPanel,
+ icon: 'fas fa-cog',
+ bg: 'var(--bg)',
+ hideHeader: true,
+};
- setup(props, context) {
- const indexInfo = {
- title: i18n.ts.controlPanel,
- icon: 'fas fa-cog',
- bg: 'var(--bg)',
- hideHeader: true,
- };
- const INFO = ref(indexInfo);
- const childInfo = ref(null);
- const page = ref(props.initialPage);
- const narrow = ref(false);
- const view = ref(null);
- const el = ref(null);
- const pageChanged = (page) => {
- if (page == null) return;
- const viewInfo = page[symbols.PAGE_INFO];
- if (isRef(viewInfo)) {
- watch(viewInfo, () => {
- childInfo.value = viewInfo.value;
- }, { immediate: true });
- } else {
- childInfo.value = viewInfo;
- }
- };
- const pageProps = ref({});
+const props = defineProps<{
+ initialPage?: string,
+}>();
- const isEmpty = (x: any) => x == null || x == '';
+provide('shouldOmitHeaderTitle', false);
- const noMaintainerInformation = ref(false);
- const noBotProtection = ref(false);
+let INFO = $ref(indexInfo);
+let childInfo = $ref(null);
+let page = $ref(props.initialPage);
+let narrow = $ref(false);
+let view = $ref(null);
+let el = $ref(null);
+let pageProps = $ref({});
+let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
+let noBotProtection = !instance.enableHcaptcha && !instance.enableRecaptcha;
- os.api('meta', { detail: true }).then(meta => {
- // TODO: 設定ãŒå®Œäº†ã—ã¦ã‚‚残ã£ãŸã¾ã¾ã«ãªã‚‹ã®ã§ã€ã‚¹ãƒˆãƒªãƒ¼ãƒŸãƒ³ã‚°ã§meta更新イベントをå—ã‘å–ã£ã¦ã‚ˆã—ãªã«æ›´æ–°ã™ã‚‹
- noMaintainerInformation.value = isEmpty(meta.maintainerName) || isEmpty(meta.maintainerEmail);
- noBotProtection.value = !meta.enableHcaptcha && !meta.enableRecaptcha;
- });
+const NARROW_THRESHOLD = 600;
+const ro = new ResizeObserver((entries, observer) => {
+ if (entries.length === 0) return;
+ narrow = entries[0].borderBoxSize[0].inlineSize < NARROW_THRESHOLD;
+});
- const menuDef = computed(() => [{
- title: i18n.ts.quickAction,
- items: [{
- type: 'button',
- icon: 'fas fa-search',
- text: i18n.ts.lookup,
- action: lookup,
- }, ...(instance.disableRegistration ? [{
- type: 'button',
- icon: 'fas fa-user',
- text: i18n.ts.invite,
- action: invite,
- }] : [])],
- }, {
- title: i18n.ts.administration,
- items: [{
- icon: 'fas fa-tachometer-alt',
- text: i18n.ts.dashboard,
- to: '/admin/overview',
- active: page.value === 'overview',
- }, {
- icon: 'fas fa-users',
- text: i18n.ts.users,
- to: '/admin/users',
- active: page.value === 'users',
- }, {
- icon: 'fas fa-laugh',
- text: i18n.ts.customEmojis,
- to: '/admin/emojis',
- active: page.value === 'emojis',
- }, {
- icon: 'fas fa-globe',
- text: i18n.ts.federation,
- to: '/admin/federation',
- active: page.value === 'federation',
- }, {
- icon: 'fas fa-clipboard-list',
- text: i18n.ts.jobQueue,
- to: '/admin/queue',
- active: page.value === 'queue',
- }, {
- icon: 'fas fa-cloud',
- text: i18n.ts.files,
- to: '/admin/files',
- active: page.value === 'files',
- }, {
- icon: 'fas fa-broadcast-tower',
- text: i18n.ts.announcements,
- to: '/admin/announcements',
- active: page.value === 'announcements',
- }, {
- icon: 'fas fa-audio-description',
- text: i18n.ts.ads,
- to: '/admin/ads',
- active: page.value === 'ads',
- }, {
- icon: 'fas fa-exclamation-circle',
- text: i18n.ts.abuseReports,
- to: '/admin/abuses',
- active: page.value === 'abuses',
- }],
- }, {
- title: i18n.ts.settings,
- items: [{
- icon: 'fas fa-cog',
- text: i18n.ts.general,
- to: '/admin/settings',
- active: page.value === 'settings',
- }, {
- icon: 'fas fa-envelope',
- text: i18n.ts.emailServer,
- to: '/admin/email-settings',
- active: page.value === 'email-settings',
- }, {
- icon: 'fas fa-cloud',
- text: i18n.ts.objectStorage,
- to: '/admin/object-storage',
- active: page.value === 'object-storage',
- }, {
- icon: 'fas fa-lock',
- text: i18n.ts.security,
- to: '/admin/security',
- active: page.value === 'security',
- }, {
- icon: 'fas fa-globe',
- text: i18n.ts.relays,
- to: '/admin/relays',
- active: page.value === 'relays',
- }, {
- icon: 'fas fa-share-alt',
- text: i18n.ts.integration,
- to: '/admin/integrations',
- active: page.value === 'integrations',
- }, {
- icon: 'fas fa-ban',
- text: i18n.ts.instanceBlocking,
- to: '/admin/instance-block',
- active: page.value === 'instance-block',
- }, {
- icon: 'fas fa-ghost',
- text: i18n.ts.proxyAccount,
- to: '/admin/proxy-account',
- active: page.value === 'proxy-account',
- }, {
- icon: 'fas fa-cogs',
- text: i18n.ts.other,
- to: '/admin/other-settings',
- active: page.value === 'other-settings',
- }],
- }, {
- title: i18n.ts.info,
- items: [{
- icon: 'fas fa-database',
- text: i18n.ts.database,
- to: '/admin/database',
- active: page.value === 'database',
- }],
- }]);
- const component = computed(() => {
- if (page.value == null) return null;
- switch (page.value) {
- case 'overview': return defineAsyncComponent(() => import('./overview.vue'));
- case 'users': return defineAsyncComponent(() => import('./users.vue'));
- case 'emojis': return defineAsyncComponent(() => import('./emojis.vue'));
- case 'federation': return defineAsyncComponent(() => import('../federation.vue'));
- case 'queue': return defineAsyncComponent(() => import('./queue.vue'));
- case 'files': return defineAsyncComponent(() => import('./files.vue'));
- case 'announcements': return defineAsyncComponent(() => import('./announcements.vue'));
- case 'ads': return defineAsyncComponent(() => import('./ads.vue'));
- case 'database': return defineAsyncComponent(() => import('./database.vue'));
- case 'abuses': return defineAsyncComponent(() => import('./abuses.vue'));
- case 'settings': return defineAsyncComponent(() => import('./settings.vue'));
- case 'email-settings': return defineAsyncComponent(() => import('./email-settings.vue'));
- case 'object-storage': return defineAsyncComponent(() => import('./object-storage.vue'));
- case 'security': return defineAsyncComponent(() => import('./security.vue'));
- case 'relays': return defineAsyncComponent(() => import('./relays.vue'));
- case 'integrations': return defineAsyncComponent(() => import('./integrations.vue'));
- case 'instance-block': return defineAsyncComponent(() => import('./instance-block.vue'));
- case 'proxy-account': return defineAsyncComponent(() => import('./proxy-account.vue'));
- case 'other-settings': return defineAsyncComponent(() => import('./other-settings.vue'));
- }
- });
+const menuDef = $computed(() => [{
+ title: i18n.ts.quickAction,
+ items: [{
+ type: 'button',
+ icon: 'fas fa-search',
+ text: i18n.ts.lookup,
+ action: lookup,
+ }, ...(instance.disableRegistration ? [{
+ type: 'button',
+ icon: 'fas fa-user',
+ text: i18n.ts.invite,
+ action: invite,
+ }] : [])],
+}, {
+ title: i18n.ts.administration,
+ items: [{
+ icon: 'fas fa-tachometer-alt',
+ text: i18n.ts.dashboard,
+ to: '/admin/overview',
+ active: props.initialPage === 'overview',
+ }, {
+ icon: 'fas fa-users',
+ text: i18n.ts.users,
+ to: '/admin/users',
+ active: props.initialPage === 'users',
+ }, {
+ icon: 'fas fa-laugh',
+ text: i18n.ts.customEmojis,
+ to: '/admin/emojis',
+ active: props.initialPage === 'emojis',
+ }, {
+ icon: 'fas fa-globe',
+ text: i18n.ts.federation,
+ to: '/admin/federation',
+ active: props.initialPage === 'federation',
+ }, {
+ icon: 'fas fa-clipboard-list',
+ text: i18n.ts.jobQueue,
+ to: '/admin/queue',
+ active: props.initialPage === 'queue',
+ }, {
+ icon: 'fas fa-cloud',
+ text: i18n.ts.files,
+ to: '/admin/files',
+ active: props.initialPage === 'files',
+ }, {
+ icon: 'fas fa-broadcast-tower',
+ text: i18n.ts.announcements,
+ to: '/admin/announcements',
+ active: props.initialPage === 'announcements',
+ }, {
+ icon: 'fas fa-audio-description',
+ text: i18n.ts.ads,
+ to: '/admin/ads',
+ active: props.initialPage === 'ads',
+ }, {
+ icon: 'fas fa-exclamation-circle',
+ text: i18n.ts.abuseReports,
+ to: '/admin/abuses',
+ active: props.initialPage === 'abuses',
+ }],
+}, {
+ title: i18n.ts.settings,
+ items: [{
+ icon: 'fas fa-cog',
+ text: i18n.ts.general,
+ to: '/admin/settings',
+ active: props.initialPage === 'settings',
+ }, {
+ icon: 'fas fa-envelope',
+ text: i18n.ts.emailServer,
+ to: '/admin/email-settings',
+ active: props.initialPage === 'email-settings',
+ }, {
+ icon: 'fas fa-cloud',
+ text: i18n.ts.objectStorage,
+ to: '/admin/object-storage',
+ active: props.initialPage === 'object-storage',
+ }, {
+ icon: 'fas fa-lock',
+ text: i18n.ts.security,
+ to: '/admin/security',
+ active: props.initialPage === 'security',
+ }, {
+ icon: 'fas fa-globe',
+ text: i18n.ts.relays,
+ to: '/admin/relays',
+ active: props.initialPage === 'relays',
+ }, {
+ icon: 'fas fa-share-alt',
+ text: i18n.ts.integration,
+ to: '/admin/integrations',
+ active: props.initialPage === 'integrations',
+ }, {
+ icon: 'fas fa-ban',
+ text: i18n.ts.instanceBlocking,
+ to: '/admin/instance-block',
+ active: props.initialPage === 'instance-block',
+ }, {
+ icon: 'fas fa-ghost',
+ text: i18n.ts.proxyAccount,
+ to: '/admin/proxy-account',
+ active: props.initialPage === 'proxy-account',
+ }, {
+ icon: 'fas fa-cogs',
+ text: i18n.ts.other,
+ to: '/admin/other-settings',
+ active: props.initialPage === 'other-settings',
+ }],
+}, {
+ title: i18n.ts.info,
+ items: [{
+ icon: 'fas fa-database',
+ text: i18n.ts.database,
+ to: '/admin/database',
+ active: props.initialPage === 'database',
+ }],
+}]);
- watch(component, () => {
- pageProps.value = {};
+const component = $computed(() => {
+ if (props.initialPage == null) return null;
+ switch (props.initialPage) {
+ case 'overview': return defineAsyncComponent(() => import('./overview.vue'));
+ case 'users': return defineAsyncComponent(() => import('./users.vue'));
+ case 'emojis': return defineAsyncComponent(() => import('./emojis.vue'));
+ case 'federation': return defineAsyncComponent(() => import('../federation.vue'));
+ case 'queue': return defineAsyncComponent(() => import('./queue.vue'));
+ case 'files': return defineAsyncComponent(() => import('./files.vue'));
+ case 'announcements': return defineAsyncComponent(() => import('./announcements.vue'));
+ case 'ads': return defineAsyncComponent(() => import('./ads.vue'));
+ case 'database': return defineAsyncComponent(() => import('./database.vue'));
+ case 'abuses': return defineAsyncComponent(() => import('./abuses.vue'));
+ case 'settings': return defineAsyncComponent(() => import('./settings.vue'));
+ case 'email-settings': return defineAsyncComponent(() => import('./email-settings.vue'));
+ case 'object-storage': return defineAsyncComponent(() => import('./object-storage.vue'));
+ case 'security': return defineAsyncComponent(() => import('./security.vue'));
+ case 'relays': return defineAsyncComponent(() => import('./relays.vue'));
+ case 'integrations': return defineAsyncComponent(() => import('./integrations.vue'));
+ case 'instance-block': return defineAsyncComponent(() => import('./instance-block.vue'));
+ case 'proxy-account': return defineAsyncComponent(() => import('./proxy-account.vue'));
+ case 'other-settings': return defineAsyncComponent(() => import('./other-settings.vue'));
+ }
+});
- nextTick(() => {
- scroll(el.value, { top: 0 });
- });
- }, { immediate: true });
+watch(component, () => {
+ pageProps = {};
- watch(() => props.initialPage, () => {
- if (props.initialPage == null && !narrow.value) {
- page.value = 'overview';
- } else {
- page.value = props.initialPage;
- if (props.initialPage == null) {
- INFO.value = indexInfo;
- }
- }
- });
+ nextTick(() => {
+ scroll(el, { top: 0 });
+ });
+}, { immediate: true });
- onMounted(() => {
- narrow.value = el.value.offsetWidth < 800;
- if (!narrow.value) {
- page.value = 'overview';
- }
- });
+watch(() => props.initialPage, () => {
+ if (props.initialPage == null && !narrow) {
+ nav.push('/admin/overview');
+ } else {
+ if (props.initialPage == null) {
+ INFO = indexInfo;
+ }
+ }
+});
- const invite = () => {
- os.api('admin/invite').then(x => {
- os.alert({
- type: 'info',
- text: x.code
- });
- }).catch(e => {
- os.alert({
- type: 'error',
- text: e
- });
- });
- };
+watch(narrow, () => {
+ if (props.initialPage == null && !narrow) {
+ nav.push('/admin/overview');
+ }
+});
- const lookup = (ev) => {
- os.popupMenu([{
- text: i18n.ts.user,
- icon: 'fas fa-user',
- action: () => {
- lookupUser();
- }
- }, {
- text: i18n.ts.note,
- icon: 'fas fa-pencil-alt',
- action: () => {
- alert('TODO');
- }
- }, {
- text: i18n.ts.file,
- icon: 'fas fa-cloud',
- action: () => {
- alert('TODO');
- }
- }, {
- text: i18n.ts.instance,
- icon: 'fas fa-globe',
- action: () => {
- alert('TODO');
- }
- }], ev.currentTarget ?? ev.target);
- };
+onMounted(() => {
+ ro.observe(el);
+
+ narrow = el.offsetWidth < NARROW_THRESHOLD;
+ if (props.initialPage == null && !narrow) {
+ nav.push('/admin/overview');
+ }
+});
+
+onUnmounted(() => {
+ ro.disconnect();
+});
+
+const pageChanged = (page) => {
+ if (page == null) {
+ childInfo = null;
+ } else {
+ childInfo = page[symbols.PAGE_INFO];
+ }
+};
- return {
- [symbols.PAGE_INFO]: INFO,
- menuDef,
- header: {
- title: i18n.ts.controlPanel,
- },
- noMaintainerInformation,
- noBotProtection,
- page,
- narrow,
- view,
- el,
- pageChanged,
- childInfo,
- pageProps,
- component,
- invite,
- lookup,
- };
- },
+const invite = () => {
+ os.api('admin/invite').then(x => {
+ os.alert({
+ type: 'info',
+ text: x.code
+ });
+ }).catch(err => {
+ os.alert({
+ type: 'error',
+ text: err,
+ });
+ });
+};
+
+const lookup = (ev) => {
+ os.popupMenu([{
+ text: i18n.ts.user,
+ icon: 'fas fa-user',
+ action: () => {
+ lookupUser();
+ }
+ }, {
+ text: i18n.ts.note,
+ icon: 'fas fa-pencil-alt',
+ action: () => {
+ alert('TODO');
+ }
+ }, {
+ text: i18n.ts.file,
+ icon: 'fas fa-cloud',
+ action: () => {
+ alert('TODO');
+ }
+ }, {
+ text: i18n.ts.instance,
+ icon: 'fas fa-globe',
+ action: () => {
+ alert('TODO');
+ }
+ }], ev.currentTarget ?? ev.target);
+};
+
+defineExpose({
+ [symbols.PAGE_INFO]: INFO,
+ header: {
+ title: i18n.ts.controlPanel,
+ }
});
</script>
diff --git a/packages/client/src/pages/admin/instance-block.vue b/packages/client/src/pages/admin/instance-block.vue
index 4cb8dc604e..3347846a80 100644
--- a/packages/client/src/pages/admin/instance-block.vue
+++ b/packages/client/src/pages/admin/instance-block.vue
@@ -2,57 +2,45 @@
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
<FormSuspense :p="init">
<FormTextarea v-model="blockedHosts" class="_formBlock">
- <span>{{ $ts.blockedInstances }}</span>
- <template #caption>{{ $ts.blockedInstancesDescription }}</template>
+ <span>{{ i18n.ts.blockedInstances }}</span>
+ <template #caption>{{ i18n.ts.blockedInstancesDescription }}</template>
</FormTextarea>
- <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+ <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ i18n.ts.save }}</FormButton>
</FormSuspense>
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormButton from '@/components/ui/button.vue';
import FormTextarea from '@/components/form/textarea.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormButton,
- FormTextarea,
- FormSuspense,
- },
+let blockedHosts: string = $ref('');
- emits: ['info'],
+async function init() {
+ const meta = await os.api('admin/meta');
+ blockedHosts = meta.blockedHosts.join('\n');
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.instanceBlocking,
- icon: 'fas fa-ban',
- bg: 'var(--bg)',
- },
- blockedHosts: '',
- }
- },
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ blockedHosts: blockedHosts.split('\n') || [],
+ }).then(() => {
+ fetchInstance();
+ });
+}
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.blockedHosts = meta.blockedHosts.join('\n');
- },
-
- save() {
- os.apiWithDialog('admin/update-meta', {
- blockedHosts: this.blockedHosts.split('\n') || [],
- }).then(() => {
- fetchInstance();
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.instanceBlocking,
+ icon: 'fas fa-ban',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/admin/integrations.discord.vue b/packages/client/src/pages/admin/integrations.discord.vue
index 6b50f1b0a9..9fdc51a6ca 100644
--- a/packages/client/src/pages/admin/integrations.discord.vue
+++ b/packages/client/src/pages/admin/integrations.discord.vue
@@ -24,57 +24,36 @@
</FormSuspense>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue';
import FormButton from '@/components/ui/button.vue';
import FormInfo from '@/components/ui/info.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
-import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
-export default defineComponent({
- components: {
- FormSwitch,
- FormInput,
- FormInfo,
- FormButton,
- FormSuspense,
- },
+let uri: string = $ref('');
+let enableDiscordIntegration: boolean = $ref(false);
+let discordClientId: string | null = $ref(null);
+let discordClientSecret: string | null = $ref(null);
- emits: ['info'],
+async function init() {
+ const meta = await os.api('admin/meta');
+ uri = meta.uri;
+ enableDiscordIntegration = meta.enableDiscordIntegration;
+ discordClientId = meta.discordClientId;
+ discordClientSecret = meta.discordClientSecret;
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: 'Discord',
- icon: 'fab fa-discord'
- },
- enableDiscordIntegration: false,
- discordClientId: null,
- discordClientSecret: null,
- }
- },
-
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.uri = meta.uri;
- this.enableDiscordIntegration = meta.enableDiscordIntegration;
- this.discordClientId = meta.discordClientId;
- this.discordClientSecret = meta.discordClientSecret;
- },
- save() {
- os.apiWithDialog('admin/update-meta', {
- enableDiscordIntegration: this.enableDiscordIntegration,
- discordClientId: this.discordClientId,
- discordClientSecret: this.discordClientSecret,
- }).then(() => {
- fetchInstance();
- });
- }
- }
-});
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ enableDiscordIntegration,
+ discordClientId,
+ discordClientSecret,
+ }).then(() => {
+ fetchInstance();
+ });
+}
</script>
diff --git a/packages/client/src/pages/admin/integrations.github.vue b/packages/client/src/pages/admin/integrations.github.vue
index 67f299e1bc..b10ccb8394 100644
--- a/packages/client/src/pages/admin/integrations.github.vue
+++ b/packages/client/src/pages/admin/integrations.github.vue
@@ -24,57 +24,36 @@
</FormSuspense>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue';
import FormButton from '@/components/ui/button.vue';
import FormInfo from '@/components/ui/info.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
-import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
-export default defineComponent({
- components: {
- FormSwitch,
- FormInput,
- FormInfo,
- FormButton,
- FormSuspense,
- },
+let uri: string = $ref('');
+let enableGithubIntegration: boolean = $ref(false);
+let githubClientId: string | null = $ref(null);
+let githubClientSecret: string | null = $ref(null);
- emits: ['info'],
+async function init() {
+ const meta = await os.api('admin/meta');
+ uri = meta.uri;
+ enableGithubIntegration = meta.enableGithubIntegration;
+ githubClientId = meta.githubClientId;
+ githubClientSecret = meta.githubClientSecret;
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: 'GitHub',
- icon: 'fab fa-github'
- },
- enableGithubIntegration: false,
- githubClientId: null,
- githubClientSecret: null,
- }
- },
-
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.uri = meta.uri;
- this.enableGithubIntegration = meta.enableGithubIntegration;
- this.githubClientId = meta.githubClientId;
- this.githubClientSecret = meta.githubClientSecret;
- },
- save() {
- os.apiWithDialog('admin/update-meta', {
- enableGithubIntegration: this.enableGithubIntegration,
- githubClientId: this.githubClientId,
- githubClientSecret: this.githubClientSecret,
- }).then(() => {
- fetchInstance();
- });
- }
- }
-});
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ enableGithubIntegration,
+ githubClientId,
+ githubClientSecret,
+ }).then(() => {
+ fetchInstance();
+ });
+}
</script>
diff --git a/packages/client/src/pages/admin/integrations.twitter.vue b/packages/client/src/pages/admin/integrations.twitter.vue
index a389c71506..11b5fd86b2 100644
--- a/packages/client/src/pages/admin/integrations.twitter.vue
+++ b/packages/client/src/pages/admin/integrations.twitter.vue
@@ -24,7 +24,7 @@
</FormSuspense>
</template>
-<script lang="ts">
+<script lang="ts" setup>
import { defineComponent } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue';
@@ -32,49 +32,28 @@ import FormButton from '@/components/ui/button.vue';
import FormInfo from '@/components/ui/info.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
-import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
-export default defineComponent({
- components: {
- FormSwitch,
- FormInput,
- FormInfo,
- FormButton,
- FormSuspense,
- },
+let uri: string = $ref('');
+let enableTwitterIntegration: boolean = $ref(false);
+let twitterConsumerKey: string | null = $ref(null);
+let twitterConsumerSecret: string | null = $ref(null);
- emits: ['info'],
+async function init() {
+ const meta = await os.api('admin/meta');
+ uri = meta.uri;
+ enableTwitterIntegration = meta.enableTwitterIntegration;
+ twitterConsumerKey = meta.twitterConsumerKey;
+ twitterConsumerSecret = meta.twitterConsumerSecret;
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: 'Twitter',
- icon: 'fab fa-twitter'
- },
- enableTwitterIntegration: false,
- twitterConsumerKey: null,
- twitterConsumerSecret: null,
- }
- },
-
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.uri = meta.uri;
- this.enableTwitterIntegration = meta.enableTwitterIntegration;
- this.twitterConsumerKey = meta.twitterConsumerKey;
- this.twitterConsumerSecret = meta.twitterConsumerSecret;
- },
- save() {
- os.apiWithDialog('admin/update-meta', {
- enableTwitterIntegration: this.enableTwitterIntegration,
- twitterConsumerKey: this.twitterConsumerKey,
- twitterConsumerSecret: this.twitterConsumerSecret,
- }).then(() => {
- fetchInstance();
- });
- }
- }
-});
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ enableTwitterIntegration,
+ twitterConsumerKey,
+ twitterConsumerSecret,
+ }).then(() => {
+ fetchInstance();
+ });
+}
</script>
diff --git a/packages/client/src/pages/admin/integrations.vue b/packages/client/src/pages/admin/integrations.vue
index 4db8a9e0a9..d6061d0e51 100644
--- a/packages/client/src/pages/admin/integrations.vue
+++ b/packages/client/src/pages/admin/integrations.vue
@@ -4,69 +4,52 @@
<FormFolder class="_formBlock">
<template #icon><i class="fab fa-twitter"></i></template>
<template #label>Twitter</template>
- <template #suffix>{{ enableTwitterIntegration ? $ts.enabled : $ts.disabled }}</template>
+ <template #suffix>{{ enableTwitterIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template>
<XTwitter/>
</FormFolder>
- <FormFolder to="/admin/integrations/github" class="_formBlock">
+ <FormFolder class="_formBlock">
<template #icon><i class="fab fa-github"></i></template>
<template #label>GitHub</template>
- <template #suffix>{{ enableGithubIntegration ? $ts.enabled : $ts.disabled }}</template>
+ <template #suffix>{{ enableGithubIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template>
<XGithub/>
</FormFolder>
- <FormFolder to="/admin/integrations/discord" class="_formBlock">
+ <FormFolder class="_formBlock">
<template #icon><i class="fab fa-discord"></i></template>
<template #label>Discord</template>
- <template #suffix>{{ enableDiscordIntegration ? $ts.enabled : $ts.disabled }}</template>
+ <template #suffix>{{ enableDiscordIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template>
<XDiscord/>
</FormFolder>
</FormSuspense>
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormFolder from '@/components/form/folder.vue';
-import FormSecion from '@/components/form/section.vue';
import FormSuspense from '@/components/form/suspense.vue';
import XTwitter from './integrations.twitter.vue';
import XGithub from './integrations.github.vue';
import XDiscord from './integrations.discord.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
-import { fetchInstance } from '@/instance';
-
-export default defineComponent({
- components: {
- FormFolder,
- FormSecion,
- FormSuspense,
- XTwitter,
- XGithub,
- XDiscord,
- },
+import { i18n } from '@/i18n';
- emits: ['info'],
+let enableTwitterIntegration: boolean = $ref(false);
+let enableGithubIntegration: boolean = $ref(false);
+let enableDiscordIntegration: boolean = $ref(false);
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.integration,
- icon: 'fas fa-share-alt',
- bg: 'var(--bg)',
- },
- enableTwitterIntegration: false,
- enableGithubIntegration: false,
- enableDiscordIntegration: false,
- }
- },
+async function init() {
+ const meta = await os.api('admin/meta');
+ enableTwitterIntegration = meta.enableTwitterIntegration;
+ enableGithubIntegration = meta.enableGithubIntegration;
+ enableDiscordIntegration = meta.enableDiscordIntegration;
+}
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.enableTwitterIntegration = meta.enableTwitterIntegration;
- this.enableGithubIntegration = meta.enableGithubIntegration;
- this.enableDiscordIntegration = meta.enableDiscordIntegration;
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.integration,
+ icon: 'fas fa-share-alt',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/admin/metrics.vue b/packages/client/src/pages/admin/metrics.vue
index 1de297fd93..7e5f5bb094 100644
--- a/packages/client/src/pages/admin/metrics.vue
+++ b/packages/client/src/pages/admin/metrics.vue
@@ -132,7 +132,7 @@ export default defineComponent({
overviewHeight: '1fr',
queueHeight: '1fr',
paused: false,
- }
+ };
},
computed: {
diff --git a/packages/client/src/pages/admin/object-storage.vue b/packages/client/src/pages/admin/object-storage.vue
index a1ee0761c8..d109db9c38 100644
--- a/packages/client/src/pages/admin/object-storage.vue
+++ b/packages/client/src/pages/admin/object-storage.vue
@@ -2,32 +2,32 @@
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
<FormSuspense :p="init">
<div class="_formRoot">
- <FormSwitch v-model="useObjectStorage" class="_formBlock">{{ $ts.useObjectStorage }}</FormSwitch>
+ <FormSwitch v-model="useObjectStorage" class="_formBlock">{{ i18n.ts.useObjectStorage }}</FormSwitch>
<template v-if="useObjectStorage">
<FormInput v-model="objectStorageBaseUrl" class="_formBlock">
- <template #label>{{ $ts.objectStorageBaseUrl }}</template>
- <template #caption>{{ $ts.objectStorageBaseUrlDesc }}</template>
+ <template #label>{{ i18n.ts.objectStorageBaseUrl }}</template>
+ <template #caption>{{ i18n.ts.objectStorageBaseUrlDesc }}</template>
</FormInput>
<FormInput v-model="objectStorageBucket" class="_formBlock">
- <template #label>{{ $ts.objectStorageBucket }}</template>
- <template #caption>{{ $ts.objectStorageBucketDesc }}</template>
+ <template #label>{{ i18n.ts.objectStorageBucket }}</template>
+ <template #caption>{{ i18n.ts.objectStorageBucketDesc }}</template>
</FormInput>
<FormInput v-model="objectStoragePrefix" class="_formBlock">
- <template #label>{{ $ts.objectStoragePrefix }}</template>
- <template #caption>{{ $ts.objectStoragePrefixDesc }}</template>
+ <template #label>{{ i18n.ts.objectStoragePrefix }}</template>
+ <template #caption>{{ i18n.ts.objectStoragePrefixDesc }}</template>
</FormInput>
<FormInput v-model="objectStorageEndpoint" class="_formBlock">
- <template #label>{{ $ts.objectStorageEndpoint }}</template>
- <template #caption>{{ $ts.objectStorageEndpointDesc }}</template>
+ <template #label>{{ i18n.ts.objectStorageEndpoint }}</template>
+ <template #caption>{{ i18n.ts.objectStorageEndpointDesc }}</template>
</FormInput>
<FormInput v-model="objectStorageRegion" class="_formBlock">
- <template #label>{{ $ts.objectStorageRegion }}</template>
- <template #caption>{{ $ts.objectStorageRegionDesc }}</template>
+ <template #label>{{ i18n.ts.objectStorageRegion }}</template>
+ <template #caption>{{ i18n.ts.objectStorageRegionDesc }}</template>
</FormInput>
<FormSplit :min-width="280">
@@ -43,17 +43,17 @@
</FormSplit>
<FormSwitch v-model="objectStorageUseSSL" class="_formBlock">
- <template #label>{{ $ts.objectStorageUseSSL }}</template>
- <template #caption>{{ $ts.objectStorageUseSSLDesc }}</template>
+ <template #label>{{ i18n.ts.objectStorageUseSSL }}</template>
+ <template #caption>{{ i18n.ts.objectStorageUseSSLDesc }}</template>
</FormSwitch>
<FormSwitch v-model="objectStorageUseProxy" class="_formBlock">
- <template #label>{{ $ts.objectStorageUseProxy }}</template>
- <template #caption>{{ $ts.objectStorageUseProxyDesc }}</template>
+ <template #label>{{ i18n.ts.objectStorageUseProxy }}</template>
+ <template #caption>{{ i18n.ts.objectStorageUseProxyDesc }}</template>
</FormSwitch>
<FormSwitch v-model="objectStorageSetPublicRead" class="_formBlock">
- <template #label>{{ $ts.objectStorageSetPublicRead }}</template>
+ <template #label>{{ i18n.ts.objectStorageSetPublicRead }}</template>
</FormSwitch>
<FormSwitch v-model="objectStorageS3ForcePathStyle" class="_formBlock">
@@ -65,8 +65,8 @@
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue';
import FormGroup from '@/components/form/group.vue';
@@ -76,84 +76,70 @@ import FormSection from '@/components/form/section.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSwitch,
- FormInput,
- FormGroup,
- FormSuspense,
- FormSplit,
- FormSection,
- },
+let useObjectStorage: boolean = $ref(false);
+let objectStorageBaseUrl: string | null = $ref(null);
+let objectStorageBucket: string | null = $ref(null);
+let objectStoragePrefix: string | null = $ref(null);
+let objectStorageEndpoint: string | null = $ref(null);
+let objectStorageRegion: string | null = $ref(null);
+let objectStoragePort: number | null = $ref(null);
+let objectStorageAccessKey: string | null = $ref(null);
+let objectStorageSecretKey: string | null = $ref(null);
+let objectStorageUseSSL: boolean = $ref(false);
+let objectStorageUseProxy: boolean = $ref(false);
+let objectStorageSetPublicRead: boolean = $ref(false);
+let objectStorageS3ForcePathStyle: boolean = $ref(true);
- emits: ['info'],
+async function init() {
+ const meta = await os.api('admin/meta');
+ useObjectStorage = meta.useObjectStorage;
+ objectStorageBaseUrl = meta.objectStorageBaseUrl;
+ objectStorageBucket = meta.objectStorageBucket;
+ objectStoragePrefix = meta.objectStoragePrefix;
+ objectStorageEndpoint = meta.objectStorageEndpoint;
+ objectStorageRegion = meta.objectStorageRegion;
+ objectStoragePort = meta.objectStoragePort;
+ objectStorageAccessKey = meta.objectStorageAccessKey;
+ objectStorageSecretKey = meta.objectStorageSecretKey;
+ objectStorageUseSSL = meta.objectStorageUseSSL;
+ objectStorageUseProxy = meta.objectStorageUseProxy;
+ objectStorageSetPublicRead = meta.objectStorageSetPublicRead;
+ objectStorageS3ForcePathStyle = meta.objectStorageS3ForcePathStyle;
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.objectStorage,
- icon: 'fas fa-cloud',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- icon: 'fas fa-check',
- text: this.$ts.save,
- handler: this.save,
- }],
- },
- useObjectStorage: false,
- objectStorageBaseUrl: null,
- objectStorageBucket: null,
- objectStoragePrefix: null,
- objectStorageEndpoint: null,
- objectStorageRegion: null,
- objectStoragePort: null,
- objectStorageAccessKey: null,
- objectStorageSecretKey: null,
- objectStorageUseSSL: false,
- objectStorageUseProxy: false,
- objectStorageSetPublicRead: false,
- objectStorageS3ForcePathStyle: true,
- }
- },
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ useObjectStorage,
+ objectStorageBaseUrl,
+ objectStorageBucket,
+ objectStoragePrefix,
+ objectStorageEndpoint,
+ objectStorageRegion,
+ objectStoragePort,
+ objectStorageAccessKey,
+ objectStorageSecretKey,
+ objectStorageUseSSL,
+ objectStorageUseProxy,
+ objectStorageSetPublicRead,
+ objectStorageS3ForcePathStyle,
+ }).then(() => {
+ fetchInstance();
+ });
+}
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.useObjectStorage = meta.useObjectStorage;
- this.objectStorageBaseUrl = meta.objectStorageBaseUrl;
- this.objectStorageBucket = meta.objectStorageBucket;
- this.objectStoragePrefix = meta.objectStoragePrefix;
- this.objectStorageEndpoint = meta.objectStorageEndpoint;
- this.objectStorageRegion = meta.objectStorageRegion;
- this.objectStoragePort = meta.objectStoragePort;
- this.objectStorageAccessKey = meta.objectStorageAccessKey;
- this.objectStorageSecretKey = meta.objectStorageSecretKey;
- this.objectStorageUseSSL = meta.objectStorageUseSSL;
- this.objectStorageUseProxy = meta.objectStorageUseProxy;
- this.objectStorageSetPublicRead = meta.objectStorageSetPublicRead;
- this.objectStorageS3ForcePathStyle = meta.objectStorageS3ForcePathStyle;
- },
- save() {
- os.apiWithDialog('admin/update-meta', {
- useObjectStorage: this.useObjectStorage,
- objectStorageBaseUrl: this.objectStorageBaseUrl ? this.objectStorageBaseUrl : null,
- objectStorageBucket: this.objectStorageBucket ? this.objectStorageBucket : null,
- objectStoragePrefix: this.objectStoragePrefix ? this.objectStoragePrefix : null,
- objectStorageEndpoint: this.objectStorageEndpoint ? this.objectStorageEndpoint : null,
- objectStorageRegion: this.objectStorageRegion ? this.objectStorageRegion : null,
- objectStoragePort: this.objectStoragePort ? this.objectStoragePort : null,
- objectStorageAccessKey: this.objectStorageAccessKey ? this.objectStorageAccessKey : null,
- objectStorageSecretKey: this.objectStorageSecretKey ? this.objectStorageSecretKey : null,
- objectStorageUseSSL: this.objectStorageUseSSL,
- objectStorageUseProxy: this.objectStorageUseProxy,
- objectStorageSetPublicRead: this.objectStorageSetPublicRead,
- objectStorageS3ForcePathStyle: this.objectStorageS3ForcePathStyle,
- }).then(() => {
- fetchInstance();
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.objectStorage,
+ icon: 'fas fa-cloud',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-check',
+ text: i18n.ts.save,
+ handler: save,
+ }],
}
});
</script>
diff --git a/packages/client/src/pages/admin/other-settings.vue b/packages/client/src/pages/admin/other-settings.vue
index 99ea6a5f32..552b05f347 100644
--- a/packages/client/src/pages/admin/other-settings.vue
+++ b/packages/client/src/pages/admin/other-settings.vue
@@ -6,52 +6,35 @@
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
-import FormSwitch from '@/components/form/switch.vue';
-import FormInput from '@/components/form/input.vue';
-import FormSection from '@/components/form/section.vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSwitch,
- FormInput,
- FormSection,
- FormSuspense,
- },
+async function init() {
+ await os.api('admin/meta');
+}
- emits: ['info'],
+function save() {
+ os.apiWithDialog('admin/update-meta').then(() => {
+ fetchInstance();
+ });
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.other,
- icon: 'fas fa-cogs',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- icon: 'fas fa-check',
- text: this.$ts.save,
- handler: this.save,
- }],
- },
- }
- },
-
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- },
- save() {
- os.apiWithDialog('admin/update-meta', {
- }).then(() => {
- fetchInstance();
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.other,
+ icon: 'fas fa-cogs',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-check',
+ text: i18n.ts.save,
+ handler: save,
+ }],
}
});
</script>
diff --git a/packages/client/src/pages/admin/overview.vue b/packages/client/src/pages/admin/overview.vue
index b8ae8ad9e1..cc69424c3b 100644
--- a/packages/client/src/pages/admin/overview.vue
+++ b/packages/client/src/pages/admin/overview.vue
@@ -5,20 +5,20 @@
<div class="label">Users</div>
<div class="value _monospace">
{{ number(stats.originalUsersCount) }}
- <MkNumberDiff v-if="usersComparedToThePrevDay != null" v-tooltip="$ts.dayOverDayChanges" class="diff" :value="usersComparedToThePrevDay"><template #before>(</template><template #after>)</template></MkNumberDiff>
+ <MkNumberDiff v-if="usersComparedToThePrevDay != null" v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="usersComparedToThePrevDay"><template #before>(</template><template #after>)</template></MkNumberDiff>
</div>
</div>
<div class="number _panel">
<div class="label">Notes</div>
<div class="value _monospace">
{{ number(stats.originalNotesCount) }}
- <MkNumberDiff v-if="notesComparedToThePrevDay != null" v-tooltip="$ts.dayOverDayChanges" class="diff" :value="notesComparedToThePrevDay"><template #before>(</template><template #after>)</template></MkNumberDiff>
+ <MkNumberDiff v-if="notesComparedToThePrevDay != null" v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="notesComparedToThePrevDay"><template #before>(</template><template #after>)</template></MkNumberDiff>
</div>
</div>
</div>
<MkContainer :foldable="true" class="charts">
- <template #header><i class="fas fa-chart-bar"></i>{{ $ts.charts }}</template>
+ <template #header><i class="fas fa-chart-bar"></i>{{ i18n.ts.charts }}</template>
<div style="padding: 12px;">
<MkInstanceStats :chart-limit="500" :detailed="true"/>
</div>
@@ -38,7 +38,7 @@
<!--<XMetrics/>-->
<MkFolder style="margin: var(--margin)">
- <template #header><i class="fas fa-info-circle"></i> {{ $ts.info }}</template>
+ <template #header><i class="fas fa-info-circle"></i> {{ i18n.ts.info }}</template>
<div class="cfcdecdf">
<div class="number _panel">
<div class="label">Misskey</div>
@@ -65,103 +65,61 @@
</div>
</template>
-<script lang="ts">
-import { computed, defineComponent, markRaw, version as vueVersion } from 'vue';
+<script lang="ts" setup>
+import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue';
import MkInstanceStats from '@/components/instance-stats.vue';
-import MkButton from '@/components/ui/button.vue';
-import MkSelect from '@/components/form/select.vue';
import MkNumberDiff from '@/components/number-diff.vue';
import MkContainer from '@/components/ui/container.vue';
import MkFolder from '@/components/ui/folder.vue';
import MkQueueChart from '@/components/queue-chart.vue';
import { version, url } from '@/config';
-import bytes from '@/filters/bytes';
import number from '@/filters/number';
import XMetrics from './metrics.vue';
import * as os from '@/os';
import { stream } from '@/stream';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkNumberDiff,
- MkInstanceStats,
- MkContainer,
- MkFolder,
- MkQueueChart,
- XMetrics,
- },
+let stats: any = $ref(null);
+let serverInfo: any = $ref(null);
+let usersComparedToThePrevDay: any = $ref(null);
+let notesComparedToThePrevDay: any = $ref(null);
+const queueStatsConnection = markRaw(stream.useChannel('queueStats'));
- emits: ['info'],
+onMounted(async () => {
+ os.api('stats', {}).then(statsResponse => {
+ stats = statsResponse;
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.dashboard,
- icon: 'fas fa-tachometer-alt',
- bg: 'var(--bg)',
- },
- version,
- vueVersion,
- url,
- stats: null,
- meta: null,
- serverInfo: null,
- usersComparedToThePrevDay: null,
- notesComparedToThePrevDay: null,
- fetchJobs: () => os.api('admin/queue/deliver-delayed', {}),
- fetchModLogs: () => os.api('admin/show-moderation-logs', {}),
- queueStatsConnection: markRaw(stream.useChannel('queueStats')),
- }
- },
-
- async mounted() {
- os.api('meta', { detail: true }).then(meta => {
- this.meta = meta;
+ os.api('charts/users', { limit: 2, span: 'day' }).then(chart => {
+ usersComparedToThePrevDay = stats.originalUsersCount - chart.local.total[1];
});
-
- os.api('stats', {}).then(stats => {
- this.stats = stats;
-
- os.api('charts/users', { limit: 2, span: 'day' }).then(chart => {
- this.usersComparedToThePrevDay = this.stats.originalUsersCount - chart.local.total[1];
- });
- os.api('charts/notes', { limit: 2, span: 'day' }).then(chart => {
- this.notesComparedToThePrevDay = this.stats.originalNotesCount - chart.local.total[1];
- });
+ os.api('charts/notes', { limit: 2, span: 'day' }).then(chart => {
+ notesComparedToThePrevDay = stats.originalNotesCount - chart.local.total[1];
});
+ });
- os.api('admin/server-info', {}).then(serverInfo => {
- this.serverInfo = serverInfo;
- });
+ os.api('admin/server-info').then(serverInfoResponse => {
+ serverInfo = serverInfoResponse;
+ });
- this.$nextTick(() => {
- this.queueStatsConnection.send('requestLog', {
- id: Math.random().toString().substr(2, 8),
- length: 200
- });
+ nextTick(() => {
+ queueStatsConnection.send('requestLog', {
+ id: Math.random().toString().substr(2, 8),
+ length: 200
});
- },
-
- beforeUnmount() {
- this.queueStatsConnection.dispose();
- },
-
- methods: {
- async showInstanceInfo(q) {
- let instance = q;
- if (typeof q === 'string') {
- instance = await os.api('federation/show-instance', {
- host: q
- });
- }
- // TODO
- },
+ });
+});
- bytes,
+onBeforeUnmount(() => {
+ queueStatsConnection.dispose();
+});
- number,
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.dashboard,
+ icon: 'fas fa-tachometer-alt',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/admin/proxy-account.vue b/packages/client/src/pages/admin/proxy-account.vue
index 00f14a176f..727e20e7e5 100644
--- a/packages/client/src/pages/admin/proxy-account.vue
+++ b/packages/client/src/pages/admin/proxy-account.vue
@@ -1,19 +1,19 @@
<template>
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
<FormSuspense :p="init">
- <MkInfo class="_formBlock">{{ $ts.proxyAccountDescription }}</MkInfo>
+ <MkInfo class="_formBlock">{{ i18n.ts.proxyAccountDescription }}</MkInfo>
<MkKeyValue class="_formBlock">
- <template #key>{{ $ts.proxyAccount }}</template>
- <template #value>{{ proxyAccount ? `@${proxyAccount.username}` : $ts.none }}</template>
+ <template #key>{{ i18n.ts.proxyAccount }}</template>
+ <template #value>{{ proxyAccount ? `@${proxyAccount.username}` : i18n.ts.none }}</template>
</MkKeyValue>
- <FormButton primary class="_formBlock" @click="chooseProxyAccount">{{ $ts.selectAccount }}</FormButton>
+ <FormButton primary class="_formBlock" @click="chooseProxyAccount">{{ i18n.ts.selectAccount }}</FormButton>
</FormSuspense>
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import MkKeyValue from '@/components/key-value.vue';
import FormButton from '@/components/ui/button.vue';
import MkInfo from '@/components/ui/info.vue';
@@ -21,53 +21,40 @@ import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkKeyValue,
- FormButton,
- MkInfo,
- FormSuspense,
- },
+let proxyAccount: any = $ref(null);
+let proxyAccountId: any = $ref(null);
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.proxyAccount,
- icon: 'fas fa-ghost',
- bg: 'var(--bg)',
- },
- proxyAccount: null,
- proxyAccountId: null,
- }
- },
+async function init() {
+ const meta = await os.api('admin/meta');
+ proxyAccountId = meta.proxyAccountId;
+ if (proxyAccountId) {
+ proxyAccount = await os.api('users/show', { userId: proxyAccountId });
+ }
+}
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.proxyAccountId = meta.proxyAccountId;
- if (this.proxyAccountId) {
- this.proxyAccount = await os.api('users/show', { userId: this.proxyAccountId });
- }
- },
+function chooseProxyAccount() {
+ os.selectUser().then(user => {
+ proxyAccount = user;
+ proxyAccountId = user.id;
+ save();
+ });
+}
- chooseProxyAccount() {
- os.selectUser().then(user => {
- this.proxyAccount = user;
- this.proxyAccountId = user.id;
- this.save();
- });
- },
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ proxyAccountId: proxyAccountId,
+ }).then(() => {
+ fetchInstance();
+ });
+}
- save() {
- os.apiWithDialog('admin/update-meta', {
- proxyAccountId: this.proxyAccountId,
- }).then(() => {
- fetchInstance();
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.proxyAccount,
+ icon: 'fas fa-ghost',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/admin/queue.chart.vue b/packages/client/src/pages/admin/queue.chart.vue
index 136fb63bb6..be63830bdd 100644
--- a/packages/client/src/pages/admin/queue.chart.vue
+++ b/packages/client/src/pages/admin/queue.chart.vue
@@ -26,62 +26,40 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent, markRaw, onMounted, onUnmounted, ref } from 'vue';
+<script lang="ts" setup>
+import { onMounted, onUnmounted, ref } from 'vue';
import number from '@/filters/number';
import MkQueueChart from '@/components/queue-chart.vue';
import * as os from '@/os';
-export default defineComponent({
- components: {
- MkQueueChart
- },
+const activeSincePrevTick = ref(0);
+const active = ref(0);
+const waiting = ref(0);
+const delayed = ref(0);
+const jobs = ref([]);
- props: {
- domain: {
- type: String,
- required: true,
- },
- connection: {
- required: true,
- },
- },
+const props = defineProps<{
+ domain: string,
+ connection: any,
+}>();
- setup(props) {
- const activeSincePrevTick = ref(0);
- const active = ref(0);
- const waiting = ref(0);
- const delayed = ref(0);
- const jobs = ref([]);
+onMounted(() => {
+ os.api(props.domain === 'inbox' ? 'admin/queue/inbox-delayed' : props.domain === 'deliver' ? 'admin/queue/deliver-delayed' : null, {}).then(result => {
+ jobs.value = result;
+ });
- onMounted(() => {
- os.api(props.domain === 'inbox' ? 'admin/queue/inbox-delayed' : props.domain === 'deliver' ? 'admin/queue/deliver-delayed' : null, {}).then(result => {
- jobs.value = result;
- });
+ const onStats = (stats) => {
+ activeSincePrevTick.value = stats[props.domain].activeSincePrevTick;
+ active.value = stats[props.domain].active;
+ waiting.value = stats[props.domain].waiting;
+ delayed.value = stats[props.domain].delayed;
+ };
- const onStats = (stats) => {
- activeSincePrevTick.value = stats[props.domain].activeSincePrevTick;
- active.value = stats[props.domain].active;
- waiting.value = stats[props.domain].waiting;
- delayed.value = stats[props.domain].delayed;
- };
+ props.connection.on('stats', onStats);
- props.connection.on('stats', onStats);
-
- onUnmounted(() => {
- props.connection.off('stats', onStats);
- });
- });
-
- return {
- jobs,
- activeSincePrevTick,
- active,
- waiting,
- delayed,
- number,
- };
- },
+ onUnmounted(() => {
+ props.connection.off('stats', onStats);
+ });
});
</script>
diff --git a/packages/client/src/pages/admin/queue.vue b/packages/client/src/pages/admin/queue.vue
index 35fd618c82..656b18199f 100644
--- a/packages/client/src/pages/admin/queue.vue
+++ b/packages/client/src/pages/admin/queue.vue
@@ -6,71 +6,60 @@
<XQueue :connection="connection" domain="deliver">
<template #title>Out</template>
</XQueue>
- <MkButton danger @click="clear()"><i class="fas fa-trash-alt"></i> {{ $ts.clearQueue }}</MkButton>
+ <MkButton danger @click="clear()"><i class="fas fa-trash-alt"></i> {{ i18n.ts.clearQueue }}</MkButton>
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent, markRaw } from 'vue';
+<script lang="ts" setup>
+import { markRaw, onMounted, onBeforeUnmount, nextTick } from 'vue';
import MkButton from '@/components/ui/button.vue';
import XQueue from './queue.chart.vue';
import * as os from '@/os';
import { stream } from '@/stream';
import * as symbols from '@/symbols';
import * as config from '@/config';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- XQueue,
- },
+const connection = markRaw(stream.useChannel('queueStats'));
- emits: ['info'],
+function clear() {
+ os.confirm({
+ type: 'warning',
+ title: i18n.ts.clearQueueConfirmTitle,
+ text: i18n.ts.clearQueueConfirmText,
+ }).then(({ canceled }) => {
+ if (canceled) return;
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.jobQueue,
- icon: 'fas fa-clipboard-list',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- icon: 'fas fa-up-right-from-square',
- text: this.$ts.dashboard,
- handler: () => {
- window.open(config.url + '/queue', '_blank');
- },
- }],
- },
- connection: markRaw(stream.useChannel('queueStats')),
- }
- },
+ os.apiWithDialog('admin/queue/clear');
+ });
+}
- mounted() {
- this.$nextTick(() => {
- this.connection.send('requestLog', {
- id: Math.random().toString().substr(2, 8),
- length: 200
- });
+onMounted(() => {
+ nextTick(() => {
+ connection.send('requestLog', {
+ id: Math.random().toString().substr(2, 8),
+ length: 200
});
- },
-
- beforeUnmount() {
- this.connection.dispose();
- },
+ });
+});
- methods: {
- clear() {
- os.confirm({
- type: 'warning',
- title: this.$ts.clearQueueConfirmTitle,
- text: this.$ts.clearQueueConfirmText,
- }).then(({ canceled }) => {
- if (canceled) return;
+onBeforeUnmount(() => {
+ connection.dispose();
+});
- os.apiWithDialog('admin/queue/clear', {});
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.jobQueue,
+ icon: 'fas fa-clipboard-list',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-up-right-from-square',
+ text: i18n.ts.dashboard,
+ handler: () => {
+ window.open(config.url + '/queue', '_blank');
+ },
+ }],
}
});
</script>
diff --git a/packages/client/src/pages/admin/relays.vue b/packages/client/src/pages/admin/relays.vue
index bb840db0a2..1a36bb4753 100644
--- a/packages/client/src/pages/admin/relays.vue
+++ b/packages/client/src/pages/admin/relays.vue
@@ -8,84 +8,71 @@
<i v-else class="fas fa-clock icon requesting"></i>
<span>{{ $t(`_relayStatus.${relay.status}`) }}</span>
</div>
- <MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
+ <MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="fas fa-trash-alt"></i> {{ i18n.ts.remove }}</MkButton>
</div>
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import MkButton from '@/components/ui/button.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- },
+let relays: any[] = $ref([]);
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.relays,
- icon: 'fas fa-globe',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- icon: 'fas fa-plus',
- text: this.$ts.addRelay,
- handler: this.addRelay,
- }],
- },
- relays: [],
- inbox: '',
- }
- },
+async function addRelay() {
+ const { canceled, result: inbox } = await os.inputText({
+ title: i18n.ts.addRelay,
+ type: 'url',
+ placeholder: i18n.ts.inboxUrl
+ });
+ if (canceled) return;
+ os.api('admin/relays/add', {
+ inbox
+ }).then((relay: any) => {
+ refresh();
+ }).catch((err: any) => {
+ os.alert({
+ type: 'error',
+ text: err.message || err
+ });
+ });
+}
- created() {
- this.refresh();
- },
+function remove(inbox: string) {
+ os.api('admin/relays/remove', {
+ inbox
+ }).then(() => {
+ refresh();
+ }).catch((err: any) => {
+ os.alert({
+ type: 'error',
+ text: err.message || err
+ });
+ });
+}
- methods: {
- async addRelay() {
- const { canceled, result: inbox } = await os.inputText({
- title: this.$ts.addRelay,
- type: 'url',
- placeholder: this.$ts.inboxUrl
- });
- if (canceled) return;
- os.api('admin/relays/add', {
- inbox
- }).then((relay: any) => {
- this.refresh();
- }).catch((e: any) => {
- os.alert({
- type: 'error',
- text: e.message || e
- });
- });
- },
+function refresh() {
+ os.api('admin/relays/list').then((relayList: any) => {
+ relays = relayList;
+ });
+}
- remove(inbox: string) {
- os.api('admin/relays/remove', {
- inbox
- }).then(() => {
- this.refresh();
- }).catch((e: any) => {
- os.alert({
- type: 'error',
- text: e.message || e
- });
- });
- },
+refresh();
- refresh() {
- os.api('admin/relays/list').then((relays: any) => {
- this.relays = relays;
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.relays,
+ icon: 'fas fa-globe',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-plus',
+ text: i18n.ts.addRelay,
+ handler: addRelay,
+ }],
}
});
</script>
diff --git a/packages/client/src/pages/admin/security.vue b/packages/client/src/pages/admin/security.vue
index d1c979b3e0..6b8f70cca5 100644
--- a/packages/client/src/pages/admin/security.vue
+++ b/packages/client/src/pages/admin/security.vue
@@ -4,10 +4,10 @@
<div class="_formRoot">
<FormFolder class="_formBlock">
<template #icon><i class="fas fa-shield-alt"></i></template>
- <template #label>{{ $ts.botProtection }}</template>
+ <template #label>{{ i18n.ts.botProtection }}</template>
<template v-if="enableHcaptcha" #suffix>hCaptcha</template>
<template v-else-if="enableRecaptcha" #suffix>reCAPTCHA</template>
- <template v-else #suffix>{{ $ts.none }} ({{ $ts.notRecommended }})</template>
+ <template v-else #suffix>{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</template>
<XBotProtection/>
</FormFolder>
@@ -21,7 +21,7 @@
<template #label>Summaly Proxy URL</template>
</FormInput>
- <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+ <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ i18n.ts.save }}</FormButton>
</div>
</FormFolder>
</div>
@@ -29,8 +29,8 @@
</MkSpacer>
</template>
-<script lang="ts">
-import { defineAsyncComponent, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormFolder from '@/components/form/folder.vue';
import FormSwitch from '@/components/form/switch.vue';
import FormInfo from '@/components/ui/info.vue';
@@ -42,49 +42,32 @@ import XBotProtection from './bot-protection.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormFolder,
- FormSwitch,
- FormInfo,
- FormSection,
- FormSuspense,
- FormButton,
- FormInput,
- XBotProtection,
- },
+let summalyProxy: string = $ref('');
+let enableHcaptcha: boolean = $ref(false);
+let enableRecaptcha: boolean = $ref(false);
- emits: ['info'],
+async function init() {
+ const meta = await os.api('admin/meta');
+ summalyProxy = meta.summalyProxy;
+ enableHcaptcha = meta.enableHcaptcha;
+ enableRecaptcha = meta.enableRecaptcha;
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.security,
- icon: 'fas fa-lock',
- bg: 'var(--bg)',
- },
- summalyProxy: '',
- enableHcaptcha: false,
- enableRecaptcha: false,
- }
- },
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ summalyProxy,
+ }).then(() => {
+ fetchInstance();
+ });
+}
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.summalyProxy = meta.summalyProxy;
- this.enableHcaptcha = meta.enableHcaptcha;
- this.enableRecaptcha = meta.enableRecaptcha;
- },
-
- save() {
- os.apiWithDialog('admin/update-meta', {
- summalyProxy: this.summalyProxy,
- }).then(() => {
- fetchInstance();
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.security,
+ icon: 'fas fa-lock',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/admin/settings.vue b/packages/client/src/pages/admin/settings.vue
index f2970d0459..6dc30fe50b 100644
--- a/packages/client/src/pages/admin/settings.vue
+++ b/packages/client/src/pages/admin/settings.vue
@@ -3,104 +3,104 @@
<FormSuspense :p="init">
<div class="_formRoot">
<FormInput v-model="name" class="_formBlock">
- <template #label>{{ $ts.instanceName }}</template>
+ <template #label>{{ i18n.ts.instanceName }}</template>
</FormInput>
<FormTextarea v-model="description" class="_formBlock">
- <template #label>{{ $ts.instanceDescription }}</template>
+ <template #label>{{ i18n.ts.instanceDescription }}</template>
</FormTextarea>
<FormInput v-model="tosUrl" class="_formBlock">
<template #prefix><i class="fas fa-link"></i></template>
- <template #label>{{ $ts.tosUrl }}</template>
+ <template #label>{{ i18n.ts.tosUrl }}</template>
</FormInput>
<FormSplit :min-width="300">
<FormInput v-model="maintainerName" class="_formBlock">
- <template #label>{{ $ts.maintainerName }}</template>
+ <template #label>{{ i18n.ts.maintainerName }}</template>
</FormInput>
<FormInput v-model="maintainerEmail" type="email" class="_formBlock">
<template #prefix><i class="fas fa-envelope"></i></template>
- <template #label>{{ $ts.maintainerEmail }}</template>
+ <template #label>{{ i18n.ts.maintainerEmail }}</template>
</FormInput>
</FormSplit>
<FormTextarea v-model="pinnedUsers" class="_formBlock">
- <template #label>{{ $ts.pinnedUsers }}</template>
- <template #caption>{{ $ts.pinnedUsersDescription }}</template>
+ <template #label>{{ i18n.ts.pinnedUsers }}</template>
+ <template #caption>{{ i18n.ts.pinnedUsersDescription }}</template>
</FormTextarea>
<FormSection>
<FormSwitch v-model="enableRegistration" class="_formBlock">
- <template #label>{{ $ts.enableRegistration }}</template>
+ <template #label>{{ i18n.ts.enableRegistration }}</template>
</FormSwitch>
<FormSwitch v-model="emailRequiredForSignup" class="_formBlock">
- <template #label>{{ $ts.emailRequiredForSignup }}</template>
+ <template #label>{{ i18n.ts.emailRequiredForSignup }}</template>
</FormSwitch>
</FormSection>
<FormSection>
- <FormSwitch v-model="enableLocalTimeline" class="_formBlock">{{ $ts.enableLocalTimeline }}</FormSwitch>
- <FormSwitch v-model="enableGlobalTimeline" class="_formBlock">{{ $ts.enableGlobalTimeline }}</FormSwitch>
- <FormInfo class="_formBlock">{{ $ts.disablingTimelinesInfo }}</FormInfo>
+ <FormSwitch v-model="enableLocalTimeline" class="_formBlock">{{ i18n.ts.enableLocalTimeline }}</FormSwitch>
+ <FormSwitch v-model="enableGlobalTimeline" class="_formBlock">{{ i18n.ts.enableGlobalTimeline }}</FormSwitch>
+ <FormInfo class="_formBlock">{{ i18n.ts.disablingTimelinesInfo }}</FormInfo>
</FormSection>
<FormSection>
- <template #label>{{ $ts.theme }}</template>
+ <template #label>{{ i18n.ts.theme }}</template>
<FormInput v-model="iconUrl" class="_formBlock">
<template #prefix><i class="fas fa-link"></i></template>
- <template #label>{{ $ts.iconUrl }}</template>
+ <template #label>{{ i18n.ts.iconUrl }}</template>
</FormInput>
<FormInput v-model="bannerUrl" class="_formBlock">
<template #prefix><i class="fas fa-link"></i></template>
- <template #label>{{ $ts.bannerUrl }}</template>
+ <template #label>{{ i18n.ts.bannerUrl }}</template>
</FormInput>
<FormInput v-model="backgroundImageUrl" class="_formBlock">
<template #prefix><i class="fas fa-link"></i></template>
- <template #label>{{ $ts.backgroundImageUrl }}</template>
+ <template #label>{{ i18n.ts.backgroundImageUrl }}</template>
</FormInput>
<FormInput v-model="themeColor" class="_formBlock">
<template #prefix><i class="fas fa-palette"></i></template>
- <template #label>{{ $ts.themeColor }}</template>
+ <template #label>{{ i18n.ts.themeColor }}</template>
<template #caption>#RRGGBB</template>
</FormInput>
<FormTextarea v-model="defaultLightTheme" class="_formBlock">
- <template #label>{{ $ts.instanceDefaultLightTheme }}</template>
- <template #caption>{{ $ts.instanceDefaultThemeDescription }}</template>
+ <template #label>{{ i18n.ts.instanceDefaultLightTheme }}</template>
+ <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
</FormTextarea>
<FormTextarea v-model="defaultDarkTheme" class="_formBlock">
- <template #label>{{ $ts.instanceDefaultDarkTheme }}</template>
- <template #caption>{{ $ts.instanceDefaultThemeDescription }}</template>
+ <template #label>{{ i18n.ts.instanceDefaultDarkTheme }}</template>
+ <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
</FormTextarea>
</FormSection>
<FormSection>
- <template #label>{{ $ts.files }}</template>
+ <template #label>{{ i18n.ts.files }}</template>
<FormSwitch v-model="cacheRemoteFiles" class="_formBlock">
- <template #label>{{ $ts.cacheRemoteFiles }}</template>
- <template #caption>{{ $ts.cacheRemoteFilesDescription }}</template>
+ <template #label>{{ i18n.ts.cacheRemoteFiles }}</template>
+ <template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}</template>
</FormSwitch>
<FormSplit :min-width="280">
<FormInput v-model="localDriveCapacityMb" type="number" class="_formBlock">
- <template #label>{{ $ts.driveCapacityPerLocalAccount }}</template>
+ <template #label>{{ i18n.ts.driveCapacityPerLocalAccount }}</template>
<template #suffix>MB</template>
- <template #caption>{{ $ts.inMb }}</template>
+ <template #caption>{{ i18n.ts.inMb }}</template>
</FormInput>
<FormInput v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles" class="_formBlock">
- <template #label>{{ $ts.driveCapacityPerRemoteAccount }}</template>
+ <template #label>{{ i18n.ts.driveCapacityPerRemoteAccount }}</template>
<template #suffix>MB</template>
- <template #caption>{{ $ts.inMb }}</template>
+ <template #caption>{{ i18n.ts.inMb }}</template>
</FormInput>
</FormSplit>
</FormSection>
@@ -109,8 +109,8 @@
<template #label>ServiceWorker</template>
<FormSwitch v-model="enableServiceWorker" class="_formBlock">
- <template #label>{{ $ts.enableServiceworker }}</template>
- <template #caption>{{ $ts.serviceworkerInfo }}</template>
+ <template #label>{{ i18n.ts.enableServiceworker }}</template>
+ <template #caption>{{ i18n.ts.serviceworkerInfo }}</template>
</FormSwitch>
<template v-if="enableServiceWorker">
@@ -142,8 +142,8 @@
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue';
import FormTextarea from '@/components/form/textarea.vue';
@@ -154,119 +154,103 @@ import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSwitch,
- FormInput,
- FormSuspense,
- FormTextarea,
- FormInfo,
- FormSection,
- FormSplit,
- },
+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);
+let bannerUrl: string | null = $ref(null);
+let backgroundImageUrl: string | null = $ref(null);
+let themeColor: any = $ref(null);
+let defaultLightTheme: any = $ref(null);
+let defaultDarkTheme: any = $ref(null);
+let enableLocalTimeline: boolean = $ref(false);
+let enableGlobalTimeline: boolean = $ref(false);
+let pinnedUsers: string = $ref('');
+let cacheRemoteFiles: boolean = $ref(false);
+let localDriveCapacityMb: any = $ref(0);
+let remoteDriveCapacityMb: any = $ref(0);
+let enableRegistration: boolean = $ref(false);
+let emailRequiredForSignup: boolean = $ref(false);
+let enableServiceWorker: boolean = $ref(false);
+let swPublicKey: any = $ref(null);
+let swPrivateKey: any = $ref(null);
+let deeplAuthKey: string = $ref('');
+let deeplIsPro: boolean = $ref(false);
- emits: ['info'],
+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;
+ themeColor = meta.themeColor;
+ defaultLightTheme = meta.defaultLightTheme;
+ defaultDarkTheme = meta.defaultDarkTheme;
+ maintainerName = meta.maintainerName;
+ maintainerEmail = meta.maintainerEmail;
+ enableLocalTimeline = !meta.disableLocalTimeline;
+ enableGlobalTimeline = !meta.disableGlobalTimeline;
+ pinnedUsers = meta.pinnedUsers.join('\n');
+ cacheRemoteFiles = meta.cacheRemoteFiles;
+ localDriveCapacityMb = meta.driveCapacityPerLocalUserMb;
+ remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb;
+ enableRegistration = !meta.disableRegistration;
+ emailRequiredForSignup = meta.emailRequiredForSignup;
+ enableServiceWorker = meta.enableServiceWorker;
+ swPublicKey = meta.swPublickey;
+ swPrivateKey = meta.swPrivateKey;
+ deeplAuthKey = meta.deeplAuthKey;
+ deeplIsPro = meta.deeplIsPro;
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.general,
- icon: 'fas fa-cog',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- icon: 'fas fa-check',
- text: this.$ts.save,
- handler: this.save,
- }],
- },
- name: null,
- description: null,
- tosUrl: null as string | null,
- maintainerName: null,
- maintainerEmail: null,
- iconUrl: null,
- bannerUrl: null,
- backgroundImageUrl: null,
- themeColor: null,
- defaultLightTheme: null,
- defaultDarkTheme: null,
- enableLocalTimeline: false,
- enableGlobalTimeline: false,
- pinnedUsers: '',
- cacheRemoteFiles: false,
- localDriveCapacityMb: 0,
- remoteDriveCapacityMb: 0,
- enableRegistration: false,
- emailRequiredForSignup: false,
- enableServiceWorker: false,
- swPublicKey: null,
- swPrivateKey: null,
- deeplAuthKey: '',
- deeplIsPro: false,
- }
- },
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ name,
+ description,
+ tosUrl,
+ iconUrl,
+ bannerUrl,
+ backgroundImageUrl,
+ themeColor: themeColor === '' ? null : themeColor,
+ defaultLightTheme: defaultLightTheme === '' ? null : defaultLightTheme,
+ defaultDarkTheme: defaultDarkTheme === '' ? null : defaultDarkTheme,
+ maintainerName,
+ maintainerEmail,
+ disableLocalTimeline: !enableLocalTimeline,
+ disableGlobalTimeline: !enableGlobalTimeline,
+ pinnedUsers: pinnedUsers.split('\n'),
+ cacheRemoteFiles,
+ localDriveCapacityMb: parseInt(localDriveCapacityMb, 10),
+ remoteDriveCapacityMb: parseInt(remoteDriveCapacityMb, 10),
+ disableRegistration: !enableRegistration,
+ emailRequiredForSignup,
+ enableServiceWorker,
+ swPublicKey,
+ swPrivateKey,
+ deeplAuthKey,
+ deeplIsPro,
+ }).then(() => {
+ fetchInstance();
+ });
+}
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.name = meta.name;
- this.description = meta.description;
- this.tosUrl = meta.tosUrl;
- this.iconUrl = meta.iconUrl;
- this.bannerUrl = meta.bannerUrl;
- this.backgroundImageUrl = meta.backgroundImageUrl;
- this.themeColor = meta.themeColor;
- this.defaultLightTheme = meta.defaultLightTheme;
- this.defaultDarkTheme = meta.defaultDarkTheme;
- this.maintainerName = meta.maintainerName;
- this.maintainerEmail = meta.maintainerEmail;
- this.enableLocalTimeline = !meta.disableLocalTimeline;
- this.enableGlobalTimeline = !meta.disableGlobalTimeline;
- this.pinnedUsers = meta.pinnedUsers.join('\n');
- this.cacheRemoteFiles = meta.cacheRemoteFiles;
- this.localDriveCapacityMb = meta.driveCapacityPerLocalUserMb;
- this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb;
- this.enableRegistration = !meta.disableRegistration;
- this.emailRequiredForSignup = meta.emailRequiredForSignup;
- this.enableServiceWorker = meta.enableServiceWorker;
- this.swPublicKey = meta.swPublickey;
- this.swPrivateKey = meta.swPrivateKey;
- this.deeplAuthKey = meta.deeplAuthKey;
- this.deeplIsPro = meta.deeplIsPro;
- },
-
- save() {
- os.apiWithDialog('admin/update-meta', {
- name: this.name,
- description: this.description,
- tosUrl: this.tosUrl,
- iconUrl: this.iconUrl,
- bannerUrl: this.bannerUrl,
- backgroundImageUrl: this.backgroundImageUrl,
- themeColor: this.themeColor === '' ? null : this.themeColor,
- defaultLightTheme: this.defaultLightTheme === '' ? null : this.defaultLightTheme,
- defaultDarkTheme: this.defaultDarkTheme === '' ? null : this.defaultDarkTheme,
- maintainerName: this.maintainerName,
- maintainerEmail: this.maintainerEmail,
- disableLocalTimeline: !this.enableLocalTimeline,
- disableGlobalTimeline: !this.enableGlobalTimeline,
- pinnedUsers: this.pinnedUsers.split('\n'),
- cacheRemoteFiles: this.cacheRemoteFiles,
- localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10),
- remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10),
- disableRegistration: !this.enableRegistration,
- emailRequiredForSignup: this.emailRequiredForSignup,
- enableServiceWorker: this.enableServiceWorker,
- swPublicKey: this.swPublicKey,
- swPrivateKey: this.swPrivateKey,
- deeplAuthKey: this.deeplAuthKey,
- deeplIsPro: this.deeplIsPro,
- }).then(() => {
- fetchInstance();
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.general,
+ icon: 'fas fa-cog',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-check',
+ text: i18n.ts.save,
+ handler: save,
+ }],
}
});
</script>
diff --git a/packages/client/src/pages/api-console.vue b/packages/client/src/pages/api-console.vue
index 142a3bee2e..88acbcd3a3 100644
--- a/packages/client/src/pages/api-console.vue
+++ b/packages/client/src/pages/api-console.vue
@@ -25,72 +25,60 @@
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
-import * as JSON5 from 'json5';
+<script lang="ts" setup>
+import { ref } from 'vue';
+import JSON5 from 'json5';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkTextarea from '@/components/form/textarea.vue';
import MkSwitch from '@/components/form/switch.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { Endpoints } from 'misskey-js';
-export default defineComponent({
- components: {
- MkButton, MkInput, MkTextarea, MkSwitch,
- },
+const body = ref('{}');
+const endpoint = ref('');
+const endpoints = ref<any[]>([]);
+const sending = ref(false);
+const res = ref('');
+const withCredential = ref(true);
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: 'API console',
- icon: 'fas fa-terminal'
- },
+os.api('endpoints').then(endpointResponse => {
+ endpoints.value = endpointResponse;
+});
- endpoint: '',
- body: '{}',
- res: null,
- sending: false,
- endpoints: [],
- withCredential: true,
+function send() {
+ sending.value = true;
+ const requestBody = JSON5.parse(body.value);
+ os.api(endpoint.value as keyof Endpoints, requestBody, requestBody.i || (withCredential.value ? undefined : null)).then(resp => {
+ sending.value = false;
+ res.value = JSON5.stringify(resp, null, 2);
+ }, err => {
+ sending.value = false;
+ res.value = JSON5.stringify(err, null, 2);
+ });
+}
- };
- },
+function onEndpointChange() {
+ os.api('endpoint', { endpoint: endpoint.value }, withCredential.value ? undefined : null).then(resp => {
+ const endpointBody = {};
+ for (const p of resp.params) {
+ endpointBody[p.name] =
+ p.type === 'String' ? '' :
+ p.type === 'Number' ? 0 :
+ p.type === 'Boolean' ? false :
+ p.type === 'Array' ? [] :
+ p.type === 'Object' ? {} :
+ null;
+ }
+ body.value = JSON5.stringify(endpointBody, null, 2);
+ });
+}
- created() {
- os.api('endpoints').then(endpoints => {
- this.endpoints = endpoints;
- });
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: 'API console',
+ icon: 'fas fa-terminal'
},
-
- methods: {
- send() {
- this.sending = true;
- const body = JSON5.parse(this.body);
- os.api(this.endpoint, body, body.i || (this.withCredential ? undefined : null)).then(res => {
- this.sending = false;
- this.res = JSON5.stringify(res, null, 2);
- }, err => {
- this.sending = false;
- this.res = JSON5.stringify(err, null, 2);
- });
- },
-
- onEndpointChange() {
- os.api('endpoint', { endpoint: this.endpoint }, this.withCredential ? undefined : null).then(endpoint => {
- const body = {};
- for (const p of endpoint.params) {
- body[p.name] =
- p.type === 'String' ? '' :
- p.type === 'Number' ? 0 :
- p.type === 'Boolean' ? false :
- p.type === 'Array' ? [] :
- p.type === 'Object' ? {} :
- null;
- }
- this.body = JSON5.stringify(body, null, 2);
- });
- }
- }
});
</script>
diff --git a/packages/client/src/pages/auth.form.vue b/packages/client/src/pages/auth.form.vue
index bc719aebd3..5feff0149a 100644
--- a/packages/client/src/pages/auth.form.vue
+++ b/packages/client/src/pages/auth.form.vue
@@ -32,7 +32,7 @@ export default defineComponent({
computed: {
name(): string {
const el = document.createElement('div');
- el.textContent = this.app.name
+ el.textContent = this.app.name;
return el.innerHTML;
},
app(): any {
diff --git a/packages/client/src/pages/channel-editor.vue b/packages/client/src/pages/channel-editor.vue
index 3818c7481a..ea3a5dab76 100644
--- a/packages/client/src/pages/channel-editor.vue
+++ b/packages/client/src/pages/channel-editor.vue
@@ -111,8 +111,8 @@ export default defineComponent({
}
},
- setBannerImage(e) {
- selectFile(e.currentTarget ?? e.target, null).then(file => {
+ setBannerImage(evt) {
+ selectFile(evt.currentTarget ?? evt.target, null).then(file => {
this.bannerId = file.id;
});
},
diff --git a/packages/client/src/pages/emojis.category.vue b/packages/client/src/pages/emojis.category.vue
index 9a317418be..c47870f4d4 100644
--- a/packages/client/src/pages/emojis.category.vue
+++ b/packages/client/src/pages/emojis.category.vue
@@ -58,7 +58,7 @@ export default defineComponent({
tags: emojiTags,
selectedTags: new Set(),
searchEmojis: null,
- }
+ };
},
watch: {
@@ -79,9 +79,9 @@ export default defineComponent({
}
if (this.selectedTags.size === 0) {
- this.searchEmojis = this.customEmojis.filter(e => e.name.includes(this.q) || e.aliases.includes(this.q));
+ this.searchEmojis = this.customEmojis.filter(emoji => emoji.name.includes(this.q) || emoji.aliases.includes(this.q));
} else {
- this.searchEmojis = this.customEmojis.filter(e => (e.name.includes(this.q) || e.aliases.includes(this.q)) && [...this.selectedTags].every(t => e.aliases.includes(t)));
+ this.searchEmojis = this.customEmojis.filter(emoji => (emoji.name.includes(this.q) || emoji.aliases.includes(this.q)) && [...this.selectedTags].every(t => emoji.aliases.includes(t)));
}
},
diff --git a/packages/client/src/pages/emojis.vue b/packages/client/src/pages/emojis.vue
index 886b5f7119..f44b29df04 100644
--- a/packages/client/src/pages/emojis.vue
+++ b/packages/client/src/pages/emojis.vue
@@ -25,10 +25,10 @@ function menu(ev) {
type: 'info',
text: i18n.ts.exportRequested,
});
- }).catch((e) => {
+ }).catch((err) => {
os.alert({
type: 'error',
- text: e.message,
+ text: err.message,
});
});
}
diff --git a/packages/client/src/pages/federation.vue b/packages/client/src/pages/federation.vue
index 5add2b5324..447918905b 100644
--- a/packages/client/src/pages/federation.vue
+++ b/packages/client/src/pages/federation.vue
@@ -127,7 +127,7 @@ function getStatus(instance) {
if (instance.isSuspended) return 'suspended';
if (instance.isNotResponding) return 'error';
return 'alive';
-};
+}
defineExpose({
[symbols.PAGE_INFO]: {
diff --git a/packages/client/src/pages/follow.vue b/packages/client/src/pages/follow.vue
index d8a6824dca..e69e0481e0 100644
--- a/packages/client/src/pages/follow.vue
+++ b/packages/client/src/pages/follow.vue
@@ -20,7 +20,7 @@ export default defineComponent({
uri: acct
});
promise.then(res => {
- if (res.type == 'User') {
+ if (res.type === 'User') {
this.follow(res.object);
} else if (res.type === 'Note') {
this.$router.push(`/notes/${res.object.id}`);
diff --git a/packages/client/src/pages/gallery/edit.vue b/packages/client/src/pages/gallery/edit.vue
index 25ee513186..bc87160c44 100644
--- a/packages/client/src/pages/gallery/edit.vue
+++ b/packages/client/src/pages/gallery/edit.vue
@@ -71,7 +71,7 @@ export default defineComponent({
description: null,
title: null,
isSensitive: false,
- }
+ };
},
watch: {
@@ -91,8 +91,8 @@ export default defineComponent({
},
methods: {
- selectFile(e) {
- selectFiles(e.currentTarget ?? e.target, null).then(files => {
+ selectFile(evt) {
+ selectFiles(evt.currentTarget ?? evt.target, null).then(files => {
this.files = this.files.concat(files);
});
},
diff --git a/packages/client/src/pages/gallery/post.vue b/packages/client/src/pages/gallery/post.vue
index 1755c23286..1ca3443e56 100644
--- a/packages/client/src/pages/gallery/post.vue
+++ b/packages/client/src/pages/gallery/post.vue
@@ -119,8 +119,8 @@ export default defineComponent({
postId: this.postId
}).then(post => {
this.post = post;
- }).catch(e => {
- this.error = e;
+ }).catch(err => {
+ this.error = err;
});
},
diff --git a/packages/client/src/pages/messaging/index.vue b/packages/client/src/pages/messaging/index.vue
index 88a1e07afc..7c1d3e3cbe 100644
--- a/packages/client/src/pages/messaging/index.vue
+++ b/packages/client/src/pages/messaging/index.vue
@@ -90,14 +90,14 @@ export default defineComponent({
getAcct: Acct.toString,
isMe(message) {
- return message.userId == this.$i.id;
+ return message.userId === this.$i.id;
},
onMessage(message) {
if (message.recipientId) {
this.messages = this.messages.filter(m => !(
- (m.recipientId == message.recipientId && m.userId == message.userId) ||
- (m.recipientId == message.userId && m.userId == message.recipientId)));
+ (m.recipientId === message.recipientId && m.userId === message.userId) ||
+ (m.recipientId === message.userId && m.userId === message.recipientId)));
this.messages.unshift(message);
} else if (message.groupId) {
@@ -108,7 +108,7 @@ export default defineComponent({
onRead(ids) {
for (const id of ids) {
- const found = this.messages.find(m => m.id == id);
+ const found = this.messages.find(m => m.id === id);
if (found) {
if (found.recipientId) {
found.isRead = true;
@@ -123,11 +123,11 @@ export default defineComponent({
os.popupMenu([{
text: this.$ts.messagingWithUser,
icon: 'fas fa-user',
- action: () => { this.startUser() }
+ action: () => { this.startUser(); }
}, {
text: this.$ts.messagingWithGroup,
icon: 'fas fa-users',
- action: () => { this.startGroup() }
+ action: () => { this.startGroup(); }
}], ev.currentTarget ?? ev.target);
},
diff --git a/packages/client/src/pages/messaging/messaging-room.form.vue b/packages/client/src/pages/messaging/messaging-room.form.vue
index 3863c8f82b..8e779c4f39 100644
--- a/packages/client/src/pages/messaging/messaging-room.form.vue
+++ b/packages/client/src/pages/messaging/messaging-room.form.vue
@@ -31,6 +31,7 @@ import * as os from '@/os';
import { stream } from '@/stream';
import { Autocomplete } from '@/scripts/autocomplete';
import { throttle } from 'throttle-debounce';
+import { uploadFile } from '@/scripts/upload';
export default defineComponent({
props: {
@@ -58,7 +59,7 @@ export default defineComponent({
return this.user ? 'user:' + this.user.id : 'group:' + this.group.id;
},
canSend(): boolean {
- return (this.text != null && this.text != '') || this.file != null;
+ return (this.text != null && this.text !== '') || this.file != null;
},
room(): any {
return this.$parent;
@@ -87,12 +88,11 @@ export default defineComponent({
}
},
methods: {
- async onPaste(e: ClipboardEvent) {
- const data = e.clipboardData;
- const items = data.items;
+ async onPaste(evt: ClipboardEvent) {
+ const items = evt.clipboardData.items;
- if (items.length == 1) {
- if (items[0].kind == 'file') {
+ if (items.length === 1) {
+ if (items[0].kind === 'file') {
const file = items[0].getAsFile();
const lio = file.name.lastIndexOf('.');
const ext = lio >= 0 ? file.name.slice(lio) : '';
@@ -100,7 +100,7 @@ export default defineComponent({
if (formatted) this.upload(file, formatted);
}
} else {
- if (items[0].kind == 'file') {
+ if (items[0].kind === 'file') {
os.alert({
type: 'error',
text: this.$ts.onlyOneFileCanBeAttached
@@ -109,23 +109,23 @@ export default defineComponent({
}
},
- onDragover(e) {
- const isFile = e.dataTransfer.items[0].kind == 'file';
- const isDriveFile = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FILE_;
+ onDragover(evt) {
+ const isFile = evt.dataTransfer.items[0].kind === 'file';
+ const isDriveFile = evt.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
if (isFile || isDriveFile) {
- e.preventDefault();
- e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
+ evt.preventDefault();
+ evt.dataTransfer.dropEffect = evt.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move';
}
},
- onDrop(e): void {
+ onDrop(evt): void {
// ファイルã ã£ãŸã‚‰
- if (e.dataTransfer.files.length == 1) {
- e.preventDefault();
- this.upload(e.dataTransfer.files[0]);
+ if (evt.dataTransfer.files.length === 1) {
+ evt.preventDefault();
+ this.upload(evt.dataTransfer.files[0]);
return;
- } else if (e.dataTransfer.files.length > 1) {
- e.preventDefault();
+ } else if (evt.dataTransfer.files.length > 1) {
+ evt.preventDefault();
os.alert({
type: 'error',
text: this.$ts.onlyOneFileCanBeAttached
@@ -134,17 +134,17 @@ export default defineComponent({
}
//#region ドライブã®ãƒ•ァイル
- const driveFile = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
- if (driveFile != null && driveFile != '') {
+ const driveFile = evt.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
+ if (driveFile != null && driveFile !== '') {
this.file = JSON.parse(driveFile);
- e.preventDefault();
+ evt.preventDefault();
}
//#endregion
},
- onKeydown(e) {
+ onKeydown(evt) {
this.typing();
- if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey) && this.canSend) {
+ if ((evt.which === 10 || evt.which === 13) && (evt.ctrlKey || evt.metaKey) && this.canSend) {
this.send();
}
},
@@ -153,8 +153,8 @@ export default defineComponent({
this.typing();
},
- chooseFile(e) {
- selectFile(e.currentTarget ?? e.target, this.$ts.selectFile).then(file => {
+ chooseFile(evt) {
+ selectFile(evt.currentTarget ?? evt.target, this.$ts.selectFile).then(file => {
this.file = file;
});
},
@@ -164,7 +164,7 @@ export default defineComponent({
},
upload(file: File, name?: string) {
- os.upload(file, this.$store.state.uploadFolder, name).then(res => {
+ uploadFile(file, this.$store.state.uploadFolder, name).then(res => {
this.file = res;
});
},
@@ -192,25 +192,25 @@ export default defineComponent({
},
saveDraft() {
- const data = JSON.parse(localStorage.getItem('message_drafts') || '{}');
+ const drafts = JSON.parse(localStorage.getItem('message_drafts') || '{}');
- data[this.draftKey] = {
+ drafts[this.draftKey] = {
updatedAt: new Date(),
data: {
text: this.text,
file: this.file
}
- }
+ };
- localStorage.setItem('message_drafts', JSON.stringify(data));
+ localStorage.setItem('message_drafts', JSON.stringify(drafts));
},
deleteDraft() {
- const data = JSON.parse(localStorage.getItem('message_drafts') || '{}');
+ const drafts = JSON.parse(localStorage.getItem('message_drafts') || '{}');
- delete data[this.draftKey];
+ delete drafts[this.draftKey];
- localStorage.setItem('message_drafts', JSON.stringify(data));
+ localStorage.setItem('message_drafts', JSON.stringify(drafts));
},
async insertEmoji(ev) {
diff --git a/packages/client/src/pages/messaging/messaging-room.vue b/packages/client/src/pages/messaging/messaging-room.vue
index 2ecc68eb54..fd1962218a 100644
--- a/packages/client/src/pages/messaging/messaging-room.vue
+++ b/packages/client/src/pages/messaging/messaging-room.vue
@@ -166,23 +166,23 @@ const Component = defineComponent({
});
},
- onDragover(e) {
- const isFile = e.dataTransfer.items[0].kind == 'file';
- const isDriveFile = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FILE_;
+ onDragover(evt) {
+ const isFile = evt.dataTransfer.items[0].kind === 'file';
+ const isDriveFile = evt.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
if (isFile || isDriveFile) {
- e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
+ evt.dataTransfer.dropEffect = evt.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move';
} else {
- e.dataTransfer.dropEffect = 'none';
+ evt.dataTransfer.dropEffect = 'none';
}
},
- onDrop(e): void {
+ onDrop(evt): void {
// ファイルã ã£ãŸã‚‰
- if (e.dataTransfer.files.length == 1) {
- this.form.upload(e.dataTransfer.files[0]);
+ if (evt.dataTransfer.files.length === 1) {
+ this.form.upload(evt.dataTransfer.files[0]);
return;
- } else if (e.dataTransfer.files.length > 1) {
+ } else if (evt.dataTransfer.files.length > 1) {
os.alert({
type: 'error',
text: this.$ts.onlyOneFileCanBeAttached
@@ -191,8 +191,8 @@ const Component = defineComponent({
}
//#region ドライブã®ãƒ•ァイル
- const driveFile = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
- if (driveFile != null && driveFile != '') {
+ const driveFile = evt.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
+ if (driveFile != null && driveFile !== '') {
const file = JSON.parse(driveFile);
this.form.file = file;
}
@@ -209,7 +209,7 @@ const Component = defineComponent({
limit: max + 1,
untilId: this.existMoreMessages ? this.messages[0].id : undefined
}).then(messages => {
- if (messages.length == max + 1) {
+ if (messages.length === max + 1) {
this.existMoreMessages = true;
messages.pop();
} else {
@@ -235,7 +235,7 @@ const Component = defineComponent({
const _isBottom = isBottom(this.$el, 64);
this.messages.push(message);
- if (message.userId != this.$i.id && !document.hidden) {
+ if (message.userId !== this.$i.id && !document.hidden) {
this.connection.send('read', {
id: message.id
});
@@ -246,7 +246,7 @@ const Component = defineComponent({
this.$nextTick(() => {
this.scrollToBottom();
});
- } else if (message.userId != this.$i.id) {
+ } else if (message.userId !== this.$i.id) {
// Notify
this.notifyNewMessage();
}
@@ -256,7 +256,7 @@ const Component = defineComponent({
if (this.user) {
if (!Array.isArray(x)) x = [x];
for (const id of x) {
- if (this.messages.some(x => x.id == id)) {
+ if (this.messages.some(x => x.id === id)) {
const exist = this.messages.map(x => x.id).indexOf(id);
this.messages[exist] = {
...this.messages[exist],
@@ -266,7 +266,7 @@ const Component = defineComponent({
}
} else if (this.group) {
for (const id of x.ids) {
- if (this.messages.some(x => x.id == id)) {
+ if (this.messages.some(x => x.id === id)) {
const exist = this.messages.map(x => x.id).indexOf(id);
this.messages[exist] = {
...this.messages[exist],
diff --git a/packages/client/src/pages/mfm-cheat-sheet.vue b/packages/client/src/pages/mfm-cheat-sheet.vue
index 83ae5741c3..2c10494ede 100644
--- a/packages/client/src/pages/mfm-cheat-sheet.vue
+++ b/packages/client/src/pages/mfm-cheat-sheet.vue
@@ -325,23 +325,23 @@ export default defineComponent({
preview_inlineMath: '\\(x= \\frac{-b\' \\pm \\sqrt{(b\')^2-ac}}{a}\\)',
preview_quote: `> ${this.$ts._mfm.dummy}`,
preview_search: `${this.$ts._mfm.dummy} 検索`,
- preview_jelly: `$[jelly ðŸ®]`,
- preview_tada: `$[tada ðŸ®]`,
- preview_jump: `$[jump ðŸ®]`,
- preview_bounce: `$[bounce ðŸ®]`,
- preview_shake: `$[shake ðŸ®]`,
- preview_twitch: `$[twitch ðŸ®]`,
- preview_spin: `$[spin ðŸ®] $[spin.left ðŸ®] $[spin.alternate ðŸ®]\n$[spin.x ðŸ®] $[spin.x,left ðŸ®] $[spin.x,alternate ðŸ®]\n$[spin.y ðŸ®] $[spin.y,left ðŸ®] $[spin.y,alternate ðŸ®]`,
+ preview_jelly: `$[jelly ðŸ®] $[jelly.speed=5s ðŸ®]`,
+ preview_tada: `$[tada ðŸ®] $[tada.speed=5s ðŸ®]`,
+ preview_jump: `$[jump ðŸ®] $[jump.speed=5s ðŸ®]`,
+ preview_bounce: `$[bounce ðŸ®] $[bounce.speed=5s ðŸ®]`,
+ preview_shake: `$[shake ðŸ®] $[shake.speed=5s ðŸ®]`,
+ preview_twitch: `$[twitch ðŸ®] $[twitch.speed=5s ðŸ®]`,
+ preview_spin: `$[spin ðŸ®] $[spin.left ðŸ®] $[spin.alternate ðŸ®]\n$[spin.x ðŸ®] $[spin.x,left ðŸ®] $[spin.x,alternate ðŸ®]\n$[spin.y ðŸ®] $[spin.y,left ðŸ®] $[spin.y,alternate ðŸ®]\n\n$[spin.speed=5s ðŸ®]`,
preview_flip: `$[flip ${this.$ts._mfm.dummy}]\n$[flip.v ${this.$ts._mfm.dummy}]\n$[flip.h,v ${this.$ts._mfm.dummy}]`,
preview_font: `$[font.serif ${this.$ts._mfm.dummy}]\n$[font.monospace ${this.$ts._mfm.dummy}]\n$[font.cursive ${this.$ts._mfm.dummy}]\n$[font.fantasy ${this.$ts._mfm.dummy}]`,
preview_x2: `$[x2 ðŸ®]`,
preview_x3: `$[x3 ðŸ®]`,
preview_x4: `$[x4 ðŸ®]`,
preview_blur: `$[blur ${this.$ts._mfm.dummy}]`,
- preview_rainbow: `$[rainbow ðŸ®]`,
+ preview_rainbow: `$[rainbow ðŸ®] $[rainbow.speed=5s ðŸ®]`,
preview_sparkle: `$[sparkle ðŸ®]`,
preview_rotate: `$[rotate ðŸ®]`,
- }
+ };
},
});
</script>
diff --git a/packages/client/src/pages/miauth.vue b/packages/client/src/pages/miauth.vue
index 6e85b784ff..4032d7723e 100644
--- a/packages/client/src/pages/miauth.vue
+++ b/packages/client/src/pages/miauth.vue
@@ -42,6 +42,7 @@ import MkSignin from '@/components/signin.vue';
import MkButton from '@/components/ui/button.vue';
import * as os from '@/os';
import { login } from '@/account';
+import { appendQuery, query } from '@/scripts/url';
export default defineComponent({
components: {
@@ -82,7 +83,9 @@ export default defineComponent({
this.state = 'accepted';
if (this.callback) {
- location.href = `${this.callback}?session=${this.session}`;
+ location.href = appendQuery(this.callback, query({
+ session: this.session
+ }));
}
},
deny() {
diff --git a/packages/client/src/pages/my-antennas/edit.vue b/packages/client/src/pages/my-antennas/edit.vue
index 04928c81a3..38e56ce35d 100644
--- a/packages/client/src/pages/my-antennas/edit.vue
+++ b/packages/client/src/pages/my-antennas/edit.vue
@@ -4,49 +4,34 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
-import MkButton from '@/components/ui/button.vue';
+<script lang="ts" setup>
+import { watch } from 'vue';
import XAntenna from './editor.vue';
import * as symbols from '@/symbols';
import * as os from '@/os';
+import { MisskeyNavigator } from '@/scripts/navigate';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- XAntenna,
- },
+const nav = new MisskeyNavigator();
- props: {
- antennaId: {
- type: String,
- required: true,
- }
- },
+let antenna: any = $ref(null);
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.manageAntennas,
- icon: 'fas fa-satellite',
- },
- antenna: null,
- };
- },
+const props = defineProps<{
+ antennaId: string
+}>();
- watch: {
- antennaId: {
- async handler() {
- this.antenna = await os.api('antennas/show', { antennaId: this.antennaId });
- },
- immediate: true,
- }
- },
+function onAntennaUpdated() {
+ nav.push('/my/antennas');
+}
- methods: {
- onAntennaUpdated() {
- this.$router.push('/my/antennas');
- },
+os.api('antennas/show', { antennaId: props.antennaId }).then((antennaResponse) => {
+ antenna = antennaResponse;
+});
+
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.manageAntennas,
+ icon: 'fas fa-satellite',
}
});
</script>
diff --git a/packages/client/src/pages/my-antennas/editor.vue b/packages/client/src/pages/my-antennas/editor.vue
index 8c1d6148fe..6f3c4afbfe 100644
--- a/packages/client/src/pages/my-antennas/editor.vue
+++ b/packages/client/src/pages/my-antennas/editor.vue
@@ -44,135 +44,100 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { watch } from 'vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkTextarea from '@/components/form/textarea.vue';
import MkSelect from '@/components/form/select.vue';
import MkSwitch from '@/components/form/switch.vue';
-import * as Acct from 'misskey-js/built/acct';
import * as os from '@/os';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton, MkInput, MkTextarea, MkSelect, MkSwitch
- },
+const props = defineProps<{
+ antenna: any
+}>();
- props: {
- antenna: {
- type: Object,
- required: true
- }
- },
+const emit = defineEmits<{
+ (ev: 'created'): void,
+ (ev: 'updated'): void,
+ (ev: 'deleted'): void,
+}>();
- data() {
- return {
- name: '',
- src: '',
- userListId: null,
- userGroupId: null,
- users: '',
- keywords: '',
- excludeKeywords: '',
- caseSensitive: false,
- withReplies: false,
- withFile: false,
- notify: false,
- userLists: null,
- userGroups: null,
- };
- },
+let name: string = $ref(props.antenna.name);
+let src: string = $ref(props.antenna.src);
+let userListId: any = $ref(props.antenna.userListId);
+let userGroupId: any = $ref(props.antenna.userGroupId);
+let users: string = $ref(props.antenna.users.join('\n'));
+let keywords: string = $ref(props.antenna.keywords.map(x => x.join(' ')).join('\n'));
+let excludeKeywords: string = $ref(props.antenna.excludeKeywords.map(x => x.join(' ')).join('\n'));
+let caseSensitive: boolean = $ref(props.antenna.caseSensitive);
+let withReplies: boolean = $ref(props.antenna.withReplies);
+let withFile: boolean = $ref(props.antenna.withFile);
+let notify: boolean = $ref(props.antenna.notify);
+let userLists: any = $ref(null);
+let userGroups: any = $ref(null);
- watch: {
- async src() {
- if (this.src === 'list' && this.userLists === null) {
- this.userLists = await os.api('users/lists/list');
- }
+watch(() => src, async () => {
+ if (src === 'list' && userLists === null) {
+ userLists = await os.api('users/lists/list');
+ }
- if (this.src === 'group' && this.userGroups === null) {
- const groups1 = await os.api('users/groups/owned');
- const groups2 = await os.api('users/groups/joined');
+ if (src === 'group' && userGroups === null) {
+ const groups1 = await os.api('users/groups/owned');
+ const groups2 = await os.api('users/groups/joined');
- this.userGroups = [...groups1, ...groups2];
- }
- }
- },
+ userGroups = [...groups1, ...groups2];
+ }
+});
- created() {
- this.name = this.antenna.name;
- this.src = this.antenna.src;
- this.userListId = this.antenna.userListId;
- this.userGroupId = this.antenna.userGroupId;
- this.users = this.antenna.users.join('\n');
- this.keywords = this.antenna.keywords.map(x => x.join(' ')).join('\n');
- this.excludeKeywords = this.antenna.excludeKeywords.map(x => x.join(' ')).join('\n');
- this.caseSensitive = this.antenna.caseSensitive;
- this.withReplies = this.antenna.withReplies;
- this.withFile = this.antenna.withFile;
- this.notify = this.antenna.notify;
- },
+async function saveAntenna() {
+ const antennaData = {
+ name,
+ src,
+ userListId,
+ userGroupId,
+ withReplies,
+ withFile,
+ notify,
+ caseSensitive,
+ users: users.trim().split('\n').map(x => x.trim()),
+ keywords: keywords.trim().split('\n').map(x => x.trim().split(' ')),
+ excludeKeywords: excludeKeywords.trim().split('\n').map(x => x.trim().split(' ')),
+ };
- methods: {
- async saveAntenna() {
- if (this.antenna.id == null) {
- await os.apiWithDialog('antennas/create', {
- name: this.name,
- src: this.src,
- userListId: this.userListId,
- userGroupId: this.userGroupId,
- withReplies: this.withReplies,
- withFile: this.withFile,
- notify: this.notify,
- caseSensitive: this.caseSensitive,
- users: this.users.trim().split('\n').map(x => x.trim()),
- keywords: this.keywords.trim().split('\n').map(x => x.trim().split(' ')),
- excludeKeywords: this.excludeKeywords.trim().split('\n').map(x => x.trim().split(' ')),
- });
- this.$emit('created');
- } else {
- await os.apiWithDialog('antennas/update', {
- antennaId: this.antenna.id,
- name: this.name,
- src: this.src,
- userListId: this.userListId,
- userGroupId: this.userGroupId,
- withReplies: this.withReplies,
- withFile: this.withFile,
- notify: this.notify,
- caseSensitive: this.caseSensitive,
- users: this.users.trim().split('\n').map(x => x.trim()),
- keywords: this.keywords.trim().split('\n').map(x => x.trim().split(' ')),
- excludeKeywords: this.excludeKeywords.trim().split('\n').map(x => x.trim().split(' ')),
- });
- this.$emit('updated');
- }
- },
+ if (props.antenna.id == null) {
+ await os.apiWithDialog('antennas/create', antennaData);
+ emit('created');
+ } else {
+ antennaData['antennaId'] = props.antenna.id;
+ await os.apiWithDialog('antennas/update', antennaData);
+ emit('updated');
+ }
+}
- async deleteAntenna() {
- const { canceled } = await os.confirm({
- type: 'warning',
- text: this.$t('removeAreYouSure', { x: this.antenna.name }),
- });
- if (canceled) return;
+async function deleteAntenna() {
+ const { canceled } = await os.confirm({
+ type: 'warning',
+ text: i18n.t('removeAreYouSure', { x: props.antenna.name }),
+ });
+ if (canceled) return;
- await os.api('antennas/delete', {
- antennaId: this.antenna.id,
- });
+ await os.api('antennas/delete', {
+ antennaId: props.antenna.id,
+ });
- os.success();
- this.$emit('deleted');
- },
+ os.success();
+ emit('deleted');
+}
- addUser() {
- os.selectUser().then(user => {
- this.users = this.users.trim();
- this.users += '\n@' + Acct.toString(user);
- this.users = this.users.trim();
- });
- }
- }
-});
+function addUser() {
+ os.selectUser().then(user => {
+ users = users.trim();
+ users += '\n@' + Acct.toString(user as any);
+ users = users.trim();
+ });
+}
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/pages/my-antennas/index.vue b/packages/client/src/pages/my-antennas/index.vue
index 7138d269a9..a568f64c52 100644
--- a/packages/client/src/pages/my-antennas/index.vue
+++ b/packages/client/src/pages/my-antennas/index.vue
@@ -1,7 +1,7 @@
<template>
<MkSpacer :content-max="700">
<div class="ieepwinx">
- <MkButton :link="true" to="/my/antennas/create" primary class="add"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton>
+ <MkButton :link="true" to="/my/antennas/create" primary class="add"><i class="fas fa-plus"></i> {{ i18n.ts.add }}</MkButton>
<div class="">
<MkPagination v-slot="{items}" ref="list" :pagination="pagination">
@@ -14,35 +14,24 @@
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import MkPagination from '@/components/ui/pagination.vue';
import MkButton from '@/components/ui/button.vue';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkPagination,
- MkButton,
- },
+const pagination = {
+ endpoint: 'antennas/list' as const,
+ limit: 10,
+};
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.manageAntennas,
- icon: 'fas fa-satellite',
- bg: 'var(--bg)',
- action: {
- icon: 'fas fa-plus',
- handler: this.create
- }
- },
- pagination: {
- endpoint: 'antennas/list' as const,
- limit: 10,
- },
- };
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.manageAntennas,
+ icon: 'fas fa-satellite',
+ bg: 'var(--bg)'
+ }
});
</script>
diff --git a/packages/client/src/pages/note.vue b/packages/client/src/pages/note.vue
index 29261ec484..f0a18ecc36 100644
--- a/packages/client/src/pages/note.vue
+++ b/packages/client/src/pages/note.vue
@@ -108,6 +108,10 @@ export default defineComponent({
},
methods: {
fetch() {
+ this.hasPrev = false;
+ this.hasNext = false;
+ this.showPrev = false;
+ this.showNext = false;
this.note = null;
os.api('notes/show', {
noteId: this.noteId
@@ -132,8 +136,8 @@ export default defineComponent({
this.hasPrev = prev.length !== 0;
this.hasNext = next.length !== 0;
});
- }).catch(e => {
- this.error = e;
+ }).catch(err => {
+ this.error = err;
});
}
}
diff --git a/packages/client/src/pages/page-editor/page-editor.vue b/packages/client/src/pages/page-editor/page-editor.vue
index f302ac4f90..9566592618 100644
--- a/packages/client/src/pages/page-editor/page-editor.vue
+++ b/packages/client/src/pages/page-editor/page-editor.vue
@@ -114,7 +114,7 @@ export default defineComponent({
readonly: this.readonly,
getScriptBlockList: this.getScriptBlockList,
getPageBlockList: this.getPageBlockList
- }
+ };
},
props: {
diff --git a/packages/client/src/pages/page.vue b/packages/client/src/pages/page.vue
index b2c039a269..5bca971438 100644
--- a/packages/client/src/pages/page.vue
+++ b/packages/client/src/pages/page.vue
@@ -139,8 +139,8 @@ export default defineComponent({
username: this.username,
}).then(page => {
this.page = page;
- }).catch(e => {
- this.error = e;
+ }).catch(err => {
+ this.error = err;
});
},
diff --git a/packages/client/src/pages/reset-password.vue b/packages/client/src/pages/reset-password.vue
index 7d008ae75c..b3e2ca8d6f 100644
--- a/packages/client/src/pages/reset-password.vue
+++ b/packages/client/src/pages/reset-password.vue
@@ -12,7 +12,7 @@
</template>
<script lang="ts" setup>
-import { onMounted } from 'vue';
+import { defineAsyncComponent, onMounted } from 'vue';
import FormInput from '@/components/form/input.vue';
import FormButton from '@/components/ui/button.vue';
import * as os from '@/os';
@@ -36,7 +36,7 @@ async function save() {
onMounted(() => {
if (props.token == null) {
- os.popup(import('@/components/forgot-password.vue'), {}, {}, 'closed');
+ os.popup(defineAsyncComponent(() => import('@/components/forgot-password.vue')), {}, {}, 'closed');
router.push('/');
}
});
diff --git a/packages/client/src/pages/scratchpad.vue b/packages/client/src/pages/scratchpad.vue
index f871dc48e8..34a41b81a5 100644
--- a/packages/client/src/pages/scratchpad.vue
+++ b/packages/client/src/pages/scratchpad.vue
@@ -6,20 +6,20 @@
</div>
<MkContainer :foldable="true" class="_gap">
- <template #header>{{ $ts.output }}</template>
+ <template #header>{{ i18n.ts.output }}</template>
<div class="bepmlvbi">
<div v-for="log in logs" :key="log.id" class="log" :class="{ print: log.print }">{{ log.text }}</div>
</div>
</MkContainer>
<div class="_gap">
- {{ $ts.scratchpadDescription }}
+ {{ i18n.ts.scratchpadDescription }}
</div>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, ref, watch } from 'vue';
import 'prismjs';
import { highlight, languages } from 'prismjs/components/prism-core';
import 'prismjs/components/prism-clike';
@@ -27,103 +27,90 @@ import 'prismjs/components/prism-javascript';
import 'prismjs/themes/prism-okaidia.css';
import { PrismEditor } from 'vue-prism-editor';
import 'vue-prism-editor/dist/prismeditor.min.css';
-import { AiScript, parse, utils, values } from '@syuilo/aiscript';
+import { AiScript, parse, utils } from '@syuilo/aiscript';
import MkContainer from '@/components/ui/container.vue';
import MkButton from '@/components/ui/button.vue';
import { createAiScriptEnv } from '@/scripts/aiscript/api';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { $i } from '@/account';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkContainer,
- MkButton,
- PrismEditor,
- },
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.scratchpad,
- icon: 'fas fa-terminal',
- },
- code: '',
- logs: [],
- }
- },
-
- watch: {
- code() {
- localStorage.setItem('scratchpad', this.code);
- }
- },
+const code = ref('');
+const logs = ref<any[]>([]);
- created() {
- const saved = localStorage.getItem('scratchpad');
- if (saved) {
- this.code = saved;
- }
- },
+const saved = localStorage.getItem('scratchpad');
+if (saved) {
+ code.value = saved;
+}
- methods: {
- async run() {
- this.logs = [];
- const aiscript = new AiScript(createAiScriptEnv({
- storageKey: 'scratchpad',
- token: this.$i?.token,
- }), {
- in: (q) => {
- return new Promise(ok => {
- os.inputText({
- title: q,
- }).then(({ canceled, result: a }) => {
- ok(a);
- });
- });
- },
- out: (value) => {
- this.logs.push({
- id: Math.random(),
- text: value.type === 'str' ? value.value : utils.valToString(value),
- print: true
- });
- },
- log: (type, params) => {
- switch (type) {
- case 'end': this.logs.push({
- id: Math.random(),
- text: utils.valToString(params.val, true),
- print: false
- }); break;
- default: break;
- }
- }
- });
+watch(code, () => {
+ localStorage.setItem('scratchpad', code.value);
+});
- let ast;
- try {
- ast = parse(this.code);
- } catch (e) {
- os.alert({
- type: 'error',
- text: 'Syntax error :('
- });
- return;
- }
- try {
- await aiscript.exec(ast);
- } catch (e) {
- os.alert({
- type: 'error',
- text: e
+async function run() {
+ logs.value = [];
+ const aiscript = new AiScript(createAiScriptEnv({
+ storageKey: 'scratchpad',
+ token: $i?.token,
+ }), {
+ in: (q) => {
+ return new Promise(ok => {
+ os.inputText({
+ title: q,
+ }).then(({ canceled, result: a }) => {
+ ok(a);
});
- }
+ });
},
-
- highlighter(code) {
- return highlight(code, languages.js, 'javascript');
+ out: (value) => {
+ logs.value.push({
+ id: Math.random(),
+ text: value.type === 'str' ? value.value : utils.valToString(value),
+ print: true
+ });
},
+ log: (type, params) => {
+ switch (type) {
+ case 'end': logs.value.push({
+ id: Math.random(),
+ text: utils.valToString(params.val, true),
+ print: false
+ }); break;
+ default: break;
+ }
+ }
+ });
+
+ let ast;
+ try {
+ ast = parse(code.value);
+ } catch (error) {
+ os.alert({
+ type: 'error',
+ text: 'Syntax error :('
+ });
+ return;
}
+ try {
+ await aiscript.exec(ast);
+ } catch (error: any) {
+ os.alert({
+ type: 'error',
+ text: error.message
+ });
+ }
+}
+
+function highlighter(code) {
+ return highlight(code, languages.js, 'javascript');
+}
+
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.scratchpad,
+ icon: 'fas fa-terminal',
+ },
});
</script>
diff --git a/packages/client/src/pages/settings/2fa.vue b/packages/client/src/pages/settings/2fa.vue
index 10599d99ff..fb3a7a17f3 100644
--- a/packages/client/src/pages/settings/2fa.vue
+++ b/packages/client/src/pages/settings/2fa.vue
@@ -1,49 +1,49 @@
<template>
<div>
- <MkButton v-if="!data && !$i.twoFactorEnabled" @click="register">{{ $ts._2fa.registerDevice }}</MkButton>
+ <MkButton v-if="!twoFactorData && !$i.twoFactorEnabled" @click="register">{{ i18n.ts._2fa.registerDevice }}</MkButton>
<template v-if="$i.twoFactorEnabled">
- <p>{{ $ts._2fa.alreadyRegistered }}</p>
- <MkButton @click="unregister">{{ $ts.unregister }}</MkButton>
+ <p>{{ i18n.ts._2fa.alreadyRegistered }}</p>
+ <MkButton @click="unregister">{{ i18n.ts.unregister }}</MkButton>
<template v-if="supportsCredentials">
<hr class="totp-method-sep">
- <h2 class="heading">{{ $ts.securityKey }}</h2>
- <p>{{ $ts._2fa.securityKeyInfo }}</p>
+ <h2 class="heading">{{ i18n.ts.securityKey }}</h2>
+ <p>{{ i18n.ts._2fa.securityKeyInfo }}</p>
<div class="key-list">
<div v-for="key in $i.securityKeysList" class="key">
<h3>{{ key.name }}</h3>
- <div class="last-used">{{ $ts.lastUsed }}<MkTime :time="key.lastUsed"/></div>
- <MkButton @click="unregisterKey(key)">{{ $ts.unregister }}</MkButton>
+ <div class="last-used">{{ i18n.ts.lastUsed }}<MkTime :time="key.lastUsed"/></div>
+ <MkButton @click="unregisterKey(key)">{{ i18n.ts.unregister }}</MkButton>
</div>
</div>
- <MkSwitch v-if="$i.securityKeysList.length > 0" v-model="usePasswordLessLogin" @update:modelValue="updatePasswordLessLogin">{{ $ts.passwordLessLogin }}</MkSwitch>
+ <MkSwitch v-if="$i.securityKeysList.length > 0" v-model="usePasswordLessLogin" @update:modelValue="updatePasswordLessLogin">{{ i18n.ts.passwordLessLogin }}</MkSwitch>
- <MkInfo v-if="registration && registration.error" warn>{{ $ts.error }} {{ registration.error }}</MkInfo>
- <MkButton v-if="!registration || registration.error" @click="addSecurityKey">{{ $ts._2fa.registerKey }}</MkButton>
+ <MkInfo v-if="registration && registration.error" warn>{{ i18n.ts.error }} {{ registration.error }}</MkInfo>
+ <MkButton v-if="!registration || registration.error" @click="addSecurityKey">{{ i18n.ts._2fa.registerKey }}</MkButton>
<ol v-if="registration && !registration.error">
<li v-if="registration.stage >= 0">
- {{ $ts.tapSecurityKey }}
+ {{ i18n.ts.tapSecurityKey }}
<i v-if="registration.saving && registration.stage == 0" class="fas fa-spinner fa-pulse fa-fw"></i>
</li>
<li v-if="registration.stage >= 1">
<MkForm :disabled="registration.stage != 1 || registration.saving">
<MkInput v-model="keyName" :max="30">
- <template #label>{{ $ts.securityKeyName }}</template>
+ <template #label>{{ i18n.ts.securityKeyName }}</template>
</MkInput>
- <MkButton :disabled="keyName.length == 0" @click="registerKey">{{ $ts.registerSecurityKey }}</MkButton>
+ <MkButton :disabled="keyName.length == 0" @click="registerKey">{{ i18n.ts.registerSecurityKey }}</MkButton>
<i v-if="registration.saving && registration.stage == 1" class="fas fa-spinner fa-pulse fa-fw"></i>
</MkForm>
</li>
</ol>
</template>
</template>
- <div v-if="data && !$i.twoFactorEnabled">
+ <div v-if="twoFactorData && !$i.twoFactorEnabled">
<ol style="margin: 0; padding: 0 0 0 1em;">
<li>
- <I18n :src="$ts._2fa.step1" tag="span">
+ <I18n :src="i18n.ts._2fa.step1" tag="span">
<template #a>
<a href="https://authy.com/" rel="noopener" target="_blank" class="_link">Authy</a>
</template>
@@ -52,19 +52,20 @@
</template>
</I18n>
</li>
- <li>{{ $ts._2fa.step2 }}<br><img :src="data.qr"></li>
- <li>{{ $ts._2fa.step3 }}<br>
- <MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false"><template #label>{{ $ts.token }}</template></MkInput>
- <MkButton primary @click="submit">{{ $ts.done }}</MkButton>
+ <li>{{ i18n.ts._2fa.step2 }}<br><img :src="twoFactorData.qr"><p>{{ $ts._2fa.step2Url }}<br>{{ twoFactorData.url }}</p></li>
+ <li>
+ {{ i18n.ts._2fa.step3 }}<br>
+ <MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false"><template #label>{{ i18n.ts.token }}</template></MkInput>
+ <MkButton primary @click="submit">{{ i18n.ts.done }}</MkButton>
</li>
</ol>
- <MkInfo>{{ $ts._2fa.step4 }}</MkInfo>
+ <MkInfo>{{ i18n.ts._2fa.step4 }}</MkInfo>
</div>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { ref } from 'vue';
import { hostname } from '@/config';
import { byteify, hexify, stringify } from '@/scripts/2fa';
import MkButton from '@/components/ui/button.vue';
@@ -72,155 +73,144 @@ import MkInfo from '@/components/ui/info.vue';
import MkInput from '@/components/form/input.vue';
import MkSwitch from '@/components/form/switch.vue';
import * as os from '@/os';
-import * as symbols from '@/symbols';
+import { $i } from '@/account';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton, MkInfo, MkInput, MkSwitch
- },
+const twoFactorData = ref<any>(null);
+const supportsCredentials = ref(!!navigator.credentials);
+const usePasswordLessLogin = ref($i!.usePasswordLessLogin);
+const registration = ref<any>(null);
+const keyName = ref('');
+const token = ref(null);
- data() {
- return {
- data: null,
- supportsCredentials: !!navigator.credentials,
- usePasswordLessLogin: this.$i.usePasswordLessLogin,
- registration: null,
- keyName: '',
- token: null,
- };
- },
+function register() {
+ os.inputText({
+ title: i18n.ts.password,
+ type: 'password'
+ }).then(({ canceled, result: password }) => {
+ if (canceled) return;
+ os.api('i/2fa/register', {
+ password: password
+ }).then(data => {
+ twoFactorData.value = data;
+ });
+ });
+}
- methods: {
- register() {
- os.inputText({
- title: this.$ts.password,
- type: 'password'
- }).then(({ canceled, result: password }) => {
- if (canceled) return;
- os.api('i/2fa/register', {
- password: password
- }).then(data => {
- this.data = data;
- });
- });
- },
+function unregister() {
+ os.inputText({
+ title: i18n.ts.password,
+ type: 'password'
+ }).then(({ canceled, result: password }) => {
+ if (canceled) return;
+ os.api('i/2fa/unregister', {
+ password: password
+ }).then(() => {
+ usePasswordLessLogin.value = false;
+ updatePasswordLessLogin();
+ }).then(() => {
+ os.success();
+ $i!.twoFactorEnabled = false;
+ });
+ });
+}
- unregister() {
- os.inputText({
- title: this.$ts.password,
- type: 'password'
- }).then(({ canceled, result: password }) => {
- if (canceled) return;
- os.api('i/2fa/unregister', {
- password: password
- }).then(() => {
- this.usePasswordLessLogin = false;
- this.updatePasswordLessLogin();
- }).then(() => {
- os.success();
- this.$i.twoFactorEnabled = false;
- });
- });
- },
+function submit() {
+ os.api('i/2fa/done', {
+ token: token.value
+ }).then(() => {
+ os.success();
+ $i!.twoFactorEnabled = true;
+ }).catch(err => {
+ os.alert({
+ type: 'error',
+ text: err,
+ });
+ });
+}
- submit() {
- os.api('i/2fa/done', {
- token: this.token
- }).then(() => {
- os.success();
- this.$i.twoFactorEnabled = true;
- }).catch(e => {
- os.alert({
- type: 'error',
- text: e
- });
- });
- },
+function registerKey() {
+ registration.value.saving = true;
+ os.api('i/2fa/key-done', {
+ password: registration.value.password,
+ name: keyName.value,
+ challengeId: registration.value.challengeId,
+ // we convert each 16 bits to a string to serialise
+ clientDataJSON: stringify(registration.value.credential.response.clientDataJSON),
+ attestationObject: hexify(registration.value.credential.response.attestationObject)
+ }).then(key => {
+ registration.value = null;
+ key.lastUsed = new Date();
+ os.success();
+ });
+}
- registerKey() {
- this.registration.saving = true;
- os.api('i/2fa/key-done', {
- password: this.registration.password,
- name: this.keyName,
- challengeId: this.registration.challengeId,
- // we convert each 16 bits to a string to serialise
- clientDataJSON: stringify(this.registration.credential.response.clientDataJSON),
- attestationObject: hexify(this.registration.credential.response.attestationObject)
- }).then(key => {
- this.registration = null;
- key.lastUsed = new Date();
- os.success();
- })
- },
+function unregisterKey(key) {
+ os.inputText({
+ title: i18n.ts.password,
+ type: 'password'
+ }).then(({ canceled, result: password }) => {
+ if (canceled) return;
+ return os.api('i/2fa/remove-key', {
+ password,
+ credentialId: key.id
+ }).then(() => {
+ usePasswordLessLogin.value = false;
+ updatePasswordLessLogin();
+ }).then(() => {
+ os.success();
+ });
+ });
+}
- unregisterKey(key) {
- os.inputText({
- title: this.$ts.password,
- type: 'password'
- }).then(({ canceled, result: password }) => {
- if (canceled) return;
- return os.api('i/2fa/remove-key', {
- password,
- credentialId: key.id
- }).then(() => {
- this.usePasswordLessLogin = false;
- this.updatePasswordLessLogin();
- }).then(() => {
- os.success();
- });
+function addSecurityKey() {
+ os.inputText({
+ title: i18n.ts.password,
+ type: 'password'
+ }).then(({ canceled, result: password }) => {
+ if (canceled) return;
+ os.api('i/2fa/register-key', {
+ password
+ }).then(reg => {
+ registration.value = {
+ password,
+ challengeId: reg!.challengeId,
+ stage: 0,
+ publicKeyOptions: {
+ challenge: byteify(reg!.challenge, 'base64'),
+ rp: {
+ id: hostname,
+ name: 'Misskey'
+ },
+ user: {
+ id: byteify($i!.id, 'ascii'),
+ name: $i!.username,
+ displayName: $i!.name,
+ },
+ pubKeyCredParams: [{ alg: -7, type: 'public-key' }],
+ timeout: 60000,
+ attestation: 'direct'
+ },
+ saving: true
+ };
+ return navigator.credentials.create({
+ publicKey: registration.value.publicKeyOptions
});
- },
+ }).then(credential => {
+ registration.value.credential = credential;
+ registration.value.saving = false;
+ registration.value.stage = 1;
+ }).catch(err => {
+ console.warn('Error while registering?', err);
+ registration.value.error = err.message;
+ registration.value.stage = -1;
+ });
+ });
+}
- addSecurityKey() {
- os.inputText({
- title: this.$ts.password,
- type: 'password'
- }).then(({ canceled, result: password }) => {
- if (canceled) return;
- os.api('i/2fa/register-key', {
- password
- }).then(registration => {
- this.registration = {
- password,
- challengeId: registration.challengeId,
- stage: 0,
- publicKeyOptions: {
- challenge: byteify(registration.challenge, 'base64'),
- rp: {
- id: hostname,
- name: 'Misskey'
- },
- user: {
- id: byteify(this.$i.id, 'ascii'),
- name: this.$i.username,
- displayName: this.$i.name,
- },
- pubKeyCredParams: [{ alg: -7, type: 'public-key' }],
- timeout: 60000,
- attestation: 'direct'
- },
- saving: true
- };
- return navigator.credentials.create({
- publicKey: this.registration.publicKeyOptions
- });
- }).then(credential => {
- this.registration.credential = credential;
- this.registration.saving = false;
- this.registration.stage = 1;
- }).catch(err => {
- console.warn('Error while registering?', err);
- this.registration.error = err.message;
- this.registration.stage = -1;
- });
- });
- },
-
- updatePasswordLessLogin() {
- os.api('i/2fa/password-less', {
- value: !!this.usePasswordLessLogin
- });
- }
- }
-});
+async function updatePasswordLessLogin() {
+ await os.api('i/2fa/password-less', {
+ value: !!usePasswordLessLogin.value
+ });
+}
</script>
diff --git a/packages/client/src/pages/settings/account-info.vue b/packages/client/src/pages/settings/account-info.vue
index c98ad056f6..12142b4dc1 100644
--- a/packages/client/src/pages/settings/account-info.vue
+++ b/packages/client/src/pages/settings/account-info.vue
@@ -7,163 +7,150 @@
<FormSection>
<MkKeyValue>
- <template #key>{{ $ts.registeredDate }}</template>
+ <template #key>{{ i18n.ts.registeredDate }}</template>
<template #value><MkTime :time="$i.createdAt" mode="detail"/></template>
</MkKeyValue>
</FormSection>
<FormSection v-if="stats">
- <template #label>{{ $ts.statistics }}</template>
+ <template #label>{{ i18n.ts.statistics }}</template>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.notesCount }}</template>
+ <template #key>{{ i18n.ts.notesCount }}</template>
<template #value>{{ number(stats.notesCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.repliesCount }}</template>
+ <template #key>{{ i18n.ts.repliesCount }}</template>
<template #value>{{ number(stats.repliesCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.renotesCount }}</template>
+ <template #key>{{ i18n.ts.renotesCount }}</template>
<template #value>{{ number(stats.renotesCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.repliedCount }}</template>
+ <template #key>{{ i18n.ts.repliedCount }}</template>
<template #value>{{ number(stats.repliedCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.renotedCount }}</template>
+ <template #key>{{ i18n.ts.renotedCount }}</template>
<template #value>{{ number(stats.renotedCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.pollVotesCount }}</template>
+ <template #key>{{ i18n.ts.pollVotesCount }}</template>
<template #value>{{ number(stats.pollVotesCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.pollVotedCount }}</template>
+ <template #key>{{ i18n.ts.pollVotedCount }}</template>
<template #value>{{ number(stats.pollVotedCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.sentReactionsCount }}</template>
+ <template #key>{{ i18n.ts.sentReactionsCount }}</template>
<template #value>{{ number(stats.sentReactionsCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.receivedReactionsCount }}</template>
+ <template #key>{{ i18n.ts.receivedReactionsCount }}</template>
<template #value>{{ number(stats.receivedReactionsCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.noteFavoritesCount }}</template>
+ <template #key>{{ i18n.ts.noteFavoritesCount }}</template>
<template #value>{{ number(stats.noteFavoritesCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.followingCount }}</template>
+ <template #key>{{ i18n.ts.followingCount }}</template>
<template #value>{{ number(stats.followingCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.followingCount }} ({{ $ts.local }})</template>
+ <template #key>{{ i18n.ts.followingCount }} ({{ i18n.ts.local }})</template>
<template #value>{{ number(stats.localFollowingCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.followingCount }} ({{ $ts.remote }})</template>
+ <template #key>{{ i18n.ts.followingCount }} ({{ i18n.ts.remote }})</template>
<template #value>{{ number(stats.remoteFollowingCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.followersCount }}</template>
+ <template #key>{{ i18n.ts.followersCount }}</template>
<template #value>{{ number(stats.followersCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.followersCount }} ({{ $ts.local }})</template>
+ <template #key>{{ i18n.ts.followersCount }} ({{ i18n.ts.local }})</template>
<template #value>{{ number(stats.localFollowersCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.followersCount }} ({{ $ts.remote }})</template>
+ <template #key>{{ i18n.ts.followersCount }} ({{ i18n.ts.remote }})</template>
<template #value>{{ number(stats.remoteFollowersCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.pageLikesCount }}</template>
+ <template #key>{{ i18n.ts.pageLikesCount }}</template>
<template #value>{{ number(stats.pageLikesCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.pageLikedCount }}</template>
+ <template #key>{{ i18n.ts.pageLikedCount }}</template>
<template #value>{{ number(stats.pageLikedCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.driveFilesCount }}</template>
+ <template #key>{{ i18n.ts.driveFilesCount }}</template>
<template #value>{{ number(stats.driveFilesCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.driveUsage }}</template>
+ <template #key>{{ i18n.ts.driveUsage }}</template>
<template #value>{{ bytes(stats.driveUsage) }}</template>
</MkKeyValue>
</FormSection>
<FormSection>
- <template #label>{{ $ts.other }}</template>
+ <template #label>{{ i18n.ts.other }}</template>
<MkKeyValue oneline style="margin: 1em 0;">
<template #key>emailVerified</template>
- <template #value>{{ $i.emailVerified ? $ts.yes : $ts.no }}</template>
+ <template #value>{{ $i.emailVerified ? i18n.ts.yes : i18n.ts.no }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
<template #key>twoFactorEnabled</template>
- <template #value>{{ $i.twoFactorEnabled ? $ts.yes : $ts.no }}</template>
+ <template #value>{{ $i.twoFactorEnabled ? i18n.ts.yes : i18n.ts.no }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
<template #key>securityKeys</template>
- <template #value>{{ $i.securityKeys ? $ts.yes : $ts.no }}</template>
+ <template #value>{{ $i.securityKeys ? i18n.ts.yes : i18n.ts.no }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
<template #key>usePasswordLessLogin</template>
- <template #value>{{ $i.usePasswordLessLogin ? $ts.yes : $ts.no }}</template>
+ <template #value>{{ $i.usePasswordLessLogin ? i18n.ts.yes : i18n.ts.no }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
<template #key>isModerator</template>
- <template #value>{{ $i.isModerator ? $ts.yes : $ts.no }}</template>
+ <template #value>{{ $i.isModerator ? i18n.ts.yes : i18n.ts.no }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
<template #key>isAdmin</template>
- <template #value>{{ $i.isAdmin ? $ts.yes : $ts.no }}</template>
+ <template #value>{{ $i.isAdmin ? i18n.ts.yes : i18n.ts.no }}</template>
</MkKeyValue>
</FormSection>
</div>
</template>
-<script lang="ts">
-import { defineAsyncComponent, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, onMounted, ref } from 'vue';
import FormSection from '@/components/form/section.vue';
import MkKeyValue from '@/components/key-value.vue';
import * as os from '@/os';
import number from '@/filters/number';
import bytes from '@/filters/bytes';
import * as symbols from '@/symbols';
+import { $i } from '@/account';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSection,
- MkKeyValue,
- },
+const stats = ref<any>({});
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.accountInfo,
- icon: 'fas fa-info-circle'
- },
- stats: null
- }
- },
-
- mounted() {
- os.api('users/stats', {
- userId: this.$i.id
- }).then(stats => {
- this.stats = stats;
- });
- },
+onMounted(() => {
+ os.api('users/stats', {
+ userId: $i!.id
+ }).then(response => {
+ stats.value = response;
+ });
+});
- methods: {
- number,
- bytes,
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.accountInfo,
+ icon: 'fas fa-info-circle'
}
});
</script>
diff --git a/packages/client/src/pages/settings/accounts.vue b/packages/client/src/pages/settings/accounts.vue
index a744a031d4..5e75639c55 100644
--- a/packages/client/src/pages/settings/accounts.vue
+++ b/packages/client/src/pages/settings/accounts.vue
@@ -1,7 +1,7 @@
<template>
<div class="_formRoot">
<FormSuspense :p="init">
- <FormButton primary @click="addAccount"><i class="fas fa-plus"></i> {{ $ts.addAccount }}</FormButton>
+ <FormButton primary @click="addAccount"><i class="fas fa-plus"></i> {{ i18n.ts.addAccount }}</FormButton>
<div v-for="account in accounts" :key="account.id" class="_panel _button lcjjdxlm" @click="menu(account, $event)">
<div class="avatar">
@@ -20,90 +20,89 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineAsyncComponent, defineExpose, ref } from 'vue';
import FormSuspense from '@/components/form/suspense.vue';
import FormButton from '@/components/ui/button.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
-import { getAccounts, addAccount, login } from '@/account';
+import { getAccounts, addAccount as addAccounts, login, $i } from '@/account';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSuspense,
- FormButton,
- },
+const storedAccounts = ref<any>(null);
+const accounts = ref<any>(null);
- emits: ['info'],
+const init = async () => {
+ getAccounts().then(accounts => {
+ storedAccounts.value = accounts.filter(x => x.id !== $i!.id);
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.accounts,
- icon: 'fas fa-users',
- bg: 'var(--bg)',
- },
- storedAccounts: getAccounts().then(accounts => accounts.filter(x => x.id !== this.$i.id)),
- accounts: null,
- init: async () => os.api('users/show', {
- userIds: (await this.storedAccounts).map(x => x.id)
- }).then(accounts => {
- this.accounts = accounts;
- }),
- };
- },
+ console.log(storedAccounts.value);
- methods: {
- menu(account, ev) {
- os.popupMenu([{
- text: this.$ts.switch,
- icon: 'fas fa-exchange-alt',
- action: () => this.switchAccount(account),
- }, {
- text: this.$ts.remove,
- icon: 'fas fa-trash-alt',
- danger: true,
- action: () => this.removeAccount(account),
- }], ev.currentTarget ?? ev.target);
- },
+ return os.api('users/show', {
+ userIds: storedAccounts.value.map(x => x.id)
+ });
+ }).then(response => {
+ accounts.value = response;
+ console.log(accounts.value);
+ });
+};
- addAccount(ev) {
- os.popupMenu([{
- text: this.$ts.existingAccount,
- action: () => { this.addExistingAccount(); },
- }, {
- text: this.$ts.createAccount,
- action: () => { this.createAccount(); },
- }], ev.currentTarget ?? ev.target);
- },
+function menu(account, ev) {
+ os.popupMenu([{
+ text: i18n.ts.switch,
+ icon: 'fas fa-exchange-alt',
+ action: () => switchAccount(account),
+ }, {
+ text: i18n.ts.remove,
+ icon: 'fas fa-trash-alt',
+ danger: true,
+ action: () => removeAccount(account),
+ }], ev.currentTarget ?? ev.target);
+}
- addExistingAccount() {
- os.popup(import('@/components/signin-dialog.vue'), {}, {
- done: res => {
- addAccount(res.id, res.i);
- os.success();
- },
- }, 'closed');
- },
+function addAccount(ev) {
+ os.popupMenu([{
+ text: i18n.ts.existingAccount,
+ action: () => { addExistingAccount(); },
+ }, {
+ text: i18n.ts.createAccount,
+ action: () => { createAccount(); },
+ }], ev.currentTarget ?? ev.target);
+}
- createAccount() {
- os.popup(import('@/components/signup-dialog.vue'), {}, {
- done: res => {
- addAccount(res.id, res.i);
- this.switchAccountWithToken(res.i);
- },
- }, 'closed');
+function addExistingAccount() {
+ os.popup(defineAsyncComponent(() => import('@/components/signin-dialog.vue')), {}, {
+ done: res => {
+ addAccounts(res.id, res.i);
+ os.success();
},
+ }, 'closed');
+}
- async switchAccount(account: any) {
- const storedAccounts = await getAccounts();
- const token = storedAccounts.find(x => x.id === account.id).token;
- this.switchAccountWithToken(token);
+function createAccount() {
+ os.popup(defineAsyncComponent(() => import('@/components/signup-dialog.vue')), {}, {
+ done: res => {
+ addAccounts(res.id, res.i);
+ switchAccountWithToken(res.i);
},
+ }, 'closed');
+}
- switchAccountWithToken(token: string) {
- login(token);
- },
+async function switchAccount(account: any) {
+ const fetchedAccounts: any[] = await getAccounts();
+ const token = fetchedAccounts.find(x => x.id === account.id).token;
+ switchAccountWithToken(token);
+}
+
+function switchAccountWithToken(token: string) {
+ login(token);
+}
+
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.accounts,
+ icon: 'fas fa-users',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/api.vue b/packages/client/src/pages/settings/api.vue
index 20ff2a8d96..e6375763f1 100644
--- a/packages/client/src/pages/settings/api.vue
+++ b/packages/client/src/pages/settings/api.vue
@@ -1,56 +1,45 @@
<template>
<div class="_formRoot">
- <FormButton primary class="_formBlock" @click="generateToken">{{ $ts.generateAccessToken }}</FormButton>
- <FormLink to="/settings/apps" class="_formBlock">{{ $ts.manageAccessTokens }}</FormLink>
+ <FormButton primary class="_formBlock" @click="generateToken">{{ i18n.ts.generateAccessToken }}</FormButton>
+ <FormLink to="/settings/apps" class="_formBlock">{{ i18n.ts.manageAccessTokens }}</FormLink>
<FormLink to="/api-console" :behavior="isDesktop ? 'window' : null" class="_formBlock">API console</FormLink>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineAsyncComponent, defineExpose, ref } from 'vue';
import FormLink from '@/components/form/link.vue';
import FormButton from '@/components/ui/button.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormButton,
- FormLink,
- },
+const isDesktop = ref(window.innerWidth >= 1100);
- emits: ['info'],
+function generateToken() {
+ os.popup(defineAsyncComponent(() => import('@/components/token-generate-window.vue')), {}, {
+ done: async result => {
+ const { name, permissions } = result;
+ const { token } = await os.api('miauth/gen-token', {
+ session: null,
+ name: name,
+ permission: permissions,
+ });
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: 'API',
- icon: 'fas fa-key',
- bg: 'var(--bg)',
- },
- isDesktop: window.innerWidth >= 1100,
- };
- },
-
- methods: {
- generateToken() {
- os.popup(import('@/components/token-generate-window.vue'), {}, {
- done: async result => {
- const { name, permissions } = result;
- const { token } = await os.api('miauth/gen-token', {
- session: null,
- name: name,
- permission: permissions,
- });
-
- os.alert({
- type: 'success',
- title: this.$ts.token,
- text: token
- });
- },
- }, 'closed');
+ os.alert({
+ type: 'success',
+ title: i18n.ts.token,
+ text: token
+ });
},
+ }, 'closed');
+}
+
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: 'API',
+ icon: 'fas fa-key',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/apps.vue b/packages/client/src/pages/settings/apps.vue
index 9c0fa8a54d..7b0b5548d5 100644
--- a/packages/client/src/pages/settings/apps.vue
+++ b/packages/client/src/pages/settings/apps.vue
@@ -4,7 +4,7 @@
<template #empty>
<div class="_fullinfo">
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
- <div>{{ $ts.nothing }}</div>
+ <div>{{ i18n.ts.nothing }}</div>
</div>
</template>
<template v-slot="{items}">
@@ -14,18 +14,18 @@
<div class="name">{{ token.name }}</div>
<div class="description">{{ token.description }}</div>
<div class="_keyValue">
- <div>{{ $ts.installedDate }}:</div>
+ <div>{{ i18n.ts.installedDate }}:</div>
<div><MkTime :time="token.createdAt"/></div>
</div>
<div class="_keyValue">
- <div>{{ $ts.lastUsedDate }}:</div>
+ <div>{{ i18n.ts.lastUsedDate }}:</div>
<div><MkTime :time="token.lastUsedAt"/></div>
</div>
<div class="actions">
<button class="_button" @click="revoke(token)"><i class="fas fa-trash-alt"></i></button>
</div>
<details>
- <summary>{{ $ts.details }}</summary>
+ <summary>{{ i18n.ts.details }}</summary>
<ul>
<li v-for="p in token.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li>
</ul>
@@ -37,42 +37,34 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, ref } from 'vue';
import FormPagination from '@/components/ui/pagination.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormPagination,
- },
+const list = ref<any>(null);
- emits: ['info'],
+const pagination = {
+ endpoint: 'i/apps' as const,
+ limit: 100,
+ params: {
+ sort: '+lastUsedAt'
+ }
+};
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.installedApps,
- icon: 'fas fa-plug',
- bg: 'var(--bg)',
- },
- pagination: {
- endpoint: 'i/apps' as const,
- limit: 100,
- params: {
- sort: '+lastUsedAt'
- }
- },
- };
- },
+function revoke(token) {
+ os.api('i/revoke-token', { tokenId: token.id }).then(() => {
+ list.value.reload();
+ });
+}
- methods: {
- revoke(token) {
- os.api('i/revoke-token', { tokenId: token.id }).then(() => {
- this.$refs.list.reload();
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.installedApps,
+ icon: 'fas fa-plug',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/custom-css.vue b/packages/client/src/pages/settings/custom-css.vue
index 556ee30c1d..20db077ceb 100644
--- a/packages/client/src/pages/settings/custom-css.vue
+++ b/packages/client/src/pages/settings/custom-css.vue
@@ -1,6 +1,6 @@
<template>
<div class="_formRoot">
- <FormInfo warn class="_formBlock">{{ $ts.customCssWarn }}</FormInfo>
+ <FormInfo warn class="_formBlock">{{ i18n.ts.customCssWarn }}</FormInfo>
<FormTextarea v-model="localCustomCss" manual-save tall class="_monospace _formBlock" style="tab-size: 2;">
<template #label>CSS</template>
@@ -8,50 +8,38 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, ref, watch } from 'vue';
import FormTextarea from '@/components/form/textarea.vue';
import FormInfo from '@/components/ui/info.vue';
import * as os from '@/os';
import { unisonReload } from '@/scripts/unison-reload';
import * as symbols from '@/symbols';
-import { defaultStore } from '@/store';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormTextarea,
- FormInfo,
- },
+const localCustomCss = ref(localStorage.getItem('customCss') ?? '');
- emits: ['info'],
+async function apply() {
+ localStorage.setItem('customCss', localCustomCss.value);
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.customCss,
- icon: 'fas fa-code',
- bg: 'var(--bg)',
- },
- localCustomCss: localStorage.getItem('customCss')
- }
- },
+ const { canceled } = await os.confirm({
+ type: 'info',
+ text: i18n.ts.reloadToApplySetting,
+ });
+ if (canceled) return;
- mounted() {
- this.$watch('localCustomCss', this.apply);
- },
+ unisonReload();
+}
- methods: {
- async apply() {
- localStorage.setItem('customCss', this.localCustomCss);
-
- const { canceled } = await os.confirm({
- type: 'info',
- text: this.$ts.reloadToApplySetting,
- });
- if (canceled) return;
+watch(localCustomCss, async () => {
+ await apply();
+});
- unisonReload();
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.customCss,
+ icon: 'fas fa-code',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/deck.vue b/packages/client/src/pages/settings/deck.vue
index 46b90d3d1a..2d868aa0a7 100644
--- a/packages/client/src/pages/settings/deck.vue
+++ b/packages/client/src/pages/settings/deck.vue
@@ -1,36 +1,36 @@
<template>
<div class="_formRoot">
<FormGroup>
- <template #label>{{ $ts.defaultNavigationBehaviour }}</template>
- <FormSwitch v-model="navWindow">{{ $ts.openInWindow }}</FormSwitch>
+ <template #label>{{ i18n.ts.defaultNavigationBehaviour }}</template>
+ <FormSwitch v-model="navWindow">{{ i18n.ts.openInWindow }}</FormSwitch>
</FormGroup>
- <FormSwitch v-model="alwaysShowMainColumn" class="_formBlock">{{ $ts._deck.alwaysShowMainColumn }}</FormSwitch>
+ <FormSwitch v-model="alwaysShowMainColumn" class="_formBlock">{{ i18n.ts._deck.alwaysShowMainColumn }}</FormSwitch>
<FormRadios v-model="columnAlign" class="_formBlock">
- <template #label>{{ $ts._deck.columnAlign }}</template>
- <option value="left">{{ $ts.left }}</option>
- <option value="center">{{ $ts.center }}</option>
+ <template #label>{{ i18n.ts._deck.columnAlign }}</template>
+ <option value="left">{{ i18n.ts.left }}</option>
+ <option value="center">{{ i18n.ts.center }}</option>
</FormRadios>
<FormRadios v-model="columnHeaderHeight" class="_formBlock">
- <template #label>{{ $ts._deck.columnHeaderHeight }}</template>
- <option :value="42">{{ $ts.narrow }}</option>
- <option :value="45">{{ $ts.medium }}</option>
- <option :value="48">{{ $ts.wide }}</option>
+ <template #label>{{ i18n.ts._deck.columnHeaderHeight }}</template>
+ <option :value="42">{{ i18n.ts.narrow }}</option>
+ <option :value="45">{{ i18n.ts.medium }}</option>
+ <option :value="48">{{ i18n.ts.wide }}</option>
</FormRadios>
<FormInput v-model="columnMargin" type="number" class="_formBlock">
- <template #label>{{ $ts._deck.columnMargin }}</template>
+ <template #label>{{ i18n.ts._deck.columnMargin }}</template>
<template #suffix>px</template>
</FormInput>
- <FormLink class="_formBlock" @click="setProfile">{{ $ts._deck.profile }}<template #suffix>{{ profile }}</template></FormLink>
+ <FormLink class="_formBlock" @click="setProfile">{{ i18n.ts._deck.profile }}<template #suffix>{{ profile }}</template></FormLink>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { computed, defineExpose, watch } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
import FormLink from '@/components/form/link.vue';
import FormRadios from '@/components/form/radios.vue';
@@ -40,59 +40,41 @@ import { deckStore } from '@/ui/deck/deck-store';
import * as os from '@/os';
import { unisonReload } from '@/scripts/unison-reload';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSwitch,
- FormLink,
- FormInput,
- FormRadios,
- FormGroup,
- },
+const navWindow = computed(deckStore.makeGetterSetter('navWindow'));
+const alwaysShowMainColumn = computed(deckStore.makeGetterSetter('alwaysShowMainColumn'));
+const columnAlign = computed(deckStore.makeGetterSetter('columnAlign'));
+const columnMargin = computed(deckStore.makeGetterSetter('columnMargin'));
+const columnHeaderHeight = computed(deckStore.makeGetterSetter('columnHeaderHeight'));
+const profile = computed(deckStore.makeGetterSetter('profile'));
- emits: ['info'],
+watch(navWindow, async () => {
+ const { canceled } = await os.confirm({
+ type: 'info',
+ text: i18n.ts.reloadToApplySetting,
+ });
+ if (canceled) return;
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.deck,
- icon: 'fas fa-columns',
- bg: 'var(--bg)',
- },
- }
- },
-
- computed: {
- navWindow: deckStore.makeGetterSetter('navWindow'),
- alwaysShowMainColumn: deckStore.makeGetterSetter('alwaysShowMainColumn'),
- columnAlign: deckStore.makeGetterSetter('columnAlign'),
- columnMargin: deckStore.makeGetterSetter('columnMargin'),
- columnHeaderHeight: deckStore.makeGetterSetter('columnHeaderHeight'),
- profile: deckStore.makeGetterSetter('profile'),
- },
-
- watch: {
- async navWindow() {
- const { canceled } = await os.confirm({
- type: 'info',
- text: this.$ts.reloadToApplySetting,
- });
- if (canceled) return;
+ unisonReload();
+});
- unisonReload();
- }
- },
+async function setProfile() {
+ const { canceled, result: name } = await os.inputText({
+ title: i18n.ts._deck.profile,
+ allowEmpty: false
+ });
+ if (canceled) return;
+
+ profile.value = name;
+ unisonReload();
+}
- methods: {
- async setProfile() {
- const { canceled, result: name } = await os.inputText({
- title: this.$ts._deck.profile,
- allowEmpty: false
- });
- if (canceled) return;
- this.profile = name;
- unisonReload();
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.deck,
+ icon: 'fas fa-columns',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/delete-account.vue b/packages/client/src/pages/settings/delete-account.vue
index 7edc81a309..e9f19aaf0b 100644
--- a/packages/client/src/pages/settings/delete-account.vue
+++ b/packages/client/src/pages/settings/delete-account.vue
@@ -1,64 +1,52 @@
<template>
<div class="_formRoot">
- <FormInfo warn class="_formBlock">{{ $ts._accountDelete.mayTakeTime }}</FormInfo>
- <FormInfo class="_formBlock">{{ $ts._accountDelete.sendEmail }}</FormInfo>
- <FormButton v-if="!$i.isDeleted" danger class="_formBlock" @click="deleteAccount">{{ $ts._accountDelete.requestAccountDelete }}</FormButton>
- <FormButton v-else disabled>{{ $ts._accountDelete.inProgress }}</FormButton>
+ <FormInfo warn class="_formBlock">{{ i18n.ts._accountDelete.mayTakeTime }}</FormInfo>
+ <FormInfo class="_formBlock">{{ i18n.ts._accountDelete.sendEmail }}</FormInfo>
+ <FormButton v-if="!$i.isDeleted" danger class="_formBlock" @click="deleteAccount">{{ i18n.ts._accountDelete.requestAccountDelete }}</FormButton>
+ <FormButton v-else disabled>{{ i18n.ts._accountDelete.inProgress }}</FormButton>
</div>
</template>
-<script lang="ts">
-import { defineAsyncComponent, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose } from 'vue';
import FormInfo from '@/components/ui/info.vue';
import FormButton from '@/components/ui/button.vue';
import * as os from '@/os';
import { signout } from '@/account';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormButton,
- FormInfo,
- },
-
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts._accountDelete.accountDelete,
- icon: 'fas fa-exclamation-triangle',
- bg: 'var(--bg)',
- },
- }
- },
+async function deleteAccount() {
+ {
+ const { canceled } = await os.confirm({
+ type: 'warning',
+ text: i18n.ts.deleteAccountConfirm,
+ });
+ if (canceled) return;
+ }
- methods: {
- async deleteAccount() {
- {
- const { canceled } = await os.confirm({
- type: 'warning',
- text: this.$ts.deleteAccountConfirm,
- });
- if (canceled) return;
- }
+ const { canceled, result: password } = await os.inputText({
+ title: i18n.ts.password,
+ type: 'password'
+ });
+ if (canceled) return;
- const { canceled, result: password } = await os.inputText({
- title: this.$ts.password,
- type: 'password'
- });
- if (canceled) return;
+ await os.apiWithDialog('i/delete-account', {
+ password: password
+ });
- await os.apiWithDialog('i/delete-account', {
- password: password
- });
+ await os.alert({
+ title: i18n.ts._accountDelete.started,
+ });
- await os.alert({
- title: this.$ts._accountDelete.started,
- });
+ await signout();
+}
- signout();
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts._accountDelete.accountDelete,
+ icon: 'fas fa-exclamation-triangle',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/drive.vue b/packages/client/src/pages/settings/drive.vue
index 9309eb5ec7..09a2537ed5 100644
--- a/packages/client/src/pages/settings/drive.vue
+++ b/packages/client/src/pages/settings/drive.vue
@@ -1,41 +1,41 @@
<template>
<div class="_formRoot">
<FormSection v-if="!fetching">
- <template #label>{{ $ts.usageAmount }}</template>
+ <template #label>{{ i18n.ts.usageAmount }}</template>
<div class="_formBlock uawsfosz">
<div class="meter"><div :style="meterStyle"></div></div>
</div>
<FormSplit>
<MkKeyValue class="_formBlock">
- <template #key>{{ $ts.capacity }}</template>
+ <template #key>{{ i18n.ts.capacity }}</template>
<template #value>{{ bytes(capacity, 1) }}</template>
</MkKeyValue>
<MkKeyValue class="_formBlock">
- <template #key>{{ $ts.inUse }}</template>
+ <template #key>{{ i18n.ts.inUse }}</template>
<template #value>{{ bytes(usage, 1) }}</template>
</MkKeyValue>
</FormSplit>
</FormSection>
<FormSection>
- <template #label>{{ $ts.statistics }}</template>
+ <template #label>{{ i18n.ts.statistics }}</template>
<MkChart src="per-user-drive" :args="{ user: $i }" span="day" :limit="7 * 5" :bar="true" :stacked="true" :detailed="false" :aspect-ratio="6"/>
</FormSection>
<FormSection>
<FormLink @click="chooseUploadFolder()">
- {{ $ts.uploadFolder }}
+ {{ i18n.ts.uploadFolder }}
<template #suffix>{{ uploadFolder ? uploadFolder.name : '-' }}</template>
<template #suffixIcon><i class="fas fa-folder-open"></i></template>
</FormLink>
- <FormSwitch v-model="keepOriginalUploading" class="_formBlock">{{ $ts.keepOriginalUploading }}<template #caption>{{ $ts.keepOriginalUploadingDescription }}</template></FormSwitch>
+ <FormSwitch v-model="keepOriginalUploading" class="_formBlock">{{ i18n.ts.keepOriginalUploading }}<template #caption>{{ i18n.ts.keepOriginalUploadingDescription }}</template></FormSwitch>
</FormSection>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
-import * as tinycolor from 'tinycolor2';
+<script lang="ts" setup>
+import { computed, defineExpose, ref } from 'vue';
+import tinycolor from 'tinycolor2';
import FormLink from '@/components/form/link.vue';
import FormSwitch from '@/components/form/switch.vue';
import FormSection from '@/components/form/section.vue';
@@ -46,80 +46,59 @@ import bytes from '@/filters/bytes';
import * as symbols from '@/symbols';
import { defaultStore } from '@/store';
import MkChart from '@/components/chart.vue';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormLink,
- FormSwitch,
- FormSection,
- MkKeyValue,
- FormSplit,
- MkChart,
- },
+const fetching = ref(true);
+const usage = ref<any>(null);
+const capacity = ref<any>(null);
+const uploadFolder = ref<any>(null);
- emits: ['info'],
+const meterStyle = computed(() => {
+ return {
+ width: `${usage.value / capacity.value * 100}%`,
+ background: tinycolor({
+ h: 180 - (usage.value / capacity.value * 180),
+ s: 0.7,
+ l: 0.5
+ })
+ };
+});
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.drive,
- icon: 'fas fa-cloud',
- bg: 'var(--bg)',
- },
- fetching: true,
- usage: null,
- capacity: null,
- uploadFolder: null,
- }
- },
+const keepOriginalUploading = computed(defaultStore.makeGetterSetter('keepOriginalUploading'));
- computed: {
- meterStyle(): any {
- return {
- width: `${this.usage / this.capacity * 100}%`,
- background: tinycolor({
- h: 180 - (this.usage / this.capacity * 180),
- s: 0.7,
- l: 0.5
- })
- };
- },
- keepOriginalUploading: defaultStore.makeGetterSetter('keepOriginalUploading'),
- },
+os.api('drive').then(info => {
+ capacity.value = info.capacity;
+ usage.value = info.usage;
+ fetching.value = false;
+});
- async created() {
- os.api('drive').then(info => {
- this.capacity = info.capacity;
- this.usage = info.usage;
- this.fetching = false;
- this.$nextTick(() => {
- this.renderChart();
- });
- });
+if (defaultStore.state.uploadFolder) {
+ os.api('drive/folders/show', {
+ folderId: defaultStore.state.uploadFolder
+ }).then(response => {
+ uploadFolder.value = response;
+ });
+}
- if (this.$store.state.uploadFolder) {
- this.uploadFolder = await os.api('drive/folders/show', {
- folderId: this.$store.state.uploadFolder
+function chooseUploadFolder() {
+ os.selectDriveFolder(false).then(async folder => {
+ defaultStore.set('uploadFolder', folder ? folder.id : null);
+ os.success();
+ if (defaultStore.state.uploadFolder) {
+ uploadFolder.value = await os.api('drive/folders/show', {
+ folderId: defaultStore.state.uploadFolder
});
+ } else {
+ uploadFolder.value = null;
}
- },
-
- methods: {
- chooseUploadFolder() {
- os.selectDriveFolder(false).then(async folder => {
- this.$store.set('uploadFolder', folder ? folder.id : null);
- os.success();
- if (this.$store.state.uploadFolder) {
- this.uploadFolder = await os.api('drive/folders/show', {
- folderId: this.$store.state.uploadFolder
- });
- } else {
- this.uploadFolder = null;
- }
- });
- },
+ });
+}
- bytes
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.drive,
+ icon: 'fas fa-cloud',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/email.vue b/packages/client/src/pages/settings/email.vue
index 4697fec9b7..37f14068e2 100644
--- a/packages/client/src/pages/settings/email.vue
+++ b/packages/client/src/pages/settings/email.vue
@@ -39,8 +39,8 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent, onMounted, ref, watch } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, onMounted, ref, watch } from 'vue';
import FormSection from '@/components/form/section.vue';
import FormInput from '@/components/form/input.vue';
import FormSwitch from '@/components/form/switch.vue';
@@ -49,79 +49,62 @@ import * as symbols from '@/symbols';
import { $i } from '@/account';
import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSection,
- FormSwitch,
- FormInput,
- },
+const emailAddress = ref($i!.email);
- emits: ['info'],
+const onChangeReceiveAnnouncementEmail = (v) => {
+ os.api('i/update', {
+ receiveAnnouncementEmail: v
+ });
+};
- setup(props, context) {
- const emailAddress = ref($i.email);
-
- const INFO = {
- title: i18n.ts.email,
- icon: 'fas fa-envelope',
- bg: 'var(--bg)',
- };
-
- const onChangeReceiveAnnouncementEmail = (v) => {
- os.api('i/update', {
- receiveAnnouncementEmail: v
- });
- };
-
- const saveEmailAddress = () => {
- os.inputText({
- title: i18n.ts.password,
- type: 'password'
- }).then(({ canceled, result: password }) => {
- if (canceled) return;
- os.apiWithDialog('i/update-email', {
- password: password,
- email: emailAddress.value,
- });
- });
- };
+const saveEmailAddress = () => {
+ os.inputText({
+ title: i18n.ts.password,
+ type: 'password'
+ }).then(({ canceled, result: password }) => {
+ if (canceled) return;
+ os.apiWithDialog('i/update-email', {
+ password: password,
+ email: emailAddress.value,
+ });
+ });
+};
- const emailNotification_mention = ref($i.emailNotificationTypes.includes('mention'));
- const emailNotification_reply = ref($i.emailNotificationTypes.includes('reply'));
- const emailNotification_quote = ref($i.emailNotificationTypes.includes('quote'));
- const emailNotification_follow = ref($i.emailNotificationTypes.includes('follow'));
- const emailNotification_receiveFollowRequest = ref($i.emailNotificationTypes.includes('receiveFollowRequest'));
- const emailNotification_groupInvited = ref($i.emailNotificationTypes.includes('groupInvited'));
+const emailNotification_mention = ref($i!.emailNotificationTypes.includes('mention'));
+const emailNotification_reply = ref($i!.emailNotificationTypes.includes('reply'));
+const emailNotification_quote = ref($i!.emailNotificationTypes.includes('quote'));
+const emailNotification_follow = ref($i!.emailNotificationTypes.includes('follow'));
+const emailNotification_receiveFollowRequest = ref($i!.emailNotificationTypes.includes('receiveFollowRequest'));
+const emailNotification_groupInvited = ref($i!.emailNotificationTypes.includes('groupInvited'));
- const saveNotificationSettings = () => {
- os.api('i/update', {
- emailNotificationTypes: [
- ...[emailNotification_mention.value ? 'mention' : null],
- ...[emailNotification_reply.value ? 'reply' : null],
- ...[emailNotification_quote.value ? 'quote' : null],
- ...[emailNotification_follow.value ? 'follow' : null],
- ...[emailNotification_receiveFollowRequest.value ? 'receiveFollowRequest' : null],
- ...[emailNotification_groupInvited.value ? 'groupInvited' : null],
- ].filter(x => x != null)
- });
- };
+const saveNotificationSettings = () => {
+ os.api('i/update', {
+ emailNotificationTypes: [
+ ...[emailNotification_mention.value ? 'mention' : null],
+ ...[emailNotification_reply.value ? 'reply' : null],
+ ...[emailNotification_quote.value ? 'quote' : null],
+ ...[emailNotification_follow.value ? 'follow' : null],
+ ...[emailNotification_receiveFollowRequest.value ? 'receiveFollowRequest' : null],
+ ...[emailNotification_groupInvited.value ? 'groupInvited' : null],
+ ].filter(x => x != null)
+ });
+};
- watch([emailNotification_mention, emailNotification_reply, emailNotification_quote, emailNotification_follow, emailNotification_receiveFollowRequest, emailNotification_groupInvited], () => {
- saveNotificationSettings();
- });
+watch([emailNotification_mention, emailNotification_reply, emailNotification_quote, emailNotification_follow, emailNotification_receiveFollowRequest, emailNotification_groupInvited], () => {
+ saveNotificationSettings();
+});
- onMounted(() => {
- watch(emailAddress, () => {
- saveEmailAddress();
- });
- });
+onMounted(() => {
+ watch(emailAddress, () => {
+ saveEmailAddress();
+ });
+});
- return {
- [symbols.PAGE_INFO]: INFO,
- emailAddress,
- onChangeReceiveAnnouncementEmail,
- emailNotification_mention, emailNotification_reply, emailNotification_quote, emailNotification_follow, emailNotification_receiveFollowRequest, emailNotification_groupInvited,
- };
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.email,
+ icon: 'fas fa-envelope',
+ bg: 'var(--bg)',
+ }
});
</script>
diff --git a/packages/client/src/pages/settings/general.vue b/packages/client/src/pages/settings/general.vue
index c8f6f58322..64b8cc3106 100644
--- a/packages/client/src/pages/settings/general.vue
+++ b/packages/client/src/pages/settings/general.vue
@@ -1,10 +1,10 @@
<template>
<div class="_formRoot">
<FormSelect v-model="lang" class="_formBlock">
- <template #label>{{ $ts.uiLanguage }}</template>
+ <template #label>{{ i18n.ts.uiLanguage }}</template>
<option v-for="x in langs" :key="x[0]" :value="x[0]">{{ x[1] }}</option>
<template #caption>
- <I18n :src="$ts.i18nInfo" tag="span">
+ <I18n :src="i18n.ts.i18nInfo" tag="span">
<template #link>
<MkLink url="https://crowdin.com/project/misskey">Crowdin</MkLink>
</template>
@@ -13,48 +13,48 @@
</FormSelect>
<FormRadios v-model="overridedDeviceKind" class="_formBlock">
- <template #label>{{ $ts.overridedDeviceKind }}</template>
- <option :value="null">{{ $ts.auto }}</option>
- <option value="smartphone"><i class="fas fa-mobile-alt"/> {{ $ts.smartphone }}</option>
- <option value="tablet"><i class="fas fa-tablet-alt"/> {{ $ts.tablet }}</option>
- <option value="desktop"><i class="fas fa-desktop"/> {{ $ts.desktop }}</option>
+ <template #label>{{ i18n.ts.overridedDeviceKind }}</template>
+ <option :value="null">{{ i18n.ts.auto }}</option>
+ <option value="smartphone"><i class="fas fa-mobile-alt"/> {{ i18n.ts.smartphone }}</option>
+ <option value="tablet"><i class="fas fa-tablet-alt"/> {{ i18n.ts.tablet }}</option>
+ <option value="desktop"><i class="fas fa-desktop"/> {{ i18n.ts.desktop }}</option>
</FormRadios>
- <FormSwitch v-model="showFixedPostForm" class="_formBlock">{{ $ts.showFixedPostForm }}</FormSwitch>
+ <FormSwitch v-model="showFixedPostForm" class="_formBlock">{{ i18n.ts.showFixedPostForm }}</FormSwitch>
<FormSection>
- <template #label>{{ $ts.behavior }}</template>
- <FormSwitch v-model="imageNewTab" class="_formBlock">{{ $ts.openImageInNewTab }}</FormSwitch>
- <FormSwitch v-model="enableInfiniteScroll" class="_formBlock">{{ $ts.enableInfiniteScroll }}</FormSwitch>
- <FormSwitch v-model="useReactionPickerForContextMenu" class="_formBlock">{{ $ts.useReactionPickerForContextMenu }}</FormSwitch>
- <FormSwitch v-model="disablePagesScript" class="_formBlock">{{ $ts.disablePagesScript }}</FormSwitch>
+ <template #label>{{ i18n.ts.behavior }}</template>
+ <FormSwitch v-model="imageNewTab" class="_formBlock">{{ i18n.ts.openImageInNewTab }}</FormSwitch>
+ <FormSwitch v-model="enableInfiniteScroll" class="_formBlock">{{ i18n.ts.enableInfiniteScroll }}</FormSwitch>
+ <FormSwitch v-model="useReactionPickerForContextMenu" class="_formBlock">{{ i18n.ts.useReactionPickerForContextMenu }}</FormSwitch>
+ <FormSwitch v-model="disablePagesScript" class="_formBlock">{{ i18n.ts.disablePagesScript }}</FormSwitch>
<FormSelect v-model="serverDisconnectedBehavior" class="_formBlock">
- <template #label>{{ $ts.whenServerDisconnected }}</template>
- <option value="reload">{{ $ts._serverDisconnectedBehavior.reload }}</option>
- <option value="dialog">{{ $ts._serverDisconnectedBehavior.dialog }}</option>
- <option value="quiet">{{ $ts._serverDisconnectedBehavior.quiet }}</option>
+ <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>
</FormSelect>
</FormSection>
<FormSection>
- <template #label>{{ $ts.appearance }}</template>
- <FormSwitch v-model="disableAnimatedMfm" class="_formBlock">{{ $ts.disableAnimatedMfm }}</FormSwitch>
- <FormSwitch v-model="reduceAnimation" class="_formBlock">{{ $ts.reduceUiAnimation }}</FormSwitch>
- <FormSwitch v-model="useBlurEffect" class="_formBlock">{{ $ts.useBlurEffect }}</FormSwitch>
- <FormSwitch v-model="useBlurEffectForModal" class="_formBlock">{{ $ts.useBlurEffectForModal }}</FormSwitch>
- <FormSwitch v-model="showGapBetweenNotesInTimeline" class="_formBlock">{{ $ts.showGapBetweenNotesInTimeline }}</FormSwitch>
- <FormSwitch v-model="loadRawImages" class="_formBlock">{{ $ts.loadRawImages }}</FormSwitch>
- <FormSwitch v-model="disableShowingAnimatedImages" class="_formBlock">{{ $ts.disableShowingAnimatedImages }}</FormSwitch>
- <FormSwitch v-model="squareAvatars" class="_formBlock">{{ $ts.squareAvatars }}</FormSwitch>
- <FormSwitch v-model="useSystemFont" class="_formBlock">{{ $ts.useSystemFont }}</FormSwitch>
- <FormSwitch v-model="useOsNativeEmojis" class="_formBlock">{{ $ts.useOsNativeEmojis }}
+ <template #label>{{ i18n.ts.appearance }}</template>
+ <FormSwitch v-model="disableAnimatedMfm" class="_formBlock">{{ i18n.ts.disableAnimatedMfm }}</FormSwitch>
+ <FormSwitch v-model="reduceAnimation" class="_formBlock">{{ i18n.ts.reduceUiAnimation }}</FormSwitch>
+ <FormSwitch v-model="useBlurEffect" class="_formBlock">{{ i18n.ts.useBlurEffect }}</FormSwitch>
+ <FormSwitch v-model="useBlurEffectForModal" class="_formBlock">{{ i18n.ts.useBlurEffectForModal }}</FormSwitch>
+ <FormSwitch v-model="showGapBetweenNotesInTimeline" class="_formBlock">{{ i18n.ts.showGapBetweenNotesInTimeline }}</FormSwitch>
+ <FormSwitch v-model="loadRawImages" class="_formBlock">{{ i18n.ts.loadRawImages }}</FormSwitch>
+ <FormSwitch v-model="disableShowingAnimatedImages" class="_formBlock">{{ i18n.ts.disableShowingAnimatedImages }}</FormSwitch>
+ <FormSwitch v-model="squareAvatars" class="_formBlock">{{ i18n.ts.squareAvatars }}</FormSwitch>
+ <FormSwitch v-model="useSystemFont" class="_formBlock">{{ i18n.ts.useSystemFont }}</FormSwitch>
+ <FormSwitch v-model="useOsNativeEmojis" class="_formBlock">{{ i18n.ts.useOsNativeEmojis }}
<div><Mfm :key="useOsNativeEmojis" text="ðŸ®ðŸ¦ðŸ­ðŸ©ðŸ°ðŸ«ðŸ¬ðŸ¥žðŸª"/></div>
</FormSwitch>
- <FormSwitch v-model="disableDrawer" class="_formBlock">{{ $ts.disableDrawer }}</FormSwitch>
+ <FormSwitch v-model="disableDrawer" class="_formBlock">{{ i18n.ts.disableDrawer }}</FormSwitch>
<FormRadios v-model="fontSize" class="_formBlock">
- <template #label>{{ $ts.fontSize }}</template>
+ <template #label>{{ i18n.ts.fontSize }}</template>
<option value="small"><span style="font-size: 14px;">Aa</span></option>
<option :value="null"><span style="font-size: 16px;">Aa</span></option>
<option value="large"><span style="font-size: 18px;">Aa</span></option>
@@ -63,36 +63,36 @@
</FormSection>
<FormSection>
- <FormSwitch v-model="aiChanMode">{{ $ts.aiChanMode }}</FormSwitch>
+ <FormSwitch v-model="aiChanMode">{{ i18n.ts.aiChanMode }}</FormSwitch>
</FormSection>
<FormSelect v-model="instanceTicker" class="_formBlock">
- <template #label>{{ $ts.instanceTicker }}</template>
- <option value="none">{{ $ts._instanceTicker.none }}</option>
- <option value="remote">{{ $ts._instanceTicker.remote }}</option>
- <option value="always">{{ $ts._instanceTicker.always }}</option>
+ <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>
</FormSelect>
<FormSelect v-model="nsfw" class="_formBlock">
- <template #label>{{ $ts.nsfw }}</template>
- <option value="respect">{{ $ts._nsfw.respect }}</option>
- <option value="ignore">{{ $ts._nsfw.ignore }}</option>
- <option value="force">{{ $ts._nsfw.force }}</option>
+ <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>
</FormSelect>
<FormGroup>
- <template #label>{{ $ts.defaultNavigationBehaviour }}</template>
- <FormSwitch v-model="defaultSideView">{{ $ts.openInSideView }}</FormSwitch>
+ <template #label>{{ i18n.ts.defaultNavigationBehaviour }}</template>
+ <FormSwitch v-model="defaultSideView">{{ i18n.ts.openInSideView }}</FormSwitch>
</FormGroup>
- <FormLink to="/settings/deck" class="_formBlock">{{ $ts.deck }}</FormLink>
+ <FormLink to="/settings/deck" class="_formBlock">{{ i18n.ts.deck }}</FormLink>
- <FormLink to="/settings/custom-css" class="_formBlock"><template #icon><i class="fas fa-code"></i></template>{{ $ts.customCss }}</FormLink>
+ <FormLink to="/settings/custom-css" class="_formBlock"><template #icon><i class="fas fa-code"></i></template>{{ i18n.ts.customCss }}</FormLink>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { computed, defineExpose, ref, watch } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
import FormSelect from '@/components/form/select.vue';
import FormRadios from '@/components/form/radios.vue';
@@ -102,122 +102,87 @@ import FormLink from '@/components/form/link.vue';
import MkLink from '@/components/link.vue';
import { langs } from '@/config';
import { defaultStore } from '@/store';
-import { ColdDeviceStorage } from '@/store';
import * as os from '@/os';
import { unisonReload } from '@/scripts/unison-reload';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkLink,
- FormSwitch,
- FormSelect,
- FormRadios,
- FormGroup,
- FormLink,
- FormSection,
- },
+const lang = ref(localStorage.getItem('lang'));
+const fontSize = ref(localStorage.getItem('fontSize'));
+const useSystemFont = ref(localStorage.getItem('useSystemFont') != null);
- emits: ['info'],
+async function reloadAsk() {
+ const { canceled } = await os.confirm({
+ type: 'info',
+ text: i18n.ts.reloadToApplySetting,
+ });
+ if (canceled) return;
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.general,
- icon: 'fas fa-cogs',
- bg: 'var(--bg)'
- },
- langs,
- lang: localStorage.getItem('lang'),
- fontSize: localStorage.getItem('fontSize'),
- useSystemFont: localStorage.getItem('useSystemFont') != null,
- }
- },
+ unisonReload();
+}
- computed: {
- overridedDeviceKind: defaultStore.makeGetterSetter('overridedDeviceKind'),
- serverDisconnectedBehavior: defaultStore.makeGetterSetter('serverDisconnectedBehavior'),
- reduceAnimation: defaultStore.makeGetterSetter('animation', v => !v, v => !v),
- useBlurEffectForModal: defaultStore.makeGetterSetter('useBlurEffectForModal'),
- useBlurEffect: defaultStore.makeGetterSetter('useBlurEffect'),
- showGapBetweenNotesInTimeline: defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline'),
- disableAnimatedMfm: defaultStore.makeGetterSetter('animatedMfm', v => !v, v => !v),
- useOsNativeEmojis: defaultStore.makeGetterSetter('useOsNativeEmojis'),
- disableDrawer: defaultStore.makeGetterSetter('disableDrawer'),
- disableShowingAnimatedImages: defaultStore.makeGetterSetter('disableShowingAnimatedImages'),
- loadRawImages: defaultStore.makeGetterSetter('loadRawImages'),
- imageNewTab: defaultStore.makeGetterSetter('imageNewTab'),
- nsfw: defaultStore.makeGetterSetter('nsfw'),
- disablePagesScript: defaultStore.makeGetterSetter('disablePagesScript'),
- showFixedPostForm: defaultStore.makeGetterSetter('showFixedPostForm'),
- defaultSideView: defaultStore.makeGetterSetter('defaultSideView'),
- instanceTicker: defaultStore.makeGetterSetter('instanceTicker'),
- enableInfiniteScroll: defaultStore.makeGetterSetter('enableInfiniteScroll'),
- useReactionPickerForContextMenu: defaultStore.makeGetterSetter('useReactionPickerForContextMenu'),
- squareAvatars: defaultStore.makeGetterSetter('squareAvatars'),
- aiChanMode: defaultStore.makeGetterSetter('aiChanMode'),
- },
+const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind'));
+const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serverDisconnectedBehavior'));
+const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v));
+const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal'));
+const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect'));
+const showGapBetweenNotesInTimeline = computed(defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline'));
+const disableAnimatedMfm = computed(defaultStore.makeGetterSetter('animatedMfm', v => !v, v => !v));
+const useOsNativeEmojis = computed(defaultStore.makeGetterSetter('useOsNativeEmojis'));
+const disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer'));
+const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages'));
+const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages'));
+const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab'));
+const nsfw = computed(defaultStore.makeGetterSetter('nsfw'));
+const disablePagesScript = computed(defaultStore.makeGetterSetter('disablePagesScript'));
+const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm'));
+const defaultSideView = computed(defaultStore.makeGetterSetter('defaultSideView'));
+const instanceTicker = computed(defaultStore.makeGetterSetter('instanceTicker'));
+const enableInfiniteScroll = computed(defaultStore.makeGetterSetter('enableInfiniteScroll'));
+const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('useReactionPickerForContextMenu'));
+const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars'));
+const aiChanMode = computed(defaultStore.makeGetterSetter('aiChanMode'));
- watch: {
- lang() {
- localStorage.setItem('lang', this.lang);
- localStorage.removeItem('locale');
- this.reloadAsk();
- },
-
- fontSize() {
- if (this.fontSize == null) {
- localStorage.removeItem('fontSize');
- } else {
- localStorage.setItem('fontSize', this.fontSize);
- }
- this.reloadAsk();
- },
-
- useSystemFont() {
- if (this.useSystemFont) {
- localStorage.setItem('useSystemFont', 't');
- } else {
- localStorage.removeItem('useSystemFont');
- }
- this.reloadAsk();
- },
-
- enableInfiniteScroll() {
- this.reloadAsk();
- },
-
- squareAvatars() {
- this.reloadAsk();
- },
-
- aiChanMode() {
- this.reloadAsk();
- },
-
- showGapBetweenNotesInTimeline() {
- this.reloadAsk();
- },
+watch(lang, () => {
+ localStorage.setItem('lang', lang.value as string);
+ localStorage.removeItem('locale');
+});
- instanceTicker() {
- this.reloadAsk();
- },
+watch(fontSize, () => {
+ if (fontSize.value == null) {
+ localStorage.removeItem('fontSize');
+ } else {
+ localStorage.setItem('fontSize', fontSize.value);
+ }
+});
- overridedDeviceKind() {
- this.reloadAsk();
- },
- },
+watch(useSystemFont, () => {
+ if (useSystemFont.value) {
+ localStorage.setItem('useSystemFont', 't');
+ } else {
+ localStorage.removeItem('useSystemFont');
+ }
+});
- methods: {
- async reloadAsk() {
- const { canceled } = await os.confirm({
- type: 'info',
- text: this.$ts.reloadToApplySetting,
- });
- if (canceled) return;
+watch([
+ lang,
+ fontSize,
+ useSystemFont,
+ enableInfiniteScroll,
+ squareAvatars,
+ aiChanMode,
+ showGapBetweenNotesInTimeline,
+ instanceTicker,
+ overridedDeviceKind
+], async () => {
+ await reloadAsk();
+});
- unisonReload();
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.general,
+ icon: 'fas fa-cogs',
+ bg: 'var(--bg)'
}
});
</script>
diff --git a/packages/client/src/pages/settings/import-export.vue b/packages/client/src/pages/settings/import-export.vue
index c153b4d28c..127cbcd4c1 100644
--- a/packages/client/src/pages/settings/import-export.vue
+++ b/packages/client/src/pages/settings/import-export.vue
@@ -37,8 +37,8 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent, onMounted, ref } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, ref } from 'vue';
import MkButton from '@/components/ui/button.vue';
import FormSection from '@/components/form/section.vue';
import FormGroup from '@/components/form/group.vue';
@@ -48,108 +48,80 @@ import { selectFile } from '@/scripts/select-file';
import * as symbols from '@/symbols';
import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSection,
- FormGroup,
- FormSwitch,
- MkButton,
- },
+const excludeMutingUsers = ref(false);
+const excludeInactiveUsers = ref(false);
- emits: ['info'],
+const onExportSuccess = () => {
+ os.alert({
+ type: 'info',
+ text: i18n.ts.exportRequested,
+ });
+};
- setup(props, context) {
- const INFO = {
- title: i18n.ts.importAndExport,
- icon: 'fas fa-boxes',
- bg: 'var(--bg)',
- };
+const onImportSuccess = () => {
+ os.alert({
+ type: 'info',
+ text: i18n.ts.importRequested,
+ });
+};
- const excludeMutingUsers = ref(false);
- const excludeInactiveUsers = ref(false);
+const onError = (ev) => {
+ os.alert({
+ type: 'error',
+ text: ev.message,
+ });
+};
- const onExportSuccess = () => {
- os.alert({
- type: 'info',
- text: i18n.ts.exportRequested,
- });
- };
+const exportNotes = () => {
+ os.api('i/export-notes', {}).then(onExportSuccess).catch(onError);
+};
- const onImportSuccess = () => {
- os.alert({
- type: 'info',
- text: i18n.ts.importRequested,
- });
- };
+const exportFollowing = () => {
+ os.api('i/export-following', {
+ excludeMuting: excludeMutingUsers.value,
+ excludeInactive: excludeInactiveUsers.value,
+ })
+ .then(onExportSuccess).catch(onError);
+};
- const onError = (e) => {
- os.alert({
- type: 'error',
- text: e.message,
- });
- };
+const exportBlocking = () => {
+ os.api('i/export-blocking', {}).then(onExportSuccess).catch(onError);
+};
- const exportNotes = () => {
- os.api('i/export-notes', {}).then(onExportSuccess).catch(onError);
- };
+const exportUserLists = () => {
+ os.api('i/export-user-lists', {}).then(onExportSuccess).catch(onError);
+};
- const exportFollowing = () => {
- os.api('i/export-following', {
- excludeMuting: excludeMutingUsers.value,
- excludeInactive: excludeInactiveUsers.value,
- })
- .then(onExportSuccess).catch(onError);
- };
+const exportMuting = () => {
+ os.api('i/export-mute', {}).then(onExportSuccess).catch(onError);
+};
- const exportBlocking = () => {
- os.api('i/export-blocking', {}).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);
+};
- const exportUserLists = () => {
- os.api('i/export-user-lists', {}).then(onExportSuccess).catch(onError);
- };
+const importUserLists = async (ev) => {
+ const file = await selectFile(ev.currentTarget ?? ev.target);
+ os.api('i/import-user-lists', { fileId: file.id }).then(onImportSuccess).catch(onError);
+};
- const exportMuting = () => {
- os.api('i/export-mute', {}).then(onExportSuccess).catch(onError);
- };
+const importMuting = async (ev) => {
+ const file = await selectFile(ev.currentTarget ?? ev.target);
+ os.api('i/import-muting', { fileId: file.id }).then(onImportSuccess).catch(onError);
+};
- const importFollowing = async (ev) => {
- const file = await selectFile(ev.currentTarget ?? ev.target);
- os.api('i/import-following', { fileId: file.id }).then(onImportSuccess).catch(onError);
- };
+const importBlocking = async (ev) => {
+ const file = await selectFile(ev.currentTarget ?? ev.target);
+ os.api('i/import-blocking', { fileId: file.id }).then(onImportSuccess).catch(onError);
+};
- const importUserLists = async (ev) => {
- const file = await selectFile(ev.currentTarget ?? ev.target);
- os.api('i/import-user-lists', { fileId: file.id }).then(onImportSuccess).catch(onError);
- };
-
- const importMuting = async (ev) => {
- const file = await selectFile(ev.currentTarget ?? ev.target);
- os.api('i/import-muting', { fileId: file.id }).then(onImportSuccess).catch(onError);
- };
-
- const importBlocking = async (ev) => {
- const file = await selectFile(ev.currentTarget ?? ev.target);
- os.api('i/import-blocking', { fileId: file.id }).then(onImportSuccess).catch(onError);
- };
-
- return {
- [symbols.PAGE_INFO]: INFO,
- excludeMutingUsers,
- excludeInactiveUsers,
-
- exportNotes,
- exportFollowing,
- exportBlocking,
- exportUserLists,
- exportMuting,
-
- importFollowing,
- importUserLists,
- importMuting,
- importBlocking,
- };
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.importAndExport,
+ icon: 'fas fa-boxes',
+ bg: 'var(--bg)',
+ }
});
</script>
diff --git a/packages/client/src/pages/settings/index.vue b/packages/client/src/pages/settings/index.vue
index 44c3be62fe..e6670ea930 100644
--- a/packages/client/src/pages/settings/index.vue
+++ b/packages/client/src/pages/settings/index.vue
@@ -2,19 +2,22 @@
<MkSpacer :content-max="900" :margin-min="20" :margin-max="32">
<div ref="el" class="vvcocwet" :class="{ wide: !narrow }">
<div class="header">
- <div class="title">{{ $ts.settings }}</div>
+ <div class="title">
+ <MkA v-if="narrow" to="/settings">{{ $ts.settings }}</MkA>
+ <template v-else>{{ $ts.settings }}</template>
+ </div>
<div v-if="childInfo" class="subtitle">{{ childInfo.title }}</div>
</div>
<div class="body">
- <div v-if="!narrow || page == null" class="nav">
+ <div v-if="!narrow || initialPage == null" class="nav">
<div class="baaadecd">
<MkInfo v-if="emailNotConfigured" warn class="info">{{ $ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ $ts.configure }}</MkA></MkInfo>
- <MkSuperMenu :def="menuDef" :grid="page == null"></MkSuperMenu>
+ <MkSuperMenu :def="menuDef" :grid="initialPage == null"></MkSuperMenu>
</div>
</div>
- <div class="main">
+ <div v-if="!(narrow && initialPage == null)" class="main">
<div class="bkzroven">
- <component :is="component" :ref="el => pageChanged(el)" :key="page" v-bind="pageProps"/>
+ <component :is="component" :ref="el => pageChanged(el)" :key="initialPage" v-bind="pageProps"/>
</div>
</div>
</div>
@@ -23,7 +26,7 @@
</template>
<script setup lang="ts">
-import { computed, defineAsyncComponent, nextTick, onMounted, ref, watch } from 'vue';
+import { computed, defineAsyncComponent, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
import { i18n } from '@/i18n';
import MkInfo from '@/components/ui/info.vue';
import MkSuperMenu from '@/components/ui/super-menu.vue';
@@ -33,6 +36,7 @@ import { unisonReload } from '@/scripts/unison-reload';
import * as symbols from '@/symbols';
import { instance } from '@/instance';
import { $i } from '@/account';
+import { MisskeyNavigator } from '@/scripts/navigate';
const props = defineProps<{
initialPage?: string
@@ -45,53 +49,61 @@ const indexInfo = {
hideHeader: true,
};
const INFO = ref(indexInfo);
-const page = ref(props.initialPage);
-const narrow = ref(false);
-const view = ref(null);
const el = ref<HTMLElement | null>(null);
const childInfo = ref(null);
+
+const nav = new MisskeyNavigator();
+
+const narrow = ref(false);
+const NARROW_THRESHOLD = 600;
+
+const ro = new ResizeObserver((entries, observer) => {
+ if (entries.length === 0) return;
+ narrow.value = entries[0].borderBoxSize[0].inlineSize < NARROW_THRESHOLD;
+});
+
const menuDef = computed(() => [{
title: i18n.ts.basicSettings,
items: [{
icon: 'fas fa-user',
text: i18n.ts.profile,
to: '/settings/profile',
- active: page.value === 'profile',
+ active: props.initialPage === 'profile',
}, {
icon: 'fas fa-lock-open',
text: i18n.ts.privacy,
to: '/settings/privacy',
- active: page.value === 'privacy',
+ active: props.initialPage === 'privacy',
}, {
icon: 'fas fa-laugh',
text: i18n.ts.reaction,
to: '/settings/reaction',
- active: page.value === 'reaction',
+ active: props.initialPage === 'reaction',
}, {
icon: 'fas fa-cloud',
text: i18n.ts.drive,
to: '/settings/drive',
- active: page.value === 'drive',
+ active: props.initialPage === 'drive',
}, {
icon: 'fas fa-bell',
text: i18n.ts.notifications,
to: '/settings/notifications',
- active: page.value === 'notifications',
+ active: props.initialPage === 'notifications',
}, {
icon: 'fas fa-envelope',
text: i18n.ts.email,
to: '/settings/email',
- active: page.value === 'email',
+ active: props.initialPage === 'email',
}, {
icon: 'fas fa-share-alt',
text: i18n.ts.integration,
to: '/settings/integration',
- active: page.value === 'integration',
+ active: props.initialPage === 'integration',
}, {
icon: 'fas fa-lock',
text: i18n.ts.security,
to: '/settings/security',
- active: page.value === 'security',
+ active: props.initialPage === 'security',
}],
}, {
title: i18n.ts.clientSettings,
@@ -99,27 +111,27 @@ const menuDef = computed(() => [{
icon: 'fas fa-cogs',
text: i18n.ts.general,
to: '/settings/general',
- active: page.value === 'general',
+ active: props.initialPage === 'general',
}, {
icon: 'fas fa-palette',
text: i18n.ts.theme,
to: '/settings/theme',
- active: page.value === 'theme',
+ active: props.initialPage === 'theme',
}, {
icon: 'fas fa-list-ul',
text: i18n.ts.menu,
to: '/settings/menu',
- active: page.value === 'menu',
+ active: props.initialPage === 'menu',
}, {
icon: 'fas fa-music',
text: i18n.ts.sounds,
to: '/settings/sounds',
- active: page.value === 'sounds',
+ active: props.initialPage === 'sounds',
}, {
icon: 'fas fa-plug',
text: i18n.ts.plugins,
to: '/settings/plugin',
- active: page.value === 'plugin',
+ active: props.initialPage === 'plugin',
}],
}, {
title: i18n.ts.otherSettings,
@@ -127,37 +139,37 @@ const menuDef = computed(() => [{
icon: 'fas fa-boxes',
text: i18n.ts.importAndExport,
to: '/settings/import-export',
- active: page.value === 'import-export',
+ active: props.initialPage === 'import-export',
}, {
icon: 'fas fa-volume-mute',
text: i18n.ts.instanceMute,
to: '/settings/instance-mute',
- active: page.value === 'instance-mute',
+ active: props.initialPage === 'instance-mute',
}, {
icon: 'fas fa-ban',
text: i18n.ts.muteAndBlock,
to: '/settings/mute-block',
- active: page.value === 'mute-block',
+ active: props.initialPage === 'mute-block',
}, {
icon: 'fas fa-comment-slash',
text: i18n.ts.wordMute,
to: '/settings/word-mute',
- active: page.value === 'word-mute',
+ active: props.initialPage === 'word-mute',
}, {
icon: 'fas fa-key',
text: 'API',
to: '/settings/api',
- active: page.value === 'api',
+ active: props.initialPage === 'api',
}, {
icon: 'fas fa-bolt',
text: 'Webhook',
to: '/settings/webhook',
- active: page.value === 'webhook',
+ active: props.initialPage === 'webhook',
}, {
icon: 'fas fa-ellipsis-h',
text: i18n.ts.other,
to: '/settings/other',
- active: page.value === 'other',
+ active: props.initialPage === 'other',
}],
}, {
items: [{
@@ -182,8 +194,8 @@ const menuDef = computed(() => [{
const pageProps = ref({});
const component = computed(() => {
- if (page.value == null) return null;
- switch (page.value) {
+ if (props.initialPage == null) return null;
+ switch (props.initialPage) {
case 'accounts': return defineAsyncComponent(() => import('./accounts.vue'));
case 'profile': return defineAsyncComponent(() => import('./profile.vue'));
case 'privacy': return defineAsyncComponent(() => import('./privacy.vue'));
@@ -230,27 +242,41 @@ watch(component, () => {
watch(() => props.initialPage, () => {
if (props.initialPage == null && !narrow.value) {
- page.value = 'profile';
+ nav.push('/settings/profile');
} else {
- page.value = props.initialPage;
if (props.initialPage == null) {
INFO.value = indexInfo;
}
}
});
+watch(narrow, () => {
+ if (props.initialPage == null && !narrow.value) {
+ nav.push('/settings/profile');
+ }
+});
+
onMounted(() => {
- narrow.value = el.value.offsetWidth < 800;
- if (!narrow.value) {
- page.value = 'profile';
+ ro.observe(el.value);
+
+ narrow.value = el.value.offsetWidth < NARROW_THRESHOLD;
+ if (props.initialPage == null && !narrow.value) {
+ nav.push('/settings/profile');
}
});
+onUnmounted(() => {
+ ro.disconnect();
+});
+
const emailNotConfigured = computed(() => instance.enableEmail && ($i.email == null || !$i.emailVerified));
const pageChanged = (page) => {
- if (page == null) return;
- childInfo.value = page[symbols.PAGE_INFO];
+ if (page == null) {
+ childInfo.value = null;
+ } else {
+ childInfo.value = page[symbols.PAGE_INFO];
+ }
};
defineExpose({
@@ -267,6 +293,7 @@ defineExpose({
font-weight: bold;
> .title {
+ display: block;
width: 34%;
}
diff --git a/packages/client/src/pages/settings/instance-mute.vue b/packages/client/src/pages/settings/instance-mute.vue
index f84a209b60..bcc2ee85ad 100644
--- a/packages/client/src/pages/settings/instance-mute.vue
+++ b/packages/client/src/pages/settings/instance-mute.vue
@@ -1,67 +1,51 @@
<template>
<div class="_formRoot">
- <MkInfo>{{ $ts._instanceMute.title }}</MkInfo>
+ <MkInfo>{{ i18n.ts._instanceMute.title }}</MkInfo>
<FormTextarea v-model="instanceMutes" class="_formBlock">
- <template #label>{{ $ts._instanceMute.heading }}</template>
- <template #caption>{{ $ts._instanceMute.instanceMuteDescription }}<br>{{ $ts._instanceMute.instanceMuteDescription2 }}</template>
+ <template #label>{{ i18n.ts._instanceMute.heading }}</template>
+ <template #caption>{{ i18n.ts._instanceMute.instanceMuteDescription }}<br>{{ i18n.ts._instanceMute.instanceMuteDescription2 }}</template>
</FormTextarea>
- <MkButton primary :disabled="!changed" class="_formBlock" @click="save()"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
+ <MkButton primary :disabled="!changed" class="_formBlock" @click="save()"><i class="fas fa-save"></i> {{ i18n.ts.save }}</MkButton>
</div>
</template>
-<script>
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, ref, watch } from 'vue';
import FormTextarea from '@/components/form/textarea.vue';
import MkInfo from '@/components/ui/info.vue';
import MkButton from '@/components/ui/button.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { $i } from '@/account';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- FormTextarea,
- MkInfo,
- },
+const instanceMutes = ref($i!.mutedInstances.join('\n'));
+const changed = ref(false);
- emits: ['info'],
+async function save() {
+ let mutes = instanceMutes.value
+ .trim().split('\n')
+ .map(el => el.trim())
+ .filter(el => el);
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.instanceMute,
- icon: 'fas fa-volume-mute'
- },
- tab: 'soft',
- instanceMutes: '',
- changed: false,
- }
- },
+ await os.api('i/update', {
+ mutedInstances: mutes,
+ });
- watch: {
- instanceMutes: {
- handler() {
- this.changed = true;
- },
- deep: true
- },
- },
+ changed.value = false;
- async created() {
- this.instanceMutes = this.$i.mutedInstances.join('\n');
- },
+ // Refresh filtered list to signal to the user how they've been saved
+ instanceMutes.value = mutes.join('\n');
+}
- methods: {
- async save() {
- let mutes = this.instanceMutes.trim().split('\n').map(el => el.trim()).filter(el => el);
- await os.api('i/update', {
- mutedInstances: mutes,
- });
- this.changed = false;
+watch(instanceMutes, () => {
+ changed.value = true;
+});
- // Refresh filtered list to signal to the user how they've been saved
- this.instanceMutes = mutes.join('\n');
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.instanceMute,
+ icon: 'fas fa-volume-mute'
}
-})
+});
</script>
diff --git a/packages/client/src/pages/settings/integration.vue b/packages/client/src/pages/settings/integration.vue
index ca36c91665..75c6200944 100644
--- a/packages/client/src/pages/settings/integration.vue
+++ b/packages/client/src/pages/settings/integration.vue
@@ -1,133 +1,98 @@
<template>
<div class="_formRoot">
- <FormSection v-if="enableTwitterIntegration">
+ <FormSection v-if="instance.enableTwitterIntegration">
<template #label><i class="fab fa-twitter"></i> Twitter</template>
- <p v-if="integrations.twitter">{{ $ts.connectedTo }}: <a :href="`https://twitter.com/${integrations.twitter.screenName}`" rel="nofollow noopener" target="_blank">@{{ integrations.twitter.screenName }}</a></p>
- <MkButton v-if="integrations.twitter" danger @click="disconnectTwitter">{{ $ts.disconnectService }}</MkButton>
- <MkButton v-else primary @click="connectTwitter">{{ $ts.connectService }}</MkButton>
+ <p v-if="integrations.twitter">{{ i18n.ts.connectedTo }}: <a :href="`https://twitter.com/${integrations.twitter.screenName}`" rel="nofollow noopener" target="_blank">@{{ integrations.twitter.screenName }}</a></p>
+ <MkButton v-if="integrations.twitter" danger @click="disconnectTwitter">{{ i18n.ts.disconnectService }}</MkButton>
+ <MkButton v-else primary @click="connectTwitter">{{ i18n.ts.connectService }}</MkButton>
</FormSection>
- <FormSection v-if="enableDiscordIntegration">
+ <FormSection v-if="instance.enableDiscordIntegration">
<template #label><i class="fab fa-discord"></i> Discord</template>
- <p v-if="integrations.discord">{{ $ts.connectedTo }}: <a :href="`https://discord.com/users/${integrations.discord.id}`" rel="nofollow noopener" target="_blank">@{{ integrations.discord.username }}#{{ integrations.discord.discriminator }}</a></p>
- <MkButton v-if="integrations.discord" danger @click="disconnectDiscord">{{ $ts.disconnectService }}</MkButton>
- <MkButton v-else primary @click="connectDiscord">{{ $ts.connectService }}</MkButton>
+ <p v-if="integrations.discord">{{ i18n.ts.connectedTo }}: <a :href="`https://discord.com/users/${integrations.discord.id}`" rel="nofollow noopener" target="_blank">@{{ integrations.discord.username }}#{{ integrations.discord.discriminator }}</a></p>
+ <MkButton v-if="integrations.discord" danger @click="disconnectDiscord">{{ i18n.ts.disconnectService }}</MkButton>
+ <MkButton v-else primary @click="connectDiscord">{{ i18n.ts.connectService }}</MkButton>
</FormSection>
- <FormSection v-if="enableGithubIntegration">
+ <FormSection v-if="instance.enableGithubIntegration">
<template #label><i class="fab fa-github"></i> GitHub</template>
- <p v-if="integrations.github">{{ $ts.connectedTo }}: <a :href="`https://github.com/${integrations.github.login}`" rel="nofollow noopener" target="_blank">@{{ integrations.github.login }}</a></p>
- <MkButton v-if="integrations.github" danger @click="disconnectGithub">{{ $ts.disconnectService }}</MkButton>
- <MkButton v-else primary @click="connectGithub">{{ $ts.connectService }}</MkButton>
+ <p v-if="integrations.github">{{ i18n.ts.connectedTo }}: <a :href="`https://github.com/${integrations.github.login}`" rel="nofollow noopener" target="_blank">@{{ integrations.github.login }}</a></p>
+ <MkButton v-if="integrations.github" danger @click="disconnectGithub">{{ i18n.ts.disconnectService }}</MkButton>
+ <MkButton v-else primary @click="connectGithub">{{ i18n.ts.connectService }}</MkButton>
</FormSection>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { computed, defineExpose, onMounted, ref, watch } from 'vue';
import { apiUrl } from '@/config';
import FormSection from '@/components/form/section.vue';
import MkButton from '@/components/ui/button.vue';
-import * as os from '@/os';
import * as symbols from '@/symbols';
+import { $i } from '@/account';
+import { instance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSection,
- MkButton
- },
+const twitterForm = ref<Window | null>(null);
+const discordForm = ref<Window | null>(null);
+const githubForm = ref<Window | null>(null);
- emits: ['info'],
+const integrations = computed(() => $i!.integrations);
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.integration,
- icon: 'fas fa-share-alt',
- bg: 'var(--bg)',
- },
- apiUrl,
- twitterForm: null,
- discordForm: null,
- githubForm: null,
- enableTwitterIntegration: false,
- enableDiscordIntegration: false,
- enableGithubIntegration: false,
- };
- },
+function openWindow(service: string, type: string) {
+ return window.open(`${apiUrl}/${type}/${service}`,
+ `${service}_${type}_window`,
+ 'height=570, width=520'
+ );
+}
- computed: {
- integrations() {
- return this.$i.integrations;
- },
-
- meta() {
- return this.$instance;
- },
- },
+function connectTwitter() {
+ twitterForm.value = openWindow('twitter', 'connect');
+}
- created() {
- this.enableTwitterIntegration = this.meta.enableTwitterIntegration;
- this.enableDiscordIntegration = this.meta.enableDiscordIntegration;
- this.enableGithubIntegration = this.meta.enableGithubIntegration;
- },
+function disconnectTwitter() {
+ openWindow('twitter', 'disconnect');
+}
- mounted() {
- document.cookie = `igi=${this.$i.token}; path=/;` +
- ` max-age=31536000;` +
- (document.location.protocol.startsWith('https') ? ' secure' : '');
+function connectDiscord() {
+ discordForm.value = openWindow('discord', 'connect');
+}
- this.$watch('integrations', () => {
- if (this.integrations.twitter) {
- if (this.twitterForm) this.twitterForm.close();
- }
- if (this.integrations.discord) {
- if (this.discordForm) this.discordForm.close();
- }
- if (this.integrations.github) {
- if (this.githubForm) this.githubForm.close();
- }
- }, {
- deep: true
- });
- },
+function disconnectDiscord() {
+ openWindow('discord', 'disconnect');
+}
- methods: {
- connectTwitter() {
- this.twitterForm = window.open(apiUrl + '/connect/twitter',
- 'twitter_connect_window',
- 'height=570, width=520');
- },
+function connectGithub() {
+ githubForm.value = openWindow('github', 'connect');
+}
- disconnectTwitter() {
- window.open(apiUrl + '/disconnect/twitter',
- 'twitter_disconnect_window',
- 'height=570, width=520');
- },
+function disconnectGithub() {
+ openWindow('github', 'disconnect');
+}
- connectDiscord() {
- this.discordForm = window.open(apiUrl + '/connect/discord',
- 'discord_connect_window',
- 'height=570, width=520');
- },
+onMounted(() => {
+ document.cookie = `igi=${$i!.token}; path=/;` +
+ ` max-age=31536000;` +
+ (document.location.protocol.startsWith('https') ? ' secure' : '');
- disconnectDiscord() {
- window.open(apiUrl + '/disconnect/discord',
- 'discord_disconnect_window',
- 'height=570, width=520');
- },
-
- connectGithub() {
- this.githubForm = window.open(apiUrl + '/connect/github',
- 'github_connect_window',
- 'height=570, width=520');
- },
+ watch(integrations, () => {
+ if (integrations.value.twitter) {
+ if (twitterForm.value) twitterForm.value.close();
+ }
+ if (integrations.value.discord) {
+ if (discordForm.value) discordForm.value.close();
+ }
+ if (integrations.value.github) {
+ if (githubForm.value) githubForm.value.close();
+ }
+ });
+});
- disconnectGithub() {
- window.open(apiUrl + '/disconnect/github',
- 'github_disconnect_window',
- 'height=570, width=520');
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.integration,
+ icon: 'fas fa-share-alt',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/menu.vue b/packages/client/src/pages/settings/menu.vue
index 6e38cd5dfe..2288c3f718 100644
--- a/packages/client/src/pages/settings/menu.vue
+++ b/packages/client/src/pages/settings/menu.vue
@@ -1,24 +1,24 @@
<template>
<div class="_formRoot">
<FormTextarea v-model="items" tall manual-save class="_formBlock">
- <template #label>{{ $ts.menu }}</template>
- <template #caption><button class="_textButton" @click="addItem">{{ $ts.addItem }}</button></template>
+ <template #label>{{ i18n.ts.menu }}</template>
+ <template #caption><button class="_textButton" @click="addItem">{{ i18n.ts.addItem }}</button></template>
</FormTextarea>
<FormRadios v-model="menuDisplay" class="_formBlock">
- <template #label>{{ $ts.display }}</template>
- <option value="sideFull">{{ $ts._menuDisplay.sideFull }}</option>
- <option value="sideIcon">{{ $ts._menuDisplay.sideIcon }}</option>
- <option value="top">{{ $ts._menuDisplay.top }}</option>
- <!-- <MkRadio v-model="menuDisplay" value="hide" disabled>{{ $ts._menuDisplay.hide }}</MkRadio>--> <!-- TODO: サイドãƒãƒ¼ã‚’完全ã«éš ã›ã‚‹ã‚ˆã†ã«ã™ã‚‹ã¨ã€åˆ¥é€”ãƒãƒ³ãƒãƒ¼ã‚¬ãƒ¼ãƒœã‚¿ãƒ³ã®ã‚ˆã†ãªã‚‚ã®ã‚’UIã«è¡¨ç¤ºã™ã‚‹å¿…è¦ãŒã‚りé¢å€’ -->
+ <template #label>{{ i18n.ts.display }}</template>
+ <option value="sideFull">{{ i18n.ts._menuDisplay.sideFull }}</option>
+ <option value="sideIcon">{{ i18n.ts._menuDisplay.sideIcon }}</option>
+ <option value="top">{{ i18n.ts._menuDisplay.top }}</option>
+ <!-- <MkRadio v-model="menuDisplay" value="hide" disabled>{{ i18n.ts._menuDisplay.hide }}</MkRadio>--> <!-- TODO: サイドãƒãƒ¼ã‚’完全ã«éš ã›ã‚‹ã‚ˆã†ã«ã™ã‚‹ã¨ã€åˆ¥é€”ãƒãƒ³ãƒãƒ¼ã‚¬ãƒ¼ãƒœã‚¿ãƒ³ã®ã‚ˆã†ãªã‚‚ã®ã‚’UIã«è¡¨ç¤ºã™ã‚‹å¿…è¦ãŒã‚りé¢å€’ -->
</FormRadios>
- <FormButton danger class="_formBlock" @click="reset()"><i class="fas fa-redo"></i> {{ $ts.default }}</FormButton>
+ <FormButton danger class="_formBlock" @click="reset()"><i class="fas fa-redo"></i> {{ i18n.ts.default }}</FormButton>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { computed, defineExpose, ref, watch } from 'vue';
import FormTextarea from '@/components/form/textarea.vue';
import FormRadios from '@/components/form/radios.vue';
import FormButton from '@/components/ui/button.vue';
@@ -27,81 +27,60 @@ import { menuDef } from '@/menu';
import { defaultStore } from '@/store';
import * as symbols from '@/symbols';
import { unisonReload } from '@/scripts/unison-reload';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormButton,
- FormTextarea,
- FormRadios,
- },
+const items = ref(defaultStore.state.menu.join('\n'));
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.menu,
- icon: 'fas fa-list-ul',
- bg: 'var(--bg)',
- },
- menuDef: menuDef,
- items: defaultStore.state.menu.join('\n'),
- }
- },
+const split = computed(() => items.value.trim().split('\n').filter(x => x.trim() !== ''));
+const menuDisplay = computed(defaultStore.makeGetterSetter('menuDisplay'));
- computed: {
- splited(): string[] {
- return this.items.trim().split('\n').filter(x => x.trim() !== '');
- },
+async function reloadAsk() {
+ const { canceled } = await os.confirm({
+ type: 'info',
+ text: i18n.ts.reloadToApplySetting
+ });
+ if (canceled) return;
- menuDisplay: defaultStore.makeGetterSetter('menuDisplay')
- },
+ unisonReload();
+}
- watch: {
- menuDisplay() {
- this.reloadAsk();
- },
+async function addItem() {
+ const menu = Object.keys(menuDef).filter(k => !defaultStore.state.menu.includes(k));
+ const { canceled, result: item } = await os.select({
+ title: i18n.ts.addItem,
+ items: [...menu.map(k => ({
+ value: k, text: i18n.ts[menuDef[k].title]
+ })), {
+ value: '-', text: i18n.ts.divider
+ }]
+ });
+ if (canceled) return;
+ items.value = [...split.value, item].join('\n');
+}
- items() {
- this.save();
- },
- },
+async function save() {
+ defaultStore.set('menu', split.value);
+ await reloadAsk();
+}
- methods: {
- async addItem() {
- const menu = Object.keys(this.menuDef).filter(k => !this.$store.state.menu.includes(k));
- const { canceled, result: item } = await os.select({
- title: this.$ts.addItem,
- items: [...menu.map(k => ({
- value: k, text: this.$ts[this.menuDef[k].title]
- })), ...[{
- value: '-', text: this.$ts.divider
- }]]
- });
- if (canceled) return;
- this.items = [...this.splited, item].join('\n');
- },
+function reset() {
+ defaultStore.reset('menu');
+ items.value = defaultStore.state.menu.join('\n');
+}
- save() {
- this.$store.set('menu', this.splited);
- this.reloadAsk();
- },
-
- reset() {
- this.$store.reset('menu');
- this.items = this.$store.state.menu.join('\n');
- },
+watch(items, async () => {
+ await save();
+});
- async reloadAsk() {
- const { canceled } = await os.confirm({
- type: 'info',
- text: this.$ts.reloadToApplySetting,
- showCancelButton: true
- });
- if (canceled) return;
+watch(menuDisplay, async () => {
+ await reloadAsk();
+});
- unisonReload();
- }
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.menu,
+ icon: 'fas fa-list-ul',
+ bg: 'var(--bg)',
+ }
});
</script>
diff --git a/packages/client/src/pages/settings/notifications.vue b/packages/client/src/pages/settings/notifications.vue
index 12171530bb..b8fff95a8d 100644
--- a/packages/client/src/pages/settings/notifications.vue
+++ b/packages/client/src/pages/settings/notifications.vue
@@ -1,71 +1,59 @@
<template>
<div class="_formRoot">
- <FormLink class="_formBlock" @click="configure"><template #icon><i class="fas fa-cog"></i></template>{{ $ts.notificationSetting }}</FormLink>
+ <FormLink class="_formBlock" @click="configure"><template #icon><i class="fas fa-cog"></i></template>{{ i18n.ts.notificationSetting }}</FormLink>
<FormSection>
- <FormLink class="_formBlock" @click="readAllNotifications">{{ $ts.markAsReadAllNotifications }}</FormLink>
- <FormLink class="_formBlock" @click="readAllUnreadNotes">{{ $ts.markAsReadAllUnreadNotes }}</FormLink>
- <FormLink class="_formBlock" @click="readAllMessagingMessages">{{ $ts.markAsReadAllTalkMessages }}</FormLink>
+ <FormLink class="_formBlock" @click="readAllNotifications">{{ i18n.ts.markAsReadAllNotifications }}</FormLink>
+ <FormLink class="_formBlock" @click="readAllUnreadNotes">{{ i18n.ts.markAsReadAllUnreadNotes }}</FormLink>
+ <FormLink class="_formBlock" @click="readAllMessagingMessages">{{ i18n.ts.markAsReadAllTalkMessages }}</FormLink>
</FormSection>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineAsyncComponent, defineExpose } from 'vue';
import FormButton from '@/components/ui/button.vue';
import FormLink from '@/components/form/link.vue';
import FormSection from '@/components/form/section.vue';
import { notificationTypes } from 'misskey-js';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { $i } from '@/account';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormLink,
- FormButton,
- FormSection,
- },
+async function readAllUnreadNotes() {
+ await os.api('i/read-all-unread-notes');
+}
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.notifications,
- icon: 'fas fa-bell',
- bg: 'var(--bg)',
- },
- }
- },
-
- methods: {
- readAllUnreadNotes() {
- os.api('i/read-all-unread-notes');
- },
+async function readAllMessagingMessages() {
+ await os.api('i/read-all-messaging-messages');
+}
- readAllMessagingMessages() {
- os.api('i/read-all-messaging-messages');
- },
+async function readAllNotifications() {
+ await os.api('notifications/mark-all-as-read');
+}
- readAllNotifications() {
- os.api('notifications/mark-all-as-read');
- },
+function configure() {
+ const includingTypes = notificationTypes.filter(x => !$i!.mutingNotificationTypes.includes(x));
+ os.popup(defineAsyncComponent(() => import('@/components/notification-setting-window.vue')), {
+ includingTypes,
+ showGlobalToggle: false,
+ }, {
+ done: async (res) => {
+ const { includingTypes: value } = res;
+ await os.apiWithDialog('i/update', {
+ mutingNotificationTypes: notificationTypes.filter(x => !value.includes(x)),
+ }).then(i => {
+ $i!.mutingNotificationTypes = i.mutingNotificationTypes;
+ });
+ }
+ }, 'closed');
+}
- configure() {
- const includingTypes = notificationTypes.filter(x => !this.$i.mutingNotificationTypes.includes(x));
- os.popup(import('@/components/notification-setting-window.vue'), {
- includingTypes,
- showGlobalToggle: false,
- }, {
- done: async (res) => {
- const { includingTypes: value } = res;
- await os.apiWithDialog('i/update', {
- mutingNotificationTypes: notificationTypes.filter(x => !value.includes(x)),
- }).then(i => {
- this.$i.mutingNotificationTypes = i.mutingNotificationTypes;
- });
- }
- }, 'closed');
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.notifications,
+ icon: 'fas fa-bell',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/other.vue b/packages/client/src/pages/settings/other.vue
index a9903acc7e..82e174a5b4 100644
--- a/packages/client/src/pages/settings/other.vue
+++ b/packages/client/src/pages/settings/other.vue
@@ -1,66 +1,44 @@
<template>
<div class="_formRoot">
- <FormSwitch :value="$i.injectFeaturedNote" class="_formBlock" @update:modelValue="onChangeInjectFeaturedNote">
- {{ $ts.showFeaturedNotesInTimeline }}
+ <FormSwitch v-model="$i.injectFeaturedNote" class="_formBlock" @update:modelValue="onChangeInjectFeaturedNote">
+ {{ i18n.ts.showFeaturedNotesInTimeline }}
</FormSwitch>
<!--
- <FormSwitch v-model="reportError" class="_formBlock">{{ $ts.sendErrorReports }}<template #caption>{{ $ts.sendErrorReportsDescription }}</template></FormSwitch>
+ <FormSwitch v-model="reportError" class="_formBlock">{{ i18n.ts.sendErrorReports }}<template #caption>{{ i18n.ts.sendErrorReportsDescription }}</template></FormSwitch>
-->
- <FormLink to="/settings/account-info" class="_formBlock">{{ $ts.accountInfo }}</FormLink>
+ <FormLink to="/settings/account-info" class="_formBlock">{{ i18n.ts.accountInfo }}</FormLink>
- <FormLink to="/settings/delete-account" class="_formBlock"><template #icon><i class="fas fa-exclamation-triangle"></i></template>{{ $ts.closeAccount }}</FormLink>
+ <FormLink to="/settings/delete-account" class="_formBlock"><template #icon><i class="fas fa-exclamation-triangle"></i></template>{{ i18n.ts.closeAccount }}</FormLink>
</div>
</template>
-<script lang="ts">
-import { defineAsyncComponent, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { computed, defineExpose } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
-import FormSection from '@/components/form/section.vue';
import FormLink from '@/components/form/link.vue';
import * as os from '@/os';
-import { debug } from '@/config';
import { defaultStore } from '@/store';
-import { unisonReload } from '@/scripts/unison-reload';
import * as symbols from '@/symbols';
+import { $i } from '@/account';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSection,
- FormSwitch,
- FormLink,
- },
+const reportError = computed(defaultStore.makeGetterSetter('reportError'));
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.other,
- icon: 'fas fa-ellipsis-h',
- bg: 'var(--bg)',
- },
- debug,
- }
- },
+function onChangeInjectFeaturedNote(v) {
+ os.api('i/update', {
+ injectFeaturedNote: v
+ }).then((i) => {
+ $i!.injectFeaturedNote = i.injectFeaturedNote;
+ });
+}
- computed: {
- reportError: defaultStore.makeGetterSetter('reportError'),
- },
-
- methods: {
- changeDebug(v) {
- console.log(v);
- localStorage.setItem('debug', v.toString());
- unisonReload();
- },
-
- onChangeInjectFeaturedNote(v) {
- os.api('i/update', {
- injectFeaturedNote: v
- });
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.other,
+ icon: 'fas fa-ellipsis-h',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/plugin.install.vue b/packages/client/src/pages/settings/plugin.install.vue
index d35d20d17a..96c0abfd99 100644
--- a/packages/client/src/pages/settings/plugin.install.vue
+++ b/packages/client/src/pages/settings/plugin.install.vue
@@ -1,19 +1,19 @@
<template>
<div class="_formRoot">
- <FormInfo warn class="_formBlock">{{ $ts._plugin.installWarn }}</FormInfo>
+ <FormInfo warn class="_formBlock">{{ i18n.ts._plugin.installWarn }}</FormInfo>
<FormTextarea v-model="code" tall class="_formBlock">
- <template #label>{{ $ts.code }}</template>
+ <template #label>{{ i18n.ts.code }}</template>
</FormTextarea>
<div class="_formBlock">
- <FormButton :disabled="code == null" primary inline @click="install"><i class="fas fa-check"></i> {{ $ts.install }}</FormButton>
+ <FormButton :disabled="code == null" primary inline @click="install"><i class="fas fa-check"></i> {{ i18n.ts.install }}</FormButton>
</div>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, defineAsyncComponent, nextTick, ref } from 'vue';
import { AiScript, parse } from '@syuilo/aiscript';
import { serialize } from '@syuilo/aiscript/built/serializer';
import { v4 as uuid } from 'uuid';
@@ -23,111 +23,101 @@ import FormInfo from '@/components/ui/info.vue';
import * as os from '@/os';
import { ColdDeviceStorage } from '@/store';
import { unisonReload } from '@/scripts/unison-reload';
+import { i18n } from '@/i18n';
import * as symbols from '@/symbols';
-export default defineComponent({
- components: {
- FormTextarea,
- FormButton,
- FormInfo,
- },
+const code = ref(null);
- emits: ['info'],
+function installPlugin({ id, meta, ast, token }) {
+ ColdDeviceStorage.set('plugins', ColdDeviceStorage.get('plugins').concat({
+ ...meta,
+ id,
+ active: true,
+ configData: {},
+ token: token,
+ ast: ast
+ }));
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts._plugin.install,
- icon: 'fas fa-download',
- bg: 'var(--bg)',
- },
- code: null,
- }
- },
+async function install() {
+ let ast;
+ try {
+ ast = parse(code.value);
+ } catch (err) {
+ os.alert({
+ type: 'error',
+ text: 'Syntax error :('
+ });
+ return;
+ }
- methods: {
- installPlugin({ id, meta, ast, token }) {
- ColdDeviceStorage.set('plugins', ColdDeviceStorage.get('plugins').concat({
- ...meta,
- id,
- active: true,
- configData: {},
- token: token,
- ast: ast
- }));
- },
+ const meta = AiScript.collectMetadata(ast);
+ if (meta == null) {
+ os.alert({
+ type: 'error',
+ text: 'No metadata found :('
+ });
+ return;
+ }
- async install() {
- let ast;
- try {
- ast = parse(this.code);
- } catch (e) {
- os.alert({
- type: 'error',
- text: 'Syntax error :('
- });
- return;
- }
- const meta = AiScript.collectMetadata(ast);
- if (meta == null) {
- os.alert({
- type: 'error',
- text: 'No metadata found :('
- });
- return;
- }
- const data = meta.get(null);
- if (data == null) {
- os.alert({
- type: 'error',
- text: 'No metadata found :('
- });
- return;
- }
- const { name, version, author, description, permissions, config } = data;
- if (name == null || version == null || author == null) {
- os.alert({
- type: 'error',
- text: 'Required property not found :('
+ const metadata = meta.get(null);
+ if (metadata == null) {
+ os.alert({
+ type: 'error',
+ text: 'No metadata found :('
+ });
+ return;
+ }
+
+ const { name, version, author, description, permissions, config } = metadata;
+ if (name == null || version == null || author == null) {
+ os.alert({
+ type: 'error',
+ text: 'Required property not found :('
+ });
+ return;
+ }
+
+ const token = permissions == null || permissions.length === 0 ? null : await new Promise((res, rej) => {
+ os.popup(defineAsyncComponent(() => import('@/components/token-generate-window.vue')), {
+ title: i18n.ts.tokenRequested,
+ information: i18n.ts.pluginTokenRequestedDescription,
+ initialName: name,
+ initialPermissions: permissions
+ }, {
+ done: async result => {
+ const { name, permissions } = result;
+ const { token } = await os.api('miauth/gen-token', {
+ session: null,
+ name: name,
+ permission: permissions,
});
- return;
+ res(token);
}
+ }, 'closed');
+ });
- const token = permissions == null || permissions.length === 0 ? null : await new Promise((res, rej) => {
- os.popup(import('@/components/token-generate-window.vue'), {
- title: this.$ts.tokenRequested,
- information: this.$ts.pluginTokenRequestedDescription,
- initialName: name,
- initialPermissions: permissions
- }, {
- done: async result => {
- const { name, permissions } = result;
- const { token } = await os.api('miauth/gen-token', {
- session: null,
- name: name,
- permission: permissions,
- });
-
- res(token);
- }
- }, 'closed');
- });
+ installPlugin({
+ id: uuid(),
+ meta: {
+ name, version, author, description, permissions, config
+ },
+ token,
+ ast: serialize(ast)
+ });
- this.installPlugin({
- id: uuid(),
- meta: {
- name, version, author, description, permissions, config
- },
- token,
- ast: serialize(ast)
- });
+ os.success();
- os.success();
+ nextTick(() => {
+ unisonReload();
+ });
+}
- this.$nextTick(() => {
- unisonReload();
- });
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts._plugin.install,
+ icon: 'fas fa-download',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/plugin.vue b/packages/client/src/pages/settings/plugin.vue
index 7a3ab9d152..873a022cbc 100644
--- a/packages/client/src/pages/settings/plugin.vue
+++ b/packages/client/src/pages/settings/plugin.vue
@@ -1,38 +1,38 @@
<template>
<div class="_formRoot">
- <FormLink to="/settings/plugin/install"><template #icon><i class="fas fa-download"></i></template>{{ $ts._plugin.install }}</FormLink>
+ <FormLink to="/settings/plugin/install"><template #icon><i class="fas fa-download"></i></template>{{ i18n.ts._plugin.install }}</FormLink>
<FormSection>
- <template #label>{{ $ts.manage }}</template>
+ <template #label>{{ i18n.ts.manage }}</template>
<div v-for="plugin in plugins" :key="plugin.id" class="_formBlock _panel" style="padding: 20px;">
<span style="display: flex;"><b>{{ plugin.name }}</b><span style="margin-left: auto;">v{{ plugin.version }}</span></span>
- <FormSwitch class="_formBlock" :modelValue="plugin.active" @update:modelValue="changeActive(plugin, $event)">{{ $ts.makeActive }}</FormSwitch>
+ <FormSwitch class="_formBlock" :modelValue="plugin.active" @update:modelValue="changeActive(plugin, $event)">{{ i18n.ts.makeActive }}</FormSwitch>
<MkKeyValue class="_formBlock">
- <template #key>{{ $ts.author }}</template>
+ <template #key>{{ i18n.ts.author }}</template>
<template #value>{{ plugin.author }}</template>
</MkKeyValue>
<MkKeyValue class="_formBlock">
- <template #key>{{ $ts.description }}</template>
+ <template #key>{{ i18n.ts.description }}</template>
<template #value>{{ plugin.description }}</template>
</MkKeyValue>
<MkKeyValue class="_formBlock">
- <template #key>{{ $ts.permission }}</template>
+ <template #key>{{ i18n.ts.permission }}</template>
<template #value>{{ plugin.permission }}</template>
</MkKeyValue>
<div style="display: flex; gap: var(--margin); flex-wrap: wrap;">
- <MkButton v-if="plugin.config" inline @click="config(plugin)"><i class="fas fa-cog"></i> {{ $ts.settings }}</MkButton>
- <MkButton inline danger @click="uninstall(plugin)"><i class="fas fa-trash-alt"></i> {{ $ts.uninstall }}</MkButton>
+ <MkButton v-if="plugin.config" inline @click="config(plugin)"><i class="fas fa-cog"></i> {{ i18n.ts.settings }}</MkButton>
+ <MkButton inline danger @click="uninstall(plugin)"><i class="fas fa-trash-alt"></i> {{ i18n.ts.uninstall }}</MkButton>
</div>
</div>
</FormSection>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, nextTick, ref } from 'vue';
import FormLink from '@/components/form/link.vue';
import FormSwitch from '@/components/form/switch.vue';
import FormSection from '@/components/form/section.vue';
@@ -41,67 +41,54 @@ import MkKeyValue from '@/components/key-value.vue';
import * as os from '@/os';
import { ColdDeviceStorage } from '@/store';
import * as symbols from '@/symbols';
+import { unisonReload } from '@/scripts/unison-reload';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormLink,
- FormSwitch,
- FormSection,
- MkButton,
- MkKeyValue,
- },
+const plugins = ref(ColdDeviceStorage.get('plugins'));
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.plugins,
- icon: 'fas fa-plug',
- bg: 'var(--bg)',
- },
- plugins: ColdDeviceStorage.get('plugins'),
- }
- },
+function uninstall(plugin) {
+ ColdDeviceStorage.set('plugins', plugins.value.filter(x => x.id !== plugin.id));
+ os.success();
+ nextTick(() => {
+ unisonReload();
+ });
+}
- methods: {
- uninstall(plugin) {
- ColdDeviceStorage.set('plugins', this.plugins.filter(x => x.id !== plugin.id));
- os.success();
- this.$nextTick(() => {
- unisonReload();
- });
- },
+// TODO: ã“ã®å‡¦ç†ã‚’storeå´ã«actionã¨ã—ã¦ç§»å‹•ã—ã€è¨­å®šç”»é¢ã‚’é–‹ãAiScriptAPIを実装ã§ãるよã†ã«ã™ã‚‹
+async function config(plugin) {
+ const config = plugin.config;
+ for (const key in plugin.configData) {
+ config[key].default = plugin.configData[key];
+ }
- // TODO: ã“ã®å‡¦ç†ã‚’storeå´ã«actionã¨ã—ã¦ç§»å‹•ã—ã€è¨­å®šç”»é¢ã‚’é–‹ãAiScriptAPIを実装ã§ãるよã†ã«ã™ã‚‹
- async config(plugin) {
- const config = plugin.config;
- for (const key in plugin.configData) {
- config[key].default = plugin.configData[key];
- }
+ const { canceled, result } = await os.form(plugin.name, config);
+ if (canceled) return;
- const { canceled, result } = await os.form(plugin.name, config);
- if (canceled) return;
+ const coldPlugins = ColdDeviceStorage.get('plugins');
+ coldPlugins.find(p => p.id === plugin.id)!.configData = result;
+ ColdDeviceStorage.set('plugins', coldPlugins);
- const plugins = ColdDeviceStorage.get('plugins');
- plugins.find(p => p.id === plugin.id).configData = result;
- ColdDeviceStorage.set('plugins', plugins);
+ nextTick(() => {
+ location.reload();
+ });
+}
- this.$nextTick(() => {
- location.reload();
- });
- },
+function changeActive(plugin, active) {
+ const coldPlugins = ColdDeviceStorage.get('plugins');
+ coldPlugins.find(p => p.id === plugin.id)!.active = active;
+ ColdDeviceStorage.set('plugins', coldPlugins);
- changeActive(plugin, active) {
- const plugins = ColdDeviceStorage.get('plugins');
- plugins.find(p => p.id === plugin.id).active = active;
- ColdDeviceStorage.set('plugins', plugins);
+ nextTick(() => {
+ location.reload();
+ });
+}
- this.$nextTick(() => {
- location.reload();
- });
- }
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.plugins,
+ icon: 'fas fa-plug',
+ bg: 'var(--bg)',
+ }
});
</script>
diff --git a/packages/client/src/pages/settings/profile.vue b/packages/client/src/pages/settings/profile.vue
index e991d725b6..b64dc93cc7 100644
--- a/packages/client/src/pages/settings/profile.vue
+++ b/packages/client/src/pages/settings/profile.vue
@@ -62,7 +62,7 @@
</template>
<script lang="ts" setup>
-import { defineComponent, reactive, watch } from 'vue';
+import { reactive, watch } from 'vue';
import MkButton from '@/components/ui/button.vue';
import FormInput from '@/components/form/input.vue';
import FormTextarea from '@/components/form/textarea.vue';
@@ -132,8 +132,21 @@ function save() {
function changeAvatar(ev) {
selectFile(ev.currentTarget ?? ev.target, i18n.ts.avatar).then(async (file) => {
+ let originalOrCropped = file;
+
+ const { canceled } = await os.confirm({
+ type: 'question',
+ text: i18n.t('cropImageAsk'),
+ });
+
+ if (!canceled) {
+ originalOrCropped = await os.cropImage(file, {
+ aspectRatio: 1,
+ });
+ }
+
const i = await os.apiWithDialog('i/update', {
- avatarId: file.id,
+ avatarId: originalOrCropped.id,
});
$i.avatarId = i.avatarId;
$i.avatarUrl = i.avatarUrl;
@@ -142,8 +155,21 @@ function changeAvatar(ev) {
function changeBanner(ev) {
selectFile(ev.currentTarget ?? ev.target, i18n.ts.banner).then(async (file) => {
+ let originalOrCropped = file;
+
+ const { canceled } = await os.confirm({
+ type: 'question',
+ text: i18n.t('cropImageAsk'),
+ });
+
+ if (!canceled) {
+ originalOrCropped = await os.cropImage(file, {
+ aspectRatio: 2,
+ });
+ }
+
const i = await os.apiWithDialog('i/update', {
- bannerId: file.id,
+ bannerId: originalOrCropped.id,
});
$i.bannerId = i.bannerId;
$i.bannerUrl = i.bannerUrl;
diff --git a/packages/client/src/pages/settings/reaction.vue b/packages/client/src/pages/settings/reaction.vue
index a188ba353d..963ac81dfa 100644
--- a/packages/client/src/pages/settings/reaction.vue
+++ b/packages/client/src/pages/settings/reaction.vue
@@ -54,7 +54,7 @@
</template>
<script lang="ts" setup>
-import { watch } from 'vue';
+import { defineAsyncComponent, watch } from 'vue';
import XDraggable from 'vuedraggable';
import FormInput from '@/components/form/input.vue';
import FormRadios from '@/components/form/radios.vue';
@@ -88,7 +88,7 @@ function remove(reaction, ev: MouseEvent) {
}
function preview(ev: MouseEvent) {
- os.popup(import('@/components/emoji-picker-dialog.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/emoji-picker-dialog.vue')), {
asReactionPicker: true,
src: ev.currentTarget ?? ev.target,
}, {}, 'closed');
diff --git a/packages/client/src/pages/settings/security.vue b/packages/client/src/pages/settings/security.vue
index 6fb3f1c413..401648790a 100644
--- a/packages/client/src/pages/settings/security.vue
+++ b/packages/client/src/pages/settings/security.vue
@@ -1,17 +1,17 @@
<template>
<div class="_formRoot">
<FormSection>
- <template #label>{{ $ts.password }}</template>
- <FormButton primary @click="change()">{{ $ts.changePassword }}</FormButton>
+ <template #label>{{ i18n.ts.password }}</template>
+ <FormButton primary @click="change()">{{ i18n.ts.changePassword }}</FormButton>
</FormSection>
<FormSection>
- <template #label>{{ $ts.twoStepAuthentication }}</template>
+ <template #label>{{ i18n.ts.twoStepAuthentication }}</template>
<X2fa/>
</FormSection>
<FormSection>
- <template #label>{{ $ts.signinHistory }}</template>
+ <template #label>{{ i18n.ts.signinHistory }}</template>
<MkPagination :pagination="pagination">
<template v-slot="{items}">
<div>
@@ -30,15 +30,15 @@
<FormSection>
<FormSlot>
- <FormButton danger @click="regenerateToken"><i class="fas fa-sync-alt"></i> {{ $ts.regenerateLoginToken }}</FormButton>
- <template #caption>{{ $ts.regenerateLoginTokenDescription }}</template>
+ <FormButton danger @click="regenerateToken"><i class="fas fa-sync-alt"></i> {{ i18n.ts.regenerateLoginToken }}</FormButton>
+ <template #caption>{{ i18n.ts.regenerateLoginTokenDescription }}</template>
</FormSlot>
</FormSection>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose } from 'vue';
import FormSection from '@/components/form/section.vue';
import FormSlot from '@/components/form/slot.vue';
import FormButton from '@/components/ui/button.vue';
@@ -46,77 +46,63 @@ import MkPagination from '@/components/ui/pagination.vue';
import X2fa from './2fa.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSection,
- FormButton,
- MkPagination,
- FormSlot,
- X2fa,
- },
-
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.security,
- icon: 'fas fa-lock',
- bg: 'var(--bg)',
- },
- pagination: {
- endpoint: 'i/signin-history' as const,
- limit: 5,
- },
- }
- },
+const pagination = {
+ endpoint: 'i/signin-history' as const,
+ limit: 5,
+};
- methods: {
- async change() {
- const { canceled: canceled1, result: currentPassword } = await os.inputText({
- title: this.$ts.currentPassword,
- type: 'password'
- });
- if (canceled1) return;
+async function change() {
+ const { canceled: canceled1, result: currentPassword } = await os.inputText({
+ title: i18n.ts.currentPassword,
+ type: 'password'
+ });
+ if (canceled1) return;
- const { canceled: canceled2, result: newPassword } = await os.inputText({
- title: this.$ts.newPassword,
- type: 'password'
- });
- if (canceled2) return;
+ const { canceled: canceled2, result: newPassword } = await os.inputText({
+ title: i18n.ts.newPassword,
+ type: 'password'
+ });
+ if (canceled2) return;
- const { canceled: canceled3, result: newPassword2 } = await os.inputText({
- title: this.$ts.newPasswordRetype,
- type: 'password'
- });
- if (canceled3) return;
+ const { canceled: canceled3, result: newPassword2 } = await os.inputText({
+ title: i18n.ts.newPasswordRetype,
+ type: 'password'
+ });
+ if (canceled3) return;
- if (newPassword !== newPassword2) {
- os.alert({
- type: 'error',
- text: this.$ts.retypedNotMatch
- });
- return;
- }
-
- os.apiWithDialog('i/change-password', {
- currentPassword,
- newPassword
- });
- },
+ if (newPassword !== newPassword2) {
+ os.alert({
+ type: 'error',
+ text: i18n.ts.retypedNotMatch
+ });
+ return;
+ }
+
+ os.apiWithDialog('i/change-password', {
+ currentPassword,
+ newPassword
+ });
+}
+
+function regenerateToken() {
+ os.inputText({
+ title: i18n.ts.password,
+ type: 'password'
+ }).then(({ canceled, result: password }) => {
+ if (canceled) return;
+ os.api('i/regenerate_token', {
+ password: password
+ });
+ });
+}
- regenerateToken() {
- os.inputText({
- title: this.$ts.password,
- type: 'password'
- }).then(({ canceled, result: password }) => {
- if (canceled) return;
- os.api('i/regenerate_token', {
- password: password
- });
- });
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.security,
+ icon: 'fas fa-lock',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/sounds.vue b/packages/client/src/pages/settings/sounds.vue
index 490a1b5514..d01e87c1f8 100644
--- a/packages/client/src/pages/settings/sounds.vue
+++ b/packages/client/src/pages/settings/sounds.vue
@@ -1,24 +1,24 @@
<template>
<div class="_formRoot">
<FormRange v-model="masterVolume" :min="0" :max="1" :step="0.05" :text-converter="(v) => `${Math.floor(v * 100)}%`" class="_formBlock">
- <template #label>{{ $ts.masterVolume }}</template>
+ <template #label>{{ i18n.ts.masterVolume }}</template>
</FormRange>
<FormSection>
- <template #label>{{ $ts.sounds }}</template>
+ <template #label>{{ i18n.ts.sounds }}</template>
<FormLink v-for="type in Object.keys(sounds)" :key="type" style="margin-bottom: 8px;" @click="edit(type)">
{{ $t('_sfx.' + type) }}
- <template #suffix>{{ sounds[type].type || $ts.none }}</template>
+ <template #suffix>{{ sounds[type].type || i18n.ts.none }}</template>
<template #suffixIcon><i class="fas fa-chevron-down"></i></template>
</FormLink>
</FormSection>
- <FormButton danger class="_formBlock" @click="reset()"><i class="fas fa-redo"></i> {{ $ts.default }}</FormButton>
+ <FormButton danger class="_formBlock" @click="reset()"><i class="fas fa-redo"></i> {{ i18n.ts.default }}</FormButton>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { computed, defineExpose, ref } from 'vue';
import FormRange from '@/components/form/range.vue';
import FormButton from '@/components/ui/button.vue';
import FormLink from '@/components/form/link.vue';
@@ -27,6 +27,28 @@ import * as os from '@/os';
import { ColdDeviceStorage } from '@/store';
import { playFile } from '@/scripts/sound';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
+
+const masterVolume = computed({
+ get: () => {
+ return ColdDeviceStorage.get('sound_masterVolume');
+ },
+ set: (value) => {
+ ColdDeviceStorage.set('sound_masterVolume', value);
+ }
+});
+
+const volumeIcon = computed(() => masterVolume.value === 0 ? 'fas fa-volume-mute' : 'fas fa-volume-up');
+
+const sounds = ref({
+ note: ColdDeviceStorage.get('sound_note'),
+ noteMy: ColdDeviceStorage.get('sound_noteMy'),
+ notification: ColdDeviceStorage.get('sound_notification'),
+ chat: ColdDeviceStorage.get('sound_chat'),
+ chatBg: ColdDeviceStorage.get('sound_chatBg'),
+ antenna: ColdDeviceStorage.get('sound_antenna'),
+ channel: ColdDeviceStorage.get('sound_channel'),
+});
const soundsTypes = [
null,
@@ -55,94 +77,58 @@ const soundsTypes = [
'noizenecio/kick_gaba2',
];
-export default defineComponent({
- components: {
- FormLink,
- FormButton,
- FormRange,
- FormSection,
- },
-
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.sounds,
- icon: 'fas fa-music',
- bg: 'var(--bg)',
- },
- sounds: {},
- }
- },
-
- computed: {
- masterVolume: { // TODO: (外部)関数ã«computedを使ã†ã®ã¯ã‚¢ãƒ¬ãªã®ã§ç›´ã™
- get() { return ColdDeviceStorage.get('sound_masterVolume'); },
- set(value) { ColdDeviceStorage.set('sound_masterVolume', value); }
+async function edit(type) {
+ const { canceled, result } = await os.form(i18n.t('_sfx.' + type), {
+ type: {
+ type: 'enum',
+ enum: soundsTypes.map(x => ({
+ value: x,
+ label: x == null ? i18n.ts.none : x,
+ })),
+ label: i18n.ts.sound,
+ default: sounds.value[type].type,
+ },
+ volume: {
+ type: 'range',
+ mim: 0,
+ max: 1,
+ step: 0.05,
+ textConverter: (v) => `${Math.floor(v * 100)}%`,
+ label: i18n.ts.volume,
+ default: sounds.value[type].volume
},
- volumeIcon() {
- return this.masterVolume === 0 ? 'fas fa-volume-mute' : 'fas fa-volume-up';
+ listen: {
+ type: 'button',
+ content: i18n.ts.listen,
+ action: (_, values) => {
+ playFile(values.type, values.volume);
+ }
}
- },
+ });
+ if (canceled) return;
- created() {
- this.sounds.note = ColdDeviceStorage.get('sound_note');
- this.sounds.noteMy = ColdDeviceStorage.get('sound_noteMy');
- this.sounds.notification = ColdDeviceStorage.get('sound_notification');
- this.sounds.chat = ColdDeviceStorage.get('sound_chat');
- this.sounds.chatBg = ColdDeviceStorage.get('sound_chatBg');
- this.sounds.antenna = ColdDeviceStorage.get('sound_antenna');
- this.sounds.channel = ColdDeviceStorage.get('sound_channel');
- },
+ const v = {
+ type: result.type,
+ volume: result.volume,
+ };
- methods: {
- async edit(type) {
- const { canceled, result } = await os.form(this.$t('_sfx.' + type), {
- type: {
- type: 'enum',
- enum: soundsTypes.map(x => ({
- value: x,
- label: x == null ? this.$ts.none : x,
- })),
- label: this.$ts.sound,
- default: this.sounds[type].type,
- },
- volume: {
- type: 'range',
- mim: 0,
- max: 1,
- step: 0.05,
- textConverter: (v) => `${Math.floor(v * 100)}%`,
- label: this.$ts.volume,
- default: this.sounds[type].volume
- },
- listen: {
- type: 'button',
- content: this.$ts.listen,
- action: (_, values) => {
- playFile(values.type, values.volume);
- }
- }
- });
- if (canceled) return;
+ ColdDeviceStorage.set('sound_' + type, v);
+ sounds.value[type] = v;
+}
- const v = {
- type: result.type,
- volume: result.volume,
- };
-
- ColdDeviceStorage.set('sound_' + type, v);
- this.sounds[type] = v;
- },
+function reset() {
+ for (const sound of Object.keys(sounds.value)) {
+ const v = ColdDeviceStorage.default['sound_' + sound];
+ ColdDeviceStorage.set('sound_' + sound, v);
+ sounds.value[sound] = v;
+ }
+}
- reset() {
- for (const sound of Object.keys(this.sounds)) {
- const v = ColdDeviceStorage.default['sound_' + sound];
- ColdDeviceStorage.set('sound_' + sound, v);
- this.sounds[sound] = v;
- }
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.sounds,
+ icon: 'fas fa-music',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/theme.install.vue b/packages/client/src/pages/settings/theme.install.vue
index 2d3514342e..25fa6c012b 100644
--- a/packages/client/src/pages/settings/theme.install.vue
+++ b/packages/client/src/pages/settings/theme.install.vue
@@ -13,7 +13,7 @@
<script lang="ts" setup>
import { } from 'vue';
-import * as JSON5 from 'json5';
+import JSON5 from 'json5';
import FormTextarea from '@/components/form/textarea.vue';
import FormButton from '@/components/ui/button.vue';
import { applyTheme, validateTheme } from '@/scripts/theme';
@@ -29,7 +29,7 @@ function parseThemeCode(code: string) {
try {
theme = JSON5.parse(code);
- } catch (e) {
+ } catch (err) {
os.alert({
type: 'error',
text: i18n.ts._theme.invalid
diff --git a/packages/client/src/pages/settings/theme.manage.vue b/packages/client/src/pages/settings/theme.manage.vue
index a1e849b540..94b2d24455 100644
--- a/packages/client/src/pages/settings/theme.manage.vue
+++ b/packages/client/src/pages/settings/theme.manage.vue
@@ -1,95 +1,77 @@
<template>
<div class="_formRoot">
<FormSelect v-model="selectedThemeId" class="_formBlock">
- <template #label>{{ $ts.theme }}</template>
- <optgroup :label="$ts._theme.installedThemes">
+ <template #label>{{ i18n.ts.theme }}</template>
+ <optgroup :label="i18n.ts._theme.installedThemes">
<option v-for="x in installedThemes" :key="x.id" :value="x.id">{{ x.name }}</option>
</optgroup>
- <optgroup :label="$ts._theme.builtinThemes">
+ <optgroup :label="i18n.ts._theme.builtinThemes">
<option v-for="x in builtinThemes" :key="x.id" :value="x.id">{{ x.name }}</option>
</optgroup>
</FormSelect>
<template v-if="selectedTheme">
- <FormInput readonly :modelValue="selectedTheme.author" class="_formBlock">
- <template #label>{{ $ts.author }}</template>
+ <FormInput readonly :model-value="selectedTheme.author" class="_formBlock">
+ <template #label>{{ i18n.ts.author }}</template>
</FormInput>
- <FormTextarea v-if="selectedTheme.desc" readonly :modelValue="selectedTheme.desc" class="_formBlock">
- <template #label>{{ $ts._theme.description }}</template>
+ <FormTextarea v-if="selectedTheme.desc" readonly :model-value="selectedTheme.desc" class="_formBlock">
+ <template #label>{{ i18n.ts._theme.description }}</template>
</FormTextarea>
- <FormTextarea readonly tall :modelValue="selectedThemeCode" class="_formBlock">
- <template #label>{{ $ts._theme.code }}</template>
- <template #caption><button class="_textButton" @click="copyThemeCode()">{{ $ts.copy }}</button></template>
+ <FormTextarea readonly tall :model-value="selectedThemeCode" class="_formBlock">
+ <template #label>{{ i18n.ts._theme.code }}</template>
+ <template #caption><button class="_textButton" @click="copyThemeCode()">{{ i18n.ts.copy }}</button></template>
</FormTextarea>
- <FormButton v-if="!builtinThemes.some(t => t.id == selectedTheme.id)" class="_formBlock" danger @click="uninstall()"><i class="fas fa-trash-alt"></i> {{ $ts.uninstall }}</FormButton>
+ <FormButton v-if="!builtinThemes.some(t => t.id == selectedTheme.id)" class="_formBlock" danger @click="uninstall()"><i class="fas fa-trash-alt"></i> {{ i18n.ts.uninstall }}</FormButton>
</template>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
-import * as JSON5 from 'json5';
+<script lang="ts" setup>
+import { computed, defineExpose, ref } from 'vue';
+import JSON5 from 'json5';
import FormTextarea from '@/components/form/textarea.vue';
import FormSelect from '@/components/form/select.vue';
import FormInput from '@/components/form/input.vue';
import FormButton from '@/components/ui/button.vue';
-import { Theme, builtinThemes } from '@/scripts/theme';
+import { Theme, getBuiltinThemesRef } from '@/scripts/theme';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import * as os from '@/os';
-import { ColdDeviceStorage } from '@/store';
import { getThemes, removeTheme } from '@/theme-store';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormTextarea,
- FormSelect,
- FormInput,
- FormButton,
- },
+const installedThemes = ref(getThemes());
+const builtinThemes = getBuiltinThemesRef();
+const selectedThemeId = ref(null);
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts._theme.manage,
- icon: 'fas fa-folder-open',
- bg: 'var(--bg)',
- },
- installedThemes: getThemes(),
- builtinThemes,
- selectedThemeId: null,
- }
- },
+const themes = computed(() => [ ...installedThemes.value, ...builtinThemes.value ]);
- computed: {
- themes(): Theme[] {
- return this.builtinThemes.concat(this.installedThemes);
- },
-
- selectedTheme() {
- if (this.selectedThemeId == null) return null;
- return this.themes.find(x => x.id === this.selectedThemeId);
- },
+const selectedTheme = computed(() => {
+ if (selectedThemeId.value == null) return null;
+ return themes.value.find(x => x.id === selectedThemeId.value);
+});
+
+const selectedThemeCode = computed(() => {
+ if (selectedTheme.value == null) return null;
+ return JSON5.stringify(selectedTheme.value, null, '\t');
+});
- selectedThemeCode() {
- if (this.selectedTheme == null) return null;
- return JSON5.stringify(this.selectedTheme, null, '\t');
- },
- },
+function copyThemeCode() {
+ copyToClipboard(selectedThemeCode.value);
+ os.success();
+}
- methods: {
- copyThemeCode() {
- copyToClipboard(this.selectedThemeCode);
- os.success();
- },
+function uninstall() {
+ removeTheme(selectedTheme.value as Theme);
+ installedThemes.value = installedThemes.value.filter(t => t.id !== selectedThemeId.value);
+ selectedThemeId.value = null;
+ os.success();
+}
- uninstall() {
- removeTheme(this.selectedTheme);
- this.installedThemes = this.installedThemes.filter(t => t.id !== this.selectedThemeId);
- this.selectedThemeId = null;
- os.success();
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts._theme.manage,
+ icon: 'fas fa-folder-open',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/theme.vue b/packages/client/src/pages/settings/theme.vue
index d134a092b6..5e7ffcff4b 100644
--- a/packages/client/src/pages/settings/theme.vue
+++ b/packages/client/src/pages/settings/theme.vue
@@ -85,116 +85,94 @@
</div>
</template>
-<script lang="ts">
-import { computed, defineComponent, onActivated, onMounted, ref, watch } from 'vue';
-import * as JSON5 from 'json5';
+<script lang="ts" setup>
+import { computed, onActivated, ref, watch } from 'vue';
+import JSON5 from 'json5';
import FormSwitch from '@/components/form/switch.vue';
import FormSelect from '@/components/form/select.vue';
-import FormGroup from '@/components/form/group.vue';
import FormSection from '@/components/form/section.vue';
import FormLink from '@/components/form/link.vue';
import FormButton from '@/components/ui/button.vue';
-import { builtinThemes } from '@/scripts/theme';
+import { getBuiltinThemesRef } from '@/scripts/theme';
import { selectFile } from '@/scripts/select-file';
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode';
import { ColdDeviceStorage } from '@/store';
import { i18n } from '@/i18n';
import { defaultStore } from '@/store';
import { instance } from '@/instance';
-import { concat, uniqueBy } from '@/scripts/array';
+import { uniqueBy } from '@/scripts/array';
import { fetchThemes, getThemes } from '@/theme-store';
import * as symbols from '@/symbols';
-export default defineComponent({
- components: {
- FormSwitch,
- FormSelect,
- FormGroup,
- FormSection,
- FormLink,
- FormButton,
- },
+const installedThemes = ref(getThemes());
+const builtinThemes = getBuiltinThemesRef();
+const instanceThemes = [];
- emits: ['info'],
+if (instance.defaultLightTheme != null) instanceThemes.push(JSON5.parse(instance.defaultLightTheme));
+if (instance.defaultDarkTheme != null) instanceThemes.push(JSON5.parse(instance.defaultDarkTheme));
- setup(props, { emit }) {
- const INFO = {
- title: i18n.ts.theme,
- icon: 'fas fa-palette',
- bg: 'var(--bg)',
- };
+const themes = computed(() => uniqueBy([ ...instanceThemes, ...builtinThemes.value, ...installedThemes.value ], theme => theme.id));
+const darkThemes = computed(() => themes.value.filter(t => t.base === 'dark' || t.kind === 'dark'));
+const lightThemes = computed(() => themes.value.filter(t => t.base === 'light' || t.kind === 'light'));
+const darkTheme = ColdDeviceStorage.ref('darkTheme');
+const darkThemeId = computed({
+ get() {
+ return darkTheme.value.id;
+ },
+ set(id) {
+ ColdDeviceStorage.set('darkTheme', themes.value.find(x => x.id === id));
+ }
+});
+const lightTheme = ColdDeviceStorage.ref('lightTheme');
+const lightThemeId = computed({
+ get() {
+ return lightTheme.value.id;
+ },
+ set(id) {
+ ColdDeviceStorage.set('lightTheme', themes.value.find(x => x.id === id));
+ }
+});
+const darkMode = computed(defaultStore.makeGetterSetter('darkMode'));
+const syncDeviceDarkMode = computed(ColdDeviceStorage.makeGetterSetter('syncDeviceDarkMode'));
+const wallpaper = ref(localStorage.getItem('wallpaper'));
+const themesCount = installedThemes.value.length;
- const installedThemes = ref(getThemes());
- const instanceThemes = [];
- if (instance.defaultLightTheme != null) instanceThemes.push(JSON5.parse(instance.defaultLightTheme));
- if (instance.defaultDarkTheme != null) instanceThemes.push(JSON5.parse(instance.defaultDarkTheme));
- const themes = computed(() => uniqueBy(instanceThemes.concat(builtinThemes.concat(installedThemes.value)), theme => theme.id));
- const darkThemes = computed(() => themes.value.filter(t => t.base === 'dark' || t.kind === 'dark'));
- const lightThemes = computed(() => themes.value.filter(t => t.base === 'light' || t.kind === 'light'));
- const darkTheme = ColdDeviceStorage.ref('darkTheme');
- const darkThemeId = computed({
- get() {
- return darkTheme.value.id;
- },
- set(id) {
- ColdDeviceStorage.set('darkTheme', themes.value.find(x => x.id === id))
- }
- });
- const lightTheme = ColdDeviceStorage.ref('lightTheme');
- const lightThemeId = computed({
- get() {
- return lightTheme.value.id;
- },
- set(id) {
- ColdDeviceStorage.set('lightTheme', themes.value.find(x => x.id === id))
- }
- });
- const darkMode = computed(defaultStore.makeGetterSetter('darkMode'));
- const syncDeviceDarkMode = computed(ColdDeviceStorage.makeGetterSetter('syncDeviceDarkMode'));
- const wallpaper = ref(localStorage.getItem('wallpaper'));
- const themesCount = installedThemes.value.length;
+watch(syncDeviceDarkMode, () => {
+ if (syncDeviceDarkMode.value) {
+ defaultStore.set('darkMode', isDeviceDarkmode());
+ }
+});
- watch(syncDeviceDarkMode, () => {
- if (syncDeviceDarkMode.value) {
- defaultStore.set('darkMode', isDeviceDarkmode());
- }
- });
+watch(wallpaper, () => {
+ if (wallpaper.value == null) {
+ localStorage.removeItem('wallpaper');
+ } else {
+ localStorage.setItem('wallpaper', wallpaper.value);
+ }
+ location.reload();
+});
- watch(wallpaper, () => {
- if (wallpaper.value == null) {
- localStorage.removeItem('wallpaper');
- } else {
- localStorage.setItem('wallpaper', wallpaper.value);
- }
- location.reload();
- });
+onActivated(() => {
+ fetchThemes().then(() => {
+ installedThemes.value = getThemes();
+ });
+});
- onActivated(() => {
- fetchThemes().then(() => {
- installedThemes.value = getThemes();
- });
- });
+fetchThemes().then(() => {
+ installedThemes.value = getThemes();
+});
- fetchThemes().then(() => {
- installedThemes.value = getThemes();
- });
+function setWallpaper(event) {
+ selectFile(event.currentTarget ?? event.target, null).then(file => {
+ wallpaper.value = file.url;
+ });
+}
- return {
- [symbols.PAGE_INFO]: INFO,
- darkThemes,
- lightThemes,
- darkThemeId,
- lightThemeId,
- darkMode,
- syncDeviceDarkMode,
- themesCount,
- wallpaper,
- setWallpaper(e) {
- selectFile(e.currentTarget ?? e.target, null).then(file => {
- wallpaper.value = file.url;
- });
- },
- };
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.theme,
+ icon: 'fas fa-palette',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/webhook.edit.vue b/packages/client/src/pages/settings/webhook.edit.vue
index bb3a25407e..3690526b41 100644
--- a/packages/client/src/pages/settings/webhook.edit.vue
+++ b/packages/client/src/pages/settings/webhook.edit.vue
@@ -43,6 +43,14 @@ import * as os from '@/os';
import * as symbols from '@/symbols';
import { i18n } from '@/i18n';
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: 'Edit webhook',
+ icon: 'fas fa-bolt',
+ bg: 'var(--bg)',
+ },
+});
+
const webhook = await os.api('i/webhooks/show', {
webhookId: new URLSearchParams(window.location.search).get('id')
});
@@ -78,12 +86,4 @@ async function save(): Promise<void> {
active,
});
}
-
-defineExpose({
- [symbols.PAGE_INFO]: {
- title: 'Edit webhook',
- icon: 'fas fa-bolt',
- bg: 'var(--bg)',
- },
-});
</script>
diff --git a/packages/client/src/pages/settings/word-mute.vue b/packages/client/src/pages/settings/word-mute.vue
index c11707b6cf..6e1a4b2ccb 100644
--- a/packages/client/src/pages/settings/word-mute.vue
+++ b/packages/client/src/pages/settings/word-mute.vue
@@ -1,35 +1,35 @@
<template>
<div class="_formRoot">
<MkTab v-model="tab" class="_formBlock">
- <option value="soft">{{ $ts._wordMute.soft }}</option>
- <option value="hard">{{ $ts._wordMute.hard }}</option>
+ <option value="soft">{{ i18n.ts._wordMute.soft }}</option>
+ <option value="hard">{{ i18n.ts._wordMute.hard }}</option>
</MkTab>
<div class="_formBlock">
<div v-show="tab === 'soft'">
- <MkInfo class="_formBlock">{{ $ts._wordMute.softDescription }}</MkInfo>
+ <MkInfo class="_formBlock">{{ i18n.ts._wordMute.softDescription }}</MkInfo>
<FormTextarea v-model="softMutedWords" class="_formBlock">
- <span>{{ $ts._wordMute.muteWords }}</span>
- <template #caption>{{ $ts._wordMute.muteWordsDescription }}<br>{{ $ts._wordMute.muteWordsDescription2 }}</template>
+ <span>{{ i18n.ts._wordMute.muteWords }}</span>
+ <template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template>
</FormTextarea>
</div>
<div v-show="tab === 'hard'">
- <MkInfo class="_formBlock">{{ $ts._wordMute.hardDescription }} {{ $ts.reflectMayTakeTime }}</MkInfo>
+ <MkInfo class="_formBlock">{{ i18n.ts._wordMute.hardDescription }} {{ i18n.ts.reflectMayTakeTime }}</MkInfo>
<FormTextarea v-model="hardMutedWords" class="_formBlock">
- <span>{{ $ts._wordMute.muteWords }}</span>
- <template #caption>{{ $ts._wordMute.muteWordsDescription }}<br>{{ $ts._wordMute.muteWordsDescription2 }}</template>
+ <span>{{ i18n.ts._wordMute.muteWords }}</span>
+ <template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template>
</FormTextarea>
<MkKeyValue v-if="hardWordMutedNotesCount != null" class="_formBlock">
- <template #key>{{ $ts._wordMute.mutedNotes }}</template>
+ <template #key>{{ i18n.ts._wordMute.mutedNotes }}</template>
<template #value>{{ number(hardWordMutedNotesCount) }}</template>
</MkKeyValue>
</div>
</div>
- <MkButton primary inline :disabled="!changed" @click="save()"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
+ <MkButton primary inline :disabled="!changed" @click="save()"><i class="fas fa-save"></i> {{ i18n.ts.save }}</MkButton>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, ref, watch } from 'vue';
import FormTextarea from '@/components/form/textarea.vue';
import MkKeyValue from '@/components/key-value.vue';
import MkButton from '@/components/ui/button.vue';
@@ -38,114 +38,90 @@ import MkTab from '@/components/tab.vue';
import * as os from '@/os';
import number from '@/filters/number';
import * as symbols from '@/symbols';
+import { defaultStore } from '@/store';
+import { $i } from '@/account';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- FormTextarea,
- MkKeyValue,
- MkTab,
- MkInfo,
- },
-
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.wordMute,
- icon: 'fas fa-comment-slash',
- bg: 'var(--bg)',
- },
- tab: 'soft',
- softMutedWords: '',
- hardMutedWords: '',
- hardWordMutedNotesCount: null,
- changed: false,
- }
- },
+const render = (mutedWords) => mutedWords.map(x => {
+ if (Array.isArray(x)) {
+ return x.join(' ');
+ } else {
+ return x;
+ }
+}).join('\n');
- watch: {
- softMutedWords: {
- handler() {
- this.changed = true;
- },
- deep: true
- },
- hardMutedWords: {
- handler() {
- this.changed = true;
- },
- deep: true
- },
- },
+const tab = ref('soft');
+const softMutedWords = ref(render(defaultStore.state.mutedWords));
+const hardMutedWords = ref(render($i!.mutedWords));
+const hardWordMutedNotesCount = ref(null);
+const changed = ref(false);
- async created() {
- const render = (mutedWords) => mutedWords.map(x => {
- if (Array.isArray(x)) {
- return x.join(' ');
- } else {
- return x;
- }
- }).join('\n');
+os.api('i/get-word-muted-notes-count', {}).then(response => {
+ hardWordMutedNotesCount.value = response?.count;
+});
- this.softMutedWords = render(this.$store.state.mutedWords);
- this.hardMutedWords = render(this.$i.mutedWords);
+watch(softMutedWords, () => {
+ changed.value = true;
+});
- this.hardWordMutedNotesCount = (await os.api('i/get-word-muted-notes-count', {})).count;
- },
+watch(hardMutedWords, () => {
+ changed.value = true;
+});
- methods: {
- async save() {
- const parseMutes = (mutes, tab) => {
- // split into lines, remove empty lines and unnecessary whitespace
- let lines = mutes.trim().split('\n').map(line => line.trim()).filter(line => line != '');
+async function save() {
+ const parseMutes = (mutes, tab) => {
+ // split into lines, remove empty lines and unnecessary whitespace
+ let lines = mutes.trim().split('\n').map(line => line.trim()).filter(line => line !== '');
- // check each line if it is a RegExp or not
- for (let i = 0; i < lines.length; i++) {
- const line = lines[i]
- const regexp = line.match(/^\/(.+)\/(.*)$/);
- if (regexp) {
- // check that the RegExp is valid
- try {
- new RegExp(regexp[1], regexp[2]);
- // note that regex lines will not be split by spaces!
- } catch (err) {
- // invalid syntax: do not save, do not reset changed flag
- os.alert({
- type: 'error',
- title: this.$ts.regexpError,
- text: this.$t('regexpErrorDescription', { tab, line: i + 1 }) + "\n" + err.toString()
- });
- // re-throw error so these invalid settings are not saved
- throw err;
- }
- } else {
- lines[i] = line.split(' ');
- }
+ // check each line if it is a RegExp or not
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i];
+ const regexp = line.match(/^\/(.+)\/(.*)$/);
+ if (regexp) {
+ // check that the RegExp is valid
+ try {
+ new RegExp(regexp[1], regexp[2]);
+ // note that regex lines will not be split by spaces!
+ } catch (err: any) {
+ // invalid syntax: do not save, do not reset changed flag
+ os.alert({
+ type: 'error',
+ title: i18n.ts.regexpError,
+ text: i18n.t('regexpErrorDescription', { tab, line: i + 1 }) + "\n" + err.toString()
+ });
+ // re-throw error so these invalid settings are not saved
+ throw err;
}
+ } else {
+ lines[i] = line.split(' ');
+ }
+ }
- return lines;
- };
+ return lines;
+ };
- let softMutes, hardMutes;
- try {
- softMutes = parseMutes(this.softMutedWords, this.$ts._wordMute.soft);
- hardMutes = parseMutes(this.hardMutedWords, this.$ts._wordMute.hard);
- } catch (err) {
- // already displayed error message in parseMutes
- return;
- }
+ let softMutes, hardMutes;
+ try {
+ softMutes = parseMutes(softMutedWords.value, i18n.ts._wordMute.soft);
+ hardMutes = parseMutes(hardMutedWords.value, i18n.ts._wordMute.hard);
+ } catch (err) {
+ // already displayed error message in parseMutes
+ return;
+ }
- this.$store.set('mutedWords', softMutes);
- await os.api('i/update', {
- mutedWords: hardMutes,
- });
+ defaultStore.set('mutedWords', softMutes);
+ await os.api('i/update', {
+ mutedWords: hardMutes,
+ });
- this.changed = false;
- },
+ changed.value = false;
+}
- number
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.wordMute,
+ icon: 'fas fa-comment-slash',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/share.vue b/packages/client/src/pages/share.vue
index 4d77de5819..1700944f82 100644
--- a/packages/client/src/pages/share.vue
+++ b/packages/client/src/pages/share.vue
@@ -56,7 +56,7 @@ export default defineComponent({
localOnly: null as boolean | null,
files: [] as Misskey.entities.DriveFile[],
visibleUsers: [] as Misskey.entities.User[],
- }
+ };
},
async created() {
@@ -153,11 +153,11 @@ export default defineComponent({
);
}
//#endregion
- } catch (e) {
+ } catch (err) {
os.alert({
type: 'error',
- title: e.message,
- text: e.name
+ title: err.message,
+ text: err.name
});
}
diff --git a/packages/client/src/pages/theme-editor.vue b/packages/client/src/pages/theme-editor.vue
index a53e23c1c5..2a11c07fd2 100644
--- a/packages/client/src/pages/theme-editor.vue
+++ b/packages/client/src/pages/theme-editor.vue
@@ -67,15 +67,17 @@
<script lang="ts" setup>
import { watch } from 'vue';
import { toUnicode } from 'punycode/';
-import * as tinycolor from 'tinycolor2';
-import { v4 as uuid} from 'uuid';
-import * as JSON5 from 'json5';
+import tinycolor from 'tinycolor2';
+import { v4 as uuid } from 'uuid';
+import JSON5 from 'json5';
import FormButton from '@/components/ui/button.vue';
import FormTextarea from '@/components/form/textarea.vue';
import FormFolder from '@/components/form/folder.vue';
-import { Theme, applyTheme, darkTheme, lightTheme } from '@/scripts/theme';
+import { Theme, applyTheme } from '@/scripts/theme';
+import lightTheme from '@/themes/_light.json5';
+import darkTheme from '@/themes/_dark.json5';
import { host } from '@/config';
import * as os from '@/os';
import { ColdDeviceStorage, defaultStore } from '@/store';
@@ -124,11 +126,11 @@ let changed = $ref(false);
useLeaveGuard($$(changed));
function showPreview() {
- os.pageWindow('preview');
+ os.pageWindow('/preview');
}
function setBgColor(color: typeof bgColors[number]) {
- if (theme.base != color.kind) {
+ if (theme.base !== color.kind) {
const base = color.kind === 'dark' ? darkTheme : lightTheme;
for (const prop of Object.keys(base.props)) {
if (prop === 'accent') continue;
diff --git a/packages/client/src/pages/timeline.vue b/packages/client/src/pages/timeline.vue
index 79f00c4b44..fe3dbc3cff 100644
--- a/packages/client/src/pages/timeline.vue
+++ b/packages/client/src/pages/timeline.vue
@@ -20,7 +20,7 @@
<script lang="ts">
export default {
name: 'MkTimelinePage',
-}
+};
</script>
<script lang="ts" setup>
diff --git a/packages/client/src/pages/user-info.vue b/packages/client/src/pages/user-info.vue
index 516ab4d440..54e1f13021 100644
--- a/packages/client/src/pages/user-info.vue
+++ b/packages/client/src/pages/user-info.vue
@@ -54,6 +54,9 @@
<FormButton v-if="user.host != null" class="_formBlock" @click="updateRemoteUser"><i class="fas fa-sync"></i> {{ $ts.updateRemoteUser }}</FormButton>
</FormSection>
+ <MkObjectView v-if="info && $i.isAdmin" tall :value="info">
+ </MkObjectView>
+
<MkObjectView tall :value="user">
</MkObjectView>
</div>
@@ -232,10 +235,10 @@ export default defineComponent({
await os.api('admin/delete-all-files-of-a-user', { userId: this.user.id });
os.success();
};
- await process().catch(e => {
+ await process().catch(err => {
os.alert({
type: 'error',
- text: e.toString()
+ text: err.toString(),
});
});
await this.refreshUser();
diff --git a/packages/client/src/pages/user/index.vue b/packages/client/src/pages/user/index.vue
index 10a86243f9..a024dd28bc 100644
--- a/packages/client/src/pages/user/index.vue
+++ b/packages/client/src/pages/user/index.vue
@@ -125,7 +125,7 @@
<script lang="ts">
import { defineComponent, defineAsyncComponent, computed } from 'vue';
-import * as age from 's-age';
+import age from 's-age';
import XUserTimeline from './index.timeline.vue';
import XNote from '@/components/note.vue';
import MkFollowButton from '@/components/follow-button.vue';
@@ -141,6 +141,7 @@ import number from '@/filters/number';
import { userPage, acct as getAcct } from '@/filters/user';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { MisskeyNavigator } from '@/scripts/navigate';
export default defineComponent({
components: {
@@ -190,33 +191,34 @@ export default defineComponent({
active: this.page === 'index',
title: this.$ts.overview,
icon: 'fas fa-home',
- onClick: () => { this.$router.push('/@' + getAcct(this.user)); },
+ onClick: () => { this.mkNav.push('/@' + getAcct(this.user)); },
}, ...(this.$i && (this.$i.id === this.user.id)) || this.user.publicReactions ? [{
active: this.page === 'reactions',
title: this.$ts.reaction,
icon: 'fas fa-laugh',
- onClick: () => { this.$router.push('/@' + getAcct(this.user) + '/reactions'); },
+ onClick: () => { this.mkNav.push('/@' + getAcct(this.user) + '/reactions'); },
}] : [], {
active: this.page === 'clips',
title: this.$ts.clips,
icon: 'fas fa-paperclip',
- onClick: () => { this.$router.push('/@' + getAcct(this.user) + '/clips'); },
+ onClick: () => { this.mkNav.push('/@' + getAcct(this.user) + '/clips'); },
}, {
active: this.page === 'pages',
title: this.$ts.pages,
icon: 'fas fa-file-alt',
- onClick: () => { this.$router.push('/@' + getAcct(this.user) + '/pages'); },
+ onClick: () => { this.mkNav.push('/@' + getAcct(this.user) + '/pages'); },
}, {
active: this.page === 'gallery',
title: this.$ts.gallery,
icon: 'fas fa-icons',
- onClick: () => { this.$router.push('/@' + getAcct(this.user) + '/gallery'); },
+ onClick: () => { this.mkNav.push('/@' + getAcct(this.user) + '/gallery'); },
}],
} : null),
user: null,
error: null,
parallaxAnimationId: null,
narrow: null,
+ mkNav: new MisskeyNavigator(),
};
},
@@ -258,8 +260,8 @@ export default defineComponent({
this.user = null;
os.api('users/show', Acct.parse(this.acct)).then(user => {
this.user = user;
- }).catch(e => {
- this.error = e;
+ }).catch(err => {
+ this.error = err;
});
},
diff --git a/packages/client/src/pages/welcome.setup.vue b/packages/client/src/pages/welcome.setup.vue
index ec23b76e29..1a2f460283 100644
--- a/packages/client/src/pages/welcome.setup.vue
+++ b/packages/client/src/pages/welcome.setup.vue
@@ -41,7 +41,7 @@ export default defineComponent({
password: '',
submitting: false,
host,
- }
+ };
},
methods: {
diff --git a/packages/client/src/pages/welcome.timeline.vue b/packages/client/src/pages/welcome.timeline.vue
index 38a85f67b1..bec9481ffd 100644
--- a/packages/client/src/pages/welcome.timeline.vue
+++ b/packages/client/src/pages/welcome.timeline.vue
@@ -39,7 +39,7 @@ export default defineComponent({
return {
notes: [],
isScrolling: false,
- }
+ };
},
created() {
diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts
index 839841f0fe..db39dd741c 100644
--- a/packages/client/src/router.ts
+++ b/packages/client/src/router.ts
@@ -1,4 +1,4 @@
-import { defineAsyncComponent, markRaw } from 'vue';
+import { AsyncComponentLoader, defineAsyncComponent, markRaw } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
import MkLoading from '@/pages/_loading_.vue';
import MkError from '@/pages/_error_.vue';
@@ -6,8 +6,9 @@ import MkTimeline from '@/pages/timeline.vue';
import { $i, iAmModerator } from './account';
import { ui } from '@/config';
-const page = (path: string, ui?: string) => defineAsyncComponent({
- loader: ui ? () => import(`./ui/${ui}/pages/${path}.vue`) : () => import(`./pages/${path}.vue`),
+// pathã«/ãŒå…¥ã‚‹ã¨rollupãŒè§£æ±ºã—ã¦ãれãªã„ã®ã§ã€() => import('*.vue')を指定ã™ã‚‹ã“ã¨
+const page = (path: string | AsyncComponentLoader<any>, uiName?: string) => defineAsyncComponent({
+ loader: typeof path === 'string' ? uiName ? () => import(`./ui/${ui}/pages/${path}.vue`) : () => import(`./pages/${path}.vue`) : path,
loadingComponent: MkLoading,
errorComponent: MkError,
});
@@ -17,10 +18,10 @@ let indexScrollPos = 0;
const defaultRoutes = [
// NOTE: MkTimelineã‚’dynamic importã™ã‚‹ã¨AsyncComponentWrapperãŒé–“ã«å…¥ã‚‹ã›ã„ã§keep-aliveã®ã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆæŒ‡å®šãŒåйã‹ãªããªã‚‹
{ path: '/', name: 'index', component: $i ? MkTimeline : page('welcome') },
- { path: '/@:acct/:page?', name: 'user', component: page('user/index'), props: route => ({ acct: route.params.acct, page: route.params.page || 'index' }) },
+ { path: '/@:acct/:page?', name: 'user', component: page(() => import('./pages/user/index.vue')), props: route => ({ acct: route.params.acct, page: route.params.page || 'index' }) },
{ path: '/@:user/pages/:page', component: page('page'), props: route => ({ pageName: route.params.page, username: route.params.user }) },
- { path: '/@:user/pages/:pageName/view-source', component: page('page-editor/page-editor'), props: route => ({ initUser: route.params.user, initPageName: route.params.pageName }) },
- { path: '/settings/:page(.*)?', name: 'settings', component: page('settings/index'), props: route => ({ initialPage: route.params.page || null }) },
+ { path: '/@:user/pages/:pageName/view-source', component: page(() => import('./pages/page-editor/page-editor.vue')), props: route => ({ initUser: route.params.user, initPageName: route.params.pageName }) },
+ { path: '/settings/:page(.*)?', name: 'settings', component: page(() => import('./pages/settings/index.vue')), props: route => ({ initialPage: route.params.page || null }) },
{ path: '/reset-password/:token?', component: page('reset-password'), props: route => ({ token: route.params.token }) },
{ path: '/signup-complete/:code', component: page('signup-complete'), props: route => ({ code: route.params.code }) },
{ path: '/announcements', component: page('announcements') },
@@ -35,12 +36,12 @@ const defaultRoutes = [
{ path: '/emojis', component: page('emojis') },
{ path: '/search', component: page('search'), props: route => ({ query: route.query.q, channel: route.query.channel }) },
{ path: '/pages', name: 'pages', component: page('pages') },
- { path: '/pages/new', component: page('page-editor/page-editor') },
- { path: '/pages/edit/:pageId', component: page('page-editor/page-editor'), props: route => ({ initPageId: route.params.pageId }) },
- { path: '/gallery', component: page('gallery/index') },
- { path: '/gallery/new', component: page('gallery/edit') },
- { path: '/gallery/:postId/edit', component: page('gallery/edit'), props: route => ({ postId: route.params.postId }) },
- { path: '/gallery/:postId', component: page('gallery/post'), props: route => ({ postId: route.params.postId }) },
+ { path: '/pages/new', component: page(() => import('./pages/page-editor/page-editor.vue')) },
+ { path: '/pages/edit/:pageId', component: page(() => import('./pages/page-editor/page-editor.vue')), props: route => ({ initPageId: route.params.pageId }) },
+ { path: '/gallery', component: page(() => import('./pages/gallery/index.vue')) },
+ { path: '/gallery/new', component: page(() => import('./pages/gallery/edit.vue')) },
+ { path: '/gallery/:postId/edit', component: page(() => import('./pages/gallery/edit.vue')), props: route => ({ postId: route.params.postId }) },
+ { path: '/gallery/:postId', component: page(() => import('./pages/gallery/edit.vue')), props: route => ({ postId: route.params.postId }) },
{ path: '/channels', component: page('channels') },
{ path: '/channels/new', component: page('channel-editor') },
{ path: '/channels/:channelId/edit', component: page('channel-editor'), props: true },
@@ -52,23 +53,23 @@ const defaultRoutes = [
{ path: '/my/favorites', component: page('favorites') },
{ path: '/my/messages', component: page('messages') },
{ path: '/my/mentions', component: page('mentions') },
- { path: '/my/messaging', name: 'messaging', component: page('messaging/index') },
- { path: '/my/messaging/:user', component: page('messaging/messaging-room'), props: route => ({ userAcct: route.params.user }) },
- { path: '/my/messaging/group/:group', component: page('messaging/messaging-room'), props: route => ({ groupId: route.params.group }) },
+ { path: '/my/messaging', name: 'messaging', component: page(() => import('./pages/messaging/index.vue')) },
+ { path: '/my/messaging/:user', component: page(() => import('./pages/messaging/messaging-room.vue')), props: route => ({ userAcct: route.params.user }) },
+ { path: '/my/messaging/group/:group', component: page(() => import('./pages/messaging/messaging-room.vue')), props: route => ({ groupId: route.params.group }) },
{ path: '/my/drive', name: 'drive', component: page('drive') },
{ path: '/my/drive/folder/:folder', component: page('drive') },
{ path: '/my/follow-requests', component: page('follow-requests') },
- { path: '/my/lists', component: page('my-lists/index') },
- { path: '/my/lists/:list', component: page('my-lists/list') },
- { path: '/my/groups', component: page('my-groups/index') },
- { path: '/my/groups/:group', component: page('my-groups/group'), props: route => ({ groupId: route.params.group }) },
- { path: '/my/antennas', component: page('my-antennas/index') },
- { path: '/my/antennas/create', component: page('my-antennas/create') },
- { path: '/my/antennas/:antennaId', component: page('my-antennas/edit'), props: true },
- { path: '/my/clips', component: page('my-clips/index') },
+ { path: '/my/lists', component: page(() => import('./pages/my-lists/index.vue')) },
+ { path: '/my/lists/:list', component: page(() => import('./pages/my-lists/list.vue')) },
+ { path: '/my/groups', component: page(() => import('./pages/my-groups/index.vue')) },
+ { path: '/my/groups/:group', component: page(() => import('./pages/my-groups/group.vue')), props: route => ({ groupId: route.params.group }) },
+ { path: '/my/antennas', component: page(() => import('./pages/my-antennas/index.vue')) },
+ { path: '/my/antennas/create', component: page(() => import('./pages/my-antennas/create.vue')) },
+ { path: '/my/antennas/:antennaId', component: page(() => import('./pages/my-antennas/edit.vue')), props: true },
+ { path: '/my/clips', component: page(() => import('./pages/my-clips/index.vue')) },
{ path: '/scratchpad', component: page('scratchpad') },
- { path: '/admin/:page(.*)?', component: iAmModerator ? page('admin/index') : page('not-found'), props: route => ({ initialPage: route.params.page || null }) },
- { path: '/admin', component: iAmModerator ? page('admin/index') : page('not-found') },
+ { path: '/admin/:page(.*)?', component: iAmModerator ? page(() => import('./pages/admin/index.vue')) : page('not-found'), props: route => ({ initialPage: route.params.page || null }) },
+ { path: '/admin', component: iAmModerator ? page(() => import('./pages/admin/index.vue')) : page('not-found') },
{ path: '/notes/:note', name: 'note', component: page('note'), props: route => ({ noteId: route.params.note }) },
{ path: '/tags/:tag', component: page('tag'), props: route => ({ tag: route.params.tag }) },
{ path: '/user-info/:user', component: page('user-info'), props: route => ({ userId: route.params.user }) },
diff --git a/packages/client/src/scripts/2fa.ts b/packages/client/src/scripts/2fa.ts
index 00363cffa6..d1b9581e72 100644
--- a/packages/client/src/scripts/2fa.ts
+++ b/packages/client/src/scripts/2fa.ts
@@ -1,11 +1,11 @@
-export function byteify(data: string, encoding: 'ascii' | 'base64' | 'hex') {
+export function byteify(string: string, encoding: 'ascii' | 'base64' | 'hex') {
switch (encoding) {
case 'ascii':
- return Uint8Array.from(data, c => c.charCodeAt(0));
+ return Uint8Array.from(string, c => c.charCodeAt(0));
case 'base64':
return Uint8Array.from(
atob(
- data
+ string
.replace(/-/g, '+')
.replace(/_/g, '/')
),
@@ -13,7 +13,7 @@ export function byteify(data: string, encoding: 'ascii' | 'base64' | 'hex') {
);
case 'hex':
return new Uint8Array(
- data
+ string
.match(/.{1,2}/g)
.map(byte => parseInt(byte, 16))
);
diff --git a/packages/client/src/scripts/autocomplete.ts b/packages/client/src/scripts/autocomplete.ts
index f4a3a4c0fc..8d9bdee8f5 100644
--- a/packages/client/src/scripts/autocomplete.ts
+++ b/packages/client/src/scripts/autocomplete.ts
@@ -1,5 +1,5 @@
-import { nextTick, Ref, ref } from 'vue';
-import * as getCaretCoordinates from 'textarea-caret';
+import { nextTick, Ref, ref, defineAsyncComponent } from 'vue';
+import getCaretCoordinates from 'textarea-caret';
import { toASCII } from 'punycode/';
import { popup } from '@/os';
@@ -74,21 +74,21 @@ export class Autocomplete {
emojiIndex,
mfmTagIndex);
- if (max == -1) {
+ if (max === -1) {
this.close();
return;
}
- const isMention = mentionIndex != -1;
- const isHashtag = hashtagIndex != -1;
- const isMfmTag = mfmTagIndex != -1;
- const isEmoji = emojiIndex != -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':');
+ const isMention = mentionIndex !== -1;
+ const isHashtag = hashtagIndex !== -1;
+ const isMfmTag = mfmTagIndex !== -1;
+ const isEmoji = emojiIndex !== -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':');
let opened = false;
if (isMention) {
const username = text.substr(mentionIndex + 1);
- if (username != '' && username.match(/^[a-zA-Z0-9_]+$/)) {
+ if (username !== '' && username.match(/^[a-zA-Z0-9_]+$/)) {
this.open('user', username);
opened = true;
} else if (username === '') {
@@ -130,7 +130,7 @@ export class Autocomplete {
* サジェストをæç¤ºã—ã¾ã™ã€‚
*/
private async open(type: string, q: string | null) {
- if (type != this.currentType) {
+ if (type !== this.currentType) {
this.close();
}
if (this.opening) return;
@@ -157,7 +157,7 @@ export class Autocomplete {
const _y = ref(y);
const _q = ref(q);
- const { dispose } = await popup(import('@/components/autocomplete.vue'), {
+ const { dispose } = await popup(defineAsyncComponent(() => import('@/components/autocomplete.vue')), {
textarea: this.textarea,
close: this.close,
type: type,
@@ -201,7 +201,7 @@ export class Autocomplete {
const caret = this.textarea.selectionStart;
- if (type == 'user') {
+ if (type === 'user') {
const source = this.text;
const before = source.substr(0, caret);
@@ -219,7 +219,7 @@ export class Autocomplete {
const pos = trimmedBefore.length + (acct.length + 2);
this.textarea.setSelectionRange(pos, pos);
});
- } else if (type == 'hashtag') {
+ } else if (type === 'hashtag') {
const source = this.text;
const before = source.substr(0, caret);
@@ -235,7 +235,7 @@ export class Autocomplete {
const pos = trimmedBefore.length + (value.length + 2);
this.textarea.setSelectionRange(pos, pos);
});
- } else if (type == 'emoji') {
+ } else if (type === 'emoji') {
const source = this.text;
const before = source.substr(0, caret);
@@ -251,7 +251,7 @@ export class Autocomplete {
const pos = trimmedBefore.length + value.length;
this.textarea.setSelectionRange(pos, pos);
});
- } else if (type == 'mfmTag') {
+ } else if (type === 'mfmTag') {
const source = this.text;
const before = source.substr(0, caret);
diff --git a/packages/client/src/scripts/contains.ts b/packages/client/src/scripts/contains.ts
index 770bda63bb..256e09d293 100644
--- a/packages/client/src/scripts/contains.ts
+++ b/packages/client/src/scripts/contains.ts
@@ -2,7 +2,7 @@ export default (parent, child, checkSame = true) => {
if (checkSame && parent === child) return true;
let node = child.parentNode;
while (node) {
- if (node == parent) return true;
+ if (node === parent) return true;
node = node.parentNode;
}
return false;
diff --git a/packages/client/src/scripts/emojilist.ts b/packages/client/src/scripts/emojilist.ts
index bd8689e4f8..4196170d24 100644
--- a/packages/client/src/scripts/emojilist.ts
+++ b/packages/client/src/scripts/emojilist.ts
@@ -8,4 +8,4 @@ export type UnicodeEmojiDef = {
}
// initial converted from https://github.com/muan/emojilib/commit/242fe68be86ed6536843b83f7e32f376468b38fb
-export const emojilist = require('../emojilist.json') as UnicodeEmojiDef[];
+export const emojilist = (await import('../emojilist.json')).default as UnicodeEmojiDef[];
diff --git a/packages/client/src/scripts/extract-avg-color-from-blurhash.ts b/packages/client/src/scripts/extract-avg-color-from-blurhash.ts
index 123ab7a06d..af517f2672 100644
--- a/packages/client/src/scripts/extract-avg-color-from-blurhash.ts
+++ b/packages/client/src/scripts/extract-avg-color-from-blurhash.ts
@@ -1,5 +1,5 @@
export function extractAvgColorFromBlurhash(hash: string) {
- return typeof hash == 'string'
+ return typeof hash === 'string'
? '#' + [...hash.slice(2, 6)]
.map(x => '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~'.indexOf(x))
.reduce((a, c) => a * 83 + c, 0)
diff --git a/packages/client/src/scripts/format-time-string.ts b/packages/client/src/scripts/format-time-string.ts
index bfb2c397ae..fb4718c007 100644
--- a/packages/client/src/scripts/format-time-string.ts
+++ b/packages/client/src/scripts/format-time-string.ts
@@ -13,7 +13,7 @@ const defaultLocaleStringFormats: {[index: string]: string} = {
function formatLocaleString(date: Date, format: string): string {
return format.replace(/\{\{(\w+)(:(\w+))?\}\}/g, (match: string, kind: string, unused?, option?: string) => {
if (['weekday', 'era', 'year', 'month', 'day', 'hour', 'minute', 'second', 'timeZoneName'].includes(kind)) {
- return date.toLocaleString(window.navigator.language, {[kind]: option ? option : defaultLocaleStringFormats[kind]});
+ return date.toLocaleString(window.navigator.language, { [kind]: option ? option : defaultLocaleStringFormats[kind] });
} else {
return match;
}
@@ -24,8 +24,8 @@ export function formatDateTimeString(date: Date, format: string): string {
return format
.replace(/yyyy/g, date.getFullYear().toString())
.replace(/yy/g, date.getFullYear().toString().slice(-2))
- .replace(/MMMM/g, date.toLocaleString(window.navigator.language, { month: 'long'}))
- .replace(/MMM/g, date.toLocaleString(window.navigator.language, { month: 'short'}))
+ .replace(/MMMM/g, date.toLocaleString(window.navigator.language, { month: 'long' }))
+ .replace(/MMM/g, date.toLocaleString(window.navigator.language, { month: 'short' }))
.replace(/MM/g, (`0${date.getMonth() + 1}`).slice(-2))
.replace(/M/g, (date.getMonth() + 1).toString())
.replace(/dd/g, (`0${date.getDate()}`).slice(-2))
diff --git a/packages/client/src/scripts/get-account-from-id.ts b/packages/client/src/scripts/get-account-from-id.ts
index ba3adceecc..1da897f176 100644
--- a/packages/client/src/scripts/get-account-from-id.ts
+++ b/packages/client/src/scripts/get-account-from-id.ts
@@ -3,5 +3,5 @@ import { get } from '@/scripts/idb-proxy';
export async function getAccountFromId(id: string) {
const accounts = await get('accounts') as { token: string; id: string; }[];
if (!accounts) console.log('Accounts are not recorded');
- return accounts.find(e => e.id === id);
+ return accounts.find(account => account.id === id);
}
diff --git a/packages/client/src/scripts/get-md5.ts b/packages/client/src/scripts/get-md5.ts
deleted file mode 100644
index b002d762b1..0000000000
--- a/packages/client/src/scripts/get-md5.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-// スクリプトサイズãŒãƒ‡ã‚«ã„
-//import * as crypto from 'crypto';
-
-export default (data: ArrayBuffer) => {
- //const buf = new Buffer(data);
- //const hash = crypto.createHash('md5');
- //hash.update(buf);
- //return hash.digest('hex');
- return '';
-};
diff --git a/packages/client/src/scripts/get-note-menu.ts b/packages/client/src/scripts/get-note-menu.ts
index b19656d3cc..78749ad6bb 100644
--- a/packages/client/src/scripts/get-note-menu.ts
+++ b/packages/client/src/scripts/get-note-menu.ts
@@ -1,4 +1,4 @@
-import { Ref } from 'vue';
+import { defineAsyncComponent, Ref } from 'vue';
import * as misskey from 'misskey-js';
import { $i } from '@/account';
import { i18n } from '@/i18n';
@@ -22,7 +22,7 @@ export function getNoteMenu(props: {
props.note.poll == null
);
- let appearNote = isRenote ? props.note.renote as misskey.entities.Note : props.note;
+ const appearNote = isRenote ? props.note.renote as misskey.entities.Note : props.note;
function del(): void {
os.confirm({
@@ -83,8 +83,8 @@ export function getNoteMenu(props: {
function togglePin(pin: boolean): void {
os.apiWithDialog(pin ? 'i/pin' : 'i/unpin', {
noteId: appearNote.id
- }, undefined, null, e => {
- if (e.id === '72dab508-c64d-498f-8740-a8eec1ba385a') {
+ }, undefined, null, res => {
+ if (res.id === '72dab508-c64d-498f-8740-a8eec1ba385a') {
os.alert({
type: 'error',
text: i18n.ts.pinLimitExceeded
@@ -209,7 +209,7 @@ export function getNoteMenu(props: {
text: i18n.ts.clip,
action: () => clip()
},
- (appearNote.userId != $i.id) ? statePromise.then(state => state.isWatching ? {
+ (appearNote.userId !== $i.id) ? statePromise.then(state => state.isWatching ? {
icon: 'fas fa-eye-slash',
text: i18n.ts.unwatch,
action: () => toggleWatch(false)
@@ -227,7 +227,7 @@ export function getNoteMenu(props: {
text: i18n.ts.muteThread,
action: () => toggleThreadMute(true)
}),
- appearNote.userId == $i.id ? ($i.pinnedNoteIds || []).includes(appearNote.id) ? {
+ appearNote.userId === $i.id ? ($i.pinnedNoteIds || []).includes(appearNote.id) ? {
icon: 'fas fa-thumbtack',
text: i18n.ts.unpin,
action: () => togglePin(false)
@@ -246,14 +246,14 @@ export function getNoteMenu(props: {
}]
: []
),*/
- ...(appearNote.userId != $i.id ? [
+ ...(appearNote.userId !== $i.id ? [
null,
{
icon: 'fas fa-exclamation-circle',
text: i18n.ts.reportAbuse,
action: () => {
const u = appearNote.url || appearNote.uri || `${url}/notes/${appearNote.id}`;
- os.popup(import('@/components/abuse-report-window.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/abuse-report-window.vue')), {
user: appearNote.user,
initialComment: `Note: ${u}\n-----\n`
}, {}, 'closed');
@@ -261,9 +261,9 @@ export function getNoteMenu(props: {
}]
: []
),
- ...(appearNote.userId == $i.id || $i.isModerator || $i.isAdmin ? [
+ ...(appearNote.userId === $i.id || $i.isModerator || $i.isAdmin ? [
null,
- appearNote.userId == $i.id ? {
+ appearNote.userId === $i.id ? {
icon: 'fas fa-edit',
text: i18n.ts.deleteAndEdit,
action: delEdit
diff --git a/packages/client/src/scripts/get-note-summary.ts b/packages/client/src/scripts/get-note-summary.ts
index 54b8d109d6..d57e1c3029 100644
--- a/packages/client/src/scripts/get-note-summary.ts
+++ b/packages/client/src/scripts/get-note-summary.ts
@@ -24,7 +24,7 @@ export const getNoteSummary = (note: misskey.entities.Note): string => {
}
// ãƒ•ã‚¡ã‚¤ãƒ«ãŒæ·»ä»˜ã•れã¦ã„ã‚‹ã¨ã
- if ((note.files || []).length != 0) {
+ if ((note.files || []).length !== 0) {
summary += ` (${i18n.t('withNFiles', { n: note.files.length })})`;
}
diff --git a/packages/client/src/scripts/get-user-menu.ts b/packages/client/src/scripts/get-user-menu.ts
index 192d14b83e..091338efd6 100644
--- a/packages/client/src/scripts/get-user-menu.ts
+++ b/packages/client/src/scripts/get-user-menu.ts
@@ -6,6 +6,7 @@ import * as os from '@/os';
import { userActions } from '@/store';
import { router } from '@/router';
import { $i, iAmModerator } from '@/account';
+import { defineAsyncComponent } from 'vue';
export function getUserMenu(user) {
const meId = $i ? $i.id : null;
@@ -127,7 +128,7 @@ export function getUserMenu(user) {
}
function reportAbuse() {
- os.popup(import('@/components/abuse-report-window.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/abuse-report-window.vue')), {
user: user,
}, {}, 'closed');
}
@@ -147,7 +148,7 @@ export function getUserMenu(user) {
userId: user.id
}).then(() => {
user.isFollowed = !user.isFollowed;
- })
+ });
}
let menu = [{
@@ -168,7 +169,7 @@ export function getUserMenu(user) {
action: () => {
os.post({ specified: user });
}
- }, meId != user.id ? {
+ }, meId !== user.id ? {
type: 'link',
icon: 'fas fa-comments',
text: i18n.ts.startMessaging,
@@ -177,13 +178,13 @@ export function getUserMenu(user) {
icon: 'fas fa-list-ul',
text: i18n.ts.addToList,
action: pushList
- }, meId != user.id ? {
+ }, meId !== user.id ? {
icon: 'fas fa-users',
text: i18n.ts.inviteToGroup,
action: inviteGroup
} : undefined] as any;
- if ($i && meId != user.id) {
+ if ($i && meId !== user.id) {
menu = menu.concat([null, {
icon: user.isMuted ? 'fas fa-eye' : 'fas fa-eye-slash',
text: user.isMuted ? i18n.ts.unmute : i18n.ts.mute,
diff --git a/packages/client/src/scripts/get-user-name.ts b/packages/client/src/scripts/get-user-name.ts
new file mode 100644
index 0000000000..d499ea0203
--- /dev/null
+++ b/packages/client/src/scripts/get-user-name.ts
@@ -0,0 +1,3 @@
+export default function(user: { name?: string | null, username: string }): string {
+ return user.name || user.username;
+}
diff --git a/packages/client/src/scripts/hotkey.ts b/packages/client/src/scripts/hotkey.ts
index 2b3f491fd8..fd9c74f6c8 100644
--- a/packages/client/src/scripts/hotkey.ts
+++ b/packages/client/src/scripts/hotkey.ts
@@ -53,34 +53,34 @@ const parseKeymap = (keymap: Keymap) => Object.entries(keymap).map(([patterns, c
const ignoreElemens = ['input', 'textarea'];
-function match(e: KeyboardEvent, patterns: Action['patterns']): boolean {
- const key = e.code.toLowerCase();
+function match(ev: KeyboardEvent, patterns: Action['patterns']): boolean {
+ const key = ev.code.toLowerCase();
return patterns.some(pattern => pattern.which.includes(key) &&
- pattern.ctrl === e.ctrlKey &&
- pattern.shift === e.shiftKey &&
- pattern.alt === e.altKey &&
- !e.metaKey
+ pattern.ctrl === ev.ctrlKey &&
+ pattern.shift === ev.shiftKey &&
+ pattern.alt === ev.altKey &&
+ !ev.metaKey
);
}
export const makeHotkey = (keymap: Keymap) => {
const actions = parseKeymap(keymap);
- return (e: KeyboardEvent) => {
+ return (ev: KeyboardEvent) => {
if (document.activeElement) {
if (ignoreElemens.some(el => document.activeElement!.matches(el))) return;
if (document.activeElement.attributes['contenteditable']) return;
}
for (const action of actions) {
- const matched = match(e, action.patterns);
+ const matched = match(ev, action.patterns);
if (matched) {
- if (!action.allowRepeat && e.repeat) return;
+ if (!action.allowRepeat && ev.repeat) return;
- e.preventDefault();
- e.stopPropagation();
- action.callback(e);
+ ev.preventDefault();
+ ev.stopPropagation();
+ action.callback(ev);
break;
}
}
diff --git a/packages/client/src/scripts/hpml/evaluator.ts b/packages/client/src/scripts/hpml/evaluator.ts
index 6329c0860e..8106687b61 100644
--- a/packages/client/src/scripts/hpml/evaluator.ts
+++ b/packages/client/src/scripts/hpml/evaluator.ts
@@ -36,7 +36,7 @@ export class Hpml {
if (this.opts.enableAiScript) {
this.aiscript = markRaw(new AiScript({ ...createAiScriptEnv({
storageKey: 'pages:' + this.page.id
- }), ...initAiLib(this)}, {
+ }), ...initAiLib(this) }, {
in: (q) => {
return new Promise(ok => {
os.inputText({
@@ -85,7 +85,7 @@ export class Hpml {
public eval() {
try {
this.vars.value = this.evaluateVars();
- } catch (e) {
+ } catch (err) {
//this.onError(e);
}
}
@@ -103,7 +103,7 @@ export class Hpml {
public callAiScript(fn: string) {
try {
if (this.aiscript) this.aiscript.execFn(this.aiscript.scope.get(fn), []);
- } catch (e) {}
+ } catch (err) {}
}
@autobind
@@ -185,7 +185,7 @@ export class Hpml {
if (this.aiscript) {
try {
return utils.valToJs(this.aiscript.scope.get(expr.value));
- } catch (e) {
+ } catch (err) {
return null;
}
} else {
@@ -194,7 +194,7 @@ export class Hpml {
}
// Define user function
- if (expr.type == 'fn') {
+ if (expr.type === 'fn') {
return {
slots: expr.value.slots.map(x => x.name),
exec: (slotArg: Record<string, any>) => {
diff --git a/packages/client/src/scripts/hpml/lib.ts b/packages/client/src/scripts/hpml/lib.ts
index 2a1ac73a40..01a44ffcdf 100644
--- a/packages/client/src/scripts/hpml/lib.ts
+++ b/packages/client/src/scripts/hpml/lib.ts
@@ -1,9 +1,9 @@
-import * as tinycolor from 'tinycolor2';
+import tinycolor from 'tinycolor2';
import { Hpml } from './evaluator';
import { values, utils } from '@syuilo/aiscript';
import { Fn, HpmlScope } from '.';
import { Expr } from './expr';
-import * as seedrandom from 'seedrandom';
+import seedrandom from 'seedrandom';
/* TODO: https://www.chartjs.org/docs/latest/configuration/canvas-background.html#color
// https://stackoverflow.com/questions/38493564/chart-area-background-color-chartjs
diff --git a/packages/client/src/scripts/idb-proxy.ts b/packages/client/src/scripts/idb-proxy.ts
index 5f76ae30bb..d462a0d7ce 100644
--- a/packages/client/src/scripts/idb-proxy.ts
+++ b/packages/client/src/scripts/idb-proxy.ts
@@ -13,8 +13,8 @@ let idbAvailable = typeof window !== 'undefined' ? !!window.indexedDB : true;
if (idbAvailable) {
try {
await iset('idb-test', 'test');
- } catch (e) {
- console.error('idb error', e);
+ } catch (err) {
+ console.error('idb error', err);
idbAvailable = false;
}
}
diff --git a/packages/client/src/scripts/initialize-sw.ts b/packages/client/src/scripts/initialize-sw.ts
index d6dbd5dbd4..7bacfbdf00 100644
--- a/packages/client/src/scripts/initialize-sw.ts
+++ b/packages/client/src/scripts/initialize-sw.ts
@@ -4,26 +4,26 @@ import { api } from '@/os';
import { lang } from '@/config';
export async function initializeSw() {
- if (instance.swPublickey &&
- ('serviceWorker' in navigator) &&
- ('PushManager' in window) &&
- $i && $i.token) {
- navigator.serviceWorker.register(`/sw.js`);
+ if (!('serviceWorker' in navigator)) return;
- navigator.serviceWorker.ready.then(registration => {
- registration.active?.postMessage({
- msg: 'initialize',
- lang,
- });
+ navigator.serviceWorker.register(`/sw.js`, { scope: '/', type: 'classic' });
+ navigator.serviceWorker.ready.then(registration => {
+ registration.active?.postMessage({
+ msg: 'initialize',
+ lang,
+ });
+
+ if (instance.swPublickey && ('PushManager' in window) && $i && $i.token) {
// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(instance.swPublickey)
- }).then(subscription => {
+ })
+ .then(subscription => {
function encode(buffer: ArrayBuffer | null) {
return btoa(String.fromCharCode.apply(null, new Uint8Array(buffer)));
}
-
+
// Register
api('sw/register', {
endpoint: subscription.endpoint,
@@ -37,15 +37,15 @@ export async function initializeSw() {
if (err.name === 'NotAllowedError') {
return;
}
-
+
// é•ã†applicationServerKey (ã¾ãŸã¯ gcm_sender_id)ã®ã‚µãƒ–スクリプションãŒ
// æ—¢ã«å­˜åœ¨ã—ã¦ã„ã‚‹ã“ã¨ãŒåŽŸå› ã§ã‚¨ãƒ©ãƒ¼ã«ãªã£ãŸå¯èƒ½æ€§ãŒã‚ã‚‹ã®ã§ã€
// ãã®ã‚µãƒ–スクリプションを解除ã—ã¦ãŠã
const subscription = await registration.pushManager.getSubscription();
if (subscription) subscription.unsubscribe();
});
- });
- }
+ }
+ });
}
/**
diff --git a/packages/client/src/scripts/lookup-user.ts b/packages/client/src/scripts/lookup-user.ts
index 8de5c84ce8..2d00e51621 100644
--- a/packages/client/src/scripts/lookup-user.ts
+++ b/packages/client/src/scripts/lookup-user.ts
@@ -25,12 +25,12 @@ export async function lookupUser() {
_notFound = true;
}
};
- usernamePromise.then(show).catch(e => {
- if (e.code === 'NO_SUCH_USER') {
+ usernamePromise.then(show).catch(err => {
+ if (err.code === 'NO_SUCH_USER') {
notFound();
}
});
- idPromise.then(show).catch(e => {
+ idPromise.then(show).catch(err => {
notFound();
});
}
diff --git a/packages/client/src/scripts/navigate.ts b/packages/client/src/scripts/navigate.ts
new file mode 100644
index 0000000000..08b891ec5b
--- /dev/null
+++ b/packages/client/src/scripts/navigate.ts
@@ -0,0 +1,34 @@
+import { inject } from 'vue';
+import { router } from '@/router';
+import { defaultStore } from '@/store';
+
+export type Navigate = (path: string, record?: boolean) => void;
+
+export class MisskeyNavigator {
+ public readonly navHook: Navigate | null = null;
+ public readonly sideViewHook: Navigate | null = null;
+
+ // It should be constructed during vue creating in order for inject function to work
+ constructor() {
+ this.navHook = inject<Navigate | null>('navHook', null);
+ this.sideViewHook = inject<Navigate | null>('sideViewHook', null);
+ }
+
+ // Use this method instead of router.push()
+ public push(path: string, record = true) {
+ if (this.navHook) {
+ this.navHook(path, record);
+ } else {
+ if (defaultStore.state.defaultSideView && this.sideViewHook && path !== '/') {
+ return this.sideViewHook(path, record);
+ }
+
+ if (router.currentRoute.value.path === path) {
+ window.scroll({ top: 0, behavior: 'smooth' });
+ } else {
+ if (record) router.push(path);
+ else router.replace(path);
+ }
+ }
+ }
+}
diff --git a/packages/client/src/scripts/physics.ts b/packages/client/src/scripts/physics.ts
index 36e476b6f9..9e657906c2 100644
--- a/packages/client/src/scripts/physics.ts
+++ b/packages/client/src/scripts/physics.ts
@@ -41,9 +41,9 @@ export function physics(container: HTMLElement) {
const groundThickness = 1024;
const ground = Matter.Bodies.rectangle(containerCenterX, containerHeight + (groundThickness / 2), containerWidth, groundThickness, {
- isStatic: true,
- restitution: 0.1,
- friction: 2
+ isStatic: true,
+ restitution: 0.1,
+ friction: 2
});
//const wallRight = Matter.Bodies.rectangle(window.innerWidth+50, window.innerHeight/2, 100, window.innerHeight, wallopts);
diff --git a/packages/client/src/scripts/please-login.ts b/packages/client/src/scripts/please-login.ts
index aeaafa124b..e21a6d2ed3 100644
--- a/packages/client/src/scripts/please-login.ts
+++ b/packages/client/src/scripts/please-login.ts
@@ -1,14 +1,21 @@
+import { defineAsyncComponent } from 'vue';
import { $i } from '@/account';
import { i18n } from '@/i18n';
-import { alert } from '@/os';
+import { popup } from '@/os';
-export function pleaseLogin() {
+export function pleaseLogin(path?: string) {
if ($i) return;
- alert({
- title: i18n.ts.signinRequired,
- text: null
- });
+ popup(defineAsyncComponent(() => import('@/components/signin-dialog.vue')), {
+ autoSet: true,
+ message: i18n.ts.signinRequired
+ }, {
+ cancelled: () => {
+ if (path) {
+ window.location.href = path;
+ }
+ },
+ }, 'closed');
throw new Error('signin required');
}
diff --git a/packages/client/src/scripts/popout.ts b/packages/client/src/scripts/popout.ts
index b8286a2a76..580031d0a3 100644
--- a/packages/client/src/scripts/popout.ts
+++ b/packages/client/src/scripts/popout.ts
@@ -1,8 +1,9 @@
import * as config from '@/config';
+import { appendQuery } from './url';
export function popout(path: string, w?: HTMLElement) {
- let url = path.startsWith('http://') || path.startsWith('https://') ? path : config.url + "/" + path;
- url += '?zen';
+ let url = path.startsWith('http://') || path.startsWith('https://') ? path : config.url + path;
+ url = appendQuery(url, 'zen');
if (w) {
const position = w.getBoundingClientRect();
const width = parseInt(getComputedStyle(w, '').width, 10);
diff --git a/packages/client/src/scripts/reaction-picker.ts b/packages/client/src/scripts/reaction-picker.ts
index 3ac1f63430..b7699cae4a 100644
--- a/packages/client/src/scripts/reaction-picker.ts
+++ b/packages/client/src/scripts/reaction-picker.ts
@@ -1,4 +1,4 @@
-import { Ref, ref } from 'vue';
+import { defineAsyncComponent, Ref, ref } from 'vue';
import { popup } from '@/os';
class ReactionPicker {
@@ -12,7 +12,7 @@ class ReactionPicker {
}
public async init() {
- await popup(import('@/components/emoji-picker-dialog.vue'), {
+ await popup(defineAsyncComponent(() => import('@/components/emoji-picker-dialog.vue')), {
src: this.src,
asReactionPicker: true,
manualShowing: this.manualShowing
diff --git a/packages/client/src/scripts/select-file.ts b/packages/client/src/scripts/select-file.ts
index 23df4edf54..461d613b42 100644
--- a/packages/client/src/scripts/select-file.ts
+++ b/packages/client/src/scripts/select-file.ts
@@ -4,6 +4,7 @@ import { stream } from '@/stream';
import { i18n } from '@/i18n';
import { defaultStore } from '@/store';
import { DriveFile } from 'misskey-js/built/entities';
+import { uploadFile } from '@/scripts/upload';
function select(src: any, label: string | null, multiple: boolean): Promise<DriveFile | DriveFile[]> {
return new Promise((res, rej) => {
@@ -14,14 +15,14 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Driv
input.type = 'file';
input.multiple = multiple;
input.onchange = () => {
- const promises = Array.from(input.files).map(file => os.upload(file, defaultStore.state.uploadFolder, undefined, keepOriginal.value));
+ const promises = Array.from(input.files).map(file => uploadFile(file, defaultStore.state.uploadFolder, undefined, keepOriginal.value));
Promise.all(promises).then(driveFiles => {
res(multiple ? driveFiles : driveFiles[0]);
- }).catch(e => {
+ }).catch(err => {
os.alert({
type: 'error',
- text: e
+ text: err
});
});
@@ -53,9 +54,9 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Driv
const marker = Math.random().toString(); // TODO: UUIDã¨ã‹ä½¿ã†
const connection = stream.useChannel('main');
- connection.on('urlUploadFinished', data => {
- if (data.marker === marker) {
- res(multiple ? [data.file] : data.file);
+ connection.on('urlUploadFinished', urlResponse => {
+ if (urlResponse.marker === marker) {
+ res(multiple ? [urlResponse.file] : urlResponse.file);
connection.dispose();
}
});
diff --git a/packages/client/src/scripts/theme-editor.ts b/packages/client/src/scripts/theme-editor.ts
index 3d69d2836a..2c917e280d 100644
--- a/packages/client/src/scripts/theme-editor.ts
+++ b/packages/client/src/scripts/theme-editor.ts
@@ -1,4 +1,4 @@
-import { v4 as uuid} from 'uuid';
+import { v4 as uuid } from 'uuid';
import { themeProps, Theme } from './theme';
diff --git a/packages/client/src/scripts/theme.ts b/packages/client/src/scripts/theme.ts
index 2cb78fae5c..dec9fb355c 100644
--- a/packages/client/src/scripts/theme.ts
+++ b/packages/client/src/scripts/theme.ts
@@ -1,5 +1,6 @@
+import { ref } from 'vue';
import { globalEvents } from '@/events';
-import * as tinycolor from 'tinycolor2';
+import tinycolor from 'tinycolor2';
export type Theme = {
id: string;
@@ -10,30 +11,38 @@ export type Theme = {
props: Record<string, string>;
};
-export const lightTheme: Theme = require('@/themes/_light.json5');
-export const darkTheme: Theme = require('@/themes/_dark.json5');
+import lightTheme from '@/themes/_light.json5';
+import darkTheme from '@/themes/_dark.json5';
export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X'));
-export const builtinThemes = [
- require('@/themes/l-light.json5'),
- require('@/themes/l-coffee.json5'),
- require('@/themes/l-apricot.json5'),
- require('@/themes/l-rainy.json5'),
- require('@/themes/l-vivid.json5'),
- require('@/themes/l-cherry.json5'),
- require('@/themes/l-sushi.json5'),
+export const getBuiltinThemes = () => Promise.all(
+ [
+ 'l-light',
+ 'l-coffee',
+ 'l-apricot',
+ 'l-rainy',
+ 'l-vivid',
+ 'l-cherry',
+ 'l-sushi',
- require('@/themes/d-dark.json5'),
- require('@/themes/d-persimmon.json5'),
- require('@/themes/d-astro.json5'),
- require('@/themes/d-future.json5'),
- require('@/themes/d-botanical.json5'),
- require('@/themes/d-cherry.json5'),
- require('@/themes/d-ice.json5'),
- require('@/themes/d-pumpkin.json5'),
- require('@/themes/d-black.json5'),
-] as Theme[];
+ 'd-dark',
+ 'd-persimmon',
+ 'd-astro',
+ 'd-future',
+ 'd-botanical',
+ 'd-cherry',
+ 'd-ice',
+ 'd-pumpkin',
+ 'd-black',
+ ].map(name => import(`../themes/${name}.json5`).then(({ default: _default }): Theme => _default))
+);
+
+export const getBuiltinThemesRef = () => {
+ const builtinThemes = ref<Theme[]>([]);
+ getBuiltinThemes().then(themes => builtinThemes.value = themes);
+ return builtinThemes;
+};
let timeout = null;
diff --git a/packages/client/src/scripts/upload.ts b/packages/client/src/scripts/upload.ts
new file mode 100644
index 0000000000..2f7b30b58d
--- /dev/null
+++ b/packages/client/src/scripts/upload.ts
@@ -0,0 +1,114 @@
+import { reactive, ref } from 'vue';
+import { defaultStore } from '@/store';
+import { apiUrl } from '@/config';
+import * as Misskey from 'misskey-js';
+import { $i } from '@/account';
+import { readAndCompressImage } from 'browser-image-resizer';
+import { alert } from '@/os';
+
+type Uploading = {
+ id: string;
+ name: string;
+ progressMax: number | undefined;
+ progressValue: number | undefined;
+ img: string;
+};
+export const uploads = ref<Uploading[]>([]);
+
+const compressTypeMap = {
+ 'image/jpeg': { quality: 0.85, mimeType: 'image/jpeg' },
+ 'image/webp': { quality: 0.85, mimeType: 'image/jpeg' },
+ 'image/svg+xml': { quality: 1, mimeType: 'image/png' },
+} as const;
+
+const mimeTypeMap = {
+ 'image/webp': 'webp',
+ 'image/jpeg': 'jpg',
+ 'image/png': 'png',
+} as const;
+
+export function uploadFile(
+ file: File,
+ folder?: any,
+ name?: string,
+ keepOriginal: boolean = defaultStore.state.keepOriginalUploading
+): Promise<Misskey.entities.DriveFile> {
+ if (folder && typeof folder === 'object') folder = folder.id;
+
+ return new Promise((resolve, reject) => {
+ const id = Math.random().toString();
+
+ const reader = new FileReader();
+ reader.onload = async (ev) => {
+ const ctx = reactive<Uploading>({
+ id: id,
+ name: name || file.name || 'untitled',
+ progressMax: undefined,
+ progressValue: undefined,
+ img: window.URL.createObjectURL(file)
+ });
+
+ uploads.value.push(ctx);
+
+ let resizedImage: any;
+ if (!keepOriginal && file.type in compressTypeMap) {
+ const imgConfig = compressTypeMap[file.type];
+
+ const config = {
+ maxWidth: 2048,
+ maxHeight: 2048,
+ debug: true,
+ ...imgConfig,
+ };
+
+ try {
+ resizedImage = await readAndCompressImage(file, config);
+ ctx.name = file.type !== imgConfig.mimeType ? `${ctx.name}.${mimeTypeMap[compressTypeMap[file.type].mimeType]}` : ctx.name;
+ } catch (err) {
+ console.error('Failed to resize image', err);
+ }
+ }
+
+ const formData = new FormData();
+ formData.append('i', $i.token);
+ formData.append('force', 'true');
+ formData.append('file', resizedImage || file);
+ formData.append('name', ctx.name);
+ if (folder) formData.append('folderId', folder);
+
+ const xhr = new XMLHttpRequest();
+ xhr.open('POST', apiUrl + '/drive/files/create', true);
+ xhr.onload = (ev) => {
+ if (xhr.status !== 200 || ev.target == null || ev.target.response == null) {
+ // TODO: 消ã™ã®ã§ã¯ãªãã¦å†é€ã§ãるよã†ã«ã—ãŸã„
+ uploads.value = uploads.value.filter(x => x.id !== id);
+
+ alert({
+ type: 'error',
+ title: 'Failed to upload',
+ text: `${JSON.stringify(ev.target?.response)}, ${JSON.stringify(xhr.response)}`
+ });
+
+ reject();
+ return;
+ }
+
+ const driveFile = JSON.parse(ev.target.response);
+
+ resolve(driveFile);
+
+ uploads.value = uploads.value.filter(x => x.id !== id);
+ };
+
+ xhr.upload.onprogress = ev => {
+ if (ev.lengthComputable) {
+ ctx.progressMax = ev.total;
+ ctx.progressValue = ev.loaded;
+ }
+ };
+
+ xhr.send(formData);
+ };
+ reader.readAsArrayBuffer(file);
+ });
+}
diff --git a/packages/client/src/scripts/url.ts b/packages/client/src/scripts/url.ts
index c7f2b7c1e7..542b00e0f0 100644
--- a/packages/client/src/scripts/url.ts
+++ b/packages/client/src/scripts/url.ts
@@ -4,7 +4,7 @@ export function query(obj: {}): string {
.reduce((a, [k, v]) => (a[k] = v, a), {} as Record<string, any>);
return Object.entries(params)
- .map((e) => `${e[0]}=${encodeURIComponent(e[1])}`)
+ .map((p) => `${p[0]}=${encodeURIComponent(p[1])}`)
.join('&');
}
diff --git a/packages/client/src/scripts/use-note-capture.ts b/packages/client/src/scripts/use-note-capture.ts
index b2a96062c7..f1f976693e 100644
--- a/packages/client/src/scripts/use-note-capture.ts
+++ b/packages/client/src/scripts/use-note-capture.ts
@@ -11,8 +11,8 @@ export function useNoteCapture(props: {
const note = props.note;
const connection = $i ? stream : null;
- function onStreamNoteUpdated(data): void {
- const { type, id, body } = data;
+ function onStreamNoteUpdated(noteData): void {
+ const { type, id, body } = noteData;
if (id !== note.value.id) return;
diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts
index b9800ec607..deee23951e 100644
--- a/packages/client/src/store.ts
+++ b/packages/client/src/store.ts
@@ -255,10 +255,13 @@ type Plugin = {
/**
* 常ã«ãƒ¡ãƒ¢ãƒªã«ãƒ­ãƒ¼ãƒ‰ã—ã¦ãŠãå¿…è¦ãŒãªã„よã†ãªè¨­å®šæƒ…報をä¿ç®¡ã™ã‚‹ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸(éžãƒªã‚¢ã‚¯ãƒ†ã‚£ãƒ–)
*/
+import lightTheme from '@/themes/l-light.json5';
+import darkTheme from '@/themes/d-dark.json5';
+
export class ColdDeviceStorage {
public static default = {
- lightTheme: require('@/themes/l-light.json5') as Theme,
- darkTheme: require('@/themes/d-dark.json5') as Theme,
+ lightTheme,
+ darkTheme,
syncDeviceDarkMode: true,
plugins: [] as Plugin[],
mediaVolume: 0.5,
diff --git a/packages/client/src/sw/compose-notification.ts b/packages/client/src/sw/compose-notification.ts
deleted file mode 100644
index e271d30949..0000000000
--- a/packages/client/src/sw/compose-notification.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-/**
- * Notification composer of Service Worker
- */
-declare var self: ServiceWorkerGlobalScope;
-
-import * as misskey from 'misskey-js';
-
-function getUserName(user: misskey.entities.User): string {
- return user.name || user.username;
-}
-
-export default async function(type, data, i18n): Promise<[string, NotificationOptions] | null | undefined> {
- if (!i18n) {
- console.log('no i18n');
- return;
- }
-
- switch (type) {
- case 'driveFileCreated': // TODO (Server Side)
- return [i18n.t('_notification.fileUploaded'), {
- body: data.name,
- icon: data.url
- }];
- case 'notification':
- switch (data.type) {
- case 'mention':
- return [i18n.t('_notification.youGotMention', { name: getUserName(data.user) }), {
- body: data.note.text,
- icon: data.user.avatarUrl
- }];
-
- case 'reply':
- return [i18n.t('_notification.youGotReply', { name: getUserName(data.user) }), {
- body: data.note.text,
- icon: data.user.avatarUrl
- }];
-
- case 'renote':
- return [i18n.t('_notification.youRenoted', { name: getUserName(data.user) }), {
- body: data.note.text,
- icon: data.user.avatarUrl
- }];
-
- case 'quote':
- return [i18n.t('_notification.youGotQuote', { name: getUserName(data.user) }), {
- body: data.note.text,
- icon: data.user.avatarUrl
- }];
-
- case 'reaction':
- return [`${data.reaction} ${getUserName(data.user)}`, {
- body: data.note.text,
- icon: data.user.avatarUrl
- }];
-
- case 'pollVote':
- return [i18n.t('_notification.youGotPoll', { name: getUserName(data.user) }), {
- body: data.note.text,
- icon: data.user.avatarUrl
- }];
-
- case 'pollEnded':
- return [i18n.t('_notification.pollEnded'), {
- body: data.note.text,
- }];
-
- case 'follow':
- return [i18n.t('_notification.youWereFollowed'), {
- body: getUserName(data.user),
- icon: data.user.avatarUrl
- }];
-
- case 'receiveFollowRequest':
- return [i18n.t('_notification.youReceivedFollowRequest'), {
- body: getUserName(data.user),
- icon: data.user.avatarUrl
- }];
-
- case 'followRequestAccepted':
- return [i18n.t('_notification.yourFollowRequestAccepted'), {
- body: getUserName(data.user),
- icon: data.user.avatarUrl
- }];
-
- case 'groupInvited':
- return [i18n.t('_notification.youWereInvitedToGroup'), {
- body: data.group.name
- }];
-
- default:
- return null;
- }
- case 'unreadMessagingMessage':
- if (data.groupId === null) {
- return [i18n.t('_notification.youGotMessagingMessageFromUser', { name: getUserName(data.user) }), {
- icon: data.user.avatarUrl,
- tag: `messaging:user:${data.user.id}`
- }];
- }
- return [i18n.t('_notification.youGotMessagingMessageFromGroup', { name: data.group.name }), {
- icon: data.user.avatarUrl,
- tag: `messaging:group:${data.group.id}`
- }];
- default:
- return null;
- }
-}
diff --git a/packages/client/src/sw/sw.ts b/packages/client/src/sw/sw.ts
deleted file mode 100644
index 68c650c771..0000000000
--- a/packages/client/src/sw/sw.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-/**
- * Service Worker
- */
-declare var self: ServiceWorkerGlobalScope;
-
-import { get, set } from 'idb-keyval';
-import composeNotification from '@/sw/compose-notification';
-import { I18n } from '@/scripts/i18n';
-
-//#region Variables
-const version = _VERSION_;
-const cacheName = `mk-cache-${version}`;
-
-let lang: string;
-let i18n: I18n<any>;
-let pushesPool: any[] = [];
-//#endregion
-
-//#region Startup
-get('lang').then(async prelang => {
- if (!prelang) return;
- lang = prelang;
- return fetchLocale();
-});
-//#endregion
-
-//#region Lifecycle: Install
-self.addEventListener('install', ev => {
- self.skipWaiting();
-});
-//#endregion
-
-//#region Lifecycle: Activate
-self.addEventListener('activate', ev => {
- ev.waitUntil(
- caches.keys()
- .then(cacheNames => Promise.all(
- cacheNames
- .filter((v) => v !== cacheName)
- .map(name => caches.delete(name))
- ))
- .then(() => self.clients.claim())
- );
-});
-//#endregion
-
-//#region When: Fetching
-self.addEventListener('fetch', ev => {
- // Nothing to do
-});
-//#endregion
-
-//#region When: Caught Notification
-self.addEventListener('push', ev => {
- // クライアントå–å¾—
- ev.waitUntil(self.clients.matchAll({
- includeUncontrolled: true
- }).then(async clients => {
- // クライアントãŒã‚ã£ãŸã‚‰ã‚¹ãƒˆãƒªãƒ¼ãƒ ã«æŽ¥ç¶šã—ã¦ã„ã‚‹ã¨ã„ã†ã“ã¨ãªã®ã§é€šçŸ¥ã—ãªã„
- if (clients.length != 0) return;
-
- const { type, body } = ev.data?.json();
-
- // localeを読ã¿è¾¼ã‚ã¦ãŠã‚‰ãši18nãŒundefinedã ã£ãŸå ´åˆã¯pushesPoolã«ãŸã‚ã¦ãŠã
- if (!i18n) return pushesPool.push({ type, body });
-
- const n = await composeNotification(type, body, i18n);
- if (n) return self.registration.showNotification(...n);
- }));
-});
-//#endregion
-
-//#region When: Caught a message from the client
-self.addEventListener('message', ev => {
- switch(ev.data) {
- case 'clear':
- return; // TODO
- default:
- break;
- }
-
- if (typeof ev.data === 'object') {
- // E.g. '[object Array]' → 'array'
- const otype = Object.prototype.toString.call(ev.data).slice(8, -1).toLowerCase();
-
- if (otype === 'object') {
- if (ev.data.msg === 'initialize') {
- lang = ev.data.lang;
- set('lang', lang);
- fetchLocale();
- }
- }
- }
-});
-//#endregion
-
-//#region Function: (Re)Load i18n instance
-async function fetchLocale() {
- //#region localeファイルã®èª­ã¿è¾¼ã¿
- // Service Workerã¯ä½•度も起動ã—ãã®ãŸã³ã«localeを読ã¿è¾¼ã‚€ã®ã§ã€CacheStorageを使ã†
- const localeUrl = `/assets/locales/${lang}.${version}.json`;
- let localeRes = await caches.match(localeUrl);
-
- if (!localeRes) {
- localeRes = await fetch(localeUrl);
- const clone = localeRes?.clone();
- if (!clone?.clone().ok) return;
-
- caches.open(cacheName).then(cache => cache.put(localeUrl, clone));
- }
-
- i18n = new I18n(await localeRes.json());
- //#endregion
-
- //#region i18nã‚’ãã¡ã‚“ã¨èª­ã¿è¾¼ã‚“ã å¾Œã«ã‚„りãŸã„処ç†
- for (const { type, body } of pushesPool) {
- const n = await composeNotification(type, body, i18n);
- if (n) self.registration.showNotification(...n);
- }
- pushesPool = [];
- //#endregion
-}
-//#endregion
diff --git a/packages/client/src/theme-store.ts b/packages/client/src/theme-store.ts
index e7962e7e8e..fdc92ed793 100644
--- a/packages/client/src/theme-store.ts
+++ b/packages/client/src/theme-store.ts
@@ -14,9 +14,9 @@ export async function fetchThemes(): Promise<void> {
try {
const themes = await api('i/registry/get', { scope: ['client'], key: 'themes' });
localStorage.setItem(lsCacheKey, JSON.stringify(themes));
- } catch (e) {
- if (e.code === 'NO_SUCH_KEY') return;
- throw e;
+ } catch (err) {
+ if (err.code === 'NO_SUCH_KEY') return;
+ throw err;
}
}
@@ -28,7 +28,7 @@ export async function addTheme(theme: Theme): Promise<void> {
}
export async function removeTheme(theme: Theme): Promise<void> {
- const themes = getThemes().filter(t => t.id != theme.id);
+ const themes = getThemes().filter(t => t.id !== theme.id);
await api('i/registry/set', { scope: ['client'], key: 'themes', value: themes });
localStorage.setItem(lsCacheKey, JSON.stringify(themes));
}
diff --git a/packages/client/src/ui/_common_/common.vue b/packages/client/src/ui/_common_/common.vue
index 05688d7c53..9f7388db53 100644
--- a/packages/client/src/ui/_common_/common.vue
+++ b/packages/client/src/ui/_common_/common.vue
@@ -17,9 +17,11 @@
<script lang="ts">
import { defineAsyncComponent, defineComponent } from 'vue';
-import { popup, popups, uploads, pendingApiRequestsCount } from '@/os';
+import { popup, popups, pendingApiRequestsCount } from '@/os';
+import { uploads } from '@/scripts/upload';
import * as sound from '@/scripts/sound';
import { $i } from '@/account';
+import { swInject } from './sw-inject';
import { stream } from '@/stream';
export default defineComponent({
@@ -37,7 +39,7 @@ export default defineComponent({
id: notification.id
});
- popup(import('@/components/notification-toast.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/notification-toast.vue')), {
notification
}, {}, 'closed');
}
@@ -48,6 +50,11 @@ export default defineComponent({
if ($i) {
const connection = stream.useChannel('main', null, 'UI');
connection.on('notification', onNotification);
+
+ //#region Listen message from SW
+ if ('serviceWorker' in navigator) {
+ swInject();
+ }
}
return {
diff --git a/packages/client/src/ui/_common_/sidebar-for-mobile.vue b/packages/client/src/ui/_common_/sidebar-for-mobile.vue
index afcc50725b..41d0837233 100644
--- a/packages/client/src/ui/_common_/sidebar-for-mobile.vue
+++ b/packages/client/src/ui/_common_/sidebar-for-mobile.vue
@@ -33,7 +33,7 @@
</template>
<script lang="ts">
-import { computed, defineComponent, ref, toRef, watch } from 'vue';
+import { computed, defineAsyncComponent, defineComponent, ref, toRef, watch } from 'vue';
import { host } from '@/config';
import { search } from '@/scripts/search';
import * as os from '@/os';
@@ -61,13 +61,13 @@ export default defineComponent({
otherMenuItemIndicated,
post: os.post,
search,
- openAccountMenu:(ev) => {
+ openAccountMenu: (ev) => {
openAccountMenu({
withExtraOperation: true,
}, ev);
},
more: () => {
- os.popup(import('@/components/launch-pad.vue'), {}, {
+ os.popup(defineAsyncComponent(() => import('@/components/launch-pad.vue')), {}, {
}, 'closed');
},
};
diff --git a/packages/client/src/ui/_common_/sidebar.vue b/packages/client/src/ui/_common_/sidebar.vue
index a23b7d4152..d65e776d86 100644
--- a/packages/client/src/ui/_common_/sidebar.vue
+++ b/packages/client/src/ui/_common_/sidebar.vue
@@ -33,7 +33,7 @@
</template>
<script lang="ts" setup>
-import { computed, ref, watch } from 'vue';
+import { computed, defineAsyncComponent, ref, watch } from 'vue';
import * as os from '@/os';
import { menuDef } from '@/menu';
import { $i, openAccountMenu as openAccountMenu_ } from '@/account';
@@ -69,7 +69,7 @@ function openAccountMenu(ev: MouseEvent) {
}
function more(ev: MouseEvent) {
- os.popup(import('@/components/launch-pad.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/launch-pad.vue')), {
src: ev.currentTarget ?? ev.target,
}, {
}, 'closed');
diff --git a/packages/client/src/ui/_common_/sw-inject.ts b/packages/client/src/ui/_common_/sw-inject.ts
new file mode 100644
index 0000000000..371f80ca15
--- /dev/null
+++ b/packages/client/src/ui/_common_/sw-inject.ts
@@ -0,0 +1,44 @@
+import { inject } from 'vue';
+import { post } from '@/os';
+import { $i, login } from '@/account';
+import { defaultStore } from '@/store';
+import { getAccountFromId } from '@/scripts/get-account-from-id';
+import { router } from '@/router';
+
+export function swInject() {
+ const navHook = inject('navHook', null);
+ const sideViewHook = inject('sideViewHook', null);
+
+ navigator.serviceWorker.addEventListener('message', ev => {
+ if (_DEV_) {
+ console.log('sw msg', ev.data);
+ }
+
+ if (ev.data.type !== 'order') return;
+
+ if (ev.data.loginId !== $i?.id) {
+ return getAccountFromId(ev.data.loginId).then(account => {
+ if (!account) return;
+ return login(account.token, ev.data.url);
+ });
+ }
+
+ switch (ev.data.order) {
+ case 'post':
+ return post(ev.data.options);
+ case 'push':
+ if (router.currentRoute.value.path === ev.data.url) {
+ return window.scroll({ top: 0, behavior: 'smooth' });
+ }
+ if (navHook) {
+ return navHook(ev.data.url);
+ }
+ if (sideViewHook && defaultStore.state.defaultSideView && ev.data.url !== '/') {
+ return sideViewHook(ev.data.url);
+ }
+ return router.push(ev.data.url);
+ default:
+ return;
+ }
+ });
+}
diff --git a/packages/client/src/ui/_common_/upload.vue b/packages/client/src/ui/_common_/upload.vue
index ab7678a505..f3703d0e8f 100644
--- a/packages/client/src/ui/_common_/upload.vue
+++ b/packages/client/src/ui/_common_/upload.vue
@@ -20,8 +20,8 @@
<script lang="ts" setup>
import { } from 'vue';
import * as os from '@/os';
+import { uploads } from '@/scripts/upload';
-const uploads = os.uploads;
const zIndex = os.claimZIndex('high');
</script>
diff --git a/packages/client/src/ui/classic.header.vue b/packages/client/src/ui/classic.header.vue
index c7fddbc491..57008aeaed 100644
--- a/packages/client/src/ui/classic.header.vue
+++ b/packages/client/src/ui/classic.header.vue
@@ -39,7 +39,7 @@
</template>
<script lang="ts">
-import { defineComponent } from 'vue';
+import { defineAsyncComponent, defineComponent } from 'vue';
import { host } from '@/config';
import { search } from '@/scripts/search';
import * as os from '@/os';
@@ -101,7 +101,7 @@ export default defineComponent({
},
more(ev) {
- os.popup(import('@/components/launch-pad.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/launch-pad.vue')), {
src: ev.currentTarget ?? ev.target,
anchor: { x: 'center', y: 'bottom' },
}, {
diff --git a/packages/client/src/ui/classic.sidebar.vue b/packages/client/src/ui/classic.sidebar.vue
index 3364ee39be..6c0ce023e4 100644
--- a/packages/client/src/ui/classic.sidebar.vue
+++ b/packages/client/src/ui/classic.sidebar.vue
@@ -41,7 +41,7 @@
</template>
<script lang="ts">
-import { defineComponent } from 'vue';
+import { defineAsyncComponent, defineComponent } from 'vue';
import { host } from '@/config';
import { search } from '@/scripts/search';
import * as os from '@/os';
@@ -121,12 +121,12 @@ export default defineComponent({
},
more(ev) {
- os.popup(import('@/components/launch-pad.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/launch-pad.vue')), {
src: ev.currentTarget ?? ev.target,
}, {}, 'closed');
},
- openAccountMenu:(ev) => {
+ openAccountMenu: (ev) => {
openAccountMenu({
withExtraOperation: true,
}, ev);
diff --git a/packages/client/src/ui/classic.widgets.vue b/packages/client/src/ui/classic.widgets.vue
index f42f27e926..6f9d18bde5 100644
--- a/packages/client/src/ui/classic.widgets.vue
+++ b/packages/client/src/ui/classic.widgets.vue
@@ -44,13 +44,13 @@ export default defineComponent({
},
removeWidget(widget) {
- this.$store.set('widgets', this.$store.state.widgets.filter(w => w.id != widget.id));
+ this.$store.set('widgets', this.$store.state.widgets.filter(w => w.id !== widget.id));
},
updateWidget({ id, data }) {
this.$store.set('widgets', this.$store.state.widgets.map(w => w.id === id ? {
...w,
- data: data
+ data,
} : w));
},
diff --git a/packages/client/src/ui/deck.vue b/packages/client/src/ui/deck.vue
index 1e0d9a1652..e538a93f06 100644
--- a/packages/client/src/ui/deck.vue
+++ b/packages/client/src/ui/deck.vue
@@ -17,7 +17,7 @@
:key="ids[0]"
class="column"
:column="columns.find(c => c.id === ids[0])"
- :is-stacked="false"
+ :is-stacked="false"
:style="columns.find(c => c.id === ids[0])!.flexible ? { flex: 1, minWidth: '350px' } : { width: columns.find(c => c.id === ids[0])!.width + 'px' }"
@parent-focus="moveFocus(ids[0], $event)"
/>
diff --git a/packages/client/src/ui/deck/antenna-column.vue b/packages/client/src/ui/deck/antenna-column.vue
index e0f56c2800..f12f5c6b25 100644
--- a/packages/client/src/ui/deck/antenna-column.vue
+++ b/packages/client/src/ui/deck/antenna-column.vue
@@ -22,8 +22,8 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'loaded'): void;
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'loaded'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
let timeline = $ref<InstanceType<typeof XTimeline>>();
diff --git a/packages/client/src/ui/deck/column-core.vue b/packages/client/src/ui/deck/column-core.vue
index 485e89a062..2667b6d745 100644
--- a/packages/client/src/ui/deck/column-core.vue
+++ b/packages/client/src/ui/deck/column-core.vue
@@ -29,7 +29,7 @@ defineProps<{
}>();
const emit = defineEmits<{
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
/*
diff --git a/packages/client/src/ui/deck/column.vue b/packages/client/src/ui/deck/column.vue
index 5f8da8cf8f..6db3549fbb 100644
--- a/packages/client/src/ui/deck/column.vue
+++ b/packages/client/src/ui/deck/column.vue
@@ -61,8 +61,8 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
- (e: 'change-active-state', v: boolean): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'change-active-state', v: boolean): void;
}>();
let body = $ref<HTMLDivElement>();
@@ -94,7 +94,6 @@ onBeforeUnmount(() => {
os.deckGlobalEvents.off('column.dragEnd', onOtherDragEnd);
});
-
function onOtherDragStart() {
dropready = true;
}
@@ -193,9 +192,9 @@ function goTop() {
});
}
-function onDragstart(e) {
- e.dataTransfer.effectAllowed = 'move';
- e.dataTransfer.setData(_DATA_TRANSFER_DECK_COLUMN_, props.column.id);
+function onDragstart(ev) {
+ ev.dataTransfer.effectAllowed = 'move';
+ ev.dataTransfer.setData(_DATA_TRANSFER_DECK_COLUMN_, props.column.id);
// Chromeã®ãƒã‚°ã§ã€Dragstartãƒãƒ³ãƒ‰ãƒ©å†…ã§ã™ãã«DOMを変更ã™ã‚‹(=リアクティブãªãƒ—ロパティを変更ã™ã‚‹)ã¨DragãŒçµ‚了ã—ã¦ã—ã¾ã†
// SEE: https://stackoverflow.com/questions/19639969/html5-dragend-event-firing-immediately
@@ -204,35 +203,34 @@ function onDragstart(e) {
}, 10);
}
-function onDragend(e) {
+function onDragend(ev) {
dragging = false;
}
-function onDragover(e) {
+function onDragover(ev) {
// 自分自身ãŒãƒ‰ãƒ©ãƒƒã‚°ã•れã¦ã„ã‚‹å ´åˆ
if (dragging) {
// 自分自身ã«ã¯ãƒ‰ãƒ­ãƒƒãƒ—ã•ã›ãªã„
- e.dataTransfer.dropEffect = 'none';
- return;
- }
+ ev.dataTransfer.dropEffect = 'none';
+ } else {
+ const isDeckColumn = ev.dataTransfer.types[0] === _DATA_TRANSFER_DECK_COLUMN_;
- const isDeckColumn = e.dataTransfer.types[0] == _DATA_TRANSFER_DECK_COLUMN_;
+ ev.dataTransfer.dropEffect = isDeckColumn ? 'move' : 'none';
- e.dataTransfer.dropEffect = isDeckColumn ? 'move' : 'none';
-
- if (!dragging && isDeckColumn) draghover = true;
+ if (isDeckColumn) draghover = true;
+ }
}
function onDragleave() {
draghover = false;
}
-function onDrop(e) {
+function onDrop(ev) {
draghover = false;
os.deckGlobalEvents.emit('column.dragEnd');
- const id = e.dataTransfer.getData(_DATA_TRANSFER_DECK_COLUMN_);
- if (id != null && id != '') {
+ const id = ev.dataTransfer.getData(_DATA_TRANSFER_DECK_COLUMN_);
+ if (id != null && id !== '') {
swapColumn(props.column.id, id);
}
}
diff --git a/packages/client/src/ui/deck/deck-store.ts b/packages/client/src/ui/deck/deck-store.ts
index f7c39ad8fd..c847bf2b43 100644
--- a/packages/client/src/ui/deck/deck-store.ts
+++ b/packages/client/src/ui/deck/deck-store.ts
@@ -72,8 +72,8 @@ export const loadDeck = async () => {
scope: ['client', 'deck', 'profiles'],
key: deckStore.state.profile,
});
- } catch (e) {
- if (e.code === 'NO_SUCH_KEY') {
+ } catch (err) {
+ if (err.code === 'NO_SUCH_KEY') {
// å¾Œæ–¹äº’æ›æ€§ã®ãŸã‚
if (deckStore.state.profile === 'default') {
saveDeck();
@@ -94,7 +94,7 @@ export const loadDeck = async () => {
deckStore.set('layout', [['a'], ['b']]);
return;
}
- throw e;
+ throw err;
}
deckStore.set('columns', deck.columns);
@@ -114,7 +114,7 @@ export const saveDeck = throttle(1000, () => {
});
export function addColumn(column: Column) {
- if (column.name == undefined) column.name = null;
+ if (column.name === undefined) column.name = null;
deckStore.push('columns', column);
deckStore.push('layout', [column.id]);
saveDeck();
@@ -129,10 +129,10 @@ export function removeColumn(id: Column['id']) {
}
export function swapColumn(a: Column['id'], b: Column['id']) {
- const aX = deckStore.state.layout.findIndex(ids => ids.indexOf(a) != -1);
- const aY = deckStore.state.layout[aX].findIndex(id => id == a);
- const bX = deckStore.state.layout.findIndex(ids => ids.indexOf(b) != -1);
- const bY = deckStore.state.layout[bX].findIndex(id => id == b);
+ const aX = deckStore.state.layout.findIndex(ids => ids.indexOf(a) !== -1);
+ const aY = deckStore.state.layout[aX].findIndex(id => id === a);
+ const bX = deckStore.state.layout.findIndex(ids => ids.indexOf(b) !== -1);
+ const bY = deckStore.state.layout[bX].findIndex(id => id === b);
const layout = copy(deckStore.state.layout);
layout[aX][aY] = b;
layout[bX][bY] = a;
@@ -259,7 +259,7 @@ export function removeColumnWidget(id: Column['id'], widget: ColumnWidget) {
const columnIndex = deckStore.state.columns.findIndex(c => c.id === id);
const column = copy(deckStore.state.columns[columnIndex]);
if (column == null) return;
- column.widgets = column.widgets.filter(w => w.id != widget.id);
+ column.widgets = column.widgets.filter(w => w.id !== widget.id);
columns[columnIndex] = column;
deckStore.set('columns', columns);
saveDeck();
@@ -276,14 +276,14 @@ export function setColumnWidgets(id: Column['id'], widgets: ColumnWidget[]) {
saveDeck();
}
-export function updateColumnWidget(id: Column['id'], widgetId: string, data: any) {
+export function updateColumnWidget(id: Column['id'], widgetId: string, WidgetData: any) {
const columns = copy(deckStore.state.columns);
const columnIndex = deckStore.state.columns.findIndex(c => c.id === id);
const column = copy(deckStore.state.columns[columnIndex]);
if (column == null) return;
column.widgets = column.widgets.map(w => w.id === widgetId ? {
...w,
- data: data
+ data: widgetData,
} : w);
columns[columnIndex] = column;
deckStore.set('columns', columns);
diff --git a/packages/client/src/ui/deck/direct-column.vue b/packages/client/src/ui/deck/direct-column.vue
index ebaba574f4..4837c0ce38 100644
--- a/packages/client/src/ui/deck/direct-column.vue
+++ b/packages/client/src/ui/deck/direct-column.vue
@@ -18,7 +18,7 @@ defineProps<{
}>();
const emit = defineEmits<{
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
const pagination = {
diff --git a/packages/client/src/ui/deck/list-column.vue b/packages/client/src/ui/deck/list-column.vue
index b990516d05..843a3bd1cb 100644
--- a/packages/client/src/ui/deck/list-column.vue
+++ b/packages/client/src/ui/deck/list-column.vue
@@ -22,8 +22,8 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'loaded'): void;
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'loaded'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
let timeline = $ref<InstanceType<typeof XTimeline>>();
diff --git a/packages/client/src/ui/deck/main-column.vue b/packages/client/src/ui/deck/main-column.vue
index 57caab44cb..3c97cd4867 100644
--- a/packages/client/src/ui/deck/main-column.vue
+++ b/packages/client/src/ui/deck/main-column.vue
@@ -35,7 +35,7 @@ defineProps<{
}>();
const emit = defineEmits<{
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
let pageInfo = $ref<Record<string, any> | null>(null);
diff --git a/packages/client/src/ui/deck/mentions-column.vue b/packages/client/src/ui/deck/mentions-column.vue
index a7a012a7fb..0b6ca3a239 100644
--- a/packages/client/src/ui/deck/mentions-column.vue
+++ b/packages/client/src/ui/deck/mentions-column.vue
@@ -18,7 +18,7 @@ defineProps<{
}>();
const emit = defineEmits<{
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
const pagination = {
diff --git a/packages/client/src/ui/deck/notifications-column.vue b/packages/client/src/ui/deck/notifications-column.vue
index 50ee12a275..6dd040cb8d 100644
--- a/packages/client/src/ui/deck/notifications-column.vue
+++ b/packages/client/src/ui/deck/notifications-column.vue
@@ -7,7 +7,7 @@
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { defineAsyncComponent } from 'vue';
import XColumn from './column.vue';
import XNotifications from '@/components/notifications.vue';
import * as os from '@/os';
@@ -20,11 +20,11 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
function func() {
- os.popup(import('@/components/notification-setting-window.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/notification-setting-window.vue')), {
includingTypes: props.column.includingTypes,
}, {
done: async (res) => {
diff --git a/packages/client/src/ui/deck/tl-column.vue b/packages/client/src/ui/deck/tl-column.vue
index 02b9ef83a1..f3ecda5aa4 100644
--- a/packages/client/src/ui/deck/tl-column.vue
+++ b/packages/client/src/ui/deck/tl-column.vue
@@ -35,8 +35,8 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'loaded'): void;
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'loaded'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
let disabled = $ref(false);
diff --git a/packages/client/src/ui/deck/widgets-column.vue b/packages/client/src/ui/deck/widgets-column.vue
index a2edc38357..10c6f5adf6 100644
--- a/packages/client/src/ui/deck/widgets-column.vue
+++ b/packages/client/src/ui/deck/widgets-column.vue
@@ -3,7 +3,7 @@
<template #header><i class="fas fa-window-maximize" style="margin-right: 8px;"></i>{{ column.name }}</template>
<div class="wtdtxvec">
- <XWidgets v-if="column.widgets" :edit="edit" :widgets="column.widgets" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="edit = false"/>
+ <XWidgets :edit="edit" :widgets="column.widgets" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="edit = false"/>
</div>
</XColumn>
</template>
@@ -20,7 +20,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
let edit = $ref(false);
diff --git a/packages/client/src/ui/universal.widgets.vue b/packages/client/src/ui/universal.widgets.vue
index 2660e80368..7aed083886 100644
--- a/packages/client/src/ui/universal.widgets.vue
+++ b/packages/client/src/ui/universal.widgets.vue
@@ -3,7 +3,7 @@
<XWidgets :edit="editMode" :widgets="defaultStore.reactiveState.widgets.value" @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="fas fa-check"></i> {{ i18n.ts.editWidgetsExit }}</button>
- <button v-else class="_textButton" style="font-size: 0.9em;" @click="editMode = true"><i class="fas fa-pencil-alt"></i> {{ i18n.ts.editWidgets }}</button>
+ <button v-else class="_textButton mk-widget-edit" style="font-size: 0.9em;" @click="editMode = true"><i class="fas fa-pencil-alt"></i> {{ i18n.ts.editWidgets }}</button>
</div>
</template>
@@ -14,7 +14,7 @@ import { i18n } from '@/i18n';
import { defaultStore } from '@/store';
const emit = defineEmits<{
- (e: 'mounted', el: Element): void;
+ (ev: 'mounted', el: Element): void;
}>();
let editMode = $ref(false);
@@ -32,13 +32,13 @@ function addWidget(widget) {
}
function removeWidget(widget) {
- defaultStore.set('widgets', defaultStore.state.widgets.filter(w => w.id != widget.id));
+ defaultStore.set('widgets', defaultStore.state.widgets.filter(w => w.id !== widget.id));
}
function updateWidget({ id, data }) {
defaultStore.set('widgets', defaultStore.state.widgets.map(w => w.id === id ? {
...w,
- data: data
+ data,
} : w));
}
diff --git a/packages/client/src/widgets/activity.calendar.vue b/packages/client/src/widgets/activity.calendar.vue
index b833bd65ca..33b95b00db 100644
--- a/packages/client/src/widgets/activity.calendar.vue
+++ b/packages/client/src/widgets/activity.calendar.vue
@@ -1,13 +1,13 @@
<template>
<svg viewBox="0 0 21 7">
- <rect v-for="record in data" class="day"
+ <rect v-for="record in activity" class="day"
width="1" height="1"
:x="record.x" :y="record.date.weekday"
rx="1" ry="1"
fill="transparent">
<title>{{ record.date.year }}/{{ record.date.month + 1 }}/{{ record.date.day }}</title>
</rect>
- <rect v-for="record in data" class="day"
+ <rect v-for="record in activity" class="day"
:width="record.v" :height="record.v"
:x="record.x + ((1 - record.v) / 2)" :y="record.date.weekday + ((1 - record.v) / 2)"
rx="1" ry="1"
@@ -15,7 +15,7 @@
style="pointer-events: none;"/>
<rect class="today"
width="1" height="1"
- :x="data[0].x" :y="data[0].date.weekday"
+ :x="activity[0].x" :y="activity[0].date.weekday"
rx="1" ry="1"
fill="none"
stroke-width="0.1"
@@ -23,45 +23,41 @@
</svg>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
-import * as os from '@/os';
+<script lang="ts" setup>
+const props = defineProps<{
+ activity: any[]
+}>();
-export default defineComponent({
- props: ['data'],
- created() {
- for (const d of this.data) {
- d.total = d.notes + d.replies + d.renotes;
- }
- const peak = Math.max.apply(null, this.data.map(d => d.total));
+for (const d of props.activity) {
+ d.total = d.notes + d.replies + d.renotes;
+}
+const peak = Math.max(...props.activity.map(d => d.total));
- const now = new Date();
- const year = now.getFullYear();
- const month = now.getMonth();
- const day = now.getDate();
+const now = new Date();
+const year = now.getFullYear();
+const month = now.getMonth();
+const day = now.getDate();
- let x = 20;
- this.data.slice().forEach((d, i) => {
- d.x = x;
+let x = 20;
+props.activity.slice().forEach((d, i) => {
+ d.x = x;
- const date = new Date(year, month, day - i);
- d.date = {
- year: date.getFullYear(),
- month: date.getMonth(),
- day: date.getDate(),
- weekday: date.getDay()
- };
+ const date = new Date(year, month, day - i);
+ d.date = {
+ year: date.getFullYear(),
+ month: date.getMonth(),
+ day: date.getDate(),
+ weekday: date.getDay()
+ };
- d.v = peak === 0 ? 0 : d.total / (peak / 2);
- if (d.v > 1) d.v = 1;
- const ch = d.date.weekday === 0 || d.date.weekday === 6 ? 275 : 170;
- const cs = d.v * 100;
- const cl = 15 + ((1 - d.v) * 80);
- d.color = `hsl(${ch}, ${cs}%, ${cl}%)`;
+ d.v = peak === 0 ? 0 : d.total / (peak / 2);
+ if (d.v > 1) d.v = 1;
+ const ch = d.date.weekday === 0 || d.date.weekday === 6 ? 275 : 170;
+ const cs = d.v * 100;
+ const cl = 15 + ((1 - d.v) * 80);
+ d.color = `hsl(${ch}, ${cs}%, ${cl}%)`;
- if (d.date.weekday === 0) x--;
- });
- }
+ if (d.date.weekday === 0) x--;
});
</script>
diff --git a/packages/client/src/widgets/activity.chart.vue b/packages/client/src/widgets/activity.chart.vue
index 9702d66663..b7db2af580 100644
--- a/packages/client/src/widgets/activity.chart.vue
+++ b/packages/client/src/widgets/activity.chart.vue
@@ -24,9 +24,19 @@
</svg>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
-import * as os from '@/os';
+<script lang="ts" setup>
+const props = defineProps<{
+ activity: any[]
+}>();
+
+let viewBoxX: number = $ref(147);
+let viewBoxY: number = $ref(60);
+let zoom: number = $ref(1);
+let pos: number = $ref(0);
+let pointsNote: any = $ref(null);
+let pointsReply: any = $ref(null);
+let pointsRenote: any = $ref(null);
+let pointsTotal: any = $ref(null);
function dragListen(fn) {
window.addEventListener('mousemove', fn);
@@ -40,60 +50,35 @@ function dragClear(fn) {
window.removeEventListener('mouseup', dragClear);
}
-export default defineComponent({
- props: ['data'],
- data() {
- return {
- viewBoxX: 147,
- viewBoxY: 60,
- zoom: 1,
- pos: 0,
- pointsNote: null,
- pointsReply: null,
- pointsRenote: null,
- pointsTotal: null
- };
- },
- created() {
- for (const d of this.data) {
- d.total = d.notes + d.replies + d.renotes;
- }
+function onMousedown(ev) {
+ const clickX = ev.clientX;
+ const clickY = ev.clientY;
+ const baseZoom = zoom;
+ const basePos = pos;
- this.render();
- },
- methods: {
- render() {
- const peak = Math.max.apply(null, this.data.map(d => d.total));
- if (peak != 0) {
- const data = this.data.slice().reverse();
- this.pointsNote = data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.notes / peak)) * this.viewBoxY}`).join(' ');
- this.pointsReply = data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.replies / peak)) * this.viewBoxY}`).join(' ');
- this.pointsRenote = data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.renotes / peak)) * this.viewBoxY}`).join(' ');
- this.pointsTotal = data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ');
- }
- },
- onMousedown(e) {
- const clickX = e.clientX;
- const clickY = e.clientY;
- const baseZoom = this.zoom;
- const basePos = this.pos;
+ // å‹•ã‹ã—ãŸæ™‚
+ dragListen(me => {
+ let moveLeft = me.clientX - clickX;
+ let moveTop = me.clientY - clickY;
- // å‹•ã‹ã—ãŸæ™‚
- dragListen(me => {
- let moveLeft = me.clientX - clickX;
- let moveTop = me.clientY - clickY;
+ zoom = Math.max(1, baseZoom + (-moveTop / 20));
+ pos = Math.min(0, basePos + moveLeft);
+ if (pos < -(((props.activity.length - 1) * zoom) - viewBoxX)) pos = -(((props.activity.length - 1) * zoom) - viewBoxX);
- this.zoom = baseZoom + (-moveTop / 20);
- this.pos = basePos + moveLeft;
- if (this.zoom < 1) this.zoom = 1;
- if (this.pos > 0) this.pos = 0;
- if (this.pos < -(((this.data.length - 1) * this.zoom) - this.viewBoxX)) this.pos = -(((this.data.length - 1) * this.zoom) - this.viewBoxX);
+ render();
+ });
+}
- this.render();
- });
- }
+function render() {
+ const peak = Math.max(...props.activity.map(d => d.total));
+ if (peak !== 0) {
+ const activity = props.activity.slice().reverse();
+ pointsNote = activity.map((d, i) => `${(i * zoom) + pos},${(1 - (d.notes / peak)) * viewBoxY}`).join(' ');
+ pointsReply = activity.map((d, i) => `${(i * zoom) + pos},${(1 - (d.replies / peak)) * viewBoxY}`).join(' ');
+ pointsRenote = activity.map((d, i) => `${(i * zoom) + pos},${(1 - (d.renotes / peak)) * viewBoxY}`).join(' ');
+ pointsTotal = activity.map((d, i) => `${(i * zoom) + pos},${(1 - (d.total / peak)) * viewBoxY}`).join(' ');
}
-});
+}
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/widgets/activity.vue b/packages/client/src/widgets/activity.vue
index acbbb7a97a..7fb9f5894c 100644
--- a/packages/client/src/widgets/activity.vue
+++ b/packages/client/src/widgets/activity.vue
@@ -1,13 +1,13 @@
<template>
-<MkContainer :show-header="widgetProps.showHeader" :naked="widgetProps.transparent">
+<MkContainer :show-header="widgetProps.showHeader" :naked="widgetProps.transparent" class="mkw-activity">
<template #header><i class="fas fa-chart-bar"></i>{{ $ts._widgets.activity }}</template>
<template #func><button class="_button" @click="toggleView()"><i class="fas fa-sort"></i></button></template>
<div>
<MkLoading v-if="fetching"/>
<template v-else>
- <XCalendar v-show="widgetProps.view === 0" :data="[].concat(activity)"/>
- <XChart v-show="widgetProps.view === 1" :data="[].concat(activity)"/>
+ <XCalendar v-show="widgetProps.view === 0" :activity="[].concat(activity)"/>
+ <XChart v-show="widgetProps.view === 1" :activity="[].concat(activity)"/>
</template>
</div>
</MkContainer>
@@ -47,7 +47,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure, save } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/aichan.vue b/packages/client/src/widgets/aichan.vue
index 03e394b976..cdd367cc84 100644
--- a/packages/client/src/widgets/aichan.vue
+++ b/packages/client/src/widgets/aichan.vue
@@ -1,5 +1,5 @@
<template>
-<MkContainer :naked="widgetProps.transparent" :show-header="false">
+<MkContainer :naked="widgetProps.transparent" :show-header="false" class="mkw-aichan">
<iframe ref="live2d" class="dedjhjmo" src="https://misskey-dev.github.io/mascot-web/?scale=1.5&y=1.1&eyeY=100" @click="touched"></iframe>
</MkContainer>
</template>
@@ -24,7 +24,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/aiscript.vue b/packages/client/src/widgets/aiscript.vue
index 0a5c0d614d..9fed292a69 100644
--- a/packages/client/src/widgets/aiscript.vue
+++ b/packages/client/src/widgets/aiscript.vue
@@ -1,5 +1,5 @@
<template>
-<MkContainer :show-header="widgetProps.showHeader">
+<MkContainer :show-header="widgetProps.showHeader" class="mkw-aiscript">
<template #header><i class="fas fa-terminal"></i>{{ $ts._widgets.aiscript }}</template>
<div class="uylguesu _monospace">
@@ -43,7 +43,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
@@ -94,7 +94,7 @@ const run = async () => {
let ast;
try {
ast = parse(widgetProps.script);
- } catch (e) {
+ } catch (err) {
os.alert({
type: 'error',
text: 'Syntax error :(',
@@ -103,10 +103,10 @@ const run = async () => {
}
try {
await aiscript.exec(ast);
- } catch (e) {
+ } catch (err) {
os.alert({
type: 'error',
- text: e,
+ text: err,
});
}
};
diff --git a/packages/client/src/widgets/button.vue b/packages/client/src/widgets/button.vue
index a33afd6e7a..ee4e9c6423 100644
--- a/packages/client/src/widgets/button.vue
+++ b/packages/client/src/widgets/button.vue
@@ -40,7 +40,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
@@ -73,7 +73,7 @@ const run = async () => {
let ast;
try {
ast = parse(widgetProps.script);
- } catch (e) {
+ } catch (err) {
os.alert({
type: 'error',
text: 'Syntax error :(',
@@ -82,10 +82,10 @@ const run = async () => {
}
try {
await aiscript.exec(ast);
- } catch (e) {
+ } catch (err) {
os.alert({
type: 'error',
- text: e,
+ text: err,
});
}
};
diff --git a/packages/client/src/widgets/calendar.vue b/packages/client/src/widgets/calendar.vue
index c6a69b3fb8..2a2b035541 100644
--- a/packages/client/src/widgets/calendar.vue
+++ b/packages/client/src/widgets/calendar.vue
@@ -53,7 +53,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/clock.vue b/packages/client/src/widgets/clock.vue
index 6acb10d74d..fbd2f9e899 100644
--- a/packages/client/src/widgets/clock.vue
+++ b/packages/client/src/widgets/clock.vue
@@ -1,5 +1,5 @@
<template>
-<MkContainer :naked="widgetProps.transparent" :show-header="false">
+<MkContainer :naked="widgetProps.transparent" :show-header="false" class="mkw-clock">
<div class="vubelbmv">
<MkAnalogClock class="clock" :thickness="widgetProps.thickness"/>
</div>
@@ -39,7 +39,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/digital-clock.vue b/packages/client/src/widgets/digital-clock.vue
index 62f052a692..a17ed040c9 100644
--- a/packages/client/src/widgets/digital-clock.vue
+++ b/packages/client/src/widgets/digital-clock.vue
@@ -41,7 +41,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/federation.vue b/packages/client/src/widgets/federation.vue
index 5f1131dce1..a3862077bb 100644
--- a/packages/client/src/widgets/federation.vue
+++ b/packages/client/src/widgets/federation.vue
@@ -1,5 +1,5 @@
<template>
-<MkContainer :show-header="widgetProps.showHeader" :foldable="foldable" :scrollable="scrollable">
+<MkContainer :show-header="widgetProps.showHeader" :foldable="foldable" :scrollable="scrollable" class="mkw-federation">
<template #header><i class="fas fa-globe"></i>{{ $ts._widgets.federation }}</template>
<div class="wbrkwalb">
@@ -41,7 +41,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps> & { foldable?: boolean; scrollable?: boolean; }>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; foldable?: boolean; scrollable?: boolean; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/job-queue.vue b/packages/client/src/widgets/job-queue.vue
index 4a2a3cf233..8897f240bd 100644
--- a/packages/client/src/widgets/job-queue.vue
+++ b/packages/client/src/widgets/job-queue.vue
@@ -73,7 +73,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/memo.vue b/packages/client/src/widgets/memo.vue
index 450598f65a..8670cb2bac 100644
--- a/packages/client/src/widgets/memo.vue
+++ b/packages/client/src/widgets/memo.vue
@@ -1,5 +1,5 @@
<template>
-<MkContainer :show-header="widgetProps.showHeader">
+<MkContainer :show-header="widgetProps.showHeader" class="mkw-memo">
<template #header><i class="fas fa-sticky-note"></i>{{ $ts._widgets.memo }}</template>
<div class="otgbylcu">
@@ -32,7 +32,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/notifications.vue b/packages/client/src/widgets/notifications.vue
index 8cf29c9271..18c546ee74 100644
--- a/packages/client/src/widgets/notifications.vue
+++ b/packages/client/src/widgets/notifications.vue
@@ -1,5 +1,5 @@
<template>
-<MkContainer :style="`height: ${widgetProps.height}px;`" :show-header="widgetProps.showHeader" :scrollable="true">
+<MkContainer :style="`height: ${widgetProps.height}px;`" :show-header="widgetProps.showHeader" :scrollable="true" class="mkw-notifications">
<template #header><i class="fas fa-bell"></i>{{ $ts.notifications }}</template>
<template #func><button class="_button" @click="configureNotification()"><i class="fas fa-cog"></i></button></template>
@@ -15,6 +15,7 @@ import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExp
import MkContainer from '@/components/ui/container.vue';
import XNotifications from '@/components/notifications.vue';
import * as os from '@/os';
+import { defineAsyncComponent } from 'vue';
const name = 'notifications';
@@ -40,7 +41,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure, save } = useWidgetPropsManager(name,
widgetPropsDef,
@@ -49,7 +50,7 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name,
);
const configureNotification = () => {
- os.popup(import('@/components/notification-setting-window.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/notification-setting-window.vue')), {
includingTypes: widgetProps.includingTypes,
}, {
done: async (res) => {
diff --git a/packages/client/src/widgets/online-users.vue b/packages/client/src/widgets/online-users.vue
index 1746a8314e..eb3184fe9d 100644
--- a/packages/client/src/widgets/online-users.vue
+++ b/packages/client/src/widgets/online-users.vue
@@ -27,7 +27,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/photos.vue b/packages/client/src/widgets/photos.vue
index 8f948dc643..5d9b9e2984 100644
--- a/packages/client/src/widgets/photos.vue
+++ b/packages/client/src/widgets/photos.vue
@@ -1,5 +1,5 @@
<template>
-<MkContainer :show-header="widgetProps.showHeader" :naked="widgetProps.transparent" :class="$style.root" :data-transparent="widgetProps.transparent ? true : null">
+<MkContainer :show-header="widgetProps.showHeader" :naked="widgetProps.transparent" :class="$style.root" :data-transparent="widgetProps.transparent ? true : null" class="mkw-photos">
<template #header><i class="fas fa-camera"></i>{{ $ts._widgets.photos }}</template>
<div class="">
@@ -43,7 +43,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/post-form.vue b/packages/client/src/widgets/post-form.vue
index 51aa8fcf6b..b542913357 100644
--- a/packages/client/src/widgets/post-form.vue
+++ b/packages/client/src/widgets/post-form.vue
@@ -1,5 +1,5 @@
<template>
-<XPostForm class="_panel" :fixed="true" :autofocus="false"/>
+<XPostForm class="_panel mkw-postForm" :fixed="true" :autofocus="false"/>
</template>
<script lang="ts" setup>
@@ -19,7 +19,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/rss.vue b/packages/client/src/widgets/rss.vue
index 9e2e503602..fc65f11813 100644
--- a/packages/client/src/widgets/rss.vue
+++ b/packages/client/src/widgets/rss.vue
@@ -1,5 +1,5 @@
<template>
-<MkContainer :show-header="widgetProps.showHeader">
+<MkContainer :show-header="widgetProps.showHeader" class="mkw-rss">
<template #header><i class="fas fa-rss-square"></i>RSS</template>
<template #func><button class="_button" @click="configure"><i class="fas fa-cog"></i></button></template>
@@ -38,7 +38,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/server-metric/cpu-mem.vue b/packages/client/src/widgets/server-metric/cpu-mem.vue
index ad9e6a8b0f..00c3a10c9b 100644
--- a/packages/client/src/widgets/server-metric/cpu-mem.vue
+++ b/packages/client/src/widgets/server-metric/cpu-mem.vue
@@ -69,79 +69,72 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { onMounted, onBeforeUnmount } from 'vue';
import { v4 as uuid } from 'uuid';
-export default defineComponent({
- props: {
- connection: {
- required: true,
- },
- meta: {
- required: true,
- }
- },
- data() {
- return {
- viewBoxX: 50,
- viewBoxY: 30,
- stats: [],
- cpuGradientId: uuid(),
- cpuMaskId: uuid(),
- memGradientId: uuid(),
- memMaskId: uuid(),
- cpuPolylinePoints: '',
- memPolylinePoints: '',
- cpuPolygonPoints: '',
- memPolygonPoints: '',
- cpuHeadX: null,
- cpuHeadY: null,
- memHeadX: null,
- memHeadY: null,
- cpuP: '',
- memP: ''
- };
- },
- mounted() {
- this.connection.on('stats', this.onStats);
- this.connection.on('statsLog', this.onStatsLog);
- this.connection.send('requestLog', {
- id: Math.random().toString().substr(2, 8)
- });
- },
- beforeUnmount() {
- this.connection.off('stats', this.onStats);
- this.connection.off('statsLog', this.onStatsLog);
- },
- methods: {
- onStats(stats) {
- this.stats.push(stats);
- if (this.stats.length > 50) this.stats.shift();
+const props = defineProps<{
+ connection: any,
+ meta: any
+}>();
- const cpuPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - s.cpu) * this.viewBoxY]);
- const memPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - (s.mem.active / this.meta.mem.total)) * this.viewBoxY]);
- this.cpuPolylinePoints = cpuPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
- this.memPolylinePoints = memPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
+let viewBoxX: number = $ref(50);
+let viewBoxY: number = $ref(30);
+let stats: any[] = $ref([]);
+const cpuGradientId = uuid();
+const cpuMaskId = uuid();
+const memGradientId = uuid();
+const memMaskId = uuid();
+let cpuPolylinePoints: string = $ref('');
+let memPolylinePoints: string = $ref('');
+let cpuPolygonPoints: string = $ref('');
+let memPolygonPoints: string = $ref('');
+let cpuHeadX: any = $ref(null);
+let cpuHeadY: any = $ref(null);
+let memHeadX: any = $ref(null);
+let memHeadY: any = $ref(null);
+let cpuP: string = $ref('');
+let memP: string = $ref('');
- this.cpuPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.cpuPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`;
- this.memPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.memPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`;
+onMounted(() => {
+ props.connection.on('stats', onStats);
+ props.connection.on('statsLog', onStatsLog);
+ props.connection.send('requestLog', {
+ id: Math.random().toString().substr(2, 8)
+ });
+});
- this.cpuHeadX = cpuPolylinePoints[cpuPolylinePoints.length - 1][0];
- this.cpuHeadY = cpuPolylinePoints[cpuPolylinePoints.length - 1][1];
- this.memHeadX = memPolylinePoints[memPolylinePoints.length - 1][0];
- this.memHeadY = memPolylinePoints[memPolylinePoints.length - 1][1];
+onBeforeUnmount(() => {
+ props.connection.off('stats', onStats);
+ props.connection.off('statsLog', onStatsLog);
+});
- this.cpuP = (stats.cpu * 100).toFixed(0);
- this.memP = (stats.mem.active / this.meta.mem.total * 100).toFixed(0);
- },
- onStatsLog(statsLog) {
- for (const stats of [...statsLog].reverse()) {
- this.onStats(stats);
- }
- }
+function onStats(connStats) {
+ stats.push(connStats);
+ if (stats.length > 50) stats.shift();
+
+ let cpuPolylinePointsStats = stats.map((s, i) => [viewBoxX - ((stats.length - 1) - i), (1 - s.cpu) * viewBoxY]);
+ let memPolylinePointsStats = stats.map((s, i) => [viewBoxX - ((stats.length - 1) - i), (1 - (s.mem.active / props.meta.mem.total)) * viewBoxY]);
+ cpuPolylinePoints = cpuPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' ');
+ memPolylinePoints = memPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' ');
+
+ cpuPolygonPoints = `${viewBoxX - (stats.length - 1)},${viewBoxY} ${cpuPolylinePoints} ${viewBoxX},${viewBoxY}`;
+ memPolygonPoints = `${viewBoxX - (stats.length - 1)},${viewBoxY} ${memPolylinePoints} ${viewBoxX},${viewBoxY}`;
+
+ cpuHeadX = cpuPolylinePointsStats[cpuPolylinePointsStats.length - 1][0];
+ cpuHeadY = cpuPolylinePointsStats[cpuPolylinePointsStats.length - 1][1];
+ memHeadX = memPolylinePointsStats[memPolylinePointsStats.length - 1][0];
+ memHeadY = memPolylinePointsStats[memPolylinePointsStats.length - 1][1];
+
+ cpuP = (connStats.cpu * 100).toFixed(0);
+ memP = (connStats.mem.active / props.meta.mem.total * 100).toFixed(0);
+}
+
+function onStatsLog(statsLog) {
+ for (const revStats of [...statsLog].reverse()) {
+ onStats(revStats);
}
-});
+}
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/widgets/server-metric/cpu.vue b/packages/client/src/widgets/server-metric/cpu.vue
index 4478ee3065..baf802cb8f 100644
--- a/packages/client/src/widgets/server-metric/cpu.vue
+++ b/packages/client/src/widgets/server-metric/cpu.vue
@@ -9,38 +9,27 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { onMounted, onBeforeUnmount } from 'vue';
import XPie from './pie.vue';
-export default defineComponent({
- components: {
- XPie
- },
- props: {
- connection: {
- required: true,
- },
- meta: {
- required: true,
- }
- },
- data() {
- return {
- usage: 0,
- };
- },
- mounted() {
- this.connection.on('stats', this.onStats);
- },
- beforeUnmount() {
- this.connection.off('stats', this.onStats);
- },
- methods: {
- onStats(stats) {
- this.usage = stats.cpu;
- }
- }
+const props = defineProps<{
+ connection: any,
+ meta: any
+}>();
+
+let usage: number = $ref(0);
+
+function onStats(stats) {
+ usage = stats.cpu;
+}
+
+onMounted(() => {
+ props.connection.on('stats', onStats);
+});
+
+onBeforeUnmount(() => {
+ props.connection.off('stats', onStats);
});
</script>
diff --git a/packages/client/src/widgets/server-metric/index.vue b/packages/client/src/widgets/server-metric/index.vue
index 2caa73fa74..9e86b811d1 100644
--- a/packages/client/src/widgets/server-metric/index.vue
+++ b/packages/client/src/widgets/server-metric/index.vue
@@ -50,7 +50,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure, save } = useWidgetPropsManager(name,
widgetPropsDef,
@@ -65,7 +65,7 @@ os.api('server-info', {}).then(res => {
});
const toggleView = () => {
- if (widgetProps.view == 4) {
+ if (widgetProps.view === 4) {
widgetProps.view = 0;
} else {
widgetProps.view++;
diff --git a/packages/client/src/widgets/server-metric/mem.vue b/packages/client/src/widgets/server-metric/mem.vue
index a6ca7b1175..6018eb4265 100644
--- a/packages/client/src/widgets/server-metric/mem.vue
+++ b/packages/client/src/widgets/server-metric/mem.vue
@@ -10,46 +10,34 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { onMounted, onBeforeUnmount } from 'vue';
import XPie from './pie.vue';
import bytes from '@/filters/bytes';
-export default defineComponent({
- components: {
- XPie
- },
- props: {
- connection: {
- required: true,
- },
- meta: {
- required: true,
- }
- },
- data() {
- return {
- usage: 0,
- total: 0,
- used: 0,
- free: 0,
- };
- },
- mounted() {
- this.connection.on('stats', this.onStats);
- },
- beforeUnmount() {
- this.connection.off('stats', this.onStats);
- },
- methods: {
- onStats(stats) {
- this.usage = stats.mem.active / this.meta.mem.total;
- this.total = this.meta.mem.total;
- this.used = stats.mem.active;
- this.free = this.meta.mem.total - stats.mem.active;
- },
- bytes
- }
+const props = defineProps<{
+ connection: any,
+ meta: any
+}>();
+
+let usage: number = $ref(0);
+let total: number = $ref(0);
+let used: number = $ref(0);
+let free: number = $ref(0);
+
+function onStats(stats) {
+ usage = stats.mem.active / props.meta.mem.total;
+ total = props.meta.mem.total;
+ used = stats.mem.active;
+ free = total - used;
+}
+
+onMounted(() => {
+ props.connection.on('stats', onStats);
+});
+
+onBeforeUnmount(() => {
+ props.connection.off('stats', onStats);
});
</script>
diff --git a/packages/client/src/widgets/server-metric/net.vue b/packages/client/src/widgets/server-metric/net.vue
index 23c148eeb6..b698953f97 100644
--- a/packages/client/src/widgets/server-metric/net.vue
+++ b/packages/client/src/widgets/server-metric/net.vue
@@ -43,79 +43,71 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { onMounted, onBeforeUnmount } from 'vue';
import bytes from '@/filters/bytes';
-export default defineComponent({
- props: {
- connection: {
- required: true,
- },
- meta: {
- required: true,
- }
- },
- data() {
- return {
- viewBoxX: 50,
- viewBoxY: 30,
- stats: [],
- inPolylinePoints: '',
- outPolylinePoints: '',
- inPolygonPoints: '',
- outPolygonPoints: '',
- inHeadX: null,
- inHeadY: null,
- outHeadX: null,
- outHeadY: null,
- inRecent: 0,
- outRecent: 0
- };
- },
- mounted() {
- this.connection.on('stats', this.onStats);
- this.connection.on('statsLog', this.onStatsLog);
- this.connection.send('requestLog', {
- id: Math.random().toString().substr(2, 8)
- });
- },
- beforeUnmount() {
- this.connection.off('stats', this.onStats);
- this.connection.off('statsLog', this.onStatsLog);
- },
- methods: {
- onStats(stats) {
- this.stats.push(stats);
- if (this.stats.length > 50) this.stats.shift();
+const props = defineProps<{
+ connection: any,
+ meta: any
+}>();
+
+let viewBoxX: number = $ref(50);
+let viewBoxY: number = $ref(30);
+let stats: any[] = $ref([]);
+let inPolylinePoints: string = $ref('');
+let outPolylinePoints: string = $ref('');
+let inPolygonPoints: string = $ref('');
+let outPolygonPoints: string = $ref('');
+let inHeadX: any = $ref(null);
+let inHeadY: any = $ref(null);
+let outHeadX: any = $ref(null);
+let outHeadY: any = $ref(null);
+let inRecent: number = $ref(0);
+let outRecent: number = $ref(0);
+
+onMounted(() => {
+ props.connection.on('stats', onStats);
+ props.connection.on('statsLog', onStatsLog);
+ props.connection.send('requestLog', {
+ id: Math.random().toString().substr(2, 8)
+ });
+});
- const inPeak = Math.max(1024 * 64, Math.max(...this.stats.map(s => s.net.rx)));
- const outPeak = Math.max(1024 * 64, Math.max(...this.stats.map(s => s.net.tx)));
+onBeforeUnmount(() => {
+ props.connection.off('stats', onStats);
+ props.connection.off('statsLog', onStatsLog);
+});
- const inPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - (s.net.rx / inPeak)) * this.viewBoxY]);
- const outPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - (s.net.tx / outPeak)) * this.viewBoxY]);
- this.inPolylinePoints = inPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
- this.outPolylinePoints = outPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
+function onStats(connStats) {
+ stats.push(connStats);
+ if (stats.length > 50) stats.shift();
- this.inPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.inPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`;
- this.outPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.outPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`;
+ const inPeak = Math.max(1024 * 64, Math.max(...stats.map(s => s.net.rx)));
+ const outPeak = Math.max(1024 * 64, Math.max(...stats.map(s => s.net.tx)));
- this.inHeadX = inPolylinePoints[inPolylinePoints.length - 1][0];
- this.inHeadY = inPolylinePoints[inPolylinePoints.length - 1][1];
- this.outHeadX = outPolylinePoints[outPolylinePoints.length - 1][0];
- this.outHeadY = outPolylinePoints[outPolylinePoints.length - 1][1];
+ let inPolylinePointsStats = stats.map((s, i) => [viewBoxX - ((stats.length - 1) - i), (1 - (s.net.rx / inPeak)) * viewBoxY]);
+ let outPolylinePointsStats = stats.map((s, i) => [viewBoxX - ((stats.length - 1) - i), (1 - (s.net.tx / outPeak)) * viewBoxY]);
+ inPolylinePoints = inPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' ');
+ outPolylinePoints = outPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' ');
- this.inRecent = stats.net.rx;
- this.outRecent = stats.net.tx;
- },
- onStatsLog(statsLog) {
- for (const stats of [...statsLog].reverse()) {
- this.onStats(stats);
- }
- },
- bytes
+ inPolygonPoints = `${viewBoxX - (stats.length - 1)},${viewBoxY} ${inPolylinePoints} ${viewBoxX},${viewBoxY}`;
+ outPolygonPoints = `${viewBoxX - (stats.length - 1)},${viewBoxY} ${outPolylinePoints} ${viewBoxX},${viewBoxY}`;
+
+ inHeadX = inPolylinePointsStats[inPolylinePointsStats.length - 1][0];
+ inHeadY = inPolylinePointsStats[inPolylinePointsStats.length - 1][1];
+ outHeadX = outPolylinePointsStats[outPolylinePointsStats.length - 1][0];
+ outHeadY = outPolylinePointsStats[outPolylinePointsStats.length - 1][1];
+
+ inRecent = connStats.net.rx;
+ outRecent = connStats.net.tx;
+}
+
+function onStatsLog(statsLog) {
+ for (const revStats of [...statsLog].reverse()) {
+ onStats(revStats);
}
-});
+}
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/widgets/slideshow.vue b/packages/client/src/widgets/slideshow.vue
index 7b2e539685..fd78edbe40 100644
--- a/packages/client/src/widgets/slideshow.vue
+++ b/packages/client/src/widgets/slideshow.vue
@@ -1,5 +1,5 @@
<template>
-<div class="kvausudm _panel" :style="{ height: widgetProps.height + 'px' }">
+<div class="kvausudm _panel mkw-slideshow" :style="{ height: widgetProps.height + 'px' }">
<div @click="choose">
<p v-if="widgetProps.folderId == null">
{{ $ts.folder }}
@@ -37,7 +37,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure, save } = useWidgetPropsManager(name,
widgetPropsDef,
@@ -51,7 +51,7 @@ const slideA = ref<HTMLElement>();
const slideB = ref<HTMLElement>();
const change = () => {
- if (images.value.length == 0) return;
+ if (images.value.length === 0) return;
const index = Math.floor(Math.random() * images.value.length);
const img = `url(${ images.value[index].url })`;
diff --git a/packages/client/src/widgets/timeline.vue b/packages/client/src/widgets/timeline.vue
index 34e3b20e36..3bcad1ae29 100644
--- a/packages/client/src/widgets/timeline.vue
+++ b/packages/client/src/widgets/timeline.vue
@@ -1,5 +1,5 @@
<template>
-<MkContainer :show-header="widgetProps.showHeader" :style="`height: ${widgetProps.height}px;`" :scrollable="true">
+<MkContainer :show-header="widgetProps.showHeader" :style="`height: ${widgetProps.height}px;`" :scrollable="true" class="mkw-timeline">
<template #header>
<button class="_button" @click="choose">
<i v-if="widgetProps.src === 'home'" class="fas fa-home"></i>
@@ -63,7 +63,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure, save } = useWidgetPropsManager(name,
widgetPropsDef,
@@ -103,19 +103,19 @@ const choose = async (ev) => {
os.popupMenu([{
text: i18n.ts._timelines.home,
icon: 'fas fa-home',
- action: () => { setSrc('home') }
+ action: () => { setSrc('home'); }
}, {
text: i18n.ts._timelines.local,
icon: 'fas fa-comments',
- action: () => { setSrc('local') }
+ action: () => { setSrc('local'); }
}, {
text: i18n.ts._timelines.social,
icon: 'fas fa-share-alt',
- action: () => { setSrc('social') }
+ action: () => { setSrc('social'); }
}, {
text: i18n.ts._timelines.global,
icon: 'fas fa-globe',
- action: () => { setSrc('global') }
+ action: () => { setSrc('global'); }
}, antennaItems.length > 0 ? null : undefined, ...antennaItems, listItems.length > 0 ? null : undefined, ...listItems], ev.currentTarget ?? ev.target).then(() => {
menuOpened.value = false;
});
diff --git a/packages/client/src/widgets/trends.vue b/packages/client/src/widgets/trends.vue
index a34710eae7..9680f1c892 100644
--- a/packages/client/src/widgets/trends.vue
+++ b/packages/client/src/widgets/trends.vue
@@ -1,5 +1,5 @@
<template>
-<MkContainer :show-header="widgetProps.showHeader">
+<MkContainer :show-header="widgetProps.showHeader" class="mkw-trends">
<template #header><i class="fas fa-hashtag"></i>{{ $ts._widgets.trends }}</template>
<div class="wbrkwala">
@@ -40,7 +40,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/widget.ts b/packages/client/src/widgets/widget.ts
index 81239bfb3b..9626d01619 100644
--- a/packages/client/src/widgets/widget.ts
+++ b/packages/client/src/widgets/widget.ts
@@ -13,7 +13,7 @@ export type WidgetComponentProps<P extends Record<string, unknown>> = {
};
export type WidgetComponentEmits<P extends Record<string, unknown>> = {
- (e: 'updateProps', props: P);
+ (ev: 'updateProps', props: P);
};
export type WidgetComponentExpose = {
@@ -45,7 +45,7 @@ export const useWidgetPropsManager = <F extends Form & Record<string, { default:
}, { deep: true, immediate: true, });
const save = throttle(3000, () => {
- emit('updateProps', widgetProps)
+ emit('updateProps', widgetProps);
});
const configure = async () => {
diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json
index b44cf2f895..f7320a7251 100644
--- a/packages/client/tsconfig.json
+++ b/packages/client/tsconfig.json
@@ -18,6 +18,9 @@
"strictNullChecks": true,
"experimentalDecorators": true,
"resolveJsonModule": true,
+ "allowSyntheticDefaultImports": true,
+ "isolatedModules": true,
+ "useDefineForClassFields": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
@@ -26,14 +29,17 @@
"node_modules/@types",
"@types",
],
+ "types": [
+ "vite/client",
+ ],
"lib": [
"esnext",
- "dom",
- "webworker"
+ "dom"
]
},
"compileOnSave": false,
"include": [
+ ".eslintrc.js",
"./**/*.ts",
"./**/*.vue"
]
diff --git a/packages/client/vite.config.ts b/packages/client/vite.config.ts
new file mode 100644
index 0000000000..af13e646c6
--- /dev/null
+++ b/packages/client/vite.config.ts
@@ -0,0 +1,72 @@
+import * as fs from 'fs';
+import pluginVue from '@vitejs/plugin-vue';
+import pluginJson5 from './vite.json5';
+import { defineConfig } from 'vite';
+
+import locales from '../../locales';
+import meta from '../../package.json';
+
+const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.json', '.json5', '.svg', '.sass', '.scss', '.css', '.vue'];
+
+export default defineConfig(({ command, mode }) => {
+ fs.mkdirSync(__dirname + '/../../built', { recursive: true });
+ fs.writeFileSync(__dirname + '/../../built/meta.json', JSON.stringify({ version: meta.version }), 'utf-8');
+
+ return {
+ base: '/assets/',
+
+ plugins: [
+ pluginVue({
+ reactivityTransform: true,
+ }),
+ pluginJson5(),
+ ],
+
+ resolve: {
+ extensions,
+ alias: {
+ '@/': __dirname + '/src/',
+ '/client-assets/': __dirname + '/assets/',
+ '/static-assets/': __dirname + '/../backend/assets/',
+ },
+ },
+
+ define: {
+ _VERSION_: JSON.stringify(meta.version),
+ _LANGS_: JSON.stringify(Object.entries(locales).map(([k, v]) => [k, v._lang_])),
+ _ENV_: JSON.stringify(process.env.NODE_ENV),
+ _DEV_: process.env.NODE_ENV !== 'production',
+ _PERF_PREFIX_: JSON.stringify('Misskey:'),
+ _DATA_TRANSFER_DRIVE_FILE_: JSON.stringify('mk_drive_file'),
+ _DATA_TRANSFER_DRIVE_FOLDER_: JSON.stringify('mk_drive_folder'),
+ _DATA_TRANSFER_DECK_COLUMN_: JSON.stringify('mk_deck_column'),
+ __VUE_OPTIONS_API__: true,
+ __VUE_PROD_DEVTOOLS__: false,
+ },
+
+ build: {
+ target: [
+ 'chrome100',
+ 'firefox100',
+ 'safari15',
+ ],
+ manifest: 'manifest.json',
+ rollupOptions: {
+ input: {
+ app: './src/init.ts',
+ },
+ output: {
+ manualChunks: {
+ vue: ['vue', 'vue-router'],
+ },
+ },
+ },
+ cssCodeSplit: true,
+ outDir: __dirname + '/../../built/_client_dist_',
+ assetsDir: '.',
+ emptyOutDir: false,
+ sourcemap: process.env.NODE_ENV !== 'production',
+ reportCompressedSize: false,
+ },
+ }
+});
diff --git a/packages/client/vite.json5.ts b/packages/client/vite.json5.ts
new file mode 100644
index 0000000000..693ee7be06
--- /dev/null
+++ b/packages/client/vite.json5.ts
@@ -0,0 +1,38 @@
+// Original: https://github.com/rollup/plugins/tree/8835dd2aed92f408d7dc72d7cc25a9728e16face/packages/json
+
+import JSON5 from 'json5';
+import { Plugin } from 'rollup';
+import { createFilter, dataToEsm } from '@rollup/pluginutils';
+import { RollupJsonOptions } from '@rollup/plugin-json';
+
+export default function json5(options: RollupJsonOptions = {}): Plugin {
+ const filter = createFilter(options.include, options.exclude);
+ const indent = 'indent' in options ? options.indent : '\t';
+
+ return {
+ name: 'json5',
+
+ // eslint-disable-next-line no-shadow
+ transform(json, id) {
+ if (id.slice(-6) !== '.json5' || !filter(id)) return null;
+
+ try {
+ const parsed = JSON5.parse(json);
+ return {
+ code: dataToEsm(parsed, {
+ preferConst: options.preferConst,
+ compact: options.compact,
+ namedExports: options.namedExports,
+ indent
+ }),
+ map: { mappings: '' }
+ };
+ } catch (err) {
+ const message = 'Could not parse JSON file';
+ const position = parseInt(/[\d]/.exec(err.message)[0], 10);
+ this.warn({ message, id, position });
+ return null;
+ }
+ }
+ };
+}
diff --git a/packages/client/webpack.config.js b/packages/client/webpack.config.js
deleted file mode 100644
index a50851e17f..0000000000
--- a/packages/client/webpack.config.js
+++ /dev/null
@@ -1,193 +0,0 @@
-/**
- * webpack configuration
- */
-
-const fs = require('fs');
-const webpack = require('webpack');
-const { VueLoaderPlugin } = require('vue-loader');
-
-class WebpackOnBuildPlugin {
- constructor(callback) {
- this.callback = callback;
- }
-
- apply(compiler) {
- compiler.hooks.done.tap('WebpackOnBuildPlugin', this.callback);
- }
-}
-
-const isProduction = process.env.NODE_ENV === 'production';
-
-const locales = require('../../locales');
-const meta = require('../../package.json');
-
-const postcss = {
- loader: 'postcss-loader',
- options: {
- postcssOptions: {
- plugins: [
- require('cssnano')({
- preset: 'default'
- })
- ]
- }
- },
-};
-
-module.exports = {
- entry: {
- app: './src/init.ts',
- sw: './src/sw/sw.ts'
- },
- module: {
- rules: [{
- test: /\.vue$/,
- exclude: /node_modules/,
- use: [{
- loader: 'vue-loader',
- options: {
- cssSourceMap: false,
- reactivityTransform: true,
- compilerOptions: {
- preserveWhitespace: false
- }
- }
- }]
- }, {
- test: /\.scss?$/,
- exclude: /node_modules/,
- oneOf: [{
- resourceQuery: /module/,
- use: [{
- loader: 'vue-style-loader'
- }, {
- loader: 'css-loader',
- options: {
- modules: true,
- esModule: false, // TODO: trueã«ã™ã‚‹ã¨å£Šã‚Œã‚‹ã€‚Vue3ç§»è¡Œã®æŠ˜ã«ã¯trueã«ã§ãã‚‹ã‹ã‚‚ã—れãªã„
- url: false,
- }
- }, postcss, {
- loader: 'sass-loader',
- options: {
- implementation: require('sass'),
- sassOptions: {
- fiber: false
- }
- }
- }]
- }, {
- use: [{
- loader: 'vue-style-loader'
- }, {
- loader: 'css-loader',
- options: {
- url: false,
- esModule: false, // TODO: trueã«ã™ã‚‹ã¨å£Šã‚Œã‚‹ã€‚Vue3ç§»è¡Œã®æŠ˜ã«ã¯trueã«ã§ãã‚‹ã‹ã‚‚ã—れãªã„
- }
- }, postcss, {
- loader: 'sass-loader',
- options: {
- implementation: require('sass'),
- sassOptions: {
- fiber: false
- }
- }
- }]
- }]
- }, {
- test: /\.css$/,
- oneOf: [{
- resourceQuery: /module/,
- use: [{
- loader: 'vue-style-loader'
- }, {
- loader: 'css-loader',
- options: {
- modules: true,
- esModule: false, // TODO: trueã«ã™ã‚‹ã¨å£Šã‚Œã‚‹ã€‚Vue3ç§»è¡Œã®æŠ˜ã«ã¯trueã«ã§ãã‚‹ã‹ã‚‚ã—れãªã„
- }
- }, postcss]
- }, {
- use: [{
- loader: 'vue-style-loader'
- }, {
- loader: 'css-loader',
- options: {
- esModule: false, // TODO: trueã«ã™ã‚‹ã¨å£Šã‚Œã‚‹ã€‚Vue3ç§»è¡Œã®æŠ˜ã«ã¯trueã«ã§ãã‚‹ã‹ã‚‚ã—れãªã„
- }
- }, postcss]
- }]
- }, {
- test: /\.svg$/,
- use: [
- 'vue-loader',
- 'vue-svg-loader',
- ],
- }, {
- test: /\.(eot|woff|woff2|svg|ttf)([?]?.*)$/,
- type: 'asset/resource'
- }, {
- test: /\.json5$/,
- loader: 'json5-loader',
- options: {
- esModule: false,
- },
- type: 'javascript/auto'
- }, {
- test: /\.ts$/,
- exclude: /node_modules/,
- use: [{
- loader: 'ts-loader',
- options: {
- happyPackMode: true,
- transpileOnly: true,
- configFile: __dirname + '/tsconfig.json',
- appendTsSuffixTo: [/\.vue$/]
- }
- }]
- }]
- },
- plugins: [
- new webpack.ProgressPlugin({}),
- new webpack.DefinePlugin({
- _VERSION_: JSON.stringify(meta.version),
- _LANGS_: JSON.stringify(Object.entries(locales).map(([k, v]) => [k, v._lang_])),
- _ENV_: JSON.stringify(process.env.NODE_ENV),
- _DEV_: process.env.NODE_ENV !== 'production',
- _PERF_PREFIX_: JSON.stringify('Misskey:'),
- _DATA_TRANSFER_DRIVE_FILE_: JSON.stringify('mk_drive_file'),
- _DATA_TRANSFER_DRIVE_FOLDER_: JSON.stringify('mk_drive_folder'),
- _DATA_TRANSFER_DECK_COLUMN_: JSON.stringify('mk_deck_column'),
- __VUE_OPTIONS_API__: true,
- __VUE_PROD_DEVTOOLS__: false,
- }),
- new VueLoaderPlugin(),
- new WebpackOnBuildPlugin(() => {
- fs.mkdirSync(__dirname + '/../../built', { recursive: true });
- fs.writeFileSync(__dirname + '/../../built/meta.json', JSON.stringify({ version: meta.version }), 'utf-8');
- }),
- ],
- output: {
- path: __dirname + '/../../built/_client_dist_',
- filename: `[name].${meta.version}.js`,
- publicPath: `/assets/`,
- pathinfo: false,
- },
- resolve: {
- extensions: [
- '.js', '.ts', '.json'
- ],
- alias: {
- '@': __dirname + '/src/',
- }
- },
- resolveLoader: {
- modules: ['node_modules']
- },
- experiments: {
- topLevelAwait: true
- },
- devtool: false, //'source-map',
- mode: isProduction ? 'production' : 'development'
-};
diff --git a/packages/client/yarn.lock b/packages/client/yarn.lock
index a4ac6d8712..796c72304a 100644
--- a/packages/client/yarn.lock
+++ b/packages/client/yarn.lock
@@ -2,27 +2,11 @@
# yarn lockfile v1
-"@babel/code-frame@^7.0.0":
- version "7.12.13"
- resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658"
- integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==
- dependencies:
- "@babel/highlight" "^7.12.13"
-
"@babel/helper-validator-identifier@^7.12.11":
version "7.12.11"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed"
integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==
-"@babel/highlight@^7.12.13":
- version "7.12.13"
- resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.12.13.tgz#8ab538393e00370b26271b01fa08f7f27f2e795c"
- integrity sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==
- dependencies:
- "@babel/helper-validator-identifier" "^7.12.11"
- chalk "^2.0.0"
- js-tokens "^4.0.0"
-
"@babel/parser@^7.16.4":
version "7.16.6"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.6.tgz#8f194828193e8fa79166f34a4b4e52f3e769a314"
@@ -56,6 +40,105 @@
lodash "^4.17.19"
to-fast-properties "^2.0.0"
+"@cropper/element-canvas@^2.0.0-beta":
+ version "2.0.0-beta"
+ resolved "https://registry.yarnpkg.com/@cropper/element-canvas/-/element-canvas-2.0.0-beta.tgz#9501e6a2512a78c7503f2974b1fc65f90c7fecca"
+ integrity sha512-cKbox0AsUx3pMCjT7mQZx3i5FoZTR/Lzz9awuRR8/EciViMN4KkfodGHWSUrIX3zSr0fECsrb2CyNKV8DKZdpQ==
+ dependencies:
+ "@cropper/element" "^2.0.0-beta"
+ "@cropper/utils" "^2.0.0-beta"
+
+"@cropper/element-crosshair@^2.0.0-beta":
+ version "2.0.0-beta"
+ resolved "https://registry.yarnpkg.com/@cropper/element-crosshair/-/element-crosshair-2.0.0-beta.tgz#9d6ee1e6ed90196b6d4d2425f84909b83ffc66df"
+ integrity sha512-V58xxH3+8TrT9PrUzNouRhcyucyX/xBV5hBv03g0zCu09C5p0BZjrhaPo3hkt8oQvnhYT9SbMTe+k5hIoZgkbQ==
+ dependencies:
+ "@cropper/element" "^2.0.0-beta"
+ "@cropper/utils" "^2.0.0-beta"
+
+"@cropper/element-grid@^2.0.0-beta":
+ version "2.0.0-beta"
+ resolved "https://registry.yarnpkg.com/@cropper/element-grid/-/element-grid-2.0.0-beta.tgz#af6f3fce213307403ad83d9935839bde39c9beeb"
+ integrity sha512-F+qVLrjuHjJbaut1Gd6qSruMqYOHudhDB/r0dcLtnRW4b1yPd/QyhM5F0KLtCX7Lh6GUvpz2V9Vb/EYQLZuOkw==
+ dependencies:
+ "@cropper/element" "^2.0.0-beta"
+ "@cropper/utils" "^2.0.0-beta"
+
+"@cropper/element-handle@^2.0.0-beta":
+ version "2.0.0-beta"
+ resolved "https://registry.yarnpkg.com/@cropper/element-handle/-/element-handle-2.0.0-beta.tgz#bd55667e133df402616d44a694110fd0e61eef0b"
+ integrity sha512-Ty12mLpiUM8XRGQN0lRNB7TKP5SOXbTWaW2Uvli1Tu3Y6iLTtXUvs2VZ/fGR8XvhB7v7Lvo+OPfzuxIRx4gwKg==
+ dependencies:
+ "@cropper/element" "^2.0.0-beta"
+ "@cropper/utils" "^2.0.0-beta"
+
+"@cropper/element-image@^2.0.0-beta":
+ version "2.0.0-beta"
+ resolved "https://registry.yarnpkg.com/@cropper/element-image/-/element-image-2.0.0-beta.tgz#170dbdfbeef75de2f2c0089d4739ad980d69390a"
+ integrity sha512-CrHEMBo5svjj72qePBPGV4ut70RTI6n5U2k2YKcZihHSNU2h6SUEx8zkN8lNIgelsv2Bpb/PvSd1eu26BrJbtA==
+ dependencies:
+ "@cropper/element" "^2.0.0-beta"
+ "@cropper/element-canvas" "^2.0.0-beta"
+ "@cropper/utils" "^2.0.0-beta"
+
+"@cropper/element-selection@^2.0.0-beta":
+ version "2.0.0-beta"
+ resolved "https://registry.yarnpkg.com/@cropper/element-selection/-/element-selection-2.0.0-beta.tgz#7e1e498773bc26bb09ddaf09b0cafbe5b359ed7b"
+ integrity sha512-MEK+pn2Bma5cXf1N9mC3fRKNvzi6Aj9V2TdhaCl6KdOn6Bp10a+SR8y555MXd80zzFAU/eR1e7TMTyJiPRJFcw==
+ dependencies:
+ "@cropper/element" "^2.0.0-beta"
+ "@cropper/element-canvas" "^2.0.0-beta"
+ "@cropper/element-image" "^2.0.0-beta"
+ "@cropper/utils" "^2.0.0-beta"
+
+"@cropper/element-shade@^2.0.0-beta":
+ version "2.0.0-beta"
+ resolved "https://registry.yarnpkg.com/@cropper/element-shade/-/element-shade-2.0.0-beta.tgz#55400aec3e352d959a706bfff1b82afca955d33e"
+ integrity sha512-vfKTTkRFio/bi0ueIbdyg2ukhS35/ufsgA13dfzOgkyUT/TUsqTLONNJA2fxO0WLKSajTtvrl1ShdrSXE+EKCQ==
+ dependencies:
+ "@cropper/element" "^2.0.0-beta"
+ "@cropper/element-canvas" "^2.0.0-beta"
+ "@cropper/element-selection" "^2.0.0-beta"
+ "@cropper/utils" "^2.0.0-beta"
+
+"@cropper/element-viewer@^2.0.0-beta":
+ version "2.0.0-beta"
+ resolved "https://registry.yarnpkg.com/@cropper/element-viewer/-/element-viewer-2.0.0-beta.tgz#9a83b670f5cc667d7fc0071f08a1476817e0ed4e"
+ integrity sha512-ZsqdOWJ8OIrK1JR00ibmYrvVMYQVFXOudXezYtf8C5lc7DdtN4elmjVOfLQQM2kxG0WvflIVo6oqqyOzFnsAFg==
+ dependencies:
+ "@cropper/element" "^2.0.0-beta"
+ "@cropper/element-canvas" "^2.0.0-beta"
+ "@cropper/element-image" "^2.0.0-beta"
+ "@cropper/element-selection" "^2.0.0-beta"
+ "@cropper/utils" "^2.0.0-beta"
+
+"@cropper/element@^2.0.0-beta":
+ version "2.0.0-beta"
+ resolved "https://registry.yarnpkg.com/@cropper/element/-/element-2.0.0-beta.tgz#7833a92471a16e8860530e10658add42e8781959"
+ integrity sha512-seS8oDe2+Vpsy+yyqUIHzjIP6WUQRxwhFjLml/s2e+L6jF9o+g0KHzLJkBCV/ASKBnyb00aLjAt0dBXPLW/KgQ==
+ dependencies:
+ "@cropper/utils" "^2.0.0-beta"
+
+"@cropper/elements@^2.0.0-beta":
+ version "2.0.0-beta"
+ resolved "https://registry.yarnpkg.com/@cropper/elements/-/elements-2.0.0-beta.tgz#e73a4edaeff7e41dcca8d096bd1bc2bdc6a376e9"
+ integrity sha512-Huyptek2Q6141fRiuejhOyec/viX4zmUeMnpi+5h7OBuorTYUowZ823mmfgBZ4bb7+VPdAl79vUECV9EYq/ciw==
+ dependencies:
+ "@cropper/element" "^2.0.0-beta"
+ "@cropper/element-canvas" "^2.0.0-beta"
+ "@cropper/element-crosshair" "^2.0.0-beta"
+ "@cropper/element-grid" "^2.0.0-beta"
+ "@cropper/element-handle" "^2.0.0-beta"
+ "@cropper/element-image" "^2.0.0-beta"
+ "@cropper/element-selection" "^2.0.0-beta"
+ "@cropper/element-shade" "^2.0.0-beta"
+ "@cropper/element-viewer" "^2.0.0-beta"
+
+"@cropper/utils@^2.0.0-beta":
+ version "2.0.0-beta"
+ resolved "https://registry.yarnpkg.com/@cropper/utils/-/utils-2.0.0-beta.tgz#7290b03c8c1dc7a2f33406c8aecc80b339425f0e"
+ integrity sha512-Bb3hCyHK2w0l0i8OtRw6C9Q5ytUC5qN+l+kx7F3GiAAFZMX7jGyfPB0uLiZ2TwDm5mosnWjyLVXmCGDcTUnYaQ==
+
"@cypress/request@^2.88.10":
version "2.88.10"
resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.10.tgz#b66d76b07f860d3a4b8d7a0604d020c662752cce"
@@ -88,34 +171,29 @@
debug "^3.1.0"
lodash.once "^4.1.1"
-"@discordapp/twemoji@13.1.1":
- version "13.1.1"
- resolved "https://registry.yarnpkg.com/@discordapp/twemoji/-/twemoji-13.1.1.tgz#f750d491ffb740eca619fac0c63650c1de7fff91"
- integrity sha512-WDnPjWq/trfCcZk7dzQ2cYH5v5XaIfPzyixJ//O9XKilYYZRVS3p61vFvax5qMwanMMbnNG1iOzeqHKtivO32A==
+"@discordapp/twemoji@14.0.2":
+ version "14.0.2"
+ resolved "https://registry.yarnpkg.com/@discordapp/twemoji/-/twemoji-14.0.2.tgz#50cc19f6f3769dc6b36eb251421b5f5d4629e837"
+ integrity sha512-eYJpFsjViDTYwq3f6v+tRu8iRc+yLAeGrlh6kmNRvvC6rroUE2bMlBfEQ/WNh+2Q1FtSEFXpxzuQPOHzRzbAyA==
dependencies:
fs-extra "^8.0.1"
jsonfile "^5.0.0"
- twemoji-parser "13.1.0"
+ twemoji-parser "14.0.0"
universalify "^0.1.2"
-"@discoveryjs/json-ext@^0.5.0":
- version "0.5.2"
- resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz#8f03a22a04de437254e8ce8cc84ba39689288752"
- integrity sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg==
-
-"@eslint/eslintrc@^1.2.1":
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6"
- integrity sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==
+"@eslint/eslintrc@^1.3.0":
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
+ integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==
dependencies:
ajv "^6.12.4"
debug "^4.3.2"
- espree "^9.3.1"
- globals "^13.9.0"
+ espree "^9.3.2"
+ globals "^13.15.0"
ignore "^5.2.0"
import-fresh "^3.2.1"
js-yaml "^4.1.0"
- minimatch "^3.0.4"
+ minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@fortawesome/fontawesome-free@6.1.1":
@@ -170,6 +248,29 @@
"@nodelib/fs.scandir" "2.1.3"
fastq "^1.6.0"
+"@rollup/plugin-alias@3.1.9":
+ version "3.1.9"
+ resolved "https://registry.yarnpkg.com/@rollup/plugin-alias/-/plugin-alias-3.1.9.tgz#a5d267548fe48441f34be8323fb64d1d4a1b3fdf"
+ integrity sha512-QI5fsEvm9bDzt32k39wpOwZhVzRcL5ydcffUHMyLVaVaLeC70I8TJZ17F1z1eMoLu4E/UOcH9BWVkKpIKdrfiw==
+ dependencies:
+ slash "^3.0.0"
+
+"@rollup/plugin-json@4.1.0":
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3"
+ integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==
+ dependencies:
+ "@rollup/pluginutils" "^3.0.8"
+
+"@rollup/pluginutils@^3.0.8":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
+ integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
+ dependencies:
+ "@types/estree" "0.0.39"
+ estree-walker "^1.0.1"
+ picomatch "^2.2.2"
+
"@sideway/address@^4.1.0":
version "4.1.2"
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.2.tgz#811b84333a335739d3969cfc434736268170cad1"
@@ -198,16 +299,6 @@
stringz "2.1.0"
uuid "7.0.3"
-"@trysound/sax@0.2.0":
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
- integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
-
-"@types/anymatch@*":
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a"
- integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==
-
"@types/color-name@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
@@ -218,39 +309,10 @@
resolved "https://registry.yarnpkg.com/@types/escape-regexp/-/escape-regexp-0.0.1.tgz#f1a977ccdf2ef059e9862bd3af5e92cbbe723e0e"
integrity sha512-ogj/ZTIdeFkiuxDwawYuZSIgC6suFGgBeZPr6Xs5lHEcvIXTjXGtH+/n8f1XhZhespaUwJ5LIGRICPji972FLw==
-"@types/eslint-scope@^3.7.0":
- version "3.7.0"
- resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86"
- integrity sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==
- dependencies:
- "@types/eslint" "*"
- "@types/estree" "*"
-
-"@types/eslint-scope@^3.7.3":
- version "3.7.3"
- resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224"
- integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==
- dependencies:
- "@types/eslint" "*"
- "@types/estree" "*"
-
-"@types/eslint@*":
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.0.tgz#eb5c5b575237334df24c53195e37b53d66478d7b"
- integrity sha512-LpUXkr7fnmPXWGxB0ZuLEzNeTURuHPavkC5zuU4sg62/TgL5ZEjamr5Y8b6AftwHtx2bPJasI+CL0TT2JwQ7aA==
- dependencies:
- "@types/estree" "*"
- "@types/json-schema" "*"
-
-"@types/estree@*", "@types/estree@^0.0.46":
- version "0.0.46"
- resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe"
- integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg==
-
-"@types/estree@^0.0.51":
- version "0.0.51"
- resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40"
- integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==
+"@types/estree@0.0.39":
+ version "0.0.39"
+ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
+ integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
"@types/events@*":
version "3.0.0"
@@ -309,21 +371,6 @@
resolved "https://registry.yarnpkg.com/@types/is-url/-/is-url-1.2.30.tgz#85567e8bee4fee69202bc3448f9fb34b0d56c50a"
integrity sha512-AnlNFwjzC8XLda5VjRl4ItSd8qp8pSNowvsut0WwQyBWHpOxjxRJm8iO6uETWqEyLdYdb9/1j+Qd9gQ4l5I4fw==
-"@types/json-schema@*":
- version "7.0.5"
- resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd"
- integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==
-
-"@types/json-schema@^7.0.6":
- version "7.0.6"
- resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
- integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==
-
-"@types/json-schema@^7.0.7":
- version "7.0.8"
- resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818"
- integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg==
-
"@types/json-schema@^7.0.9":
version "7.0.9"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
@@ -349,10 +396,10 @@
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
-"@types/mocha@9.1.0":
- version "9.1.0"
- resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.0.tgz#baf17ab2cca3fcce2d322ebc30454bff487efad5"
- integrity sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==
+"@types/mocha@9.1.1":
+ version "9.1.1"
+ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4"
+ integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==
"@types/node@*":
version "16.6.2"
@@ -371,26 +418,11 @@
dependencies:
"@types/node" "*"
-"@types/parse-json@^4.0.0":
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
- integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
-
-"@types/parse5@6.0.3":
- version "6.0.3"
- resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.3.tgz#705bb349e789efa06f43f128cef51240753424cb"
- integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==
-
"@types/punycode@2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@types/punycode/-/punycode-2.1.0.tgz#89e4f3d09b3f92e87a80505af19be7e0c31d4e83"
integrity sha512-PG5aLpW6PJOeV2fHRslP4IOMWn+G+Uq8CfnyJ+PDS8ndCbU+soO+fB3NKCKo0p/Jh2Y4aPaiQZsrOXFdzpcA6g==
-"@types/q@^1.5.1":
- version "1.5.2"
- resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
- integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
-
"@types/qrcode@1.4.2":
version "1.4.2"
resolved "https://registry.yarnpkg.com/@types/qrcode/-/qrcode-1.4.2.tgz#7d7142d6fa9921f195db342ed08b539181546c74"
@@ -418,33 +450,16 @@
resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef"
integrity sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==
-"@types/source-list-map@*":
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
- integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==
-
-"@types/tapable@^1":
- version "1.0.7"
- resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.7.tgz#545158342f949e8fd3bfd813224971ecddc3fac4"
- integrity sha512-0VBprVqfgFD7Ehb2vd8Lh9TG3jP98gvr8rgehQqzztZNI7o8zS8Ad4jyZneKELphpuE212D8J70LnSNQSyO6bQ==
-
-"@types/throttle-debounce@2.1.0":
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/@types/throttle-debounce/-/throttle-debounce-2.1.0.tgz#1c3df624bfc4b62f992d3012b84c56d41eab3776"
- integrity sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ==
+"@types/throttle-debounce@5.0.0":
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/@types/throttle-debounce/-/throttle-debounce-5.0.0.tgz#8208087f0af85107bcc681c50fa837fc9505483e"
+ integrity sha512-Pb7k35iCGFcGPECoNE4DYp3Oyf2xcTd3FbFQxXUI9hEYKUl6YX+KLf7HrBmgVcD05nl50LIH6i+80js4iYmWbw==
"@types/tinycolor2@1.4.3":
version "1.4.3"
resolved "https://registry.yarnpkg.com/@types/tinycolor2/-/tinycolor2-1.4.3.tgz#ed4a0901f954b126e6a914b4839c77462d56e706"
integrity sha512-Kf1w9NE5HEgGxCRyIcRXR/ZYtDv0V8FVPtYHwLxl0O+maGX0erE77pQlD0gpP+/KByMZ87mOA79SjifhSB3PjQ==
-"@types/uglify-js@*":
- version "3.9.0"
- resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.9.0.tgz#4490a140ca82aa855ad68093829e7fd6ae94ea87"
- integrity sha512-3ZcoyPYHVOCcLpnfZwD47KFLr8W/mpUcgjpf1M4Q78TMJIw7KMAHSjiCLJp1z3ZrBR9pTLbe191O0TldFK5zcw==
- dependencies:
- source-map "^0.6.1"
-
"@types/undertaker-registry@*":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/undertaker-registry/-/undertaker-registry-1.0.1.tgz#4306d4a03d7acedb974b66530832b90729e1d1da"
@@ -479,44 +494,6 @@
"@types/expect" "^1.20.4"
"@types/node" "*"
-"@types/webpack-sources@*":
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-0.1.7.tgz#0a330a9456113410c74a5d64180af0cbca007141"
- integrity sha512-XyaHrJILjK1VHVC4aVlKsdNN5KBTwufMb43cQs+flGxtPAf/1Qwl8+Q0tp5BwEGaI8D6XT1L+9bSWXckgkjTLw==
- dependencies:
- "@types/node" "*"
- "@types/source-list-map" "*"
- source-map "^0.6.1"
-
-"@types/webpack-stream@3.2.12":
- version "3.2.12"
- resolved "https://registry.yarnpkg.com/@types/webpack-stream/-/webpack-stream-3.2.12.tgz#cf13e64067a662a7acd8cd0524b3f64c86b0ecb6"
- integrity sha512-znMUl4kKT0V0SwkUgRgwUNSAO7J5I/jdTCBNy3utkCsgMJ3IHp4FBTDwsQC+tfQ73TWeKIH05QNmbUYmeGThGw==
- dependencies:
- "@types/node" "*"
- "@types/webpack" "^4"
-
-"@types/webpack@5.28.0":
- version "5.28.0"
- resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-5.28.0.tgz#78dde06212f038d77e54116cfe69e88ae9ed2c03"
- integrity sha512-8cP0CzcxUiFuA9xGJkfeVpqmWTk9nx6CWwamRGCj95ph1SmlRRk9KlCZ6avhCbZd4L68LvYT6l1kpdEnQXrF8w==
- dependencies:
- "@types/node" "*"
- tapable "^2.2.0"
- webpack "^5"
-
-"@types/webpack@^4":
- version "4.41.27"
- resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.27.tgz#f47da488c8037e7f1b2dbf2714fbbacb61ec0ffc"
- integrity sha512-wK/oi5gcHi72VMTbOaQ70VcDxSQ1uX8S2tukBK9ARuGXrYM/+u4ou73roc7trXDNmCxCoerE8zruQqX/wuHszA==
- dependencies:
- "@types/anymatch" "*"
- "@types/node" "*"
- "@types/tapable" "^1"
- "@types/uglify-js" "*"
- "@types/webpack-sources" "*"
- source-map "^0.6.0"
-
"@types/websocket@1.0.5":
version "1.0.5"
resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.5.tgz#3fb80ed8e07f88e51961211cd3682a3a4a81569c"
@@ -538,454 +515,190 @@
dependencies:
"@types/node" "*"
-"@typescript-eslint/eslint-plugin@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.18.0.tgz#950df411cec65f90d75d6320a03b2c98f6c3af7d"
- integrity sha512-tzrmdGMJI/uii9/V6lurMo4/o+dMTKDH82LkNjhJ3adCW22YQydoRs5MwTiqxGF9CSYxPxQ7EYb4jLNlIs+E+A==
+"@typescript-eslint/eslint-plugin@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.27.1.tgz#fdf59c905354139046b41b3ed95d1609913d0758"
+ integrity sha512-6dM5NKT57ZduNnJfpY81Phe9nc9wolnMCnknb1im6brWi1RYv84nbMS3olJa27B6+irUVV1X/Wb+Am0FjJdGFw==
dependencies:
- "@typescript-eslint/scope-manager" "5.18.0"
- "@typescript-eslint/type-utils" "5.18.0"
- "@typescript-eslint/utils" "5.18.0"
- debug "^4.3.2"
+ "@typescript-eslint/scope-manager" "5.27.1"
+ "@typescript-eslint/type-utils" "5.27.1"
+ "@typescript-eslint/utils" "5.27.1"
+ debug "^4.3.4"
functional-red-black-tree "^1.0.1"
- ignore "^5.1.8"
+ ignore "^5.2.0"
regexpp "^3.2.0"
- semver "^7.3.5"
+ semver "^7.3.7"
tsutils "^3.21.0"
-"@typescript-eslint/parser@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.18.0.tgz#2bcd4ff21df33621df33e942ccb21cb897f004c6"
- integrity sha512-+08nYfurBzSSPndngnHvFw/fniWYJ5ymOrn/63oMIbgomVQOvIDhBoJmYZ9lwQOCnQV9xHGvf88ze3jFGUYooQ==
+"@typescript-eslint/parser@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.27.1.tgz#3a4dcaa67e45e0427b6ca7bb7165122c8b569639"
+ integrity sha512-7Va2ZOkHi5NP+AZwb5ReLgNF6nWLGTeUJfxdkVUAPPSaAdbWNnFZzLZ4EGGmmiCTg+AwlbE1KyUYTBglosSLHQ==
dependencies:
- "@typescript-eslint/scope-manager" "5.18.0"
- "@typescript-eslint/types" "5.18.0"
- "@typescript-eslint/typescript-estree" "5.18.0"
- debug "^4.3.2"
+ "@typescript-eslint/scope-manager" "5.27.1"
+ "@typescript-eslint/types" "5.27.1"
+ "@typescript-eslint/typescript-estree" "5.27.1"
+ debug "^4.3.4"
-"@typescript-eslint/scope-manager@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.18.0.tgz#a7d7b49b973ba8cebf2a3710eefd457ef2fb5505"
- integrity sha512-C0CZML6NyRDj+ZbMqh9FnPscg2PrzSaVQg3IpTmpe0NURMVBXlghGZgMYqBw07YW73i0MCqSDqv2SbywnCS8jQ==
+"@typescript-eslint/scope-manager@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.27.1.tgz#4d1504392d01fe5f76f4a5825991ec78b7b7894d"
+ integrity sha512-fQEOSa/QroWE6fAEg+bJxtRZJTH8NTskggybogHt4H9Da8zd4cJji76gA5SBlR0MgtwF7rebxTbDKB49YUCpAg==
dependencies:
- "@typescript-eslint/types" "5.18.0"
- "@typescript-eslint/visitor-keys" "5.18.0"
+ "@typescript-eslint/types" "5.27.1"
+ "@typescript-eslint/visitor-keys" "5.27.1"
-"@typescript-eslint/type-utils@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.18.0.tgz#62dbfc8478abf36ba94a90ddf10be3cc8e471c74"
- integrity sha512-vcn9/6J5D6jtHxpEJrgK8FhaM8r6J1/ZiNu70ZUJN554Y3D9t3iovi6u7JF8l/e7FcBIxeuTEidZDR70UuCIfA==
+"@typescript-eslint/type-utils@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.27.1.tgz#369f695199f74c1876e395ebea202582eb1d4166"
+ integrity sha512-+UC1vVUWaDHRnC2cQrCJ4QtVjpjjCgjNFpg8b03nERmkHv9JV9X5M19D7UFMd+/G7T/sgFwX2pGmWK38rqyvXw==
dependencies:
- "@typescript-eslint/utils" "5.18.0"
- debug "^4.3.2"
+ "@typescript-eslint/utils" "5.27.1"
+ debug "^4.3.4"
tsutils "^3.21.0"
-"@typescript-eslint/types@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.18.0.tgz#4f0425d85fdb863071680983853c59a62ce9566e"
- integrity sha512-bhV1+XjM+9bHMTmXi46p1Led5NP6iqQcsOxgx7fvk6gGiV48c6IynY0apQb7693twJDsXiVzNXTflhplmaiJaw==
+"@typescript-eslint/types@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.27.1.tgz#34e3e629501349d38be6ae97841298c03a6ffbf1"
+ integrity sha512-LgogNVkBhCTZU/m8XgEYIWICD6m4dmEDbKXESCbqOXfKZxRKeqpiJXQIErv66sdopRKZPo5l32ymNqibYEH/xg==
-"@typescript-eslint/typescript-estree@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.18.0.tgz#6498e5ee69a32e82b6e18689e2f72e4060986474"
- integrity sha512-wa+2VAhOPpZs1bVij9e5gyVu60ReMi/KuOx4LKjGx2Y3XTNUDJgQ+5f77D49pHtqef/klglf+mibuHs9TrPxdQ==
+"@typescript-eslint/typescript-estree@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.27.1.tgz#7621ee78607331821c16fffc21fc7a452d7bc808"
+ integrity sha512-DnZvvq3TAJ5ke+hk0LklvxwYsnXpRdqUY5gaVS0D4raKtbznPz71UJGnPTHEFo0GDxqLOLdMkkmVZjSpET1hFw==
dependencies:
- "@typescript-eslint/types" "5.18.0"
- "@typescript-eslint/visitor-keys" "5.18.0"
- debug "^4.3.2"
- globby "^11.0.4"
+ "@typescript-eslint/types" "5.27.1"
+ "@typescript-eslint/visitor-keys" "5.27.1"
+ debug "^4.3.4"
+ globby "^11.1.0"
is-glob "^4.0.3"
- semver "^7.3.5"
+ semver "^7.3.7"
tsutils "^3.21.0"
-"@typescript-eslint/utils@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.18.0.tgz#27fc84cf95c1a96def0aae31684cb43a37e76855"
- integrity sha512-+hFGWUMMri7OFY26TsOlGa+zgjEy1ssEipxpLjtl4wSll8zy85x0GrUSju/FHdKfVorZPYJLkF3I4XPtnCTewA==
+"@typescript-eslint/utils@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.27.1.tgz#b4678b68a94bc3b85bf08f243812a6868ac5128f"
+ integrity sha512-mZ9WEn1ZLDaVrhRaYgzbkXBkTPghPFsup8zDbbsYTxC5OmqrFE7skkKS/sraVsLP3TcT3Ki5CSyEFBRkLH/H/w==
dependencies:
"@types/json-schema" "^7.0.9"
- "@typescript-eslint/scope-manager" "5.18.0"
- "@typescript-eslint/types" "5.18.0"
- "@typescript-eslint/typescript-estree" "5.18.0"
+ "@typescript-eslint/scope-manager" "5.27.1"
+ "@typescript-eslint/types" "5.27.1"
+ "@typescript-eslint/typescript-estree" "5.27.1"
eslint-scope "^5.1.1"
eslint-utils "^3.0.0"
-"@typescript-eslint/visitor-keys@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.18.0.tgz#c7c07709823804171d569017f3b031ced7253e60"
- integrity sha512-Hf+t+dJsjAKpKSkg3EHvbtEpFFb/1CiOHnvI8bjHgOD4/wAw3gKrA0i94LrbekypiZVanJu3McWJg7rWDMzRTg==
+"@typescript-eslint/visitor-keys@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.27.1.tgz#05a62666f2a89769dac2e6baa48f74e8472983af"
+ integrity sha512-xYs6ffo01nhdJgPieyk7HAOpjhTsx7r/oB9LWEhwAXgwn33tkr+W8DI2ChboqhZlC4q3TC6geDYPoiX8ROqyOQ==
dependencies:
- "@typescript-eslint/types" "5.18.0"
- eslint-visitor-keys "^3.0.0"
+ "@typescript-eslint/types" "5.27.1"
+ eslint-visitor-keys "^3.3.0"
"@ungap/promise-all-settled@1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
-"@vue/compiler-core@3.2.31":
- version "3.2.31"
- resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.31.tgz#d38f06c2cf845742403b523ab4596a3fda152e89"
- integrity sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==
+"@vitejs/plugin-vue@2.3.3":
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-2.3.3.tgz#fbf80cc039b82ac21a1acb0f0478de8f61fbf600"
+ integrity sha512-SmQLDyhz+6lGJhPELsBdzXGc+AcaT8stgkbiTFGpXPe8Tl1tJaBw1A6pxDqDuRsVkD8uscrkx3hA7QDOoKYtyw==
+
+"@vue/compiler-core@3.2.37":
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.37.tgz#b3c42e04c0e0f2c496ff1784e543fbefe91e215a"
+ integrity sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg==
dependencies:
"@babel/parser" "^7.16.4"
- "@vue/shared" "3.2.31"
+ "@vue/shared" "3.2.37"
estree-walker "^2.0.2"
source-map "^0.6.1"
-"@vue/compiler-dom@3.2.31":
- version "3.2.31"
- resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.31.tgz#b1b7dfad55c96c8cc2b919cd7eb5fd7e4ddbf00e"
- integrity sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg==
+"@vue/compiler-dom@3.2.37":
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz#10d2427a789e7c707c872da9d678c82a0c6582b5"
+ integrity sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ==
dependencies:
- "@vue/compiler-core" "3.2.31"
- "@vue/shared" "3.2.31"
+ "@vue/compiler-core" "3.2.37"
+ "@vue/shared" "3.2.37"
-"@vue/compiler-sfc@3.2.31":
- version "3.2.31"
- resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.31.tgz#d02b29c3fe34d599a52c5ae1c6937b4d69f11c2f"
- integrity sha512-748adc9msSPGzXgibHiO6T7RWgfnDcVQD+VVwYgSsyyY8Ans64tALHZANrKtOzvkwznV/F4H7OAod/jIlp/dkQ==
+"@vue/compiler-sfc@3.2.37":
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz#3103af3da2f40286edcd85ea495dcb35bc7f5ff4"
+ integrity sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg==
dependencies:
"@babel/parser" "^7.16.4"
- "@vue/compiler-core" "3.2.31"
- "@vue/compiler-dom" "3.2.31"
- "@vue/compiler-ssr" "3.2.31"
- "@vue/reactivity-transform" "3.2.31"
- "@vue/shared" "3.2.31"
+ "@vue/compiler-core" "3.2.37"
+ "@vue/compiler-dom" "3.2.37"
+ "@vue/compiler-ssr" "3.2.37"
+ "@vue/reactivity-transform" "3.2.37"
+ "@vue/shared" "3.2.37"
estree-walker "^2.0.2"
magic-string "^0.25.7"
postcss "^8.1.10"
source-map "^0.6.1"
-"@vue/compiler-ssr@3.2.31":
- version "3.2.31"
- resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.31.tgz#4fa00f486c9c4580b40a4177871ebbd650ecb99c"
- integrity sha512-mjN0rqig+A8TVDnsGPYJM5dpbjlXeHUm2oZHZwGyMYiGT/F4fhJf/cXy8QpjnLQK4Y9Et4GWzHn9PS8AHUnSkw==
+"@vue/compiler-ssr@3.2.37":
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz#4899d19f3a5fafd61524a9d1aee8eb0505313cff"
+ integrity sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw==
dependencies:
- "@vue/compiler-dom" "3.2.31"
- "@vue/shared" "3.2.31"
+ "@vue/compiler-dom" "3.2.37"
+ "@vue/shared" "3.2.37"
"@vue/devtools-api@^6.0.0":
version "6.0.12"
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.0.12.tgz#7b57cce215ae9f37a86984633b3aa3d595aa5b46"
integrity sha512-iO/4FIezHKXhiDBdKySCvJVh8/mZPxHpiQrTy+PXVqJZgpTPTdHy4q8GXulaY+UKEagdkBb0onxNQZ0LNiqVhw==
-"@vue/reactivity-transform@3.2.31":
- version "3.2.31"
- resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.31.tgz#0f5b25c24e70edab2b613d5305c465b50fc00911"
- integrity sha512-uS4l4z/W7wXdI+Va5pgVxBJ345wyGFKvpPYtdSgvfJfX/x2Ymm6ophQlXXB6acqGHtXuBqNyyO3zVp9b1r0MOA==
+"@vue/reactivity-transform@3.2.37":
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz#0caa47c4344df4ae59f5a05dde2a8758829f8eca"
+ integrity sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg==
dependencies:
"@babel/parser" "^7.16.4"
- "@vue/compiler-core" "3.2.31"
- "@vue/shared" "3.2.31"
+ "@vue/compiler-core" "3.2.37"
+ "@vue/shared" "3.2.37"
estree-walker "^2.0.2"
magic-string "^0.25.7"
-"@vue/reactivity@3.2.31":
- version "3.2.31"
- resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.31.tgz#fc90aa2cdf695418b79e534783aca90d63a46bbd"
- integrity sha512-HVr0l211gbhpEKYr2hYe7hRsV91uIVGFYNHj73njbARVGHQvIojkImKMaZNDdoDZOIkMsBc9a1sMqR+WZwfSCw==
+"@vue/reactivity@3.2.37":
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.37.tgz#5bc3847ac58828e2b78526e08219e0a1089f8848"
+ integrity sha512-/7WRafBOshOc6m3F7plwzPeCu/RCVv9uMpOwa/5PiY1Zz+WLVRWiy0MYKwmg19KBdGtFWsmZ4cD+LOdVPcs52A==
dependencies:
- "@vue/shared" "3.2.31"
+ "@vue/shared" "3.2.37"
-"@vue/runtime-core@3.2.31":
- version "3.2.31"
- resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.31.tgz#9d284c382f5f981b7a7b5971052a1dc4ef39ac7a"
- integrity sha512-Kcog5XmSY7VHFEMuk4+Gap8gUssYMZ2+w+cmGI6OpZWYOEIcbE0TPzzPHi+8XTzAgx1w/ZxDFcXhZeXN5eKWsA==
+"@vue/runtime-core@3.2.37":
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.37.tgz#7ba7c54bb56e5d70edfc2f05766e1ca8519966e3"
+ integrity sha512-JPcd9kFyEdXLl/i0ClS7lwgcs0QpUAWj+SKX2ZC3ANKi1U4DOtiEr6cRqFXsPwY5u1L9fAjkinIdB8Rz3FoYNQ==
dependencies:
- "@vue/reactivity" "3.2.31"
- "@vue/shared" "3.2.31"
+ "@vue/reactivity" "3.2.37"
+ "@vue/shared" "3.2.37"
-"@vue/runtime-dom@3.2.31":
- version "3.2.31"
- resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.31.tgz#79ce01817cb3caf2c9d923f669b738d2d7953eff"
- integrity sha512-N+o0sICVLScUjfLG7u9u5XCjvmsexAiPt17GNnaWHJUfsKed5e85/A3SWgKxzlxx2SW/Hw7RQxzxbXez9PtY3g==
+"@vue/runtime-dom@3.2.37":
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.37.tgz#002bdc8228fa63949317756fb1e92cdd3f9f4bbd"
+ integrity sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw==
dependencies:
- "@vue/runtime-core" "3.2.31"
- "@vue/shared" "3.2.31"
+ "@vue/runtime-core" "3.2.37"
+ "@vue/shared" "3.2.37"
csstype "^2.6.8"
-"@vue/server-renderer@3.2.31":
- version "3.2.31"
- resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.31.tgz#201e9d6ce735847d5989403af81ef80960da7141"
- integrity sha512-8CN3Zj2HyR2LQQBHZ61HexF5NReqngLT3oahyiVRfSSvak+oAvVmu8iNLSu6XR77Ili2AOpnAt1y8ywjjqtmkg==
- dependencies:
- "@vue/compiler-ssr" "3.2.31"
- "@vue/shared" "3.2.31"
-
-"@vue/shared@3.2.31":
- version "3.2.31"
- resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.31.tgz#c90de7126d833dcd3a4c7534d534be2fb41faa4e"
- integrity sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ==
-
-"@webassemblyjs/ast@1.11.0":
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.0.tgz#a5aa679efdc9e51707a4207139da57920555961f"
- integrity sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg==
+"@vue/server-renderer@3.2.37":
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.37.tgz#840a29c8dcc29bddd9b5f5ffa22b95c0e72afdfc"
+ integrity sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA==
dependencies:
- "@webassemblyjs/helper-numbers" "1.11.0"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
+ "@vue/compiler-ssr" "3.2.37"
+ "@vue/shared" "3.2.37"
-"@webassemblyjs/ast@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7"
- integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==
- dependencies:
- "@webassemblyjs/helper-numbers" "1.11.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
-
-"@webassemblyjs/floating-point-hex-parser@1.11.0":
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz#34d62052f453cd43101d72eab4966a022587947c"
- integrity sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA==
-
-"@webassemblyjs/floating-point-hex-parser@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f"
- integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==
-
-"@webassemblyjs/helper-api-error@1.11.0":
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz#aaea8fb3b923f4aaa9b512ff541b013ffb68d2d4"
- integrity sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w==
-
-"@webassemblyjs/helper-api-error@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16"
- integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==
-
-"@webassemblyjs/helper-buffer@1.11.0":
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz#d026c25d175e388a7dbda9694e91e743cbe9b642"
- integrity sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA==
-
-"@webassemblyjs/helper-buffer@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5"
- integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==
-
-"@webassemblyjs/helper-numbers@1.11.0":
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz#7ab04172d54e312cc6ea4286d7d9fa27c88cd4f9"
- integrity sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ==
- dependencies:
- "@webassemblyjs/floating-point-hex-parser" "1.11.0"
- "@webassemblyjs/helper-api-error" "1.11.0"
- "@xtuc/long" "4.2.2"
-
-"@webassemblyjs/helper-numbers@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae"
- integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==
- dependencies:
- "@webassemblyjs/floating-point-hex-parser" "1.11.1"
- "@webassemblyjs/helper-api-error" "1.11.1"
- "@xtuc/long" "4.2.2"
-
-"@webassemblyjs/helper-wasm-bytecode@1.11.0":
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz#85fdcda4129902fe86f81abf7e7236953ec5a4e1"
- integrity sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA==
-
-"@webassemblyjs/helper-wasm-bytecode@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1"
- integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==
-
-"@webassemblyjs/helper-wasm-section@1.11.0":
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz#9ce2cc89300262509c801b4af113d1ca25c1a75b"
- integrity sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew==
- dependencies:
- "@webassemblyjs/ast" "1.11.0"
- "@webassemblyjs/helper-buffer" "1.11.0"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
- "@webassemblyjs/wasm-gen" "1.11.0"
-
-"@webassemblyjs/helper-wasm-section@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a"
- integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/helper-buffer" "1.11.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
- "@webassemblyjs/wasm-gen" "1.11.1"
-
-"@webassemblyjs/ieee754@1.11.0":
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz#46975d583f9828f5d094ac210e219441c4e6f5cf"
- integrity sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA==
- dependencies:
- "@xtuc/ieee754" "^1.2.0"
-
-"@webassemblyjs/ieee754@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614"
- integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==
- dependencies:
- "@xtuc/ieee754" "^1.2.0"
-
-"@webassemblyjs/leb128@1.11.0":
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.0.tgz#f7353de1df38aa201cba9fb88b43f41f75ff403b"
- integrity sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g==
- dependencies:
- "@xtuc/long" "4.2.2"
-
-"@webassemblyjs/leb128@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5"
- integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==
- dependencies:
- "@xtuc/long" "4.2.2"
-
-"@webassemblyjs/utf8@1.11.0":
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.0.tgz#86e48f959cf49e0e5091f069a709b862f5a2cadf"
- integrity sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw==
-
-"@webassemblyjs/utf8@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff"
- integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==
-
-"@webassemblyjs/wasm-edit@1.11.0":
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz#ee4a5c9f677046a210542ae63897094c2027cb78"
- integrity sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ==
- dependencies:
- "@webassemblyjs/ast" "1.11.0"
- "@webassemblyjs/helper-buffer" "1.11.0"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
- "@webassemblyjs/helper-wasm-section" "1.11.0"
- "@webassemblyjs/wasm-gen" "1.11.0"
- "@webassemblyjs/wasm-opt" "1.11.0"
- "@webassemblyjs/wasm-parser" "1.11.0"
- "@webassemblyjs/wast-printer" "1.11.0"
-
-"@webassemblyjs/wasm-edit@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6"
- integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/helper-buffer" "1.11.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
- "@webassemblyjs/helper-wasm-section" "1.11.1"
- "@webassemblyjs/wasm-gen" "1.11.1"
- "@webassemblyjs/wasm-opt" "1.11.1"
- "@webassemblyjs/wasm-parser" "1.11.1"
- "@webassemblyjs/wast-printer" "1.11.1"
-
-"@webassemblyjs/wasm-gen@1.11.0":
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz#3cdb35e70082d42a35166988dda64f24ceb97abe"
- integrity sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ==
- dependencies:
- "@webassemblyjs/ast" "1.11.0"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
- "@webassemblyjs/ieee754" "1.11.0"
- "@webassemblyjs/leb128" "1.11.0"
- "@webassemblyjs/utf8" "1.11.0"
-
-"@webassemblyjs/wasm-gen@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76"
- integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
- "@webassemblyjs/ieee754" "1.11.1"
- "@webassemblyjs/leb128" "1.11.1"
- "@webassemblyjs/utf8" "1.11.1"
-
-"@webassemblyjs/wasm-opt@1.11.0":
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz#1638ae188137f4bb031f568a413cd24d32f92978"
- integrity sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg==
- dependencies:
- "@webassemblyjs/ast" "1.11.0"
- "@webassemblyjs/helper-buffer" "1.11.0"
- "@webassemblyjs/wasm-gen" "1.11.0"
- "@webassemblyjs/wasm-parser" "1.11.0"
-
-"@webassemblyjs/wasm-opt@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2"
- integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/helper-buffer" "1.11.1"
- "@webassemblyjs/wasm-gen" "1.11.1"
- "@webassemblyjs/wasm-parser" "1.11.1"
-
-"@webassemblyjs/wasm-parser@1.11.0":
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz#3e680b8830d5b13d1ec86cc42f38f3d4a7700754"
- integrity sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw==
- dependencies:
- "@webassemblyjs/ast" "1.11.0"
- "@webassemblyjs/helper-api-error" "1.11.0"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
- "@webassemblyjs/ieee754" "1.11.0"
- "@webassemblyjs/leb128" "1.11.0"
- "@webassemblyjs/utf8" "1.11.0"
-
-"@webassemblyjs/wasm-parser@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199"
- integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/helper-api-error" "1.11.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
- "@webassemblyjs/ieee754" "1.11.1"
- "@webassemblyjs/leb128" "1.11.1"
- "@webassemblyjs/utf8" "1.11.1"
-
-"@webassemblyjs/wast-printer@1.11.0":
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz#680d1f6a5365d6d401974a8e949e05474e1fab7e"
- integrity sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ==
- dependencies:
- "@webassemblyjs/ast" "1.11.0"
- "@xtuc/long" "4.2.2"
-
-"@webassemblyjs/wast-printer@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0"
- integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@xtuc/long" "4.2.2"
-
-"@webpack-cli/configtest@^1.1.1":
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.1.1.tgz#9f53b1b7946a6efc2a749095a4f450e2932e8356"
- integrity sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==
-
-"@webpack-cli/info@^1.4.1":
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.4.1.tgz#2360ea1710cbbb97ff156a3f0f24556e0fc1ebea"
- integrity sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA==
- dependencies:
- envinfo "^7.7.3"
-
-"@webpack-cli/serve@^1.6.1":
- version "1.6.1"
- resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.6.1.tgz#0de2875ac31b46b6c5bb1ae0a7d7f0ba5678dffe"
- integrity sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==
-
-"@xtuc/ieee754@^1.2.0":
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
- integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==
-
-"@xtuc/long@4.2.2":
- version "4.2.2"
- resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
- integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
+"@vue/shared@3.2.37":
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.37.tgz#8e6adc3f2759af52f0e85863dfb0b711ecc5c702"
+ integrity sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw==
abort-controller@3.0.0:
version "3.0.0"
@@ -994,40 +707,20 @@ abort-controller@3.0.0:
dependencies:
event-target-shim "^5.0.0"
-acorn-import-assertions@^1.7.6:
- version "1.7.6"
- resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.7.6.tgz#580e3ffcae6770eebeec76c3b9723201e9d01f78"
- integrity sha512-FlVvVFA1TX6l3lp8VjDnYYq7R1nyW6x3svAt4nDgrWQ9SBaSh9CnbwgSUTasgfNfOG5HlM1ehugCvM+hjo56LA==
-
-acorn-jsx@^5.3.1:
- version "5.3.1"
- resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
- integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==
+acorn-jsx@^5.3.2:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+ integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
acorn@^7.1.1:
version "7.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
-acorn@^8.0.4:
- version "8.1.0"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.0.tgz#52311fd7037ae119cbb134309e901aa46295b3fe"
- integrity sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==
-
-acorn@^8.4.1:
- version "8.4.1"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c"
- integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==
-
-acorn@^8.5.0:
- version "8.5.0"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2"
- integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==
-
-acorn@^8.7.0:
- version "8.7.0"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
- integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
+acorn@^8.7.1:
+ version "8.7.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
+ integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
aggregate-error@^3.0.0:
version "3.1.0"
@@ -1037,12 +730,7 @@ aggregate-error@^3.0.0:
clean-stack "^2.0.0"
indent-string "^4.0.0"
-ajv-keywords@^3.5.2:
- version "3.5.2"
- resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
- integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
-
-ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5:
+ajv@^6.10.0, ajv@^6.12.4:
version "6.12.5"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da"
integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==
@@ -1069,13 +757,6 @@ ansi-regex@^5.0.0, ansi-regex@^5.0.1:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
-ansi-styles@^3.2.1:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
- integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
- dependencies:
- color-convert "^1.9.0"
-
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
@@ -1097,13 +778,6 @@ arch@^2.2.0:
resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11"
integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==
-argparse@^1.0.7:
- version "1.0.10"
- resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
- integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
- dependencies:
- sprintf-js "~1.0.2"
-
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
@@ -1161,17 +835,10 @@ astral-regex@^2.0.0:
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
-async@^2.6.0:
- version "2.6.3"
- resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
- integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
- dependencies:
- lodash "^4.17.14"
-
async@^3.2.0:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/async/-/async-3.2.1.tgz#d3274ec66d107a47476a4c49136aacdb00665fc8"
- integrity sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg==
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9"
+ integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==
asynckit@^0.4.0:
version "0.4.0"
@@ -1241,16 +908,6 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
-big-integer@^1.6.16:
- version "1.6.48"
- resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
- integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==
-
-big.js@^5.2.2:
- version "5.2.2"
- resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
- integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
-
binary-extensions@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
@@ -1271,7 +928,7 @@ blurhash@1.1.5:
resolved "https://registry.yarnpkg.com/blurhash/-/blurhash-1.1.5.tgz#3034104cd5dce5a3e5caa871ae2f0f1f2d0ab566"
integrity sha512-a+LO3A2DfxTaTztsmkbLYmUzUeApi0LZuKalwbNmqAHR6HhJGMt1qSV/R3wc+w4DL28holjqO3Bg74aUGavGjg==
-boolbase@^1.0.0, boolbase@~1.0.0:
+boolbase@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
@@ -1284,53 +941,47 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
-braces@^3.0.1, braces@~3.0.2:
+brace-expansion@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+ integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+ dependencies:
+ balanced-match "^1.0.0"
+
+braces@^3.0.1, braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
-broadcast-channel@4.10.0:
- version "4.10.0"
- resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-4.10.0.tgz#d19fb902df227df40b1b580351713d30c302d198"
- integrity sha512-hOUh312XyHk6JTVyX9cyXaH1UYs+2gHVtnW16oQAu9FL7ALcXGXc/YoJWqlkV8vUn14URQPMmRi4A9q4UrwVEQ==
+broadcast-channel@4.13.0:
+ version "4.13.0"
+ resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-4.13.0.tgz#21387b2602b9e9ec3b97b03bd8a8d2c198352ff6"
+ integrity sha512-fcDr8QNJ4SOb6jyjUNZatVNmcHtSWfW4PFcs4xIEFZAtorKCIFoEYtjIjaQ4c0jrbr/Bl8NIwOWiLSyspoAnEQ==
dependencies:
"@babel/runtime" "^7.16.0"
detect-node "^2.1.0"
- microseconds "0.2.0"
- nano-time "1.0.0"
- oblivious-set "1.0.0"
+ microtime "3.0.0"
+ oblivious-set "1.1.1"
p-queue "6.6.2"
rimraf "3.0.2"
unload "2.3.1"
+"browser-image-resizer@git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.2":
+ version "2.2.1-misskey.2"
+ resolved "git+https://github.com/misskey-dev/browser-image-resizer#a58834f5fe2af9f9f31ff115121aef3de6f9d416"
+
browser-stdout@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
-browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6:
- version "4.19.1"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3"
- integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==
- dependencies:
- caniuse-lite "^1.0.30001286"
- electron-to-chromium "^1.4.17"
- escalade "^3.1.1"
- node-releases "^2.0.1"
- picocolors "^1.0.0"
-
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
-buffer-from@^1.0.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
- integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
-
buffer@^5.6.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
@@ -1374,21 +1025,6 @@ camelcase@^6.0.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
-caniuse-api@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
- integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==
- dependencies:
- browserslist "^4.0.0"
- caniuse-lite "^1.0.0"
- lodash.memoize "^4.1.2"
- lodash.uniq "^4.5.0"
-
-caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001286:
- version "1.0.30001311"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001311.tgz#682ef3f4e617f1a177ad943de59775ed3032e511"
- integrity sha512-mleTFtFKfykEeW34EyfhGIFjGCqzhh38Y0LhdQ9aWF+HorZTtdgKV/1hEE0NlFkG2ubvisPV6l400tlbPys98A==
-
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
@@ -1402,15 +1038,6 @@ chalk@4.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
-chalk@^2.0.0, chalk@^2.4.1:
- version "2.4.2"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
- integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
- dependencies:
- ansi-styles "^3.2.1"
- escape-string-regexp "^1.0.5"
- supports-color "^5.3.0"
-
chalk@^4.0.0, chalk@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
@@ -1431,20 +1058,20 @@ character-parser@^2.2.0:
dependencies:
is-regex "^1.0.3"
-chart.js@3.7.1:
- version "3.7.1"
- resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.7.1.tgz#0516f690c6a8680c6c707e31a4c1807a6f400ada"
- integrity sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==
+chart.js@3.8.0:
+ version "3.8.0"
+ resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.8.0.tgz#c6c14c457b9dc3ce7f1514a59e9b262afd6f1a94"
+ integrity sha512-cr8xhrXjLIXVLOBZPkBZVF6NDeiVIrPLHcMhnON7UufudL+CNeRrD+wpYanswlm8NpudMdrt3CHoLMQMxJhHRg==
chartjs-adapter-date-fns@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-2.0.0.tgz#5e53b2f660b993698f936f509c86dddf9ed44c6b"
integrity sha512-rmZINGLe+9IiiEB0kb57vH3UugAtYw33anRiw5kS2Tu87agpetDDoouquycWc9pRsKtQo5j+vLsYHyr8etAvFw==
-chartjs-plugin-gradient@0.2.2:
- version "0.2.2"
- resolved "https://registry.yarnpkg.com/chartjs-plugin-gradient/-/chartjs-plugin-gradient-0.2.2.tgz#e271d8cbaa9cb52581addc99f2facc4adae40e43"
- integrity sha512-fb38h1Zl5DDkHvpempZ/rY/lWg9/dgF0I56GI1dbqRj5P/ZoHSX4hx+P+5Az/JMNZ1s1/2zo5TmTTHQo8xJUXQ==
+chartjs-plugin-gradient@0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/chartjs-plugin-gradient/-/chartjs-plugin-gradient-0.5.0.tgz#907b15102ce164fc32640d43f9c3bad2f5ae3fd5"
+ integrity sha512-VHys58pMPNYRXngCrN5kvQZb1EiAvl/BhU3G9wNXxf2hETWiPYgN63Ud6RK1hyST+nZdZ61x4us546djZX2rYQ==
chartjs-plugin-zoom@1.2.1:
version "1.2.1"
@@ -1458,7 +1085,7 @@ check-more-types@2.24.0, check-more-types@^2.24.0:
resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600"
integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=
-chokidar@3.5.3, "chokidar@>=3.0.0 <4.0.0", chokidar@^3.3.1, chokidar@^3.5.2:
+chokidar@3.5.3, "chokidar@>=3.0.0 <4.0.0", chokidar@^3.3.1, chokidar@^3.5.3:
version "3.3.1"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.1.tgz#c84e5b3d18d9a4d77558fef466b1bf16bbeb3450"
integrity sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==
@@ -1473,13 +1100,6 @@ chokidar@3.5.3, "chokidar@>=3.0.0 <4.0.0", chokidar@^3.3.1, chokidar@^3.5.2:
optionalDependencies:
fsevents "~2.1.2"
-chrome-trace-event@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4"
- integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==
- dependencies:
- tslib "^1.9.0"
-
ci-info@^3.1.1:
version "3.2.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6"
@@ -1532,31 +1152,6 @@ cliui@^7.0.2:
strip-ansi "^6.0.0"
wrap-ansi "^7.0.0"
-clone-deep@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
- integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
- dependencies:
- is-plain-object "^2.0.4"
- kind-of "^6.0.2"
- shallow-clone "^3.0.0"
-
-coa@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3"
- integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==
- dependencies:
- "@types/q" "^1.5.1"
- chalk "^2.4.1"
- q "^1.1.2"
-
-color-convert@^1.9.0:
- version "1.9.3"
- resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
- integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
- dependencies:
- color-name "1.1.3"
-
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
@@ -1564,31 +1159,16 @@ color-convert@^2.0.1:
dependencies:
color-name "~1.1.4"
-color-name@1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
- integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
-
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
-colord@^2.9.1:
- version "2.9.1"
- resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.1.tgz#c961ea0efeb57c9f0f4834458f26cb9cc4a3f90e"
- integrity sha512-4LBMSt09vR0uLnPVkOUBnmxgoaeN4ewRbx801wY/bXcltXfpR/G46OdWn96XpYmCWuYvO46aBZP4NgX8HpNAcw==
-
colorette@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
-colorette@^2.0.14:
- version "2.0.15"
- resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.15.tgz#8e634aa0429b110d24be82eac4d42f5ea65ab2d5"
- integrity sha512-lIFQhufWaVvwi4wOlX9Gx5b0Nmw3XAZ8HzHNH9dfxhe+JaKNTmX6QLk4o7UHyI+tUY8ClvyfaHUm5bf61O3psA==
-
colors@1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
@@ -1601,31 +1181,21 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
dependencies:
delayed-stream "~1.0.0"
-commander@^2.20.0:
- version "2.20.3"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
- integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
-
commander@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==
-commander@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-7.0.0.tgz#3e2bbfd8bb6724760980988fb5b22b7ee6b71ab2"
- integrity sha512-ovx/7NkTrnPuIV8sqk/GjUIIM1+iUQeqA3ye2VNpq9sVoiZsooObWlQy+OPWGI17GDaEoybuAGJm6U8yC077BA==
-
-commander@^7.2.0:
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
- integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
-
-commander@^8.0.0, commander@^8.3.0:
+commander@^8.0.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
+commander@^9.0.0:
+ version "9.2.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-9.2.0.tgz#6e21014b2ed90d8b7c9647230d8b7a94a4a419a9"
+ integrity sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==
+
common-tags@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
@@ -1661,16 +1231,13 @@ core-util-is@1.0.2:
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
-cosmiconfig@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3"
- integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==
+cropperjs@2.0.0-beta:
+ version "2.0.0-beta"
+ resolved "https://registry.yarnpkg.com/cropperjs/-/cropperjs-2.0.0-beta.tgz#bf3f9c19c426657d63c1e6dd55f635546ccec0a5"
+ integrity sha512-mwupI1Ct84PUynnC9S7KenCtgXiuRYAfLwzxPlJwc392iNX8fZUPP6a8gEpmRQTgvsE9Ubme1tXLM6/HLXksiQ==
dependencies:
- "@types/parse-json" "^4.0.0"
- import-fresh "^3.2.1"
- parse-json "^5.0.0"
- path-type "^4.0.0"
- yaml "^1.10.0"
+ "@cropper/elements" "^2.0.0-beta"
+ "@cropper/utils" "^2.0.0-beta"
cross-env@7.0.3:
version "7.0.3"
@@ -1688,162 +1255,20 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
-css-declaration-sorter@^6.2.2:
- version "6.2.2"
- resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.2.2.tgz#bfd2f6f50002d6a3ae779a87d3a0c5d5b10e0f02"
- integrity sha512-Ufadglr88ZLsrvS11gjeu/40Lw74D9Am/Jpr3LlYm5Q4ZP5KdlUhG+6u2EjyXeZcxmZ2h1ebCKngDjolpeLHpg==
-
-css-loader@6.7.1:
- version "6.7.1"
- resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.1.tgz#e98106f154f6e1baf3fc3bc455cb9981c1d5fd2e"
- integrity sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==
- dependencies:
- icss-utils "^5.1.0"
- postcss "^8.4.7"
- postcss-modules-extract-imports "^3.0.0"
- postcss-modules-local-by-default "^4.0.0"
- postcss-modules-scope "^3.0.0"
- postcss-modules-values "^4.0.0"
- postcss-value-parser "^4.2.0"
- semver "^7.3.5"
-
-css-select-base-adapter@^0.1.1:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7"
- integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==
-
-css-select@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef"
- integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==
- dependencies:
- boolbase "^1.0.0"
- css-what "^3.2.1"
- domutils "^1.7.0"
- nth-check "^1.0.2"
-
-css-select@^4.1.3:
- version "4.1.3"
- resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067"
- integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==
- dependencies:
- boolbase "^1.0.0"
- css-what "^5.0.0"
- domhandler "^4.2.0"
- domutils "^2.6.0"
- nth-check "^2.0.0"
-
-css-tree@1.0.0-alpha.37:
- version "1.0.0-alpha.37"
- resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22"
- integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==
- dependencies:
- mdn-data "2.0.4"
- source-map "^0.6.1"
-
-css-tree@1.0.0-alpha.39:
- version "1.0.0-alpha.39"
- resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb"
- integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==
- dependencies:
- mdn-data "2.0.6"
- source-map "^0.6.1"
-
-css-tree@^1.1.2, css-tree@^1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
- integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
- dependencies:
- mdn-data "2.0.14"
- source-map "^0.6.1"
-
-css-what@^3.2.1:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.2.1.tgz#f4a8f12421064621b456755e34a03a2c22df5da1"
- integrity sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==
-
-css-what@^5.0.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe"
- integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==
-
cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
-cssnano-preset-default@^5.2.7:
- version "5.2.7"
- resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.7.tgz#791e3603fb8f1b46717ac53b47e3c418e950f5f3"
- integrity sha512-JiKP38ymZQK+zVKevphPzNSGHSlTI+AOwlasoSRtSVMUU285O7/6uZyd5NbW92ZHp41m0sSHe6JoZosakj63uA==
- dependencies:
- css-declaration-sorter "^6.2.2"
- cssnano-utils "^3.1.0"
- postcss-calc "^8.2.3"
- postcss-colormin "^5.3.0"
- postcss-convert-values "^5.1.0"
- postcss-discard-comments "^5.1.1"
- postcss-discard-duplicates "^5.1.0"
- postcss-discard-empty "^5.1.1"
- postcss-discard-overridden "^5.1.0"
- postcss-merge-longhand "^5.1.4"
- postcss-merge-rules "^5.1.1"
- postcss-minify-font-values "^5.1.0"
- postcss-minify-gradients "^5.1.1"
- postcss-minify-params "^5.1.2"
- postcss-minify-selectors "^5.2.0"
- postcss-normalize-charset "^5.1.0"
- postcss-normalize-display-values "^5.1.0"
- postcss-normalize-positions "^5.1.0"
- postcss-normalize-repeat-style "^5.1.0"
- postcss-normalize-string "^5.1.0"
- postcss-normalize-timing-functions "^5.1.0"
- postcss-normalize-unicode "^5.1.0"
- postcss-normalize-url "^5.1.0"
- postcss-normalize-whitespace "^5.1.1"
- postcss-ordered-values "^5.1.1"
- postcss-reduce-initial "^5.1.0"
- postcss-reduce-transforms "^5.1.0"
- postcss-svgo "^5.1.0"
- postcss-unique-selectors "^5.1.1"
-
-cssnano-utils@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz#95684d08c91511edfc70d2636338ca37ef3a6861"
- integrity sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==
-
-cssnano@5.1.7:
- version "5.1.7"
- resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.7.tgz#99858bef6c76c9240f0cdc9239570bc7db8368be"
- integrity sha512-pVsUV6LcTXif7lvKKW9ZrmX+rGRzxkEdJuVJcp5ftUjWITgwam5LMZOgaTvUrWPkcORBey6he7JKb4XAJvrpKg==
- dependencies:
- cssnano-preset-default "^5.2.7"
- lilconfig "^2.0.3"
- yaml "^1.10.2"
-
-csso@^4.0.2:
- version "4.0.3"
- resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.3.tgz#0d9985dc852c7cc2b2cacfbbe1079014d1a8e903"
- integrity sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==
- dependencies:
- css-tree "1.0.0-alpha.39"
-
-csso@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529"
- integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==
- dependencies:
- css-tree "^1.1.2"
-
csstype@^2.6.8:
version "2.6.13"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.13.tgz#a6893015b90e84dd6e85d0e3b442a1e84f2dbe0f"
integrity sha512-ul26pfSQTZW8dcOnD2iiJssfXw0gdNVX9IJDH/X3K5DGPfj+fUYe3kB+swUY6BF3oZDxaID3AJt+9/ojSAE05A==
-cypress@9.5.3:
- version "9.5.3"
- resolved "https://registry.yarnpkg.com/cypress/-/cypress-9.5.3.tgz#7c56b50fc1f1aa69ef10b271d895aeb4a1d7999e"
- integrity sha512-ItelIVmqMTnKYbo1JrErhsGgQGjWOxCpHT1TfMvwnIXKXN/OSlPjEK7rbCLYDZhejQL99PmUqul7XORI24Ik0A==
+cypress@10.0.3:
+ version "10.0.3"
+ resolved "https://registry.yarnpkg.com/cypress/-/cypress-10.0.3.tgz#889b4bef863b7d1ef1b608b85b964394ad350c5f"
+ integrity sha512-8C82XTybsEmJC9POYSNITGUhMLCRwB9LadP0x33H+52QVoBjhsWvIzrI+ybCe0+TyxaF0D5/9IL2kSTgjqCB9A==
dependencies:
"@cypress/request" "^2.88.10"
"@cypress/xvfb" "^1.2.4"
@@ -1920,10 +1345,10 @@ debug@4.3.2, debug@^4.3.2:
dependencies:
ms "2.1.2"
-debug@4.3.3:
- version "4.3.3"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
- integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
+debug@4.3.4, debug@^4.3.4:
+ version "4.3.4"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+ integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
@@ -1963,7 +1388,7 @@ deep-is@^0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
-define-properties@^1.1.2, define-properties@^1.1.3:
+define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
@@ -1975,11 +1400,6 @@ delayed-stream@~1.0.0:
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
-detect-file@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
- integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=
-
detect-node@2.1.0, detect-node@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
@@ -2021,69 +1441,6 @@ doctypes@^1.1.0:
resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9"
integrity sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=
-dom-serializer@0:
- version "0.2.2"
- resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
- integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
- dependencies:
- domelementtype "^2.0.1"
- entities "^2.0.0"
-
-dom-serializer@^1.0.1:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.1.tgz#d845a1565d7c041a95e5dab62184ab41e3a519be"
- integrity sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q==
- dependencies:
- domelementtype "^2.0.1"
- domhandler "^4.0.0"
- entities "^2.0.0"
-
-domelementtype@1:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
- integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
-
-domelementtype@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d"
- integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==
-
-domelementtype@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
- integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
-
-domhandler@^4.0.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.1.0.tgz#c1d8d494d5ec6db22de99e46a149c2a4d23ddd43"
- integrity sha512-/6/kmsGlMY4Tup/nGVutdrK9yQi4YjWVcVeoQmixpzjOUK1U7pQkvAPHBJeUxOgxF0J8f8lwCJSlCfD0V4CMGQ==
- dependencies:
- domelementtype "^2.2.0"
-
-domhandler@^4.2.0:
- version "4.2.2"
- resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f"
- integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==
- dependencies:
- domelementtype "^2.2.0"
-
-domutils@^1.7.0:
- version "1.7.0"
- resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
- integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
- dependencies:
- dom-serializer "0"
- domelementtype "1"
-
-domutils@^2.6.0:
- version "2.8.0"
- resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
- integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
- dependencies:
- dom-serializer "^1.0.1"
- domelementtype "^2.2.0"
- domhandler "^4.2.0"
-
duplexer@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
@@ -2097,21 +1454,11 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
-electron-to-chromium@^1.4.17:
- version "1.4.68"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.68.tgz#d79447b6bd1bec9183f166bb33d4bef0d5e4e568"
- integrity sha512-cId+QwWrV8R1UawO6b9BR1hnkJ4EJPCPAr4h315vliHUtVUJDk39Sg1PMNnaWKfj5x+93ssjeJ9LKL6r8LaMiA==
-
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
-emojis-list@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
- integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
-
encode-utf8@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda"
@@ -2124,30 +1471,6 @@ end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
-enhanced-resolve@^5.0.0:
- version "5.8.0"
- resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.0.tgz#d9deae58f9d3773b6a111a5a46831da5be5c9ac0"
- integrity sha512-Sl3KRpJA8OpprrtaIswVki3cWPiPKxXuFxJXBp+zNb6s6VwNWwFRUdtmzd2ReUut8n+sCPx7QCtQ7w5wfJhSgQ==
- dependencies:
- graceful-fs "^4.2.4"
- tapable "^2.2.0"
-
-enhanced-resolve@^5.7.0:
- version "5.7.0"
- resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz#525c5d856680fbd5052de453ac83e32049958b5c"
- integrity sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw==
- dependencies:
- graceful-fs "^4.2.4"
- tapable "^2.2.0"
-
-enhanced-resolve@^5.9.2:
- version "5.9.2"
- resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz#0224dcd6a43389ebfb2d55efee517e5466772dd9"
- integrity sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==
- dependencies:
- graceful-fs "^4.2.4"
- tapable "^2.2.0"
-
enquirer@^2.3.6:
version "2.3.6"
resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
@@ -2155,40 +1478,6 @@ enquirer@^2.3.6:
dependencies:
ansi-colors "^4.1.1"
-entities@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
- integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
-
-envinfo@^7.7.3:
- version "7.7.3"
- resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.7.3.tgz#4b2d8622e3e7366afb8091b23ed95569ea0208cc"
- integrity sha512-46+j5QxbPWza0PB1i15nZx0xQ4I/EfQxg9J8Had3b408SV63nEtor2e+oiY63amTo9KTuh2a3XLObNwduxYwwA==
-
-error-ex@^1.3.1:
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
- integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
- dependencies:
- is-arrayish "^0.2.1"
-
-es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5:
- version "1.17.5"
- resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9"
- integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==
- dependencies:
- es-to-primitive "^1.2.1"
- function-bind "^1.1.1"
- has "^1.0.3"
- has-symbols "^1.0.1"
- is-callable "^1.1.5"
- is-regex "^1.0.5"
- object-inspect "^1.7.0"
- object-keys "^1.1.1"
- object.assign "^4.1.0"
- string.prototype.trimleft "^2.1.1"
- string.prototype.trimright "^2.1.1"
-
es-abstract@^1.19.0, es-abstract@^1.19.1:
version "1.19.1"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3"
@@ -2215,16 +1504,6 @@ es-abstract@^1.19.0, es-abstract@^1.19.1:
string.prototype.trimstart "^1.0.4"
unbox-primitive "^1.0.1"
-es-module-lexer@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.4.0.tgz#21f4181cc8b7eee06855f1c59e6087c7bc4f77b0"
- integrity sha512-iuEGihqqhKWFgh72Q/Jtch7V2t/ft8w8IPP2aEN8ArYKO+IWyo6hsi96hCdgyeEDQIV3InhYQ9BlwUFPGXrbEQ==
-
-es-module-lexer@^0.9.0:
- version "0.9.0"
- resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.0.tgz#fe4c4621977bc668e285c5f1f70ca3b451095fda"
- integrity sha512-qU2eN/XHsrl3E4y7mK1wdWnyy5c8gXtCbfP6Xcsemm7fPUR1PIV1JhZfP7ojcN0Fzp69CfrS3u76h2tusvfKiQ==
-
es-to-primitive@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
@@ -2260,6 +1539,132 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.3:
d "^1.0.1"
ext "^1.1.2"
+esbuild-android-64@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.38.tgz#5b94a1306df31d55055f64a62ff6b763a47b7f64"
+ integrity sha512-aRFxR3scRKkbmNuGAK+Gee3+yFxkTJO/cx83Dkyzo4CnQl/2zVSurtG6+G86EQIZ+w+VYngVyK7P3HyTBKu3nw==
+
+esbuild-android-arm64@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.38.tgz#78acc80773d16007de5219ccce544c036abd50b8"
+ integrity sha512-L2NgQRWuHFI89IIZIlpAcINy9FvBk6xFVZ7xGdOwIm8VyhX1vNCEqUJO3DPSSy945Gzdg98cxtNt8Grv1CsyhA==
+
+esbuild-darwin-64@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.38.tgz#e02b1291f629ebdc2aa46fabfacc9aa28ff6aa46"
+ integrity sha512-5JJvgXkX87Pd1Og0u/NJuO7TSqAikAcQQ74gyJ87bqWRVeouky84ICoV4sN6VV53aTW+NE87qLdGY4QA2S7KNA==
+
+esbuild-darwin-arm64@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.38.tgz#01eb6650ec010b18c990e443a6abcca1d71290a9"
+ integrity sha512-eqF+OejMI3mC5Dlo9Kdq/Ilbki9sQBw3QlHW3wjLmsLh+quNfHmGMp3Ly1eWm981iGBMdbtSS9+LRvR2T8B3eQ==
+
+esbuild-freebsd-64@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.38.tgz#790b8786729d4aac7be17648f9ea8e0e16475b5e"
+ integrity sha512-epnPbhZUt93xV5cgeY36ZxPXDsQeO55DppzsIgWM8vgiG/Rz+qYDLmh5ts3e+Ln1wA9dQ+nZmVHw+RjaW3I5Ig==
+
+esbuild-freebsd-arm64@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.38.tgz#b66340ab28c09c1098e6d9d8ff656db47d7211e6"
+ integrity sha512-/9icXUYJWherhk+y5fjPI5yNUdFPtXHQlwP7/K/zg8t8lQdHVj20SqU9/udQmeUo5pDFHMYzcEFfJqgOVeKNNQ==
+
+esbuild-linux-32@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.38.tgz#7927f950986fd39f0ff319e92839455912b67f70"
+ integrity sha512-QfgfeNHRFvr2XeHFzP8kOZVnal3QvST3A0cgq32ZrHjSMFTdgXhMhmWdKzRXP/PKcfv3e2OW9tT9PpcjNvaq6g==
+
+esbuild-linux-64@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.38.tgz#4893d07b229d9cfe34a2b3ce586399e73c3ac519"
+ integrity sha512-uuZHNmqcs+Bj1qiW9k/HZU3FtIHmYiuxZ/6Aa+/KHb/pFKr7R3aVqvxlAudYI9Fw3St0VCPfv7QBpUITSmBR1Q==
+
+esbuild-linux-arm64@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.38.tgz#8442402e37d0b8ae946ac616784d9c1a2041056a"
+ integrity sha512-HlMGZTEsBrXrivr64eZ/EO0NQM8H8DuSENRok9d+Jtvq8hOLzrxfsAT9U94K3KOGk2XgCmkaI2KD8hX7F97lvA==
+
+esbuild-linux-arm@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.38.tgz#d5dbf32d38b7f79be0ec6b5fb2f9251fd9066986"
+ integrity sha512-FiFvQe8J3VKTDXG01JbvoVRXQ0x6UZwyrU4IaLBZeq39Bsbatd94Fuc3F1RGqPF5RbIWW7RvkVQjn79ejzysnA==
+
+esbuild-linux-mips64le@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.38.tgz#95081e42f698bbe35d8ccee0e3a237594b337eb5"
+ integrity sha512-qd1dLf2v7QBiI5wwfil9j0HG/5YMFBAmMVmdeokbNAMbcg49p25t6IlJFXAeLzogv1AvgaXRXvgFNhScYEUXGQ==
+
+esbuild-linux-ppc64le@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.38.tgz#dceb0a1b186f5df679618882a7990bd422089b47"
+ integrity sha512-mnbEm7o69gTl60jSuK+nn+pRsRHGtDPfzhrqEUXyCl7CTOCLtWN2bhK8bgsdp6J/2NyS/wHBjs1x8aBWwP2X9Q==
+
+esbuild-linux-riscv64@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.38.tgz#61fb8edb75f475f9208c4a93ab2bfab63821afd2"
+ integrity sha512-+p6YKYbuV72uikChRk14FSyNJZ4WfYkffj6Af0/Tw63/6TJX6TnIKE+6D3xtEc7DeDth1fjUOEqm+ApKFXbbVQ==
+
+esbuild-linux-s390x@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.38.tgz#34c7126a4937406bf6a5e69100185fd702d12fe0"
+ integrity sha512-0zUsiDkGJiMHxBQ7JDU8jbaanUY975CdOW1YDrurjrM0vWHfjv9tLQsW9GSyEb/heSK1L5gaweRjzfUVBFoybQ==
+
+esbuild-netbsd-64@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.38.tgz#322ea9937d9e529183ee281c7996b93eb38a5d95"
+ integrity sha512-cljBAApVwkpnJZfnRVThpRBGzCi+a+V9Ofb1fVkKhtrPLDYlHLrSYGtmnoTVWDQdU516qYI8+wOgcGZ4XIZh0Q==
+
+esbuild-openbsd-64@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.38.tgz#1ca29bb7a2bf09592dcc26afdb45108f08a2cdbd"
+ integrity sha512-CDswYr2PWPGEPpLDUO50mL3WO/07EMjnZDNKpmaxUPsrW+kVM3LoAqr/CE8UbzugpEiflYqJsGPLirThRB18IQ==
+
+esbuild-sunos-64@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.38.tgz#c9446f7d8ebf45093e7bb0e7045506a88540019b"
+ integrity sha512-2mfIoYW58gKcC3bck0j7lD3RZkqYA7MmujFYmSn9l6TiIcAMpuEvqksO+ntBgbLep/eyjpgdplF7b+4T9VJGOA==
+
+esbuild-windows-32@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.38.tgz#f8e9b4602fd0ccbd48e5c8d117ec0ba4040f2ad1"
+ integrity sha512-L2BmEeFZATAvU+FJzJiRLFUP+d9RHN+QXpgaOrs2klshoAm1AE6Us4X6fS9k33Uy5SzScn2TpcgecbqJza1Hjw==
+
+esbuild-windows-64@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.38.tgz#280f58e69f78535f470905ce3e43db1746518107"
+ integrity sha512-Khy4wVmebnzue8aeSXLC+6clo/hRYeNIm0DyikoEqX+3w3rcvrhzpoix0S+MF9vzh6JFskkIGD7Zx47ODJNyCw==
+
+esbuild-windows-arm64@0.14.38:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.38.tgz#d97e9ac0f95a4c236d9173fa9f86c983d6a53f54"
+ integrity sha512-k3FGCNmHBkqdJXuJszdWciAH77PukEyDsdIryEHn9cKLQFxzhT39dSumeTuggaQcXY57UlmLGIkklWZo2qzHpw==
+
+esbuild@^0.14.27:
+ version "0.14.38"
+ resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.38.tgz#99526b778cd9f35532955e26e1709a16cca2fb30"
+ integrity sha512-12fzJ0fsm7gVZX1YQ1InkOE5f9Tl7cgf6JPYXRJtPIoE0zkWAbHdPHVPPaLi9tYAcEBqheGzqLn/3RdTOyBfcA==
+ optionalDependencies:
+ esbuild-android-64 "0.14.38"
+ esbuild-android-arm64 "0.14.38"
+ esbuild-darwin-64 "0.14.38"
+ esbuild-darwin-arm64 "0.14.38"
+ esbuild-freebsd-64 "0.14.38"
+ esbuild-freebsd-arm64 "0.14.38"
+ esbuild-linux-32 "0.14.38"
+ esbuild-linux-64 "0.14.38"
+ esbuild-linux-arm "0.14.38"
+ esbuild-linux-arm64 "0.14.38"
+ esbuild-linux-mips64le "0.14.38"
+ esbuild-linux-ppc64le "0.14.38"
+ esbuild-linux-riscv64 "0.14.38"
+ esbuild-linux-s390x "0.14.38"
+ esbuild-netbsd-64 "0.14.38"
+ esbuild-openbsd-64 "0.14.38"
+ esbuild-sunos-64 "0.14.38"
+ esbuild-windows-32 "0.14.38"
+ esbuild-windows-64 "0.14.38"
+ esbuild-windows-arm64 "0.14.38"
+
escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
@@ -2315,17 +1720,20 @@ eslint-plugin-import@2.26.0:
resolve "^1.22.0"
tsconfig-paths "^3.14.1"
-eslint-plugin-vue@8.6.0:
- version "8.6.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-8.6.0.tgz#fbdf0f13f8d208a4cba752bf54042661a1aec5c3"
- integrity sha512-abXiF2J18n/7ZPy9foSlJyouKf54IqpKlNvNmzhM93N0zs3QUxZG/oBd3tVPOJTKg7SlhBUtPxugpqzNbgGpQQ==
+eslint-plugin-vue@9.1.0:
+ version "9.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.1.0.tgz#b528941325e26a24bc5d5c5030c0a8996c36659c"
+ integrity sha512-EPCeInPicQ/YyfOWJDr1yfEeSNoFCMzUus107lZyYi37xejdOolNzS5MXGXp8+9bkoKZMdv/1AcZzQebME6r+g==
dependencies:
eslint-utils "^3.0.0"
natural-compare "^1.4.0"
+ nth-check "^2.0.1"
+ postcss-selector-parser "^6.0.9"
semver "^7.3.5"
- vue-eslint-parser "^8.0.1"
+ vue-eslint-parser "^9.0.1"
+ xml-name-validator "^4.0.0"
-eslint-scope@5.1.1, eslint-scope@^5.1.1:
+eslint-scope@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
@@ -2333,14 +1741,6 @@ eslint-scope@5.1.1, eslint-scope@^5.1.1:
esrecurse "^4.3.0"
estraverse "^4.1.1"
-eslint-scope@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-6.0.0.tgz#9cf45b13c5ac8f3d4c50f46a5121f61b3e318978"
- integrity sha512-uRDL9MWmQCkaFus8RF5K9/L/2fn+80yoW3jkD53l4shjCh26fCtvJGasxjUqP5OT87SYTxCVA3BwTUzuELx9kA==
- dependencies:
- esrecurse "^4.3.0"
- estraverse "^5.2.0"
-
eslint-scope@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642"
@@ -2361,22 +1761,17 @@ eslint-visitor-keys@^2.0.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
-eslint-visitor-keys@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz#e32e99c6cdc2eb063f204eda5db67bfe58bb4186"
- integrity sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q==
-
eslint-visitor-keys@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
-eslint@8.13.0:
- version "8.13.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.13.0.tgz#6fcea43b6811e655410f5626cfcf328016badcd7"
- integrity sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==
+eslint@8.17.0:
+ version "8.17.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.17.0.tgz#1cfc4b6b6912f77d24b874ca1506b0fe09328c21"
+ integrity sha512-gq0m0BTJfci60Fz4nczYxNAlED+sMcihltndR8t9t1evnU/azx53x3t2UHXC/uRjcbvRw/XctpaNygSTcQD+Iw==
dependencies:
- "@eslint/eslintrc" "^1.2.1"
+ "@eslint/eslintrc" "^1.3.0"
"@humanwhocodes/config-array" "^0.9.2"
ajv "^6.10.0"
chalk "^4.0.0"
@@ -2387,14 +1782,14 @@ eslint@8.13.0:
eslint-scope "^7.1.1"
eslint-utils "^3.0.0"
eslint-visitor-keys "^3.3.0"
- espree "^9.3.1"
+ espree "^9.3.2"
esquery "^1.4.0"
esutils "^2.0.2"
fast-deep-equal "^3.1.3"
file-entry-cache "^6.0.1"
functional-red-black-tree "^1.0.1"
glob-parent "^6.0.1"
- globals "^13.6.0"
+ globals "^13.15.0"
ignore "^5.2.0"
import-fresh "^3.0.0"
imurmurhash "^0.1.4"
@@ -2403,7 +1798,7 @@ eslint@8.13.0:
json-stable-stringify-without-jsonify "^1.0.1"
levn "^0.4.1"
lodash.merge "^4.6.2"
- minimatch "^3.0.4"
+ minimatch "^3.1.2"
natural-compare "^1.4.0"
optionator "^0.9.1"
regexpp "^3.2.0"
@@ -2412,29 +1807,15 @@ eslint@8.13.0:
text-table "^0.2.0"
v8-compile-cache "^2.0.3"
-espree@^9.0.0:
- version "9.0.0"
- resolved "https://registry.yarnpkg.com/espree/-/espree-9.0.0.tgz#e90a2965698228502e771c7a58489b1a9d107090"
- integrity sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==
+espree@^9.3.1, espree@^9.3.2:
+ version "9.3.2"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.2.tgz#f58f77bd334731182801ced3380a8cc859091596"
+ integrity sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==
dependencies:
- acorn "^8.5.0"
- acorn-jsx "^5.3.1"
- eslint-visitor-keys "^3.0.0"
-
-espree@^9.3.1:
- version "9.3.1"
- resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd"
- integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==
- dependencies:
- acorn "^8.7.0"
- acorn-jsx "^5.3.1"
+ acorn "^8.7.1"
+ acorn-jsx "^5.3.2"
eslint-visitor-keys "^3.3.0"
-esprima@^4.0.0:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
- integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
-
esquery@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5"
@@ -2464,6 +1845,11 @@ estraverse@^5.2.0:
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880"
integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==
+estree-walker@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700"
+ integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==
+
estree-walker@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
@@ -2502,11 +1888,6 @@ eventemitter3@4.0.7, eventemitter3@^4.0.4, eventemitter3@^4.0.7:
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
-events@^3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379"
- integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==
-
execa@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a"
@@ -2537,21 +1918,6 @@ execa@5.1.1:
signal-exit "^3.0.3"
strip-final-newline "^2.0.0"
-execa@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376"
- integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==
- dependencies:
- cross-spawn "^7.0.3"
- get-stream "^6.0.0"
- human-signals "^2.1.0"
- is-stream "^2.0.0"
- merge-stream "^2.0.0"
- npm-run-path "^4.0.1"
- onetime "^5.1.2"
- signal-exit "^3.0.3"
- strip-final-newline "^2.0.0"
-
executable@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c"
@@ -2559,13 +1925,6 @@ executable@^4.1.1:
dependencies:
pify "^2.2.0"
-expand-tilde@^2.0.0, expand-tilde@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502"
- integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=
- dependencies:
- homedir-polyfill "^1.0.1"
-
ext@^1.1.2:
version "1.4.0"
resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244"
@@ -2621,6 +1980,17 @@ fast-glob@^3.1.1:
micromatch "^4.0.2"
picomatch "^2.2.1"
+fast-glob@^3.2.9:
+ version "3.2.11"
+ resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
+ integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
+ dependencies:
+ "@nodelib/fs.stat" "^2.0.2"
+ "@nodelib/fs.walk" "^1.2.3"
+ glob-parent "^5.1.2"
+ merge2 "^1.3.0"
+ micromatch "^4.0.4"
+
fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
@@ -2631,11 +2001,6 @@ fast-levenshtein@^2.0.6:
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
-fastest-levenshtein@^1.0.12:
- version "1.0.12"
- resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2"
- integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==
-
fastq@^1.6.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481"
@@ -2678,14 +2043,6 @@ fill-range@^7.0.1:
dependencies:
to-regex-range "^5.0.1"
-find-node-modules@^2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/find-node-modules/-/find-node-modules-2.1.2.tgz#57565a3455baf671b835bc6b2134a9b938b9c53c"
- integrity sha512-x+3P4mbtRPlSiVE1Qco0Z4YLU8WFiFcuWTf3m75OV9Uzcfs2Bg+O9N+r/K0AnmINBW06KpfqKwYJbFlFq4qNug==
- dependencies:
- findup-sync "^4.0.0"
- merge "^2.1.0"
-
find-up@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
@@ -2701,7 +2058,7 @@ find-up@^2.1.0:
dependencies:
locate-path "^2.0.0"
-find-up@^4.0.0, find-up@^4.1.0:
+find-up@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
@@ -2709,16 +2066,6 @@ find-up@^4.0.0, find-up@^4.1.0:
locate-path "^5.0.0"
path-exists "^4.0.0"
-findup-sync@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-4.0.0.tgz#956c9cdde804052b881b428512905c4a5f2cdef0"
- integrity sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==
- dependencies:
- detect-file "^1.0.0"
- is-glob "^4.0.0"
- micromatch "^4.0.2"
- resolve-dir "^1.0.1"
-
flat-cache@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
@@ -2790,6 +2137,11 @@ fsevents@~2.1.2:
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e"
integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==
+fsevents@~2.3.2:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
+ integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
+
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
@@ -2848,7 +2200,7 @@ getpass@^0.1.1:
dependencies:
assert-plus "^1.0.0"
-glob-parent@^5.1.0, glob-parent@~5.1.0:
+glob-parent@^5.1.0, glob-parent@^5.1.2, glob-parent@~5.1.0:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
@@ -2862,11 +2214,6 @@ glob-parent@^6.0.1:
dependencies:
is-glob "^4.0.3"
-glob-to-regexp@^0.4.1:
- version "0.4.1"
- resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
- integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
-
glob@7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
@@ -2898,37 +2245,10 @@ global-dirs@^3.0.0:
dependencies:
ini "2.0.0"
-global-modules@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea"
- integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==
- dependencies:
- global-prefix "^1.0.1"
- is-windows "^1.0.1"
- resolve-dir "^1.0.0"
-
-global-prefix@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe"
- integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=
- dependencies:
- expand-tilde "^2.0.2"
- homedir-polyfill "^1.0.1"
- ini "^1.3.4"
- is-windows "^1.0.1"
- which "^1.2.14"
-
-globals@^13.6.0:
- version "13.7.0"
- resolved "https://registry.yarnpkg.com/globals/-/globals-13.7.0.tgz#aed3bcefd80ad3ec0f0be2cf0c895110c0591795"
- integrity sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA==
- dependencies:
- type-fest "^0.20.2"
-
-globals@^13.9.0:
- version "13.9.0"
- resolved "https://registry.yarnpkg.com/globals/-/globals-13.9.0.tgz#4bf2bf635b334a173fb1daf7c5e6b218ecdc06cb"
- integrity sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==
+globals@^13.15.0:
+ version "13.15.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-13.15.0.tgz#38113218c907d2f7e98658af246cef8b77e90bac"
+ integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==
dependencies:
type-fest "^0.20.2"
@@ -2944,7 +2264,19 @@ globby@^11.0.4:
merge2 "^1.3.0"
slash "^3.0.0"
-graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.4:
+globby@^11.1.0:
+ version "11.1.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
+ integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
+ dependencies:
+ array-union "^2.1.0"
+ dir-glob "^3.0.1"
+ fast-glob "^3.2.9"
+ ignore "^5.2.0"
+ merge2 "^1.4.1"
+ slash "^3.0.0"
+
+graceful-fs@^4.1.6:
version "4.2.4"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
@@ -2954,16 +2286,6 @@ graceful-fs@^4.2.0:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
-graceful-fs@^4.2.9:
- version "4.2.9"
- resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96"
- integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==
-
-growl@1.10.5:
- version "1.10.5"
- resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
- integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
-
hammerjs@^2.0.8:
version "2.0.8"
resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1"
@@ -2974,17 +2296,12 @@ has-bigints@^1.0.1:
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==
-has-flag@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
- integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
-
has-flag@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
-has-symbols@^1.0.0, has-symbols@^1.0.1:
+has-symbols@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
@@ -3008,28 +2325,11 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
-hash-sum@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04"
- integrity sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=
-
-hash-sum@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a"
- integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==
-
he@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
-homedir-polyfill@^1.0.1:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8"
- integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==
- dependencies:
- parse-passwd "^1.0.0"
-
http-signature@~1.3.6:
version "1.3.6"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9"
@@ -3049,11 +2349,6 @@ human-signals@^2.1.0:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
-icss-utils@^5.0.0, icss-utils@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
- integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
-
idb-keyval@6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-6.1.0.tgz#e659cff41188e6097d7fadd69926f6adbbe70041"
@@ -3071,11 +2366,6 @@ ignore@^5.1.4:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
-ignore@^5.1.8:
- version "5.1.9"
- resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.9.tgz#9ec1a5cbe8e1446ec60d4420060d43aa6e7382fb"
- integrity sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==
-
ignore@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
@@ -3094,14 +2384,6 @@ import-fresh@^3.0.0, import-fresh@^3.2.1:
parent-module "^1.0.0"
resolve-from "^4.0.0"
-import-local@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6"
- integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==
- dependencies:
- pkg-dir "^4.2.0"
- resolve-cwd "^3.0.0"
-
imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
@@ -3112,11 +2394,6 @@ indent-string@^4.0.0:
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
-indexes-of@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
- integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
-
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@@ -3135,11 +2412,6 @@ ini@2.0.0:
resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==
-ini@^1.3.4:
- version "1.3.7"
- resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84"
- integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==
-
insert-text-at-cursor@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/insert-text-at-cursor/-/insert-text-at-cursor-0.3.0.tgz#1819607680ec1570618347c4cd475e791faa25da"
@@ -3154,27 +2426,6 @@ internal-slot@^1.0.3:
has "^1.0.3"
side-channel "^1.0.4"
-interpret@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
- integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
-
-ip-address@^7.1.0:
- version "7.1.0"
- resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-7.1.0.tgz#4a9c699e75b51cbeb18b38de8ed216efa1a490c5"
- integrity sha512-V9pWC/VJf2lsXqP7IWJ+pe3P1/HCYGBMZrrnT62niLGjAfCbeiwXMUxaeHvnVlz19O27pvXP4azs+Pj/A0x+SQ==
- dependencies:
- jsbn "1.1.0"
- sprintf-js "1.1.2"
-
-ip-cidr@3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/ip-cidr/-/ip-cidr-3.0.4.tgz#a915c47e00f47ea8d5f8ed662ea6161471c44375"
- integrity sha512-pKNiqmBlTvEkhaLAa3+FOmYSY0/jjADVxxjA3NbujZZTT8mjLI90Q+6mwg6kd0fNm0RuAOkWJ1u1a/ETmlrPNQ==
- dependencies:
- ip-address "^7.1.0"
- jsbn "^1.1.0"
-
ip-regex@^4.0.0, ip-regex@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5"
@@ -3185,11 +2436,6 @@ ipaddr.js@^2.0.1:
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0"
integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==
-is-arrayish@^0.2.1:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
- integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
-
is-bigint@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3"
@@ -3212,7 +2458,7 @@ is-boolean-object@^1.1.0:
call-bind "^1.0.2"
has-tostringtag "^1.0.0"
-is-callable@^1.1.4, is-callable@^1.1.5:
+is-callable@^1.1.4:
version "1.1.5"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab"
integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==
@@ -3229,13 +2475,6 @@ is-ci@^3.0.0:
dependencies:
ci-info "^3.1.1"
-is-core-module@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.0.0.tgz#58531b70aed1db7c0e8d4eb1a0a2d1ddd64bd12d"
- integrity sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw==
- dependencies:
- has "^1.0.3"
-
is-core-module@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a"
@@ -3300,13 +2539,6 @@ is-negative-zero@^2.0.1:
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==
-is-number-like@^1.0.3:
- version "1.0.8"
- resolved "https://registry.yarnpkg.com/is-number-like/-/is-number-like-1.0.8.tgz#2e129620b50891042e44e9bbbb30593e75cfbbe3"
- integrity sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==
- dependencies:
- lodash.isfinite "^3.3.2"
-
is-number-object@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0"
@@ -3329,19 +2561,12 @@ is-plain-obj@^2.1.0:
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
-is-plain-object@^2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
- integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
- dependencies:
- isobject "^3.0.1"
-
is-promise@^2.0.0:
version "2.2.2"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1"
integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==
-is-regex@^1.0.3, is-regex@^1.0.5:
+is-regex@^1.0.3:
version "1.0.5"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae"
integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==
@@ -3404,44 +2629,16 @@ is-weakref@^1.0.1:
dependencies:
call-bind "^1.0.0"
-is-windows@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
- integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
-
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
-isobject@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
- integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
-
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
-jest-worker@^26.6.2:
- version "26.6.2"
- resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed"
- integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==
- dependencies:
- "@types/node" "*"
- merge-stream "^2.0.0"
- supports-color "^7.0.0"
-
-jest-worker@^27.0.2:
- version "27.0.6"
- resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.6.tgz#a5fdb1e14ad34eb228cfe162d9f729cdbfa28aed"
- integrity sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA==
- dependencies:
- "@types/node" "*"
- merge-stream "^2.0.0"
- supports-color "^8.0.0"
-
joi@^17.4.0:
version "17.4.2"
resolved "https://registry.yarnpkg.com/joi/-/joi-17.4.2.tgz#02f4eb5cf88e515e614830239379dcbbe28ce7f7"
@@ -3458,11 +2655,6 @@ js-stringify@^1.0.2:
resolved "https://registry.yarnpkg.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db"
integrity sha1-Fzb939lyTyijaCrcYjCufk6Weds=
-js-tokens@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
- integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
-
js-yaml@4.1.0, js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
@@ -3470,34 +2662,11 @@ js-yaml@4.1.0, js-yaml@^4.1.0:
dependencies:
argparse "^2.0.1"
-js-yaml@^3.13.1:
- version "3.14.1"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
- integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
- dependencies:
- argparse "^1.0.7"
- esprima "^4.0.0"
-
-jsbn@1.1.0, jsbn@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
- integrity sha1-sBMHyym2GKHtJux56RH4A8TaAEA=
-
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
-json-parse-better-errors@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
- integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
-
-json-parse-even-better-errors@^2.3.0:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
- integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
-
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
@@ -3518,16 +2687,7 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
-json5-loader@4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/json5-loader/-/json5-loader-4.0.1.tgz#6d17a1181e8f3c3d9204dca2a4ce4627306c8498"
- integrity sha512-c9viNZlZTz0MTIcf/4qvek5Dz1/PU3DNCB4PwUhlEZIV3qb1bSD6vQQymlV17/Wm6ncra1aCvmIPsuRj+KfEEg==
- dependencies:
- json5 "^2.1.3"
- loader-utils "^2.0.0"
- schema-utils "^3.0.0"
-
-json5@2.2.1:
+json5@2.2.1, json5@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
@@ -3539,13 +2699,6 @@ json5@^1.0.1:
dependencies:
minimist "^1.2.0"
-json5@^2.1.2, json5@^2.1.3:
- version "2.1.3"
- resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43"
- integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==
- dependencies:
- minimist "^1.2.5"
-
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
@@ -3589,28 +2742,13 @@ jstransformer@1.0.0:
is-promise "^2.0.0"
promise "^7.0.1"
-katex@0.15.3:
- version "0.15.3"
- resolved "https://registry.yarnpkg.com/katex/-/katex-0.15.3.tgz#08781a7ed26800b20380d959d1ffcd62bca0ec14"
- integrity sha512-Al6V7RJsmjklT9QItyHWGaQCt+NYTle1bZwB1e9MR/tLoIT1MXaHy9UpfGSB7eaqDgjjqqRxQOaQGrALCrEyBQ==
+katex@0.15.6:
+ version "0.15.6"
+ resolved "https://registry.yarnpkg.com/katex/-/katex-0.15.6.tgz#c4e2f6ced2ac4de1ef6f737fe7c67d3026baa0e5"
+ integrity sha512-UpzJy4yrnqnhXvRPhjEuLA4lcPn6eRngixW7Q3TJErjg3Aw2PuLFBzTkdUb89UtumxjhHTqL3a5GDGETMSwgJA==
dependencies:
commander "^8.0.0"
-kind-of@^6.0.2:
- version "6.0.3"
- resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
- integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
-
-klona@^2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0"
- integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==
-
-klona@^2.0.5:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc"
- integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==
-
lazy-ass@1.6.0, lazy-ass@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513"
@@ -3624,16 +2762,6 @@ levn@^0.4.1:
prelude-ls "^1.2.1"
type-check "~0.4.0"
-lilconfig@^2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.3.tgz#68f3005e921dafbd2a2afb48379986aa6d2579fd"
- integrity sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==
-
-lines-and-columns@^1.1.6:
- version "1.1.6"
- resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
- integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
-
listr2@^3.8.3:
version "3.11.0"
resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.11.0.tgz#9771b02407875aa78e73d6e0ff6541bbec0aaee9"
@@ -3647,29 +2775,6 @@ listr2@^3.8.3:
through "^2.3.8"
wrap-ansi "^7.0.0"
-loader-runner@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384"
- integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==
-
-loader-utils@^1.0.2:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613"
- integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==
- dependencies:
- big.js "^5.2.2"
- emojis-list "^3.0.0"
- json5 "^1.0.1"
-
-loader-utils@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
- integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
- dependencies:
- big.js "^5.2.2"
- emojis-list "^3.0.0"
- json5 "^2.1.2"
-
locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
@@ -3692,16 +2797,6 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
-lodash.isfinite@^3.3.2:
- version "3.3.2"
- resolved "https://registry.yarnpkg.com/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz#fb89b65a9a80281833f0b7478b3a5104f898ebb3"
- integrity sha1-+4m2WpqAKBgz8LdHizpRBPiY67M=
-
-lodash.memoize@^4.1.2:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
- integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
-
lodash.merge@^4.6.2:
version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
@@ -3712,12 +2807,7 @@ lodash.once@^4.1.1:
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
-lodash.uniq@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
- integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
-
-lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.21:
+lodash@^4.17.19, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -3764,44 +2854,24 @@ matter-js@0.18.0:
resolved "https://registry.yarnpkg.com/matter-js/-/matter-js-0.18.0.tgz#083ced04eb6768f7664dc7ca8948a10e46ad3ed6"
integrity sha512-/ZVem4WygUnbmo/iE4oHZpZS97btfBtYy5Iwn1396vUZU7YhgVEN8J4UWwfZwY1ZqoTYlPgjvSw9WXauuXL0mg==
-mdn-data@2.0.14:
- version "2.0.14"
- resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
- integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
-
-mdn-data@2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
- integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
-
-mdn-data@2.0.6:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978"
- integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==
-
merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
-merge2@^1.3.0:
+merge2@^1.3.0, merge2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
-merge@^2.1.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/merge/-/merge-2.1.1.tgz#59ef4bf7e0b3e879186436e8481c06a6c162ca98"
- integrity sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==
-
-mfm-js@0.21.0:
- version "0.21.0"
- resolved "https://registry.yarnpkg.com/mfm-js/-/mfm-js-0.21.0.tgz#954cc6e7071700b0b1872c78a90bada10be7f772"
- integrity sha512-nyQXaipa7rmAw9ER9uYigMvGcdCwhSv93abZBwccnSnPOc1W3S/WW0+sN28g3YSmlHDCA0i2q9aAFc9EgOi5KA==
+mfm-js@0.22.1:
+ version "0.22.1"
+ resolved "https://registry.yarnpkg.com/mfm-js/-/mfm-js-0.22.1.tgz#ad5f0b95cc903ca5a5e414e2edf64ac4648dc8c2"
+ integrity sha512-UV5zvDKlWPpBFeABhyCzuOTJ3RwrNrmVpJ+zz/dFX6D/ntEywljgxkfsLamcy0ZSwUAr0O+WQxGHvAwyxUgsAQ==
dependencies:
- twemoji-parser "13.1.x"
+ twemoji-parser "14.0.x"
-micromatch@^4.0.0, micromatch@^4.0.2:
+micromatch@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259"
integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==
@@ -3809,17 +2879,28 @@ micromatch@^4.0.0, micromatch@^4.0.2:
braces "^3.0.1"
picomatch "^2.0.5"
-microseconds@0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39"
- integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==
+micromatch@^4.0.4:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+ integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+ dependencies:
+ braces "^3.0.2"
+ picomatch "^2.3.1"
+
+microtime@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/microtime/-/microtime-3.0.0.tgz#d140914bde88aa89b4f9fd2a18620b435af0f39b"
+ integrity sha512-SirJr7ZL4ow2iWcb54bekS4aWyBQNVcEDBiwAz9D/sTgY59A+uE8UJU15cp5wyZmPBwg/3zf8lyCJ5NUe1nVlQ==
+ dependencies:
+ node-addon-api "^1.2.0"
+ node-gyp-build "^3.8.0"
mime-db@1.44.0:
version "1.44.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
-mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19:
+mime-types@^2.1.12, mime-types@~2.1.19:
version "2.1.27"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
@@ -3831,12 +2912,12 @@ mimic-fn@^2.1.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
-minimatch@4.2.1:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4"
- integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==
+minimatch@5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b"
+ integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==
dependencies:
- brace-expansion "^1.1.7"
+ brace-expansion "^2.0.1"
minimatch@^3.0.4:
version "3.0.4"
@@ -3866,39 +2947,30 @@ misskey-js@0.0.14:
eventemitter3 "^4.0.7"
reconnecting-websocket "^4.4.0"
-mkdirp@~0.5.1:
- version "0.5.5"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
- integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
- dependencies:
- minimist "^1.2.5"
-
-mocha@9.2.2:
- version "9.2.2"
- resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9"
- integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==
+mocha@10.0.0:
+ version "10.0.0"
+ resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.0.0.tgz#205447d8993ec755335c4b13deba3d3a13c4def9"
+ integrity sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==
dependencies:
"@ungap/promise-all-settled" "1.1.2"
ansi-colors "4.1.1"
browser-stdout "1.3.1"
chokidar "3.5.3"
- debug "4.3.3"
+ debug "4.3.4"
diff "5.0.0"
escape-string-regexp "4.0.0"
find-up "5.0.0"
glob "7.2.0"
- growl "1.10.5"
he "1.2.0"
js-yaml "4.1.0"
log-symbols "4.1.0"
- minimatch "4.2.1"
+ minimatch "5.0.1"
ms "2.1.3"
- nanoid "3.3.1"
+ nanoid "3.3.3"
serialize-javascript "6.0.0"
strip-json-comments "3.1.1"
supports-color "8.1.1"
- which "2.0.2"
- workerpool "6.2.0"
+ workerpool "6.2.1"
yargs "16.2.0"
yargs-parser "20.2.4"
yargs-unparser "2.0.0"
@@ -3918,33 +2990,21 @@ ms@2.1.3, ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
-mylas@^2.1.6:
- version "2.1.6"
- resolved "https://registry.yarnpkg.com/mylas/-/mylas-2.1.6.tgz#40f3ac6faf77b966c2c2f7b9c0d21ea65b3d9800"
- integrity sha512-5ggCu4hVRJZE6NpQ309y6ArykK5vujK6LfSAXvsrmBNSX/9Gfq7D9zjxhHyjSR/sbFzCe2hI9LO1EY9KXv/XkQ==
+mylas@^2.1.9:
+ version "2.1.9"
+ resolved "https://registry.yarnpkg.com/mylas/-/mylas-2.1.9.tgz#8329626f95c0ce522ca7d3c192eca6221d172cdc"
+ integrity sha512-pa+cQvmhoM8zzgitPYZErmDt9EdTNVnXsH1XFjMeM4TyG4FFcgxrvK1+jwabVFwUOEDaSWuXBMjg43kqt/Ydlg==
-nano-time@1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef"
- integrity sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8=
- dependencies:
- big-integer "^1.6.16"
-
-nanoid@3.3.1, nanoid@^3.1.20, nanoid@^3.3.1:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35"
- integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==
+nanoid@3.3.3, nanoid@^3.3.3:
+ version "3.3.3"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25"
+ integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==
natural-compare@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
-neo-async@^2.6.2:
- version "2.6.2"
- resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
- integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
-
nested-property@4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/nested-property/-/nested-property-4.0.0.tgz#a67b5a31991e701e03cdbaa6453bc5b1011bb88d"
@@ -3960,26 +3020,26 @@ next-tick@~1.0.0:
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
+node-addon-api@^1.2.0:
+ version "1.7.2"
+ resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d"
+ integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==
+
+node-gyp-build@^3.8.0:
+ version "3.9.0"
+ resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.9.0.tgz#53a350187dd4d5276750da21605d1cb681d09e25"
+ integrity sha512-zLcTg6P4AbcHPq465ZMFNXx7XpKKJh+7kkN699NiQWisR2uWYOWNWqRHAmbnmKiL4e9aLSlmy5U7rEMUXV59+A==
+
node-gyp-build@~3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.7.0.tgz#daa77a4f547b9aed3e2aac779eaf151afd60ec8d"
integrity sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==
-node-releases@^2.0.1:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01"
- integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==
-
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
-normalize-url@^6.0.1:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
- integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
-
npm-run-path@^4.0.0, npm-run-path@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
@@ -3987,17 +3047,10 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1:
dependencies:
path-key "^3.0.0"
-nth-check@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
- integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
- dependencies:
- boolbase "~1.0.0"
-
-nth-check@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125"
- integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==
+nth-check@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2"
+ integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==
dependencies:
boolbase "^1.0.0"
@@ -4016,26 +3069,11 @@ object-inspect@^1.11.0, object-inspect@^1.9.0:
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1"
integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==
-object-inspect@^1.7.0:
- version "1.7.0"
- resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67"
- integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==
-
-object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1:
+object-keys@^1.0.12, object-keys@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
-object.assign@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
- integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==
- dependencies:
- define-properties "^1.1.2"
- function-bind "^1.1.1"
- has-symbols "^1.0.0"
- object-keys "^1.0.11"
-
object.assign@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
@@ -4046,24 +3084,6 @@ object.assign@^4.1.2:
has-symbols "^1.0.1"
object-keys "^1.1.1"
-object.getownpropertydescriptors@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649"
- integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==
- dependencies:
- define-properties "^1.1.3"
- es-abstract "^1.17.0-next.1"
-
-object.values@^1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e"
- integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==
- dependencies:
- define-properties "^1.1.3"
- es-abstract "^1.17.0-next.1"
- function-bind "^1.1.1"
- has "^1.0.3"
-
object.values@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac"
@@ -4073,10 +3093,10 @@ object.values@^1.1.5:
define-properties "^1.1.3"
es-abstract "^1.19.1"
-oblivious-set@1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566"
- integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==
+oblivious-set@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.1.1.tgz#d9d38e9491d51f27a5c3ec1681d2ba40aa81e98b"
+ integrity sha512-Oh+8fK09mgGmAshFdH6hSVco6KZmd1tTwNFWj35OvzdmJTMZtAkbn05zar2iG3v6sDs1JLEtOiBGNb6BHwkb2w==
once@^1.3.0, once@^1.3.1, once@^1.4.0:
version "1.4.0"
@@ -4135,13 +3155,6 @@ p-limit@^3.0.2:
dependencies:
p-try "^2.0.0"
-p-limit@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
- integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
- dependencies:
- yocto-queue "^0.1.0"
-
p-locate@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
@@ -4202,26 +3215,6 @@ parent-module@^1.0.0:
dependencies:
callsites "^3.0.0"
-parse-json@^5.0.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646"
- integrity sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==
- dependencies:
- "@babel/code-frame" "^7.0.0"
- error-ex "^1.3.1"
- json-parse-even-better-errors "^2.3.0"
- lines-and-columns "^1.1.6"
-
-parse-passwd@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
- integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=
-
-parse5@6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
- integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
-
path-exists@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
@@ -4269,10 +3262,10 @@ performance-now@^2.1.0:
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
-photoswipe@5.2.4:
- version "5.2.4"
- resolved "https://registry.yarnpkg.com/photoswipe/-/photoswipe-5.2.4.tgz#918fd64c6b41b6a693743e5d70ee1a59747f491d"
- integrity sha512-7p+VH7ELUnW9/3rCULCmyXcUCEuZwcsxvxPQYzR4wk3EaXcLCiINMCspc9Qq9AJYNsqYo1qGVL1y1Tch8uKAjw==
+photoswipe@5.2.7:
+ version "5.2.7"
+ resolved "https://registry.yarnpkg.com/photoswipe/-/photoswipe-5.2.7.tgz#9ff2aaf2a3e03c817ac2835dc6dee0f901e8159d"
+ integrity sha512-AogMba7W/O5gOtDIZ8cQuou1ltwxlaLNoZY1qi1s+kbYXpZk9D6rXxnNGAfDppl+bfe+sKLW2w2sx+3uQ8oPzg==
picocolors@^1.0.0:
version "1.0.0"
@@ -4284,267 +3277,28 @@ picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.0.7, picomatch@^2.2.1:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
+picomatch@^2.2.2, picomatch@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+ integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
pify@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
-pkg-dir@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
- integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
+plimit-lit@^1.2.6:
+ version "1.2.6"
+ resolved "https://registry.yarnpkg.com/plimit-lit/-/plimit-lit-1.2.6.tgz#8c1336f26a042b6e9f1acc665be5eee4c2a55fb3"
+ integrity sha512-EuVnKyDeFgr58aidKf2G7DI41r23bxphlvBKAZ8e8dT9of0Ez2g9w6JbJGUP1YBNC2yG9+ZCCbjLj4yS1P5Gzw==
dependencies:
- find-up "^4.0.0"
+ queue-lit "^1.2.7"
pngjs@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb"
integrity sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==
-portscanner@2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/portscanner/-/portscanner-2.2.0.tgz#6059189b3efa0965c9d96a56b958eb9508411cf1"
- integrity sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==
- dependencies:
- async "^2.6.0"
- is-number-like "^1.0.3"
-
-postcss-calc@^8.2.3:
- version "8.2.4"
- resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.4.tgz#77b9c29bfcbe8a07ff6693dc87050828889739a5"
- integrity sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==
- dependencies:
- postcss-selector-parser "^6.0.9"
- postcss-value-parser "^4.2.0"
-
-postcss-colormin@^5.3.0:
- version "5.3.0"
- resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.3.0.tgz#3cee9e5ca62b2c27e84fce63affc0cfb5901956a"
- integrity sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==
- dependencies:
- browserslist "^4.16.6"
- caniuse-api "^3.0.0"
- colord "^2.9.1"
- postcss-value-parser "^4.2.0"
-
-postcss-convert-values@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.1.0.tgz#f8d3abe40b4ce4b1470702a0706343eac17e7c10"
- integrity sha512-GkyPbZEYJiWtQB0KZ0X6qusqFHUepguBCNFi9t5JJc7I2OTXG7C0twbTLvCfaKOLl3rSXmpAwV7W5txd91V84g==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-discard-comments@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.1.1.tgz#e90019e1a0e5b99de05f63516ce640bd0df3d369"
- integrity sha512-5JscyFmvkUxz/5/+TB3QTTT9Gi9jHkcn8dcmmuN68JQcv3aQg4y88yEHHhwFB52l/NkaJ43O0dbksGMAo49nfQ==
-
-postcss-discard-duplicates@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz#9eb4fe8456706a4eebd6d3b7b777d07bad03e848"
- integrity sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==
-
-postcss-discard-empty@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz#e57762343ff7f503fe53fca553d18d7f0c369c6c"
- integrity sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==
-
-postcss-discard-overridden@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz#7e8c5b53325747e9d90131bb88635282fb4a276e"
- integrity sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==
-
-postcss-loader@6.2.1:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-6.2.1.tgz#0895f7346b1702103d30fdc66e4d494a93c008ef"
- integrity sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==
- dependencies:
- cosmiconfig "^7.0.0"
- klona "^2.0.5"
- semver "^7.3.5"
-
-postcss-merge-longhand@^5.1.4:
- version "5.1.4"
- resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.4.tgz#0f46f8753989a33260efc47de9a0cdc571f2ec5c"
- integrity sha512-hbqRRqYfmXoGpzYKeW0/NCZhvNyQIlQeWVSao5iKWdyx7skLvCfQFGIUsP9NUs3dSbPac2IC4Go85/zG+7MlmA==
- dependencies:
- postcss-value-parser "^4.2.0"
- stylehacks "^5.1.0"
-
-postcss-merge-rules@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.1.tgz#d327b221cd07540bcc8d9ff84446d8b404d00162"
- integrity sha512-8wv8q2cXjEuCcgpIB1Xx1pIy8/rhMPIQqYKNzEdyx37m6gpq83mQQdCxgIkFgliyEnKvdwJf/C61vN4tQDq4Ww==
- dependencies:
- browserslist "^4.16.6"
- caniuse-api "^3.0.0"
- cssnano-utils "^3.1.0"
- postcss-selector-parser "^6.0.5"
-
-postcss-minify-font-values@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz#f1df0014a726083d260d3bd85d7385fb89d1f01b"
- integrity sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-minify-gradients@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz#f1fe1b4f498134a5068240c2f25d46fcd236ba2c"
- integrity sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==
- dependencies:
- colord "^2.9.1"
- cssnano-utils "^3.1.0"
- postcss-value-parser "^4.2.0"
-
-postcss-minify-params@^5.1.2:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.1.2.tgz#77e250780c64198289c954884ebe3ee4481c3b1c"
- integrity sha512-aEP+p71S/urY48HWaRHasyx4WHQJyOYaKpQ6eXl8k0kxg66Wt/30VR6/woh8THgcpRbonJD5IeD+CzNhPi1L8g==
- dependencies:
- browserslist "^4.16.6"
- cssnano-utils "^3.1.0"
- postcss-value-parser "^4.2.0"
-
-postcss-minify-selectors@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.2.0.tgz#17c2be233e12b28ffa8a421a02fc8b839825536c"
- integrity sha512-vYxvHkW+iULstA+ctVNx0VoRAR4THQQRkG77o0oa4/mBS0OzGvvzLIvHDv/nNEM0crzN2WIyFU5X7wZhaUK3RA==
- dependencies:
- postcss-selector-parser "^6.0.5"
-
-postcss-modules-extract-imports@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d"
- integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==
-
-postcss-modules-local-by-default@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c"
- integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==
- dependencies:
- icss-utils "^5.0.0"
- postcss-selector-parser "^6.0.2"
- postcss-value-parser "^4.1.0"
-
-postcss-modules-scope@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06"
- integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==
- dependencies:
- postcss-selector-parser "^6.0.4"
-
-postcss-modules-values@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c"
- integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==
- dependencies:
- icss-utils "^5.0.0"
-
-postcss-normalize-charset@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz#9302de0b29094b52c259e9b2cf8dc0879879f0ed"
- integrity sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==
-
-postcss-normalize-display-values@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz#72abbae58081960e9edd7200fcf21ab8325c3da8"
- integrity sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-normalize-positions@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.0.tgz#902a7cb97cf0b9e8b1b654d4a43d451e48966458"
- integrity sha512-8gmItgA4H5xiUxgN/3TVvXRoJxkAWLW6f/KKhdsH03atg0cB8ilXnrB5PpSshwVu/dD2ZsRFQcR1OEmSBDAgcQ==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-normalize-repeat-style@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.0.tgz#f6d6fd5a54f51a741cc84a37f7459e60ef7a6398"
- integrity sha512-IR3uBjc+7mcWGL6CtniKNQ4Rr5fTxwkaDHwMBDGGs1x9IVRkYIT/M4NelZWkAOBdV6v3Z9S46zqaKGlyzHSchw==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-normalize-string@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz#411961169e07308c82c1f8c55f3e8a337757e228"
- integrity sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-normalize-timing-functions@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz#d5614410f8f0b2388e9f240aa6011ba6f52dafbb"
- integrity sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-normalize-unicode@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz#3d23aede35e160089a285e27bf715de11dc9db75"
- integrity sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==
- dependencies:
- browserslist "^4.16.6"
- postcss-value-parser "^4.2.0"
-
-postcss-normalize-url@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz#ed9d88ca82e21abef99f743457d3729a042adcdc"
- integrity sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==
- dependencies:
- normalize-url "^6.0.1"
- postcss-value-parser "^4.2.0"
-
-postcss-normalize-whitespace@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz#08a1a0d1ffa17a7cc6efe1e6c9da969cc4493cfa"
- integrity sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-ordered-values@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.1.1.tgz#0b41b610ba02906a3341e92cab01ff8ebc598adb"
- integrity sha512-7lxgXF0NaoMIgyihL/2boNAEZKiW0+HkMhdKMTD93CjW8TdCy2hSdj8lsAo+uwm7EDG16Da2Jdmtqpedl0cMfw==
- dependencies:
- cssnano-utils "^3.1.0"
- postcss-value-parser "^4.2.0"
-
-postcss-reduce-initial@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz#fc31659ea6e85c492fb2a7b545370c215822c5d6"
- integrity sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==
- dependencies:
- browserslist "^4.16.6"
- caniuse-api "^3.0.0"
-
-postcss-reduce-transforms@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz#333b70e7758b802f3dd0ddfe98bb1ccfef96b6e9"
- integrity sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4:
- version "6.0.4"
- resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3"
- integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==
- dependencies:
- cssesc "^3.0.0"
- indexes-of "^1.0.1"
- uniq "^1.0.1"
- util-deprecate "^1.0.2"
-
-postcss-selector-parser@^6.0.5:
- version "6.0.6"
- resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea"
- integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==
- dependencies:
- cssesc "^3.0.0"
- util-deprecate "^1.0.2"
-
postcss-selector-parser@^6.0.9:
version "6.0.9"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz#ee71c3b9ff63d9cd130838876c13a2ec1a992b2f"
@@ -4553,55 +3307,12 @@ postcss-selector-parser@^6.0.9:
cssesc "^3.0.0"
util-deprecate "^1.0.2"
-postcss-svgo@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d"
- integrity sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==
- dependencies:
- postcss-value-parser "^4.2.0"
- svgo "^2.7.0"
-
-postcss-unique-selectors@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz#a9f273d1eacd09e9aa6088f4b0507b18b1b541b6"
- integrity sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==
- dependencies:
- postcss-selector-parser "^6.0.5"
-
-postcss-value-parser@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
- integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==
-
-postcss-value-parser@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
- integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
-
-postcss@8.4.12:
- version "8.4.12"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905"
- integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==
+postcss@^8.1.10, postcss@^8.4.13:
+ version "8.4.13"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.13.tgz#7c87bc268e79f7f86524235821dfdf9f73e5d575"
+ integrity sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==
dependencies:
- nanoid "^3.3.1"
- picocolors "^1.0.0"
- source-map-js "^1.0.2"
-
-postcss@^8.1.10:
- version "8.2.8"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.8.tgz#0b90f9382efda424c4f0f69a2ead6f6830d08ece"
- integrity sha512-1F0Xb2T21xET7oQV9eKuctbM9S7BC0fetoHCc4H13z0PT6haiRLP4T0ZY4XWh7iLP0usgqykT6p9B2RtOf4FPw==
- dependencies:
- colorette "^1.2.2"
- nanoid "^3.1.20"
- source-map "^0.6.1"
-
-postcss@^8.4.7:
- version "8.4.8"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.8.tgz#dad963a76e82c081a0657d3a2f3602ce10c2e032"
- integrity sha512-2tXEqGxrjvAO6U+CJzDL2Fk2kPHTv1jQsYkSoMeOis2SsYaXRO2COxTdQp99cYvif9JTXaAk9lYGc3VhJt7JPQ==
- dependencies:
- nanoid "^3.3.1"
+ nanoid "^3.3.3"
picocolors "^1.0.0"
source-map-js "^1.0.2"
@@ -4615,10 +3326,10 @@ pretty-bytes@^5.6.0:
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==
-prismjs@1.27.0:
- version "1.27.0"
- resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.27.0.tgz#bb6ee3138a0b438a3653dd4d6ce0cc6510a45057"
- integrity sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==
+prismjs@1.28.0:
+ version "1.28.0"
+ resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.28.0.tgz#0d8f561fa0f7cf6ebca901747828b149147044b6"
+ integrity sha512-8aaXdYvl1F7iC7Xm1spqSaY/OJBpYW3v+KJ+F17iYxvdc8sfjW194COK5wVhMZX45tGteiBQgdvD/nhxcRwylw==
private-ip@2.3.3:
version "2.3.3"
@@ -4775,11 +3486,6 @@ punycode@2.1.1, punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
-q@^1.1.2:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
- integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
-
qrcode@1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.5.0.tgz#95abb8a91fdafd86f8190f2836abbfc500c72d1b"
@@ -4800,6 +3506,11 @@ querystring@0.2.1:
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd"
integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==
+queue-lit@^1.2.7:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/queue-lit/-/queue-lit-1.2.7.tgz#69081656c9e7b81f09770bb2de6aa007f1a90763"
+ integrity sha512-K/rTdggORRcmf3+c89ijPlgJ/ldGP4oBj6Sm7VcTup4B2clf03Jo8QaXTnMst4EEQwkUbOZFN4frKocq2I85gw==
+
random-seed@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/random-seed/-/random-seed-0.3.0.tgz#d945f2e1f38f49e8d58913431b8bf6bb937556cd"
@@ -4826,13 +3537,6 @@ readdirp@~3.3.0:
dependencies:
picomatch "^2.0.7"
-rechoir@^0.7.0:
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.0.tgz#32650fd52c21ab252aa5d65b19310441c7e03aca"
- integrity sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==
- dependencies:
- resolve "^1.9.0"
-
reconnecting-websocket@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz#3b0e5b96ef119e78a03135865b8bb0af1b948783"
@@ -4870,31 +3574,11 @@ require-main-filename@^2.0.0:
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
-resolve-cwd@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
- integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==
- dependencies:
- resolve-from "^5.0.0"
-
-resolve-dir@^1.0.0, resolve-dir@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43"
- integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=
- dependencies:
- expand-tilde "^2.0.0"
- global-modules "^1.0.0"
-
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
-resolve-from@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
- integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
-
resolve@^1.15.1, resolve@^1.20.0:
version "1.20.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
@@ -4912,14 +3596,6 @@ resolve@^1.22.0:
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
-resolve@^1.9.0:
- version "1.18.1"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.18.1.tgz#018fcb2c5b207d2a6424aee361c5a266da8f4130"
- integrity sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==
- dependencies:
- is-core-module "^2.0.0"
- path-parse "^1.0.6"
-
restore-cursor@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
@@ -4948,6 +3624,20 @@ rndstr@1.0.0:
rangestr "0.0.1"
seedrandom "2.4.2"
+rollup@2.75.6:
+ version "2.75.6"
+ resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.75.6.tgz#ac4dc8600f95942a0180f61c7c9d6200e374b439"
+ integrity sha512-OEf0TgpC9vU6WGROJIk1JA3LR5vk/yvqlzxqdrE2CzzXnqKXNzbAwlWUXis8RS3ZPe7LAq+YUxsRa0l3r27MLA==
+ optionalDependencies:
+ fsevents "~2.3.2"
+
+rollup@^2.59.0:
+ version "2.70.2"
+ resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.70.2.tgz#808d206a8851628a065097b7ba2053bd83ba0c0d"
+ integrity sha512-EitogNZnfku65I1DD5Mxe8JYRUCy0hkK5X84IlDtUs+O6JRMpRciXTzyCUuX11b5L5pvjH+OmFXiQ3XjabcXgg==
+ optionalDependencies:
+ fsevents "~2.3.2"
+
run-parallel@^1.1.9:
version "1.1.9"
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679"
@@ -4992,46 +3682,20 @@ safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
-sass-loader@12.6.0:
- version "12.6.0"
- resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-12.6.0.tgz#5148362c8e2cdd4b950f3c63ac5d16dbfed37bcb"
- integrity sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==
- dependencies:
- klona "^2.0.4"
- neo-async "^2.6.2"
-
-sass@1.50.0:
- version "1.50.0"
- resolved "https://registry.yarnpkg.com/sass/-/sass-1.50.0.tgz#3e407e2ebc53b12f1e35ce45efb226ea6063c7c8"
- integrity sha512-cLsD6MEZ5URXHStxApajEh7gW189kkjn4Rc8DQweMyF+o5HF5nfEz8QYLMlPsTOD88DknatTmBWkOcw5/LnJLQ==
+sass@1.52.3:
+ version "1.52.3"
+ resolved "https://registry.yarnpkg.com/sass/-/sass-1.52.3.tgz#b7cc7ffea2341ccc9a0c4fd372bf1b3f9be1b6cb"
+ integrity sha512-LNNPJ9lafx+j1ArtA7GyEJm9eawXN8KlA1+5dF6IZyoONg1Tyo/g+muOsENWJH/2Q1FHbbV4UwliU0cXMa/VIA==
dependencies:
chokidar ">=3.0.0 <4.0.0"
immutable "^4.0.0"
source-map-js ">=0.6.2 <2.0.0"
-sax@^1.2.4, sax@~1.2.4:
+sax@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
-schema-utils@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef"
- integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==
- dependencies:
- "@types/json-schema" "^7.0.6"
- ajv "^6.12.5"
- ajv-keywords "^3.5.2"
-
-schema-utils@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.0.tgz#95986eb604f66daadeed56e379bfe7a7f963cdb9"
- integrity sha512-tTEaeYkyIhEZ9uWgAjDerWov3T9MgX8dhhy2r0IGeeX4W8ngtGl1++dUve/RUqzuaASSh7shwCDJjEzthxki8w==
- dependencies:
- "@types/json-schema" "^7.0.7"
- ajv "^6.12.5"
- ajv-keywords "^3.5.2"
-
seedrandom@2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-2.4.2.tgz#18d78c41287d13aff8eadb29e235938b248aa9ff"
@@ -5042,7 +3706,7 @@ seedrandom@3.0.5:
resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7"
integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==
-semver@^7.3.2, semver@^7.3.4:
+semver@^7.3.2:
version "7.3.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
@@ -5056,32 +3720,25 @@ semver@^7.3.5:
dependencies:
lru-cache "^6.0.0"
-serialize-javascript@6.0.0, serialize-javascript@^6.0.0:
+semver@^7.3.6, semver@^7.3.7:
+ version "7.3.7"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
+ integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
+ dependencies:
+ lru-cache "^6.0.0"
+
+serialize-javascript@6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
dependencies:
randombytes "^2.1.0"
-serialize-javascript@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4"
- integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==
- dependencies:
- randombytes "^2.1.0"
-
set-blocking@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
-shallow-clone@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
- integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
- dependencies:
- kind-of "^6.0.2"
-
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@@ -5136,39 +3793,16 @@ sortablejs@1.10.2:
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.10.2.tgz#6e40364d913f98b85a14f6678f92b5c1221f5290"
integrity sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==
-source-list-map@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
- integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
-
-"source-map-js@>=0.6.2 <2.0.0":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf"
- integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==
-
-source-map-js@^1.0.2:
+"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
-source-map-support@~0.5.19:
- version "0.5.19"
- resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
- integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
- dependencies:
- buffer-from "^1.0.0"
- source-map "^0.6.0"
-
-source-map@^0.6.0, source-map@^0.6.1:
+source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
-source-map@~0.7.2:
- version "0.7.3"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
- integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
-
sourcemap-codec@^1.4.4:
version "1.4.8"
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
@@ -5181,16 +3815,6 @@ split@0.3:
dependencies:
through "2"
-sprintf-js@1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
- integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
-
-sprintf-js@~1.0.2:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
- integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
-
sshpk@^1.14.1:
version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
@@ -5206,11 +3830,6 @@ sshpk@^1.14.1:
safer-buffer "^2.0.2"
tweetnacl "~0.14.0"
-stable@^0.1.8:
- version "0.1.8"
- resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
- integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
-
start-server-and-test@1.14.0:
version "1.14.0"
resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-1.14.0.tgz#c57f04f73eac15dd51733b551d775b40837fdde3"
@@ -5245,14 +3864,6 @@ string-width@^4.1.0, string-width@^4.2.0:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
-string.prototype.trimend@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913"
- integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==
- dependencies:
- define-properties "^1.1.3"
- es-abstract "^1.17.5"
-
string.prototype.trimend@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80"
@@ -5261,32 +3872,6 @@ string.prototype.trimend@^1.0.4:
call-bind "^1.0.2"
define-properties "^1.1.3"
-string.prototype.trimleft@^2.1.1:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz#4408aa2e5d6ddd0c9a80739b087fbc067c03b3cc"
- integrity sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==
- dependencies:
- define-properties "^1.1.3"
- es-abstract "^1.17.5"
- string.prototype.trimstart "^1.0.0"
-
-string.prototype.trimright@^2.1.1:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz#c76f1cef30f21bbad8afeb8db1511496cfb0f2a3"
- integrity sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==
- dependencies:
- define-properties "^1.1.3"
- es-abstract "^1.17.5"
- string.prototype.trimend "^1.0.0"
-
-string.prototype.trimstart@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54"
- integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==
- dependencies:
- define-properties "^1.1.3"
- es-abstract "^1.17.5"
-
string.prototype.trimstart@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed"
@@ -5331,34 +3916,14 @@ strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
-style-loader@3.3.1:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575"
- integrity sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==
-
-stylehacks@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.0.tgz#a40066490ca0caca04e96c6b02153ddc39913520"
- integrity sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==
- dependencies:
- browserslist "^4.16.6"
- postcss-selector-parser "^6.0.4"
-
-supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.1:
+supports-color@8.1.1, supports-color@^8.1.1:
version "8.1.1"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
dependencies:
has-flag "^4.0.0"
-supports-color@^5.3.0:
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
- integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
- dependencies:
- has-flag "^3.0.0"
-
-supports-color@^7.0.0, supports-color@^7.1.0:
+supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
@@ -5370,90 +3935,11 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
-svgo@^1.3.2:
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167"
- integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==
- dependencies:
- chalk "^2.4.1"
- coa "^2.0.2"
- css-select "^2.0.0"
- css-select-base-adapter "^0.1.1"
- css-tree "1.0.0-alpha.37"
- csso "^4.0.2"
- js-yaml "^3.13.1"
- mkdirp "~0.5.1"
- object.values "^1.1.0"
- sax "~1.2.4"
- stable "^0.1.8"
- unquote "~1.1.1"
- util.promisify "~1.0.0"
-
-svgo@^2.7.0:
- version "2.8.0"
- resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24"
- integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==
- dependencies:
- "@trysound/sax" "0.2.0"
- commander "^7.2.0"
- css-select "^4.1.3"
- css-tree "^1.1.3"
- csso "^4.2.0"
- picocolors "^1.0.0"
- stable "^0.1.8"
-
syuilo-password-strength@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/syuilo-password-strength/-/syuilo-password-strength-0.0.1.tgz#08f71a8f0ecb77db649f3d9a6424510d9d945f52"
integrity sha1-CPcajw7Ld9tknz2aZCRRDZ2UX1I=
-tapable@^2.1.1, tapable@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b"
- integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==
-
-terser-webpack-plugin@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.1.tgz#7effadee06f7ecfa093dbbd3e9ab23f5f3ed8673"
- integrity sha512-5XNNXZiR8YO6X6KhSGXfY0QrGrCRlSwAEjIIrlRQR4W8nP69TaJUlh3bkuac6zzgspiGPfKEHcY295MMVExl5Q==
- dependencies:
- jest-worker "^26.6.2"
- p-limit "^3.1.0"
- schema-utils "^3.0.0"
- serialize-javascript "^5.0.1"
- source-map "^0.6.1"
- terser "^5.5.1"
-
-terser-webpack-plugin@^5.1.3:
- version "5.1.4"
- resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.4.tgz#c369cf8a47aa9922bd0d8a94fe3d3da11a7678a1"
- integrity sha512-C2WkFwstHDhVEmsmlCxrXUtVklS+Ir1A7twrYzrDrQQOIMOaVAYykaoo/Aq1K0QRkMoY2hhvDQY1cm4jnIMFwA==
- dependencies:
- jest-worker "^27.0.2"
- p-limit "^3.1.0"
- schema-utils "^3.0.0"
- serialize-javascript "^6.0.0"
- source-map "^0.6.1"
- terser "^5.7.0"
-
-terser@^5.5.1:
- version "5.5.1"
- resolved "https://registry.yarnpkg.com/terser/-/terser-5.5.1.tgz#540caa25139d6f496fdea056e414284886fb2289"
- integrity sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ==
- dependencies:
- commander "^2.20.0"
- source-map "~0.7.2"
- source-map-support "~0.5.19"
-
-terser@^5.7.0:
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/terser/-/terser-5.7.1.tgz#2dc7a61009b66bb638305cb2a824763b116bf784"
- integrity sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg==
- dependencies:
- commander "^2.20.0"
- source-map "~0.7.2"
- source-map-support "~0.5.19"
-
text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@@ -5464,15 +3950,15 @@ textarea-caret@3.1.0:
resolved "https://registry.yarnpkg.com/textarea-caret/-/textarea-caret-3.1.0.tgz#5d5a35bb035fd06b2ff0e25d5359e97f2655087f"
integrity sha512-cXAvzO9pP5CGa6NKx0WYHl+8CHKZs8byMkt3PCJBCmq2a34YA9pO1NrQET5pzeqnBjBdToF5No4rrmkDUgQC2Q==
-three@0.139.2:
- version "0.139.2"
- resolved "https://registry.yarnpkg.com/three/-/three-0.139.2.tgz#b110799a15736df673b9293e31653a4ac73648dd"
- integrity sha512-gV7q7QY8rogu7HLFZR9cWnOQAUedUhu2WXAnpr2kdXZP9YDKsG/0ychwQvWkZN5PlNw9mv5MoCTin6zNTXoONg==
+three@0.141.0:
+ version "0.141.0"
+ resolved "https://registry.yarnpkg.com/three/-/three-0.141.0.tgz#16677a12b9dd0c3e1568ebad0fd09de15d5a8216"
+ integrity sha512-JaSDAPWuk4RTzG5BYRQm8YZbERUxTfTDVouWgHMisS2to4E5fotMS9F2zPFNOIJyEFTTQDDKPpsgZVThKU3pXA==
-throttle-debounce@4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-4.0.0.tgz#ec763b1c050c3a8f73eddd2e853a720893102a40"
- integrity sha512-bO2OiH++k8Z3cTNZccOJRlxY5Sk3Tx3Kz6cQl3VY5pTRcEgqbPxwEKtrC00whFAo2jIBQlaH1ZG5mtfrBef3qw==
+throttle-debounce@5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.0.tgz#a17a4039e82a2ed38a5e7268e4132d6960d41933"
+ integrity sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg==
throttleit@^1.0.0:
version "1.0.0"
@@ -5521,29 +4007,28 @@ tough-cookie@~2.5.0:
psl "^1.1.28"
punycode "^2.1.1"
-ts-loader@9.2.8:
- version "9.2.8"
- resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.2.8.tgz#e89aa32fa829c5cad0a1d023d6b3adecd51d5a48"
- integrity sha512-gxSak7IHUuRtwKf3FIPSW1VpZcqF9+MBrHOvBp9cjHh+525SjtCIJKVGjRKIAfxBwDGDGCFF00rTfzB1quxdSw==
+tsc-alias@1.6.9:
+ version "1.6.9"
+ resolved "https://registry.yarnpkg.com/tsc-alias/-/tsc-alias-1.6.9.tgz#d04d95124b95ad8eea55e52d45cf65a744c26baa"
+ integrity sha512-5lv5uAHn0cgxY1XfpXIdquUSz2xXq3ryQyNtxC6DYH7YT5rt/W+9Gsft2uyLFTh+ozk4qU8iCSP3VemjT69xlQ==
dependencies:
- chalk "^4.1.0"
- enhanced-resolve "^5.0.0"
- micromatch "^4.0.0"
- semver "^7.3.4"
-
-tsc-alias@1.5.0:
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/tsc-alias/-/tsc-alias-1.5.0.tgz#bc26f8dccf96e4ea350adc3f64ad3d2325cad967"
- integrity sha512-Pb3y7ZjULKFHEV2US5dS58/hV76sE9Sn5iehiPjYqHcm/lx4eCGAJYoSmrVXQMPX+6baTnDFJD0MGOyqn94dIg==
- dependencies:
- chokidar "^3.5.2"
- commander "^8.3.0"
- find-node-modules "^2.1.2"
+ chokidar "^3.5.3"
+ commander "^9.0.0"
globby "^11.0.4"
- mylas "^2.1.6"
+ mylas "^2.1.9"
normalize-path "^3.0.0"
+ plimit-lit "^1.2.6"
-tsconfig-paths@3.14.1, tsconfig-paths@^3.14.1:
+tsconfig-paths@4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.0.0.tgz#1082f5d99fd127b72397eef4809e4dd06d229b64"
+ integrity sha512-SLBg2GBKlR6bVtMgJJlud/o3waplKtL7skmLkExomIiaAtLGtVsoXIqP3SYdjbcH9lq/KVv7pMZeCBpLYOit6Q==
+ dependencies:
+ json5 "^2.2.1"
+ minimist "^1.2.6"
+ strip-bom "^3.0.0"
+
+tsconfig-paths@^3.14.1:
version "3.14.1"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a"
integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==
@@ -5582,12 +4067,7 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
-twemoji-parser@13.1.0, twemoji-parser@13.1.x:
- version "13.1.0"
- resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-13.1.0.tgz#65e7e449c59258791b22ac0b37077349127e3ea4"
- integrity sha512-AQOzLJpYlpWMy8n+0ATyKKZzWlZBJN+G0C+5lhX7Ftc2PeEVdUU/7ns2Pn2vVje26AIZ/OHwFoUbdv6YYD/wGg==
-
-twemoji-parser@14.0.0:
+twemoji-parser@14.0.0, twemoji-parser@14.0.x:
version "14.0.0"
resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-14.0.0.tgz#13dabcb6d3a261d9efbf58a1666b182033bf2b62"
integrity sha512-9DUOTGLOWs0pFWnh1p6NF+C3CkQ96PWmEFwhOVmT3WbecRC+68AIqpsnJXygfkFcp4aXbOp8Dwbhh/HQgvoRxA==
@@ -5626,10 +4106,10 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
-typescript@4.6.3:
- version "4.6.3"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c"
- integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==
+typescript@4.7.3:
+ version "4.7.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.3.tgz#8364b502d5257b540f9de4c40be84c98e23a129d"
+ integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==
unbox-primitive@^1.0.1:
version "1.0.1"
@@ -5641,11 +4121,6 @@ unbox-primitive@^1.0.1:
has-symbols "^1.0.2"
which-boxed-primitive "^1.0.2"
-uniq@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
- integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=
-
universalify@^0.1.0, universalify@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
@@ -5664,11 +4139,6 @@ unload@2.3.1:
"@babel/runtime" "^7.6.2"
detect-node "2.1.0"
-unquote@~1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544"
- integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=
-
untildify@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
@@ -5693,16 +4163,6 @@ util-deprecate@^1.0.2:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
-util.promisify@~1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee"
- integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==
- dependencies:
- define-properties "^1.1.3"
- es-abstract "^1.17.2"
- has-symbols "^1.0.1"
- object.getownpropertydescriptors "^2.1.0"
-
uuid@7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
@@ -5737,72 +4197,58 @@ verror@1.10.0:
core-util-is "1.0.2"
extsprintf "^1.2.0"
+vite@2.9.10:
+ version "2.9.10"
+ resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.10.tgz#f574d96655622c2e0fbc662edd0ed199c60fe91a"
+ integrity sha512-TwZRuSMYjpTurLqXspct+HZE7ONiW9d+wSWgvADGxhDPPyoIcNywY+RX4ng+QpK30DCa1l/oZgi2PLZDibhzbQ==
+ dependencies:
+ esbuild "^0.14.27"
+ postcss "^8.4.13"
+ resolve "^1.22.0"
+ rollup "^2.59.0"
+ optionalDependencies:
+ fsevents "~2.3.2"
+
void-elements@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
integrity sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=
-vue-eslint-parser@^8.0.1:
- version "8.0.1"
- resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-8.0.1.tgz#25e08b20a414551531f3e19f999902e1ecf45f13"
- integrity sha512-lhWjDXJhe3UZw2uu3ztX51SJAPGPey1Tff2RK3TyZURwbuI4vximQLzz4nQfCv8CZq4xx7uIiogHMMoSJPr33A==
+vue-eslint-parser@^9.0.1:
+ version "9.0.2"
+ resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.0.2.tgz#d2535516f3f55adb387939427fe741065eb7948a"
+ integrity sha512-uCPQwTGjOtAYrwnU+76pYxalhjsh7iFBsHwBqDHiOPTxtICDaraO4Szw54WFTNZTAEsgHHzqFOu1mmnBOBRzDA==
dependencies:
- debug "^4.3.2"
- eslint-scope "^6.0.0"
- eslint-visitor-keys "^3.0.0"
- espree "^9.0.0"
+ debug "^4.3.4"
+ eslint-scope "^7.1.1"
+ eslint-visitor-keys "^3.3.0"
+ espree "^9.3.1"
esquery "^1.4.0"
lodash "^4.17.21"
- semver "^7.3.5"
-
-vue-loader@17.0.0:
- version "17.0.0"
- resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-17.0.0.tgz#2eaa80aab125b19f00faa794b5bd867b17f85acb"
- integrity sha512-OWSXjrzIvbF2LtOUmxT3HYgwwubbfFelN8PAP9R9dwpIkj48TVioHhWWSx7W7fk+iF5cgg3CBJRxwTdtLU4Ecg==
- dependencies:
- chalk "^4.1.0"
- hash-sum "^2.0.0"
- loader-utils "^2.0.0"
+ semver "^7.3.6"
vue-prism-editor@2.0.0-alpha.2:
version "2.0.0-alpha.2"
resolved "https://registry.yarnpkg.com/vue-prism-editor/-/vue-prism-editor-2.0.0-alpha.2.tgz#aa53a88efaaed628027cbb282c2b1d37fc7c5c69"
integrity sha512-Gu42ba9nosrE+gJpnAEuEkDMqG9zSUysIR8SdXUw8MQKDjBnnNR9lHC18uOr/ICz7yrA/5c7jHJr9lpElODC7w==
-vue-router@4.0.14:
- version "4.0.14"
- resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.0.14.tgz#ce2028c1c5c33e30c7287950c973f397fce1bd65"
- integrity sha512-wAO6zF9zxA3u+7AkMPqw9LjoUCjSxfFvINQj3E/DceTt6uEz1XZLraDhdg2EYmvVwTBSGlLYsUw8bDmx0754Mw==
+vue-router@4.0.16:
+ version "4.0.16"
+ resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.0.16.tgz#9477beeeef36e80e04d041a1738801a55e6e862e"
+ integrity sha512-JcO7cb8QJLBWE+DfxGUL3xUDOae/8nhM1KVdnudadTAORbuxIC/xAydC5Zr/VLHUDQi1ppuTF5/rjBGzgzrJNA==
dependencies:
"@vue/devtools-api" "^6.0.0"
-vue-style-loader@4.1.3:
- version "4.1.3"
- resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz#6d55863a51fa757ab24e89d9371465072aa7bc35"
- integrity sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==
+vue@3.2.37:
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.37.tgz#da220ccb618d78579d25b06c7c21498ca4e5452e"
+ integrity sha512-bOKEZxrm8Eh+fveCqS1/NkG/n6aMidsI6hahas7pa0w/l7jkbssJVsRhVDs07IdDq7h9KHswZOgItnwJAgtVtQ==
dependencies:
- hash-sum "^1.0.2"
- loader-utils "^1.0.2"
-
-vue-svg-loader@0.17.0-beta.2:
- version "0.17.0-beta.2"
- resolved "https://registry.yarnpkg.com/vue-svg-loader/-/vue-svg-loader-0.17.0-beta.2.tgz#954b2a08b5488998dd81ec371ab5fb5ea4182ef7"
- integrity sha512-iMUGJTKEcuNAG8VXOchjA8443IqEmEi2Aw6EVIHWma2cC4TUQ7Oet5Yry9IFfqXQXXvyzXz5EyttVvfRGTNH4Q==
- dependencies:
- loader-utils "^2.0.0"
- semver "^7.3.2"
- svgo "^1.3.2"
-
-vue@3.2.31:
- version "3.2.31"
- resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.31.tgz#e0c49924335e9f188352816788a4cca10f817ce6"
- integrity sha512-odT3W2tcffTiQCy57nOT93INw1auq5lYLLYtWpPYQQYQOOdHiqFct9Xhna6GJ+pJQaF67yZABraH47oywkJgFw==
- dependencies:
- "@vue/compiler-dom" "3.2.31"
- "@vue/compiler-sfc" "3.2.31"
- "@vue/runtime-dom" "3.2.31"
- "@vue/server-renderer" "3.2.31"
- "@vue/shared" "3.2.31"
+ "@vue/compiler-dom" "3.2.37"
+ "@vue/compiler-sfc" "3.2.37"
+ "@vue/runtime-dom" "3.2.37"
+ "@vue/server-renderer" "3.2.37"
+ "@vue/shared" "3.2.37"
vuedraggable@4.0.1:
version "4.0.1"
@@ -5822,120 +4268,6 @@ wait-on@6.0.0:
minimist "^1.2.5"
rxjs "^7.1.0"
-watchpack@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.0.0.tgz#b12248f32f0fd4799b7be0802ad1f6573a45955c"
- integrity sha512-xSdCxxYZWNk3VK13bZRYhsQpfa8Vg63zXG+3pyU8ouqSLRCv4IGXIp9Kr226q6GBkGRlZrST2wwKtjfKz2m7Cg==
- dependencies:
- glob-to-regexp "^0.4.1"
- graceful-fs "^4.1.2"
-
-watchpack@^2.3.1:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.3.1.tgz#4200d9447b401156eeca7767ee610f8809bc9d25"
- integrity sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==
- dependencies:
- glob-to-regexp "^0.4.1"
- graceful-fs "^4.1.2"
-
-webpack-cli@4.9.2:
- version "4.9.2"
- resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.9.2.tgz#77c1adaea020c3f9e2db8aad8ea78d235c83659d"
- integrity sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==
- dependencies:
- "@discoveryjs/json-ext" "^0.5.0"
- "@webpack-cli/configtest" "^1.1.1"
- "@webpack-cli/info" "^1.4.1"
- "@webpack-cli/serve" "^1.6.1"
- colorette "^2.0.14"
- commander "^7.0.0"
- execa "^5.0.0"
- fastest-levenshtein "^1.0.12"
- import-local "^3.0.2"
- interpret "^2.2.0"
- rechoir "^0.7.0"
- webpack-merge "^5.7.3"
-
-webpack-merge@^5.7.3:
- version "5.7.3"
- resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.7.3.tgz#2a0754e1877a25a8bbab3d2475ca70a052708213"
- integrity sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==
- dependencies:
- clone-deep "^4.0.1"
- wildcard "^2.0.0"
-
-webpack-sources@^2.1.1:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac"
- integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==
- dependencies:
- source-list-map "^2.0.1"
- source-map "^0.6.1"
-
-webpack-sources@^3.2.3:
- version "3.2.3"
- resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
- integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
-
-webpack@5.72.0:
- version "5.72.0"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.72.0.tgz#f8bc40d9c6bb489a4b7a8a685101d6022b8b6e28"
- integrity sha512-qmSmbspI0Qo5ld49htys8GY9XhS9CGqFoHTsOVAnjBdg0Zn79y135R+k4IR4rKK6+eKaabMhJwiVB7xw0SJu5w==
- dependencies:
- "@types/eslint-scope" "^3.7.3"
- "@types/estree" "^0.0.51"
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/wasm-edit" "1.11.1"
- "@webassemblyjs/wasm-parser" "1.11.1"
- acorn "^8.4.1"
- acorn-import-assertions "^1.7.6"
- browserslist "^4.14.5"
- chrome-trace-event "^1.0.2"
- enhanced-resolve "^5.9.2"
- es-module-lexer "^0.9.0"
- eslint-scope "5.1.1"
- events "^3.2.0"
- glob-to-regexp "^0.4.1"
- graceful-fs "^4.2.9"
- json-parse-better-errors "^1.0.2"
- loader-runner "^4.2.0"
- mime-types "^2.1.27"
- neo-async "^2.6.2"
- schema-utils "^3.1.0"
- tapable "^2.1.1"
- terser-webpack-plugin "^5.1.3"
- watchpack "^2.3.1"
- webpack-sources "^3.2.3"
-
-webpack@^5:
- version "5.33.2"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.33.2.tgz#c049717c9b038febf5a72fd2f53319ad59a8c1fc"
- integrity sha512-X4b7F1sYBmJx8mlh2B7mV5szEkE0jYNJ2y3akgAP0ERi0vLCG1VvdsIxt8lFd4st6SUy0lf7W0CCQS566MBpJg==
- dependencies:
- "@types/eslint-scope" "^3.7.0"
- "@types/estree" "^0.0.46"
- "@webassemblyjs/ast" "1.11.0"
- "@webassemblyjs/wasm-edit" "1.11.0"
- "@webassemblyjs/wasm-parser" "1.11.0"
- acorn "^8.0.4"
- browserslist "^4.14.5"
- chrome-trace-event "^1.0.2"
- enhanced-resolve "^5.7.0"
- es-module-lexer "^0.4.0"
- eslint-scope "^5.1.1"
- events "^3.2.0"
- glob-to-regexp "^0.4.1"
- graceful-fs "^4.2.4"
- json-parse-better-errors "^1.0.2"
- loader-runner "^4.2.0"
- mime-types "^2.1.27"
- neo-async "^2.6.2"
- schema-utils "^3.0.0"
- tapable "^2.1.1"
- terser-webpack-plugin "^5.1.1"
- watchpack "^2.0.0"
- webpack-sources "^2.1.1"
-
websocket@1.0.34:
version "1.0.34"
resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111"
@@ -5964,25 +4296,13 @@ which-module@^2.0.0:
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
-which@2.0.2, which@^2.0.1:
+which@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
dependencies:
isexe "^2.0.0"
-which@^1.2.14:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
- integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
- dependencies:
- isexe "^2.0.0"
-
-wildcard@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"
- integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
-
with@^7.0.0:
version "7.0.2"
resolved "https://registry.yarnpkg.com/with/-/with-7.0.2.tgz#ccee3ad542d25538a7a7a80aad212b9828495bac"
@@ -5998,10 +4318,10 @@ word-wrap@^1.2.3:
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
-workerpool@6.2.0:
- version "6.2.0"
- resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b"
- integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==
+workerpool@6.2.1:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
+ integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
wrap-ansi@^6.2.0:
version "6.2.0"
@@ -6026,10 +4346,10 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
-ws@8.5.0:
- version "8.5.0"
- resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f"
- integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==
+ws@8.8.0:
+ version "8.8.0"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769"
+ integrity sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==
xml-js@^1.6.11:
version "1.6.11"
@@ -6038,6 +4358,11 @@ xml-js@^1.6.11:
dependencies:
sax "^1.2.4"
+xml-name-validator@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"
+ integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==
+
y18n@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4"
@@ -6058,16 +4383,6 @@ yallist@^4.0.0:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
-yaml@^1.10.0:
- version "1.10.0"
- resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e"
- integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==
-
-yaml@^1.10.2:
- version "1.10.2"
- resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
- integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
-
yargs-parser@20.2.4, yargs-parser@^20.2.2:
version "20.2.4"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
@@ -6128,8 +4443,3 @@ yauzl@^2.10.0:
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"
-
-yocto-queue@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
- integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
diff --git a/packages/shared/.eslintrc.js b/packages/shared/.eslintrc.js
index 2d3356c3a6..4d9d6f2c85 100644
--- a/packages/shared/.eslintrc.js
+++ b/packages/shared/.eslintrc.js
@@ -68,5 +68,8 @@ module.exports = {
}],
'import/no-unresolved': ['off'],
'import/no-default-export': ['warn'],
+ 'import/order': ['warn', {
+ 'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
+ }]
},
};
diff --git a/packages/sw/.eslintrc.js b/packages/sw/.eslintrc.js
new file mode 100644
index 0000000000..9d56daca83
--- /dev/null
+++ b/packages/sw/.eslintrc.js
@@ -0,0 +1,22 @@
+module.exports = {
+ root: true,
+ env: {
+ "node": false
+ },
+ parserOptions: {
+ "parser": "@typescript-eslint/parser",
+ tsconfigRootDir: __dirname,
+ //project: ['./tsconfig.json'],
+ },
+ extends: [
+ //"../shared/.eslintrc.js",
+ ],
+ globals: {
+ "require": false,
+ "_DEV_": false,
+ "_LANGS_": false,
+ "_VERSION_": false,
+ "_ENV_": false,
+ "_PERF_PREFIX_": false,
+ }
+}
diff --git a/packages/sw/.npmrc b/packages/sw/.npmrc
new file mode 100644
index 0000000000..6b5f38e890
--- /dev/null
+++ b/packages/sw/.npmrc
@@ -0,0 +1,2 @@
+save-exact = true
+package-lock = false
diff --git a/packages/sw/.yarnrc b/packages/sw/.yarnrc
new file mode 100644
index 0000000000..788570fcd5
--- /dev/null
+++ b/packages/sw/.yarnrc
@@ -0,0 +1 @@
+network-timeout 600000
diff --git a/packages/sw/build.js b/packages/sw/build.js
new file mode 100644
index 0000000000..72d9db9c0f
--- /dev/null
+++ b/packages/sw/build.js
@@ -0,0 +1,37 @@
+const esbuild = require('esbuild');
+const locales = require('../../locales');
+const meta = require('../../package.json');
+const watch = process.argv[2]?.includes('watch');
+
+console.log('Starting SW building...');
+
+esbuild.build({
+ entryPoints: [ `${__dirname}/src/sw.ts` ],
+ bundle: true,
+ format: 'esm',
+ treeShaking: true,
+ minify: process.env.NODE_ENV === 'production',
+ absWorkingDir: __dirname,
+ outbase: `${__dirname}/src`,
+ outdir: `${__dirname}/../../built/_sw_dist_`,
+ loader: {
+ '.ts': 'ts'
+ },
+ tsconfig: `${__dirname}/tsconfig.json`,
+ define: {
+ _VERSION_: JSON.stringify(meta.version),
+ _LANGS_: JSON.stringify(Object.entries(locales).map(([k, v]) => [k, v._lang_])),
+ _ENV_: JSON.stringify(process.env.NODE_ENV),
+ _DEV_: process.env.NODE_ENV !== 'production',
+ _PERF_PREFIX_: JSON.stringify('Misskey:'),
+ },
+ watch: watch ? {
+ onRebuild(error, result) {
+ if (error) console.error('SW: watch build failed:', error);
+ else console.log('SW: watch build succeeded:', result);
+ },
+ } : false,
+}).then(result => {
+ if (watch) console.log('watching...');
+ else console.log('done,', JSON.stringify(result));
+});
diff --git a/packages/sw/package.json b/packages/sw/package.json
new file mode 100644
index 0000000000..41dfe19b85
--- /dev/null
+++ b/packages/sw/package.json
@@ -0,0 +1,17 @@
+{
+ "private": true,
+ "scripts": {
+ "watch": "node build.js watch",
+ "build": "node build.js",
+ "lint": "eslint --quiet src/**/*.{ts}"
+ },
+ "resolutions": {},
+ "dependencies": {
+ "esbuild": "^0.14.13",
+ "idb-keyval": "^6.0.3",
+ "misskey-js": "0.0.14"
+ },
+ "devDependencies": {
+ "eslint": "^8.2.0"
+ }
+}
diff --git a/packages/sw/src/filters/user.ts b/packages/sw/src/filters/user.ts
new file mode 100644
index 0000000000..09437eb19a
--- /dev/null
+++ b/packages/sw/src/filters/user.ts
@@ -0,0 +1,14 @@
+import * as misskey from 'misskey-js';
+import * as Acct from 'misskey-js/built/acct';
+
+export const acct = (user: misskey.Acct) => {
+ return Acct.toString(user);
+};
+
+export const userName = (user: misskey.entities.User) => {
+ return user.name || user.username;
+};
+
+export const userPage = (user: misskey.Acct, path?, absolute = false) => {
+ return `${absolute ? origin : ''}/@${acct(user)}${(path ? `/${path}` : '')}`;
+};
diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts
new file mode 100644
index 0000000000..6d7ba7d524
--- /dev/null
+++ b/packages/sw/src/scripts/create-notification.ts
@@ -0,0 +1,237 @@
+/*
+ * Notification manager for SW
+ */
+declare var self: ServiceWorkerGlobalScope;
+
+import { swLang } from '@/scripts/lang';
+import { cli } from '@/scripts/operations';
+import { pushNotificationDataMap } from '@/types';
+import getUserName from '@/scripts/get-user-name';
+import { I18n } from '@/scripts/i18n';
+import { getAccountFromId } from '@/scripts/get-account-from-id';
+
+export async function createNotification<K extends keyof pushNotificationDataMap>(data: pushNotificationDataMap[K]) {
+ const n = await composeNotification(data);
+
+ if (n) {
+ return self.registration.showNotification(...n);
+ } else {
+ console.error('Could not compose notification', data);
+ return createEmptyNotification();
+ }
+}
+
+async function composeNotification<K extends keyof pushNotificationDataMap>(data: pushNotificationDataMap[K]): Promise<[string, NotificationOptions] | null> {
+ if (!swLang.i18n) swLang.fetchLocale();
+ const i18n = await swLang.i18n as I18n<any>;
+ const { t } = i18n;
+ switch (data.type) {
+ /*
+ case 'driveFileCreated': // TODO (Server Side)
+ return [t('_notification.fileUploaded'), {
+ body: body.name,
+ icon: body.url,
+ data
+ }];
+ */
+ case 'notification':
+ switch (data.body.type) {
+ case 'follow':
+ // users/showã®åž‹å®šç¾©ã‚’swos.apiã¸å½“ã¦ã¯ã‚ã‚‹ã®ãŒå›°é›£ãªã®ã§apiFetch.requestを直接使用
+ const account = await getAccountFromId(data.userId);
+ if (!account) return null;
+ const userDetail = await cli.request('users/show', { userId: data.body.userId }, account.token);
+ return [t('_notification.youWereFollowed'), {
+ body: getUserName(data.body.user),
+ icon: data.body.user.avatarUrl,
+ data,
+ actions: userDetail.isFollowing ? [] : [
+ {
+ action: 'follow',
+ title: t('_notification._actions.followBack')
+ }
+ ],
+ }];
+
+ case 'mention':
+ return [t('_notification.youGotMention', { name: getUserName(data.body.user) }), {
+ body: data.body.note.text || '',
+ icon: data.body.user.avatarUrl,
+ data,
+ actions: [
+ {
+ action: 'reply',
+ title: t('_notification._actions.reply')
+ }
+ ],
+ }];
+
+ case 'reply':
+ return [t('_notification.youGotReply', { name: getUserName(data.body.user) }), {
+ body: data.body.note.text || '',
+ icon: data.body.user.avatarUrl,
+ data,
+ actions: [
+ {
+ action: 'reply',
+ title: t('_notification._actions.reply')
+ }
+ ],
+ }];
+
+ case 'renote':
+ return [t('_notification.youRenoted', { name: getUserName(data.body.user) }), {
+ body: data.body.note.text || '',
+ icon: data.body.user.avatarUrl,
+ data,
+ actions: [
+ {
+ action: 'showUser',
+ title: getUserName(data.body.user)
+ }
+ ],
+ }];
+
+ case 'quote':
+ return [t('_notification.youGotQuote', { name: getUserName(data.body.user) }), {
+ body: data.body.note.text || '',
+ icon: data.body.user.avatarUrl,
+ data,
+ actions: [
+ {
+ action: 'reply',
+ title: t('_notification._actions.reply')
+ },
+ ...((data.body.note.visibility === 'public' || data.body.note.visibility === 'home') ? [
+ {
+ action: 'renote',
+ title: t('_notification._actions.renote')
+ }
+ ] : [])
+ ],
+ }];
+
+ case 'reaction':
+ return [`${data.body.reaction} ${getUserName(data.body.user)}`, {
+ body: data.body.note.text || '',
+ icon: data.body.user.avatarUrl,
+ data,
+ actions: [
+ {
+ action: 'showUser',
+ title: getUserName(data.body.user)
+ }
+ ],
+ }];
+
+ case 'pollVote':
+ return [t('_notification.youGotPoll', { name: getUserName(data.body.user) }), {
+ body: data.body.note.text || '',
+ icon: data.body.user.avatarUrl,
+ data,
+ }];
+
+ case 'pollEnded':
+ return [t('_notification.pollEnded'), {
+ body: data.body.note.text || '',
+ data,
+ }];
+
+ case 'receiveFollowRequest':
+ return [t('_notification.youReceivedFollowRequest'), {
+ body: getUserName(data.body.user),
+ icon: data.body.user.avatarUrl,
+ data,
+ actions: [
+ {
+ action: 'accept',
+ title: t('accept')
+ },
+ {
+ action: 'reject',
+ title: t('reject')
+ }
+ ],
+ }];
+
+ case 'followRequestAccepted':
+ return [t('_notification.yourFollowRequestAccepted'), {
+ body: getUserName(data.body.user),
+ icon: data.body.user.avatarUrl,
+ data,
+ }];
+
+ case 'groupInvited':
+ return [t('_notification.youWereInvitedToGroup', { userName: getUserName(data.body.user) }), {
+ body: data.body.invitation.group.name,
+ data,
+ actions: [
+ {
+ action: 'accept',
+ title: t('accept')
+ },
+ {
+ action: 'reject',
+ title: t('reject')
+ }
+ ],
+ }];
+
+ case 'app':
+ return [data.body.header || data.body.body, {
+ body: data.body.header && data.body.body,
+ icon: data.body.icon,
+ data
+ }];
+
+ default:
+ return null;
+ }
+ case 'unreadMessagingMessage':
+ if (data.body.groupId === null) {
+ return [t('_notification.youGotMessagingMessageFromUser', { name: getUserName(data.body.user) }), {
+ icon: data.body.user.avatarUrl,
+ tag: `messaging:user:${data.body.userId}`,
+ data,
+ renotify: true,
+ }];
+ }
+ return [t('_notification.youGotMessagingMessageFromGroup', { name: data.body.group.name }), {
+ icon: data.body.user.avatarUrl,
+ tag: `messaging:group:${data.body.groupId}`,
+ data,
+ renotify: true,
+ }];
+ default:
+ return null;
+ }
+}
+
+export async function createEmptyNotification() {
+ return new Promise<void>(async res => {
+ if (!swLang.i18n) swLang.fetchLocale();
+ const i18n = await swLang.i18n as I18n<any>;
+ const { t } = i18n;
+
+ await self.registration.showNotification(
+ t('_notification.emptyPushNotificationMessage'),
+ {
+ silent: true,
+ tag: 'read_notification',
+ }
+ );
+
+ res();
+
+ setTimeout(async () => {
+ for (const n of
+ [
+ ...(await self.registration.getNotifications({ tag: 'user_visible_auto_notification' })),
+ ...(await self.registration.getNotifications({ tag: 'read_notification' }))
+ ]
+ ) {
+ n.close();
+ }
+ }, 1000);
+ });
+}
diff --git a/packages/sw/src/scripts/get-account-from-id.ts b/packages/sw/src/scripts/get-account-from-id.ts
new file mode 100644
index 0000000000..be4cfaeba4
--- /dev/null
+++ b/packages/sw/src/scripts/get-account-from-id.ts
@@ -0,0 +1,7 @@
+import { get } from 'idb-keyval';
+
+export async function getAccountFromId(id: string) {
+ const accounts = await get('accounts') as { token: string; id: string; }[];
+ if (!accounts) console.log('Accounts are not recorded');
+ return accounts.find(e => e.id === id);
+}
diff --git a/packages/sw/src/scripts/get-user-name.ts b/packages/sw/src/scripts/get-user-name.ts
new file mode 100644
index 0000000000..d499ea0203
--- /dev/null
+++ b/packages/sw/src/scripts/get-user-name.ts
@@ -0,0 +1,3 @@
+export default function(user: { name?: string | null, username: string }): string {
+ return user.name || user.username;
+}
diff --git a/packages/sw/src/scripts/i18n.ts b/packages/sw/src/scripts/i18n.ts
new file mode 100644
index 0000000000..3fe88e5514
--- /dev/null
+++ b/packages/sw/src/scripts/i18n.ts
@@ -0,0 +1,29 @@
+export class I18n<T extends Record<string, any>> {
+ public ts: T;
+
+ constructor(locale: T) {
+ this.ts = locale;
+
+ //#region BIND
+ this.t = this.t.bind(this);
+ //#endregion
+ }
+
+ // string ã«ã—ã¦ã„ã‚‹ã®ã¯ã€ãƒ‰ãƒƒãƒˆåŒºåˆ‡ã‚Šã§ã®ãƒ‘ス指定を許å¯ã™ã‚‹ãŸã‚
+ // ãªã‚‹ã¹ãã“ã®ãƒ¡ã‚½ãƒƒãƒ‰ä½¿ã†ã‚ˆã‚Šã‚‚locale直接å‚ç…§ã®æ–¹ãŒvueã®ã‚­ãƒ£ãƒƒã‚·ãƒ¥åйã„ã¦ãƒ‘フォーマンスãŒè‰¯ã„ã‹ã‚‚
+ public t(key: string, args?: Record<string, string>): string {
+ try {
+ let str = key.split('.').reduce((o, i) => o[i], this.ts) as unknown as string;
+
+ if (args) {
+ for (const [k, v] of Object.entries(args)) {
+ str = str.replace(`{${k}}`, v);
+ }
+ }
+ return str;
+ } catch (err) {
+ console.warn(`missing localization '${key}'`);
+ return key;
+ }
+ }
+}
diff --git a/packages/sw/src/scripts/lang.ts b/packages/sw/src/scripts/lang.ts
new file mode 100644
index 0000000000..2d05404ef9
--- /dev/null
+++ b/packages/sw/src/scripts/lang.ts
@@ -0,0 +1,47 @@
+/*
+ * Language manager for SW
+ */
+declare var self: ServiceWorkerGlobalScope;
+
+import { get, set } from 'idb-keyval';
+import { I18n } from '@/scripts/i18n';
+
+class SwLang {
+ public cacheName = `mk-cache-${_VERSION_}`;
+
+ public lang: Promise<string> = get('lang').then(async prelang => {
+ if (!prelang) return 'en-US';
+ return prelang;
+ });
+
+ public setLang(newLang: string) {
+ this.lang = Promise.resolve(newLang);
+ set('lang', newLang);
+ return this.fetchLocale();
+ }
+
+ public i18n: Promise<I18n<any>> | null = null;
+
+ public fetchLocale() {
+ return this.i18n = this._fetch();
+ }
+
+ private async _fetch() {
+ // Service Workerã¯ä½•度も起動ã—ãã®ãŸã³ã«localeを読ã¿è¾¼ã‚€ã®ã§ã€CacheStorageを使ã†
+ const localeUrl = `/assets/locales/${await this.lang}.${_VERSION_}.json`;
+ let localeRes = await caches.match(localeUrl);
+
+ // _DEV_ãŒtrueã®å ´åˆã¯å¸¸ã«æœ€æ–°åŒ–
+ if (!localeRes || _DEV_) {
+ localeRes = await fetch(localeUrl);
+ const clone = localeRes?.clone();
+ if (!clone?.clone().ok) Error('locale fetching error');
+
+ caches.open(this.cacheName).then(cache => cache.put(localeUrl, clone));
+ }
+
+ return new I18n(await localeRes.json());
+ }
+}
+
+export const swLang = new SwLang();
diff --git a/packages/sw/src/scripts/login-id.ts b/packages/sw/src/scripts/login-id.ts
new file mode 100644
index 0000000000..0f9c6be4a9
--- /dev/null
+++ b/packages/sw/src/scripts/login-id.ts
@@ -0,0 +1,11 @@
+export function getUrlWithLoginId(url: string, loginId: string) {
+ const u = new URL(url, origin);
+ u.searchParams.append('loginId', loginId);
+ return u.toString();
+}
+
+export function getUrlWithoutLoginId(url: string) {
+ const u = new URL(url);
+ u.searchParams.delete('loginId');
+ return u.toString();
+}
diff --git a/packages/sw/src/scripts/notification-read.ts b/packages/sw/src/scripts/notification-read.ts
new file mode 100644
index 0000000000..8433f902b4
--- /dev/null
+++ b/packages/sw/src/scripts/notification-read.ts
@@ -0,0 +1,50 @@
+declare var self: ServiceWorkerGlobalScope;
+
+import { get } from 'idb-keyval';
+import { pushNotificationDataMap } from '@/types';
+import { api } from '@/scripts/operations';
+
+type Accounts = {
+ [x: string]: {
+ queue: string[],
+ timeout: number | null
+ }
+};
+
+class SwNotificationReadManager {
+ private accounts: Accounts = {};
+
+ public async construct() {
+ const accounts = await get('accounts');
+ if (!accounts) Error('Accounts are not recorded');
+
+ this.accounts = accounts.reduce((acc, e) => {
+ acc[e.id] = {
+ queue: [],
+ timeout: null
+ };
+ return acc;
+ }, {} as Accounts);
+
+ return this;
+ }
+
+ // ãƒ—ãƒƒã‚·ãƒ¥é€šçŸ¥ã®æ—¢èª­ã‚’サーãƒãƒ¼ã«é€ä¿¡
+ public async read<K extends keyof pushNotificationDataMap>(data: pushNotificationDataMap[K]) {
+ if (data.type !== 'notification' || !(data.userId in this.accounts)) return;
+
+ const account = this.accounts[data.userId];
+
+ account.queue.push(data.body.id as string);
+
+ // 最後ã®å‘¼ã³å‡ºã—ã‹ã‚‰200mså¾…ã£ã¦ã¾ã¨ã‚ã¦å‡¦ç†ã™ã‚‹
+ if (account.timeout) clearTimeout(account.timeout);
+ account.timeout = setTimeout(() => {
+ account.timeout = null;
+
+ api('notifications/read', data.userId, { notificationIds: account.queue });
+ }, 200);
+ }
+}
+
+export const swNotificationRead = (new SwNotificationReadManager()).construct();
diff --git a/packages/sw/src/scripts/operations.ts b/packages/sw/src/scripts/operations.ts
new file mode 100644
index 0000000000..02cf0d96cf
--- /dev/null
+++ b/packages/sw/src/scripts/operations.ts
@@ -0,0 +1,70 @@
+/*
+ * Operations
+ * å„種æ“作
+ */
+declare var self: ServiceWorkerGlobalScope;
+
+import * as Misskey from 'misskey-js';
+import { SwMessage, swMessageOrderType } from '@/types';
+import { acct as getAcct } from '@/filters/user';
+import { getAccountFromId } from '@/scripts/get-account-from-id';
+import { getUrlWithLoginId } from '@/scripts/login-id';
+
+export const cli = new Misskey.api.APIClient({ origin, fetch: (...args) => fetch(...args) });
+
+export async function api<E extends keyof Misskey.Endpoints>(endpoint: E, userId: string, options?: Misskey.Endpoints[E]['req']) {
+ const account = await getAccountFromId(userId);
+ if (!account) return;
+
+ return cli.request(endpoint, options, account.token);
+}
+
+// rendered acctã‹ã‚‰ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’é–‹ã
+export function openUser(acct: string, loginId: string) {
+ return openClient('push', `/@${acct}`, loginId, { acct });
+}
+
+// noteIdã‹ã‚‰ãƒŽãƒ¼ãƒˆã‚’é–‹ã
+export function openNote(noteId: string, loginId: string) {
+ return openClient('push', `/notes/${noteId}`, loginId, { noteId });
+}
+
+export async function openChat(body: any, loginId: string) {
+ if (body.groupId === null) {
+ return openClient('push', `/my/messaging/${getAcct(body.user)}`, loginId, { body });
+ } else {
+ return openClient('push', `/my/messaging/group/${body.groupId}`, loginId, { body });
+ }
+}
+
+// post-formã®ã‚ªãƒ—ションã‹ã‚‰æŠ•稿フォームを開ã
+export async function openPost(options: any, loginId: string) {
+ // クエリを作æˆã—ã¦ãŠã
+ let url = `/share?`;
+ if (options.initialText) url += `text=${options.initialText}&`;
+ if (options.reply) url += `replyId=${options.reply.id}&`;
+ if (options.renote) url += `renoteId=${options.renote.id}&`;
+
+ return openClient('post', url, loginId, { options });
+}
+
+export async function openClient(order: swMessageOrderType, url: string, loginId: string, query: any = {}) {
+ const client = await findClient();
+
+ if (client) {
+ client.postMessage({ type: 'order', ...query, order, loginId, url } as SwMessage);
+ return client;
+ }
+
+ return self.clients.openWindow(getUrlWithLoginId(url, loginId));
+}
+
+export async function findClient() {
+ const clients = await self.clients.matchAll({
+ type: 'window'
+ });
+ for (const c of clients) {
+ if (c.url.indexOf('?zen') < 0) return c;
+ }
+ return null;
+}
diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts
new file mode 100644
index 0000000000..0ba6a6e4af
--- /dev/null
+++ b/packages/sw/src/sw.ts
@@ -0,0 +1,200 @@
+declare var self: ServiceWorkerGlobalScope;
+
+import { createEmptyNotification, createNotification } from '@/scripts/create-notification';
+import { swLang } from '@/scripts/lang';
+import { swNotificationRead } from '@/scripts/notification-read';
+import { pushNotificationDataMap } from '@/types';
+import * as swos from '@/scripts/operations';
+import { acct as getAcct } from '@/filters/user';
+
+self.addEventListener('install', ev => {
+ ev.waitUntil(self.skipWaiting());
+});
+
+self.addEventListener('activate', ev => {
+ ev.waitUntil(
+ caches.keys()
+ .then(cacheNames => Promise.all(
+ cacheNames
+ .filter((v) => v !== swLang.cacheName)
+ .map(name => caches.delete(name))
+ ))
+ .then(() => self.clients.claim())
+ );
+});
+
+self.addEventListener('fetch', ev => {
+ ev.respondWith(
+ fetch(ev.request)
+ .catch(() => new Response(`Offline. Service Worker @${_VERSION_}`, { status: 200 }))
+ );
+});
+
+self.addEventListener('push', ev => {
+ // クライアントå–å¾—
+ ev.waitUntil(self.clients.matchAll({
+ includeUncontrolled: true,
+ type: 'window'
+ }).then(async <K extends keyof pushNotificationDataMap>(clients: readonly WindowClient[]) => {
+ const data: pushNotificationDataMap[K] = ev.data?.json();
+
+ switch (data.type) {
+ // case 'driveFileCreated':
+ case 'notification':
+ case 'unreadMessagingMessage':
+ // クライアントãŒã‚ã£ãŸã‚‰ã‚¹ãƒˆãƒªãƒ¼ãƒ ã«æŽ¥ç¶šã—ã¦ã„ã‚‹ã¨ã„ã†ã“ã¨ãªã®ã§é€šçŸ¥ã—ãªã„
+ if (clients.length != 0) return;
+ return createNotification(data);
+ case 'readAllNotifications':
+ for (const n of await self.registration.getNotifications()) {
+ if (n?.data?.type === 'notification') n.close();
+ }
+ break;
+ case 'readAllMessagingMessages':
+ for (const n of await self.registration.getNotifications()) {
+ if (n?.data?.type === 'unreadMessagingMessage') n.close();
+ }
+ break;
+ case 'readNotifications':
+ for (const n of await self.registration.getNotifications()) {
+ if (data.body?.notificationIds?.includes(n.data.body.id)) {
+ n.close();
+ }
+ }
+ break;
+ case 'readAllMessagingMessagesOfARoom':
+ for (const n of await self.registration.getNotifications()) {
+ if (n.data.type === 'unreadMessagingMessage'
+ && ('userId' in data.body
+ ? data.body.userId === n.data.body.userId
+ : data.body.groupId === n.data.body.groupId)
+ ) {
+ n.close();
+ }
+ }
+ break;
+ }
+
+ return createEmptyNotification();
+ }));
+});
+
+self.addEventListener('notificationclick', <K extends keyof pushNotificationDataMap>(ev: ServiceWorkerGlobalScopeEventMap['notificationclick']) => {
+ ev.waitUntil((async () => {
+ if (_DEV_) {
+ console.log('notificationclick', ev.action, ev.notification.data);
+ }
+
+ const { action, notification } = ev;
+ const data: pushNotificationDataMap[K] = notification.data;
+ const { userId: id } = data;
+ let client: WindowClient | null = null;
+
+ switch (data.type) {
+ case 'notification':
+ switch (action) {
+ case 'follow':
+ if ('userId' in data.body) await swos.api('following/create', id, { userId: data.body.userId });
+ break;
+ case 'showUser':
+ if ('user' in data.body) client = await swos.openUser(getAcct(data.body.user), id);
+ break;
+ case 'reply':
+ if ('note' in data.body) client = await swos.openPost({ reply: data.body.note }, id);
+ break;
+ case 'renote':
+ if ('note' in data.body) await swos.api('notes/create', id, { renoteId: data.body.note.id });
+ break;
+ case 'accept':
+ switch (data.body.type) {
+ case 'receiveFollowRequest':
+ await swos.api('following/requests/accept', id, { userId: data.body.userId });
+ break;
+ case 'groupInvited':
+ await swos.api('users/groups/invitations/accept', id, { invitationId: data.body.invitation.id });
+ break;
+ }
+ break;
+ case 'reject':
+ switch (data.body.type) {
+ case 'receiveFollowRequest':
+ await swos.api('following/requests/reject', id, { userId: data.body.userId });
+ break;
+ case 'groupInvited':
+ await swos.api('users/groups/invitations/reject', id, { invitationId: data.body.invitation.id });
+ break;
+ }
+ break;
+ case 'showFollowRequests':
+ client = await swos.openClient('push', '/my/follow-requests', id);
+ break;
+ default:
+ switch (data.body.type) {
+ case 'receiveFollowRequest':
+ client = await swos.openClient('push', '/my/follow-requests', id);
+ break;
+ case 'groupInvited':
+ client = await swos.openClient('push', '/my/groups', id);
+ break;
+ case 'reaction':
+ client = await swos.openNote(data.body.note.id, id);
+ break;
+ default:
+ if ('note' in data.body) {
+ client = await swos.openNote(data.body.note.id, id);
+ } else if ('user' in data.body) {
+ client = await swos.openUser(getAcct(data.body.user), id);
+ }
+ break;
+ }
+ }
+ break;
+ case 'unreadMessagingMessage':
+ client = await swos.openChat(data.body, id);
+ break;
+ }
+
+ if (client) {
+ client.focus();
+ }
+ if (data.type === 'notification') {
+ swNotificationRead.then(that => that.read(data));
+ }
+
+ notification.close();
+
+ })());
+});
+
+self.addEventListener('notificationclose', <K extends keyof pushNotificationDataMap>(ev: ServiceWorkerGlobalScopeEventMap['notificationclose']) => {
+ const data: pushNotificationDataMap[K] = ev.notification.data;
+
+ if (data.type === 'notification') {
+ swNotificationRead.then(that => that.read(data));
+ }
+});
+
+self.addEventListener('message', (ev: ServiceWorkerGlobalScopeEventMap['message']) => {
+ ev.waitUntil((async () => {
+ switch (ev.data) {
+ case 'clear':
+ // Cache Storage全削除
+ await caches.keys()
+ .then(cacheNames => Promise.all(
+ cacheNames.map(name => caches.delete(name))
+ ));
+ return; // TODO
+ }
+
+ if (typeof ev.data === 'object') {
+ // E.g. '[object Array]' → 'array'
+ const otype = Object.prototype.toString.call(ev.data).slice(8, -1).toLowerCase();
+
+ if (otype === 'object') {
+ if (ev.data.msg === 'initialize') {
+ swLang.setLang(ev.data.lang);
+ }
+ }
+ }
+ })());
+});
diff --git a/packages/sw/src/types.ts b/packages/sw/src/types.ts
new file mode 100644
index 0000000000..6aa3726eac
--- /dev/null
+++ b/packages/sw/src/types.ts
@@ -0,0 +1,31 @@
+import * as Misskey from 'misskey-js';
+
+export type swMessageOrderType = 'post' | 'push';
+
+export type SwMessage = {
+ type: 'order';
+ order: swMessageOrderType;
+ loginId: string;
+ url: string;
+ [x: string]: any;
+};
+
+// Defined also @/services/push-notification.ts#L7-L14
+type pushNotificationDataSourceMap = {
+ notification: Misskey.entities.Notification;
+ unreadMessagingMessage: Misskey.entities.MessagingMessage;
+ readNotifications: { notificationIds: string[] };
+ readAllNotifications: undefined;
+ readAllMessagingMessages: undefined;
+ readAllMessagingMessagesOfARoom: { userId: string } | { groupId: string };
+};
+
+export type pushNotificationData<K extends keyof pushNotificationDataSourceMap> = {
+ type: K;
+ body: pushNotificationDataSourceMap[K];
+ userId: string;
+};
+
+export type pushNotificationDataMap = {
+ [K in keyof pushNotificationDataSourceMap]: pushNotificationData<K>;
+};
diff --git a/packages/sw/tsconfig.json b/packages/sw/tsconfig.json
new file mode 100644
index 0000000000..c3a845f12a
--- /dev/null
+++ b/packages/sw/tsconfig.json
@@ -0,0 +1,39 @@
+{
+ "compilerOptions": {
+ "allowJs": true,
+ "noEmitOnError": false,
+ "noImplicitAny": false,
+ "noImplicitReturns": true,
+ "noUnusedParameters": false,
+ "noUnusedLocals": true,
+ "noFallthroughCasesInSwitch": true,
+ "declaration": false,
+ "sourceMap": false,
+ "target": "es2017",
+ "module": "esnext",
+ "moduleResolution": "node",
+ "removeComments": false,
+ "noLib": false,
+ "strict": true,
+ "strictNullChecks": true,
+ "experimentalDecorators": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"],
+ },
+ "typeRoots": [
+ "node_modules/@types",
+ "@types",
+ ],
+ "lib": [
+ "esnext",
+ "webworker"
+ ]
+ },
+ "compileOnSave": false,
+ "include": [
+ "./**/*.ts"
+ ]
+}
diff --git a/packages/sw/yarn.lock b/packages/sw/yarn.lock
new file mode 100644
index 0000000000..e6d683bc42
--- /dev/null
+++ b/packages/sw/yarn.lock
@@ -0,0 +1,710 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@eslint/eslintrc@^1.0.5":
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.0.5.tgz#33f1b838dbf1f923bfa517e008362b78ddbbf318"
+ integrity sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==
+ dependencies:
+ ajv "^6.12.4"
+ debug "^4.3.2"
+ espree "^9.2.0"
+ globals "^13.9.0"
+ ignore "^4.0.6"
+ import-fresh "^3.2.1"
+ js-yaml "^4.1.0"
+ minimatch "^3.0.4"
+ strip-json-comments "^3.1.1"
+
+"@humanwhocodes/config-array@^0.9.2":
+ version "0.9.3"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.3.tgz#f2564c744b387775b436418491f15fce6601f63e"
+ integrity sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==
+ dependencies:
+ "@humanwhocodes/object-schema" "^1.2.1"
+ debug "^4.1.1"
+ minimatch "^3.0.4"
+
+"@humanwhocodes/object-schema@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
+ integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
+
+acorn-jsx@^5.3.1:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+ integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
+acorn@^8.7.0:
+ version "8.7.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
+ integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
+
+ajv@^6.10.0, ajv@^6.12.4:
+ version "6.12.6"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
+ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-styles@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ dependencies:
+ color-convert "^2.0.1"
+
+argparse@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+ integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+autobind-decorator@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/autobind-decorator/-/autobind-decorator-2.4.0.tgz#ea9e1c98708cf3b5b356f7cf9f10f265ff18239c"
+ integrity sha512-OGYhWUO72V6DafbF8PM8rm3EPbfuyMZcJhtm5/n26IDwO18pohE4eNazLoCGhPiXOCD0gEGmrbU3849QvM8bbw==
+
+balanced-match@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+ integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+callsites@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+ integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+chalk@^4.0.0:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+ integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
+color-convert@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+ dependencies:
+ color-name "~1.1.4"
+
+color-name@~1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+cross-spawn@^7.0.2:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+ integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+ dependencies:
+ path-key "^3.1.0"
+ shebang-command "^2.0.0"
+ which "^2.0.1"
+
+debug@^4.1.1, debug@^4.3.2:
+ version "4.3.3"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
+ integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
+ dependencies:
+ ms "2.1.2"
+
+deep-is@^0.1.3:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+ integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+
+doctrine@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+ integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+ dependencies:
+ esutils "^2.0.2"
+
+esbuild-android-arm64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.17.tgz#7216810cb8d5b8cd03ce70bdc241dcdd90c34755"
+ integrity sha512-y7EJm8ADC9qKbo/dJ2zBXwNdIILJ76tTv7JDGvOkbLT8HJXIsgbpa0NJk7iFhyvP4GpsYvXTbvEQNn0DhyBhLA==
+
+esbuild-darwin-64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.17.tgz#1419e020f41814f8a74ce92b2dcab29a6d47e510"
+ integrity sha512-V2JAP8yyVbW6qR4SVXsEDqRicYM0x5niUuB05IFiE5itPI45k8j2dA2l+DtirR2SGXr+LEqgX347+2VA6eyTiA==
+
+esbuild-darwin-arm64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.17.tgz#95acf1022066d48346a63ffc5e4d36a07b83c9b0"
+ integrity sha512-ENkSKpjF4SImyA2TdHhKiZqtYc1DkMykICe1KSBw0YNF1sentjFI6wu+CRiYMpC7REf/3TQXoems2XPqIqDMlQ==
+
+esbuild-freebsd-64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.17.tgz#a3455199862110854937b05a0eecbed3e1aeec41"
+ integrity sha512-2i0nTNJM8ftNTvtR00vdqkru8XpHwAbkR2MBLoK2IDSzjsLStwCj+mxf6v83eVM9Abe3QA8xP+irqOdBlwDQ2g==
+
+esbuild-freebsd-arm64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.17.tgz#8a70f2a36f5b0da7d2efdd6fd02aa78611007fd0"
+ integrity sha512-QOmRi1n+uly2G7BbMbHb86YiFA5aM7B2T96A6OF1VG57LNwXwy8LPVM0PVjl7f9cV3pE3fy3VtXPJHJo8XggTA==
+
+esbuild-linux-32@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.17.tgz#b7123f6e4780687e017454604d909fbe558862e9"
+ integrity sha512-qG5NDk7FHHUVw01rjHESON0HvigF2X80b645TUlgTKsWRlrbzzHhMCmQguA01O5PiCimKnyoxti8aJIFNHpQnQ==
+
+esbuild-linux-64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.17.tgz#47a6b510c2f7faef595a4d6257a629e65385fdc3"
+ integrity sha512-De8OcmNvfNyFfQRLWbfuZqau6NpYBJxNTLP7Ls/PqQcw0HAwfaYThutY8ozHpPbKFPa7wgqabXlIC4NVSWT0/A==
+
+esbuild-linux-arm64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.17.tgz#dfd9022b7215ca660d464fcb20597b88887c7e64"
+ integrity sha512-WDEOD/YRA4J1lxhETKZff3gRxGYqqZEiVwIOqNfvCh2YcwWU2y6UmNGZsxcuKk18wot4dAXCXQyNZgBkVUTCLw==
+
+esbuild-linux-arm@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.17.tgz#e6f6bb9fe52def5260d7d49b790fbec0e7c6d9cb"
+ integrity sha512-ZwsgFUk3gR2pEMJdh5z4Ds18fvGETgElPqmNdx1NtZTCOVlFMAwFB5u/tOR2FrXbMFv+LkGnNxPDh48PYPDz9A==
+
+esbuild-linux-mips64le@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.17.tgz#bceaad33ff18a822b6da0396c6497a231397b6c3"
+ integrity sha512-Lf4X9NB7r6imzp/11TaGs4kWL0DUn1JxI9gAAKotnKh6T8Y/0sLvZSvQS8WvSZcr0V8RRCrRZwiQqjOALUU/9g==
+
+esbuild-linux-ppc64le@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.17.tgz#9562f094d1e5e6c3b61b776b15a9bbd657042654"
+ integrity sha512-aExhxbrK7/Mh9FArdiC9MbvrQz2bGCDI8cBALKJbmhKg0h7LNt6y1E1S9GGBZ/ZXkHDvV9FFVrXXZKFVU5Qpiw==
+
+esbuild-linux-s390x@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.17.tgz#2963cfe62c227bbf1da64e36d4ca0b23db8008fe"
+ integrity sha512-b0T20rNcS7POi5YLw5dFlsiC+riobR5IfppQGn5NWer6QiIkdL1vOx9eC9CUD3z1itpkLboRAZYieZfKfhCA2Q==
+
+esbuild-netbsd-64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.17.tgz#1d156023f9ae6be79b8627ab0cda2d7feb7f3a48"
+ integrity sha512-pFgTaAa2JF18nqNfCND9wOu1jbZ/mbDSaMxUp5fTkLlofyHhXeb5aChgXUkeipty2Pgq0OwOnxjHmiAxMI7N4g==
+
+esbuild-openbsd-64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.17.tgz#3fc44102c9b65375385112f4ce5899ae5e38f349"
+ integrity sha512-K5+plb6gsAfBcFqB0EG4KvLbgBKslVAfEyJggicwt/QoDwQGJAzao4M6zOA4PG7LlXOwWSqv7VmSFbH+b6DyKw==
+
+esbuild-sunos-64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.17.tgz#5bd24e7a7e863ea89d7e4eafd5364a155c9ea507"
+ integrity sha512-o1FINkbHRi9JB1YteOSXZdkDOmVUbmnCxRmTLkHvk8pfCFNpv/5/7ktt95teYKbEiJna2dEt3M4ckJ/+UVnW+w==
+
+esbuild-windows-32@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.17.tgz#8bda31c550fb6b425707114141d2c6ba034dab9b"
+ integrity sha512-Qutilz0I7OADWBtWrC/FD+2O/TNAkhwbZ+wIns7kF87lxIMtmqpBt3KnMk1e4F47aTrZRr0oH55Zhztd7m2PAA==
+
+esbuild-windows-64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.17.tgz#50b42c06908d3ce9fab8f0f9673199de5d0f9cbc"
+ integrity sha512-b21/oRV+PHrav0HkRpKjbM2yNRVe34gAfbdMppbZFea416wa8SrjcmVfSd7n4jgqoTQG0xe+MGgOpwXtjiB3DQ==
+
+esbuild-windows-arm64@0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.17.tgz#62d3921a810b64a03fcace76dad4db51d2128b45"
+ integrity sha512-4HN9E1idllewYvptcrrdfTA6DIWgg11kK0Zrv6yjxstJZLJeKxfilGBEaksLGs4Pst2rAYMx3H2vbYq7AWLQNA==
+
+esbuild@^0.14.13:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.17.tgz#6a634e56447aa0e90b34c42091d472d802d399e5"
+ integrity sha512-JLgyC6Uv31mv9T9Mm2xF1LntUMCNBSzvg2n32d8cTKZMwFr1wmMFY2FkVum98TSoEsDff0cR+Aj49H2sbBcjKQ==
+ optionalDependencies:
+ esbuild-android-arm64 "0.14.17"
+ esbuild-darwin-64 "0.14.17"
+ esbuild-darwin-arm64 "0.14.17"
+ esbuild-freebsd-64 "0.14.17"
+ esbuild-freebsd-arm64 "0.14.17"
+ esbuild-linux-32 "0.14.17"
+ esbuild-linux-64 "0.14.17"
+ esbuild-linux-arm "0.14.17"
+ esbuild-linux-arm64 "0.14.17"
+ esbuild-linux-mips64le "0.14.17"
+ esbuild-linux-ppc64le "0.14.17"
+ esbuild-linux-s390x "0.14.17"
+ esbuild-netbsd-64 "0.14.17"
+ esbuild-openbsd-64 "0.14.17"
+ esbuild-sunos-64 "0.14.17"
+ esbuild-windows-32 "0.14.17"
+ esbuild-windows-64 "0.14.17"
+ esbuild-windows-arm64 "0.14.17"
+
+escape-string-regexp@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+ integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+eslint-scope@^7.1.0:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.0.tgz#c1f6ea30ac583031f203d65c73e723b01298f153"
+ integrity sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==
+ dependencies:
+ esrecurse "^4.3.0"
+ estraverse "^5.2.0"
+
+eslint-utils@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672"
+ integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==
+ dependencies:
+ eslint-visitor-keys "^2.0.0"
+
+eslint-visitor-keys@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
+ integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
+
+eslint-visitor-keys@^3.1.0, eslint-visitor-keys@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz#6fbb166a6798ee5991358bc2daa1ba76cc1254a1"
+ integrity sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==
+
+eslint@^8.2.0:
+ version "8.8.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.8.0.tgz#9762b49abad0cb4952539ffdb0a046392e571a2d"
+ integrity sha512-H3KXAzQGBH1plhYS3okDix2ZthuYJlQQEGE5k0IKuEqUSiyu4AmxxlJ2MtTYeJ3xB4jDhcYCwGOg2TXYdnDXlQ==
+ dependencies:
+ "@eslint/eslintrc" "^1.0.5"
+ "@humanwhocodes/config-array" "^0.9.2"
+ ajv "^6.10.0"
+ chalk "^4.0.0"
+ cross-spawn "^7.0.2"
+ debug "^4.3.2"
+ doctrine "^3.0.0"
+ escape-string-regexp "^4.0.0"
+ eslint-scope "^7.1.0"
+ eslint-utils "^3.0.0"
+ eslint-visitor-keys "^3.2.0"
+ espree "^9.3.0"
+ esquery "^1.4.0"
+ esutils "^2.0.2"
+ fast-deep-equal "^3.1.3"
+ file-entry-cache "^6.0.1"
+ functional-red-black-tree "^1.0.1"
+ glob-parent "^6.0.1"
+ globals "^13.6.0"
+ ignore "^5.2.0"
+ import-fresh "^3.0.0"
+ imurmurhash "^0.1.4"
+ is-glob "^4.0.0"
+ js-yaml "^4.1.0"
+ json-stable-stringify-without-jsonify "^1.0.1"
+ levn "^0.4.1"
+ lodash.merge "^4.6.2"
+ minimatch "^3.0.4"
+ natural-compare "^1.4.0"
+ optionator "^0.9.1"
+ regexpp "^3.2.0"
+ strip-ansi "^6.0.1"
+ strip-json-comments "^3.1.0"
+ text-table "^0.2.0"
+ v8-compile-cache "^2.0.3"
+
+espree@^9.2.0, espree@^9.3.0:
+ version "9.3.0"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.0.tgz#c1240d79183b72aaee6ccfa5a90bc9111df085a8"
+ integrity sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==
+ dependencies:
+ acorn "^8.7.0"
+ acorn-jsx "^5.3.1"
+ eslint-visitor-keys "^3.1.0"
+
+esquery@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5"
+ integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==
+ dependencies:
+ estraverse "^5.1.0"
+
+esrecurse@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+ integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+ dependencies:
+ estraverse "^5.2.0"
+
+estraverse@^5.1.0, estraverse@^5.2.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+ integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+esutils@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+ integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+eventemitter3@^4.0.7:
+ version "4.0.7"
+ resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
+ integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-json-stable-stringify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+ integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+
+file-entry-cache@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
+ integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
+ dependencies:
+ flat-cache "^3.0.4"
+
+flat-cache@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
+ integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
+ dependencies:
+ flatted "^3.1.0"
+ rimraf "^3.0.2"
+
+flatted@^3.1.0:
+ version "3.2.5"
+ resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3"
+ integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+functional-red-black-tree@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+ integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
+
+glob-parent@^6.0.1:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+ integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+ dependencies:
+ is-glob "^4.0.3"
+
+glob@^7.1.3:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
+ integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+globals@^13.6.0, globals@^13.9.0:
+ version "13.12.1"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.1.tgz#ec206be932e6c77236677127577aa8e50bf1c5cb"
+ integrity sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==
+ dependencies:
+ type-fest "^0.20.2"
+
+has-flag@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+ integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+idb-keyval@^6.0.3:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-6.1.0.tgz#e659cff41188e6097d7fadd69926f6adbbe70041"
+ integrity sha512-u/qHZ75rlD3gH+Zah8dAJVJcGW/RfCnfNrFkElC5RpRCnpsCXXhqjVk+6MoVKJ3WhmNbRYdI6IIVP88e+5sxGw==
+ dependencies:
+ safari-14-idb-fix "^3.0.0"
+
+ignore@^4.0.6:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
+ integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
+
+ignore@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
+ integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
+
+import-fresh@^3.0.0, import-fresh@^3.2.1:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+ integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+ dependencies:
+ parent-module "^1.0.0"
+ resolve-from "^4.0.0"
+
+imurmurhash@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+ integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+is-extglob@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
+
+is-glob@^4.0.0, is-glob@^4.0.3:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+ integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+ dependencies:
+ is-extglob "^2.1.1"
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+
+js-yaml@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+ integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+ dependencies:
+ argparse "^2.0.1"
+
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-stable-stringify-without-jsonify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+ integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
+
+levn@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+ integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+ dependencies:
+ prelude-ls "^1.2.1"
+ type-check "~0.4.0"
+
+lodash.merge@^4.6.2:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+ integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
+minimatch@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+ integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+misskey-js@0.0.14:
+ version "0.0.14"
+ resolved "https://registry.yarnpkg.com/misskey-js/-/misskey-js-0.0.14.tgz#1a616bdfbe81c6ee6900219eaf425bb5c714dd4d"
+ integrity sha512-bvLx6U3OwQwqHfp/WKwIVwdvNYAAPk0+YblXyxmSG3dwlzCgBRRLcB8o6bNruUDyJgh3t73pLDcOz3myxcUmww==
+ dependencies:
+ autobind-decorator "^2.4.0"
+ eventemitter3 "^4.0.7"
+ reconnecting-websocket "^4.4.0"
+
+ms@2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+ integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+natural-compare@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+ integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
+
+once@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+ dependencies:
+ wrappy "1"
+
+optionator@^0.9.1:
+ version "0.9.1"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
+ integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
+ dependencies:
+ deep-is "^0.1.3"
+ fast-levenshtein "^2.0.6"
+ levn "^0.4.1"
+ prelude-ls "^1.2.1"
+ type-check "^0.4.0"
+ word-wrap "^1.2.3"
+
+parent-module@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+ integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+ dependencies:
+ callsites "^3.0.0"
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+path-key@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+ integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+prelude-ls@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+ integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
+punycode@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+ integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+reconnecting-websocket@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz#3b0e5b96ef119e78a03135865b8bb0af1b948783"
+ integrity sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==
+
+regexpp@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
+ integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
+
+resolve-from@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+ integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+rimraf@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+ integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+ dependencies:
+ glob "^7.1.3"
+
+safari-14-idb-fix@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/safari-14-idb-fix/-/safari-14-idb-fix-3.0.0.tgz#450fc049b996ec7f3fd9ca2f89d32e0761583440"
+ integrity sha512-eBNFLob4PMq8JA1dGyFn6G97q3/WzNtFK4RnzT1fnLq+9RyrGknzYiM/9B12MnKAxuj1IXr7UKYtTNtjyKMBog==
+
+shebang-command@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+ integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+ dependencies:
+ shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+ integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+ integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
+supports-color@^7.1.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+ dependencies:
+ has-flag "^4.0.0"
+
+text-table@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+ integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
+
+type-check@^0.4.0, type-check@~0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+ integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+ dependencies:
+ prelude-ls "^1.2.1"
+
+type-fest@^0.20.2:
+ version "0.20.2"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+ integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
+uri-js@^4.2.2:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+ dependencies:
+ punycode "^2.1.0"
+
+v8-compile-cache@^2.0.3:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
+ integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
+
+which@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+ integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+ dependencies:
+ isexe "^2.0.0"
+
+word-wrap@^1.2.3:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+ integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
diff --git a/scripts/build.js b/scripts/build.js
index 783af78271..608648b953 100644
--- a/scripts/build.js
+++ b/scripts/build.js
@@ -17,6 +17,14 @@ const execa = require('execa');
stderr: process.stderr,
});
+ console.log('building packages/sw ...');
+
+ await execa('npm', ['run', 'build'], {
+ cwd: __dirname + '/../packages/sw',
+ stdout: process.stdout,
+ stderr: process.stderr,
+ });
+
console.log('build finishing ...');
await execa('npm', ['run', 'gulp'], {
diff --git a/scripts/clean-all.js b/scripts/clean-all.js
index 814ff3f257..456b88032b 100644
--- a/scripts/clean-all.js
+++ b/scripts/clean-all.js
@@ -7,6 +7,9 @@ const fs = require('fs');
fs.rmSync(__dirname + '/../packages/client/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/client/node_modules', { recursive: true, force: true });
+ fs.rmSync(__dirname + '/../packages/sw/built', { recursive: true, force: true });
+ fs.rmSync(__dirname + '/../packages/sw/node_modules', { recursive: true, force: true });
+
fs.rmSync(__dirname + '/../built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../node_modules', { recursive: true, force: true });
})();
diff --git a/scripts/clean.js b/scripts/clean.js
index a14f1fb35b..70b9d882b5 100644
--- a/scripts/clean.js
+++ b/scripts/clean.js
@@ -3,5 +3,6 @@ const fs = require('fs');
(async () => {
fs.rmSync(__dirname + '/../packages/backend/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/client/built', { recursive: true, force: true });
+ fs.rmSync(__dirname + '/../packages/sw/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../built', { recursive: true, force: true });
})();
diff --git a/scripts/dev.js b/scripts/dev.js
index b7dd870c41..c5dbb7b35a 100644
--- a/scripts/dev.js
+++ b/scripts/dev.js
@@ -25,6 +25,12 @@ const execa = require('execa');
stderr: process.stderr,
});
+ execa('npm', ['run', 'watch'], {
+ cwd: __dirname + '/../packages/sw',
+ stdout: process.stdout,
+ stderr: process.stderr,
+ });
+
const start = async () => {
try {
await execa('npm', ['run', 'start'], {
diff --git a/scripts/install-packages.js b/scripts/install-packages.js
index c25063b29a..d1dea3ebe5 100644
--- a/scripts/install-packages.js
+++ b/scripts/install-packages.js
@@ -3,7 +3,7 @@ const execa = require('execa');
(async () => {
console.log('installing dependencies of packages/backend ...');
- await execa('yarn', ['install'], {
+ await execa('yarn', ['--force', 'install'], {
cwd: __dirname + '/../packages/backend',
stdout: process.stdout,
stderr: process.stderr,
@@ -16,4 +16,12 @@ const execa = require('execa');
stdout: process.stdout,
stderr: process.stderr,
});
+
+ console.log('installing dependencies of packages/sw ...');
+
+ await execa('yarn', ['install'], {
+ cwd: __dirname + '/../packages/sw',
+ stdout: process.stdout,
+ stderr: process.stderr,
+ });
})();
diff --git a/scripts/lint.js b/scripts/lint.js
index 11aa4909b1..72a63f4ba3 100644
--- a/scripts/lint.js
+++ b/scripts/lint.js
@@ -14,4 +14,11 @@ const execa = require('execa');
stdout: process.stdout,
stderr: process.stderr,
});
+
+ console.log('linting packages/sw ...');
+ await execa('npm', ['run', 'lint'], {
+ cwd: __dirname + '/../packages/sw',
+ stdout: process.stdout,
+ stderr: process.stderr,
+ });
})();
diff --git a/yarn.lock b/yarn.lock
index 2dc0d12ecf..327eac0172 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -194,49 +194,49 @@
dependencies:
"@types/node" "*"
-"@typescript-eslint/parser@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.18.0.tgz#2bcd4ff21df33621df33e942ccb21cb897f004c6"
- integrity sha512-+08nYfurBzSSPndngnHvFw/fniWYJ5ymOrn/63oMIbgomVQOvIDhBoJmYZ9lwQOCnQV9xHGvf88ze3jFGUYooQ==
+"@typescript-eslint/parser@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.27.1.tgz#3a4dcaa67e45e0427b6ca7bb7165122c8b569639"
+ integrity sha512-7Va2ZOkHi5NP+AZwb5ReLgNF6nWLGTeUJfxdkVUAPPSaAdbWNnFZzLZ4EGGmmiCTg+AwlbE1KyUYTBglosSLHQ==
dependencies:
- "@typescript-eslint/scope-manager" "5.18.0"
- "@typescript-eslint/types" "5.18.0"
- "@typescript-eslint/typescript-estree" "5.18.0"
- debug "^4.3.2"
+ "@typescript-eslint/scope-manager" "5.27.1"
+ "@typescript-eslint/types" "5.27.1"
+ "@typescript-eslint/typescript-estree" "5.27.1"
+ debug "^4.3.4"
-"@typescript-eslint/scope-manager@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.18.0.tgz#a7d7b49b973ba8cebf2a3710eefd457ef2fb5505"
- integrity sha512-C0CZML6NyRDj+ZbMqh9FnPscg2PrzSaVQg3IpTmpe0NURMVBXlghGZgMYqBw07YW73i0MCqSDqv2SbywnCS8jQ==
+"@typescript-eslint/scope-manager@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.27.1.tgz#4d1504392d01fe5f76f4a5825991ec78b7b7894d"
+ integrity sha512-fQEOSa/QroWE6fAEg+bJxtRZJTH8NTskggybogHt4H9Da8zd4cJji76gA5SBlR0MgtwF7rebxTbDKB49YUCpAg==
dependencies:
- "@typescript-eslint/types" "5.18.0"
- "@typescript-eslint/visitor-keys" "5.18.0"
+ "@typescript-eslint/types" "5.27.1"
+ "@typescript-eslint/visitor-keys" "5.27.1"
-"@typescript-eslint/types@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.18.0.tgz#4f0425d85fdb863071680983853c59a62ce9566e"
- integrity sha512-bhV1+XjM+9bHMTmXi46p1Led5NP6iqQcsOxgx7fvk6gGiV48c6IynY0apQb7693twJDsXiVzNXTflhplmaiJaw==
+"@typescript-eslint/types@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.27.1.tgz#34e3e629501349d38be6ae97841298c03a6ffbf1"
+ integrity sha512-LgogNVkBhCTZU/m8XgEYIWICD6m4dmEDbKXESCbqOXfKZxRKeqpiJXQIErv66sdopRKZPo5l32ymNqibYEH/xg==
-"@typescript-eslint/typescript-estree@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.18.0.tgz#6498e5ee69a32e82b6e18689e2f72e4060986474"
- integrity sha512-wa+2VAhOPpZs1bVij9e5gyVu60ReMi/KuOx4LKjGx2Y3XTNUDJgQ+5f77D49pHtqef/klglf+mibuHs9TrPxdQ==
+"@typescript-eslint/typescript-estree@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.27.1.tgz#7621ee78607331821c16fffc21fc7a452d7bc808"
+ integrity sha512-DnZvvq3TAJ5ke+hk0LklvxwYsnXpRdqUY5gaVS0D4raKtbznPz71UJGnPTHEFo0GDxqLOLdMkkmVZjSpET1hFw==
dependencies:
- "@typescript-eslint/types" "5.18.0"
- "@typescript-eslint/visitor-keys" "5.18.0"
- debug "^4.3.2"
- globby "^11.0.4"
+ "@typescript-eslint/types" "5.27.1"
+ "@typescript-eslint/visitor-keys" "5.27.1"
+ debug "^4.3.4"
+ globby "^11.1.0"
is-glob "^4.0.3"
- semver "^7.3.5"
+ semver "^7.3.7"
tsutils "^3.21.0"
-"@typescript-eslint/visitor-keys@5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.18.0.tgz#c7c07709823804171d569017f3b031ced7253e60"
- integrity sha512-Hf+t+dJsjAKpKSkg3EHvbtEpFFb/1CiOHnvI8bjHgOD4/wAw3gKrA0i94LrbekypiZVanJu3McWJg7rWDMzRTg==
+"@typescript-eslint/visitor-keys@5.27.1":
+ version "5.27.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.27.1.tgz#05a62666f2a89769dac2e6baa48f74e8472983af"
+ integrity sha512-xYs6ffo01nhdJgPieyk7HAOpjhTsx7r/oB9LWEhwAXgwn33tkr+W8DI2ChboqhZlC4q3TC6geDYPoiX8ROqyOQ==
dependencies:
- "@typescript-eslint/types" "5.18.0"
- eslint-visitor-keys "^3.0.0"
+ "@typescript-eslint/types" "5.27.1"
+ eslint-visitor-keys "^3.3.0"
aggregate-error@^3.0.0:
version "3.1.0"
@@ -382,7 +382,7 @@ arr-union@^3.1.0:
array-each@^1.0.0, array-each@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f"
- integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8=
+ integrity sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==
array-initial@^1.0.0:
version "1.1.0"
@@ -468,9 +468,9 @@ async-settle@^1.0.0:
async-done "^1.2.2"
async@^3.2.0:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/async/-/async-3.2.1.tgz#d3274ec66d107a47476a4c49136aacdb00665fc8"
- integrity sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg==
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9"
+ integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==
asynckit@^0.4.0:
version "0.4.0"
@@ -1005,12 +1005,12 @@ copy-descriptor@^0.1.0:
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
copy-props@^2.0.1:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/copy-props/-/copy-props-2.0.4.tgz#93bb1cadfafd31da5bb8a9d4b41f471ec3a72dfe"
- integrity sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/copy-props/-/copy-props-2.0.5.tgz#03cf9ae328d4ebb36f8f1d804448a6af9ee3f2d2"
+ integrity sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw==
dependencies:
- each-props "^1.3.0"
- is-plain-object "^2.0.1"
+ each-props "^1.3.2"
+ is-plain-object "^5.0.0"
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
@@ -1084,10 +1084,10 @@ csso@~2.3.1:
clap "^1.0.9"
source-map "^0.5.3"
-cypress@9.5.3:
- version "9.5.3"
- resolved "https://registry.yarnpkg.com/cypress/-/cypress-9.5.3.tgz#7c56b50fc1f1aa69ef10b271d895aeb4a1d7999e"
- integrity sha512-ItelIVmqMTnKYbo1JrErhsGgQGjWOxCpHT1TfMvwnIXKXN/OSlPjEK7rbCLYDZhejQL99PmUqul7XORI24Ik0A==
+cypress@10.0.3:
+ version "10.0.3"
+ resolved "https://registry.yarnpkg.com/cypress/-/cypress-10.0.3.tgz#889b4bef863b7d1ef1b608b85b964394ad350c5f"
+ integrity sha512-8C82XTybsEmJC9POYSNITGUhMLCRwB9LadP0x33H+52QVoBjhsWvIzrI+ybCe0+TyxaF0D5/9IL2kSTgjqCB9A==
dependencies:
"@cypress/request" "^2.88.10"
"@cypress/xvfb" "^1.2.4"
@@ -1173,6 +1173,13 @@ debug@^3.1.0:
dependencies:
ms "^2.1.1"
+debug@^4.3.4:
+ version "4.3.4"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+ integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+ dependencies:
+ ms "2.1.2"
+
decamelize@^1.1.1, decamelize@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -1261,7 +1268,7 @@ duplexify@^3.6.0:
readable-stream "^2.0.0"
stream-shift "^1.0.0"
-each-props@^1.3.0:
+each-props@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/each-props/-/each-props-1.3.2.tgz#ea45a414d16dd5cfa419b1a81720d5ca06892333"
integrity sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==
@@ -1349,10 +1356,10 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.3, escape-string-regexp@^
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
-eslint-visitor-keys@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz#eee4acea891814cda67a7d8812d9647dd0179af2"
- integrity sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==
+eslint-visitor-keys@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
+ integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
esprima@^2.6.0:
version "2.7.3"
@@ -1506,10 +1513,10 @@ fancy-log@^1.3.2:
parse-node-version "^1.0.0"
time-stamp "^1.0.0"
-fast-glob@^3.1.1:
- version "3.2.7"
- resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1"
- integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==
+fast-glob@^3.2.9:
+ version "3.2.11"
+ resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
+ integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
dependencies:
"@nodelib/fs.stat" "^2.0.2"
"@nodelib/fs.walk" "^1.2.3"
@@ -1826,16 +1833,16 @@ global-prefix@^1.0.1:
is-windows "^1.0.1"
which "^1.2.14"
-globby@^11.0.4:
- version "11.0.4"
- resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5"
- integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==
+globby@^11.1.0:
+ version "11.1.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
+ integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
dependencies:
array-union "^2.1.0"
dir-glob "^3.0.1"
- fast-glob "^3.1.1"
- ignore "^5.1.4"
- merge2 "^1.3.0"
+ fast-glob "^3.2.9"
+ ignore "^5.2.0"
+ merge2 "^1.4.1"
slash "^3.0.0"
glogg@^1.0.0:
@@ -2001,9 +2008,9 @@ homedir-polyfill@^1.0.1:
parse-passwd "^1.0.0"
hosted-git-info@^2.1.4:
- version "2.8.8"
- resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
- integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
+ version "2.8.9"
+ resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
+ integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
html-comment-regex@^1.1.0:
version "1.1.2"
@@ -2034,10 +2041,10 @@ ieee754@^1.1.13:
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
-ignore@^5.1.4:
- version "5.1.9"
- resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.9.tgz#9ec1a5cbe8e1446ec60d4420060d43aa6e7382fb"
- integrity sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==
+ignore@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
+ integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
indent-string@^4.0.0:
version "4.0.0"
@@ -2276,6 +2283,11 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
dependencies:
isobject "^3.0.1"
+is-plain-object@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
+ integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
+
is-relative@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d"
@@ -2622,7 +2634,7 @@ merge-stream@^2.0.0:
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
-merge2@^1.3.0:
+merge2@^1.3.0, merge2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
@@ -2969,9 +2981,9 @@ path-key@^3.0.0, path-key@^3.1.0:
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
path-parse@^1.0.6:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
- integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-root-regex@^0.1.0:
version "0.1.2"
@@ -3658,13 +3670,20 @@ semver-greatest-satisfied-range@^1.1.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
-semver@^7.3.2, semver@^7.3.5:
+semver@^7.3.2:
version "7.3.5"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
dependencies:
lru-cache "^6.0.0"
+semver@^7.3.7:
+ version "7.3.7"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
+ integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
+ dependencies:
+ lru-cache "^6.0.0"
+
set-blocking@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
@@ -4182,10 +4201,10 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
-typescript@4.6.3:
- version "4.6.3"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c"
- integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==
+typescript@4.7.3:
+ version "4.7.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.3.tgz#8364b502d5257b540f9de4c40be84c98e23a129d"
+ integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==
unc-path-regex@^0.1.2:
version "0.1.2"