summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorHazelnoot <acomputerdog@gmail.com>2025-04-29 15:37:43 -0400
committerHazelnoot <acomputerdog@gmail.com>2025-04-29 15:54:11 -0400
commit9c301fa5aac7e339a2b41feab8d0d247a60f50aa (patch)
tree26e1423620a2811a5e9372bcee6858851d9fad3e /packages
parentalign `docker_example.yml` with `example.yml` (diff)
parentchore: follow up on fixing Chromatic CI diff strategy (#15912) (diff)
downloadsharkey-9c301fa5aac7e339a2b41feab8d0d247a60f50aa.tar.gz
sharkey-9c301fa5aac7e339a2b41feab8d0d247a60f50aa.tar.bz2
sharkey-9c301fa5aac7e339a2b41feab8d0d247a60f50aa.zip
Merge branch 'misskey-develop' into merge/2025-03-24
# Conflicts: # .github/workflows/api-misskey-js.yml # .github/workflows/changelog-check.yml # .github/workflows/check-misskey-js-autogen.yml # .github/workflows/get-api-diff.yml # .github/workflows/lint.yml # .github/workflows/locale.yml # .github/workflows/on-release-created.yml # .github/workflows/storybook.yml # .github/workflows/test-backend.yml # .github/workflows/test-federation.yml # .github/workflows/test-frontend.yml # .github/workflows/test-misskey-js.yml # .github/workflows/test-production.yml # .github/workflows/validate-api-json.yml # package.json # packages/backend/package.json # packages/backend/src/server/api/ApiCallService.ts # packages/backend/src/server/api/endpoints/drive/files/create.ts # packages/frontend-shared/js/url.ts # packages/frontend/package.json # packages/frontend/src/components/MkFileCaptionEditWindow.vue # packages/frontend/src/components/MkInfo.vue # packages/frontend/src/components/MkLink.vue # packages/frontend/src/components/MkNote.vue # packages/frontend/src/components/MkNotes.vue # packages/frontend/src/components/MkPageWindow.vue # packages/frontend/src/components/MkReactionsViewer.vue # packages/frontend/src/components/MkTimeline.vue # packages/frontend/src/components/MkUrlPreview.vue # packages/frontend/src/components/MkUserPopup.vue # packages/frontend/src/components/global/MkPageHeader.vue # packages/frontend/src/components/global/MkUrl.vue # packages/frontend/src/components/global/PageWithHeader.vue # packages/frontend/src/pages/about-misskey.vue # packages/frontend/src/pages/announcements.vue # packages/frontend/src/pages/antenna-timeline.vue # packages/frontend/src/pages/channel.vue # packages/frontend/src/pages/instance-info.vue # packages/frontend/src/pages/note.vue # packages/frontend/src/pages/page.vue # packages/frontend/src/pages/role.vue # packages/frontend/src/pages/tag.vue # packages/frontend/src/pages/timeline.vue # packages/frontend/src/pages/user-list-timeline.vue # packages/frontend/src/pages/user/followers.vue # packages/frontend/src/pages/user/following.vue # packages/frontend/src/pages/user/home.vue # packages/frontend/src/pages/user/index.vue # packages/frontend/src/ui/deck.vue # packages/misskey-js/generator/package.json # pnpm-lock.yaml # scripts/changelog-checker/package-lock.json # scripts/changelog-checker/package.json
Diffstat (limited to 'packages')
-rw-r--r--packages/backend/package.json70
-rw-r--r--packages/backend/src/core/DriveService.ts7
-rw-r--r--packages/backend/src/core/FanoutTimelineEndpointService.ts20
-rw-r--r--packages/backend/src/core/ImageProcessingService.ts1
-rw-r--r--packages/backend/src/core/QueryService.ts38
-rw-r--r--packages/backend/src/core/RoleService.ts3
-rw-r--r--packages/backend/src/core/SearchService.ts12
-rw-r--r--packages/backend/src/models/json-schema/role.ts4
-rw-r--r--packages/backend/src/server/ServerService.ts7
-rw-r--r--packages/backend/src/server/api/ApiCallService.ts166
-rw-r--r--packages/backend/src/server/api/endpoint-base.ts10
-rw-r--r--packages/backend/src/server/api/endpoints/antennas/notes.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/channels/timeline.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/chat/messages/delete.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/chat/messages/react.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/chat/messages/unreact.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/chat/rooms/delete.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/chat/rooms/invitations/ignore.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/chat/rooms/join.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/chat/rooms/leave.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/chat/rooms/mute.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/clips/notes.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/drive/files/create.ts7
-rw-r--r--packages/backend/src/server/api/endpoints/notes/children.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/notes/featured.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/notes/local-timeline.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/notes/mentions.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/notes/renotes.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/notes/replies.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/notes/search-by-tag.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/notes/timeline.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/roles/notes.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/users/featured-notes.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/users/notes.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/reactions.ts1
-rw-r--r--packages/backend/src/server/web/style.css1
-rw-r--r--packages/backend/src/server/web/style.embed.css1
-rw-r--r--packages/backend/test-federation/compose.tpl.yml4
-rw-r--r--packages/backend/test-federation/compose.yml8
-rw-r--r--packages/backend/test/e2e/api.ts4
-rw-r--r--packages/backend/test/unit/server/api/drive/files/create.ts108
-rw-r--r--packages/frontend-embed/package.json24
-rw-r--r--packages/frontend-shared/js/collapsed.ts37
-rw-r--r--packages/frontend-shared/js/const.ts1
-rw-r--r--packages/frontend-shared/package.json10
-rw-r--r--packages/frontend/.storybook/changes.ts2
-rw-r--r--packages/frontend/package.json38
-rw-r--r--packages/frontend/src/components/MkAbuseReportWindow.vue4
-rw-r--r--packages/frontend/src/components/MkAntennaEditor.vue4
-rw-r--r--packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue4
-rw-r--r--packages/frontend/src/components/MkDrive.vue7
-rw-r--r--packages/frontend/src/components/MkFileCaptionEditWindow.vue4
-rw-r--r--packages/frontend/src/components/MkFolder.vue40
-rw-r--r--packages/frontend/src/components/MkForgotPassword.vue4
-rw-r--r--packages/frontend/src/components/MkFormDialog.vue4
-rw-r--r--packages/frontend/src/components/MkInfo.vue1
-rw-r--r--packages/frontend/src/components/MkNote.vue2
-rw-r--r--packages/frontend/src/components/MkNoteDetailed.vue2
-rw-r--r--packages/frontend/src/components/MkNotes.vue30
-rw-r--r--packages/frontend/src/components/MkNotificationSelectWindow.vue4
-rw-r--r--packages/frontend/src/components/MkPageWindow.vue3
-rw-r--r--packages/frontend/src/components/MkPasswordDialog.vue4
-rw-r--r--packages/frontend/src/components/MkReactionsViewer.reaction.vue1
-rw-r--r--packages/frontend/src/components/MkReactionsViewer.vue2
-rw-r--r--packages/frontend/src/components/MkRemoteEmojiEditDialog.vue4
-rw-r--r--packages/frontend/src/components/MkRoleSelectDialog.vue5
-rw-r--r--packages/frontend/src/components/MkSchedulePostListDialog.vue4
-rw-r--r--packages/frontend/src/components/MkSignupDialog.form.vue4
-rw-r--r--packages/frontend/src/components/MkSignupDialog.rules.vue4
-rw-r--r--packages/frontend/src/components/MkSystemWebhookEditor.vue4
-rw-r--r--packages/frontend/src/components/MkTimeline.vue110
-rw-r--r--packages/frontend/src/components/MkTokenGenerateWindow.vue4
-rw-r--r--packages/frontend/src/components/MkTutorialDialog.vue28
-rw-r--r--packages/frontend/src/components/MkUserAnnouncementEditDialog.vue4
-rw-r--r--packages/frontend/src/components/MkUserSetupDialog.vue26
-rw-r--r--packages/frontend/src/components/SkMfmWindow.vue4
-rw-r--r--packages/frontend/src/components/global/MkPageHeader.vue27
-rw-r--r--packages/frontend/src/components/global/MkSpacer.vue58
-rw-r--r--packages/frontend/src/components/global/PageWithHeader.vue47
-rw-r--r--packages/frontend/src/components/global/RouterView.vue46
-rw-r--r--packages/frontend/src/components/grid/MkGrid.vue60
-rw-r--r--packages/frontend/src/components/index.ts3
-rw-r--r--packages/frontend/src/di.ts1
-rw-r--r--packages/frontend/src/pages/about-sharkey.vue4
-rw-r--r--packages/frontend/src/pages/about.vue29
-rw-r--r--packages/frontend/src/pages/achievements.vue4
-rw-r--r--packages/frontend/src/pages/admin-file.vue4
-rw-r--r--packages/frontend/src/pages/admin-user.vue4
-rw-r--r--packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue4
-rw-r--r--packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue4
-rw-r--r--packages/frontend/src/pages/admin/abuses.vue4
-rw-r--r--packages/frontend/src/pages/admin/ads.vue4
-rw-r--r--packages/frontend/src/pages/admin/announcements.vue4
-rw-r--r--packages/frontend/src/pages/admin/approvals.vue4
-rw-r--r--packages/frontend/src/pages/admin/branding.vue8
-rw-r--r--packages/frontend/src/pages/admin/custom-emojis-manager.local.list.logs.vue4
-rw-r--r--packages/frontend/src/pages/admin/custom-emojis-manager.local.list.search.vue4
-rw-r--r--packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue2
-rw-r--r--packages/frontend/src/pages/admin/custom-emojis-manager.local.vue35
-rw-r--r--packages/frontend/src/pages/admin/custom-emojis-manager.register.vue (renamed from packages/frontend/src/pages/admin/custom-emojis-manager.local.register.vue)112
-rw-r--r--packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue11
-rw-r--r--packages/frontend/src/pages/admin/custom-emojis-manager2.vue22
-rw-r--r--packages/frontend/src/pages/admin/database.vue4
-rw-r--r--packages/frontend/src/pages/admin/email-settings.vue8
-rw-r--r--packages/frontend/src/pages/admin/external-services.vue4
-rw-r--r--packages/frontend/src/pages/admin/federation-job-queue.vue4
-rw-r--r--packages/frontend/src/pages/admin/federation.vue4
-rw-r--r--packages/frontend/src/pages/admin/files.vue4
-rw-r--r--packages/frontend/src/pages/admin/index.vue4
-rw-r--r--packages/frontend/src/pages/admin/invites.vue4
-rw-r--r--packages/frontend/src/pages/admin/job-queue.vue8
-rw-r--r--packages/frontend/src/pages/admin/moderation.vue4
-rw-r--r--packages/frontend/src/pages/admin/modlog.vue4
-rw-r--r--packages/frontend/src/pages/admin/object-storage.vue8
-rw-r--r--packages/frontend/src/pages/admin/overview.vue4
-rw-r--r--packages/frontend/src/pages/admin/performance.vue4
-rw-r--r--packages/frontend/src/pages/admin/relays.vue4
-rw-r--r--packages/frontend/src/pages/admin/roles.edit.vue8
-rw-r--r--packages/frontend/src/pages/admin/roles.editor.vue20
-rw-r--r--packages/frontend/src/pages/admin/roles.role.vue4
-rw-r--r--packages/frontend/src/pages/admin/roles.vue12
-rw-r--r--packages/frontend/src/pages/admin/security.vue4
-rw-r--r--packages/frontend/src/pages/admin/server-rules.vue4
-rw-r--r--packages/frontend/src/pages/admin/settings.vue4
-rw-r--r--packages/frontend/src/pages/admin/system-webhook.vue4
-rw-r--r--packages/frontend/src/pages/admin/users.vue4
-rw-r--r--packages/frontend/src/pages/ads.vue4
-rw-r--r--packages/frontend/src/pages/announcement.vue4
-rw-r--r--packages/frontend/src/pages/announcements.vue7
-rw-r--r--packages/frontend/src/pages/antenna-timeline.vue4
-rw-r--r--packages/frontend/src/pages/api-console.vue4
-rw-r--r--packages/frontend/src/pages/auth.vue4
-rw-r--r--packages/frontend/src/pages/avatar-decoration-edit-dialog.vue4
-rw-r--r--packages/frontend/src/pages/avatar-decorations.vue4
-rw-r--r--packages/frontend/src/pages/channel-editor.vue4
-rw-r--r--packages/frontend/src/pages/channel.vue11
-rw-r--r--packages/frontend/src/pages/channels.vue99
-rw-r--r--packages/frontend/src/pages/chat/home.vue17
-rw-r--r--packages/frontend/src/pages/chat/message.vue4
-rw-r--r--packages/frontend/src/pages/chat/room.vue16
-rw-r--r--packages/frontend/src/pages/clicker.vue4
-rw-r--r--packages/frontend/src/pages/clip.vue4
-rw-r--r--packages/frontend/src/pages/contact.vue4
-rw-r--r--packages/frontend/src/pages/custom-emojis-manager.vue4
-rw-r--r--packages/frontend/src/pages/drive.file.vue8
-rw-r--r--packages/frontend/src/pages/drop-and-fusion.game.vue4
-rw-r--r--packages/frontend/src/pages/drop-and-fusion.vue4
-rw-r--r--packages/frontend/src/pages/emoji-edit-dialog.vue4
-rw-r--r--packages/frontend/src/pages/explore.featured.vue4
-rw-r--r--packages/frontend/src/pages/explore.roles.vue4
-rw-r--r--packages/frontend/src/pages/explore.users.vue4
-rw-r--r--packages/frontend/src/pages/explore.vue24
-rw-r--r--packages/frontend/src/pages/favorites.vue4
-rw-r--r--packages/frontend/src/pages/flash/flash-edit.vue8
-rw-r--r--packages/frontend/src/pages/flash/flash-index.vue47
-rw-r--r--packages/frontend/src/pages/flash/flash.vue4
-rw-r--r--packages/frontend/src/pages/follow-requests.vue59
-rw-r--r--packages/frontend/src/pages/gallery/edit.vue4
-rw-r--r--packages/frontend/src/pages/gallery/index.vue61
-rw-r--r--packages/frontend/src/pages/gallery/post.vue4
-rw-r--r--packages/frontend/src/pages/games.vue4
-rw-r--r--packages/frontend/src/pages/install-extensions.vue4
-rw-r--r--packages/frontend/src/pages/instance-info.vue7
-rw-r--r--packages/frontend/src/pages/invite.vue8
-rw-r--r--packages/frontend/src/pages/list.vue8
-rw-r--r--packages/frontend/src/pages/lookup.vue4
-rw-r--r--packages/frontend/src/pages/my-antennas/index.vue4
-rw-r--r--packages/frontend/src/pages/my-clips/index.vue27
-rw-r--r--packages/frontend/src/pages/my-lists/index.vue4
-rw-r--r--packages/frontend/src/pages/my-lists/list.vue4
-rw-r--r--packages/frontend/src/pages/note.vue4
-rw-r--r--packages/frontend/src/pages/notifications.vue7
-rw-r--r--packages/frontend/src/pages/page-editor/page-editor.vue4
-rw-r--r--packages/frontend/src/pages/page.vue4
-rw-r--r--packages/frontend/src/pages/pages.vue53
-rw-r--r--packages/frontend/src/pages/registry.keys.vue4
-rw-r--r--packages/frontend/src/pages/registry.value.vue4
-rw-r--r--packages/frontend/src/pages/registry.vue4
-rw-r--r--packages/frontend/src/pages/reset-password.vue4
-rw-r--r--packages/frontend/src/pages/reversi/game.board.vue4
-rw-r--r--packages/frontend/src/pages/reversi/game.setting.vue8
-rw-r--r--packages/frontend/src/pages/reversi/index.vue10
-rw-r--r--packages/frontend/src/pages/role.vue12
-rw-r--r--packages/frontend/src/pages/scratchpad.vue9
-rw-r--r--packages/frontend/src/pages/search.vue27
-rw-r--r--packages/frontend/src/pages/settings/2fa.qrdialog.vue12
-rw-r--r--packages/frontend/src/pages/settings/avatar-decoration.dialog.vue4
-rw-r--r--packages/frontend/src/pages/settings/emoji-palette.vue6
-rw-r--r--packages/frontend/src/pages/settings/index.vue4
-rw-r--r--packages/frontend/src/pages/settings/preferences.vue9
-rw-r--r--packages/frontend/src/pages/share.vue4
-rw-r--r--packages/frontend/src/pages/tag.vue8
-rw-r--r--packages/frontend/src/pages/theme-editor.vue4
-rw-r--r--packages/frontend/src/pages/timeline.vue63
-rw-r--r--packages/frontend/src/pages/user-list-timeline.vue4
-rw-r--r--packages/frontend/src/pages/user-tag.vue4
-rw-r--r--packages/frontend/src/pages/user/achievements.vue4
-rw-r--r--packages/frontend/src/pages/user/activity.vue4
-rw-r--r--packages/frontend/src/pages/user/clips.vue4
-rw-r--r--packages/frontend/src/pages/user/files.vue4
-rw-r--r--packages/frontend/src/pages/user/flashs.vue4
-rw-r--r--packages/frontend/src/pages/user/followers.vue4
-rw-r--r--packages/frontend/src/pages/user/following.vue4
-rw-r--r--packages/frontend/src/pages/user/gallery.vue4
-rw-r--r--packages/frontend/src/pages/user/home.vue4
-rw-r--r--packages/frontend/src/pages/user/index.vue33
-rw-r--r--packages/frontend/src/pages/user/lists.vue5
-rw-r--r--packages/frontend/src/pages/user/pages.vue4
-rw-r--r--packages/frontend/src/pages/user/raw.vue4
-rw-r--r--packages/frontend/src/pages/user/reactions.vue4
-rw-r--r--packages/frontend/src/preferences/def.ts3
-rw-r--r--packages/frontend/src/store.ts4
-rw-r--r--packages/frontend/src/style.scss25
-rw-r--r--packages/frontend/src/ui/_common_/common.vue2
-rw-r--r--packages/frontend/src/ui/_common_/mobile-footer-menu.vue15
-rw-r--r--packages/frontend/src/ui/_common_/navbar-for-mobile.vue1
-rw-r--r--packages/frontend/src/ui/_common_/navbar.vue22
-rw-r--r--packages/frontend/src/ui/_common_/titlebar.vue87
-rw-r--r--packages/frontend/src/ui/deck.vue151
-rw-r--r--packages/frontend/src/ui/deck/column.vue2
-rw-r--r--packages/frontend/src/ui/universal.vue47
-rw-r--r--packages/frontend/src/utility/upload.ts2
-rw-r--r--packages/megalodon/package.json21
-rw-r--r--packages/misskey-bubble-game/package.json12
-rw-r--r--packages/misskey-js/etc/misskey-js.api.md32
-rw-r--r--packages/misskey-js/generator/package.json10
-rw-r--r--packages/misskey-js/package.json22
-rw-r--r--packages/misskey-js/src/autogen/endpoint.ts24
-rw-r--r--packages/misskey-js/src/autogen/entities.ts8
-rw-r--r--packages/misskey-js/src/autogen/types.ts65
-rw-r--r--packages/misskey-reversi/package.json12
-rw-r--r--packages/sw/package.json6
234 files changed, 1624 insertions, 1435 deletions
diff --git a/packages/backend/package.json b/packages/backend/package.json
index cd8ab7121f..0d62c8a536 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -37,17 +37,17 @@
},
"optionalDependencies": {
"@swc/core-android-arm64": "1.3.11",
- "@swc/core-darwin-arm64": "1.11.18",
- "@swc/core-darwin-x64": "1.11.18",
+ "@swc/core-darwin-arm64": "1.11.22",
+ "@swc/core-darwin-x64": "1.11.22",
"@swc/core-freebsd-x64": "1.3.11",
- "@swc/core-linux-arm-gnueabihf": "1.11.18",
- "@swc/core-linux-arm64-gnu": "1.11.18",
- "@swc/core-linux-arm64-musl": "1.11.18",
- "@swc/core-linux-x64-gnu": "1.11.18",
- "@swc/core-linux-x64-musl": "1.11.18",
- "@swc/core-win32-arm64-msvc": "1.11.18",
- "@swc/core-win32-ia32-msvc": "1.11.18",
- "@swc/core-win32-x64-msvc": "1.11.18",
+ "@swc/core-linux-arm-gnueabihf": "1.11.22",
+ "@swc/core-linux-arm64-gnu": "1.11.22",
+ "@swc/core-linux-arm64-musl": "1.11.22",
+ "@swc/core-linux-x64-gnu": "1.11.22",
+ "@swc/core-linux-x64-musl": "1.11.22",
+ "@swc/core-win32-arm64-msvc": "1.11.22",
+ "@swc/core-win32-ia32-msvc": "1.11.22",
+ "@swc/core-win32-x64-msvc": "1.11.22",
"bufferutil": "4.0.9",
"slacc-android-arm-eabi": "0.0.10",
"slacc-android-arm64": "0.0.10",
@@ -65,8 +65,8 @@
"utf-8-validate": "6.0.5"
},
"dependencies": {
- "@aws-sdk/client-s3": "3.782.0",
- "@aws-sdk/lib-storage": "3.782.0",
+ "@aws-sdk/client-s3": "3.797.0",
+ "@aws-sdk/lib-storage": "3.797.0",
"@discordapp/twemoji": "15.1.0",
"@fastify/accepts": "5.0.2",
"@fastify/cookie": "11.0.2",
@@ -78,17 +78,17 @@
"@fastify/view": "10.0.2",
"@misskey-dev/sharp-read-bmp": "1.3.0",
"@misskey-dev/summaly": "5.2.1",
- "@nestjs/common": "11.0.16",
- "@nestjs/core": "11.0.15",
- "@nestjs/testing": "11.0.15",
+ "@nestjs/common": "11.1.0",
+ "@nestjs/core": "11.1.0",
+ "@nestjs/testing": "11.1.0",
"@peertube/http-signature": "1.7.0",
"@sentry/node": "8.55.0",
"@sentry/profiling-node": "8.55.0",
"@simplewebauthn/server": "12.0.0",
"@sinonjs/fake-timers": "11.3.1",
"@smithy/node-http-handler": "2.5.0",
- "@swc/cli": "0.6.0",
- "@swc/core": "1.11.18",
+ "@swc/cli": "0.7.3",
+ "@swc/core": "1.11.22",
"@transfem-org/sfm-js": "0.24.6",
"@twemoji/parser": "15.1.1",
"@types/redis-info": "3.0.3",
@@ -101,7 +101,7 @@
"bcryptjs": "2.4.3",
"blurhash": "2.0.5",
"body-parser": "1.20.3",
- "bullmq": "5.48.1",
+ "bullmq": "5.51.1",
"cacheable-lookup": "7.0.0",
"canvas": "^3.1.0",
"cbor": "9.0.2",
@@ -127,18 +127,18 @@
"hpagent": "1.2.0",
"htmlescape": "1.1.1",
"http-link-header": "1.1.3",
- "ioredis": "5.6.0",
+ "ioredis": "5.6.1",
"ip-cidr": "4.0.2",
"ipaddr.js": "2.2.0",
"is-svg": "5.1.0",
"js-yaml": "4.1.0",
- "jsdom": "26.0.0",
+ "jsdom": "26.1.0",
"json5": "2.2.3",
"jsonld": "8.3.3",
"jsrsasign": "11.1.0",
"juice": "11.0.1",
"megalodon": "workspace:*",
- "meilisearch": "0.49.0",
+ "meilisearch": "0.50.0",
"microformats-parser": "2.0.2",
"mime-types": "2.1.35",
"misskey-js": "workspace:*",
@@ -148,14 +148,14 @@
"nanoid": "5.1.5",
"nested-property": "4.0.0",
"node-fetch": "3.3.2",
- "nodemailer": "6.10.0",
+ "nodemailer": "6.10.1",
"oauth": "0.10.2",
"oauth2orize": "1.12.0",
"oauth2orize-pkce": "0.1.2",
"os-utils": "0.0.14",
"otpauth": "9.4.0",
- "parse5": "7.2.1",
- "pg": "8.14.1",
+ "parse5": "7.3.0",
+ "pg": "8.15.6",
"pkce-challenge": "4.1.0",
"probe-image-size": "7.2.3",
"promise-limit": "2.7.0",
@@ -172,7 +172,7 @@
"rename": "1.0.4",
"rss-parser": "3.13.0",
"rxjs": "7.8.2",
- "sanitize-html": "2.15.0",
+ "sanitize-html": "2.16.0",
"secure-json-parse": "3.0.2",
"sharp": "0.34.1",
"slacc": "0.0.10",
@@ -194,10 +194,10 @@
},
"devDependencies": {
"@jest/globals": "29.7.0",
- "@nestjs/platform-express": "10.4.15",
- "@sentry/vue": "9.12.0",
+ "@nestjs/platform-express": "10.4.17",
+ "@sentry/vue": "9.14.0",
"@simplewebauthn/types": "12.0.0",
- "@swc/jest": "0.2.37",
+ "@swc/jest": "0.2.38",
"@types/accepts": "1.3.7",
"@types/archiver": "6.0.3",
"@types/bcryptjs": "2.4.6",
@@ -214,12 +214,12 @@
"@types/jsrsasign": "10.5.15",
"@types/mime-types": "2.1.4",
"@types/ms": "0.7.34",
- "@types/node": "22.14.0",
+ "@types/node": "22.15.2",
"@types/nodemailer": "6.4.17",
"@types/oauth": "0.9.6",
"@types/oauth2orize": "1.11.5",
"@types/oauth2orize-pkce": "0.1.2",
- "@types/pg": "8.11.11",
+ "@types/pg": "8.11.14",
"@types/proxy-addr": "^2.0.3",
"@types/pug": "2.0.10",
"@types/qrcode": "1.5.5",
@@ -230,14 +230,15 @@
"@types/semver": "7.7.0",
"@types/simple-oauth2": "5.0.7",
"@types/sinonjs__fake-timers": "8.1.5",
+ "@types/supertest": "6.0.3",
"@types/tinycolor2": "1.4.6",
"@types/tmp": "0.2.6",
"@types/uuid": "^9.0.4",
"@types/vary": "1.1.3",
"@types/web-push": "3.6.4",
"@types/ws": "8.18.1",
- "@typescript-eslint/eslint-plugin": "8.29.1",
- "@typescript-eslint/parser": "8.29.1",
+ "@typescript-eslint/eslint-plugin": "8.31.0",
+ "@typescript-eslint/parser": "8.31.0",
"aws-sdk-client-mock": "4.1.0",
"cross-env": "7.0.3",
"eslint-plugin-import": "2.31.0",
@@ -245,8 +246,9 @@
"fkill": "9.0.0",
"jest": "29.7.0",
"jest-mock": "29.7.0",
- "nodemon": "3.1.9",
+ "nodemon": "3.1.10",
"pid-port": "1.0.2",
- "simple-oauth2": "5.1.0"
+ "simple-oauth2": "5.1.0",
+ "supertest": "7.1.0"
}
}
diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts
index 4be1b0e41b..bb76b680a4 100644
--- a/packages/backend/src/core/DriveService.ts
+++ b/packages/backend/src/core/DriveService.ts
@@ -515,9 +515,16 @@ export class DriveService {
const policies = await this.roleService.getUserPolicies(user.id);
const driveCapacity = 1024 * 1024 * policies.driveCapacityMb;
+ const maxFileSize = 1024 * 1024 * policies.maxFileSizeMb;
this.registerLogger.debug('drive capacity override applied');
this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`);
+ if (maxFileSize < info.size) {
+ if (isLocalUser) {
+ throw new IdentifiableError('f9e4e5f3-4df4-40b5-b400-f236945f7073', 'Max file size exceeded.');
+ }
+ }
+
// If usage limit exceeded
if (driveCapacity < usage + info.size) {
if (isLocalUser) {
diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts
index 84ca06ec1e..af2723e99d 100644
--- a/packages/backend/src/core/FanoutTimelineEndpointService.ts
+++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts
@@ -8,10 +8,12 @@ import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import type { MiUser } from '@/models/User.js';
import type { MiNote } from '@/models/Note.js';
+import type { MiMeta } from '@/models/Meta.js';
import { Packed } from '@/misc/json-schema.js';
import type { NotesRepository } from '@/models/_.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { FanoutTimelineName, FanoutTimelineService } from '@/core/FanoutTimelineService.js';
+import { UtilityService } from '@/core/UtilityService.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { isQuote, isRenote } from '@/misc/is-renote.js';
import { CacheService } from '@/core/CacheService.js';
@@ -30,6 +32,7 @@ type TimelineOptions = {
alwaysIncludeMyNotes?: boolean;
ignoreAuthorFromBlock?: boolean;
ignoreAuthorFromMute?: boolean;
+ ignoreAuthorFromInstanceBlock?: boolean;
excludeNoFiles?: boolean;
excludeReplies?: boolean;
excludeBots?: boolean;
@@ -43,9 +46,13 @@ export class FanoutTimelineEndpointService {
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
+ @Inject(DI.meta)
+ private meta: MiMeta,
+
private noteEntityService: NoteEntityService,
private cacheService: CacheService,
private fanoutTimelineService: FanoutTimelineService,
+ private utilityService: UtilityService,
) {
}
@@ -125,6 +132,19 @@ export class FanoutTimelineEndpointService {
};
}
+ {
+ const parentFilter = filter;
+ filter = (note) => {
+ if (!ps.ignoreAuthorFromInstanceBlock) {
+ if (this.utilityService.isBlockedHost(this.meta.blockedHosts, note.userHost)) return false;
+ }
+ if (note.userId !== note.renoteUserId && this.utilityService.isBlockedHost(this.meta.blockedHosts, note.renoteUserHost)) return false;
+ if (note.userId !== note.replyUserId && this.utilityService.isBlockedHost(this.meta.blockedHosts, note.replyUserHost)) return false;
+
+ return parentFilter(note);
+ };
+ }
+
const redisTimeline: MiNote[] = [];
let readFromRedis = 0;
let lastSuccessfulRate = 1; // rateをキャッシュする?
diff --git a/packages/backend/src/core/ImageProcessingService.ts b/packages/backend/src/core/ImageProcessingService.ts
index 6f978b34c8..6f60475442 100644
--- a/packages/backend/src/core/ImageProcessingService.ts
+++ b/packages/backend/src/core/ImageProcessingService.ts
@@ -34,6 +34,7 @@ export const webpDefault: sharp.WebpOptions = {
smartSubsample: true,
mixed: true,
effort: 2,
+ loop: 0,
};
export const avifDefault: sharp.AvifOptions = {
diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts
index c611fe5f1e..fb4335b55d 100644
--- a/packages/backend/src/core/QueryService.ts
+++ b/packages/backend/src/core/QueryService.ts
@@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { Brackets, ObjectLiteral } from 'typeorm';
import { DI } from '@/di-symbols.js';
import type { MiUser } from '@/models/User.js';
-import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository, RenoteMutingsRepository } from '@/models/_.js';
+import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository, RenoteMutingsRepository, MiMeta } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import type { SelectQueryBuilder } from 'typeorm';
@@ -36,6 +36,9 @@ export class QueryService {
@Inject(DI.renoteMutingsRepository)
private renoteMutingsRepository: RenoteMutingsRepository,
+ @Inject(DI.meta)
+ private meta: MiMeta,
+
private idService: IdService,
) {
}
@@ -251,4 +254,37 @@ export class QueryService {
q.setParameters(mutingQuery.getParameters());
}
+
+ @bindThis
+ public generateBlockedHostQueryForNote(q: SelectQueryBuilder<any>, excludeAuthor?: boolean): void {
+ let nonBlockedHostQuery: (part: string) => string;
+ if (this.meta.blockedHosts.length === 0) {
+ nonBlockedHostQuery = () => '1=1';
+ } else {
+ nonBlockedHostQuery = (match: string) => `${match} NOT ILIKE ALL(ARRAY[:...blocked])`;
+ q.setParameters({ blocked: this.meta.blockedHosts.flatMap(x => [x, `%.${x}`]) });
+ }
+
+ if (excludeAuthor) {
+ const instanceSuspension = (user: string) => new Brackets(qb => qb
+ .where(`note.${user}Id IS NULL`) // no corresponding user
+ .orWhere(`note.userId = note.${user}Id`)
+ .orWhere(`note.${user}Host IS NULL`) // local
+ .orWhere(nonBlockedHostQuery(`note.${user}Host`)));
+
+ q
+ .andWhere(instanceSuspension('replyUser'))
+ .andWhere(instanceSuspension('renoteUser'));
+ } else {
+ const instanceSuspension = (user: string) => new Brackets(qb => qb
+ .where(`note.${user}Id IS NULL`) // no corresponding user
+ .orWhere(`note.${user}Host IS NULL`) // local
+ .orWhere(nonBlockedHostQuery(`note.${user}Host`)));
+
+ q
+ .andWhere(instanceSuspension('user'))
+ .andWhere(instanceSuspension('replyUser'))
+ .andWhere(instanceSuspension('renoteUser'));
+ }
+ }
}
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 8b98680f4c..229781c079 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -48,6 +48,7 @@ export type RolePolicies = {
canUseTranslator: boolean;
canHideAds: boolean;
driveCapacityMb: number;
+ maxFileSizeMb: number;
alwaysMarkNsfw: boolean;
canUpdateBioMedia: boolean;
pinLimit: number;
@@ -86,6 +87,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
canUseTranslator: true,
canHideAds: false,
driveCapacityMb: 100,
+ maxFileSizeMb: 10,
alwaysMarkNsfw: false,
canUpdateBioMedia: true,
pinLimit: 5,
@@ -399,6 +401,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
canUseTranslator: calc('canUseTranslator', vs => vs.some(v => v === true)),
canHideAds: calc('canHideAds', vs => vs.some(v => v === true)),
driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)),
+ maxFileSizeMb: calc('maxFileSizeMb', vs => Math.max(...vs)),
alwaysMarkNsfw: calc('alwaysMarkNsfw', vs => vs.some(v => v === true)),
canUpdateBioMedia: calc('canUpdateBioMedia', vs => vs.some(v => v === true)),
pinLimit: calc('pinLimit', vs => Math.max(...vs)),
diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts
index e17677bcb5..37238dc4b0 100644
--- a/packages/backend/src/core/SearchService.ts
+++ b/packages/backend/src/core/SearchService.ts
@@ -300,6 +300,7 @@ export class SearchService {
}
this.queryService.generateVisibilityQuery(query, me);
+ this.queryService.generateBlockedHostQueryForNote(query);
if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
@@ -366,9 +367,14 @@ export class SearchService {
this.cacheService.userBlockedCache.fetch(me.id),
])
: [new Set<string>(), new Set<string>()];
- const notes = (await this.notesRepository.findBy({
- id: In(res.hits.map(x => x.id)),
- })).filter(note => {
+
+ const query = this.notesRepository.createQueryBuilder('note');
+
+ query.where('note.id IN (:...noteIds)', { noteIds: res.hits.map(x => x.id) });
+
+ this.queryService.generateBlockedHostQueryForNote(query);
+
+ const notes = (await query.getMany()).filter(note => {
if (me && isUserRelated(note, userIdsWhoBlockingMe)) return false;
if (me && isUserRelated(note, userIdsWhoMeMuting)) return false;
return true;
diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts
index 9e95684f67..307c114c96 100644
--- a/packages/backend/src/models/json-schema/role.ts
+++ b/packages/backend/src/models/json-schema/role.ts
@@ -228,6 +228,10 @@ export const packedRolePoliciesSchema = {
type: 'integer',
optional: false, nullable: false,
},
+ maxFileSizeMb: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
alwaysMarkNsfw: {
type: 'boolean',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts
index dce47e2290..5857b3059e 100644
--- a/packages/backend/src/server/ServerService.ts
+++ b/packages/backend/src/server/ServerService.ts
@@ -75,7 +75,7 @@ export class ServerService implements OnApplicationShutdown {
}
@bindThis
- public async launch(): Promise<void> {
+ public async launch() {
const fastify = Fastify({
trustProxy: true,
logger: false,
@@ -135,8 +135,8 @@ export class ServerService implements OnApplicationShutdown {
reply.header('content-type', 'text/plain; charset=utf-8');
reply.header('link', `<${encodeURI(location)}>; rel="canonical"`);
done(null, [
- "Refusing to relay remote ActivityPub object lookup.",
- "",
+ 'Refusing to relay remote ActivityPub object lookup.',
+ '',
`Please remove 'application/activity+json' and 'application/ld+json' from the Accept header or fetch using the authoritative URL at ${location}.`,
].join('\n'));
});
@@ -304,6 +304,7 @@ export class ServerService implements OnApplicationShutdown {
}
await fastify.ready();
+ return fastify;
}
@bindThis
diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts
index b22a8c1837..1b8d33f9c9 100644
--- a/packages/backend/src/server/api/ApiCallService.ts
+++ b/packages/backend/src/server/api/ApiCallService.ts
@@ -6,8 +6,11 @@
import { randomUUID } from 'node:crypto';
import * as fs from 'node:fs';
import * as stream from 'node:stream/promises';
+import { Transform } from 'node:stream';
+import { type MultipartFile } from '@fastify/multipart';
import { Inject, Injectable } from '@nestjs/common';
import * as Sentry from '@sentry/node';
+import { AttachmentFile } from '@/server/api/endpoint-base.js';
import { DI } from '@/di-symbols.js';
import { getIpHash } from '@/misc/get-ip-hash.js';
import type { MiLocalUser, MiUser } from '@/models/User.js';
@@ -16,7 +19,7 @@ import type Logger from '@/logger.js';
import type { MiMeta, UserIpsRepository } from '@/models/_.js';
import { createTemp } from '@/misc/create-temp.js';
import { bindThis } from '@/decorators.js';
-import { RoleService } from '@/core/RoleService.js';
+import { type RolePolicies, RoleService } from '@/core/RoleService.js';
import type { Config } from '@/config.js';
import { sendRateLimitHeaders } from '@/misc/rate-limit-utils.js';
import { SkRateLimiterService } from '@/server/SkRateLimiterService.js';
@@ -191,18 +194,6 @@ export class ApiCallService implements OnApplicationShutdown {
return;
}
- const [path, cleanup] = await createTemp();
- await stream.pipeline(multipartData.file, fs.createWriteStream(path));
-
- // ファイルサイズが制限を超えていた場合
- // なお truncated はストリームを読み切ってからでないと機能しないため、stream.pipeline より後にある必要がある
- if (multipartData.file.truncated) {
- cleanup();
- reply.code(413);
- reply.send();
- return;
- }
-
const fields = {} as Record<string, unknown>;
for (const [k, v] of Object.entries(multipartData.fields)) {
fields[k] = typeof v === 'object' && 'value' in v ? v.value : undefined;
@@ -217,10 +208,7 @@ export class ApiCallService implements OnApplicationShutdown {
return;
}
this.authenticateService.authenticate(token).then(([user, app]) => {
- this.call(endpoint, user, app, fields, {
- name: multipartData.filename,
- path: path,
- }, request, reply).then((res) => {
+ this.call(endpoint, user, app, fields, multipartData, request, reply).then((res) => {
this.send(reply, res);
}).catch((err: ApiError) => {
this.#sendApiError(reply, err);
@@ -290,10 +278,7 @@ export class ApiCallService implements OnApplicationShutdown {
user: MiLocalUser | null | undefined,
token: MiAccessToken | null | undefined,
data: any,
- file: {
- name: string;
- path: string;
- } | null,
+ multipartFile: MultipartFile | null,
request: FastifyRequest<{ Body: Record<string, unknown> | undefined, Querystring: Record<string, unknown> }>,
reply: FastifyReply,
) {
@@ -369,6 +354,37 @@ export class ApiCallService implements OnApplicationShutdown {
}
}
+ // Cast non JSON input
+ if ((ep.meta.requireFile || request.method === 'GET') && ep.params.properties) {
+ for (const k of Object.keys(ep.params.properties)) {
+ const param = ep.params.properties![k];
+ if (['boolean', 'number', 'integer'].includes(param.type ?? '') && typeof data[k] === 'string') {
+ try {
+ data[k] = JSON.parse(data[k]);
+ } catch (e) {
+ throw new ApiError({
+ message: 'Invalid param.',
+ code: 'INVALID_PARAM',
+ id: '0b5f1631-7c1a-41a6-b399-cce335f34d85',
+ }, {
+ param: k,
+ reason: `cannot cast to ${param.type}`,
+ });
+ }
+ }
+ }
+ }
+
+ if (token && ((ep.meta.kind && !token.permission.some(p => p === ep.meta.kind))
+ || (!ep.meta.kind && (ep.meta.requireCredential || ep.meta.requireModerator || ep.meta.requireAdmin)))) {
+ throw new ApiError({
+ message: 'Your app does not have the necessary permissions to use this endpoint.',
+ code: 'PERMISSION_DENIED',
+ kind: 'permission',
+ id: '1370e5b7-d4eb-4566-bb1d-7748ee6a1838',
+ });
+ }
+
if ((ep.meta.requireModerator || ep.meta.requireAdmin) && (this.meta.rootUserId !== user!.id)) {
const myRoles = await this.roleService.getUserRoles(user!.id);
if (ep.meta.requireModerator && !myRoles.some(r => r.isModerator || r.isAdministrator)) {
@@ -402,47 +418,89 @@ export class ApiCallService implements OnApplicationShutdown {
}
}
- if (token && ((ep.meta.kind && !token.permission.some(p => p === ep.meta.kind))
- || (!ep.meta.kind && (ep.meta.requireCredential || ep.meta.requireModerator || ep.meta.requireAdmin)))) {
- throw new ApiError({
- message: 'Your app does not have the necessary permissions to use this endpoint.',
- code: 'PERMISSION_DENIED',
- kind: 'permission',
- id: '1370e5b7-d4eb-4566-bb1d-7748ee6a1838',
- });
- }
-
- // Cast non JSON input
- if ((ep.meta.requireFile || request.method === 'GET') && ep.params.properties) {
- for (const k of Object.keys(ep.params.properties)) {
- const param = ep.params.properties![k];
- if (['boolean', 'number', 'integer'].includes(param.type ?? '') && typeof data[k] === 'string') {
- try {
- data[k] = JSON.parse(data[k]);
- } catch (e) {
- throw new ApiError({
- message: 'Invalid param.',
- code: 'INVALID_PARAM',
- id: '0b5f1631-7c1a-41a6-b399-cce335f34d85',
- }, {
- param: k,
- reason: `cannot cast to ${param.type}`,
- });
- }
- }
- }
+ let attachmentFile: AttachmentFile | null = null;
+ let cleanup = () => {};
+ if (ep.meta.requireFile && request.method === 'POST' && multipartFile) {
+ const policies = await this.roleService.getUserPolicies(user!.id);
+ const result = await this.handleAttachmentFile(
+ Math.min((policies.maxFileSizeMb * 1024 * 1024), this.config.maxFileSize),
+ multipartFile,
+ );
+ attachmentFile = result.attachmentFile;
+ cleanup = result.cleanup;
}
// API invoking
if (this.config.sentryForBackend) {
return await Sentry.startSpan({
name: 'API: ' + ep.name,
- }, () => ep.exec(data, user, token, file, request.ip, request.headers)
- .catch((err: Error) => this.#onExecError(ep, data, err, user?.id)));
+ }, () => {
+ return ep.exec(data, user, token, attachmentFile, request.ip, request.headers)
+ .catch((err: Error) => this.#onExecError(ep, data, err, user?.id))
+ .finally(() => cleanup());
+ });
} else {
- return await ep.exec(data, user, token, file, request.ip, request.headers)
- .catch((err: Error) => this.#onExecError(ep, data, err, user?.id));
+ return await ep.exec(data, user, token, attachmentFile, request.ip, request.headers)
+ .catch((err: Error) => this.#onExecError(ep, data, err, user?.id))
+ .finally(() => cleanup());
+ }
+ }
+
+ @bindThis
+ private async handleAttachmentFile(
+ fileSizeLimit: number,
+ multipartFile: MultipartFile,
+ ) {
+ function createTooLongError() {
+ return new ApiError({
+ httpStatusCode: 413,
+ kind: 'client',
+ message: 'File size is too large.',
+ code: 'FILE_SIZE_TOO_LARGE',
+ id: 'ff827ce8-9b4b-4808-8511-422222a3362f',
+ });
+ }
+
+ function createLimitStream(limit: number) {
+ let total = 0;
+
+ return new Transform({
+ transform(chunk, _, callback) {
+ total += chunk.length;
+ if (total > limit) {
+ callback(createTooLongError());
+ } else {
+ callback(null, chunk);
+ }
+ },
+ });
}
+
+ const [path, cleanup] = await createTemp();
+ try {
+ await stream.pipeline(
+ multipartFile.file,
+ createLimitStream(fileSizeLimit),
+ fs.createWriteStream(path),
+ );
+
+ // ファイルサイズが制限を超えていた場合
+ // なお truncated はストリームを読み切ってからでないと機能しないため、stream.pipeline より後にある必要がある
+ if (multipartFile.file.truncated) {
+ throw createTooLongError();
+ }
+ } catch (err) {
+ cleanup();
+ throw err;
+ }
+
+ return {
+ attachmentFile: {
+ name: multipartFile.filename,
+ path,
+ },
+ cleanup,
+ };
}
@bindThis
diff --git a/packages/backend/src/server/api/endpoint-base.ts b/packages/backend/src/server/api/endpoint-base.ts
index e061aa3a8e..b063487305 100644
--- a/packages/backend/src/server/api/endpoint-base.ts
+++ b/packages/backend/src/server/api/endpoint-base.ts
@@ -21,23 +21,23 @@ ajv.addFormat('misskey:id', /^[a-zA-Z0-9]+$/);
export type Response = Record<string, any> | void;
-type File = {
+export type AttachmentFile = {
name: string | null;
path: string;
};
// TODO: paramsの型をT['params']のスキーマ定義から推論する
type Executor<T extends IEndpointMeta, Ps extends Schema> =
- (params: SchemaType<Ps>, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: File, cleanup?: () => any, ip?: string | null, headers?: Record<string, string> | null) =>
- Promise<T['res'] extends undefined ? Response : SchemaType<NonNullable<T['res']>>>;
+ (params: SchemaType<Ps>, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: AttachmentFile, cleanup?: () => any, ip?: string | null, headers?: Record<string, string> | null) =>
+ Promise<T['res'] extends undefined ? Response : SchemaType<NonNullable<T['res']>>>;
export abstract class Endpoint<T extends IEndpointMeta, Ps extends Schema> {
- public exec: (params: any, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: File, ip?: string | null, headers?: Record<string, string> | null) => Promise<any>;
+ public exec: (params: any, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: AttachmentFile, ip?: string | null, headers?: Record<string, string> | null) => Promise<any>;
constructor(meta: T, paramDef: Ps, cb: Executor<T, Ps>) {
const validate = ajv.compile(paramDef);
- this.exec = (params: any, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: File, ip?: string | null, headers?: Record<string, string> | null) => {
+ this.exec = (params: any, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: AttachmentFile, ip?: string | null, headers?: Record<string, string> | null) => {
let cleanup: undefined | (() => void) = undefined;
if (meta.requireFile) {
diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts
index 57c62b7c89..b90ba6aa0d 100644
--- a/packages/backend/src/server/api/endpoints/antennas/notes.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts
@@ -117,6 +117,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
// NOTE: センシティブ除外の設定はこのエンドポイントでは無視する。
// https://github.com/misskey-dev/misskey/pull/15346#discussion_r1929950255
+ this.queryService.generateBlockedHostQueryForNote(query);
this.queryService.generateVisibilityQuery(query, me);
this.queryService.generateMutedUserQueryForNotes(query, me);
this.queryService.generateBlockedUserQueryForNotes(query, me);
diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts
index 5fc0ae00b2..6336f43e9f 100644
--- a/packages/backend/src/server/api/endpoints/channels/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts
@@ -137,6 +137,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('renote.user', 'renoteUser')
.leftJoinAndSelect('note.channel', 'channel');
+ this.queryService.generateBlockedHostQueryForNote(query);
if (me) {
this.queryService.generateMutedUserQueryForNotes(query, me);
this.queryService.generateBlockedUserQueryForNotes(query, me);
diff --git a/packages/backend/src/server/api/endpoints/chat/messages/delete.ts b/packages/backend/src/server/api/endpoints/chat/messages/delete.ts
index 63b75fb6a7..52a054303b 100644
--- a/packages/backend/src/server/api/endpoints/chat/messages/delete.ts
+++ b/packages/backend/src/server/api/endpoints/chat/messages/delete.ts
@@ -16,9 +16,6 @@ export const meta = {
kind: 'write:chat',
- res: {
- },
-
errors: {
noSuchMessage: {
message: 'No such message.',
diff --git a/packages/backend/src/server/api/endpoints/chat/messages/react.ts b/packages/backend/src/server/api/endpoints/chat/messages/react.ts
index 5f61e7e992..2197e7bf80 100644
--- a/packages/backend/src/server/api/endpoints/chat/messages/react.ts
+++ b/packages/backend/src/server/api/endpoints/chat/messages/react.ts
@@ -16,9 +16,6 @@ export const meta = {
kind: 'write:chat',
- res: {
- },
-
errors: {
noSuchMessage: {
message: 'No such message.',
diff --git a/packages/backend/src/server/api/endpoints/chat/messages/unreact.ts b/packages/backend/src/server/api/endpoints/chat/messages/unreact.ts
index 6784bb6ecf..adfcd232f9 100644
--- a/packages/backend/src/server/api/endpoints/chat/messages/unreact.ts
+++ b/packages/backend/src/server/api/endpoints/chat/messages/unreact.ts
@@ -16,9 +16,6 @@ export const meta = {
kind: 'write:chat',
- res: {
- },
-
errors: {
noSuchMessage: {
message: 'No such message.',
diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/delete.ts b/packages/backend/src/server/api/endpoints/chat/rooms/delete.ts
index 82a8e1f30d..1ea81448c1 100644
--- a/packages/backend/src/server/api/endpoints/chat/rooms/delete.ts
+++ b/packages/backend/src/server/api/endpoints/chat/rooms/delete.ts
@@ -16,9 +16,6 @@ export const meta = {
kind: 'write:chat',
- res: {
- },
-
errors: {
noSuchRoom: {
message: 'No such room.',
diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/invitations/ignore.ts b/packages/backend/src/server/api/endpoints/chat/rooms/invitations/ignore.ts
index b8a228089b..88ea234527 100644
--- a/packages/backend/src/server/api/endpoints/chat/rooms/invitations/ignore.ts
+++ b/packages/backend/src/server/api/endpoints/chat/rooms/invitations/ignore.ts
@@ -16,9 +16,6 @@ export const meta = {
kind: 'write:chat',
- res: {
- },
-
errors: {
noSuchRoom: {
message: 'No such room.',
diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/join.ts b/packages/backend/src/server/api/endpoints/chat/rooms/join.ts
index d561f9e03f..550b4da1a6 100644
--- a/packages/backend/src/server/api/endpoints/chat/rooms/join.ts
+++ b/packages/backend/src/server/api/endpoints/chat/rooms/join.ts
@@ -16,9 +16,6 @@ export const meta = {
kind: 'write:chat',
- res: {
- },
-
errors: {
noSuchRoom: {
message: 'No such room.',
diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/leave.ts b/packages/backend/src/server/api/endpoints/chat/rooms/leave.ts
index a3ad0c2d6f..f99b408d67 100644
--- a/packages/backend/src/server/api/endpoints/chat/rooms/leave.ts
+++ b/packages/backend/src/server/api/endpoints/chat/rooms/leave.ts
@@ -16,9 +16,6 @@ export const meta = {
kind: 'write:chat',
- res: {
- },
-
errors: {
noSuchRoom: {
message: 'No such room.',
diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/mute.ts b/packages/backend/src/server/api/endpoints/chat/rooms/mute.ts
index 11cbe7b8b9..ee60f92505 100644
--- a/packages/backend/src/server/api/endpoints/chat/rooms/mute.ts
+++ b/packages/backend/src/server/api/endpoints/chat/rooms/mute.ts
@@ -16,9 +16,6 @@ export const meta = {
kind: 'write:chat',
- res: {
- },
-
errors: {
noSuchRoom: {
message: 'No such room.',
diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts
index 69ff45a1c6..59513e530d 100644
--- a/packages/backend/src/server/api/endpoints/clips/notes.ts
+++ b/packages/backend/src/server/api/endpoints/clips/notes.ts
@@ -91,6 +91,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('renote.user', 'renoteUser')
.andWhere('clipNote.clipId = :clipId', { clipId: clip.id });
+ this.queryService.generateBlockedHostQueryForNote(query);
if (me) {
this.queryService.generateVisibilityQuery(query, me);
this.queryService.generateMutedUserQueryForNotes(query, me);
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 f67ff6ddc4..7043f4883a 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/create.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/create.ts
@@ -62,6 +62,12 @@ export const meta = {
code: 'COMMENT_TOO_LONG',
id: '333652d9-0826-40f5-a2c3-e2bedcbb9fe5',
},
+
+ maxFileSizeExceeded: {
+ message: 'Cannot upload the file because it exceeds the maximum file size.',
+ code: 'MAX_FILE_SIZE_EXCEEDED',
+ id: 'b9d8c348-33f0-4673-b9a9-5d4da058977a',
+ },
},
} as const;
@@ -128,6 +134,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (err instanceof IdentifiableError) {
if (err.id === '282f77bf-5816-4f72-9264-aa14d8261a21') throw new ApiError(meta.errors.inappropriate);
if (err.id === 'c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6') throw new ApiError(meta.errors.noFreeSpace);
+ if (err.id === 'f9e4e5f3-4df4-40b5-b400-f236945f7073') throw new ApiError(meta.errors.maxFileSizeExceeded);
}
throw new ApiError();
} finally {
diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts
index c97a0c0bc7..8f19d534d4 100644
--- a/packages/backend/src/server/api/endpoints/notes/children.ts
+++ b/packages/backend/src/server/api/endpoints/notes/children.ts
@@ -79,6 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
+ this.queryService.generateBlockedHostQueryForNote(query);
if (me) {
this.queryService.generateMutedUserQueryForNotes(query, me);
this.queryService.generateBlockedUserQueryForNotes(query, me);
diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts
index 4853489827..734ff31700 100644
--- a/packages/backend/src/server/api/endpoints/notes/featured.ts
+++ b/packages/backend/src/server/api/endpoints/notes/featured.ts
@@ -11,6 +11,7 @@ import { DI } from '@/di-symbols.js';
import { FeaturedService } from '@/core/FeaturedService.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { CacheService } from '@/core/CacheService.js';
+import { QueryService } from '@/core/QueryService.js';
export const meta = {
tags: ['notes'],
@@ -58,6 +59,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private cacheService: CacheService,
private noteEntityService: NoteEntityService,
private featuredService: FeaturedService,
+ private queryService: QueryService,
) {
super(meta, paramDef, async (ps, me) => {
let noteIds: string[];
@@ -100,6 +102,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('renote.user', 'renoteUser')
.leftJoinAndSelect('note.channel', 'channel');
+ this.queryService.generateBlockedHostQueryForNote(query);
+
const notes = (await query.getMany()).filter(note => {
if (me && isUserRelated(note, userIdsWhoBlockingMe)) return false;
if (me && isUserRelated(note, userIdsWhoMeMuting)) return false;
diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
index b0e3327411..6461a2e33f 100644
--- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -254,6 +254,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
this.queryService.generateVisibilityQuery(query, me);
+ this.queryService.generateBlockedHostQueryForNote(query);
this.queryService.generateMutedUserQueryForNotes(query, me);
this.queryService.generateBlockedUserQueryForNotes(query, me);
this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
index 864592ed9b..f55853f3f3 100644
--- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
@@ -167,6 +167,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
+ this.queryService.generateBlockedHostQueryForNote(query);
if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts
index bfcd120f49..269b57366c 100644
--- a/packages/backend/src/server/api/endpoints/notes/mentions.ts
+++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts
@@ -78,6 +78,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
+ this.queryService.generateBlockedHostQueryForNote(query);
this.queryService.generateMutedUserQueryForNotes(query, me);
this.queryService.generateMutedNoteThreadQuery(query, me);
this.queryService.generateBlockedUserQueryForNotes(query, me);
diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts
index 01f09433a9..0f08cc9cf2 100644
--- a/packages/backend/src/server/api/endpoints/notes/renotes.ts
+++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts
@@ -91,6 +91,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
this.queryService.generateVisibilityQuery(query, me);
+ this.queryService.generateBlockedHostQueryForNote(query);
if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts
index f04c9b0ec4..0882e19182 100644
--- a/packages/backend/src/server/api/endpoints/notes/replies.ts
+++ b/packages/backend/src/server/api/endpoints/notes/replies.ts
@@ -62,6 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
+ this.queryService.generateBlockedHostQueryForNote(query);
if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts
index af9bc3b426..91874a8195 100644
--- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts
+++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts
@@ -97,6 +97,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (!this.serverSettings.enableBotTrending) query.andWhere('user.isBot = FALSE');
this.queryService.generateVisibilityQuery(query, me);
+ this.queryService.generateBlockedHostQueryForNote(query);
if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts
index 9f19117426..a2dfa7fdac 100644
--- a/packages/backend/src/server/api/endpoints/notes/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts
@@ -209,6 +209,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}));
this.queryService.generateVisibilityQuery(query, me);
+ this.queryService.generateBlockedHostQueryForNote(query);
this.queryService.generateMutedUserQueryForNotes(query, me);
this.queryService.generateBlockedUserQueryForNotes(query, me);
this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
index 4c37edfdec..60f18a09b0 100644
--- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -190,6 +190,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}));
this.queryService.generateVisibilityQuery(query, me);
+ this.queryService.generateBlockedHostQueryForNote(query);
this.queryService.generateMutedUserQueryForNotes(query, me);
this.queryService.generateBlockedUserQueryForNotes(query, me);
this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts
index dd40e49d3d..d1c2e4b686 100644
--- a/packages/backend/src/server/api/endpoints/roles/notes.ts
+++ b/packages/backend/src/server/api/endpoints/roles/notes.ts
@@ -108,6 +108,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
+ this.queryService.generateBlockedHostQueryForNote(query);
this.queryService.generateMutedUserQueryForNotes(query, me);
this.queryService.generateBlockedUserQueryForNotes(query, me);
diff --git a/packages/backend/src/server/api/endpoints/users/featured-notes.ts b/packages/backend/src/server/api/endpoints/users/featured-notes.ts
index e6acae08b1..3fb091cc0e 100644
--- a/packages/backend/src/server/api/endpoints/users/featured-notes.ts
+++ b/packages/backend/src/server/api/endpoints/users/featured-notes.ts
@@ -11,6 +11,7 @@ import { DI } from '@/di-symbols.js';
import { FeaturedService } from '@/core/FeaturedService.js';
import { CacheService } from '@/core/CacheService.js';
import { isUserRelated } from '@/misc/is-user-related.js';
+import { QueryService } from '@/core/QueryService.js';
export const meta = {
tags: ['notes'],
@@ -55,6 +56,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private noteEntityService: NoteEntityService,
private featuredService: FeaturedService,
private cacheService: CacheService,
+ private queryService: QueryService,
) {
super(meta, paramDef, async (ps, me) => {
const userIdsWhoBlockingMe = me ? await this.cacheService.userBlockedCache.fetch(me.id) : new Set<string>();
@@ -91,6 +93,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('renote.user', 'renoteUser')
.leftJoinAndSelect('note.channel', 'channel');
+ this.queryService.generateBlockedHostQueryForNote(query);
+
const notes = (await query.getMany()).filter(note => {
if (me && isUserRelated(note, userIdsWhoBlockingMe, false)) return false;
if (me && isUserRelated(note, userIdsWhoMeMuting, true)) return false;
diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts
index a4eee544f0..965baa859a 100644
--- a/packages/backend/src/server/api/endpoints/users/notes.ts
+++ b/packages/backend/src/server/api/endpoints/users/notes.ts
@@ -145,6 +145,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
redisTimelines,
useDbFallback: true,
ignoreAuthorFromMute: true,
+ ignoreAuthorFromInstanceBlock: true,
excludeReplies: ps.withChannelNotes && !ps.withReplies, // userTimelineWithChannel may include replies
excludeNoFiles: ps.withChannelNotes && ps.withFiles, // userTimelineWithChannel may include notes without files
excludePureRenotes: !ps.withRenotes,
@@ -216,6 +217,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
this.queryService.generateVisibilityQuery(query, me);
+ this.queryService.generateBlockedHostQueryForNote(query, true);
if (me) {
this.queryService.generateMutedUserQueryForNotes(query, me, { id: ps.userId });
this.queryService.generateBlockedUserQueryForNotes(query, me);
diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts
index 49c1190197..56f59bd285 100644
--- a/packages/backend/src/server/api/endpoints/users/reactions.ts
+++ b/packages/backend/src/server/api/endpoints/users/reactions.ts
@@ -108,6 +108,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('reaction.note', 'note');
this.queryService.generateVisibilityQuery(query, me);
+ this.queryService.generateBlockedHostQueryForNote(query);
const reactions = (await query
.limit(ps.limit)
diff --git a/packages/backend/src/server/web/style.css b/packages/backend/src/server/web/style.css
index 8094a0f6de..1c63d77e06 100644
--- a/packages/backend/src/server/web/style.css
+++ b/packages/backend/src/server/web/style.css
@@ -31,6 +31,7 @@ html {
margin: auto;
width: 64px;
height: 64px;
+ border-radius: 10px;
pointer-events: none;
}
diff --git a/packages/backend/src/server/web/style.embed.css b/packages/backend/src/server/web/style.embed.css
index 5e8786cc4e..0911d562bf 100644
--- a/packages/backend/src/server/web/style.embed.css
+++ b/packages/backend/src/server/web/style.embed.css
@@ -53,6 +53,7 @@ html.embed.noborder #splash {
margin: auto;
width: 64px;
height: 64px;
+ border-radius: 10px;
pointer-events: none;
}
diff --git a/packages/backend/test-federation/compose.tpl.yml b/packages/backend/test-federation/compose.tpl.yml
index 25770063d3..a7e907c3ee 100644
--- a/packages/backend/test-federation/compose.tpl.yml
+++ b/packages/backend/test-federation/compose.tpl.yml
@@ -75,10 +75,6 @@ services:
target: /misskey/pnpm-workspace.yaml
read_only: true
- type: bind
- source: ../../../scripts/dependency-patches
- target: /misskey/scripts/dependency-patches
- read_only: true
- - type: bind
source: ./certificates/rootCA.crt
target: /usr/local/share/ca-certificates/rootCA.crt
read_only: true
diff --git a/packages/backend/test-federation/compose.yml b/packages/backend/test-federation/compose.yml
index dfa51b940a..4df4ced365 100644
--- a/packages/backend/test-federation/compose.yml
+++ b/packages/backend/test-federation/compose.yml
@@ -71,10 +71,6 @@ services:
target: /misskey/pnpm-workspace.yaml
read_only: true
- type: bind
- source: ../../../scripts/dependency-patches
- target: /misskey/scripts/dependency-patches
- read_only: true
- - type: bind
source: ./certificates/rootCA.crt
target: /usr/local/share/ca-certificates/rootCA.crt
read_only: true
@@ -118,10 +114,6 @@ services:
source: ../../../pnpm-workspace.yaml
target: /misskey/pnpm-workspace.yaml
read_only: true
- - type: bind
- source: ../../../scripts/dependency-patches
- target: /misskey/scripts/dependency-patches
- read_only: true
working_dir: /misskey
command: >
bash -c "
diff --git a/packages/backend/test/e2e/api.ts b/packages/backend/test/e2e/api.ts
index 49c6a0636b..f9e65aaa84 100644
--- a/packages/backend/test/e2e/api.ts
+++ b/packages/backend/test/e2e/api.ts
@@ -159,8 +159,8 @@ describe('API', () => {
user: { token: application3 },
}, {
status: 403,
- code: 'ROLE_PERMISSION_DENIED',
- id: 'c3d38592-54c0-429d-be96-5636b0431a61',
+ code: 'PERMISSION_DENIED',
+ id: '1370e5b7-d4eb-4566-bb1d-7748ee6a1838',
});
await failedApiCall({
diff --git a/packages/backend/test/unit/server/api/drive/files/create.ts b/packages/backend/test/unit/server/api/drive/files/create.ts
new file mode 100644
index 0000000000..b98892fa03
--- /dev/null
+++ b/packages/backend/test/unit/server/api/drive/files/create.ts
@@ -0,0 +1,108 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { S3Client } from '@aws-sdk/client-s3';
+import { Test, TestingModule } from '@nestjs/testing';
+import { mockClient } from 'aws-sdk-client-mock';
+import { FastifyInstance } from 'fastify';
+import request from 'supertest';
+import { CoreModule } from '@/core/CoreModule.js';
+import { RoleService } from '@/core/RoleService.js';
+import { DI } from '@/di-symbols.js';
+import { GlobalModule } from '@/GlobalModule.js';
+import { MiRole, UserProfilesRepository, UsersRepository } from '@/models/_.js';
+import { MiUser } from '@/models/User.js';
+import { ServerModule } from '@/server/ServerModule.js';
+import { ServerService } from '@/server/ServerService.js';
+
+describe('/drive/files/create', () => {
+ let module: TestingModule;
+ let server: FastifyInstance;
+ const s3Mock = mockClient(S3Client);
+ let roleService: RoleService;
+
+ let root: MiUser;
+ let role_tinyAttachment: MiRole;
+
+ beforeAll(async () => {
+ module = await Test.createTestingModule({
+ imports: [GlobalModule, CoreModule, ServerModule],
+ }).compile();
+ module.enableShutdownHooks();
+
+ const serverService = module.get<ServerService>(ServerService);
+ server = await serverService.launch();
+
+ const usersRepository = module.get<UsersRepository>(DI.usersRepository);
+ root = await usersRepository.insert({
+ id: 'root',
+ username: 'root',
+ usernameLower: 'root',
+ token: '1234567890123456',
+ }).then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
+
+ const userProfilesRepository = module.get<UserProfilesRepository>(DI.userProfilesRepository);
+ await userProfilesRepository.insert({
+ userId: root.id,
+ });
+
+ roleService = module.get<RoleService>(RoleService);
+ role_tinyAttachment = await roleService.create({
+ name: 'test-role001',
+ description: 'Test role001 description',
+ target: 'manual',
+ policies: {
+ maxFileSizeMb: {
+ useDefault: false,
+ priority: 1,
+ // 10byte
+ value: 10 / 1024 / 1024,
+ },
+ },
+ });
+ });
+
+ beforeEach(async () => {
+ s3Mock.reset();
+ await roleService.unassign(root.id, role_tinyAttachment.id).catch(() => {});
+ });
+
+ afterAll(async () => {
+ await server.close();
+ await module.close();
+ });
+
+ test('200 ok', async () => {
+ const result = await request(server.server)
+ .post('/api/drive/files/create')
+ .set('Content-Type', 'multipart/form-data')
+ .set('Authorization', `Bearer ${root.token}`)
+ .attach('file', Buffer.from('a'.repeat(1024 * 1024)));
+ expect(result.statusCode).toBe(200);
+ });
+
+ test('200 ok(with role)', async () => {
+ await roleService.assign(root.id, role_tinyAttachment.id);
+
+ const result = await request(server.server)
+ .post('/api/drive/files/create')
+ .set('Content-Type', 'multipart/form-data')
+ .set('Authorization', `Bearer ${root.token}`)
+ .attach('file', Buffer.from('a'.repeat(10)));
+ expect(result.statusCode).toBe(200);
+ });
+
+ test('413 too large', async () => {
+ await roleService.assign(root.id, role_tinyAttachment.id);
+
+ const result = await request(server.server)
+ .post('/api/drive/files/create')
+ .set('Content-Type', 'multipart/form-data')
+ .set('Authorization', `Bearer ${root.token}`)
+ .attach('file', Buffer.from('a'.repeat(11)));
+ expect(result.statusCode).toBe(413);
+ expect(result.body.error.code).toBe('FILE_SIZE_TOO_LARGE');
+ });
+});
diff --git a/packages/frontend-embed/package.json b/packages/frontend-embed/package.json
index 40e8802ab4..7788d65305 100644
--- a/packages/frontend-embed/package.json
+++ b/packages/frontend-embed/package.json
@@ -26,15 +26,15 @@
"json5": "2.2.3",
"misskey-js": "workspace:*",
"punycode.js": "2.3.1",
- "rollup": "4.39.0",
- "sass": "1.86.3",
- "shiki": "3.2.2",
+ "rollup": "4.40.0",
+ "sass": "1.87.0",
+ "shiki": "3.3.0",
"tinycolor2": "1.6.0",
"tsc-alias": "1.8.15",
"tsconfig-paths": "4.2.0",
"typescript": "5.8.3",
"uuid": "11.1.0",
- "vite": "6.3.1",
+ "vite": "6.3.3",
"vue": "3.5.13"
},
"devDependencies": {
@@ -42,13 +42,13 @@
"@testing-library/vue": "8.1.0",
"@types/estree": "1.0.7",
"@types/micromatch": "4.0.9",
- "@types/node": "22.14.0",
+ "@types/node": "22.15.2",
"@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1",
- "@typescript-eslint/eslint-plugin": "8.29.1",
- "@typescript-eslint/parser": "8.29.1",
- "@vitest/coverage-v8": "3.1.1",
+ "@typescript-eslint/eslint-plugin": "8.31.0",
+ "@typescript-eslint/parser": "8.31.0",
+ "@vitest/coverage-v8": "3.1.2",
"@vue/runtime-core": "3.5.13",
"acorn": "8.14.1",
"cross-env": "7.0.3",
@@ -58,13 +58,13 @@
"happy-dom": "17.4.4",
"intersection-observer": "0.12.2",
"micromatch": "4.0.8",
- "msw": "2.7.3",
- "nodemon": "3.1.9",
+ "msw": "2.7.5",
+ "nodemon": "3.1.10",
"prettier": "3.5.3",
"start-server-and-test": "2.0.11",
"vite-plugin-turbosnap": "1.0.3",
- "vue-component-type-helpers": "2.2.8",
+ "vue-component-type-helpers": "2.2.10",
"vue-eslint-parser": "10.1.3",
- "vue-tsc": "2.2.8"
+ "vue-tsc": "2.2.10"
}
}
diff --git a/packages/frontend-shared/js/collapsed.ts b/packages/frontend-shared/js/collapsed.ts
index af1f88cb73..aa24c43bcb 100644
--- a/packages/frontend-shared/js/collapsed.ts
+++ b/packages/frontend-shared/js/collapsed.ts
@@ -6,17 +6,30 @@
import * as Misskey from 'misskey-js';
export function shouldCollapsed(note: Misskey.entities.Note, urls: string[]): boolean {
- const collapsed = note.cw == null && (
- (note.text != null && (
- (note.text.includes('$[x2')) ||
- (note.text.includes('$[x3')) ||
- (note.text.includes('$[x4')) ||
- (note.text.includes('$[scale')) ||
- (note.text.split('\n').length > 9) ||
- (note.text.length > 500) ||
- (urls.length >= 4)
- )) || (note.files != null && note.files.length >= 5)
- );
+ if (note.cw != null) {
+ return false;
+ }
- return collapsed;
+ if (note.text != null) {
+ if (
+ note.text.includes('$[x2') ||
+ note.text.includes('$[x3') ||
+ note.text.includes('$[x4') ||
+ note.text.includes('$[scale') ||
+ note.text.split('\n').length > 9 ||
+ note.text.length > 500
+ ) {
+ return true;
+ }
+ }
+
+ if (urls.length >= 4) {
+ return true;
+ }
+
+ if (note.files != null && note.files.length >= 5) {
+ return true;
+ }
+
+ return false;
}
diff --git a/packages/frontend-shared/js/const.ts b/packages/frontend-shared/js/const.ts
index 22e4e36292..0aba2b486e 100644
--- a/packages/frontend-shared/js/const.ts
+++ b/packages/frontend-shared/js/const.ts
@@ -157,6 +157,7 @@ export const ROLE_POLICIES = [
'canUseTranslator',
'canHideAds',
'driveCapacityMb',
+ 'maxFileSizeMb',
'alwaysMarkNsfw',
'canUpdateBioMedia',
'pinLimit',
diff --git a/packages/frontend-shared/package.json b/packages/frontend-shared/package.json
index a4874b8f77..f129121d19 100644
--- a/packages/frontend-shared/package.json
+++ b/packages/frontend-shared/package.json
@@ -21,12 +21,12 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
- "@types/node": "22.14.0",
- "@typescript-eslint/eslint-plugin": "8.29.1",
- "@typescript-eslint/parser": "8.29.1",
- "esbuild": "0.25.2",
+ "@types/node": "22.15.2",
+ "@typescript-eslint/eslint-plugin": "8.31.0",
+ "@typescript-eslint/parser": "8.31.0",
+ "esbuild": "0.25.3",
"eslint-plugin-vue": "10.0.0",
- "nodemon": "3.1.9",
+ "nodemon": "3.1.10",
"typescript": "5.8.3",
"vue-eslint-parser": "10.1.3"
},
diff --git a/packages/frontend/.storybook/changes.ts b/packages/frontend/.storybook/changes.ts
index 1299910499..c7e0048818 100644
--- a/packages/frontend/.storybook/changes.ts
+++ b/packages/frontend/.storybook/changes.ts
@@ -55,7 +55,7 @@ await fs.readFile(
'../../locales/ja-JP.yml',
'assets/**',
'public/**',
- '../../pnpm-lock.yaml',
+ 'package.json',
]).length
) {
return;
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index d3453fb8cb..4810d40fc6 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -25,7 +25,7 @@
"@rollup/plugin-replace": "6.0.2",
"@rollup/pluginutils": "5.1.4",
"@ruffle-rs/ruffle": "0.1.0-nightly.2024.10.15",
- "@sentry/vue": "9.12.0",
+ "@sentry/vue": "9.14.0",
"@syuilo/aiscript": "0.19.0",
"@transfem-org/sfm-js": "0.24.6",
"@twemoji/parser": "15.1.1",
@@ -36,12 +36,12 @@
"broadcast-channel": "7.1.0",
"buraha": "0.0.1",
"canvas-confetti": "1.9.3",
- "chart.js": "4.4.8",
+ "chart.js": "4.4.9",
"chartjs-adapter-date-fns": "3.0.0",
"chartjs-chart-matrix": "2.1.1",
"chartjs-plugin-gradient": "0.6.1",
"chartjs-plugin-zoom": "2.2.0",
- "chromatic": "11.28.0",
+ "chromatic": "11.28.2",
"compare-versions": "6.1.1",
"cropperjs": "2.0.0",
"date-fns": "4.1.0",
@@ -61,13 +61,13 @@
"moment": "^2.30.1",
"photoswipe": "5.4.4",
"punycode.js": "2.3.1",
- "rollup": "4.39.0",
- "sanitize-html": "2.15.0",
- "sass": "1.86.3",
- "shiki": "3.2.2",
+ "rollup": "4.40.0",
+ "sanitize-html": "2.16.0",
+ "sass": "1.87.0",
+ "shiki": "3.3.0",
"strict-event-emitter-types": "2.0.0",
"textarea-caret": "3.1.0",
- "three": "0.175.0",
+ "three": "0.176.0",
"throttle-debounce": "5.0.2",
"tinycolor2": "1.6.0",
"tsc-alias": "1.8.15",
@@ -75,13 +75,13 @@
"typescript": "5.8.3",
"uuid": "11.1.0",
"v-code-diff": "1.13.1",
- "vite": "6.3.1",
+ "vite": "6.3.3",
"vue": "3.5.13",
"vuedraggable": "next",
"wanakana": "5.3.1"
},
"optionalDependencies": {
- "cypress": "13.15.2"
+ "cypress": "14.3.2"
},
"devDependencies": {
"@misskey-dev/summaly": "5.2.1",
@@ -109,16 +109,16 @@
"@types/katex": "^0.16.7",
"@types/matter-js": "0.19.8",
"@types/micromatch": "4.0.9",
- "@types/node": "22.14.0",
+ "@types/node": "22.15.2",
"@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/sanitize-html": "2.15.0",
"@types/seedrandom": "3.0.8",
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1",
- "@typescript-eslint/eslint-plugin": "8.29.1",
- "@typescript-eslint/parser": "8.29.1",
- "@vitest/coverage-v8": "3.1.1",
+ "@typescript-eslint/eslint-plugin": "8.31.0",
+ "@typescript-eslint/parser": "8.31.0",
+ "@vitest/coverage-v8": "3.1.2",
"@vue/compiler-core": "3.5.13",
"@vue/runtime-core": "3.5.13",
"acorn": "8.14.1",
@@ -130,9 +130,9 @@
"intersection-observer": "0.12.2",
"micromatch": "4.0.8",
"minimatch": "10.0.1",
- "msw": "2.7.3",
+ "msw": "2.7.5",
"msw-storybook-addon": "2.0.4",
- "nodemon": "3.1.9",
+ "nodemon": "3.1.10",
"prettier": "3.5.3",
"react": "19.1.0",
"react-dom": "19.1.0",
@@ -141,10 +141,10 @@
"storybook": "8.6.12",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"vite-plugin-turbosnap": "1.0.3",
- "vitest": "3.1.1",
+ "vitest": "3.1.2",
"vitest-fetch-mock": "0.4.5",
- "vue-component-type-helpers": "2.2.8",
+ "vue-component-type-helpers": "2.2.10",
"vue-eslint-parser": "10.1.3",
- "vue-tsc": "2.2.8"
+ "vue-tsc": "2.2.10"
}
}
diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue
index dbac5e9dd7..61297fdc76 100644
--- a/packages/frontend/src/components/MkAbuseReportWindow.vue
+++ b/packages/frontend/src/components/MkAbuseReportWindow.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</I18n>
</template>
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<div class="_gaps_m" :class="$style.root">
<div class="">
<MkTextarea v-model="comment">
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton primary full :disabled="comment.length === 0" @click="send">{{ i18n.ts.send }}</MkButton>
</div>
</div>
- </MkSpacer>
+ </div>
</MkWindow>
</template>
diff --git a/packages/frontend/src/components/MkAntennaEditor.vue b/packages/frontend/src/components/MkAntennaEditor.vue
index 59099d54bd..e2febf7225 100644
--- a/packages/frontend/src/components/MkAntennaEditor.vue
+++ b/packages/frontend/src/components/MkAntennaEditor.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer :contentMax="700">
+<div class="_spacer" style="--MI_SPACER-w: 700px;">
<div>
<div class="_gaps_m">
<MkInput v-model="name">
@@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</div>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
index 54fda6bf7c..ed5a20b4eb 100644
--- a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
+++ b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkModalWindow ref="dialogEl" @close="cancel()" @closed="emit('closed')">
<template #header>:{{ emoji.name }}:</template>
<template #default>
- <MkSpacer>
+ <div class="_spacer">
<div style="display: flex; flex-direction: column; gap: 1em;">
<div :class="$style.emojiImgWrapper">
<MkCustomEmoji :name="emoji.name" :normal="true" :useOriginalSize="true" style="height: 100%;"></MkCustomEmoji>
@@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</MkKeyValue>
</div>
- </MkSpacer>
+ </div>
</template>
</MkModalWindow>
</template>
diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue
index cb7c270e91..3627704c1b 100644
--- a/packages/frontend/src/components/MkDrive.vue
+++ b/packages/frontend/src/components/MkDrive.vue
@@ -52,6 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@contextmenu.stop="onContextmenu"
>
<div ref="contents">
+ <MkInfo v-if="!store.r.readDriveTip.value" closable @close="closeTip()"><div v-html="i18n.ts.driveAboutTip"></div></MkInfo>
<div v-show="folders.length > 0" ref="foldersContainer" :class="$style.folders">
<XFolder
v-for="(f, i) in folders"
@@ -108,6 +109,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { nextTick, onActivated, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue';
import * as Misskey from 'misskey-js';
import MkButton from './MkButton.vue';
+import MkInfo from './MkInfo.vue';
import type { MenuItem } from '@/types/menu.js';
import XNavFolder from '@/components/MkDrive.navFolder.vue';
import XFolder from '@/components/MkDrive.folder.vue';
@@ -121,6 +123,7 @@ import { uploadFile, uploads } from '@/utility/upload.js';
import { claimAchievement } from '@/utility/achievements.js';
import { prefer } from '@/preferences.js';
import { chooseFileFromPc } from '@/utility/select-file.js';
+import { store } from '@/store.js';
const searchQuery = ref('');
@@ -723,6 +726,10 @@ function onContextmenu(ev: MouseEvent) {
os.contextMenu(getMenu(), ev);
}
+function closeTip() {
+ store.set('readDriveTip', true);
+}
+
onMounted(() => {
if (prefer.s.enableInfiniteScroll && loadMoreFiles.value) {
nextTick(() => {
diff --git a/packages/frontend/src/components/MkFileCaptionEditWindow.vue b/packages/frontend/src/components/MkFileCaptionEditWindow.vue
index 7b5eefdc10..bdfbe13fb4 100644
--- a/packages/frontend/src/components/MkFileCaptionEditWindow.vue
+++ b/packages/frontend/src/components/MkFileCaptionEditWindow.vue
@@ -15,12 +15,12 @@ SPDX-License-Identifier: AGPL-3.0-only
@closed="emit('closed')"
>
<template #header>{{ i18n.ts.describeFile }}</template>
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<MkDriveFileThumbnail :file="file" fit="contain" style="height: 193px; margin-bottom: 16px;"/>
<MkTextarea v-model="caption" autofocus :placeholder="i18n.ts.inputNewDescription" @keydown="onKeydown($event)">
<template #label>{{ i18n.ts.caption }}</template>
</MkTextarea>
- </MkSpacer>
+ </div>
</MkModalWindow>
</template>
diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue
index c228853bea..2e5d0a3dea 100644
--- a/packages/frontend/src/components/MkFolder.vue
+++ b/packages/frontend/src/components/MkFolder.vue
@@ -31,10 +31,6 @@ SPDX-License-Identifier: AGPL-3.0-only
:leaveActiveClass="prefer.s.animation ? $style.transition_toggle_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_toggle_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_toggle_leaveTo : ''"
- @enter="enter"
- @afterEnter="afterEnter"
- @leave="leave"
- @afterLeave="afterLeave"
>
<KeepAlive>
<div v-show="opened">
@@ -45,9 +41,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</template>
- <MkSpacer v-if="withSpacer" :marginMin="spacerMin" :marginMax="spacerMax">
+ <div v-if="withSpacer" class="_spacer" :style="{ '--MI_SPACER-min': props.spacerMin + 'px', '--MI_SPACER-max': props.spacerMax + 'px' }">
<slot></slot>
- </MkSpacer>
+ </div>
<div v-else>
<slot></slot>
</div>
@@ -90,32 +86,6 @@ const bgSame = ref(false);
const opened = ref(props.defaultOpen);
const openedAtLeastOnce = ref(props.defaultOpen);
-function enter(el: Element) {
- if (!(el instanceof HTMLElement)) return;
- const elementHeight = el.getBoundingClientRect().height;
- el.style.height = '0';
- el.offsetHeight; // reflow
- el.style.height = `${Math.min(elementHeight, props.maxHeight ?? Infinity)}px`;
-}
-
-function afterEnter(el: Element) {
- if (!(el instanceof HTMLElement)) return;
- el.style.height = '';
-}
-
-function leave(el: Element) {
- if (!(el instanceof HTMLElement)) return;
- const elementHeight = el.getBoundingClientRect().height;
- el.style.height = `${elementHeight}px`;
- el.offsetHeight; // reflow
- el.style.height = '0';
-}
-
-function afterLeave(el: Element) {
- if (!(el instanceof HTMLElement)) return;
- el.style.height = '';
-}
-
function toggle() {
if (!opened.value) {
openedAtLeastOnce.value = true;
@@ -137,16 +107,18 @@ onMounted(() => {
<style lang="scss" module>
.transition_toggle_enterActive,
.transition_toggle_leaveActive {
- overflow-y: clip;
- transition: opacity 0.3s, height 0.3s, transform 0.3s !important;
+ overflow-y: hidden; // 子要素のmarginが突き出るため clip を使ってはいけない
+ transition: opacity 0.3s, height 0.3s !important;
}
.transition_toggle_enterFrom,
.transition_toggle_leaveTo {
opacity: 0;
+ height: 0;
}
.root {
display: block;
+ interpolate-size: allow-keywords; // heightのtransitionを動作させるために必要
}
.header {
diff --git a/packages/frontend/src/components/MkForgotPassword.vue b/packages/frontend/src/components/MkForgotPassword.vue
index 35112ad45d..57946aaf2b 100644
--- a/packages/frontend/src/components/MkForgotPassword.vue
+++ b/packages/frontend/src/components/MkForgotPassword.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<template #header>{{ i18n.ts.forgotPassword }}</template>
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<form v-if="instance.enableEmail" @submit.prevent="onSubmit">
<div class="_gaps_m">
<MkInput v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autofocus required>
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-else>
{{ i18n.ts._forgotPassword.contactAdmin }}
</div>
- </MkSpacer>
+ </div>
</MkModalWindow>
</template>
diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue
index 4756079e76..0884cdc016 100644
--- a/packages/frontend/src/components/MkFormDialog.vue
+++ b/packages/frontend/src/components/MkFormDialog.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ title }}
</template>
- <MkSpacer :marginMin="20" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 32px;">
<div v-if="Object.keys(form).filter(item => !form[item].hidden).length > 0" class="_gaps_m">
<template v-for="(v, k) in Object.fromEntries(Object.entries(form))">
<template v-if="typeof v.hidden == 'function' ? v.hidden(values) : v.hidden"></template>
@@ -66,7 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<img :src="infoImageUrl" draggable="false"/>
<div>{{ i18n.ts.nothing }}</div>
</div>
- </MkSpacer>
+ </div>
</MkModalWindow>
</template>
diff --git a/packages/frontend/src/components/MkInfo.vue b/packages/frontend/src/components/MkInfo.vue
index 410e3accab..fb66098a54 100644
--- a/packages/frontend/src/components/MkInfo.vue
+++ b/packages/frontend/src/components/MkInfo.vue
@@ -39,7 +39,6 @@ function closeInfo() {
background: color-mix(in srgb, var(--MI_THEME-infoBg) 65%, transparent);
color: var(--MI_THEME-infoFg);
border-radius: var(--MI-radius);
- white-space: pre-wrap;
z-index: 1;
&.warn {
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 51836ce093..55efc3c193 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -114,7 +114,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA>
</bdi>
</div>
- <MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" :note="appearNote" :maxNumber="16" @click.stop @mockUpdateMyReaction="emitUpdReaction">
+ <MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" style="margin-top: 6px;" :note="appearNote" :maxNumber="16" @click.stop @mockUpdateMyReaction="emitUpdReaction">
<template #more>
<MkA :to="`/notes/${appearNote.id}/reactions`" :class="[$style.reactionOmitted]">{{ i18n.ts.more }}</MkA>
</template>
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 3c3136a705..488ee16e62 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -133,7 +133,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkTime :time="appearNote.createdAt" mode="detail" colored/>
</MkA>
</div>
- <MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" ref="reactionsViewer" :note="appearNote"/>
+ <MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" ref="reactionsViewer" style="margin-top: 6px;" :note="appearNote"/>
<button class="_button" :class="$style.noteFooterButton" @click="reply()">
<i class="ti ti-arrow-back-up"></i>
<p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.repliesCount) }}</p>
diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotes.vue
index 5edae908b0..602619402a 100644
--- a/packages/frontend/src/components/MkNotes.vue
+++ b/packages/frontend/src/components/MkNotes.vue
@@ -13,16 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<template #default="{ items: notes }">
- <component
- :is="prefer.s.animation ? TransitionGroup : 'div'"
- :class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap, [$style.reverse]: pagination.reversed }]"
- :enterActiveClass="$style.transition_x_enterActive"
- :leaveActiveClass="$style.transition_x_leaveActive"
- :enterFromClass="$style.transition_x_enterFrom"
- :leaveToClass="$style.transition_x_leaveTo"
- :moveClass=" $style.transition_x_move"
- tag="div"
- >
+ <div :class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap, [$style.reverse]: pagination.reversed }]">
<template v-for="(note, i) in notes" :key="note.id">
<div v-if="note._shouldInsertAd_" :class="[$style.noteWithAd, { '_gaps': !noGap }]" :data-scroll-anchor="note.id">
<DynamicNote :class="$style.note" :note="note as Misskey.entities.Note" :withHardMute="true"/>
@@ -32,20 +23,19 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<DynamicNote v-else :class="$style.note" :note="note as Misskey.entities.Note" :withHardMute="true" :data-scroll-anchor="note.id"/>
</template>
- </component>
+ </div>
</template>
</MkPagination>
</template>
<script lang="ts" setup>
import * as Misskey from 'misskey-js';
-import { useTemplateRef, TransitionGroup } from 'vue';
+import { useTemplateRef } from 'vue';
import type { Paging } from '@/components/MkPagination.vue';
import DynamicNote from '@/components/DynamicNote.vue';
import MkPagination from '@/components/MkPagination.vue';
import { i18n } from '@/i18n.js';
import { infoImageUrl } from '@/instance.js';
-import { prefer } from '@/preferences.js';
const props = defineProps<{
pagination: Paging;
@@ -61,20 +51,6 @@ defineExpose({
</script>
<style lang="scss" module>
-.transition_x_move,
-.transition_x_enterActive,
-.transition_x_leaveActive {
- transition: opacity 0.3s cubic-bezier(0,.5,.5,1), transform 0.3s cubic-bezier(0,.5,.5,1) !important;
-}
-.transition_x_enterFrom,
-.transition_x_leaveTo {
- opacity: 0;
- transform: translateY(-50%);
-}
-.transition_x_leaveActive {
- position: absolute;
-}
-
.reverse {
display: flex;
flex-direction: column-reverse;
diff --git a/packages/frontend/src/components/MkNotificationSelectWindow.vue b/packages/frontend/src/components/MkNotificationSelectWindow.vue
index d074dceb2f..bb01a008bd 100644
--- a/packages/frontend/src/components/MkNotificationSelectWindow.vue
+++ b/packages/frontend/src/components/MkNotificationSelectWindow.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<template #header>{{ i18n.ts.notificationSetting }}</template>
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<div class="_gaps_m">
<MkInfo>{{ i18n.ts.notificationSettingDesc }}</MkInfo>
<div class="_buttons">
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<MkSwitch v-for="ntype in notificationTypes" :key="ntype" v-model="typesMap[ntype].value">{{ i18n.ts._notification._types[ntype] }}</MkSwitch>
</div>
- </MkSpacer>
+ </div>
</MkModalWindow>
</template>
diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue
index d5b43cbf2e..a9e4704b24 100644
--- a/packages/frontend/src/components/MkPageWindow.vue
+++ b/packages/frontend/src/components/MkPageWindow.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</template>
- <div :class="$style.root">
+ <div :class="$style.root" class="_forceShrinkSpacer">
<StackingRouterView v-if="prefer.s['experimental.stackingRouterView']" :key="reloadCount" :router="windowRouter"/>
<RouterView v-else :key="reloadCount" :router="windowRouter"/>
</div>
@@ -121,7 +121,6 @@ provideMetadataReceiver((metadataGetter) => {
provideReactiveMetadata(pageMetadata);
provide('shouldOmitHeaderTitle', true);
provide('shouldHeaderThin', true);
-provide(DI.forceSpacerMin, true);
provide('shouldBackButton', false);
const contextmenu = computed(() => ([{
diff --git a/packages/frontend/src/components/MkPasswordDialog.vue b/packages/frontend/src/components/MkPasswordDialog.vue
index 2abf8669ed..826081ffe5 100644
--- a/packages/frontend/src/components/MkPasswordDialog.vue
+++ b/packages/frontend/src/components/MkPasswordDialog.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<template #header>{{ i18n.ts.authentication }}</template>
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<div style="padding: 0 0 16px 0; text-align: center;">
<img src="/client-assets/locked_with_key_3d.png" alt="🔐" style="display: block; margin: 0 auto; width: 48px;">
<div style="margin-top: 16px;">{{ i18n.ts.authenticationRequiredToContinue }}</div>
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton :disabled="(password ?? '') == '' || ($i.twoFactorEnabled && (token ?? '') == '')" type="submit" primary rounded style="margin: 0 auto;"><i class="ti ti-lock-open"></i> {{ i18n.ts.continue }}</MkButton>
</div>
</form>
- </MkSpacer>
+ </div>
</MkModalWindow>
</template>
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index e66a056a3f..494b61ca9d 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -182,7 +182,6 @@ if (!mock) {
.root {
display: inline-flex;
height: 42px;
- margin: 2px;
padding: 0 6px;
font-size: 1.5em;
border-radius: var(--MI-radius-sm);
diff --git a/packages/frontend/src/components/MkReactionsViewer.vue b/packages/frontend/src/components/MkReactionsViewer.vue
index 6b19f4a55c..70d0ddca0a 100644
--- a/packages/frontend/src/components/MkReactionsViewer.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.vue
@@ -106,7 +106,7 @@ watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumbe
display: flex;
flex-wrap: wrap;
align-items: center;
- margin: 4px -2px 0 -2px;
+ gap: 4px;
cursor: auto; /* not clickToOpen-able */
&:empty {
diff --git a/packages/frontend/src/components/MkRemoteEmojiEditDialog.vue b/packages/frontend/src/components/MkRemoteEmojiEditDialog.vue
index dc9bacf481..cb50df1743 100644
--- a/packages/frontend/src/components/MkRemoteEmojiEditDialog.vue
+++ b/packages/frontend/src/components/MkRemoteEmojiEditDialog.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #header>:{{ name }}:</template>
<div style="display: flex; flex-direction: column; min-height: 100%;">
- <MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px; flex-grow: 1;">
<div class="_gaps_m">
<div v-if="imgUrl != null" :class="$style.imgs">
<div style="background: #000;" :class="$style.imgContainer">
@@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #value>{{ license }}</template>
</MkKeyValue>
</div>
- </MkSpacer>
+ </div>
<div :class="$style.footer">
<MkButton primary rounded style="margin: 0 auto;" @click="done">
<i class="ti ti-plus"></i> {{ i18n.ts.import }}
diff --git a/packages/frontend/src/components/MkRoleSelectDialog.vue b/packages/frontend/src/components/MkRoleSelectDialog.vue
index fd56e4902c..6888824437 100644
--- a/packages/frontend/src/components/MkRoleSelectDialog.vue
+++ b/packages/frontend/src/components/MkRoleSelectDialog.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@closed="emit('closed')"
>
<template #header>{{ title }}</template>
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<MkLoading v-if="fetching"/>
<div v-else class="_gaps" :class="$style.root">
<div :class="$style.header">
@@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton @click="onCancelClicked">{{ i18n.ts.cancel }}</MkButton>
</div>
</div>
- </MkSpacer>
+ </div>
</MkModalWindow>
</template>
@@ -51,7 +51,6 @@ import MkInfo from '@/components/MkInfo.vue';
import MkRolePreview from '@/components/MkRolePreview.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
import * as os from '@/os.js';
-import MkSpacer from '@/components/global/MkSpacer.vue';
import MkModalWindow from '@/components/MkModalWindow.vue';
import MkLoading from '@/components/global/MkLoading.vue';
diff --git a/packages/frontend/src/components/MkSchedulePostListDialog.vue b/packages/frontend/src/components/MkSchedulePostListDialog.vue
index 0bcbb41192..41f366b082 100644
--- a/packages/frontend/src/components/MkSchedulePostListDialog.vue
+++ b/packages/frontend/src/components/MkSchedulePostListDialog.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@close="cancel()"
>
<template #header>{{ i18n.ts.schedulePostList }}</template>
- <MkSpacer :marginMin="14" :marginMax="16">
+ <div class="_spacer" style="--MI_SPACER-min: 14px; --MI_SPACER-max: 16px;">
<MkPagination ref="paginationEl" :pagination="pagination">
<template #empty>
<div class="_fullinfo">
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</template>
</MkPagination>
- </MkSpacer>
+ </div>
</MkModalWindow>
</template>
diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue
index b152ba81a6..365b23f4ce 100644
--- a/packages/frontend/src/components/MkSignupDialog.form.vue
+++ b/packages/frontend/src/components/MkSignupDialog.form.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.banner">
<i class="ti ti-user-edit"></i>
</div>
- <MkSpacer :marginMin="20" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 32px;">
<form class="_gaps_m" autocomplete="new-password" @submit.prevent="onSubmit">
<MkInput v-if="instance.disableRegistration" v-model="invitationCode" type="text" :spellcheck="false" required>
<template #label>{{ i18n.ts.invitationCode }}</template>
@@ -79,7 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-else>{{ i18n.ts.start }}</template>
</MkButton>
</form>
- </MkSpacer>
+ </div>
</div>
</template>
diff --git a/packages/frontend/src/components/MkSignupDialog.rules.vue b/packages/frontend/src/components/MkSignupDialog.rules.vue
index f3d358c874..3034f2269b 100644
--- a/packages/frontend/src/components/MkSignupDialog.rules.vue
+++ b/packages/frontend/src/components/MkSignupDialog.rules.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.banner">
<i class="ti ti-checklist"></i>
</div>
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<div class="_gaps_m">
<div v-if="instance.disableRegistration || instance.federation !== 'all'" class="_gaps_s">
<MkInfo v-if="instance.disableRegistration" warn>{{ i18n.ts.invitationRequiredToRegister }}</MkInfo>
@@ -59,7 +59,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton inline primary rounded gradate :disabled="!agreed" data-cy-signup-rules-continue @click="emit('done')">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
</div>
</div>
- </MkSpacer>
+ </div>
</div>
</template>
diff --git a/packages/frontend/src/components/MkSystemWebhookEditor.vue b/packages/frontend/src/components/MkSystemWebhookEditor.vue
index 86e755a3c3..cd72204fce 100644
--- a/packages/frontend/src/components/MkSystemWebhookEditor.vue
+++ b/packages/frontend/src/components/MkSystemWebhookEditor.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<div style="display: flex; flex-direction: column; min-height: 100%;">
- <MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px; flex-grow: 1;">
<MkLoading v-if="loading !== 0"/>
<div v-else :class="$style.root" class="_gaps_m">
<MkInput v-model="title">
@@ -79,7 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.enable }}</template>
</MkSwitch>
</div>
- </MkSpacer>
+ </div>
<div :class="$style.footer" class="_buttonsCenter">
<MkButton primary rounded :disabled="disableSubmitButton" @click="onSubmitClicked">
<i class="ti ti-check"></i>
diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue
index 7b740167ec..32b2e1ae11 100644
--- a/packages/frontend/src/components/MkTimeline.vue
+++ b/packages/frontend/src/components/MkTimeline.vue
@@ -5,29 +5,55 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<MkPullToRefresh ref="prComponent" :refresher="() => reloadTimeline()">
- <MkNotes
- v-if="paginationQuery"
- ref="tlComponent"
- :pagination="paginationQuery"
- :noGap="!prefer.s.showGapBetweenNotesInTimeline"
- @queue="emit('queue', $event)"
- @status="prComponent?.setDisabled($event)"
- />
+ <MkPagination v-if="paginationQuery" ref="pagingComponent" :pagination="paginationQuery" @queue="emit('queue', $event)" @status="prComponent?.setDisabled($event)">
+ <template #empty>
+ <div class="_fullinfo">
+ <img :src="infoImageUrl" draggable="false"/>
+ <div>{{ i18n.ts.noNotes }}</div>
+ </div>
+ </template>
+
+ <template #default="{ items: notes }">
+ <component
+ :is="prefer.s.animation ? TransitionGroup : 'div'"
+ :class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap, [$style.reverse]: paginationQuery.reversed }]"
+ :enterActiveClass="$style.transition_x_enterActive"
+ :leaveActiveClass="$style.transition_x_leaveActive"
+ :enterFromClass="$style.transition_x_enterFrom"
+ :leaveToClass="$style.transition_x_leaveTo"
+ :moveClass=" $style.transition_x_move"
+ tag="div"
+ >
+ <template v-for="(note, i) in notes" :key="note.id">
+ <div v-if="note._shouldInsertAd_" :class="[$style.noteWithAd, { '_gaps': !noGap }]" :data-scroll-anchor="note.id">
+ <MkNote :class="$style.note" :note="note" :withHardMute="true"/>
+ <div :class="$style.ad">
+ <MkAd :preferForms="['horizontal', 'horizontal-big']"/>
+ </div>
+ </div>
+ <MkNote v-else :class="$style.note" :note="note" :withHardMute="true" :data-scroll-anchor="note.id"/>
+ </template>
+ </component>
+ </template>
+ </MkPagination>
</MkPullToRefresh>
</template>
<script lang="ts" setup>
-import { computed, watch, onUnmounted, provide, useTemplateRef } from 'vue';
+import { computed, watch, onUnmounted, provide, useTemplateRef, TransitionGroup } from 'vue';
import * as Misskey from 'misskey-js';
import type { BasicTimelineType } from '@/timelines.js';
import type { Paging } from '@/components/MkPagination.vue';
-import MkNotes from '@/components/MkNotes.vue';
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
import { useStream } from '@/stream.js';
import * as sound from '@/utility/sound.js';
import { $i } from '@/i.js';
import { instance } from '@/instance.js';
import { prefer } from '@/preferences.js';
+import MkNote from '@/components/MkNote.vue';
+import MkPagination from '@/components/MkPagination.vue';
+import { i18n } from '@/i18n.js';
+import { infoImageUrl } from '@/instance.js';
const props = withDefaults(defineProps<{
src: BasicTimelineType | 'mentions' | 'directs' | 'list' | 'antenna' | 'channel' | 'role';
@@ -71,12 +97,12 @@ type TimelineQueryType = {
};
const prComponent = useTemplateRef('prComponent');
-const tlComponent = useTemplateRef('tlComponent');
+const pagingComponent = useTemplateRef('pagingComponent');
let tlNotesCount = 0;
function prepend(note: Misskey.entities.Note) {
- if (tlComponent.value == null) return;
+ if (pagingComponent.value == null) return;
tlNotesCount++;
@@ -84,7 +110,7 @@ function prepend(note: Misskey.entities.Note) {
note._shouldInsertAd_ = true;
}
- tlComponent.value.pagingComponent?.prepend(note);
+ pagingComponent.value.prepend(note);
emit('note');
@@ -96,6 +122,7 @@ function prepend(note: Misskey.entities.Note) {
let connection: Misskey.ChannelConnection | null = null;
let connection2: Misskey.ChannelConnection | null = null;
let paginationQuery: Paging | null = null;
+const noGap = !prefer.s.showGapBetweenNotesInTimeline;
const stream = useStream();
@@ -290,11 +317,11 @@ onUnmounted(() => {
function reloadTimeline() {
return new Promise<void>((res) => {
- if (tlComponent.value == null) return;
+ if (pagingComponent.value == null) return;
tlNotesCount = 0;
- tlComponent.value.pagingComponent?.reload().then(() => {
+ pagingComponent.value.reload().then(() => {
res();
});
});
@@ -304,3 +331,56 @@ defineExpose({
reloadTimeline,
});
</script>
+
+<style lang="scss" module>
+.transition_x_move,
+.transition_x_enterActive,
+.transition_x_leaveActive {
+ transition: opacity 0.3s cubic-bezier(0,.5,.5,1), transform 0.3s cubic-bezier(0,.5,.5,1) !important;
+}
+.transition_x_enterFrom,
+.transition_x_leaveTo {
+ opacity: 0;
+ transform: translateY(-50%);
+}
+.transition_x_leaveActive {
+ position: absolute;
+}
+
+.reverse {
+ display: flex;
+ flex-direction: column-reverse;
+}
+
+.root {
+ container-type: inline-size;
+
+ &.noGap {
+ background: var(--MI_THEME-panel);
+
+ .note {
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
+ }
+
+ .ad {
+ padding: 8px;
+ background-size: auto auto;
+ background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, var(--MI_THEME-bg) 8px, var(--MI_THEME-bg) 14px);
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
+ }
+ }
+
+ &:not(.noGap) {
+ background: var(--MI_THEME-bg);
+
+ .note {
+ background: var(--MI_THEME-panel);
+ border-radius: var(--MI-radius);
+ }
+ }
+}
+
+.ad:empty {
+ display: none;
+}
+</style>
diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue
index b449155edb..42cb6f1e82 100644
--- a/packages/frontend/src/components/MkTokenGenerateWindow.vue
+++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<template #header>{{ title || i18n.ts.generateAccessToken }}</template>
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<div class="_gaps_m">
<div v-if="information">
<MkInfo warn>{{ information }}</MkInfo>
@@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</div>
- </MkSpacer>
+ </div>
</MkModalWindow>
</template>
diff --git a/packages/frontend/src/components/MkTutorialDialog.vue b/packages/frontend/src/components/MkTutorialDialog.vue
index 92f71b01af..d6abbf6504 100644
--- a/packages/frontend/src/components/MkTutorialDialog.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.vue
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-if="page === 0">
<div :class="$style.centerPage">
<MkAnimBg style="position: absolute; top: 0;" :scale="1.5"/>
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<div class="_gaps" style="text-align: center;">
<i class="ti ti-confetti" style="display: block; margin: auto; font-size: 3em; color: var(--MI_THEME-accent);"></i>
<div style="font-size: 120%;">{{ i18n.ts._initialTutorial._landing.title }}</div>
@@ -37,15 +37,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" @click="page++">{{ i18n.ts._initialTutorial.launchTutorial }} <i class="ti ti-arrow-right"></i></MkButton>
<MkButton style="margin: 0 auto;" transparent rounded @click="close(true)">{{ i18n.ts.close }}</MkButton>
</div>
- </MkSpacer>
+ </div>
</div>
</template>
<template v-else-if="page === 1">
<div style="height: 100cqh; overflow: auto;">
<div :class="$style.pageRoot">
- <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;" :class="$style.pageMain">
<XNote phase="aboutNote"/>
- </MkSpacer>
+ </div>
<div :class="$style.pageFooter">
<div class="_buttonsCenter">
<MkButton v-if="initialPage !== 1" rounded @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton>
@@ -58,12 +58,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-else-if="page === 2">
<div style="height: 100cqh; overflow: auto;">
<div :class="$style.pageRoot">
- <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;" :class="$style.pageMain">
<div class="_gaps">
<XNote phase="howToReact" @reacted="isReactionTutorialPushed = true"/>
<div v-if="!isReactionTutorialPushed">{{ i18n.ts._initialTutorial._reaction.reactToContinue }}</div>
</div>
- </MkSpacer>
+ </div>
<div :class="$style.pageFooter">
<div class="_buttonsCenter">
<MkButton v-if="initialPage !== 2" rounded @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton>
@@ -76,9 +76,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-else-if="page === 3">
<div style="height: 100cqh; overflow: auto;">
<div :class="$style.pageRoot">
- <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;" :class="$style.pageMain">
<XTimeline/>
- </MkSpacer>
+ </div>
<div :class="$style.pageFooter">
<div class="_buttonsCenter">
<MkButton v-if="initialPage !== 3" rounded @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton>
@@ -91,9 +91,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-else-if="page === 4">
<div style="height: 100cqh; overflow: auto;">
<div :class="$style.pageRoot">
- <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;" :class="$style.pageMain">
<XPostNote/>
- </MkSpacer>
+ </div>
<div :class="$style.pageFooter">
<div class="_buttonsCenter">
<MkButton v-if="initialPage !== 3" rounded @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton>
@@ -106,12 +106,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-else-if="page === 5">
<div style="height: 100cqh; overflow: auto;">
<div :class="$style.pageRoot">
- <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;" :class="$style.pageMain">
<div class="_gaps">
<XSensitive @succeeded="isSensitiveTutorialSucceeded = true"/>
<div v-if="!isSensitiveTutorialSucceeded">{{ i18n.ts._initialTutorial._howToMakeAttachmentsSensitive.doItToContinue }}</div>
</div>
- </MkSpacer>
+ </div>
<div :class="$style.pageFooter">
<div class="_buttonsCenter">
<MkButton v-if="initialPage !== 2" rounded @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton>
@@ -124,7 +124,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-else-if="page === 6">
<div :class="$style.centerPage">
<MkAnimBg style="position: absolute; top: 0;" :scale="1.5"/>
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<div class="_gaps" style="text-align: center;">
<i class="ti ti-check" style="display: block; margin: auto; font-size: 3em; color: var(--MI_THEME-accent);"></i>
<div style="font-size: 120%;">{{ i18n.ts._initialTutorial._done.title }}</div>
@@ -139,7 +139,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton rounded primary gradate @click="close(false)">{{ i18n.ts.close }}</MkButton>
</div>
</div>
- </MkSpacer>
+ </div>
</div>
</template>
</Transition>
diff --git a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
index 34e86444ad..aaefa5036a 100644
--- a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
+++ b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-else #header>New announcement</template>
<div>
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<div class="_gaps_m">
<MkInput v-model="title">
<template #label>{{ i18n.ts.title }}</template>
@@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch>
<MkButton v-if="announcement" danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
</div>
- </MkSpacer>
+ </div>
<div :class="$style.footer">
<MkButton primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.announcement ? i18n.ts.update : i18n.ts.create }}</MkButton>
</div>
diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue
index 767f5c591a..82214ed5a5 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.vue
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-if="page === 0">
<div :class="$style.centerPage">
<MkAnimBg style="position: absolute; top: 0;" :scale="1.5"/>
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<div class="_gaps" style="text-align: center;">
<i class="ti ti-confetti" style="display: block; margin: auto; font-size: 3em; color: var(--MI_THEME-accent);"></i>
<div style="font-size: 120%;">{{ i18n.ts._initialAccountSetting.accountCreated }}</div>
@@ -41,15 +41,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" data-cy-user-setup-continue @click="page++">{{ i18n.ts._initialAccountSetting.profileSetting }} <i class="ti ti-arrow-right"></i></MkButton>
<MkButton style="margin: 0 auto;" transparent rounded @click="later(true)">{{ i18n.ts.later }}</MkButton>
</div>
- </MkSpacer>
+ </div>
</div>
</template>
<template v-else-if="page === 1">
<div style="height: 100cqh; overflow: auto;">
<div :class="$style.pageRoot">
- <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;" :class="$style.pageMain">
<XProfile/>
- </MkSpacer>
+ </div>
<div :class="$style.pageFooter">
<div class="_buttonsCenter">
<MkButton rounded data-cy-user-setup-back @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton>
@@ -62,9 +62,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-else-if="page === 2">
<div style="height: 100cqh; overflow: auto;">
<div :class="$style.pageRoot">
- <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;" :class="$style.pageMain">
<XPrivacy/>
- </MkSpacer>
+ </div>
<div :class="$style.pageFooter">
<div class="_buttonsCenter">
<MkButton rounded data-cy-user-setup-back @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton>
@@ -76,9 +76,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<template v-else-if="page === 3">
<div style="height: 100cqh; overflow: auto;">
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<XFollow/>
- </MkSpacer>
+ </div>
<div :class="$style.pageFooter">
<div class="_buttonsCenter">
<MkButton rounded data-cy-user-setup-back @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton>
@@ -89,7 +89,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<template v-else-if="page === 4">
<div :class="$style.centerPage">
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<div class="_gaps" style="text-align: center;">
<i class="ti ti-bell-ringing-2" style="display: block; margin: auto; font-size: 3em; color: var(--MI_THEME-accent);"></i>
<div style="font-size: 120%;">{{ i18n.ts.pushNotification }}</div>
@@ -100,13 +100,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton primary rounded gradate data-cy-user-setup-continue @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
</div>
</div>
- </MkSpacer>
+ </div>
</div>
</template>
<template v-else-if="page === 5">
<div :class="$style.centerPage">
<MkAnimBg style="position: absolute; top: 0;" :scale="1.5"/>
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<div class="_gaps" style="text-align: center;">
<i class="ti ti-check" style="display: block; margin: auto; font-size: 3em; color: var(--MI_THEME-accent);"></i>
<div style="font-size: 120%;">{{ i18n.ts._initialAccountSetting.initialAccountSettingCompleted }}</div>
@@ -119,7 +119,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton rounded primary data-cy-user-setup-continue @click="setupComplete()">{{ i18n.ts.close }}</MkButton>
</div>
</div>
- </MkSpacer>
+ </div>
</div>
</template>
</Transition>
@@ -147,7 +147,7 @@ const emit = defineEmits<{
}>();
const dialog = useTemplateRef('dialog');
-
+
const page = ref(store.s.accountSetupWizard);
watch(page, () => {
diff --git a/packages/frontend/src/components/SkMfmWindow.vue b/packages/frontend/src/components/SkMfmWindow.vue
index a628758a0f..14d309b7ba 100644
--- a/packages/frontend/src/components/SkMfmWindow.vue
+++ b/packages/frontend/src/components/SkMfmWindow.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
MFM Cheatsheet
</template>
<MkStickyContainer>
- <MkSpacer :contentMax="800">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<div class="mfm-cheat-sheet">
<div>{{ i18n.ts._mfm.intro }}</div>
<br/>
@@ -402,7 +402,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</div>
- </MkSpacer>
+ </div>
</MkStickyContainer>
</MkWindow>
</template>
diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index 42bb49e8d9..c939f0b44e 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -49,19 +49,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</template>
-<script lang="ts" setup>
-import { onMounted, onUnmounted, ref, inject, useTemplateRef, computed } from 'vue';
-import { scrollToTop } from '@@/js/scroll.js';
-import XTabs from './MkPageHeader.tabs.vue';
-import type { Tab } from './MkPageHeader.tabs.vue';
+<script lang="ts">
import type { PageHeaderItem } from '@/types/page-header.js';
import type { PageMetadata } from '@/page.js';
-import { globalEvents } from '@/events.js';
-import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
-import { $i } from '@/i.js';
-import { DI } from '@/di.js';
+import type { Tab } from './MkPageHeader.tabs.vue';
-const props = withDefaults(defineProps<{
+export type PageHeaderProps = {
overridePageMetadata?: PageMetadata;
tabs?: Tab[];
tab?: string;
@@ -70,7 +63,19 @@ const props = withDefaults(defineProps<{
hideTitle?: boolean;
displayMyAvatar?: boolean;
displayBackButton?: boolean;
-}>(), {
+};
+</script>
+
+<script lang="ts" setup>
+import { onMounted, onUnmounted, ref, inject, useTemplateRef, computed } from 'vue';
+import { scrollToTop } from '@@/js/scroll.js';
+import XTabs from './MkPageHeader.tabs.vue';
+import { globalEvents } from '@/events.js';
+import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
+import { $i } from '@/i.js';
+import { DI } from '@/di.js';
+
+const props = withDefaults(defineProps<PageHeaderProps>(), {
tabs: () => ([] as Tab[]),
});
diff --git a/packages/frontend/src/components/global/MkSpacer.vue b/packages/frontend/src/components/global/MkSpacer.vue
deleted file mode 100644
index c3bc37cb92..0000000000
--- a/packages/frontend/src/components/global/MkSpacer.vue
+++ /dev/null
@@ -1,58 +0,0 @@
-<!--
-SPDX-FileCopyrightText: syuilo and misskey-project
-SPDX-License-Identifier: AGPL-3.0-only
--->
-
-<template>
-<div :class="[$style.root, { [$style.rootMin]: forceSpacerMin }]">
- <div :class="$style.content">
- <slot></slot>
- </div>
-</div>
-</template>
-
-<script lang="ts" setup>
-import { inject } from 'vue';
-import { deviceKind } from '@/utility/device-kind.js';
-import { DI } from '@/di.js';
-
-const props = withDefaults(defineProps<{
- contentMax?: number | null;
- marginMin?: number;
- marginMax?: number;
-}>(), {
- contentMax: null,
- marginMin: 12,
- marginMax: 24,
-});
-
-const forceSpacerMin = inject(DI.forceSpacerMin, false) || deviceKind === 'smartphone';
-</script>
-
-<style lang="scss" module>
-.root {
- box-sizing: border-box;
- width: 100%;
-}
-.rootMin {
- padding: v-bind('props.marginMin + "px"') !important;
-}
-
-.content {
- margin: 0 auto;
- max-width: v-bind('props.contentMax + "px"');
- container-type: inline-size;
-}
-
-@container (max-width: 450px) {
- .root {
- padding: v-bind('props.marginMin + "px"');
- }
-}
-
-@container (min-width: 451px) {
- .root {
- padding: v-bind('props.marginMax + "px"');
- }
-}
-</style>
diff --git a/packages/frontend/src/components/global/PageWithHeader.vue b/packages/frontend/src/components/global/PageWithHeader.vue
index 85e61fd532..58c222038a 100644
--- a/packages/frontend/src/components/global/PageWithHeader.vue
+++ b/packages/frontend/src/components/global/PageWithHeader.vue
@@ -6,9 +6,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div ref="rootEl" :class="[$style.root, reversed ? '_pageScrollableReversed' : '_pageScrollable']">
<MkStickyContainer>
- <template #header><MkPageHeader v-model:tab="tab" :actions="actions" :tabs="tabs" :displayBackButton="displayBackButton"/></template>
+ <template #header><MkPageHeader v-model:tab="tab" v-bind="pageHeaderProps"/></template>
<div :class="$style.body">
- <slot></slot>
+ <MkSwiper v-if="swipable && (props.tabs?.length ?? 1) > 1" v-model:tab="tab" :class="$style.swiper" :tabs="props.tabs">
+ <slot></slot>
+ </MkSwiper>
+ <slot v-else></slot>
</div>
<template #footer><slot name="footer"></slot></template>
</MkStickyContainer>
@@ -16,22 +19,24 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { useTemplateRef } from 'vue';
+import { computed, useTemplateRef } from 'vue';
import { scrollInContainer } from '@@/js/scroll.js';
-import type { PageHeaderItem } from '@/types/page-header.js';
-import type { Tab } from './MkPageHeader.tabs.vue';
+import type { PageHeaderProps } from './MkPageHeader.vue';
import { useScrollPositionKeeper } from '@/use/use-scroll-position-keeper.js';
+import MkSwiper from '@/components/MkSwiper.vue';
+import { useRouter } from '@/router.js';
-const props = withDefaults(defineProps<{
- tabs?: Tab[];
- actions?: PageHeaderItem[] | null;
- thin?: boolean;
- hideTitle?: boolean;
- displayMyAvatar?: boolean;
+const props = withDefaults(defineProps<PageHeaderProps & {
reversed?: boolean;
- displayBackButton?: boolean;
+ swipable?: boolean;
}>(), {
- tabs: () => ([] as Tab[]),
+ reversed: false,
+ swipable: true,
+});
+
+const pageHeaderProps = computed(() => {
+ const { reversed, ...rest } = props;
+ return rest;
});
const tab = defineModel<string>('tab');
@@ -39,10 +44,18 @@ const rootEl = useTemplateRef('rootEl');
useScrollPositionKeeper(rootEl);
+const router = useRouter();
+
+router.useListener('same', () => {
+ scrollToTop();
+});
+
+function scrollToTop() {
+ if (rootEl.value) scrollInContainer(rootEl.value, { top: 0, behavior: 'smooth' });
+}
+
defineExpose({
- scrollToTop: () => {
- if (rootEl.value) scrollInContainer(rootEl.value, { top: 0, behavior: 'smooth' });
- },
+ scrollToTop,
});
</script>
@@ -51,7 +64,7 @@ defineExpose({
}
-.body {
+.body, .swiper {
min-height: calc(100cqh - (var(--MI-stickyTop, 0px) + var(--MI-stickyBottom, 0px)));
}
</style>
diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue
index 78ac6900a3..27f7b18559 100644
--- a/packages/frontend/src/components/global/RouterView.vue
+++ b/packages/frontend/src/components/global/RouterView.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div ref="rootEl" class="_pageContainer" :class="$style.root">
+<div class="_pageContainer" :class="$style.root">
<KeepAlive :max="prefer.s.numberOfPageCache">
<Suspense :timeout="0">
<component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/>
@@ -42,37 +42,6 @@ provide(DI.viewId, viewId);
const currentDepth = inject(DI.routerCurrentDepth, 0);
provide(DI.routerCurrentDepth, currentDepth + 1);
-const rootEl = useTemplateRef('rootEl');
-onMounted(() => {
- if (prefer.s.animation) {
- rootEl.value.style.viewTransitionName = viewId; // view-transition-nameにcss varが使えないっぽいため直接代入
- }
-});
-
-// view-transition-newなどの<pt-name-selector>にはcss varが使えず、v-bindできないため直接スタイルを生成
-const viewTransitionStylesTag = window.document.createElement('style');
-viewTransitionStylesTag.textContent = `
-@keyframes ${viewId}-old {
- to { transform: scale(0.95); opacity: 0; }
-}
-
-@keyframes ${viewId}-new {
- from { transform: scale(0.95); opacity: 0; }
-}
-
-::view-transition-old(${viewId}) {
- animation-duration: 0.2s;
- animation-name: ${viewId}-old;
-}
-
-::view-transition-new(${viewId}) {
- animation-duration: 0.2s;
- animation-name: ${viewId}-new;
-}
-`;
-
-window.document.head.appendChild(viewTransitionStylesTag);
-
const current = router.current!;
const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage);
const currentPageProps = ref(current.props);
@@ -90,18 +59,7 @@ router.useListener('change', ({ resolved }) => {
currentRoutePath = resolved.route.path;
}
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
- if (prefer.s.animation && window.document.startViewTransition) {
- window.document.startViewTransition(() => new Promise((res) => {
- _();
- nextTick(() => {
- res();
- //setTimeout(res, 100);
- });
- }));
- } else {
- _();
- }
+ _();
});
</script>
diff --git a/packages/frontend/src/components/grid/MkGrid.vue b/packages/frontend/src/components/grid/MkGrid.vue
index c37f3df0d3..f80f037285 100644
--- a/packages/frontend/src/components/grid/MkGrid.vue
+++ b/packages/frontend/src/components/grid/MkGrid.vue
@@ -38,7 +38,6 @@ SPDX-License-Identifier: AGPL-3.0-only
:setting="rowSetting"
:bus="bus"
:using="row.using"
- :class="[lastLine === row.index ? 'last_row' : '']"
@operation:beginEdit="onCellEditBegin"
@operation:endEdit="onCellEditEnd"
@change:value="onChangeCellValue"
@@ -1301,8 +1300,6 @@ onMounted(() => {
</style>
<style lang="scss">
-$borderSetting: solid 0.5px var(--MI_THEME-divider);
-
// 配下コンポーネントを含めて一括してコントロールするため、scopedもmoduleも使用できない
.mk_grid_border {
--rootBorderSetting: none;
@@ -1310,66 +1307,39 @@ $borderSetting: solid 0.5px var(--MI_THEME-divider);
border-spacing: 0;
- &.mk_grid_root_border {
- --rootBorderSetting: #{$borderSetting};
- }
-
&.mk_grid_root_rounded {
--borderRadius: var(--MI-radius);
}
.mk_grid_thead {
+ position: sticky;
+ z-index: 1;
+ left: 0;
+ top: 0;
+ -webkit-backdrop-filter: var(--MI-blur, blur(8px));
+ backdrop-filter: var(--MI-blur, blur(20px));
+ background: color(from var(--MI_THEME-bg) srgb r g b / 0.5);
+
.mk_grid_tr {
.mk_grid_th {
- border-left: $borderSetting;
- border-top: var(--rootBorderSetting);
- &:first-child {
- // 左上セル
- border-left: var(--rootBorderSetting);
- border-top-left-radius: var(--borderRadius);
- }
-
- &:last-child {
- // 右上セル
- border-top-right-radius: var(--borderRadius);
- border-right: var(--rootBorderSetting);
- }
}
}
}
.mk_grid_tbody {
.mk_grid_tr {
- .mk_grid_td, .mk_grid_th {
- border-left: $borderSetting;
- border-top: $borderSetting;
-
- &:first-child {
- // 左端の列
- border-left: var(--rootBorderSetting);
- }
+ &:nth-child(odd) {
+ background: var(--MI_THEME-panel);
+ }
- &:last-child {
- // 一番右端の列
- border-right: var(--rootBorderSetting);
- }
+ &:nth-child(even) {
+ background: var(--MI_THEME-bg);
}
- }
- .last_row {
.mk_grid_td, .mk_grid_th {
- // 一番下の行
- border-bottom: var(--rootBorderSetting);
-
- &:first-child {
- // 左下セル
- border-bottom-left-radius: var(--borderRadius);
- }
-
- &:last-child {
- // 右下セル
- border-bottom-right-radius: var(--borderRadius);
+ &:hover {
+ box-shadow: 0 0 0 1px var(--MI_THEME-divider) inset;
}
}
}
diff --git a/packages/frontend/src/components/index.ts b/packages/frontend/src/components/index.ts
index 34cf598b84..ec6ea7c569 100644
--- a/packages/frontend/src/components/index.ts
+++ b/packages/frontend/src/components/index.ts
@@ -22,7 +22,6 @@ import MkLoading from './global/MkLoading.vue';
import MkError from './global/MkError.vue';
import MkAd from './global/MkAd.vue';
import MkPageHeader from './global/MkPageHeader.vue';
-import MkSpacer from './global/MkSpacer.vue';
import MkStickyContainer from './global/MkStickyContainer.vue';
import MkLazy from './global/MkLazy.vue';
import PageWithHeader from './global/PageWithHeader.vue';
@@ -60,7 +59,6 @@ export const components = {
MkError: MkError,
MkAd: MkAd,
MkPageHeader: MkPageHeader,
- MkSpacer: MkSpacer,
MkStickyContainer: MkStickyContainer,
MkLazy: MkLazy,
PageWithHeader: PageWithHeader,
@@ -92,7 +90,6 @@ declare module '@vue/runtime-core' {
MkError: typeof MkError;
MkAd: typeof MkAd;
MkPageHeader: typeof MkPageHeader;
- MkSpacer: typeof MkSpacer;
MkStickyContainer: typeof MkStickyContainer;
MkLazy: typeof MkLazy;
PageWithHeader: typeof PageWithHeader;
diff --git a/packages/frontend/src/di.ts b/packages/frontend/src/di.ts
index 58a2cce207..e2590da60b 100644
--- a/packages/frontend/src/di.ts
+++ b/packages/frontend/src/di.ts
@@ -17,5 +17,4 @@ export const DI = {
mfmEmojiReactCallback: Symbol() as InjectionKey<(emoji: string) => void>,
inModal: Symbol() as InjectionKey<boolean>,
inAppSearchMarkerId: Symbol() as InjectionKey<Ref<string | null>>,
- forceSpacerMin: Symbol() as InjectionKey<boolean>,
};
diff --git a/packages/frontend/src/pages/about-sharkey.vue b/packages/frontend/src/pages/about-sharkey.vue
index 833aed2c89..94e899e408 100644
--- a/packages/frontend/src/pages/about-sharkey.vue
+++ b/packages/frontend/src/pages/about-sharkey.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkStickyContainer>
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
<div style="overflow: clip;">
- <MkSpacer :contentMax="600" :marginMin="20">
+ <div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 20px;">
<div class="_gaps_m znqjceqz">
<div v-panel class="about">
<div ref="containerEl" class="container" :class="{ playing: easterEggEngine != null }">
@@ -85,7 +85,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-if="section.link" #description><MkLink :url="section.link.url">{{ section.link.label }}</MkLink></template>
</FormSection>
</div>
- </MkSpacer>
+ </div>
</div>
</MkStickyContainer>
</template>
diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue
index d955857e20..6b7b650a75 100644
--- a/packages/frontend/src/pages/about.vue
+++ b/packages/frontend/src/pages/about.vue
@@ -4,21 +4,19 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSwiper v-model:tab="tab" :tabs="headerTabs">
- <MkSpacer v-if="tab === 'overview'" :contentMax="600" :marginMin="20">
- <XOverview/>
- </MkSpacer>
- <MkSpacer v-else-if="tab === 'emojis'" :contentMax="1000" :marginMin="20">
- <XEmojis/>
- </MkSpacer>
- <MkSpacer v-else-if="instance.federation !== 'none' && tab === 'federation'" :contentMax="1000" :marginMin="20">
- <XFederation/>
- </MkSpacer>
- <MkSpacer v-else-if="tab === 'charts'" :contentMax="1000" :marginMin="20">
- <MkInstanceStats/>
- </MkSpacer>
- </MkSwiper>
+<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true">
+ <div v-if="tab === 'overview'" class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 20px;">
+ <XOverview/>
+ </div>
+ <div v-else-if="tab === 'emojis'" class="_spacer" style="--MI_SPACER-w: 1000px; --MI_SPACER-min: 20px;">
+ <XEmojis/>
+ </div>
+ <div v-else-if="instance.federation !== 'none' && tab === 'federation'" class="_spacer" style="--MI_SPACER-w: 1000px; --MI_SPACER-min: 20px;">
+ <XFederation/>
+ </div>
+ <div v-else-if="tab === 'charts'" class="_spacer" style="--MI_SPACER-w: 1000px; --MI_SPACER-min: 20px;">
+ <MkInstanceStats/>
+ </div>
</PageWithHeader>
</template>
@@ -28,7 +26,6 @@ import { instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { claimAchievement } from '@/utility/achievements.js';
import { definePage } from '@/page.js';
-import MkSwiper from '@/components/MkSwiper.vue';
const XOverview = defineAsyncComponent(() => import('@/pages/about.overview.vue'));
const XEmojis = defineAsyncComponent(() => import('@/pages/about.emojis.vue'));
diff --git a/packages/frontend/src/pages/achievements.vue b/packages/frontend/src/pages/achievements.vue
index 423e709da4..1560403b70 100644
--- a/packages/frontend/src/pages/achievements.vue
+++ b/packages/frontend/src/pages/achievements.vue
@@ -5,9 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader>
- <MkSpacer :contentMax="1200">
+ <div class="_spacer" style="--MI_SPACER-w: 1200px;">
<MkAchievements :user="$i"/>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue
index 1e3e106842..8495642a8c 100644
--- a/packages/frontend/src/pages/admin-file.vue
+++ b/packages/frontend/src/pages/admin-file.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSpacer v-if="file" :contentMax="600" :marginMin="16" :marginMax="32">
+ <div v-if="file" class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<div v-if="tab === 'overview'" class="cxqhhsmd _gaps_m">
<a class="thumbnail" :href="file.url" target="_blank">
<MkDriveFileThumbnail class="thumbnail" :file="file" fit="contain"/>
@@ -65,7 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkObjectView v-if="info" tall :value="info">
</MkObjectView>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index 06d557f045..efe547ff21 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="600" :marginMin="16" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<FormSuspense :p="init">
<div v-if="tab === 'overview'" class="_gaps_m">
<div class="aeakzknw">
@@ -201,7 +201,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkObjectView>
</div>
</FormSuspense>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue
index 10925fa4ab..b69c818b48 100644
--- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue
+++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ mode === 'create' ? i18n.ts._abuseReport._notificationRecipient.createRecipient : i18n.ts._abuseReport._notificationRecipient.modifyRecipient }}
</template>
<div v-if="loading === 0" style="display: flex; flex-direction: column; min-height: 100%;">
- <MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px; flex-grow: 1;">
<div :class="$style.root" class="_gaps_m">
<MkInput v-model="title">
<template #label>{{ i18n.ts.title }}</template>
@@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.enable }}</template>
</MkSwitch>
</div>
- </MkSpacer>
+ </div>
<div :class="$style.footer" class="_buttonsCenter">
<MkButton primary rounded :disabled="disableSubmitButton" @click="onSubmitClicked"><i class="ti ti-check"></i> {{ i18n.ts.ok }}</MkButton>
diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue
index a569ab7c33..f5e77cbe4e 100644
--- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue
+++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="900">
+ <div class="_spacer" style="--MI_SPACER-w: 900px;">
<div :class="$style.root" class="_gaps_m">
<div :class="$style.addButton">
<MkButton primary @click="onAddButtonClicked">
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
/>
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue
index 3f1df95f25..4ec4372492 100644
--- a/packages/frontend/src/pages/admin/abuses.vue
+++ b/packages/frontend/src/pages/admin/abuses.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="900">
+ <div class="_spacer" style="--MI_SPACER-w: 900px;">
<div :class="$style.root" class="_gaps">
<div :class="$style.subMenus" class="_gaps">
<MkButton link to="/admin/abuse-report-notification-recipient" primary>{{ i18n.ts.notificationSetting }}</MkButton>
@@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkPagination>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue
index aa8ba2f7c3..c5baeda7b0 100644
--- a/packages/frontend/src/pages/admin/ads.vue
+++ b/packages/frontend/src/pages/admin/ads.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="900">
+ <div class="_spacer" style="--MI_SPACER-w: 900px;">
<MkSelect v-model="filterType" :class="$style.input" @update:modelValue="filterItems">
<template #label>{{ i18n.ts.state }}</template>
<option value="all">{{ i18n.ts.all }}</option>
@@ -77,7 +77,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<i class="ti ti-reload"></i>{{ i18n.ts.more }}
</MkButton>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue
index ea7f0cc73d..b2d7b4889a 100644
--- a/packages/frontend/src/pages/admin/announcements.vue
+++ b/packages/frontend/src/pages/admin/announcements.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="900">
+ <div class="_spacer" style="--MI_SPACER-w: 900px;">
<div class="_gaps">
<MkInfo>{{ i18n.ts._announcement.shouldNotBeUsedToPresentPermanentInfo }}</MkInfo>
<MkInfo v-if="announcements.length > 5" warn>{{ i18n.ts._announcement.tooManyActiveAnnouncementDescription }}</MkInfo>
@@ -79,7 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkButton>
</template>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/approvals.vue b/packages/frontend/src/pages/admin/approvals.vue
index c972728c85..77b4cc38a1 100644
--- a/packages/frontend/src/pages/admin/approvals.vue
+++ b/packages/frontend/src/pages/admin/approvals.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div>
<PageWithHeader :tabs="headerTabs">
- <MkSpacer :contentMax="900">
+ <div class="_spacer" style="--MI_SPACER-w: 900px;">
<div class="_gaps_m">
<MkPagination ref="paginationComponent" :pagination="pagination" :displayLimit="50">
<template #default="{ items }">
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</MkPagination>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</div>
</template>
diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue
index 1afc643a0a..64faf49848 100644
--- a/packages/frontend/src/pages/admin/branding.vue
+++ b/packages/frontend/src/pages/admin/branding.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :tabs="headerTabs">
- <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<FormSuspense :p="init">
<div class="_gaps_m">
<MkInput v-model="iconUrl" type="url">
@@ -105,12 +105,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkTextarea>
</div>
</FormSuspense>
- </MkSpacer>
+ </div>
<template #footer>
<div :class="$style.footer">
- <MkSpacer :contentMax="700" :marginMin="16" :marginMax="16">
+ <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 16px;">
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
- </MkSpacer>
+ </div>
</div>
</template>
</PageWithHeader>
diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.logs.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.logs.vue
index 4b145db0ed..c544561b13 100644
--- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.logs.vue
+++ b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.logs.vue
@@ -14,9 +14,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #header>
<i class="ti ti-notes" style="margin-right: 0.5em;"></i> {{ i18n.ts._customEmojisManager._gridCommon.registrationLogs }}
</template>
- <MkSpacer>
+ <div class="_spacer">
<XRegisterLogs :logs="logs"/>
- </MkSpacer>
+ </div>
</MkWindow>
</template>
diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.search.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.search.vue
index ae43507d66..9938d5cc4a 100644
--- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.search.vue
+++ b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.search.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<i class="ti ti-search" style="margin-right: 0.5em;"></i> {{ i18n.ts.search }}
</template>
<div :class="$style.root">
- <MkSpacer>
+ <div class="_spacer">
<div class="_gaps">
<div class="_gaps_s">
<MkInput
@@ -107,7 +107,7 @@ SPDX-License-Identifier: AGPL-3.0-only
/>
</MkFolder>
</div>
- </MkSpacer>
+ </div>
<div :class="$style.footerActions">
<MkButton primary @click="onSearchRequest">
{{ i18n.ts.search }}
diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue
index 260177c894..59b780bff6 100644
--- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue
+++ b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue
@@ -285,7 +285,7 @@ const searchQuery = ref<EmojiSearchQuery>({
localOnly: null,
roles: [],
sortOrders: [],
- limit: 25,
+ limit: 100,
});
let searchWindowOpening = false;
diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.local.vue
deleted file mode 100644
index 6e7e7e53e3..0000000000
--- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.vue
+++ /dev/null
@@ -1,35 +0,0 @@
-<!--
-SPDX-FileCopyrightText: syuilo and other misskey contributors
-SPDX-License-Identifier: AGPL-3.0-only
--->
-
-<template>
-<MkStickyContainer>
- <template #header>
- <MkPageHeader v-model:tab="headerTab" :tabs="headerTabs" hideTitle thin/>
- </template>
- <XListComponent v-if="headerTab === 'list'" key="localList"/>
- <MkSpacer v-else key="localRegister">
- <XRegisterComponent/>
- </MkSpacer>
-</MkStickyContainer>
-</template>
-
-<script setup lang="ts">
-import { ref, computed } from 'vue';
-import { i18n } from '@/i18n.js';
-import XListComponent from '@/pages/admin/custom-emojis-manager.local.list.vue';
-import XRegisterComponent from '@/pages/admin/custom-emojis-manager.local.register.vue';
-
-type PageMode = 'list' | 'register';
-
-const headerTab = ref<PageMode>('list');
-
-const headerTabs = computed(() => [{
- key: 'list',
- title: i18n.ts._customEmojisManager._local.tabTitleList,
-}, {
- key: 'register',
- title: i18n.ts._customEmojisManager._local.tabTitleRegister,
-}]);
-</script>
diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.register.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.register.vue
index eff7efd0fa..e8e944df32 100644
--- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.register.vue
+++ b/packages/frontend/src/pages/admin/custom-emojis-manager.register.vue
@@ -4,67 +4,69 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div class="_gaps">
- <MkFolder>
- <template #icon><i class="ti ti-settings"></i></template>
- <template #label>{{ i18n.ts._customEmojisManager._local._register.uploadSettingTitle }}</template>
- <template #caption>{{ i18n.ts._customEmojisManager._local._register.uploadSettingDescription }}</template>
+<div class="_spacer">
+ <div class="_gaps">
+ <MkFolder>
+ <template #icon><i class="ti ti-settings"></i></template>
+ <template #label>{{ i18n.ts._customEmojisManager._local._register.uploadSettingTitle }}</template>
+ <template #caption>{{ i18n.ts._customEmojisManager._local._register.uploadSettingDescription }}</template>
- <div class="_gaps">
- <MkSelect v-model="selectedFolderId">
- <template #label>{{ i18n.ts.uploadFolder }}</template>
- <option v-for="folder in uploadFolders" :key="folder.id" :value="folder.id">
- {{ folder.name }}
- </option>
- </MkSelect>
+ <div class="_gaps">
+ <MkSelect v-model="selectedFolderId">
+ <template #label>{{ i18n.ts.uploadFolder }}</template>
+ <option v-for="folder in uploadFolders" :key="folder.id" :value="folder.id">
+ {{ folder.name }}
+ </option>
+ </MkSelect>
- <MkSwitch v-model="directoryToCategory">
- <template #label>{{ i18n.ts._customEmojisManager._local._register.directoryToCategoryLabel }}</template>
- <template #caption>{{ i18n.ts._customEmojisManager._local._register.directoryToCategoryCaption }}</template>
- </MkSwitch>
- </div>
- </MkFolder>
+ <MkSwitch v-model="directoryToCategory">
+ <template #label>{{ i18n.ts._customEmojisManager._local._register.directoryToCategoryLabel }}</template>
+ <template #caption>{{ i18n.ts._customEmojisManager._local._register.directoryToCategoryCaption }}</template>
+ </MkSwitch>
+ </div>
+ </MkFolder>
- <MkFolder>
- <template #icon><i class="ti ti-notes"></i></template>
- <template #label>{{ i18n.ts._customEmojisManager._gridCommon.registrationLogs }}</template>
- <template #caption>
- {{ i18n.ts._customEmojisManager._gridCommon.registrationLogsCaption }}
- </template>
- <XRegisterLogs :logs="requestLogs"/>
- </MkFolder>
+ <MkFolder>
+ <template #icon><i class="ti ti-notes"></i></template>
+ <template #label>{{ i18n.ts._customEmojisManager._gridCommon.registrationLogs }}</template>
+ <template #caption>
+ {{ i18n.ts._customEmojisManager._gridCommon.registrationLogsCaption }}
+ </template>
+ <XRegisterLogs :logs="requestLogs"/>
+ </MkFolder>
- <div
- :class="[$style.uploadBox, [isDragOver ? $style.dragOver : {}]]"
- @dragover.prevent="isDragOver = true"
- @dragleave.prevent="isDragOver = false"
- @drop.prevent.stop="onDrop"
- >
- <div style="margin-top: 1em">
- {{ i18n.ts._customEmojisManager._local._register.emojiInputAreaCaption }}
+ <div
+ :class="[$style.uploadBox, [isDragOver ? $style.dragOver : {}]]"
+ @dragover.prevent="isDragOver = true"
+ @dragleave.prevent="isDragOver = false"
+ @drop.prevent.stop="onDrop"
+ >
+ <div style="margin-top: 1em">
+ {{ i18n.ts._customEmojisManager._local._register.emojiInputAreaCaption }}
+ </div>
+ <ul>
+ <li>{{ i18n.ts._customEmojisManager._local._register.emojiInputAreaList1 }}</li>
+ <li><a @click.prevent="onFileSelectClicked">{{ i18n.ts._customEmojisManager._local._register.emojiInputAreaList2 }}</a></li>
+ <li><a @click.prevent="onDriveSelectClicked">{{ i18n.ts._customEmojisManager._local._register.emojiInputAreaList3 }}</a></li>
+ </ul>
</div>
- <ul>
- <li>{{ i18n.ts._customEmojisManager._local._register.emojiInputAreaList1 }}</li>
- <li><a @click.prevent="onFileSelectClicked">{{ i18n.ts._customEmojisManager._local._register.emojiInputAreaList2 }}</a></li>
- <li><a @click.prevent="onDriveSelectClicked">{{ i18n.ts._customEmojisManager._local._register.emojiInputAreaList3 }}</a></li>
- </ul>
- </div>
- <div v-if="gridItems.length > 0" :class="$style.gridArea">
- <MkGrid
- :data="gridItems"
- :settings="setupGrid()"
- @event="onGridEvent"
- />
- </div>
+ <div v-if="gridItems.length > 0" :class="$style.gridArea">
+ <MkGrid
+ :data="gridItems"
+ :settings="setupGrid()"
+ @event="onGridEvent"
+ />
+ </div>
- <div v-if="gridItems.length > 0" :class="$style.footer">
- <MkButton primary :disabled="registerButtonDisabled" @click="onRegistryClicked">
- {{ i18n.ts.registration }}
- </MkButton>
- <MkButton @click="onClearClicked">
- {{ i18n.ts.clear }}
- </MkButton>
+ <div v-if="gridItems.length > 0" :class="$style.footer">
+ <MkButton primary :disabled="registerButtonDisabled" @click="onRegistryClicked">
+ {{ i18n.ts.registration }}
+ </MkButton>
+ <MkButton @click="onClearClicked">
+ {{ i18n.ts.clear }}
+ </MkButton>
+ </div>
</div>
</div>
</template>
@@ -407,7 +409,7 @@ function fromDriveFile(it: Misskey.entities.DriveFile): GridItem {
return {
fileId: it.id,
url: it.url,
- name: it.name.replace(/(\.[a-zA-Z0-9]+)+$/, ''),
+ name: it.name.replace(/(\.[a-zA-Z0-9]+)+$/, '').replaceAll('-', '_').replaceAll(' ', '_'),
host: '',
category: '',
aliases: '',
diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue
index c868a700f1..2fd7e331a2 100644
--- a/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue
+++ b/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue
@@ -142,6 +142,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<script setup lang="ts">
import { computed, onMounted, ref, useCssModule } from 'vue';
import * as Misskey from 'misskey-js';
+import type { GridSortOrderKey, RequestLogItem } from '@/pages/admin/custom-emojis-manager.impl.js';
+import type { GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js';
+import type { GridSetting } from '@/components/grid/grid.js';
+import type { SortOrder } from '@/components/MkSortOrderEditor.define.js';
import MkRemoteEmojiEditDialog from '@/components/MkRemoteEmojiEditDialog.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
@@ -157,11 +161,6 @@ import MkPagingButtons from '@/components/MkPagingButtons.vue';
import MkSortOrderEditor from '@/components/MkSortOrderEditor.vue';
import { useLoading } from '@/components/hook/useLoading.js';
-import type { GridSortOrderKey, RequestLogItem } from '@/pages/admin/custom-emojis-manager.impl.js';
-import type { GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js';
-import type { GridSetting } from '@/components/grid/grid.js';
-import type { SortOrder } from '@/components/MkSortOrderEditor.define.js';
-
type GridItem = {
checked: boolean;
id: string;
@@ -260,7 +259,7 @@ const queryHost = ref<string | null>(null);
const queryLicense = ref<string | null>(null);
const queryUri = ref<string | null>(null);
const queryPublicUrl = ref<string | null>(null);
-const queryLimit = ref<number>(25);
+const queryLimit = ref<number>(100);
const previousQuery = ref<string | undefined>(undefined);
const sortOrders = ref<SortOrder<GridSortOrderKey>[]>([]);
const requestLogs = ref<RequestLogItem[]>([]);
diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager2.vue b/packages/frontend/src/pages/admin/custom-emojis-manager2.vue
index 7667206fa8..14773d7f04 100644
--- a/packages/frontend/src/pages/admin/custom-emojis-manager2.vue
+++ b/packages/frontend/src/pages/admin/custom-emojis-manager2.vue
@@ -4,25 +4,20 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div>
- <MkStickyContainer>
- <template #header>
- <MkPageHeader v-model:tab="headerTab" :tabs="headerTabs"/>
- </template>
- <XGridLocalComponent v-if="headerTab === 'local'" :class="$style.local"/>
- <XGridRemoteComponent v-else/>
- </MkStickyContainer>
-</div>
+<PageWithHeader v-model:tab="headerTab" :tabs="headerTabs">
+ <XGridLocalComponent v-if="headerTab === 'local'" :class="$style.local"/>
+ <XGridRemoteComponent v-else-if="headerTab === 'remote'" :class="$style.remote"/>
+ <XRegisterComponent v-else-if="headerTab === 'register'" :class="$style.register"/>
+</PageWithHeader>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
-import XGridLocalComponent from '@/pages/admin/custom-emojis-manager.local.vue';
+import XGridLocalComponent from '@/pages/admin/custom-emojis-manager.local.list.vue';
import XGridRemoteComponent from '@/pages/admin/custom-emojis-manager.remote.vue';
-import MkPageHeader from '@/components/global/MkPageHeader.vue';
-import MkStickyContainer from '@/components/global/MkStickyContainer.vue';
+import XRegisterComponent from '@/pages/admin/custom-emojis-manager.register.vue';
type PageMode = 'local' | 'remote';
@@ -34,6 +29,9 @@ const headerTabs = computed(() => [{
}, {
key: 'remote',
title: i18n.ts.remote,
+}, {
+ key: 'register',
+ title: i18n.ts._customEmojisManager._local.tabTitleRegister,
}]);
definePage(computed(() => ({
diff --git a/packages/frontend/src/pages/admin/database.vue b/packages/frontend/src/pages/admin/database.vue
index 6691142a64..d51f43c098 100644
--- a/packages/frontend/src/pages/admin/database.vue
+++ b/packages/frontend/src/pages/admin/database.vue
@@ -5,14 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="800" :marginMin="16" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-w: 800px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<FormSuspense v-slot="{ result: database }" :p="databasePromiseFactory">
<MkKeyValue v-for="table in database" :key="table[0]" oneline style="margin: 1em 0;">
<template #key>{{ table[0] }}</template>
<template #value>{{ bytes(table[1].size) }} ({{ number(table[1].count) }} recs)</template>
</MkKeyValue>
</FormSuspense>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue
index f1827d756b..cdbca12435 100644
--- a/packages/frontend/src/pages/admin/email-settings.vue
+++ b/packages/frontend/src/pages/admin/email-settings.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :tabs="headerTabs">
- <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<FormSuspense :p="init">
<div class="_gaps_m">
<MkSwitch v-model="enableEmail">
@@ -48,15 +48,15 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</div>
</FormSuspense>
- </MkSpacer>
+ </div>
<template #footer>
<div :class="$style.footer">
- <MkSpacer :contentMax="700" :marginMin="16" :marginMax="16">
+ <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 16px;">
<div class="_buttons">
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
<MkButton rounded @click="testEmail"><i class="ti ti-send"></i> {{ i18n.ts.testEmail }}</MkButton>
</div>
- </MkSpacer>
+ </div>
</div>
</template>
</PageWithHeader>
diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue
index 131989844a..3cfc51af00 100644
--- a/packages/frontend/src/pages/admin/external-services.vue
+++ b/packages/frontend/src/pages/admin/external-services.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<FormSuspense :p="init">
<div class="_gaps_m">
<MkFolder>
@@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkFolder>
</div>
</FormSuspense>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/federation-job-queue.vue b/packages/frontend/src/pages/admin/federation-job-queue.vue
index 77e460d0eb..173cffedc2 100644
--- a/packages/frontend/src/pages/admin/federation-job-queue.vue
+++ b/packages/frontend/src/pages/admin/federation-job-queue.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="800">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<XQueue v-if="tab === 'deliver'" domain="deliver"/>
<XQueue v-else-if="tab === 'inbox'" domain="inbox"/>
<br>
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton @click="promoteAllQueues"><i class="ti ti-reload"></i> {{ i18n.ts.retryAllQueuesNow }}</MkButton>
<MkButton danger @click="clear"><i class="ti ti-trash"></i> {{ i18n.ts.clearQueue }}</MkButton>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue
index dcc8c52e55..16bed50002 100644
--- a/packages/frontend/src/pages/admin/federation.vue
+++ b/packages/frontend/src/pages/admin/federation.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="900">
+ <div class="_spacer" style="--MI_SPACER-w: 900px;">
<div class="_gaps">
<div>
<MkInput v-model="host" :debounce="true" class="">
@@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkPagination>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/files.vue b/packages/frontend/src/pages/admin/files.vue
index 12c633bf7f..87595a820b 100644
--- a/packages/frontend/src/pages/admin/files.vue
+++ b/packages/frontend/src/pages/admin/files.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="900">
+ <div class="_spacer" style="--MI_SPACER-w: 900px;">
<div class="_gaps">
<div class="inputs" style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;">
<MkSelect v-model="origin" style="margin: 0; flex: 1;">
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<MkFileListForAdmin :pagination="pagination" :viewMode="viewMode"/>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index c366a7cd6a..0cb9d54bbe 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div ref="el" class="hiyeyicy" :class="{ wide: !narrow }">
<div v-if="!narrow || currentPage?.route.name == null" class="nav">
- <MkSpacer :contentMax="700" :marginMin="16">
+ <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px;">
<div class="lxpfedzu _gaps">
<div class="banner">
<img :src="instance.iconUrl || '/favicon.ico'" alt="" class="icon"/>
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSuperMenu :def="menuDef" :grid="narrow"></MkSuperMenu>
</div>
- </MkSpacer>
+ </div>
</div>
<div v-if="!(narrow && currentPage?.route.name == null)" class="main _pageContainer" style="height: 100%;">
<NestedRouterView/>
diff --git a/packages/frontend/src/pages/admin/invites.vue b/packages/frontend/src/pages/admin/invites.vue
index 7f0c35d6bc..83b040935b 100644
--- a/packages/frontend/src/pages/admin/invites.vue
+++ b/packages/frontend/src/pages/admin/invites.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="800">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<div class="_gaps_m">
<MkFolder :expanded="false">
<template #icon><i class="ti ti-plus"></i></template>
@@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</MkPagination>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/job-queue.vue b/packages/frontend/src/pages/admin/job-queue.vue
index 528c473c4f..3d405c566f 100644
--- a/packages/frontend/src/pages/admin/job-queue.vue
+++ b/packages/frontend/src/pages/admin/job-queue.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSpacer>
+ <div class="_spacer">
<div v-if="tab === '-'" class="_gaps">
<div :class="$style.queues">
<div v-for="q in queueInfos" :key="q.name" :class="$style.queue" @click="tab = q.name">
@@ -139,7 +139,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</template>
- <MkSpacer>
+ <div class="_spacer">
<MkInput
v-model="searchQuery"
:placeholder="i18n.ts.search"
@@ -163,10 +163,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<XJob :job="job" :queueType="tab" style="margin: 4px 0;" @needRefresh="refreshJob(job.id)"/>
</template>
</MkTl>
- </MkSpacer>
+ </div>
</MkFolder>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue
index 5491a2df45..9675bdc21a 100644
--- a/packages/frontend/src/pages/admin/moderation.vue
+++ b/packages/frontend/src/pages/admin/moderation.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :tabs="headerTabs">
- <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<FormSuspense :p="init">
<div class="_gaps_m">
<MkSwitch :modelValue="enableRegistration" @update:modelValue="onChange_enableRegistration">
@@ -148,7 +148,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkFolder>
</div>
</FormSuspense>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue
index 2d43e3b790..cf7d46153f 100644
--- a/packages/frontend/src/pages/admin/modlog.vue
+++ b/packages/frontend/src/pages/admin/modlog.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="900">
+ <div class="_spacer" style="--MI_SPACER-w: 900px;">
<div class="_gaps">
<div style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;">
<MkSelect v-model="type" style="margin: 0; flex: 1;">
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton primary rounded style="margin: 0 auto;" @click="fetchMore">{{ i18n.ts.loadMore }}</MkButton>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue
index 36f4392142..7a46ae41c6 100644
--- a/packages/frontend/src/pages/admin/object-storage.vue
+++ b/packages/frontend/src/pages/admin/object-storage.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :tabs="headerTabs">
- <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<FormSuspense :p="init">
<div class="_gaps_m">
<MkSwitch v-model="useObjectStorage">{{ i18n.ts.useObjectStorage }}</MkSwitch>
@@ -70,12 +70,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</div>
</FormSuspense>
- </MkSpacer>
+ </div>
<template #footer>
<div :class="$style.footer">
- <MkSpacer :contentMax="700" :marginMin="16" :marginMax="16">
+ <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 16px;">
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
- </MkSpacer>
+ </div>
</div>
</template>
</PageWithHeader>
diff --git a/packages/frontend/src/pages/admin/overview.vue b/packages/frontend/src/pages/admin/overview.vue
index 616815a6a6..caa888b51d 100644
--- a/packages/frontend/src/pages/admin/overview.vue
+++ b/packages/frontend/src/pages/admin/overview.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer :contentMax="1000">
+<div class="_spacer" style="--MI_SPACER-w: 1000px;">
<div ref="rootEl" :class="$style.root">
<MkFoldableSection class="item">
<template #header>Stats</template>
@@ -61,7 +61,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<XQueue domain="inbox"/>
</MkFoldableSection>
</div>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/pages/admin/performance.vue b/packages/frontend/src/pages/admin/performance.vue
index 075db4ebef..a272b9adea 100644
--- a/packages/frontend/src/pages/admin/performance.vue
+++ b/packages/frontend/src/pages/admin/performance.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<div class="_gaps">
<div class="_panel" style="padding: 16px;">
<MkSwitch v-model="enableServerMachineStats" @change="onChange_enableServerMachineStats">
@@ -102,7 +102,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkFolder>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/relays.vue b/packages/frontend/src/pages/admin/relays.vue
index 7803edc360..aabf64342e 100644
--- a/packages/frontend/src/pages/admin/relays.vue
+++ b/packages/frontend/src/pages/admin/relays.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="800">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<div class="_gaps">
<div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel" style="padding: 16px;">
<div>{{ relay.inbox }}</div>
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton>
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue
index 62777f59ef..7790fe3925 100644
--- a/packages/frontend/src/pages/admin/roles.edit.vue
+++ b/packages/frontend/src/pages/admin/roles.edit.vue
@@ -5,14 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :tabs="headerTabs">
- <MkSpacer :contentMax="600" :marginMin="16" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<XEditor v-if="data" v-model="data"/>
- </MkSpacer>
+ </div>
<template #footer>
<div :class="$style.footer">
- <MkSpacer :contentMax="600" :marginMin="16" :marginMax="16">
+ <div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px; --MI_SPACER-max: 16px;">
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
- </MkSpacer>
+ </div>
</div>
</template>
</PageWithHeader>
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index c49a1bf286..bca619c2e1 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -445,6 +445,26 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkFolder>
+ <MkFolder v-if="matchQuery([i18n.ts._role._options.maxFileSize, 'maxFileSizeMb'])">
+ <template #label>{{ i18n.ts._role._options.maxFileSize }}</template>
+ <template #suffix>
+ <span v-if="role.policies.maxFileSizeMb.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
+ <span v-else>{{ role.policies.maxFileSizeMb.value + 'MB' }}</span>
+ <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.maxFileSizeMb)"></i></span>
+ </template>
+ <div class="_gaps">
+ <MkSwitch v-model="role.policies.maxFileSizeMb.useDefault" :readonly="readonly">
+ <template #label>{{ i18n.ts._role.useBaseValue }}</template>
+ </MkSwitch>
+ <MkInput v-model="role.policies.maxFileSizeMb.value" :disabled="role.policies.maxFileSizeMb.useDefault" type="number" :readonly="readonly">
+ <template #suffix>MB</template>
+ </MkInput>
+ <MkRange v-model="role.policies.maxFileSizeMb.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+ <template #label>{{ i18n.ts._role.priority }}</template>
+ </MkRange>
+ </div>
+ </MkFolder>
+
<MkFolder v-if="matchQuery([i18n.ts._role._options.alwaysMarkNsfw, 'alwaysMarkNsfw'])">
<template #label>{{ i18n.ts._role._options.alwaysMarkNsfw }}</template>
<template #suffix>
diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue
index 992f47bff5..04e6405f9b 100644
--- a/packages/frontend/src/pages/admin/roles.role.vue
+++ b/packages/frontend/src/pages/admin/roles.role.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700">
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
<div class="_gaps">
<div class="_buttons">
<MkButton primary rounded @click="edit"><i class="ti ti-pencil"></i> {{ i18n.ts.edit }}</MkButton>
@@ -54,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkFolder>
<MkInfo v-else>{{ i18n.ts._role.isConditionalRole }}</MkInfo>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index 2e9a33d1b8..52b8240733 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700">
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
<div class="_gaps">
<MkFolder>
<template #label>{{ i18n.ts._role.baseRole }}</template>
@@ -165,6 +165,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkInput>
</MkFolder>
+ <MkFolder v-if="matchQuery([i18n.ts._role._options.maxFileSize, 'maxFileSizeMb'])">
+ <template #label>{{ i18n.ts._role._options.maxFileSize }}</template>
+ <template #suffix>{{ policies.maxFileSizeMb }}MB</template>
+ <MkInput v-model="policies.maxFileSizeMb" type="number">
+ <template #suffix>MB</template>
+ </MkInput>
+ </MkFolder>
+
<MkFolder v-if="matchQuery([i18n.ts._role._options.alwaysMarkNsfw, 'alwaysMarkNsfw'])">
<template #label>{{ i18n.ts._role._options.alwaysMarkNsfw }}</template>
<template #suffix>{{ policies.alwaysMarkNsfw ? i18n.ts.yes : i18n.ts.no }}</template>
@@ -310,7 +318,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkFoldableSection>
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index 537c788ccf..414dc145ff 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<div class="_gaps_m">
<MkFolder v-if="meta.federation !== 'none'">
<template #label>{{ i18n.ts.authorizedFetchSection }}</template>
@@ -87,7 +87,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkFolder>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue
index 98cd0775fe..4ad056edff 100644
--- a/packages/frontend/src/pages/admin/server-rules.vue
+++ b/packages/frontend/src/pages/admin/server-rules.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :tabs="headerTabs">
- <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<div class="_gaps_m">
<div>{{ i18n.ts._serverRules.description }}</div>
<Sortable
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index 21fd1b4b1a..40335e7914 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :tabs="headerTabs">
- <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<div class="_gaps_m">
<MkFolder :defaultOpen="true">
<template #icon><i class="ti ti-info-circle"></i></template>
@@ -282,7 +282,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkFolder>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/system-webhook.vue b/packages/frontend/src/pages/admin/system-webhook.vue
index a3214028e6..d5402f608c 100644
--- a/packages/frontend/src/pages/admin/system-webhook.vue
+++ b/packages/frontend/src/pages/admin/system-webhook.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="900">
+ <div class="_spacer" style="--MI_SPACER-w: 900px;">
<div class="_gaps_m">
<MkButton primary @click="onCreateWebhookClicked">
<i class="ti ti-plus"></i> {{ i18n.ts._webhookSettings.createWebhook }}
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</FormSection>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/users.vue b/packages/frontend/src/pages/admin/users.vue
index d37f7dbf80..080ed9b90c 100644
--- a/packages/frontend/src/pages/admin/users.vue
+++ b/packages/frontend/src/pages/admin/users.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="900">
+ <div class="_spacer" style="--MI_SPACER-w: 900px;">
<div class="_gaps">
<div :class="$style.inputs">
<MkButton style="margin-left: auto" @click="resetQuery">{{ i18n.ts.reset }}</MkButton>
@@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkPagination>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/ads.vue b/packages/frontend/src/pages/ads.vue
index 700ac0bd1a..31b66584d9 100644
--- a/packages/frontend/src/pages/ads.vue
+++ b/packages/frontend/src/pages/ads.vue
@@ -5,11 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader>
- <MkSpacer :contentMax="500">
+ <div class="_spacer" style="--MI_SPACER-w: 500px;">
<div class="_gaps">
<MkAd v-for="ad in instance.ads" :key="ad.id" :specify="ad"/>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/announcement.vue b/packages/frontend/src/pages/announcement.vue
index 4bba0acdc2..9fa06dfd9a 100644
--- a/packages/frontend/src/pages/announcement.vue
+++ b/packages/frontend/src/pages/announcement.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="800">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<Transition
:enterActiveClass="prefer.s.animation ? $style.fadeEnterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.fadeLeaveActive : ''"
@@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkError v-else-if="error" @retry="fetch()"/>
<MkLoading v-else/>
</Transition>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue
index d529ad592c..e145121e23 100644
--- a/packages/frontend/src/pages/announcements.vue
+++ b/packages/frontend/src/pages/announcements.vue
@@ -4,8 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="800">
+<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<MkSwiper v-model:tab="tab" :tabs="headerTabs">
<div class="_gaps">
<MkInfo v-if="$i && $i.hasUnreadAnnouncement && tab === 'current'" warn>{{ i18n.ts.youHaveUnreadAnnouncements }}</MkInfo>
@@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkPagination>
</div>
</MkSwiper>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
@@ -50,7 +50,6 @@ import { ref, computed } from 'vue';
import MkPagination from '@/components/MkPagination.vue';
import MkButton from '@/components/MkButton.vue';
import MkInfo from '@/components/MkInfo.vue';
-import MkSwiper from '@/components/MkSwiper.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue
index c5c5b2144f..1bf7e257e5 100644
--- a/packages/frontend/src/pages/antenna-timeline.vue
+++ b/packages/frontend/src/pages/antenna-timeline.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :displayBackButton="true" :tabs="headerTabs">
- <MkSpacer :contentMax="800">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<div ref="rootEl">
<div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
<div :class="$style.tl">
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
/>
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/api-console.vue b/packages/frontend/src/pages/api-console.vue
index 53020bfb08..140974e695 100644
--- a/packages/frontend/src/pages/api-console.vue
+++ b/packages/frontend/src/pages/api-console.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700">
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
<div class="_gaps_m">
<div class="_gaps_m">
<MkInput v-model="endpoint" :datalist="endpoints" debounce @update:modelValue="onEndpointChange()">
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkTextarea>
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue
index 5aa8dbcc5d..26a4edfe0c 100644
--- a/packages/frontend/src/pages/auth.vue
+++ b/packages/frontend/src/pages/auth.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="500">
+ <div class="_spacer" style="--MI_SPACER-w: 500px;">
<div v-if="state == 'fetch-session-error'">
<p>{{ i18n.ts.somethingHappened }}</p>
</div>
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<p :class="$style.loginMessage">{{ i18n.ts._auth.pleaseLogin }}</p>
<MkSignin @login="onLogin"/>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue b/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue
index 5a5e305f80..cb0e1666f8 100644
--- a/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue
+++ b/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-else #header>New decoration</template>
<div style="display: flex; flex-direction: column; min-height: 100%;">
- <MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px; flex-grow: 1;">
<div class="_gaps_m">
<div :class="$style.preview">
<div :class="[$style.previewItem, $style.light]">
@@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkFolder>
<MkButton v-if="avatarDecoration" danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
</div>
- </MkSpacer>
+ </div>
<div :class="$style.footer">
<MkButton primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.avatarDecoration ? i18n.ts.update : i18n.ts.create }}</MkButton>
</div>
diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue
index 2bab449089..675e558de9 100644
--- a/packages/frontend/src/pages/avatar-decorations.vue
+++ b/packages/frontend/src/pages/avatar-decorations.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="900">
+ <div class="_spacer" style="--MI_SPACER-w: 900px;">
<div class="_gaps">
<div :class="$style.decorations">
<div
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue
index 084fee15cf..009514cdc8 100644
--- a/packages/frontend/src/pages/channel-editor.vue
+++ b/packages/frontend/src/pages/channel-editor.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700">
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
<div v-if="channelId == null || channel != null" class="_gaps_m">
<MkInput v-model="name">
<template #label>{{ i18n.ts.name }}</template>
@@ -63,7 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton v-if="channelId" danger @click="archive()"><i class="ti ti-trash"></i> {{ i18n.ts.archive }}</MkButton>
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue
index 606fb06324..56d037758f 100644
--- a/packages/frontend/src/pages/channel.vue
+++ b/packages/frontend/src/pages/channel.vue
@@ -4,8 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700">
+<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true">
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
<MkSwiper v-model:tab="tab" :tabs="headerTabs">
<div v-if="channel && tab === 'overview'" class="_gaps">
<div class="_panel" :class="$style.bannerContainer">
@@ -58,14 +58,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</MkSwiper>
- </MkSpacer>
+ </div>
<template #footer>
<div :class="$style.footer">
- <MkSpacer :contentMax="700" :marginMin="16" :marginMax="16">
+ <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 16px;">
<div class="_buttonsCenter">
<MkButton inline rounded primary gradate @click="openPostForm()"><i class="ti ti-pencil"></i> {{ i18n.ts.postToTheChannel }}</MkButton>
</div>
- </MkSpacer>
+ </div>
</div>
</template>
</PageWithHeader>
@@ -93,7 +93,6 @@ import { prefer } from '@/preferences.js';
import MkNote from '@/components/MkNote.vue';
import MkInfo from '@/components/MkInfo.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
-import MkSwiper from '@/components/MkSwiper.vue';
import { isSupportShare } from '@/utility/navigator.js';
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
import { notesSearchAvailable } from '@/utility/check-permissions.js';
diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue
index 27a6a6168d..b2b2bc02d2 100644
--- a/packages/frontend/src/pages/channels.vue
+++ b/packages/frontend/src/pages/channels.vue
@@ -4,57 +4,55 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="1200">
- <MkSwiper v-model:tab="tab" :tabs="headerTabs">
- <div v-if="tab === 'search'" :class="$style.searchRoot">
- <div class="_gaps">
- <MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
- <template #prefix><i class="ti ti-search"></i></template>
- </MkInput>
- <MkRadios v-model="searchType" @update:modelValue="search()">
- <option value="nameAndDescription">{{ i18n.ts._channel.nameAndDescription }}</option>
- <option value="nameOnly">{{ i18n.ts._channel.nameOnly }}</option>
- </MkRadios>
- <MkButton large primary gradate rounded @click="search">{{ i18n.ts.search }}</MkButton>
- </div>
-
- <MkFoldableSection v-if="channelPagination">
- <template #header>{{ i18n.ts.searchResult }}</template>
- <MkChannelList :key="key" :pagination="channelPagination"/>
- </MkFoldableSection>
- </div>
- <div v-if="tab === 'featured'">
- <MkPagination v-slot="{items}" :pagination="featuredPagination">
- <div :class="$style.root">
- <MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
- </div>
- </MkPagination>
- </div>
- <div v-else-if="tab === 'favorites'">
- <MkPagination v-slot="{items}" :pagination="favoritesPagination">
- <div :class="$style.root">
- <MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
- </div>
- </MkPagination>
- </div>
- <div v-else-if="tab === 'following'">
- <MkPagination v-slot="{items}" :pagination="followingPagination">
- <div :class="$style.root">
- <MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
- </div>
- </MkPagination>
- </div>
- <div v-else-if="tab === 'owned'">
- <MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton>
- <MkPagination v-slot="{items}" :pagination="ownedPagination">
- <div :class="$style.root">
- <MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
- </div>
- </MkPagination>
+<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true">
+ <div class="_spacer" style="--MI_SPACER-w: 1200px;">
+ <div v-if="tab === 'search'" :class="$style.searchRoot">
+ <div class="_gaps">
+ <MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
+ <template #prefix><i class="ti ti-search"></i></template>
+ </MkInput>
+ <MkRadios v-model="searchType" @update:modelValue="search()">
+ <option value="nameAndDescription">{{ i18n.ts._channel.nameAndDescription }}</option>
+ <option value="nameOnly">{{ i18n.ts._channel.nameOnly }}</option>
+ </MkRadios>
+ <MkButton large primary gradate rounded @click="search">{{ i18n.ts.search }}</MkButton>
</div>
- </MkSwiper>
- </MkSpacer>
+
+ <MkFoldableSection v-if="channelPagination">
+ <template #header>{{ i18n.ts.searchResult }}</template>
+ <MkChannelList :key="key" :pagination="channelPagination"/>
+ </MkFoldableSection>
+ </div>
+ <div v-if="tab === 'featured'">
+ <MkPagination v-slot="{items}" :pagination="featuredPagination">
+ <div :class="$style.root">
+ <MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
+ </div>
+ </MkPagination>
+ </div>
+ <div v-else-if="tab === 'favorites'">
+ <MkPagination v-slot="{items}" :pagination="favoritesPagination">
+ <div :class="$style.root">
+ <MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
+ </div>
+ </MkPagination>
+ </div>
+ <div v-else-if="tab === 'following'">
+ <MkPagination v-slot="{items}" :pagination="followingPagination">
+ <div :class="$style.root">
+ <MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
+ </div>
+ </MkPagination>
+ </div>
+ <div v-else-if="tab === 'owned'">
+ <MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton>
+ <MkPagination v-slot="{items}" :pagination="ownedPagination">
+ <div :class="$style.root">
+ <MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
+ </div>
+ </MkPagination>
+ </div>
+ </div>
</PageWithHeader>
</template>
@@ -67,7 +65,6 @@ import MkInput from '@/components/MkInput.vue';
import MkRadios from '@/components/MkRadios.vue';
import MkButton from '@/components/MkButton.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
-import MkSwiper from '@/components/MkSwiper.vue';
import { definePage } from '@/page.js';
import { i18n } from '@/i18n.js';
import { useRouter } from '@/router.js';
diff --git a/packages/frontend/src/pages/chat/home.vue b/packages/frontend/src/pages/chat/home.vue
index 1edd18ddf0..652ab04be6 100644
--- a/packages/frontend/src/pages/chat/home.vue
+++ b/packages/frontend/src/pages/chat/home.vue
@@ -4,16 +4,14 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
+<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true">
<MkPolkadots v-if="tab === 'home'" accented/>
- <MkSpacer :contentMax="700">
- <MkSwiper v-model:tab="tab" :tabs="headerTabs">
- <XHome v-if="tab === 'home'"/>
- <XInvitations v-else-if="tab === 'invitations'"/>
- <XJoiningRooms v-else-if="tab === 'joiningRooms'"/>
- <XOwnedRooms v-else-if="tab === 'ownedRooms'"/>
- </MkSwiper>
- </MkSpacer>
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
+ <XHome v-if="tab === 'home'"/>
+ <XInvitations v-else-if="tab === 'invitations'"/>
+ <XJoiningRooms v-else-if="tab === 'joiningRooms'"/>
+ <XOwnedRooms v-else-if="tab === 'ownedRooms'"/>
+ </div>
</PageWithHeader>
</template>
@@ -25,7 +23,6 @@ import XJoiningRooms from './home.joiningRooms.vue';
import XOwnedRooms from './home.ownedRooms.vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
-import MkSwiper from '@/components/MkSwiper.vue';
import MkPolkadots from '@/components/MkPolkadots.vue';
const tab = ref('home');
diff --git a/packages/frontend/src/pages/chat/message.vue b/packages/frontend/src/pages/chat/message.vue
index 3ac90a93fd..a04ec7fd87 100644
--- a/packages/frontend/src/pages/chat/message.vue
+++ b/packages/frontend/src/pages/chat/message.vue
@@ -5,14 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader>
- <MkSpacer :contentMax="700">
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
<div v-if="initializing || message == null">
<MkLoading/>
</div>
<div v-else>
<XMessage :message="message" :isSearchResult="true"/>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/chat/room.vue b/packages/frontend/src/pages/chat/room.vue
index 8b351c1ec8..e05125a3b2 100644
--- a/packages/frontend/src/pages/chat/room.vue
+++ b/packages/frontend/src/pages/chat/room.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader v-model:tab="tab" :reversed="tab === 'chat'" :tabs="headerTabs" :actions="headerActions">
- <MkSpacer v-if="tab === 'chat'" :contentMax="700">
+ <div v-if="tab === 'chat'" class="_spacer" style="--MI_SPACER-w: 700px;">
<div class="_gaps">
<div v-if="initializing">
<MkLoading/>
@@ -56,19 +56,19 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInfo v-if="$i.policies.chatAvailability !== 'available'" warn>{{ $i.policies.chatAvailability === 'readonly' ? i18n.ts._chat.chatIsReadOnlyForThisAccountOrServer : i18n.ts._chat.chatNotAvailableForThisAccountOrServer }}</MkInfo>
</div>
- </MkSpacer>
+ </div>
- <MkSpacer v-else-if="tab === 'search'" :contentMax="700">
+ <div v-else-if="tab === 'search'" class="_spacer" style="--MI_SPACER-w: 700px;">
<XSearch :userId="userId" :roomId="roomId"/>
- </MkSpacer>
+ </div>
- <MkSpacer v-else-if="tab === 'members'" :contentMax="700">
+ <div v-else-if="tab === 'members'" class="_spacer" style="--MI_SPACER-w: 700px;">
<XMembers v-if="room != null" :room="room" @inviteUser="inviteUser"/>
- </MkSpacer>
+ </div>
- <MkSpacer v-else-if="tab === 'info'" :contentMax="700">
+ <div v-else-if="tab === 'info'" class="_spacer" style="--MI_SPACER-w: 700px;">
<XInfo v-if="room != null" :room="room"/>
- </MkSpacer>
+ </div>
<template #footer>
<div v-if="tab === 'chat'" :class="$style.footer">
diff --git a/packages/frontend/src/pages/clicker.vue b/packages/frontend/src/pages/clicker.vue
index 479204f39b..d418a78ee5 100644
--- a/packages/frontend/src/pages/clicker.vue
+++ b/packages/frontend/src/pages/clicker.vue
@@ -5,9 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader>
- <MkSpacer :contentMax="800">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<MkClickerGame/>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index 22c8a6b49d..87a361c1e2 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions">
- <MkSpacer :contentMax="800">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<div v-if="clip" class="_gaps">
<div class="_panel">
<div class="_gaps_s" :class="$style.description">
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkNotes :pagination="pagination" :detail="true"/>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/contact.vue b/packages/frontend/src/pages/contact.vue
index 39d70cafc7..eb94f23ac9 100644
--- a/packages/frontend/src/pages/contact.vue
+++ b/packages/frontend/src/pages/contact.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader>
- <MkSpacer :contentMax="600" :marginMin="20">
+ <div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 20px;">
<div class="_gaps_m">
<MkKeyValue :copy="instance.maintainerName">
<template #key>{{ i18n.ts.administrator }}</template>
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</MkKeyValue>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index 7205cca42f..0bca5c1076 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="900">
+ <div class="_spacer" style="--MI_SPACER-w: 900px;">
<div class="ogwlenmc">
<div v-if="tab === 'local'" class="local">
<MkInput v-model="query" :debounce="true" type="search" autocapitalize="off">
@@ -66,7 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkPagination>
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/drive.file.vue b/packages/frontend/src/pages/drive.file.vue
index 170d48064f..2a7924f56f 100644
--- a/packages/frontend/src/pages/drive.file.vue
+++ b/packages/frontend/src/pages/drive.file.vue
@@ -10,13 +10,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<MkSwiper v-model:tab="tab" :tabs="headerTabs">
- <MkSpacer v-if="tab === 'info'" :contentMax="800">
+ <div v-if="tab === 'info'" class="_spacer" style="--MI_SPACER-w: 800px;">
<XFileInfo :fileId="fileId"/>
- </MkSpacer>
+ </div>
- <MkSpacer v-else-if="tab === 'notes'" :contentMax="800">
+ <div v-else-if="tab === 'notes'" class="_spacer" style="--MI_SPACER-w: 800px;">
<XNotes :fileId="fileId"/>
- </MkSpacer>
+ </div>
</MkSwiper>
</MkStickyContainer>
</template>
diff --git a/packages/frontend/src/pages/drop-and-fusion.game.vue b/packages/frontend/src/pages/drop-and-fusion.game.vue
index eee174a6af..6b17c07b1c 100644
--- a/packages/frontend/src/pages/drop-and-fusion.game.vue
+++ b/packages/frontend/src/pages/drop-and-fusion.game.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer :contentMax="800">
+<div class="_spacer" style="--MI_SPACER-w: 800px;">
<div :class="$style.root">
<div v-if="!gameLoaded" :class="$style.loadingScreen">
<div>{{ i18n.ts.loading }}<MkEllipsis/></div>
@@ -187,7 +187,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</div>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/pages/drop-and-fusion.vue b/packages/frontend/src/pages/drop-and-fusion.vue
index 7f571a7c36..bc957ff38a 100644
--- a/packages/frontend/src/pages/drop-and-fusion.vue
+++ b/packages/frontend/src/pages/drop-and-fusion.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:moveClass="$style.transition_zoom_move"
mode="out-in"
>
- <MkSpacer v-if="!gameStarted" :contentMax="800">
+ <div v-if="!gameStarted" class="_spacer" style="--MI_SPACER-w: 800px;">
<div :class="$style.root">
<div class="_gaps">
<div class="_woodenFrame" style="text-align: center;">
@@ -80,7 +80,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</div>
- </MkSpacer>
+ </div>
<XGame v-else :gameMode="gameMode" :mute="mute" @end="onGameEnd"/>
</Transition>
</template>
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index dc570b05e4..2c10dadab7 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-else #header>New emoji</template>
<div style="display: flex; flex-direction: column; min-height: 100%;">
- <MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px; flex-grow: 1;">
<div class="_gaps_m">
<div v-if="imgUrl != null" :class="$style.imgs">
<div style="background: #000;" :class="$style.imgContainer">
@@ -70,7 +70,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch>
<MkButton v-if="emoji" danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
</div>
- </MkSpacer>
+ </div>
<div :class="$style.footer">
<MkButton primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton>
</div>
diff --git a/packages/frontend/src/pages/explore.featured.vue b/packages/frontend/src/pages/explore.featured.vue
index 8b16a88ff3..a47e3efbc8 100644
--- a/packages/frontend/src/pages/explore.featured.vue
+++ b/packages/frontend/src/pages/explore.featured.vue
@@ -4,14 +4,14 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer :contentMax="800">
+<div class="_spacer" style="--MI_SPACER-w: 800px;">
<MkTab v-model="tab" style="margin-bottom: var(--MI-margin);">
<option value="notes">{{ i18n.ts.notes }}</option>
<option value="polls">{{ i18n.ts.poll }}</option>
</MkTab>
<MkNotes v-if="tab === 'notes'" :pagination="paginationForNotes"/>
<MkNotes v-else-if="tab === 'polls'" :pagination="paginationForPolls"/>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/pages/explore.roles.vue b/packages/frontend/src/pages/explore.roles.vue
index ffefeb9618..7ee01610a7 100644
--- a/packages/frontend/src/pages/explore.roles.vue
+++ b/packages/frontend/src/pages/explore.roles.vue
@@ -4,11 +4,11 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer :contentMax="700">
+<div class="_spacer" style="--MI_SPACER-w: 700px;">
<div class="_gaps_s">
<MkRolePreview v-for="role in roles" :key="role.id" :role="role" :forModeration="false"/>
</div>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/pages/explore.users.vue b/packages/frontend/src/pages/explore.users.vue
index 4db26e799c..6375944edf 100644
--- a/packages/frontend/src/pages/explore.users.vue
+++ b/packages/frontend/src/pages/explore.users.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer :contentMax="1200">
+<div class="_spacer" style="--MI_SPACER-w: 1200px;">
<MkTab v-if="instance.federation !== 'none'" v-model="origin" style="margin-bottom: var(--MI-margin);">
<option value="local">{{ i18n.ts.local }}</option>
<option value="remote">{{ i18n.ts.remote }}</option>
@@ -59,7 +59,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkFoldableSection>
</template>
</div>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/pages/explore.vue b/packages/frontend/src/pages/explore.vue
index bcece47e35..c4f6ddc33e 100644
--- a/packages/frontend/src/pages/explore.vue
+++ b/packages/frontend/src/pages/explore.vue
@@ -4,18 +4,16 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSwiper v-model:tab="tab" :tabs="headerTabs">
- <div v-if="tab === 'featured'">
- <XFeatured/>
- </div>
- <div v-else-if="tab === 'users'">
- <XUsers/>
- </div>
- <div v-else-if="tab === 'roles'">
- <XRoles/>
- </div>
- </MkSwiper>
+<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true">
+ <div v-if="tab === 'featured'">
+ <XFeatured/>
+ </div>
+ <div v-else-if="tab === 'users'">
+ <XUsers/>
+ </div>
+ <div v-else-if="tab === 'roles'">
+ <XRoles/>
+ </div>
</PageWithHeader>
</template>
@@ -24,8 +22,6 @@ import { computed, watch, ref, useTemplateRef } from 'vue';
import XFeatured from './explore.featured.vue';
import XUsers from './explore.users.vue';
import XRoles from './explore.roles.vue';
-import MkFoldableSection from '@/components/MkFoldableSection.vue';
-import MkSwiper from '@/components/MkSwiper.vue';
import { definePage } from '@/page.js';
import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/pages/favorites.vue b/packages/frontend/src/pages/favorites.vue
index 6c95d37296..aa18f44e88 100644
--- a/packages/frontend/src/pages/favorites.vue
+++ b/packages/frontend/src/pages/favorites.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader>
- <MkSpacer :contentMax="800">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<MkPagination :pagination="pagination">
<template #empty>
<div class="_fullinfo">
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkDateSeparatedList>
</template>
</MkPagination>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index 825a3be7c1..4386209f7c 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700">
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
<div class="_gaps">
<MkInput v-model="title">
<template #label>{{ i18n.ts._play.title }}</template>
@@ -24,16 +24,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts._play.script }}</template>
</MkCodeEditor>
</div>
- </MkSpacer>
+ </div>
<template #footer>
<div :class="$style.footer">
- <MkSpacer>
+ <div class="_spacer">
<div class="_buttons">
<MkButton primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
<MkButton @click="show"><i class="ti ti-eye"></i> {{ i18n.ts.show }}</MkButton>
<MkButton v-if="flash" danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
</div>
- </MkSpacer>
+ </div>
</div>
</template>
</PageWithHeader>
diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue
index 4ef33cbe0f..f3365fcedf 100644
--- a/packages/frontend/src/pages/flash/flash-index.vue
+++ b/packages/frontend/src/pages/flash/flash-index.vue
@@ -4,37 +4,35 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700">
- <MkSwiper v-model:tab="tab" :tabs="headerTabs">
- <div v-if="tab === 'featured'">
- <MkPagination v-slot="{items}" :pagination="featuredFlashsPagination">
+<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true">
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
+ <div v-if="tab === 'featured'">
+ <MkPagination v-slot="{items}" :pagination="featuredFlashsPagination">
+ <div class="_gaps_s">
+ <MkFlashPreview v-for="flash in items" :key="flash.id" :flash="flash"/>
+ </div>
+ </MkPagination>
+ </div>
+
+ <div v-else-if="tab === 'my'">
+ <div class="_gaps">
+ <MkButton gradate rounded style="margin: 0 auto;" @click="create()"><i class="ti ti-plus"></i></MkButton>
+ <MkPagination v-slot="{items}" :pagination="myFlashsPagination">
<div class="_gaps_s">
<MkFlashPreview v-for="flash in items" :key="flash.id" :flash="flash"/>
</div>
</MkPagination>
</div>
+ </div>
- <div v-else-if="tab === 'my'">
- <div class="_gaps">
- <MkButton gradate rounded style="margin: 0 auto;" @click="create()"><i class="ti ti-plus"></i></MkButton>
- <MkPagination v-slot="{items}" :pagination="myFlashsPagination">
- <div class="_gaps_s">
- <MkFlashPreview v-for="flash in items" :key="flash.id" :flash="flash"/>
- </div>
- </MkPagination>
+ <div v-else-if="tab === 'liked'">
+ <MkPagination v-slot="{items}" :pagination="likedFlashsPagination">
+ <div class="_gaps_s">
+ <MkFlashPreview v-for="like in items" :key="like.flash.id" :flash="like.flash"/>
</div>
- </div>
-
- <div v-else-if="tab === 'liked'">
- <MkPagination v-slot="{items}" :pagination="likedFlashsPagination">
- <div class="_gaps_s">
- <MkFlashPreview v-for="like in items" :key="like.flash.id" :flash="like.flash"/>
- </div>
- </MkPagination>
- </div>
- </MkSwiper>
- </MkSpacer>
+ </MkPagination>
+ </div>
+ </div>
</PageWithHeader>
</template>
@@ -43,7 +41,6 @@ import { computed, ref } from 'vue';
import MkFlashPreview from '@/components/MkFlashPreview.vue';
import MkPagination from '@/components/MkPagination.vue';
import MkButton from '@/components/MkButton.vue';
-import MkSwiper from '@/components/MkSwiper.vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { useRouter } from '@/router.js';
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index e1a51ccaad..b17faca2a9 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700">
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
<Transition :name="prefer.s.animation ? 'fade' : ''" mode="out-in">
<div v-if="flash" :key="flash.id">
<Transition :name="prefer.s.animation ? 'zoom' : ''" mode="out-in">
@@ -56,7 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkError v-else-if="error" @retry="fetchFlash()"/>
<MkLoading v-else/>
</Transition>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue
index f6357ba1b1..dd8ec34214 100644
--- a/packages/frontend/src/pages/follow-requests.vue
+++ b/packages/frontend/src/pages/follow-requests.vue
@@ -4,39 +4,37 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="800">
- <MkSwiper v-model:tab="tab" :tabs="headerTabs">
- <MkPagination ref="paginationComponent" :pagination="pagination">
- <template #empty>
- <div class="_fullinfo">
- <img :src="infoImageUrl" draggable="false"/>
- <div>{{ i18n.ts.noFollowRequests }}</div>
- </div>
- </template>
- <template #default="{items}">
- <div class="mk-follow-requests _gaps">
- <div v-for="req in items" :key="req.id" class="user _panel">
- <MkAvatar class="avatar" :user="displayUser(req)" indicator link preview/>
- <div class="body">
- <div class="name">
- <MkA v-user-preview="displayUser(req).id" class="name" :to="userPage(displayUser(req))"><MkUserName :user="displayUser(req)"/></MkA>
- <p class="acct">@{{ acct(displayUser(req)) }}</p>
- </div>
- <div v-if="tab === 'list'" class="commands">
- <MkButton class="command" rounded primary @click="accept(displayUser(req))"><i class="ti ti-check"/> {{ i18n.ts.accept }}</MkButton>
- <MkButton class="command" rounded danger @click="reject(displayUser(req))"><i class="ti ti-x"/> {{ i18n.ts.reject }}</MkButton>
- </div>
- <div v-else class="commands">
- <MkButton class="command" rounded danger @click="cancel(displayUser(req))"><i class="ti ti-x"/> {{ i18n.ts.cancel }}</MkButton>
- </div>
+<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
+ <MkPagination ref="paginationComponent" :pagination="pagination">
+ <template #empty>
+ <div class="_fullinfo">
+ <img :src="infoImageUrl" draggable="false"/>
+ <div>{{ i18n.ts.noFollowRequests }}</div>
+ </div>
+ </template>
+ <template #default="{items}">
+ <div class="mk-follow-requests _gaps">
+ <div v-for="req in items" :key="req.id" class="user _panel">
+ <MkAvatar class="avatar" :user="displayUser(req)" indicator link preview/>
+ <div class="body">
+ <div class="name">
+ <MkA v-user-preview="displayUser(req).id" class="name" :to="userPage(displayUser(req))"><MkUserName :user="displayUser(req)"/></MkA>
+ <p class="acct">@{{ acct(displayUser(req)) }}</p>
+ </div>
+ <div v-if="tab === 'list'" class="commands">
+ <MkButton class="command" rounded primary @click="accept(displayUser(req))"><i class="ti ti-check"/> {{ i18n.ts.accept }}</MkButton>
+ <MkButton class="command" rounded danger @click="reject(displayUser(req))"><i class="ti ti-x"/> {{ i18n.ts.reject }}</MkButton>
+ </div>
+ <div v-else class="commands">
+ <MkButton class="command" rounded danger @click="cancel(displayUser(req))"><i class="ti ti-x"/> {{ i18n.ts.cancel }}</MkButton>
</div>
</div>
</div>
- </template>
- </MkPagination>
- </MkSwiper>
- </MkSpacer>
+ </div>
+ </template>
+ </MkPagination>
+ </div>
</PageWithHeader>
</template>
@@ -52,7 +50,6 @@ import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { infoImageUrl } from '@/instance.js';
import { $i } from '@/i.js';
-import MkSwiper from '@/components/MkSwiper.vue';
const paginationComponent = useTemplateRef('paginationComponent');
diff --git a/packages/frontend/src/pages/gallery/edit.vue b/packages/frontend/src/pages/gallery/edit.vue
index 7831e084a2..caae30f9fd 100644
--- a/packages/frontend/src/pages/gallery/edit.vue
+++ b/packages/frontend/src/pages/gallery/edit.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="800" :marginMin="16" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-w: 800px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<FormSuspense :p="init" class="_gaps">
<MkInput v-model="title">
<template #label>{{ i18n.ts.title }}</template>
@@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton v-if="postId" danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
</div>
</FormSuspense>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/gallery/index.vue b/packages/frontend/src/pages/gallery/index.vue
index f9e1c9c9a3..f56a1dddf9 100644
--- a/packages/frontend/src/pages/gallery/index.vue
+++ b/packages/frontend/src/pages/gallery/index.vue
@@ -4,44 +4,42 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="1400">
- <MkSwiper v-model:tab="tab" :tabs="headerTabs">
- <div v-if="tab === 'explore'">
- <MkFoldableSection class="_margin">
- <template #header><i class="ti ti-clock"></i>{{ i18n.ts.recentPosts }}</template>
- <MkPagination v-slot="{items}" :pagination="recentPostsPagination" :disableAutoLoad="true">
- <div :class="$style.items">
- <MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/>
- </div>
- </MkPagination>
- </MkFoldableSection>
- <MkFoldableSection class="_margin">
- <template #header><i class="ti ti-comet"></i>{{ i18n.ts.popularPosts }}</template>
- <MkPagination v-slot="{items}" :pagination="popularPostsPagination" :disableAutoLoad="true">
- <div :class="$style.items">
- <MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/>
- </div>
- </MkPagination>
- </MkFoldableSection>
- </div>
- <div v-else-if="tab === 'liked'">
- <MkPagination v-slot="{items}" :pagination="likedPostsPagination">
+<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true">
+ <div class="_spacer" style="--MI_SPACER-w: 1400px;">
+ <div v-if="tab === 'explore'">
+ <MkFoldableSection class="_margin">
+ <template #header><i class="ti ti-clock"></i>{{ i18n.ts.recentPosts }}</template>
+ <MkPagination v-slot="{items}" :pagination="recentPostsPagination" :disableAutoLoad="true">
<div :class="$style.items">
- <MkGalleryPostPreview v-for="like in items" :key="like.id" :post="like.post" class="post"/>
+ <MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/>
</div>
</MkPagination>
- </div>
- <div v-else-if="tab === 'my'">
- <MkA to="/gallery/new" class="_link" style="margin: 16px;"><i class="ti ti-plus"></i> {{ i18n.ts.postToGallery }}</MkA>
- <MkPagination v-slot="{items}" :pagination="myPostsPagination">
+ </MkFoldableSection>
+ <MkFoldableSection class="_margin">
+ <template #header><i class="ti ti-comet"></i>{{ i18n.ts.popularPosts }}</template>
+ <MkPagination v-slot="{items}" :pagination="popularPostsPagination" :disableAutoLoad="true">
<div :class="$style.items">
<MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/>
</div>
</MkPagination>
- </div>
- </MkSwiper>
- </MkSpacer>
+ </MkFoldableSection>
+ </div>
+ <div v-else-if="tab === 'liked'">
+ <MkPagination v-slot="{items}" :pagination="likedPostsPagination">
+ <div :class="$style.items">
+ <MkGalleryPostPreview v-for="like in items" :key="like.id" :post="like.post" class="post"/>
+ </div>
+ </MkPagination>
+ </div>
+ <div v-else-if="tab === 'my'">
+ <MkA to="/gallery/new" class="_link" style="margin: 16px;"><i class="ti ti-plus"></i> {{ i18n.ts.postToGallery }}</MkA>
+ <MkPagination v-slot="{items}" :pagination="myPostsPagination">
+ <div :class="$style.items">
+ <MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/>
+ </div>
+ </MkPagination>
+ </div>
+ </div>
</PageWithHeader>
</template>
@@ -50,7 +48,6 @@ import { watch, ref, computed } from 'vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
import MkPagination from '@/components/MkPagination.vue';
import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue';
-import MkSwiper from '@/components/MkSwiper.vue';
import { definePage } from '@/page.js';
import { i18n } from '@/i18n.js';
import { useRouter } from '@/router.js';
diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue
index 27f4687eb4..7c754f0b03 100644
--- a/packages/frontend/src/pages/gallery/post.vue
+++ b/packages/frontend/src/pages/gallery/post.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="1000" :marginMin="16" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-w: 1000px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<div class="_root">
<Transition :name="prefer.s.animation ? 'fade' : ''" mode="out-in">
<div v-if="post" class="rkxwuolj">
@@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkLoading v-else/>
</Transition>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/games.vue b/packages/frontend/src/pages/games.vue
index 7436c13332..12b84d19aa 100644
--- a/packages/frontend/src/pages/games.vue
+++ b/packages/frontend/src/pages/games.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader>
- <MkSpacer :contentMax="800">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<div class="_gaps">
<div class="_panel" :class="$style.link">
<MkA to="/bubble-game">
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA>
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/install-extensions.vue b/packages/frontend/src/pages/install-extensions.vue
index bf57b0c231..4e814ef84f 100644
--- a/packages/frontend/src/pages/install-extensions.vue
+++ b/packages/frontend/src/pages/install-extensions.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithAnimBg>
- <MkSpacer :contentMax="550" :marginMax="50">
+ <div class="_spacer" style="--MI_SPACER-w: 550px; --MI_SPACER-max: 50px;">
<MkLoading v-if="uiPhase === 'fetching'"/>
<MkExtensionInstaller v-else-if="uiPhase === 'confirm' && data" :extension="data" @confirm="install()" @cancel="close_()">
<template #additionalInfo>
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton @click="close_()">{{ i18n.ts.close }}</MkButton>
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithAnimBg>
</template>
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index eff513d241..479774faef 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -4,8 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSpacer v-if="instance" :contentMax="600" :marginMin="16" :marginMax="32">
+<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true">
+ <div v-if="instance" class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<MkSwiper v-model:tab="tab" :tabs="headerTabs">
<div v-if="tab === 'overview'" class="_gaps_m">
<div class="fnfelxur">
@@ -166,7 +166,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkObjectView>
</div>
</MkSwiper>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
@@ -192,7 +192,6 @@ import { definePage } from '@/page.js';
import { i18n } from '@/i18n.js';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import MkPagination from '@/components/MkPagination.vue';
-import MkSwiper from '@/components/MkSwiper.vue';
import { getProxiedImageUrlNullable } from '@/utility/media-proxy.js';
import { dateString } from '@/filters/date.js';
import MkTextarea from '@/components/MkTextarea.vue';
diff --git a/packages/frontend/src/pages/invite.vue b/packages/frontend/src/pages/invite.vue
index 77ad1cdd96..0342d5c604 100644
--- a/packages/frontend/src/pages/invite.vue
+++ b/packages/frontend/src/pages/invite.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader>
- <MkSpacer v-if="!instance.disableRegistration || !($i && ($i.isAdmin || $i.policies.canInvite))" :contentMax="1200">
+ <div v-if="!instance.disableRegistration || !($i && ($i.isAdmin || $i.policies.canInvite))" class="_spacer" style="--MI_SPACER-w: 1200px;">
<div :class="$style.root">
<img :class="$style.img" :src="serverErrorImageUrl" draggable="false"/>
<div :class="$style.text">
@@ -13,8 +13,8 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.nothing }}
</div>
</div>
- </MkSpacer>
- <MkSpacer v-else :contentMax="800">
+ </div>
+ <div v-else class="_spacer" style="--MI_SPACER-w: 800px;">
<div class="_gaps_m" style="text-align: center;">
<div v-if="resetCycle && inviteLimit">{{ i18n.tsx.inviteLimitResetCycle({ time: resetCycle, limit: inviteLimit }) }}</div>
<MkButton inline primary rounded :disabled="currentInviteLimit !== null && currentInviteLimit <= 0" @click="create"><i class="ti ti-user-plus"></i> {{ i18n.ts.createInviteCode }}</MkButton>
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</MkPagination>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/list.vue b/packages/frontend/src/pages/list.vue
index 3a1f98db74..1676441191 100644
--- a/packages/frontend/src/pages/list.vue
+++ b/packages/frontend/src/pages/list.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer v-if="error != null" :contentMax="1200">
+ <div v-if="error != null" class="_spacer" style="--MI_SPACER-w: 1200px;">
<div :class="$style.root">
<img :class="$style.img" :src="serverErrorImageUrl" draggable="false"/>
<p :class="$style.text">
@@ -13,8 +13,8 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.nothing }}
</p>
</div>
- </MkSpacer>
- <MkSpacer v-else-if="list" :contentMax="700">
+ </div>
+ <div v-else-if="list" class="_spacer" style="--MI_SPACER-w: 700px;">
<div v-if="list" class="members _margin">
<div :class="$style.member_text">{{ i18n.ts.members }}</div>
<div class="_gaps_s">
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton v-if="list.isLiked" v-tooltip="i18n.ts.unlike" inline :class="$style.button" asLike primary @click="unlike()"><i class="ti ti-heart-off"></i><span v-if="list.likedCount > 0" class="count">{{ list.likedCount }}</span></MkButton>
<MkButton v-if="!list.isLiked" v-tooltip="i18n.ts.like" inline :class="$style.button" asLike @click="like()"><i class="ti ti-heart"></i><span v-if="1 > 0" class="count">{{ list.likedCount }}</span></MkButton>
<MkButton inline @click="create()"><i class="ti ti-download" :class="$style.import"></i>{{ i18n.ts.import }}</MkButton>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/lookup.vue b/packages/frontend/src/pages/lookup.vue
index 623c2a6779..c969473b19 100644
--- a/packages/frontend/src/pages/lookup.vue
+++ b/packages/frontend/src/pages/lookup.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="800">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<div v-if="state === 'done'" class="_buttonsCenter">
<MkButton @click="close">{{ i18n.ts.close }}</MkButton>
<MkButton @click="goToMisskey">{{ i18n.ts.goToMisskey }}</MkButton>
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-else class="_fullInfo">
<MkLoading/>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/my-antennas/index.vue b/packages/frontend/src/pages/my-antennas/index.vue
index 297436ad61..a19f7126d0 100644
--- a/packages/frontend/src/pages/my-antennas/index.vue
+++ b/packages/frontend/src/pages/my-antennas/index.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700">
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
<div>
<div v-if="antennas.length === 0" class="empty">
<div class="_fullinfo">
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA>
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue
index 5b9b3af90b..9e427ecf35 100644
--- a/packages/frontend/src/pages/my-clips/index.vue
+++ b/packages/frontend/src/pages/my-clips/index.vue
@@ -4,21 +4,19 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700">
- <MkSwiper v-model:tab="tab" :tabs="headerTabs">
- <div v-if="tab === 'my'" class="_gaps">
- <MkButton primary rounded class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
+<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true">
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
+ <div v-if="tab === 'my'" class="_gaps">
+ <MkButton primary rounded class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
- <MkPagination v-slot="{ items }" ref="pagingComponent" :pagination="pagination" class="_gaps">
- <MkClipPreview v-for="item in items" :key="item.id" :clip="item" :noUserInfo="true"/>
- </MkPagination>
- </div>
- <div v-else-if="tab === 'favorites'" class="_gaps">
- <MkClipPreview v-for="item in favorites" :key="item.id" :clip="item"/>
- </div>
- </MkSwiper>
- </MkSpacer>
+ <MkPagination v-slot="{ items }" ref="pagingComponent" :pagination="pagination" class="_gaps">
+ <MkClipPreview v-for="item in items" :key="item.id" :clip="item" :noUserInfo="true"/>
+ </MkPagination>
+ </div>
+ <div v-else-if="tab === 'favorites'" class="_gaps">
+ <MkClipPreview v-for="item in favorites" :key="item.id" :clip="item"/>
+ </div>
+ </div>
</PageWithHeader>
</template>
@@ -33,7 +31,6 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { clipsCache } from '@/cache.js';
-import MkSwiper from '@/components/MkSwiper.vue';
const pagination = {
endpoint: 'clips/list' as const,
diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue
index 6e23769083..f4a5eafb71 100644
--- a/packages/frontend/src/pages/my-lists/index.vue
+++ b/packages/frontend/src/pages/my-lists/index.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700">
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
<div class="_gaps">
<div v-if="items.length === 0" class="empty">
<div class="_fullinfo">
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA>
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue
index c187435af9..0b76fb4725 100644
--- a/packages/frontend/src/pages/my-lists/list.vue
+++ b/packages/frontend/src/pages/my-lists/list.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700">
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
<div v-if="list" class="_gaps">
<MkFolder>
<template #label>{{ i18n.ts.settings }}</template>
@@ -47,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkFolder>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue
index ff4e9c7514..d801db017e 100644
--- a/packages/frontend/src/pages/note.vue
+++ b/packages/frontend/src/pages/note.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :displayBackButton="true" :tabs="headerTabs">
- <MkSpacer :contentMax="800">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<div>
<Transition :name="prefer.s.animation ? 'fade' : ''" mode="out-in">
<div v-if="note">
@@ -43,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkLoading v-else/>
</Transition>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue
index 61a1b2725c..5cb71945dd 100644
--- a/packages/frontend/src/pages/notifications.vue
+++ b/packages/frontend/src/pages/notifications.vue
@@ -4,8 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="800">
+<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<div v-if="tab === 'all'">
<XNotifications :class="$style.notifications" :excludeTypes="excludeTypes"/>
</div>
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-else-if="tab === 'directNotes'">
<MkNotes :pagination="directNotesPagination"/>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
@@ -24,7 +24,6 @@ import { computed, ref } from 'vue';
import { notificationTypes } from '@@/js/const.js';
import XNotifications from '@/components/MkNotifications.vue';
import MkNotes from '@/components/MkNotes.vue';
-import MkSwiper from '@/components/MkSwiper.vue';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue
index 67134f0976..7368e0329a 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700">
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
<div class="jqqmcavi">
<MkButton v-if="pageId" class="button" inline link :to="`/@${ author.username }/pages/${ currentName }`"><i class="ti ti-external-link"></i> {{ i18n.ts._pages.viewPage }}</MkButton>
<MkButton v-if="!readonly" inline primary class="button" @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
@@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton v-if="!readonly" rounded class="add" @click="add()"><i class="ti ti-plus"></i></MkButton>
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index 9e76a450e6..59b1a5a137 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :displayBackButton="true" :tabs="headerTabs">
- <MkSpacer :contentMax="800">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<Transition
:enterActiveClass="prefer.s.animation ? $style.fadeEnterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.fadeLeaveActive : ''"
@@ -92,7 +92,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkError v-else-if="error" @retry="fetchPage()"/>
<MkLoading v-else/>
</Transition>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/pages.vue b/packages/frontend/src/pages/pages.vue
index d412bad616..880c4deb25 100644
--- a/packages/frontend/src/pages/pages.vue
+++ b/packages/frontend/src/pages/pages.vue
@@ -4,35 +4,33 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="700">
- <MkSwiper v-model:tab="tab" :tabs="headerTabs">
- <div v-if="tab === 'featured'">
- <MkPagination v-slot="{items}" :pagination="featuredPagesPagination">
- <div class="_gaps">
- <MkPagePreview v-for="page in items" :key="page.id" :page="page"/>
- </div>
- </MkPagination>
- </div>
+<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true">
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
+ <div v-if="tab === 'featured'">
+ <MkPagination v-slot="{items}" :pagination="featuredPagesPagination">
+ <div class="_gaps">
+ <MkPagePreview v-for="page in items" :key="page.id" :page="page"/>
+ </div>
+ </MkPagination>
+ </div>
- <div v-else-if="tab === 'my'" class="_gaps">
- <MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton>
- <MkPagination v-slot="{items}" :pagination="myPagesPagination">
- <div class="_gaps">
- <MkPagePreview v-for="page in items" :key="page.id" :page="page"/>
- </div>
- </MkPagination>
- </div>
+ <div v-else-if="tab === 'my'" class="_gaps">
+ <MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton>
+ <MkPagination v-slot="{items}" :pagination="myPagesPagination">
+ <div class="_gaps">
+ <MkPagePreview v-for="page in items" :key="page.id" :page="page"/>
+ </div>
+ </MkPagination>
+ </div>
- <div v-else-if="tab === 'liked'">
- <MkPagination v-slot="{items}" :pagination="likedPagesPagination">
- <div class="_gaps">
- <MkPagePreview v-for="like in items" :key="like.page.id" :page="like.page"/>
- </div>
- </MkPagination>
- </div>
- </MkSwiper>
- </MkSpacer>
+ <div v-else-if="tab === 'liked'">
+ <MkPagination v-slot="{items}" :pagination="likedPagesPagination">
+ <div class="_gaps">
+ <MkPagePreview v-for="like in items" :key="like.page.id" :page="like.page"/>
+ </div>
+ </MkPagination>
+ </div>
+ </div>
</PageWithHeader>
</template>
@@ -41,7 +39,6 @@ import { computed, ref } from 'vue';
import MkPagePreview from '@/components/MkPagePreview.vue';
import MkPagination from '@/components/MkPagination.vue';
import MkButton from '@/components/MkButton.vue';
-import MkSwiper from '@/components/MkSwiper.vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { useRouter } from '@/router.js';
diff --git a/packages/frontend/src/pages/registry.keys.vue b/packages/frontend/src/pages/registry.keys.vue
index 9140555f86..9dea3eba73 100644
--- a/packages/frontend/src/pages/registry.keys.vue
+++ b/packages/frontend/src/pages/registry.keys.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="600" :marginMin="16">
+ <div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px;">
<div class="_gaps_m">
<FormSplit>
<MkKeyValue>
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</FormSection>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/registry.value.vue b/packages/frontend/src/pages/registry.value.vue
index 7c0a7f20bb..5c5bbfba39 100644
--- a/packages/frontend/src/pages/registry.value.vue
+++ b/packages/frontend/src/pages/registry.value.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="600" :marginMin="16">
+ <div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px;">
<div class="_gaps_m">
<FormInfo warn>{{ i18n.ts.editTheseSettingsMayBreakAccount }}</FormInfo>
@@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
</template>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/registry.vue b/packages/frontend/src/pages/registry.vue
index c60833920b..5e59082b50 100644
--- a/packages/frontend/src/pages/registry.vue
+++ b/packages/frontend/src/pages/registry.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="600" :marginMin="16">
+ <div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px;">
<MkButton primary @click="createKey">{{ i18n.ts._registry.createKey }}</MkButton>
<div v-if="scopesWithDomain" class="_gaps_m">
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</FormSection>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/reset-password.vue b/packages/frontend/src/pages/reset-password.vue
index 0a7726a7f8..6584888148 100644
--- a/packages/frontend/src/pages/reset-password.vue
+++ b/packages/frontend/src/pages/reset-password.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer v-if="token" :contentMax="700" :marginMin="16" :marginMax="32">
+ <div v-if="token" class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<div class="_gaps_m">
<MkInput v-model="password" type="password">
<template #prefix><i class="ti ti-lock"></i></template>
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton primary @click="save">{{ i18n.ts.save }}</MkButton>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/reversi/game.board.vue b/packages/frontend/src/pages/reversi/game.board.vue
index b7434bff9f..c0c90cb993 100644
--- a/packages/frontend/src/pages/reversi/game.board.vue
+++ b/packages/frontend/src/pages/reversi/game.board.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer :contentMax="500">
+<div class="_spacer" style="--MI_SPACER-w: 500px;">
<div :class="$style.root" class="_gaps">
<div style="display: flex; align-items: center; justify-content: center; gap: 10px;">
<span>({{ i18n.ts._reversi.black }})</span>
@@ -138,7 +138,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<img src="/client-assets/reversi/logo.png" style="display: block; max-width: 100%; width: 200px; margin: auto;"/>
</MkA>
</div>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/pages/reversi/game.setting.vue b/packages/frontend/src/pages/reversi/game.setting.vue
index 957b1cfc3d..8392384963 100644
--- a/packages/frontend/src/pages/reversi/game.setting.vue
+++ b/packages/frontend/src/pages/reversi/game.setting.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<MkStickyContainer>
- <MkSpacer :contentMax="600">
+ <div class="_spacer" style="--MI_SPACER-w: 600px;">
<div style="text-align: center;"><b><MkUserName :user="game.user1"/></b> vs <b><MkUserName :user="game.user2"/></b></div>
<div :class="{ [$style.disallow]: isReady }">
@@ -82,10 +82,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</div>
</div>
- </MkSpacer>
+ </div>
<template #footer>
<div :class="$style.footer">
- <MkSpacer :contentMax="700" :marginMin="16" :marginMax="16">
+ <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 16px;">
<div style="text-align: center;" class="_gaps_s">
<div v-if="opponentHasSettingsChanged" style="color: var(--MI_THEME-warn);">{{ i18n.ts._reversi.opponentHasSettingsChanged }}</div>
<div>
@@ -103,7 +103,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="shareWhenStart">{{ i18n.ts._reversi.shareToTlTheGameWhenStart }}</MkSwitch>
</div>
</div>
- </MkSpacer>
+ </div>
</div>
</template>
</MkStickyContainer>
diff --git a/packages/frontend/src/pages/reversi/index.vue b/packages/frontend/src/pages/reversi/index.vue
index e3f01d9938..f3252402d7 100644
--- a/packages/frontend/src/pages/reversi/index.vue
+++ b/packages/frontend/src/pages/reversi/index.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer v-if="!matchingAny && !matchingUser" :contentMax="600">
+<div v-if="!matchingAny && !matchingUser" class="_spacer" style="--MI_SPACER-w: 600px;">
<div class="_gaps">
<div>
<img src="/client-assets/reversi/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/>
@@ -83,8 +83,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkPagination>
</MkFolder>
</div>
-</MkSpacer>
-<MkSpacer v-else :contentMax="600">
+</div>
+<div v-else class="_spacer" style="--MI_SPACER-w: 600px;">
<div :class="$style.waitingScreen">
<div v-if="matchingUser" :class="$style.waitingScreenTitle">
<I18n :src="i18n.ts.waitingFor" tag="span">
@@ -101,12 +101,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton inline rounded @click="cancelMatching">{{ i18n.ts.cancel }}</MkButton>
</div>
</div>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
import { onDeactivated, onMounted, onUnmounted, ref } from 'vue';
import * as Misskey from 'misskey-js';
+import { useInterval } from '@@/js/use-interval.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { definePage } from '@/page.js';
import { useStream } from '@/stream.js';
@@ -117,7 +118,6 @@ import { $i } from '@/i.js';
import MkPagination from '@/components/MkPagination.vue';
import { useRouter } from '@/router.js';
import * as os from '@/os.js';
-import { useInterval } from '@@/js/use-interval.js';
import { pleaseLogin } from '@/utility/please-login.js';
import * as sound from '@/utility/sound.js';
diff --git a/packages/frontend/src/pages/role.vue b/packages/frontend/src/pages/role.vue
index 5a1ed70e2f..f85dd8696a 100644
--- a/packages/frontend/src/pages/role.vue
+++ b/packages/frontend/src/pages/role.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader v-model:tab="tab" :displayBackButton="true" :tabs="headerTabs">
- <MkSpacer v-if="error != null" :contentMax="1200">
+ <div v-if="error != null" class="_spacer" style="--MI_SPACER-w: 1200px;">
<div :class="$style.root">
<img :class="$style.img" :src="serverErrorImageUrl" draggable="false"/>
<p :class="$style.text">
@@ -13,8 +13,8 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ error }}
</p>
</div>
- </MkSpacer>
- <MkSpacer v-else-if="tab === 'users'" :contentMax="1200">
+ </div>
+ <div v-else-if="tab === 'users'" class="_spacer" style="--MI_SPACER-w: 1200px;">
<div class="_gaps_s">
<div v-if="role">{{ role.description }}</div>
<MkUserList v-if="visible" :pagination="users" :extractor="(item) => item.user"/>
@@ -23,14 +23,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<div>{{ i18n.ts.nothing }}</div>
</div>
</div>
- </MkSpacer>
- <MkSpacer v-else-if="tab === 'timeline'" :contentMax="700">
+ </div>
+ <div v-else-if="tab === 'timeline'" class="_spacer" style="--MI_SPACER-w: 700px;">
<MkTimeline v-if="visible" ref="timeline" src="role" :role="props.roleId"/>
<div v-else-if="!visible" class="_fullinfo">
<img :src="infoImageUrl" draggable="false"/>
<div>{{ i18n.ts.nothing }}</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue
index 3655620f4e..21ba637958 100644
--- a/packages/frontend/src/pages/scratchpad.vue
+++ b/packages/frontend/src/pages/scratchpad.vue
@@ -5,8 +5,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader>
- <MkSpacer :contentMax="800">
- <div :class="$style.root">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
+ <div class="_gaps">
<div class="_gaps_s">
<div :class="$style.editor" class="_panel">
<MkCodeEditor v-model="code" lang="aiscript" debounce/>
@@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.scratchpadDescription }}
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
@@ -207,9 +207,6 @@ definePage(() => ({
<style lang="scss" module>
.root {
- display: flex;
- flex-direction: column;
- gap: var(--MI-margin);
}
.editor {
diff --git a/packages/frontend/src/pages/search.vue b/packages/frontend/src/pages/search.vue
index 814ddf3cb9..b6d21a4616 100644
--- a/packages/frontend/src/pages/search.vue
+++ b/packages/frontend/src/pages/search.vue
@@ -4,21 +4,19 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
- <MkSwiper v-model:tab="tab" :tabs="headerTabs">
- <MkSpacer v-if="tab === 'note'" :contentMax="800">
- <div v-if="notesSearchAvailable || ignoreNotesSearchAvailable">
- <XNote v-bind="props"/>
- </div>
- <div v-else>
- <MkInfo warn>{{ i18n.ts.notesSearchNotAvailable }}</MkInfo>
- </div>
- </MkSpacer>
+<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true">
+ <div v-if="tab === 'note'" class="_spacer" style="--MI_SPACER-w: 800px;">
+ <div v-if="notesSearchAvailable || ignoreNotesSearchAvailable">
+ <XNote v-bind="props"/>
+ </div>
+ <div v-else>
+ <MkInfo warn>{{ i18n.ts.notesSearchNotAvailable }}</MkInfo>
+ </div>
+ </div>
- <MkSpacer v-else-if="tab === 'user'" :contentMax="800">
- <XUser v-bind="props"/>
- </MkSpacer>
- </MkSwiper>
+ <div v-else-if="tab === 'user'" class="_spacer" style="--MI_SPACER-w: 800px;">
+ <XUser v-bind="props"/>
+ </div>
</PageWithHeader>
</template>
@@ -28,7 +26,6 @@ import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { notesSearchAvailable } from '@/utility/check-permissions.js';
import MkInfo from '@/components/MkInfo.vue';
-import MkSwiper from '@/components/MkSwiper.vue';
const props = withDefaults(defineProps<{
query?: string,
diff --git a/packages/frontend/src/pages/settings/2fa.qrdialog.vue b/packages/frontend/src/pages/settings/2fa.qrdialog.vue
index 03f973a33e..5bb125e67c 100644
--- a/packages/frontend/src/pages/settings/2fa.qrdialog.vue
+++ b/packages/frontend/src/pages/settings/2fa.qrdialog.vue
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<template v-if="page === 0">
<div style="height: 100cqh; overflow: auto; text-align: center;">
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<div class="_gaps">
<MkInfo><MkLink url="https://misskey-hub.net/docs/for-users/stepped-guides/how-to-enable-2fa/" target="_blank">{{ i18n.ts._2fa.moreDetailedGuideHere }}</MkLink></MkInfo>
@@ -50,12 +50,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton rounded @click="cancel">{{ i18n.ts.cancel }}</MkButton>
<MkButton primary rounded gradate @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
</div>
- </MkSpacer>
+ </div>
</div>
</template>
<template v-else-if="page === 1">
<div style="height: 100cqh; overflow: auto;">
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<div class="_gaps">
<div>{{ i18n.ts._2fa.step3Title }}</div>
<MkInput v-model="token" autocomplete="one-time-code" inputmode="numeric"></MkInput>
@@ -65,12 +65,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton rounded @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton>
<MkButton primary rounded gradate @click="tokenDone">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
</div>
- </MkSpacer>
+ </div>
</div>
</template>
<template v-else-if="page === 2">
<div style="height: 100cqh; overflow: auto;">
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<div class="_gaps">
<div style="text-align: center;">{{ i18n.ts._2fa.setupCompleted }}🎉</div>
<div style="text-align: center;">{{ i18n.ts._2fa.step4 }}</div>
@@ -97,7 +97,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_buttonsCenter" style="margin-top: 16px;">
<MkButton primary rounded gradate @click="allDone">{{ i18n.ts.done }}</MkButton>
</div>
- </MkSpacer>
+ </div>
</div>
</template>
</Transition>
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
index 1fd977cbd4..91280dccb9 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #header>{{ i18n.ts.avatarDecorations }}</template>
<div>
- <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<div style="text-align: center;">
<div :class="$style.name">{{ decoration.name }}</div>
<MkAvatar style="width: 64px; height: 64px; margin-bottom: 20px;" :user="$i" :decorations="decorationsForPreview" forceShowDecoration/>
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.flip }}</template>
</MkSwitch>
</div>
- </MkSpacer>
+ </div>
<div :class="$style.footer" class="_buttonsCenter">
<MkButton v-if="usingIndex != null" primary rounded @click="update"><i class="ti ti-check"></i> {{ i18n.ts.update }}</MkButton>
diff --git a/packages/frontend/src/pages/settings/emoji-palette.vue b/packages/frontend/src/pages/settings/emoji-palette.vue
index 4e92b7a3ab..e1a1c39f03 100644
--- a/packages/frontend/src/pages/settings/emoji-palette.vue
+++ b/packages/frontend/src/pages/settings/emoji-palette.vue
@@ -260,12 +260,6 @@ definePage(() => ({
</script>
<style lang="scss" module>
-.tab {
- margin: calc(var(--MI-margin) / 2) 0;
- padding: calc(var(--MI-margin) / 2) 0;
- background: var(--MI_THEME-bg);
-}
-
.emojis {
padding: 12px;
font-size: 1.1em;
diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue
index a11ae2a6f6..61e3ca8b6c 100644
--- a/packages/frontend/src/pages/settings/index.vue
+++ b/packages/frontend/src/pages/settings/index.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :tabs="headerTabs" :actions="headerActions">
- <MkSpacer :contentMax="900" :marginMin="20" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-w: 900px; --MI_SPACER-min: 20px; --MI_SPACER-max: 32px;">
<div ref="el" class="vvcocwet" :class="{ wide: !narrow }">
<div class="body">
<div v-if="!narrow || currentPage?.route.name == null" class="nav">
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue
index 519ce6db2f..7c588acae2 100644
--- a/packages/frontend/src/pages/settings/preferences.vue
+++ b/packages/frontend/src/pages/settings/preferences.vue
@@ -42,6 +42,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</SearchMarker>
<div class="_gaps_s">
+ <SearchMarker :keywords="['titlebar', 'show']">
+ <MkPreferenceContainer k="showTitlebar">
+ <MkSwitch v-model="showTitlebar">
+ <template #label><SearchLabel>{{ i18n.ts.showTitlebar }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
<SearchMarker :keywords="['avatar', 'icon', 'decoration', 'show']">
<MkPreferenceContainer k="showAvatarDecorations">
<MkSwitch v-model="showAvatarDecorations">
@@ -945,6 +953,7 @@ const lang = ref(miLocalStorage.getItem('lang'));
const dataSaver = ref(prefer.s.dataSaver);
const overridedDeviceKind = prefer.model('overridedDeviceKind');
+const showTitlebar = prefer.model('showTitlebar');
const keepCw = prefer.model('keepCw');
const serverDisconnectedBehavior = prefer.model('serverDisconnectedBehavior');
const hemisphere = prefer.model('hemisphere');
diff --git a/packages/frontend/src/pages/share.vue b/packages/frontend/src/pages/share.vue
index 57afdb9121..71f572657b 100644
--- a/packages/frontend/src/pages/share.vue
+++ b/packages/frontend/src/pages/share.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="800">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<MkPostForm
v-if="state === 'writing'"
fixed
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton primary @click="close">{{ i18n.ts.close }}</MkButton>
<MkButton @click="goToMisskey">{{ i18n.ts.goToMisskey }}</MkButton>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/tag.vue b/packages/frontend/src/pages/tag.vue
index 77e102f239..40562ff27e 100644
--- a/packages/frontend/src/pages/tag.vue
+++ b/packages/frontend/src/pages/tag.vue
@@ -5,14 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :displayBackButton="true" :tabs="headerTabs">
- <MkSpacer :contentMax="800">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<MkNotes ref="notes" class="" :pagination="pagination"/>
- </MkSpacer>
+ </div>
<template v-if="$i" #footer>
<div :class="$style.footer">
- <MkSpacer :contentMax="800" :marginMin="16" :marginMax="16">
+ <div class="_spacer" style="--MI_SPACER-w: 800px; --MI_SPACER-min: 16px; --MI_SPACER-max: 16px;">
<MkButton rounded primary :class="$style.button" @click="post()"><i class="ti ti-pencil"></i>{{ i18n.ts.postToHashtag }}</MkButton>
- </MkSpacer>
+ </div>
</div>
</template>
</PageWithHeader>
diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue
index 2570c40fe0..585d96bd08 100644
--- a/packages/frontend/src/pages/theme-editor.vue
+++ b/packages/frontend/src/pages/theme-editor.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
- <MkSpacer :contentMax="800" :marginMin="16" :marginMax="32">
+ <div class="_spacer" style="--MI_SPACER-w: 800px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<div class="cwepdizn _gaps_m">
<MkFolder :defaultOpen="true">
<template #label>{{ i18n.ts.backgroundColor }}</template>
@@ -67,7 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkFolder>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index 731242425c..f499c35c9a 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -4,37 +4,33 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div ref="rootEl" class="_pageScrollable">
- <MkStickyContainer>
- <template #header><MkPageHeader v-model:tab="src" :displayMyAvatar="true" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin"/></template>
- <MkSpacer :contentMax="800">
- <MkInfo v-if="isBasicTimeline(src) && !store.r.timelineTutorials.value[src]" style="margin-bottom: var(--MI-margin);" closable @close="closeTutorial()">
- {{ i18n.ts._timelineDescription[src] }}
- </MkInfo>
- <MkPostForm v-if="prefer.r.showFixedPostForm.value" :class="$style.postForm" class="_panel" fixed style="margin-bottom: var(--MI-margin);"/>
- <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
- <MkTimeline
- ref="tlComponent"
- :key="src + withRenotes + withBots + withReplies + onlyFiles + withSensitive"
- :class="$style.tl"
- :src="src.split(':')[0]"
- :list="src.split(':')[1]"
- :withRenotes="withRenotes"
- :withReplies="withReplies"
- :withSensitive="withSensitive"
- :onlyFiles="onlyFiles"
- :withBots="withBots"
- :sound="true"
- @queue="queueUpdated"
- />
- </MkSpacer>
- </MkStickyContainer>
-</div>
+<PageWithHeader ref="pageComponent" v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :swipable="true" :displayMyAvatar="true">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
+ <MkInfo v-if="isBasicTimeline(src) && !store.r.timelineTutorials.value[src]" style="margin-bottom: var(--MI-margin);" closable @close="closeTutorial()">
+ {{ i18n.ts._timelineDescription[src] }}
+ </MkInfo>
+ <MkPostForm v-if="prefer.r.showFixedPostForm.value" :class="$style.postForm" class="_panel" fixed style="margin-bottom: var(--MI-margin);"/>
+ <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
+ <MkTimeline
+ ref="tlComponent"
+ :key="src + withRenotes + withBots + withReplies + onlyFiles + withSensitive"
+ :class="$style.tl"
+ :src="src.split(':')[0]"
+ :list="src.split(':')[1]"
+ :withRenotes="withRenotes"
+ :withReplies="withReplies"
+ :withSensitive="withSensitive"
+ :onlyFiles="onlyFiles"
+ :withBots="withBots"
+ :sound="true"
+ @queue="queueUpdated"
+ />
+ </div>
+</PageWithHeader>
</template>
<script lang="ts" setup>
import { computed, watch, provide, useTemplateRef, ref, onMounted, onActivated } from 'vue';
-import { scrollInContainer } from '@@/js/scroll.js';
import type { Tab } from '@/components/global/MkPageHeader.tabs.vue';
import type { MenuItem } from '@/types/menu.js';
import type { BasicTimelineType } from '@/timelines.js';
@@ -52,20 +48,11 @@ import { deepMerge } from '@/utility/merge.js';
import { miLocalStorage } from '@/local-storage.js';
import { availableBasicTimelines, hasWithReplies, isAvailableBasicTimeline, isBasicTimeline, basicTimelineIconClass } from '@/timelines.js';
import { prefer } from '@/preferences.js';
-import { useRouter } from '@/router.js';
-import { useScrollPositionKeeper } from '@/use/use-scroll-position-keeper.js';
provide('shouldOmitHeaderTitle', true);
const tlComponent = useTemplateRef('tlComponent');
-const rootEl = useTemplateRef('rootEl');
-
-useScrollPositionKeeper(rootEl);
-
-const router = useRouter();
-router.useListener('same', () => {
- top();
-});
+const pageComponent = useTemplateRef('pageComponent');
type TimelinePageSrc = BasicTimelineType | `list:${string}`;
@@ -138,7 +125,7 @@ function queueUpdated(q: number): void {
}
function top(): void {
- if (rootEl.value) scrollInContainer(rootEl.value, { top: 0, behavior: 'instant' });
+ if (pageComponent.value) pageComponent.value.scrollToTop();
}
async function chooseList(ev: MouseEvent): Promise<void> {
diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue
index 754ae2467e..cb4d6f4240 100644
--- a/packages/frontend/src/pages/user-list-timeline.vue
+++ b/packages/frontend/src/pages/user-list-timeline.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :displayBackButton="true" :tabs="headerTabs">
- <MkSpacer :contentMax="800">
+ <div class="_spacer" style="--MI_SPACER-w: 800px;">
<div ref="rootEl">
<div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
<div :class="$style.tl">
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
/>
</div>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/user-tag.vue b/packages/frontend/src/pages/user-tag.vue
index d1dc721a4b..959d449e40 100644
--- a/packages/frontend/src/pages/user-tag.vue
+++ b/packages/frontend/src/pages/user-tag.vue
@@ -5,11 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader>
- <MkSpacer :contentMax="1200">
+ <div class="_spacer" style="--MI_SPACER-w: 1200px;">
<div class="_gaps_s">
<MkUserList :pagination="tagUsers"/>
</div>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/user/achievements.vue b/packages/frontend/src/pages/user/achievements.vue
index 8f13e959e1..d40998c307 100644
--- a/packages/frontend/src/pages/user/achievements.vue
+++ b/packages/frontend/src/pages/user/achievements.vue
@@ -4,9 +4,9 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer :contentMax="1200">
+<div class="_spacer" style="--MI_SPACER-w: 1200px;">
<MkAchievements :user="user" :withLocked="false" :withDescription="$i != null && (props.user.id === $i.id)"/>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/pages/user/activity.vue b/packages/frontend/src/pages/user/activity.vue
index 994bd52705..a49b82e630 100644
--- a/packages/frontend/src/pages/user/activity.vue
+++ b/packages/frontend/src/pages/user/activity.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer :contentMax="700">
+<div class="_spacer" style="--MI_SPACER-w: 700px;">
<div class="_gaps">
<MkFoldableSection class="item">
<template #header><i class="ti ti-activity"></i> Heatmap</template>
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<XPv :user="user"/>
</MkFoldableSection>
</div>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/pages/user/clips.vue b/packages/frontend/src/pages/user/clips.vue
index 38ce78e8d5..c980c83a26 100644
--- a/packages/frontend/src/pages/user/clips.vue
+++ b/packages/frontend/src/pages/user/clips.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer :contentMax="700">
+<div class="_spacer" style="--MI_SPACER-w: 700px;">
<div>
<MkPagination v-slot="{items}" ref="list" :pagination="pagination">
<MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" :class="$style.item" class="_panel _margin">
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA>
</MkPagination>
</div>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/pages/user/files.vue b/packages/frontend/src/pages/user/files.vue
index b6c7c1c777..91ebcad0b2 100644
--- a/packages/frontend/src/pages/user/files.vue
+++ b/packages/frontend/src/pages/user/files.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
- <MkSpacer :contentMax="1100">
+ <div class="_spacer" style="--MI_SPACER-w: 1100px;">
<div :class="$style.root">
<MkPagination v-slot="{items}" :pagination="pagination">
<div :class="$style.stream">
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkPagination>
</div>
- </MkSpacer>
+ </div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/pages/user/flashs.vue b/packages/frontend/src/pages/user/flashs.vue
index b3313476e1..16957a5a2b 100644
--- a/packages/frontend/src/pages/user/flashs.vue
+++ b/packages/frontend/src/pages/user/flashs.vue
@@ -4,11 +4,11 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer :contentMax="700">
+<div class="_spacer" style="--MI_SPACER-w: 700px;">
<MkPagination v-slot="{items}" ref="list" :pagination="pagination">
<MkFlashPreview v-for="flash in items" :key="flash.id" :flash="flash" class="_margin"/>
</MkPagination>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/pages/user/followers.vue b/packages/frontend/src/pages/user/followers.vue
index b8ba023f74..ae9765b60a 100644
--- a/packages/frontend/src/pages/user/followers.vue
+++ b/packages/frontend/src/pages/user/followers.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :displayBackButton="true" :tabs="headerTabs">
- <MkSpacer :contentMax="1000">
+ <div class="_spacer" style="--MI_SPACER-w: 1000px;">
<Transition name="fade" mode="out-in">
<div v-if="user">
<XFollowList :user="user" type="followers"/>
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkError v-else-if="error" @retry="fetchUser()"/>
<MkLoading v-else/>
</Transition>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/user/following.vue b/packages/frontend/src/pages/user/following.vue
index 1fe64c3042..8fd594c4e0 100644
--- a/packages/frontend/src/pages/user/following.vue
+++ b/packages/frontend/src/pages/user/following.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader :actions="headerActions" :displayBackButton="true" :tabs="headerTabs">
- <MkSpacer :contentMax="1000">
+ <div class="_spacer" style="--MI_SPACER-w: 1000px;">
<Transition name="fade" mode="out-in">
<div v-if="user">
<XFollowList :user="user" type="following"/>
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkError v-else-if="error" @retry="fetchUser()"/>
<MkLoading v-else/>
</Transition>
- </MkSpacer>
+ </div>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/user/gallery.vue b/packages/frontend/src/pages/user/gallery.vue
index 0bc5628528..11874bfd87 100644
--- a/packages/frontend/src/pages/user/gallery.vue
+++ b/packages/frontend/src/pages/user/gallery.vue
@@ -4,13 +4,13 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer :contentMax="700">
+<div class="_spacer" style="--MI_SPACER-w: 700px;">
<MkPagination v-slot="{items}" :pagination="pagination">
<div :class="$style.root">
<MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/>
</div>
</MkPagination>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index c057312f63..f15e4d7bcd 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer :contentMax="narrow ? 800 : 1100" :style="background" style="transform: none !important;">
+<div class="_spacer" :style="{ '--MI_SPACER-w': narrow ? '800px' : '1100px', ...background, transform: 'none !important;' }">
<div ref="rootEl" class="ftskorzw" :class="{ wide: !narrow }" style="container-type: inline-size;">
<div class="main _gaps">
<MkInfo v-if="user.isSuspended" :warn="true">{{ i18n.ts.userSuspended }}</MkInfo>
@@ -192,7 +192,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
<div class="background"></div>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue
index 17dd1d5f3c..38b1590a51 100644
--- a/packages/frontend/src/pages/user/index.vue
+++ b/packages/frontend/src/pages/user/index.vue
@@ -4,24 +4,22 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<PageWithHeader v-model:tab="tab" :displayBackButton="true" :tabs="headerTabs" :actions="headerActions">
+<PageWithHeader v-model:tab="tab" :displayBackButton="true" :tabs="headerTabs" :actions="headerActions" :swipable="true">
<div v-if="user">
- <MkSwiper v-model:tab="tab" :tabs="headerTabs">
- <XHome v-if="tab === 'home'" :user="user" @unfoldFiles="() => { tab = 'files'; }"/>
- <MkSpacer v-else-if="tab === 'notes'" :contentMax="800" style="padding-top: 0">
- <XTimeline :user="user"/>
- </MkSpacer>
- <XFiles v-else-if="tab === 'files'" :user="user"/>
- <XActivity v-else-if="tab === 'activity'" :user="user"/>
- <XAchievements v-else-if="tab === 'achievements'" :user="user"/>
- <XReactions v-else-if="tab === 'reactions'" :user="user"/>
- <XClips v-else-if="tab === 'clips'" :user="user"/>
- <XLists v-else-if="tab === 'lists'" :user="user"/>
- <XPages v-else-if="tab === 'pages'" :user="user"/>
- <XFlashs v-else-if="tab === 'flashs'" :user="user"/>
- <XGallery v-else-if="tab === 'gallery'" :user="user"/>
- <XRaw v-else-if="tab === 'raw'" :user="user"/>
- </MkSwiper>
+ <XHome v-if="tab === 'home'" :user="user" @unfoldFiles="() => { tab = 'files'; }"/>
+ <div v-else-if="tab === 'notes'" class="_spacer" style="--MI_SPACER-w: 800px;">
+ <XTimeline :user="user"/>
+ </div>
+ <XFiles v-else-if="tab === 'files'" :user="user"/>
+ <XActivity v-else-if="tab === 'activity'" :user="user"/>
+ <XAchievements v-else-if="tab === 'achievements'" :user="user"/>
+ <XReactions v-else-if="tab === 'reactions'" :user="user"/>
+ <XClips v-else-if="tab === 'clips'" :user="user"/>
+ <XLists v-else-if="tab === 'lists'" :user="user"/>
+ <XPages v-else-if="tab === 'pages'" :user="user"/>
+ <XFlashs v-else-if="tab === 'flashs'" :user="user"/>
+ <XGallery v-else-if="tab === 'gallery'" :user="user"/>
+ <XRaw v-else-if="tab === 'raw'" :user="user"/>
</div>
<MkError v-else-if="error" @retry="fetchUser()"/>
<MkLoading v-else/>
@@ -36,7 +34,6 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import { definePage } from '@/page.js';
import { i18n } from '@/i18n.js';
import { $i } from '@/i.js';
-import MkSwiper from '@/components/MkSwiper.vue';
import { serverContext, assertServerContext } from '@/server-context.js';
const XHome = defineAsyncComponent(() => import('./home.vue'));
diff --git a/packages/frontend/src/pages/user/lists.vue b/packages/frontend/src/pages/user/lists.vue
index a3d1974ced..d8ebea41c0 100644
--- a/packages/frontend/src/pages/user/lists.vue
+++ b/packages/frontend/src/pages/user/lists.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<MkStickyContainer>
- <MkSpacer :contentMax="700">
+ <div class="_spacer" style="--MI_SPACER-w: 700px;">
<div>
<MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="lists">
<MkA v-for="list in items" :key="list.id" class="_panel" :class="$style.list" :to="`/list/${ list.id }`">
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA>
</MkPagination>
</div>
- </MkSpacer>
+ </div>
</MkStickyContainer>
</template>
@@ -23,7 +23,6 @@ import {} from 'vue';
import * as Misskey from 'misskey-js';
import MkPagination from '@/components/MkPagination.vue';
import MkStickyContainer from '@/components/global/MkStickyContainer.vue';
-import MkSpacer from '@/components/global/MkSpacer.vue';
import MkAvatars from '@/components/MkAvatars.vue';
const props = defineProps<{
diff --git a/packages/frontend/src/pages/user/pages.vue b/packages/frontend/src/pages/user/pages.vue
index 6375bf7d74..fe6141285e 100644
--- a/packages/frontend/src/pages/user/pages.vue
+++ b/packages/frontend/src/pages/user/pages.vue
@@ -4,11 +4,11 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer :contentMax="700">
+<div class="_spacer" style="--MI_SPACER-w: 700px;">
<MkPagination v-slot="{items}" ref="list" :pagination="pagination">
<MkPagePreview v-for="page in items" :key="page.id" :page="page" class="_margin"/>
</MkPagination>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/pages/user/raw.vue b/packages/frontend/src/pages/user/raw.vue
index f24a215afc..35dc00fbaf 100644
--- a/packages/frontend/src/pages/user/raw.vue
+++ b/packages/frontend/src/pages/user/raw.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer :contentMax="600" :marginMin="16" :marginMax="32">
+<div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
<div class="_gaps_m">
<div :class="$style.userMInfoRoot">
<MkAvatar :class="$style.userMInfoAvatar" :user="user" indicator link preview/>
@@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkObjectView tall :value="user"></MkObjectView>
</FormSection>
</div>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/pages/user/reactions.vue b/packages/frontend/src/pages/user/reactions.vue
index 7168778e12..9b7a3bc3bd 100644
--- a/packages/frontend/src/pages/user/reactions.vue
+++ b/packages/frontend/src/pages/user/reactions.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkSpacer :contentMax="700">
+<div class="_spacer" style="--MI_SPACER-w: 700px;">
<MkPagination v-slot="{items}" ref="list" :pagination="pagination">
<div v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="_panel _margin">
<div :class="$style.header">
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkNote :key="item.id" :note="item.note"/>
</div>
</MkPagination>
-</MkSpacer>
+</div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts
index 6dd6c15ec5..58e26cb005 100644
--- a/packages/frontend/src/preferences/def.ts
+++ b/packages/frontend/src/preferences/def.ts
@@ -334,6 +334,9 @@ export const PREF_DEF = {
showNavbarSubButtons: {
default: true,
},
+ showTitlebar: {
+ default: false,
+ },
plugins: {
default: [] as Plugin[],
},
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 36949e272e..0626779869 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -38,6 +38,10 @@ export const store = markRaw(new Pizzax('base', {
where: 'account',
default: false,
},
+ readDriveTip: {
+ where: 'account',
+ default: false,
+ },
memo: {
where: 'account',
default: null,
diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss
index 304df91617..18e67eaa95 100644
--- a/packages/frontend/src/style.scss
+++ b/packages/frontend/src/style.scss
@@ -205,6 +205,31 @@ rt {
text-align: center;
}
+/* TODO: 引数は現在CSS変数経由で受け取っているが、将来的にはattr()を使った方が綺麗そう */
+._spacer {
+ width: 100%;
+ max-width: min(var(--MI_SPACER-w, 100%), calc(100% - (var(--MI_SPACER-max, 24px) * 2)));
+ margin: var(--MI_SPACER-max, 24px) auto;
+ container-type: inline-size;
+
+ /* 子に継承させない */
+ --MI_SPACER-w: initial;
+ --MI_SPACER-min: initial;
+ --MI_SPACER-max: initial;
+}
+
+._forceShrinkSpacer ._spacer {
+ max-width: min(var(--MI_SPACER-w, 100%), calc(100% - (var(--MI_SPACER-min, 12px) * 2)));
+ margin: var(--MI_SPACER-min, 12px) auto;
+}
+
+@container (max-width: 450px) {
+ ._spacer {
+ max-width: min(var(--MI_SPACER-w, 100%), calc(100% - (var(--MI_SPACER-min, 12px) * 2)));
+ margin: var(--MI_SPACER-min, 12px) auto;
+ }
+}
+
._pageContainer {
container-type: size;
contain: strict;
diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue
index fd7a89dc22..14c4424e67 100644
--- a/packages/frontend/src/ui/_common_/common.vue
+++ b/packages/frontend/src/ui/_common_/common.vue
@@ -421,7 +421,7 @@ function getPointerEvents() {
#devTicker {
position: fixed;
- top: 0;
+ bottom: 0;
left: 0;
z-index: 2147483647;
color: #ff0;
diff --git a/packages/frontend/src/ui/_common_/mobile-footer-menu.vue b/packages/frontend/src/ui/_common_/mobile-footer-menu.vue
index 7c2de12221..88c6191e5a 100644
--- a/packages/frontend/src/ui/_common_/mobile-footer-menu.vue
+++ b/packages/frontend/src/ui/_common_/mobile-footer-menu.vue
@@ -79,10 +79,9 @@ watch(rootEl, () => {
.root {
position: relative;
z-index: 1;
- padding: 12px 12px max(12px, env(safe-area-inset-bottom, 0px)) 12px;
+ padding-bottom: env(safe-area-inset-bottom, 0px);
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
- grid-gap: 8px;
width: 100%;
box-sizing: border-box;
background: var(--MI_THEME-navBg);
@@ -91,6 +90,16 @@ watch(rootEl, () => {
}
.item {
+ padding: 12px 0;
+
+ &:first-child {
+ padding-left: 12px;
+ }
+
+ &:last-child {
+ padding-right: 12px;
+ }
+
&.post {
.itemInner {
background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
@@ -112,7 +121,7 @@ watch(rootEl, () => {
padding: 0;
aspect-ratio: 1;
width: 100%;
- max-width: 45px;
+ max-width: 42px;
margin: auto;
align-content: center;
border-radius: 100%;
diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
index 94f333da41..f61e178bce 100644
--- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
+++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
@@ -121,6 +121,7 @@ function more() {
display: inline-block;
width: 38px;
aspect-ratio: 1;
+ border-radius: 8px;
}
.wideInstanceIcon {
diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue
index 2708683acb..6bf0dfc17c 100644
--- a/packages/frontend/src/ui/_common_/navbar.vue
+++ b/packages/frontend/src/ui/_common_/navbar.vue
@@ -7,7 +7,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="[$style.root, { [$style.iconOnly]: iconOnly }]">
<div :class="$style.body">
<div :class="$style.top">
- <div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }"></div>
<button v-tooltip.noDelay.right="instance.name ?? i18n.ts.instance" class="_button" :class="$style.instance" @click="openInstanceMenu">
<img :src="instance.sidebarLogoUrl && !iconOnly ? instance.sidebarLogoUrl : instance.iconUrl || '/favicon.ico'" alt="" :class="instance.sidebarLogoUrl && !iconOnly ? $style.wideInstanceIcon : $style.instanceIcon" style="viewTransitionName: navbar-serverIcon;"/>
</button>
@@ -183,12 +182,9 @@ function menuEdit() {
}
.body {
- position: fixed;
- top: 0;
- left: 0;
- z-index: 1001;
+ position: relative;
width: var(--nav-icon-only-width);
- height: 100dvh;
+ height: 100%;
box-sizing: border-box;
overflow: auto;
overflow-x: clip;
@@ -303,18 +299,6 @@ function menuEdit() {
backdrop-filter: var(--MI-blur, blur(8px));
}
- .banner {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background-size: cover;
- background-position: center center;
- -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 15%, rgba(0,0,0,0.75) 100%);
- mask-image: linear-gradient(0deg, rgba(0,0,0,0) 15%, rgba(0,0,0,0.75) 100%);
- }
-
.instance {
position: relative;
display: block;
@@ -335,6 +319,7 @@ function menuEdit() {
display: inline-block;
width: 38px;
aspect-ratio: 1;
+ border-radius: 8px;
}
.wideInstanceIcon {
@@ -566,6 +551,7 @@ function menuEdit() {
display: inline-block;
width: 30px;
aspect-ratio: 1;
+ border-radius: 8px;
}
.bottom {
diff --git a/packages/frontend/src/ui/_common_/titlebar.vue b/packages/frontend/src/ui/_common_/titlebar.vue
new file mode 100644
index 0000000000..c62b13b73a
--- /dev/null
+++ b/packages/frontend/src/ui/_common_/titlebar.vue
@@ -0,0 +1,87 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :class="$style.root">
+ <div :class="$style.title">
+ <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/>
+ <span :class="$style.instanceTitle">{{ instance.name ?? host }}</span>
+ </div>
+ <div :class="$style.controls">
+ <span :class="$style.left">
+ <button v-if="canBack" class="_button" :class="$style.button" @click="goBack"><i class="ti ti-arrow-left"></i></button>
+ </span>
+ <span :class="$style.right">
+ </span>
+ </div>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { host } from '@@/js/config.js';
+import { ref } from 'vue';
+import { instance } from '@/instance.js';
+import { prefer } from '@/preferences.js';
+
+const canBack = ref(true);
+
+function goBack() {
+ window.history.back();
+}
+</script>
+
+<style lang="scss" module>
+.root {
+ --height: 36px;
+
+ background: var(--MI_THEME-navBg);
+ height: var(--height);
+ font-size: 90%;
+}
+
+.title {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ text-align: center;
+ height: var(--height);
+}
+
+.controls {
+ position: absolute;
+ top: 0;
+ left: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: var(--height);
+}
+
+.instanceIcon {
+ display: inline-block;
+ width: 20px;
+ aspect-ratio: 1;
+ border-radius: 5px;
+ margin-right: 8px;
+}
+
+.instanceTitle {
+ display: inline-block;
+}
+
+.left {
+ margin-right: auto;
+}
+
+.right {
+ margin-left: auto;
+}
+
+.button {
+ display: inline-block;
+ height: var(--height);
+ aspect-ratio: 1;
+}
+</style>
diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index fbe86bc4cb..ed85bb3bc5 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -4,76 +4,80 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div :class="[$style.root, { [$style.withWallpaper]: withWallpaper }]">
- <XSidebar v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'left'"/>
+<div :class="[$style.root]">
+ <XTitlebar v-if="prefer.r.showTitlebar.value" style="flex-shrink: 0;"/>
- <div :class="$style.main">
- <XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'top'"/>
+ <div :class="$style.nonTitlebarArea">
+ <XSidebar v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'left'"/>
- <XAnnouncements v-if="$i"/>
- <XStatusBars/>
- <div :class="$style.columnsWrapper">
- <!-- passive: https://bugs.webkit.org/show_bug.cgi?id=281300 -->
- <div ref="columnsEl" :class="[$style.columns, { [$style.center]: prefer.r['deck.columnAlign'].value === 'center', [$style.snapScroll]: snapScroll }]" @contextmenu.self.prevent="onContextmenu" @wheel.passive.self="onWheel">
- <!-- sectionを利用しているのは、deck.vue側でcolumnに対してfirst-of-typeを効かせるため -->
- <section
- v-for="ids in layout"
- :class="$style.section"
- :style="columns.filter(c => ids.includes(c.id)).some(c => c.flexible) ? { flex: 1, minWidth: '350px' } : { width: Math.max(...columns.filter(c => ids.includes(c.id)).map(c => c.width)) + 'px' }"
- @wheel.passive.self="onWheel"
- >
- <Suspense>
- <component
- :is="columnComponents[columns.find(c => c.id === id)!.type] ?? XTlColumn"
- v-for="id in ids"
- :ref="id"
- :key="id"
- :class="[$style.column, { '_shadow': withWallpaper }]"
- :column="columns.find(c => c.id === id)!"
- :isStacked="ids.length > 1"
- @headerWheel="onWheel"
- />
- <template #fallback>
- <MkLoading/>
- </template>
- </Suspense>
- </section>
- <div v-if="layout.length === 0" class="_panel" :class="$style.onboarding">
- <div>{{ i18n.ts._deck.introduction }}</div>
- <div>{{ i18n.ts._deck.introduction2 }}</div>
+ <div :class="[$style.main, { [$style.withWallpaper]: withWallpaper, [$style.withSidebarAndTitlebar]: !isMobile && prefer.r['deck.navbarPosition'].value === 'left' && prefer.r.showTitlebar.value }]" :style="{ backgroundImage: prefer.s['deck.wallpaper'] != null ? `url(${ prefer.s['deck.wallpaper'] })` : null }">
+ <XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'top'"/>
+
+ <XAnnouncements v-if="$i"/>
+ <XStatusBars/>
+ <div :class="$style.columnsWrapper">
+ <!-- passive: https://bugs.webkit.org/show_bug.cgi?id=281300 -->
+ <div ref="columnsEl" :class="[$style.columns, { [$style.center]: prefer.r['deck.columnAlign'].value === 'center', [$style.snapScroll]: snapScroll }]" @contextmenu.self.prevent="onContextmenu" @wheel.passive.self="onWheel">
+ <!-- sectionを利用しているのは、deck.vue側でcolumnに対してfirst-of-typeを効かせるため -->
+ <section
+ v-for="ids in layout"
+ :class="$style.section"
+ :style="columns.filter(c => ids.includes(c.id)).some(c => c.flexible) ? { flex: 1, minWidth: '350px' } : { width: Math.max(...columns.filter(c => ids.includes(c.id)).map(c => c.width)) + 'px' }"
+ @wheel.passive.self="onWheel"
+ >
+ <Suspense>
+ <component
+ :is="columnComponents[columns.find(c => c.id === id)!.type] ?? XTlColumn"
+ v-for="id in ids"
+ :ref="id"
+ :key="id"
+ :class="[$style.column, { '_shadow': withWallpaper }]"
+ :column="columns.find(c => c.id === id)!"
+ :isStacked="ids.length > 1"
+ @headerWheel="onWheel"
+ />
+ <template #fallback>
+ <MkLoading/>
+ </template>
+ </Suspense>
+ </section>
+ <div v-if="layout.length === 0" class="_panel" :class="$style.onboarding">
+ <div>{{ i18n.ts._deck.introduction }}</div>
+ <div>{{ i18n.ts._deck.introduction2 }}</div>
+ </div>
+ </div>
+
+ <div v-if="prefer.r['deck.menuPosition'].value === 'right'" :class="$style.sideMenu">
+ <div :class="$style.sideMenuTop">
+ <button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.sideMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button>
+ <button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.sideMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button>
+ </div>
+ <div :class="$style.sideMenuMiddle">
+ <button v-tooltip.noDelay.left="i18n.ts._deck.addColumn" :class="$style.sideMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button>
+ </div>
+ <div :class="$style.sideMenuBottom">
+ <button v-tooltip.noDelay.left="i18n.ts.settings" :class="$style.sideMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings-2"></i></button>
+ </div>
</div>
</div>
- <div v-if="prefer.r['deck.menuPosition'].value === 'right'" :class="$style.sideMenu">
- <div :class="$style.sideMenuTop">
- <button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.sideMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button>
- <button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.sideMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button>
+ <div v-if="prefer.r['deck.menuPosition'].value === 'bottom'" :class="$style.bottomMenu">
+ <div :class="$style.bottomMenuLeft">
+ <button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.bottomMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button>
+ <button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.bottomMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button>
</div>
- <div :class="$style.sideMenuMiddle">
- <button v-tooltip.noDelay.left="i18n.ts._deck.addColumn" :class="$style.sideMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button>
+ <div :class="$style.bottomMenuMiddle">
+ <button v-tooltip.noDelay.left="i18n.ts._deck.addColumn" :class="$style.bottomMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button>
</div>
- <div :class="$style.sideMenuBottom">
- <button v-tooltip.noDelay.left="i18n.ts.settings" :class="$style.sideMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings-2"></i></button>
+ <div :class="$style.bottomMenuRight">
+ <button v-tooltip.noDelay.left="i18n.ts.settings" :class="$style.bottomMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings-2"></i></button>
</div>
</div>
- </div>
-
- <div v-if="prefer.r['deck.menuPosition'].value === 'bottom'" :class="$style.bottomMenu">
- <div :class="$style.bottomMenuLeft">
- <button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.bottomMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button>
- <button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.bottomMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button>
- </div>
- <div :class="$style.bottomMenuMiddle">
- <button v-tooltip.noDelay.left="i18n.ts._deck.addColumn" :class="$style.bottomMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button>
- </div>
- <div :class="$style.bottomMenuRight">
- <button v-tooltip.noDelay.left="i18n.ts.settings" :class="$style.bottomMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings-2"></i></button>
- </div>
- </div>
- <XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'bottom'"/>
+ <XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'bottom'"/>
- <XMobileFooterMenu v-if="isMobile" v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/>
+ <XMobileFooterMenu v-if="isMobile" v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/>
+ </div>
</div>
<XCommon v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/>
@@ -87,6 +91,7 @@ import XCommon from './_common_/common.vue';
import XSidebar from '@/ui/_common_/navbar.vue';
import XNavbarH from '@/ui/_common_/navbar-h.vue';
import XMobileFooterMenu from '@/ui/_common_/mobile-footer-menu.vue';
+import XTitlebar from '@/ui/_common_/titlebar.vue';
import * as os from '@/os.js';
import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
@@ -216,30 +221,26 @@ async function deleteProfile() {
window.document.documentElement.style.overflowY = 'hidden';
window.document.documentElement.style.scrollBehavior = 'auto';
-
-if (prefer.s['deck.wallpaper'] != null) {
- window.document.documentElement.style.backgroundImage = `url(${prefer.s['deck.wallpaper']})`;
-}
</script>
<style lang="scss" module>
.root {
- $nav-hide-threshold: 650px; // TODO: どこかに集約したい
-
--MI-margin: var(--MI-marginHalf);
--columnGap: v-bind("gap + 'px'");
display: flex;
+ flex-direction: column;
height: 100dvh;
box-sizing: border-box;
flex: 1;
+ background: var(--MI_THEME-navBg);
+}
- &.withWallpaper {
- .main {
- background: transparent;
- }
- }
+.nonTitlebarArea {
+ display: flex;
+ flex: 1;
+ min-height: 0;
}
.main {
@@ -247,7 +248,15 @@ if (prefer.s['deck.wallpaper'] != null) {
min-width: 0;
display: flex;
flex-direction: column;
- background: var(--MI_THEME-deckBg);
+
+ &:not(.withWallpaper) {
+ background: var(--MI_THEME-deckBg);
+ }
+
+ &.withSidebarAndTitlebar {
+ border-radius: 12px 0 0 0;
+ overflow: clip;
+ }
}
.columnsWrapper {
diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue
index 4c816f1544..62c59468bd 100644
--- a/packages/frontend/src/ui/deck/column.vue
+++ b/packages/frontend/src/ui/deck/column.vue
@@ -5,6 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div
+ class="_forceShrinkSpacer"
:class="[$style.root, { [$style.paged]: isMainColumn, [$style.naked]: naked, [$style.active]: active, [$style.draghover]: draghover, [$style.dragging]: dragging, [$style.dropready]: dropready, [$style.withWallpaper]: withWallpaper }]"
@dragover.prevent.stop="onDragover"
@dragleave="onDragleave"
@@ -53,7 +54,6 @@ import { DI } from '@/di.js';
provide('shouldHeaderThin', true);
provide('shouldOmitHeaderTitle', true);
-provide(DI.forceSpacerMin, true);
const withWallpaper = prefer.s['deck.wallpaper'] != null;
diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue
index 940cf72e28..fa2343ba27 100644
--- a/packages/frontend/src/ui/universal.vue
+++ b/packages/frontend/src/ui/universal.vue
@@ -4,22 +4,26 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div :class="$style.root">
- <XSidebar v-if="!isMobile" :class="$style.sidebar" :showWidgetButton="!isDesktop" @widgetButtonClick="widgetsShowing = true"/>
+<div :class="[$style.root, { '_forceShrinkSpacer': deviceKind === 'smartphone' }]">
+ <XTitlebar v-if="prefer.r.showTitlebar.value" style="flex-shrink: 0;"/>
- <div :class="$style.contents" @contextmenu.stop="onContextmenu">
- <div>
- <XPreferenceRestore v-if="shouldSuggestRestoreBackup"/>
- <XAnnouncements v-if="$i"/>
- <XStatusBars :class="$style.statusbars"/>
+ <div :class="$style.nonTitlebarArea">
+ <XSidebar v-if="!isMobile" :class="$style.sidebar" :showWidgetButton="!isDesktop" @widgetButtonClick="widgetsShowing = true"/>
+
+ <div :class="[$style.contents, !isMobile && prefer.r.showTitlebar.value ? $style.withSidebarAndTitlebar : null]" @contextmenu.stop="onContextmenu">
+ <div>
+ <XPreferenceRestore v-if="shouldSuggestRestoreBackup"/>
+ <XAnnouncements v-if="$i"/>
+ <XStatusBars :class="$style.statusbars"/>
+ </div>
+ <StackingRouterView v-if="prefer.s['experimental.stackingRouterView']" :class="$style.content"/>
+ <RouterView v-else :class="$style.content"/>
+ <XMobileFooterMenu v-if="isMobile" ref="navFooter" v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/>
</div>
- <StackingRouterView v-if="prefer.s['experimental.stackingRouterView']" :class="$style.content"/>
- <RouterView v-else :class="$style.content"/>
- <XMobileFooterMenu v-if="isMobile" ref="navFooter" v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/>
- </div>
- <div v-if="isDesktop && !pageMetadata?.needWideArea" :class="$style.widgets">
- <XWidgets/>
+ <div v-if="isDesktop && !pageMetadata?.needWideArea" :class="$style.widgets">
+ <XWidgets/>
+ </div>
</div>
<XCommon v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/>
@@ -34,6 +38,7 @@ import XCommon from './_common_/common.vue';
import type { PageMetadata } from '@/page.js';
import XMobileFooterMenu from '@/ui/_common_/mobile-footer-menu.vue';
import XPreferenceRestore from '@/ui/_common_/PreferenceRestore.vue';
+import XTitlebar from '@/ui/_common_/titlebar.vue';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { $i } from '@/i.js';
@@ -128,8 +133,15 @@ $widgets-hide-threshold: 1090px;
height: 100dvh;
overflow: clip;
contain: strict;
- box-sizing: border-box;
display: flex;
+ flex-direction: column;
+ background: var(--MI_THEME-navBg);
+}
+
+.nonTitlebarArea {
+ display: flex;
+ flex: 1;
+ min-height: 0;
}
.sidebar {
@@ -142,7 +154,12 @@ $widgets-hide-threshold: 1090px;
flex: 1;
height: 100%;
min-width: 0;
- background: var(--MI_THEME-bg);
+
+ &.withSidebarAndTitlebar {
+ background: var(--MI_THEME-navBg);
+ border-radius: 12px 0 0 0;
+ overflow: clip;
+ }
}
.content {
diff --git a/packages/frontend/src/utility/upload.ts b/packages/frontend/src/utility/upload.ts
index b43fea8e15..03240749e9 100644
--- a/packages/frontend/src/utility/upload.ts
+++ b/packages/frontend/src/utility/upload.ts
@@ -40,7 +40,7 @@ export function uploadFile(
const _folder = typeof folder === 'string' ? folder : folder?.id;
- if (file.size > instance.maxFileSize) {
+ if ((file.size > instance.maxFileSize) || (file.size > ($i.policies.maxFileSizeMb * 1024 * 1024))) {
alert({
type: 'error',
title: i18n.ts.failedToUpload,
diff --git a/packages/megalodon/package.json b/packages/megalodon/package.json
index 8b66d567f5..1ceb47759d 100644
--- a/packages/megalodon/package.json
+++ b/packages/megalodon/package.json
@@ -64,26 +64,25 @@
"@types/ws": "^8.5.10",
"axios": "1.7.4",
"dayjs": "^1.11.10",
- "form-data": "^4.0.0",
+ "form-data": "4.0.2",
"https-proxy-agent": "^7.0.2",
- "oauth": "^0.10.0",
+ "oauth": "0.10.2",
"object-assign-deep": "^0.4.0",
"parse-link-header": "^2.0.0",
"socks-proxy-agent": "^8.0.2",
- "typescript": "5.1.6",
- "uuid": "^9.0.1",
+ "typescript": "5.8.3",
+ "uuid": "11.1.0",
"ws": "8.17.1"
},
"devDependencies": {
- "@typescript-eslint/eslint-plugin": "8.27.0",
- "@typescript-eslint/parser": "8.27.0",
- "eslint": "9.22.0",
+ "@typescript-eslint/eslint-plugin": "8.31.0",
+ "@typescript-eslint/parser": "8.31.0",
+ "eslint": "9.25.1",
"eslint-config-prettier": "^9.0.0",
- "jest": "^29.7.0",
+ "jest": "29.7.0",
"jest-worker": "^29.7.0",
"lodash": "4.17.21",
- "prettier": "^3.1.0",
- "ts-jest": "^29.1.1",
- "typedoc": "^0.25.3"
+ "prettier": "3.5.3",
+ "ts-jest": "^29.1.1"
}
}
diff --git a/packages/misskey-bubble-game/package.json b/packages/misskey-bubble-game/package.json
index 4f0c5b2577..522aa18d1d 100644
--- a/packages/misskey-bubble-game/package.json
+++ b/packages/misskey-bubble-game/package.json
@@ -24,14 +24,14 @@
"devDependencies": {
"@types/matter-js": "0.19.8",
"@types/seedrandom": "3.0.8",
- "@types/node": "22.14.0",
- "@typescript-eslint/eslint-plugin": "8.29.1",
- "@typescript-eslint/parser": "8.29.1",
- "nodemon": "3.1.9",
+ "@types/node": "22.15.2",
+ "@typescript-eslint/eslint-plugin": "8.31.0",
+ "@typescript-eslint/parser": "8.31.0",
+ "nodemon": "3.1.10",
"execa": "9.5.2",
"typescript": "5.8.3",
- "esbuild": "0.25.2",
- "glob": "11.0.1"
+ "esbuild": "0.25.3",
+ "glob": "11.0.2"
},
"files": [
"built"
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index b2a4fb9cab..18cb070af5 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -1087,15 +1087,9 @@ type ChatMessagesCreateToUserResponse = operations['chat___messages___create-to-
type ChatMessagesDeleteRequest = operations['chat___messages___delete']['requestBody']['content']['application/json'];
// @public (undocumented)
-type ChatMessagesDeleteResponse = operations['chat___messages___delete']['responses']['200']['content']['application/json'];
-
-// @public (undocumented)
type ChatMessagesReactRequest = operations['chat___messages___react']['requestBody']['content']['application/json'];
// @public (undocumented)
-type ChatMessagesReactResponse = operations['chat___messages___react']['responses']['200']['content']['application/json'];
-
-// @public (undocumented)
type ChatMessagesRoomTimelineRequest = operations['chat___messages___room-timeline']['requestBody']['content']['application/json'];
// @public (undocumented)
@@ -1117,9 +1111,6 @@ type ChatMessagesShowResponse = operations['chat___messages___show']['responses'
type ChatMessagesUnreactRequest = operations['chat___messages___unreact']['requestBody']['content']['application/json'];
// @public (undocumented)
-type ChatMessagesUnreactResponse = operations['chat___messages___unreact']['responses']['200']['content']['application/json'];
-
-// @public (undocumented)
type ChatMessagesUserTimelineRequest = operations['chat___messages___user-timeline']['requestBody']['content']['application/json'];
// @public (undocumented)
@@ -1144,9 +1135,6 @@ type ChatRoomsCreateResponse = operations['chat___rooms___create']['responses'][
type ChatRoomsDeleteRequest = operations['chat___rooms___delete']['requestBody']['content']['application/json'];
// @public (undocumented)
-type ChatRoomsDeleteResponse = operations['chat___rooms___delete']['responses']['200']['content']['application/json'];
-
-// @public (undocumented)
type ChatRoomsInvitationsCreateRequest = operations['chat___rooms___invitations___create']['requestBody']['content']['application/json'];
// @public (undocumented)
@@ -1156,9 +1144,6 @@ type ChatRoomsInvitationsCreateResponse = operations['chat___rooms___invitations
type ChatRoomsInvitationsIgnoreRequest = operations['chat___rooms___invitations___ignore']['requestBody']['content']['application/json'];
// @public (undocumented)
-type ChatRoomsInvitationsIgnoreResponse = operations['chat___rooms___invitations___ignore']['responses']['200']['content']['application/json'];
-
-// @public (undocumented)
type ChatRoomsInvitationsInboxRequest = operations['chat___rooms___invitations___inbox']['requestBody']['content']['application/json'];
// @public (undocumented)
@@ -1180,15 +1165,9 @@ type ChatRoomsJoiningResponse = operations['chat___rooms___joining']['responses'
type ChatRoomsJoinRequest = operations['chat___rooms___join']['requestBody']['content']['application/json'];
// @public (undocumented)
-type ChatRoomsJoinResponse = operations['chat___rooms___join']['responses']['200']['content']['application/json'];
-
-// @public (undocumented)
type ChatRoomsLeaveRequest = operations['chat___rooms___leave']['requestBody']['content']['application/json'];
// @public (undocumented)
-type ChatRoomsLeaveResponse = operations['chat___rooms___leave']['responses']['200']['content']['application/json'];
-
-// @public (undocumented)
type ChatRoomsMembersRequest = operations['chat___rooms___members']['requestBody']['content']['application/json'];
// @public (undocumented)
@@ -1198,9 +1177,6 @@ type ChatRoomsMembersResponse = operations['chat___rooms___members']['responses'
type ChatRoomsMuteRequest = operations['chat___rooms___mute']['requestBody']['content']['application/json'];
// @public (undocumented)
-type ChatRoomsMuteResponse = operations['chat___rooms___mute']['responses']['200']['content']['application/json'];
-
-// @public (undocumented)
type ChatRoomsOwnedRequest = operations['chat___rooms___owned']['requestBody']['content']['application/json'];
// @public (undocumented)
@@ -1736,9 +1712,7 @@ declare namespace entities {
ChatMessagesCreateToUserRequest,
ChatMessagesCreateToUserResponse,
ChatMessagesDeleteRequest,
- ChatMessagesDeleteResponse,
ChatMessagesReactRequest,
- ChatMessagesReactResponse,
ChatMessagesRoomTimelineRequest,
ChatMessagesRoomTimelineResponse,
ChatMessagesSearchRequest,
@@ -1746,31 +1720,25 @@ declare namespace entities {
ChatMessagesShowRequest,
ChatMessagesShowResponse,
ChatMessagesUnreactRequest,
- ChatMessagesUnreactResponse,
ChatMessagesUserTimelineRequest,
ChatMessagesUserTimelineResponse,
ChatRoomsCreateRequest,
ChatRoomsCreateResponse,
ChatRoomsDeleteRequest,
- ChatRoomsDeleteResponse,
ChatRoomsInvitationsCreateRequest,
ChatRoomsInvitationsCreateResponse,
ChatRoomsInvitationsIgnoreRequest,
- ChatRoomsInvitationsIgnoreResponse,
ChatRoomsInvitationsInboxRequest,
ChatRoomsInvitationsInboxResponse,
ChatRoomsInvitationsOutboxRequest,
ChatRoomsInvitationsOutboxResponse,
ChatRoomsJoinRequest,
- ChatRoomsJoinResponse,
ChatRoomsJoiningRequest,
ChatRoomsJoiningResponse,
ChatRoomsLeaveRequest,
- ChatRoomsLeaveResponse,
ChatRoomsMembersRequest,
ChatRoomsMembersResponse,
ChatRoomsMuteRequest,
- ChatRoomsMuteResponse,
ChatRoomsOwnedRequest,
ChatRoomsOwnedResponse,
ChatRoomsShowRequest,
diff --git a/packages/misskey-js/generator/package.json b/packages/misskey-js/generator/package.json
index b63ddd3e58..f45d5c49eb 100644
--- a/packages/misskey-js/generator/package.json
+++ b/packages/misskey-js/generator/package.json
@@ -8,15 +8,15 @@
},
"devDependencies": {
"@readme/openapi-parser": "2.7.0",
- "@types/node": "22.13.15",
- "@typescript-eslint/eslint-plugin": "8.29.0",
- "@typescript-eslint/parser": "8.29.0",
- "eslint": "9.22.0",
+ "@types/node": "22.15.2",
+ "@typescript-eslint/eslint-plugin": "8.31.0",
+ "@typescript-eslint/parser": "8.31.0",
+ "eslint": "9.25.1",
"openapi-types": "12.1.3",
"openapi-typescript": "6.7.6",
"ts-case-convert": "2.1.0",
"tsx": "4.19.3",
- "typescript": "5.8.2"
+ "typescript": "5.8.3"
},
"files": [
"built"
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 8b52924b29..e7e9737897 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
{
"type": "module",
"name": "misskey-js",
- "version": "2025.4.1-alpha.2",
+ "version": "2025.4.1-beta.9",
"description": "Misskey SDK for JavaScript",
"license": "MIT",
"main": "./built/index.js",
@@ -35,23 +35,23 @@
"directory": "packages/misskey-js"
},
"devDependencies": {
- "@microsoft/api-extractor": "7.52.2",
- "@swc/jest": "0.2.37",
+ "@microsoft/api-extractor": "7.52.5",
+ "@swc/jest": "0.2.38",
"@types/jest": "29.5.14",
- "@types/node": "22.13.15",
- "@typescript-eslint/eslint-plugin": "8.29.0",
- "@typescript-eslint/parser": "8.29.0",
+ "@types/node": "22.15.2",
+ "@typescript-eslint/eslint-plugin": "8.31.0",
+ "@typescript-eslint/parser": "8.31.0",
"jest": "29.7.0",
"jest-fetch-mock": "3.0.3",
"jest-websocket-mock": "2.5.0",
"mock-socket": "9.3.1",
"ncp": "2.0.0",
- "nodemon": "3.1.9",
+ "nodemon": "3.1.10",
"execa": "8.0.1",
- "tsd": "0.31.2",
- "typescript": "5.8.2",
- "esbuild": "0.25.2",
- "glob": "11.0.1"
+ "tsd": "0.32.0",
+ "typescript": "5.8.3",
+ "esbuild": "0.25.3",
+ "glob": "11.0.2"
},
"files": [
"built"
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index 6c66c4beb0..9293a5e950 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -228,9 +228,7 @@ import type {
ChatMessagesCreateToUserRequest,
ChatMessagesCreateToUserResponse,
ChatMessagesDeleteRequest,
- ChatMessagesDeleteResponse,
ChatMessagesReactRequest,
- ChatMessagesReactResponse,
ChatMessagesRoomTimelineRequest,
ChatMessagesRoomTimelineResponse,
ChatMessagesSearchRequest,
@@ -238,31 +236,25 @@ import type {
ChatMessagesShowRequest,
ChatMessagesShowResponse,
ChatMessagesUnreactRequest,
- ChatMessagesUnreactResponse,
ChatMessagesUserTimelineRequest,
ChatMessagesUserTimelineResponse,
ChatRoomsCreateRequest,
ChatRoomsCreateResponse,
ChatRoomsDeleteRequest,
- ChatRoomsDeleteResponse,
ChatRoomsInvitationsCreateRequest,
ChatRoomsInvitationsCreateResponse,
ChatRoomsInvitationsIgnoreRequest,
- ChatRoomsInvitationsIgnoreResponse,
ChatRoomsInvitationsInboxRequest,
ChatRoomsInvitationsInboxResponse,
ChatRoomsInvitationsOutboxRequest,
ChatRoomsInvitationsOutboxResponse,
ChatRoomsJoinRequest,
- ChatRoomsJoinResponse,
ChatRoomsJoiningRequest,
ChatRoomsJoiningResponse,
ChatRoomsLeaveRequest,
- ChatRoomsLeaveResponse,
ChatRoomsMembersRequest,
ChatRoomsMembersResponse,
ChatRoomsMuteRequest,
- ChatRoomsMuteResponse,
ChatRoomsOwnedRequest,
ChatRoomsOwnedResponse,
ChatRoomsShowRequest,
@@ -824,24 +816,24 @@ export type Endpoints = {
'chat/history': { req: ChatHistoryRequest; res: ChatHistoryResponse };
'chat/messages/create-to-room': { req: ChatMessagesCreateToRoomRequest; res: ChatMessagesCreateToRoomResponse };
'chat/messages/create-to-user': { req: ChatMessagesCreateToUserRequest; res: ChatMessagesCreateToUserResponse };
- 'chat/messages/delete': { req: ChatMessagesDeleteRequest; res: ChatMessagesDeleteResponse };
- 'chat/messages/react': { req: ChatMessagesReactRequest; res: ChatMessagesReactResponse };
+ 'chat/messages/delete': { req: ChatMessagesDeleteRequest; res: EmptyResponse };
+ 'chat/messages/react': { req: ChatMessagesReactRequest; res: EmptyResponse };
'chat/messages/room-timeline': { req: ChatMessagesRoomTimelineRequest; res: ChatMessagesRoomTimelineResponse };
'chat/messages/search': { req: ChatMessagesSearchRequest; res: ChatMessagesSearchResponse };
'chat/messages/show': { req: ChatMessagesShowRequest; res: ChatMessagesShowResponse };
- 'chat/messages/unreact': { req: ChatMessagesUnreactRequest; res: ChatMessagesUnreactResponse };
+ 'chat/messages/unreact': { req: ChatMessagesUnreactRequest; res: EmptyResponse };
'chat/messages/user-timeline': { req: ChatMessagesUserTimelineRequest; res: ChatMessagesUserTimelineResponse };
'chat/rooms/create': { req: ChatRoomsCreateRequest; res: ChatRoomsCreateResponse };
- 'chat/rooms/delete': { req: ChatRoomsDeleteRequest; res: ChatRoomsDeleteResponse };
+ 'chat/rooms/delete': { req: ChatRoomsDeleteRequest; res: EmptyResponse };
'chat/rooms/invitations/create': { req: ChatRoomsInvitationsCreateRequest; res: ChatRoomsInvitationsCreateResponse };
- 'chat/rooms/invitations/ignore': { req: ChatRoomsInvitationsIgnoreRequest; res: ChatRoomsInvitationsIgnoreResponse };
+ 'chat/rooms/invitations/ignore': { req: ChatRoomsInvitationsIgnoreRequest; res: EmptyResponse };
'chat/rooms/invitations/inbox': { req: ChatRoomsInvitationsInboxRequest; res: ChatRoomsInvitationsInboxResponse };
'chat/rooms/invitations/outbox': { req: ChatRoomsInvitationsOutboxRequest; res: ChatRoomsInvitationsOutboxResponse };
- 'chat/rooms/join': { req: ChatRoomsJoinRequest; res: ChatRoomsJoinResponse };
+ 'chat/rooms/join': { req: ChatRoomsJoinRequest; res: EmptyResponse };
'chat/rooms/joining': { req: ChatRoomsJoiningRequest; res: ChatRoomsJoiningResponse };
- 'chat/rooms/leave': { req: ChatRoomsLeaveRequest; res: ChatRoomsLeaveResponse };
+ 'chat/rooms/leave': { req: ChatRoomsLeaveRequest; res: EmptyResponse };
'chat/rooms/members': { req: ChatRoomsMembersRequest; res: ChatRoomsMembersResponse };
- 'chat/rooms/mute': { req: ChatRoomsMuteRequest; res: ChatRoomsMuteResponse };
+ 'chat/rooms/mute': { req: ChatRoomsMuteRequest; res: EmptyResponse };
'chat/rooms/owned': { req: ChatRoomsOwnedRequest; res: ChatRoomsOwnedResponse };
'chat/rooms/show': { req: ChatRoomsShowRequest; res: ChatRoomsShowResponse };
'chat/rooms/update': { req: ChatRoomsUpdateRequest; res: ChatRoomsUpdateResponse };
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index 404746b3f2..f71407a6ae 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -231,9 +231,7 @@ export type ChatMessagesCreateToRoomResponse = operations['chat___messages___cre
export type ChatMessagesCreateToUserRequest = operations['chat___messages___create-to-user']['requestBody']['content']['application/json'];
export type ChatMessagesCreateToUserResponse = operations['chat___messages___create-to-user']['responses']['200']['content']['application/json'];
export type ChatMessagesDeleteRequest = operations['chat___messages___delete']['requestBody']['content']['application/json'];
-export type ChatMessagesDeleteResponse = operations['chat___messages___delete']['responses']['200']['content']['application/json'];
export type ChatMessagesReactRequest = operations['chat___messages___react']['requestBody']['content']['application/json'];
-export type ChatMessagesReactResponse = operations['chat___messages___react']['responses']['200']['content']['application/json'];
export type ChatMessagesRoomTimelineRequest = operations['chat___messages___room-timeline']['requestBody']['content']['application/json'];
export type ChatMessagesRoomTimelineResponse = operations['chat___messages___room-timeline']['responses']['200']['content']['application/json'];
export type ChatMessagesSearchRequest = operations['chat___messages___search']['requestBody']['content']['application/json'];
@@ -241,31 +239,25 @@ export type ChatMessagesSearchResponse = operations['chat___messages___search'][
export type ChatMessagesShowRequest = operations['chat___messages___show']['requestBody']['content']['application/json'];
export type ChatMessagesShowResponse = operations['chat___messages___show']['responses']['200']['content']['application/json'];
export type ChatMessagesUnreactRequest = operations['chat___messages___unreact']['requestBody']['content']['application/json'];
-export type ChatMessagesUnreactResponse = operations['chat___messages___unreact']['responses']['200']['content']['application/json'];
export type ChatMessagesUserTimelineRequest = operations['chat___messages___user-timeline']['requestBody']['content']['application/json'];
export type ChatMessagesUserTimelineResponse = operations['chat___messages___user-timeline']['responses']['200']['content']['application/json'];
export type ChatRoomsCreateRequest = operations['chat___rooms___create']['requestBody']['content']['application/json'];
export type ChatRoomsCreateResponse = operations['chat___rooms___create']['responses']['200']['content']['application/json'];
export type ChatRoomsDeleteRequest = operations['chat___rooms___delete']['requestBody']['content']['application/json'];
-export type ChatRoomsDeleteResponse = operations['chat___rooms___delete']['responses']['200']['content']['application/json'];
export type ChatRoomsInvitationsCreateRequest = operations['chat___rooms___invitations___create']['requestBody']['content']['application/json'];
export type ChatRoomsInvitationsCreateResponse = operations['chat___rooms___invitations___create']['responses']['200']['content']['application/json'];
export type ChatRoomsInvitationsIgnoreRequest = operations['chat___rooms___invitations___ignore']['requestBody']['content']['application/json'];
-export type ChatRoomsInvitationsIgnoreResponse = operations['chat___rooms___invitations___ignore']['responses']['200']['content']['application/json'];
export type ChatRoomsInvitationsInboxRequest = operations['chat___rooms___invitations___inbox']['requestBody']['content']['application/json'];
export type ChatRoomsInvitationsInboxResponse = operations['chat___rooms___invitations___inbox']['responses']['200']['content']['application/json'];
export type ChatRoomsInvitationsOutboxRequest = operations['chat___rooms___invitations___outbox']['requestBody']['content']['application/json'];
export type ChatRoomsInvitationsOutboxResponse = operations['chat___rooms___invitations___outbox']['responses']['200']['content']['application/json'];
export type ChatRoomsJoinRequest = operations['chat___rooms___join']['requestBody']['content']['application/json'];
-export type ChatRoomsJoinResponse = operations['chat___rooms___join']['responses']['200']['content']['application/json'];
export type ChatRoomsJoiningRequest = operations['chat___rooms___joining']['requestBody']['content']['application/json'];
export type ChatRoomsJoiningResponse = operations['chat___rooms___joining']['responses']['200']['content']['application/json'];
export type ChatRoomsLeaveRequest = operations['chat___rooms___leave']['requestBody']['content']['application/json'];
-export type ChatRoomsLeaveResponse = operations['chat___rooms___leave']['responses']['200']['content']['application/json'];
export type ChatRoomsMembersRequest = operations['chat___rooms___members']['requestBody']['content']['application/json'];
export type ChatRoomsMembersResponse = operations['chat___rooms___members']['responses']['200']['content']['application/json'];
export type ChatRoomsMuteRequest = operations['chat___rooms___mute']['requestBody']['content']['application/json'];
-export type ChatRoomsMuteResponse = operations['chat___rooms___mute']['responses']['200']['content']['application/json'];
export type ChatRoomsOwnedRequest = operations['chat___rooms___owned']['requestBody']['content']['application/json'];
export type ChatRoomsOwnedResponse = operations['chat___rooms___owned']['responses']['200']['content']['application/json'];
export type ChatRoomsShowRequest = operations['chat___rooms___show']['requestBody']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 46b9972bbb..b96cec5e7b 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -5487,6 +5487,7 @@ export type components = {
canUseTranslator: boolean;
canHideAds: boolean;
driveCapacityMb: number;
+ maxFileSizeMb: number;
alwaysMarkNsfw: boolean;
canUpdateBioMedia: boolean;
pinLimit: number;
@@ -15634,11 +15635,9 @@ export type operations = {
};
};
responses: {
- /** @description OK (with results) */
- 200: {
- content: {
- 'application/json': unknown;
- };
+ /** @description OK (without any results) */
+ 204: {
+ content: never;
};
/** @description Client error */
400: {
@@ -15689,11 +15688,9 @@ export type operations = {
};
};
responses: {
- /** @description OK (with results) */
- 200: {
- content: {
- 'application/json': unknown;
- };
+ /** @description OK (without any results) */
+ 204: {
+ content: never;
};
/** @description Client error */
400: {
@@ -15917,11 +15914,9 @@ export type operations = {
};
};
responses: {
- /** @description OK (with results) */
- 200: {
- content: {
- 'application/json': unknown;
- };
+ /** @description OK (without any results) */
+ 204: {
+ content: never;
};
/** @description Client error */
400: {
@@ -16091,11 +16086,9 @@ export type operations = {
};
};
responses: {
- /** @description OK (with results) */
- 200: {
- content: {
- 'application/json': unknown;
- };
+ /** @description OK (without any results) */
+ 204: {
+ content: never;
};
/** @description Client error */
400: {
@@ -16207,11 +16200,9 @@ export type operations = {
};
};
responses: {
- /** @description OK (with results) */
- 200: {
- content: {
- 'application/json': unknown;
- };
+ /** @description OK (without any results) */
+ 204: {
+ content: never;
};
/** @description Client error */
400: {
@@ -16379,11 +16370,9 @@ export type operations = {
};
};
responses: {
- /** @description OK (with results) */
- 200: {
- content: {
- 'application/json': unknown;
- };
+ /** @description OK (without any results) */
+ 204: {
+ content: never;
};
/** @description Client error */
400: {
@@ -16491,11 +16480,9 @@ export type operations = {
};
};
responses: {
- /** @description OK (with results) */
- 200: {
- content: {
- 'application/json': unknown;
- };
+ /** @description OK (without any results) */
+ 204: {
+ content: never;
};
/** @description Client error */
400: {
@@ -16606,11 +16593,9 @@ export type operations = {
};
};
responses: {
- /** @description OK (with results) */
- 200: {
- content: {
- 'application/json': unknown;
- };
+ /** @description OK (without any results) */
+ 204: {
+ content: never;
};
/** @description Client error */
400: {
diff --git a/packages/misskey-reversi/package.json b/packages/misskey-reversi/package.json
index 7e12bc4a23..b6f30a1320 100644
--- a/packages/misskey-reversi/package.json
+++ b/packages/misskey-reversi/package.json
@@ -22,14 +22,14 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
- "@types/node": "22.14.0",
- "@typescript-eslint/eslint-plugin": "8.29.1",
- "@typescript-eslint/parser": "8.29.1",
+ "@types/node": "22.15.2",
+ "@typescript-eslint/eslint-plugin": "8.31.0",
+ "@typescript-eslint/parser": "8.31.0",
"execa": "9.5.2",
- "nodemon": "3.1.9",
+ "nodemon": "3.1.10",
"typescript": "5.8.3",
- "esbuild": "0.25.2",
- "glob": "11.0.1"
+ "esbuild": "0.25.3",
+ "glob": "11.0.2"
},
"files": [
"built"
diff --git a/packages/sw/package.json b/packages/sw/package.json
index 1a493e916c..502b05238b 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -9,15 +9,15 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"dependencies": {
- "esbuild": "0.25.2",
+ "esbuild": "0.25.3",
"idb-keyval": "6.2.1",
"misskey-js": "workspace:*"
},
"devDependencies": {
- "@typescript-eslint/parser": "8.29.1",
+ "@typescript-eslint/parser": "8.31.0",
"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.74",
"eslint-plugin-import": "2.31.0",
- "nodemon": "3.1.9",
+ "nodemon": "3.1.10",
"typescript": "5.8.3"
},
"type": "module"