summaryrefslogtreecommitdiff
path: root/packages/frontend/src
diff options
context:
space:
mode:
authordakkar <dakkar@thenautilus.net>2024-11-08 15:52:37 +0000
committerdakkar <dakkar@thenautilus.net>2024-11-08 15:52:37 +0000
commitf079edaf3ccc1fea9242f0f8522ebbfc7e8242e4 (patch)
treeead184cf29c147bc74ed92ce905b46e5e42209c1 /packages/frontend/src
parentmerge: Bump version number (!735) (diff)
parentRelease: 2024.10.1 (diff)
downloadsharkey-f079edaf3ccc1fea9242f0f8522ebbfc7e8242e4.tar.gz
sharkey-f079edaf3ccc1fea9242f0f8522ebbfc7e8242e4.tar.bz2
sharkey-f079edaf3ccc1fea9242f0f8522ebbfc7e8242e4.zip
Merge tag '2024.10.1' into feature/2024.10
Diffstat (limited to 'packages/frontend/src')
-rw-r--r--packages/frontend/src/_dev_boot_.ts2
-rw-r--r--packages/frontend/src/account.ts8
-rw-r--r--packages/frontend/src/boot/common.ts12
-rw-r--r--packages/frontend/src/components/MkAbuseReport.stories.impl.ts54
-rw-r--r--packages/frontend/src/components/MkAbuseReport.vue214
-rw-r--r--packages/frontend/src/components/MkAccountMoved.vue6
-rw-r--r--packages/frontend/src/components/MkAnalogClock.vue6
-rw-r--r--packages/frontend/src/components/MkAnnouncementDialog.vue10
-rw-r--r--packages/frontend/src/components/MkAntennaEditor.vue2
-rw-r--r--packages/frontend/src/components/MkAsUi.vue4
-rw-r--r--packages/frontend/src/components/MkAutocomplete.vue6
-rw-r--r--packages/frontend/src/components/MkButton.vue22
-rw-r--r--packages/frontend/src/components/MkCaptcha.vue28
-rw-r--r--packages/frontend/src/components/MkChannelFollowButton.vue16
-rw-r--r--packages/frontend/src/components/MkChannelPreview.vue12
-rw-r--r--packages/frontend/src/components/MkChart.vue4
-rw-r--r--packages/frontend/src/components/MkChartLegend.vue4
-rw-r--r--packages/frontend/src/components/MkClipPreview.vue6
-rw-r--r--packages/frontend/src/components/MkCode.core.vue2
-rw-r--r--packages/frontend/src/components/MkCode.vue6
-rw-r--r--packages/frontend/src/components/MkCodeEditor.vue14
-rw-r--r--packages/frontend/src/components/MkCodeInline.vue2
-rw-r--r--packages/frontend/src/components/MkColorInput.vue14
-rw-r--r--packages/frontend/src/components/MkContainer.vue15
-rw-r--r--packages/frontend/src/components/MkCropperDialog.vue6
-rw-r--r--packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue10
-rw-r--r--packages/frontend/src/components/MkDateSeparatedList.vue21
-rw-r--r--packages/frontend/src/components/MkDialog.vue8
-rw-r--r--packages/frontend/src/components/MkDivider.vue2
-rw-r--r--packages/frontend/src/components/MkDonation.vue6
-rw-r--r--packages/frontend/src/components/MkDrive.file.vue8
-rw-r--r--packages/frontend/src/components/MkDrive.folder.vue16
-rw-r--r--packages/frontend/src/components/MkDrive.vue6
-rw-r--r--packages/frontend/src/components/MkDriveFileThumbnail.vue4
-rw-r--r--packages/frontend/src/components/MkEmbedCodeGenDialog.vue8
-rw-r--r--packages/frontend/src/components/MkEmojiPicker.section.vue4
-rw-r--r--packages/frontend/src/components/MkEmojiPicker.vue24
-rw-r--r--packages/frontend/src/components/MkExtensionInstaller.stories.impl.ts83
-rw-r--r--packages/frontend/src/components/MkExtensionInstaller.vue146
-rw-r--r--packages/frontend/src/components/MkFileListForAdmin.vue2
-rw-r--r--packages/frontend/src/components/MkFlashPreview.vue5
-rw-r--r--packages/frontend/src/components/MkFoldableSection.vue10
-rw-r--r--packages/frontend/src/components/MkFolder.vue41
-rw-r--r--packages/frontend/src/components/MkFollowButton.vue16
-rw-r--r--packages/frontend/src/components/MkFormDialog.file.vue2
-rw-r--r--packages/frontend/src/components/MkFormFooter.vue2
-rw-r--r--packages/frontend/src/components/MkFukidashi.vue100
-rw-r--r--packages/frontend/src/components/MkGalleryPostPreview.vue2
-rw-r--r--packages/frontend/src/components/MkGoogle.vue4
-rw-r--r--packages/frontend/src/components/MkInfo.vue10
-rw-r--r--packages/frontend/src/components/MkInput.vue14
-rw-r--r--packages/frontend/src/components/MkInstanceCardMini.vue6
-rw-r--r--packages/frontend/src/components/MkInstanceStats.vue6
-rw-r--r--packages/frontend/src/components/MkInviteCode.vue4
-rw-r--r--packages/frontend/src/components/MkLaunchPad.vue11
-rw-r--r--packages/frontend/src/components/MkMediaAudio.vue12
-rw-r--r--packages/frontend/src/components/MkMediaBanner.vue1
-rw-r--r--packages/frontend/src/components/MkMediaImage.vue22
-rw-r--r--packages/frontend/src/components/MkMediaList.vue14
-rw-r--r--packages/frontend/src/components/MkMediaRange.vue4
-rw-r--r--packages/frontend/src/components/MkMediaVideo.vue18
-rw-r--r--packages/frontend/src/components/MkMention.vue15
-rw-r--r--packages/frontend/src/components/MkMenu.vue45
-rw-r--r--packages/frontend/src/components/MkMiniChart.vue2
-rw-r--r--packages/frontend/src/components/MkModalWindow.vue26
-rw-r--r--packages/frontend/src/components/MkNote.vue59
-rw-r--r--packages/frontend/src/components/MkNoteDetailed.vue38
-rw-r--r--packages/frontend/src/components/MkNoteHeader.vue22
-rw-r--r--packages/frontend/src/components/MkNoteSimple.vue2
-rw-r--r--packages/frontend/src/components/MkNoteSub.vue4
-rw-r--r--packages/frontend/src/components/MkNotes.vue8
-rw-r--r--packages/frontend/src/components/MkNotification.vue27
-rw-r--r--packages/frontend/src/components/MkNotifications.vue2
-rw-r--r--packages/frontend/src/components/MkNumberDiff.vue4
-rw-r--r--packages/frontend/src/components/MkObjectView.value.vue8
-rw-r--r--packages/frontend/src/components/MkOmit.vue8
-rw-r--r--packages/frontend/src/components/MkPagePreview.vue15
-rw-r--r--packages/frontend/src/components/MkPageWindow.vue4
-rw-r--r--packages/frontend/src/components/MkPoll.vue14
-rw-r--r--packages/frontend/src/components/MkPostForm.vue30
-rw-r--r--packages/frontend/src/components/MkPostFormAttaches.vue2
-rw-r--r--packages/frontend/src/components/MkRadio.vue20
-rw-r--r--packages/frontend/src/components/MkRadios.vue2
-rw-r--r--packages/frontend/src/components/MkRange.vue14
-rw-r--r--packages/frontend/src/components/MkReactionEffect.vue2
-rw-r--r--packages/frontend/src/components/MkReactionsViewer.details.vue2
-rw-r--r--packages/frontend/src/components/MkReactionsViewer.reaction.vue10
-rw-r--r--packages/frontend/src/components/MkRemoteCaution.vue8
-rw-r--r--packages/frontend/src/components/MkRetentionLineChart.vue2
-rw-r--r--packages/frontend/src/components/MkRippleEffect.vue4
-rw-r--r--packages/frontend/src/components/MkRolePreview.vue8
-rw-r--r--packages/frontend/src/components/MkSelect.vue14
-rw-r--r--packages/frontend/src/components/MkSignin.input.vue206
-rw-r--r--packages/frontend/src/components/MkSignin.passkey.vue92
-rw-r--r--packages/frontend/src/components/MkSignin.password.vue188
-rw-r--r--packages/frontend/src/components/MkSignin.totp.vue74
-rw-r--r--packages/frontend/src/components/MkSignin.vue579
-rw-r--r--packages/frontend/src/components/MkSigninDialog.vue85
-rw-r--r--packages/frontend/src/components/MkSignupDialog.form.vue133
-rw-r--r--packages/frontend/src/components/MkSignupDialog.rules.vue16
-rw-r--r--packages/frontend/src/components/MkSignupDialog.vue4
-rw-r--r--packages/frontend/src/components/MkSourceCodeAvailablePopup.vue6
-rw-r--r--packages/frontend/src/components/MkSubNoteContent.vue14
-rw-r--r--packages/frontend/src/components/MkSuperMenu.vue16
-rw-r--r--packages/frontend/src/components/MkSwitch.button.vue12
-rw-r--r--packages/frontend/src/components/MkSwitch.vue8
-rw-r--r--packages/frontend/src/components/MkSystemWebhookEditor.vue28
-rw-r--r--packages/frontend/src/components/MkTab.vue8
-rw-r--r--packages/frontend/src/components/MkTagCloud.vue2
-rw-r--r--packages/frontend/src/components/MkTextarea.vue14
-rw-r--r--packages/frontend/src/components/MkTokenGenerateWindow.vue8
-rw-r--r--packages/frontend/src/components/MkTooltip.vue2
-rw-r--r--packages/frontend/src/components/MkTutorialDialog.Note.vue10
-rw-r--r--packages/frontend/src/components/MkTutorialDialog.PostNote.vue12
-rw-r--r--packages/frontend/src/components/MkTutorialDialog.Sensitive.vue14
-rw-r--r--packages/frontend/src/components/MkTutorialDialog.Timeline.vue12
-rw-r--r--packages/frontend/src/components/MkTutorialDialog.vue8
-rw-r--r--packages/frontend/src/components/MkUpdated.vue4
-rw-r--r--packages/frontend/src/components/MkUrlPreview.vue11
-rw-r--r--packages/frontend/src/components/MkUserAnnouncementEditDialog.vue12
-rw-r--r--packages/frontend/src/components/MkUserCardMini.vue6
-rw-r--r--packages/frontend/src/components/MkUserInfo.vue12
-rw-r--r--packages/frontend/src/components/MkUserList.vue2
-rw-r--r--packages/frontend/src/components/MkUserOnlineIndicator.vue2
-rw-r--r--packages/frontend/src/components/MkUserPopup.vue10
-rw-r--r--packages/frontend/src/components/MkUserSelectDialog.vue4
-rw-r--r--packages/frontend/src/components/MkUserSetupDialog.Follow.vue2
-rw-r--r--packages/frontend/src/components/MkUserSetupDialog.Profile.vue5
-rw-r--r--packages/frontend/src/components/MkUserSetupDialog.User.vue6
-rw-r--r--packages/frontend/src/components/MkUserSetupDialog.vue10
-rw-r--r--packages/frontend/src/components/MkVisibilityPicker.vue2
-rw-r--r--packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue2
-rw-r--r--packages/frontend/src/components/MkVisitorDashboard.vue10
-rw-r--r--packages/frontend/src/components/MkWaitingDialog.vue6
-rw-r--r--packages/frontend/src/components/MkWidgets.vue4
-rw-r--r--packages/frontend/src/components/MkWindow.vue22
-rw-r--r--packages/frontend/src/components/form/link.vue10
-rw-r--r--packages/frontend/src/components/form/section.vue6
-rw-r--r--packages/frontend/src/components/form/slot.vue2
-rw-r--r--packages/frontend/src/components/global/MkAd.vue28
-rw-r--r--packages/frontend/src/components/global/MkLoading.vue2
-rw-r--r--packages/frontend/src/components/global/MkMfm.ts19
-rw-r--r--packages/frontend/src/components/global/MkPageHeader.tabs.vue2
-rw-r--r--packages/frontend/src/components/global/MkPageHeader.vue12
-rw-r--r--packages/frontend/src/components/global/MkStickyContainer.vue59
-rw-r--r--packages/frontend/src/components/global/MkTime.vue4
-rw-r--r--packages/frontend/src/components/global/RouterView.vue3
-rw-r--r--packages/frontend/src/components/page/page.dynamic.vue6
-rw-r--r--packages/frontend/src/components/page/page.image.vue4
-rw-r--r--packages/frontend/src/components/page/page.note.vue4
-rw-r--r--packages/frontend/src/directives/adaptive-bg.ts2
-rw-r--r--packages/frontend/src/directives/adaptive-border.ts2
-rw-r--r--packages/frontend/src/directives/panel.ts6
-rw-r--r--packages/frontend/src/os.ts8
-rw-r--r--packages/frontend/src/pages/about.federation.vue2
-rw-r--r--packages/frontend/src/pages/about.overview.vue8
-rw-r--r--packages/frontend/src/pages/admin-user.vue33
-rw-r--r--packages/frontend/src/pages/admin/RolesEditorFormula.vue6
-rw-r--r--packages/frontend/src/pages/admin/_header_.vue10
-rw-r--r--packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue8
-rw-r--r--packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue4
-rw-r--r--packages/frontend/src/pages/admin/abuses.vue17
-rw-r--r--packages/frontend/src/pages/admin/ads.vue2
-rw-r--r--packages/frontend/src/pages/admin/announcements.vue12
-rw-r--r--packages/frontend/src/pages/admin/bot-protection.vue13
-rw-r--r--packages/frontend/src/pages/admin/branding.vue4
-rw-r--r--packages/frontend/src/pages/admin/email-settings.vue4
-rw-r--r--packages/frontend/src/pages/admin/federation.vue2
-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/moderation.vue25
-rw-r--r--packages/frontend/src/pages/admin/modlog.ModLog.vue13
-rw-r--r--packages/frontend/src/pages/admin/modlog.vue11
-rw-r--r--packages/frontend/src/pages/admin/object-storage.vue4
-rw-r--r--packages/frontend/src/pages/admin/overview.ap-requests.vue2
-rw-r--r--packages/frontend/src/pages/admin/overview.federation.vue4
-rw-r--r--packages/frontend/src/pages/admin/overview.pie.vue2
-rw-r--r--packages/frontend/src/pages/admin/overview.queue.vue4
-rw-r--r--packages/frontend/src/pages/admin/overview.stats.vue4
-rw-r--r--packages/frontend/src/pages/admin/performance.vue16
-rw-r--r--packages/frontend/src/pages/admin/queue.chart.vue4
-rw-r--r--packages/frontend/src/pages/admin/relays.vue4
-rw-r--r--packages/frontend/src/pages/admin/roles.edit.vue4
-rw-r--r--packages/frontend/src/pages/admin/roles.role.vue2
-rw-r--r--packages/frontend/src/pages/admin/server-rules.vue10
-rw-r--r--packages/frontend/src/pages/admin/settings.vue2
-rw-r--r--packages/frontend/src/pages/admin/system-webhook.item.vue7
-rw-r--r--packages/frontend/src/pages/announcement.vue6
-rw-r--r--packages/frontend/src/pages/announcements.vue6
-rw-r--r--packages/frontend/src/pages/antenna-timeline.vue10
-rw-r--r--packages/frontend/src/pages/avatar-decorations.vue8
-rw-r--r--packages/frontend/src/pages/channel-editor.vue2
-rw-r--r--packages/frontend/src/pages/channel.vue14
-rw-r--r--packages/frontend/src/pages/clip.vue2
-rw-r--r--packages/frontend/src/pages/custom-emojis-manager.vue16
-rw-r--r--packages/frontend/src/pages/drive.file.info.vue22
-rw-r--r--packages/frontend/src/pages/drop-and-fusion.game.vue2
-rw-r--r--packages/frontend/src/pages/emoji-edit-dialog.vue8
-rw-r--r--packages/frontend/src/pages/emojis.emoji.vue4
-rw-r--r--packages/frontend/src/pages/explore.featured.vue2
-rw-r--r--packages/frontend/src/pages/explore.users.vue2
-rw-r--r--packages/frontend/src/pages/favorites.vue4
-rw-r--r--packages/frontend/src/pages/flash/flash-edit.vue6
-rw-r--r--packages/frontend/src/pages/flash/flash-index.vue3
-rw-r--r--packages/frontend/src/pages/flash/flash.vue4
-rw-r--r--packages/frontend/src/pages/gallery/edit.vue4
-rw-r--r--packages/frontend/src/pages/gallery/index.vue2
-rw-r--r--packages/frontend/src/pages/gallery/post.vue16
-rw-r--r--packages/frontend/src/pages/games.vue2
-rw-r--r--packages/frontend/src/pages/install-extensions.vue125
-rw-r--r--packages/frontend/src/pages/instance-info.vue1
-rw-r--r--packages/frontend/src/pages/list.vue2
-rw-r--r--packages/frontend/src/pages/my-antennas/index.vue4
-rw-r--r--packages/frontend/src/pages/my-lists/index.vue4
-rw-r--r--packages/frontend/src/pages/my-lists/list.vue10
-rw-r--r--packages/frontend/src/pages/note.vue8
-rw-r--r--packages/frontend/src/pages/notifications.vue2
-rw-r--r--packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue2
-rw-r--r--packages/frontend/src/pages/page-editor/page-editor.container.vue6
-rw-r--r--packages/frontend/src/pages/page.vue30
-rw-r--r--packages/frontend/src/pages/reversi/game.board.vue16
-rw-r--r--packages/frontend/src/pages/reversi/game.setting.vue16
-rw-r--r--packages/frontend/src/pages/reversi/index.vue20
-rw-r--r--packages/frontend/src/pages/scratchpad.vue6
-rw-r--r--packages/frontend/src/pages/search.note.vue4
-rw-r--r--packages/frontend/src/pages/settings/2fa.vue2
-rw-r--r--packages/frontend/src/pages/settings/accounts.vue18
-rw-r--r--packages/frontend/src/pages/settings/apps.vue73
-rw-r--r--packages/frontend/src/pages/settings/avatar-decoration.decoration.vue6
-rw-r--r--packages/frontend/src/pages/settings/avatar-decoration.dialog.vue6
-rw-r--r--packages/frontend/src/pages/settings/avatar-decoration.vue2
-rw-r--r--packages/frontend/src/pages/settings/drive-cleaner.vue2
-rw-r--r--packages/frontend/src/pages/settings/email.vue2
-rw-r--r--packages/frontend/src/pages/settings/emoji-picker.vue8
-rw-r--r--packages/frontend/src/pages/settings/index.vue2
-rw-r--r--packages/frontend/src/pages/settings/mute-block.vue2
-rw-r--r--packages/frontend/src/pages/settings/navbar.vue2
-rw-r--r--packages/frontend/src/pages/settings/other.vue8
-rw-r--r--packages/frontend/src/pages/settings/preferences-backups.vue2
-rw-r--r--packages/frontend/src/pages/settings/profile.vue52
-rw-r--r--packages/frontend/src/pages/settings/security.vue6
-rw-r--r--packages/frontend/src/pages/settings/sounds.sound.vue4
-rw-r--r--packages/frontend/src/pages/settings/theme.vue14
-rw-r--r--packages/frontend/src/pages/settings/webhook.edit.vue2
-rw-r--r--packages/frontend/src/pages/settings/webhook.vue4
-rw-r--r--packages/frontend/src/pages/signup-complete.vue6
-rw-r--r--packages/frontend/src/pages/tag.vue8
-rw-r--r--packages/frontend/src/pages/theme-editor.vue2
-rw-r--r--packages/frontend/src/pages/timeline.vue16
-rw-r--r--packages/frontend/src/pages/user-list-timeline.vue10
-rw-r--r--packages/frontend/src/pages/user/clips.vue2
-rw-r--r--packages/frontend/src/pages/user/follow-list.vue2
-rw-r--r--packages/frontend/src/pages/user/gallery.vue2
-rw-r--r--packages/frontend/src/pages/user/home.vue67
-rw-r--r--packages/frontend/src/pages/user/index.timeline.vue8
-rw-r--r--packages/frontend/src/pages/user/lists.vue4
-rw-r--r--packages/frontend/src/pages/user/raw.vue12
-rw-r--r--packages/frontend/src/pages/user/reactions.vue2
-rw-r--r--packages/frontend/src/pages/welcome.entrance.a.vue12
-rw-r--r--packages/frontend/src/pages/welcome.setup.vue34
-rw-r--r--packages/frontend/src/pages/welcome.timeline.note.vue4
-rw-r--r--packages/frontend/src/pages/welcome.timeline.vue4
-rw-r--r--packages/frontend/src/scripts/get-note-menu.ts11
-rw-r--r--packages/frontend/src/scripts/init-chart.ts2
-rw-r--r--packages/frontend/src/scripts/theme.ts2
-rw-r--r--packages/frontend/src/store.ts10
-rw-r--r--packages/frontend/src/style.scss129
-rw-r--r--packages/frontend/src/ui/_common_/announcements.vue12
-rw-r--r--packages/frontend/src/ui/_common_/common.vue14
-rw-r--r--packages/frontend/src/ui/_common_/navbar-for-mobile.vue33
-rw-r--r--packages/frontend/src/ui/_common_/navbar.vue76
-rw-r--r--packages/frontend/src/ui/_common_/statusbars.vue4
-rw-r--r--packages/frontend/src/ui/_common_/stream-indicator.vue4
-rw-r--r--packages/frontend/src/ui/_common_/upload.vue4
-rw-r--r--packages/frontend/src/ui/classic.header.vue15
-rw-r--r--packages/frontend/src/ui/classic.sidebar.vue13
-rw-r--r--packages/frontend/src/ui/classic.vue29
-rw-r--r--packages/frontend/src/ui/deck.vue41
-rw-r--r--packages/frontend/src/ui/deck/column.vue36
-rw-r--r--packages/frontend/src/ui/deck/widgets-column.vue6
-rw-r--r--packages/frontend/src/ui/universal.vue71
-rw-r--r--packages/frontend/src/ui/visitor.vue10
-rw-r--r--packages/frontend/src/ui/zen.vue12
-rw-r--r--packages/frontend/src/widgets/WidgetAiscript.vue6
-rw-r--r--packages/frontend/src/widgets/WidgetBirthdayFollowings.vue6
-rw-r--r--packages/frontend/src/widgets/WidgetCalendar.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetFederation.vue4
-rw-r--r--packages/frontend/src/widgets/WidgetJobQueue.vue8
-rw-r--r--packages/frontend/src/widgets/WidgetMemo.vue4
-rw-r--r--packages/frontend/src/widgets/WidgetOnlineUsers.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetRss.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetRssTicker.vue4
-rw-r--r--packages/frontend/src/widgets/WidgetTrends.vue4
293 files changed, 3036 insertions, 2014 deletions
diff --git a/packages/frontend/src/_dev_boot_.ts b/packages/frontend/src/_dev_boot_.ts
index 1601f247d7..f312765dcf 100644
--- a/packages/frontend/src/_dev_boot_.ts
+++ b/packages/frontend/src/_dev_boot_.ts
@@ -43,7 +43,7 @@ async function main() {
const theme = localStorage.getItem('theme');
if (theme) {
for (const [k, v] of Object.entries(JSON.parse(theme))) {
- document.documentElement.style.setProperty(`--${k}`, v.toString());
+ document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
// HTMLの theme-color 適用
if (k === 'htmlThemeColor') {
diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts
index e3416f2c29..995578dece 100644
--- a/packages/frontend/src/account.ts
+++ b/packages/frontend/src/account.ts
@@ -227,7 +227,7 @@ export async function openAccountMenu(opts: {
function showSigninDialog() {
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {}, {
- done: res => {
+ done: (res: Misskey.entities.SigninFlowResponse & { finished: true }) => {
addAccount(res.id, res.i);
success();
},
@@ -237,9 +237,9 @@ export async function openAccountMenu(opts: {
function createAccount() {
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSignupDialog.vue')), {}, {
- done: res => {
- addAccount(res.id, res.i);
- switchAccountWithToken(res.i);
+ done: (res: Misskey.entities.SignupResponse) => {
+ addAccount(res.id, res.token);
+ switchAccountWithToken(res.token);
},
closed: () => dispose(),
});
diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts
index af8bbf57d2..6102f88ae5 100644
--- a/packages/frontend/src/boot/common.ts
+++ b/packages/frontend/src/boot/common.ts
@@ -183,24 +183,18 @@ export async function common(createVue: () => App<Element>) {
if (instance.defaultLightTheme != null) ColdDeviceStorage.set('lightTheme', JSON.parse(instance.defaultLightTheme));
if (instance.defaultDarkTheme != null) ColdDeviceStorage.set('darkTheme', JSON.parse(instance.defaultDarkTheme));
defaultStore.set('themeInitial', false);
- } else {
- if (defaultStore.state.darkMode) {
- applyTheme(darkTheme.value);
- } else {
- applyTheme(lightTheme.value);
- }
}
});
watch(defaultStore.reactiveState.useBlurEffectForModal, v => {
- document.documentElement.style.setProperty('--modalBgFilter', v ? 'blur(4px)' : 'none');
+ document.documentElement.style.setProperty('--MI-modalBgFilter', v ? 'blur(4px)' : 'none');
}, { immediate: true });
watch(defaultStore.reactiveState.useBlurEffect, v => {
if (v) {
- document.documentElement.style.removeProperty('--blur');
+ document.documentElement.style.removeProperty('--MI-blur');
} else {
- document.documentElement.style.setProperty('--blur', 'none');
+ document.documentElement.style.setProperty('--MI-blur', 'none');
}
}, { immediate: true });
diff --git a/packages/frontend/src/components/MkAbuseReport.stories.impl.ts b/packages/frontend/src/components/MkAbuseReport.stories.impl.ts
deleted file mode 100644
index cf09c96fd4..0000000000
--- a/packages/frontend/src/components/MkAbuseReport.stories.impl.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-/* eslint-disable @typescript-eslint/explicit-function-return-type */
-import { action } from '@storybook/addon-actions';
-import { StoryObj } from '@storybook/vue3';
-import { HttpResponse, http } from 'msw';
-import { abuseUserReport } from '../../.storybook/fakes.js';
-import { commonHandlers } from '../../.storybook/mocks.js';
-import MkAbuseReport from './MkAbuseReport.vue';
-export const Default = {
- render(args) {
- return {
- components: {
- MkAbuseReport,
- },
- setup() {
- return {
- args,
- };
- },
- computed: {
- props() {
- return {
- ...this.args,
- };
- },
- events() {
- return {
- resolved: action('resolved'),
- };
- },
- },
- template: '<MkAbuseReport v-bind="props" v-on="events" />',
- };
- },
- args: {
- report: abuseUserReport(),
- },
- parameters: {
- layout: 'fullscreen',
- msw: {
- handlers: [
- ...commonHandlers,
- http.post('/api/admin/resolve-abuse-user-report', async ({ request }) => {
- action('POST /api/admin/resolve-abuse-user-report')(await request.json());
- return HttpResponse.json({});
- }),
- ],
- },
- },
-} satisfies StoryObj<typeof MkAbuseReport>;
diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue
index d13eedaade..4d6757a09f 100644
--- a/packages/frontend/src/components/MkAbuseReport.vue
+++ b/packages/frontend/src/components/MkAbuseReport.vue
@@ -4,141 +4,153 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
- <div class="bcekxzvu _margin _panel">
- <div class="target">
- <MkA v-user-preview="report.targetUserId" class="info" :to="`/admin/user/${report.targetUserId}`" :behavior="'window'">
- <MkAvatar class="avatar" :user="report.targetUser" indicator/>
- <div class="names">
- <MkUserName class="name" :user="report.targetUser"/>
- <MkAcct class="acct" :user="report.targetUser" style="display: block;"/>
- </div>
- </MkA>
- <div class="keyvalCtn">
- <MkKeyValue>
- <template #key>{{ i18n.ts.registeredDate }}</template>
- <template #value>{{ dateString(report.targetUser.createdAt) }} (<MkTime :time="report.targetUser.createdAt"/>)</template>
- </MkKeyValue>
- <MkKeyValue>
- <template #key>{{ i18n.ts.reporter }}</template>
- <template #value><MkA :to="`/admin/user/${report.reporter.id}`" class="_link" :behavior="'window'">@{{ report.reporter.username }}</MkA></template>
- </MkKeyValue>
- <MkKeyValue>
- <template #key>{{ i18n.ts.createdAt }}</template>
- <template #value><MkTime :time="report.createdAt" mode="absolute"/> (<MkTime :time="report.createdAt" mode="relative"/>)</template>
- </MkKeyValue>
- </div>
- <hr>
+<MkFolder>
+ <template #icon>
+ <i v-if="report.resolved && report.resolvedAs === 'accept'" class="ti ti-check" style="color: var(--MI_THEME-success)"></i>
+ <i v-else-if="report.resolved && report.resolvedAs === 'reject'" class="ti ti-x" style="color: var(--MI_THEME-error)"></i>
+ <i v-else-if="report.resolved" class="ti ti-slash"></i>
+ <i v-else class="ti ti-exclamation-circle" style="color: var(--MI_THEME-warn)"></i>
+ </template>
+ <template #label><MkAcct :user="report.targetUser"/> (by <MkAcct :user="report.reporter"/>)</template>
+ <template #caption>{{ report.comment }}</template>
+ <template #suffix><MkTime :time="report.createdAt"/></template>
+ <template #footer>
+ <div class="_buttons">
+ <template v-if="!report.resolved">
+ <MkButton @click="resolve('accept')"><i class="ti ti-check" style="color: var(--MI_THEME-success)"></i> {{ i18n.ts._abuseUserReport.resolve }} ({{ i18n.ts._abuseUserReport.accept }})</MkButton>
+ <MkButton @click="resolve('reject')"><i class="ti ti-x" style="color: var(--MI_THEME-error)"></i> {{ i18n.ts._abuseUserReport.resolve }} ({{ i18n.ts._abuseUserReport.reject }})</MkButton>
+ <MkButton @click="resolve(null)"><i class="ti ti-slash"></i> {{ i18n.ts._abuseUserReport.resolve }} ({{ i18n.ts.other }})</MkButton>
+ </template>
+ <template v-if="report.targetUser.host != null">
+ <MkButton :disabled="report.forwarded" primary @click="forward"><i class="ti ti-corner-up-right"></i> {{ i18n.ts._abuseUserReport.forward }}</MkButton>
+ <div v-tooltip:dialog="i18n.ts._abuseUserReport.forwardDescription" class="_button _help"><i class="ti ti-help-circle"></i></div>
+ </template>
+ <button class="_button" style="margin-left: auto; width: 34px;" @click="showMenu"><i class="ti ti-dots"></i></button>
</div>
- <div class="detail">
- <div>
+ </template>
+
+ <div :class="$style.root" class="_gaps_s">
+ <MkFolder :withSpacer="false">
+ <template #icon><MkAvatar :user="report.targetUser" style="width: 18px; height: 18px;"/></template>
+ <template #label>{{ i18n.ts.target }}: <MkAcct :user="report.targetUser"/></template>
+ <template #suffix>#{{ report.targetUserId.toUpperCase() }}</template>
+
+ <div style="container-type: inline-size;">
+ <RouterView :router="targetRouter"/>
+ </div>
+ </MkFolder>
+
+ <MkFolder :defaultOpen="true">
+ <template #icon><i class="ti ti-message-2"></i></template>
+ <template #label>{{ i18n.ts.details }}</template>
+ <div class="_gaps_s">
<Mfm :text="report.comment" :isBlock="true" :linkNavigationBehavior="'window'"/>
</div>
- <hr/>
- <div v-if="report.assignee" class="assignee">
- {{ i18n.ts.moderator }}:
- <MkA :to="`/admin/user/${report.assignee.id}`" class="_link" :behavior="'window'">@{{ report.assignee.username }}</MkA>
+ </MkFolder>
+
+ <MkFolder :withSpacer="false">
+ <template #icon><MkAvatar :user="report.reporter" style="width: 18px; height: 18px;"/></template>
+ <template #label>{{ i18n.ts.reporter }}: <MkAcct :user="report.reporter"/></template>
+ <template #suffix>#{{ report.reporterId.toUpperCase() }}</template>
+
+ <div style="container-type: inline-size;">
+ <RouterView :router="reporterRouter"/>
</div>
- <div class="action">
- <MkSwitch v-model="forward" c:disabled="report.targetUser.host == null || report.resolved">
- {{ i18n.ts.forwardReport }}
- <template #caption>{{ i18n.ts.forwardReportIsAnonymous }}</template>
- </MkSwitch>
- <MkButton v-if="!report.resolved" primary @click="resolve">{{ i18n.ts.abuseMarkAsResolved }}</MkButton>
+ </MkFolder>
+
+ <MkFolder :defaultOpen="false">
+ <template #icon><i class="ti ti-message-2"></i></template>
+ <template #label>{{ i18n.ts.moderationNote }}</template>
+ <template #suffix>{{ moderationNote.length > 0 ? '...' : i18n.ts.none }}</template>
+ <div class="_gaps_s">
+ <MkTextarea v-model="moderationNote" manualSave>
+ <template #caption>{{ i18n.ts.moderationNoteDescription }}</template>
+ </MkTextarea>
</div>
+ </MkFolder>
+
+ <div v-if="report.assignee">
+ {{ i18n.ts.moderator }}:
+ <MkAcct :user="report.assignee"/>
</div>
</div>
+</MkFolder>
</template>
<script lang="ts" setup>
-import { ref } from 'vue';
+import { provide, ref, watch } from 'vue';
+import * as Misskey from 'misskey-js';
import MkButton from '@/components/MkButton.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { dateString } from '@/filters/date.js';
+import MkFolder from '@/components/MkFolder.vue';
+import RouterView from '@/components/global/RouterView.vue';
+import { useRouterFactory } from '@/router/supplier';
+import MkTextarea from '@/components/MkTextarea.vue';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
const props = defineProps<{
- report: any;
+ report: Misskey.entities.AdminAbuseUserReportsResponse[number];
}>();
const emit = defineEmits<{
(ev: 'resolved', reportId: string): void;
}>();
-const forward = ref(props.report.forwarded);
+const routerFactory = useRouterFactory();
+const targetRouter = routerFactory(`/admin/user/${props.report.targetUserId}`);
+targetRouter.init();
+const reporterRouter = routerFactory(`/admin/user/${props.report.reporterId}`);
+reporterRouter.init();
+
+const moderationNote = ref(props.report.moderationNote ?? '');
+
+watch(moderationNote, async () => {
+ os.apiWithDialog('admin/update-abuse-user-report', {
+ reportId: props.report.id,
+ moderationNote: moderationNote.value,
+ }).then(() => {
+ });
+});
-function resolve() {
+function resolve(resolvedAs) {
os.apiWithDialog('admin/resolve-abuse-user-report', {
- forward: forward.value,
reportId: props.report.id,
+ resolvedAs,
}).then(() => {
emit('resolved', props.report.id);
});
}
-</script>
-
-<style lang="scss" scoped>
-.bcekxzvu {
- display: flex;
- flex-direction: column;
- transition: .1s;
-
- > .target {
- box-sizing: border-box;
- text-align: left;
- padding: 24px 24px 0px 24px;
-
- > .info {
- display: flex;
- box-sizing: border-box;
- align-items: center;
- padding: 14px;
- border-radius: var(--radius-sm);
- --c: rgb(255 196 0 / 15%);
- background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%);
- background-size: 16px 16px;
-
- > .avatar {
- width: 42px;
- height: 42px;
- }
-
- > .names {
- margin-left: 0.3em;
- padding: 0 8px;
- flex: 1;
-
- white-space: pre;
- overflow: hidden;
- > .name {
- font-weight: bold;
- }
- }
- }
-
- > .keyvalCtn {
- display: inline-flex;
- gap: 15px;
- margin-top: 15px;
- }
- }
+function forward() {
+ os.apiWithDialog('admin/forward-abuse-user-report', {
+ reportId: props.report.id,
+ }).then(() => {
- > .detail {
- display: flex;
- flex-direction: column;
- padding: 0px 24px 24px 24px;
+ });
+}
- .assignee {
- margin-bottom: 15px;
- }
+function showMenu(ev: MouseEvent) {
+ os.popupMenu([{
+ icon: 'ti ti-id',
+ text: 'Copy ID',
+ action: () => {
+ copyToClipboard(props.report.id);
+ },
+ }, {
+ icon: 'ti ti-json',
+ text: 'Copy JSON',
+ action: () => {
+ copyToClipboard(JSON.stringify(props.report, null, '\t'));
+ },
+ }], ev.currentTarget ?? ev.target);
+}
+</script>
- .action {
- display: flex;
- flex-direction: column;
- gap: 15px;
- }
- }
+<style lang="scss" module>
+.root {
}
</style>
diff --git a/packages/frontend/src/components/MkAccountMoved.vue b/packages/frontend/src/components/MkAccountMoved.vue
index 796524fce9..0839955d9d 100644
--- a/packages/frontend/src/components/MkAccountMoved.vue
+++ b/packages/frontend/src/components/MkAccountMoved.vue
@@ -32,9 +32,9 @@ misskeyApi('users/show', { userId: props.movedTo }).then(u => user.value = u);
.root {
padding: 16px;
font-size: 90%;
- background: var(--infoWarnBg);
- color: var(--error);
- border-radius: var(--radius);
+ background: var(--MI_THEME-infoWarnBg);
+ color: var(--MI_THEME-error);
+ border-radius: var(--MI-radius);
}
.link {
diff --git a/packages/frontend/src/components/MkAnalogClock.vue b/packages/frontend/src/components/MkAnalogClock.vue
index 835efbd6cd..c8fa6246e0 100644
--- a/packages/frontend/src/components/MkAnalogClock.vue
+++ b/packages/frontend/src/components/MkAnalogClock.vue
@@ -193,12 +193,12 @@ tick();
function calcColors() {
const computedStyle = getComputedStyle(document.documentElement);
- const dark = tinycolor(computedStyle.getPropertyValue('--bg')).isDark();
- const accent = tinycolor(computedStyle.getPropertyValue('--accent')).toHexString();
+ const dark = tinycolor(computedStyle.getPropertyValue('--MI_THEME-bg')).isDark();
+ const accent = tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString();
majorGraduationColor.value = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';
//minorGraduationColor = dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
sHandColor.value = dark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)';
- mHandColor.value = tinycolor(computedStyle.getPropertyValue('--fg')).toHexString();
+ mHandColor.value = tinycolor(computedStyle.getPropertyValue('--MI_THEME-fg')).toHexString();
hHandColor.value = accent;
nowColor.value = accent;
}
diff --git a/packages/frontend/src/components/MkAnnouncementDialog.vue b/packages/frontend/src/components/MkAnnouncementDialog.vue
index c81fea175c..0e85b27ad8 100644
--- a/packages/frontend/src/components/MkAnnouncementDialog.vue
+++ b/packages/frontend/src/components/MkAnnouncementDialog.vue
@@ -9,9 +9,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.header">
<span :class="$style.icon">
<i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i>
- <i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--warn);"></i>
- <i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--error);"></i>
- <i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--success);"></i>
+ <i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i>
+ <i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--MI_THEME-error);"></i>
+ <i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--MI_THEME-success);"></i>
</span>
<span :class="$style.title">{{ announcement.title }}</span>
</div>
@@ -83,8 +83,8 @@ onMounted(() => {
min-width: 320px;
max-width: 480px;
box-sizing: border-box;
- background: var(--panel);
- border-radius: var(--radius);
+ background: var(--MI_THEME-panel);
+ border-radius: var(--MI-radius);
}
.header {
diff --git a/packages/frontend/src/components/MkAntennaEditor.vue b/packages/frontend/src/components/MkAntennaEditor.vue
index cb7ee3d6ca..2386ba6fa7 100644
--- a/packages/frontend/src/components/MkAntennaEditor.vue
+++ b/packages/frontend/src/components/MkAntennaEditor.vue
@@ -170,6 +170,6 @@ function addUser() {
.actions {
margin-top: 16px;
padding: 24px 0;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
</style>
diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue
index e2af4f034e..13680e7d9c 100644
--- a/packages/frontend/src/components/MkAsUi.vue
+++ b/packages/frontend/src/components/MkAsUi.vue
@@ -106,7 +106,7 @@ const containerStyle = computed(() => {
const border = isBordered ? {
borderWidth: c.borderWidth ?? '1px',
- borderColor: c.borderColor ?? 'var(--divider)',
+ borderColor: c.borderColor ?? 'var(--MI_THEME-divider)',
borderStyle: c.borderStyle ?? 'solid',
} : undefined;
@@ -165,7 +165,7 @@ function openPostForm() {
}
.postForm {
- background: var(--bg);
+ background: var(--MI_THEME-bg);
border-radius: var(--radius-sm);
}
</style>
diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue
index de5207f350..ef6b8c69e5 100644
--- a/packages/frontend/src/components/MkAutocomplete.vue
+++ b/packages/frontend/src/components/MkAutocomplete.vue
@@ -407,16 +407,16 @@ onBeforeUnmount(() => {
text-overflow: ellipsis;
&:hover {
- background: var(--X3);
+ background: var(--MI_THEME-X3);
}
&[data-selected='true'] {
- background: var(--accent);
+ background: var(--MI_THEME-accent);
color: #fff !important;
}
&:active {
- background: var(--accentDarken);
+ background: var(--MI_THEME-accentDarken);
color: #fff !important;
}
}
diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue
index e30f74460d..6427f9b58a 100644
--- a/packages/frontend/src/components/MkButton.vue
+++ b/packages/frontend/src/components/MkButton.vue
@@ -129,7 +129,7 @@ function onMousedown(evt: MouseEvent): void {
font-size: 95%;
box-shadow: none;
text-decoration: none;
- background: var(--buttonBg);
+ background: var(--MI_THEME-buttonBg);
border-radius: var(--radius-xs);
overflow: clip;
box-sizing: border-box;
@@ -140,11 +140,11 @@ function onMousedown(evt: MouseEvent): void {
}
&:not(:disabled):hover {
- background: var(--buttonHoverBg);
+ background: var(--MI_THEME-buttonHoverBg);
}
&:not(:disabled):active {
- background: var(--buttonHoverBg);
+ background: var(--MI_THEME-buttonHoverBg);
}
&.small {
@@ -167,15 +167,15 @@ function onMousedown(evt: MouseEvent): void {
&.primary {
font-weight: bold;
- color: var(--fgOnAccent) !important;
- background: var(--accent);
+ color: var(--MI_THEME-fgOnAccent) !important;
+ background: var(--MI_THEME-accent);
&:not(:disabled):hover {
- background: hsl(from var(--accent) h s calc(l + 5));
+ background: hsl(from var(--MI_THEME-accent) h s calc(l + 5));
}
&:not(:disabled):active {
- background: hsl(from var(--accent) h s calc(l + 5));
+ background: hsl(from var(--MI_THEME-accent) h s calc(l + 5));
}
}
@@ -216,15 +216,15 @@ function onMousedown(evt: MouseEvent): void {
&.gradate {
font-weight: bold;
- color: var(--fgOnAccent) !important;
- background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
+ color: var(--MI_THEME-fgOnAccent) !important;
+ background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
&:not(:disabled):hover {
- background: linear-gradient(90deg, hsl(from var(--accent) h s calc(l + 5)), hsl(from var(--accent) h s calc(l + 5)));
+ background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5)));
}
&:not(:disabled):active {
- background: linear-gradient(90deg, hsl(from var(--accent) h s calc(l + 5)), hsl(from var(--accent) h s calc(l + 5)));
+ background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5)));
}
}
diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue
index ab00ea9930..aebf3128b0 100644
--- a/packages/frontend/src/components/MkCaptcha.vue
+++ b/packages/frontend/src/components/MkCaptcha.vue
@@ -10,6 +10,17 @@ SPDX-License-Identifier: AGPL-3.0-only
<div id="mcaptcha__widget-container" class="m-captcha-style"></div>
<div ref="captchaEl"></div>
</div>
+ <div v-if="props.provider == 'testcaptcha'" style="background: #eee; border: solid 1px #888; padding: 8px; color: #000; max-width: 320px; display: flex; gap: 10px; align-items: center; box-shadow: 2px 2px 6px #0004; border-radius: 4px;">
+ <img src="/client-assets/testcaptcha.png" style="width: 60px; height: 60px; "/>
+ <div v-if="testcaptchaPassed">
+ <div style="color: green;">Test captcha passed!</div>
+ </div>
+ <div v-else>
+ <div style="font-size: 13px; margin-bottom: 4px;">Type "ai-chan-kawaii" to pass captcha</div>
+ <input v-model="testcaptchaInput" data-cy-testcaptcha-input/>
+ <button type="button" data-cy-testcaptcha-submit @click="testcaptchaSubmit">Submit</button>
+ </div>
+ </div>
<div v-else ref="captchaEl"></div>
</div>
</template>
@@ -32,7 +43,7 @@ export type Captcha = {
}): void;
};
-export type CaptchaProvider = 'hcaptcha' | 'recaptcha' | 'turnstile' | 'mcaptcha' | 'fc';
+export type CaptchaProvider = 'hcaptcha' | 'recaptcha' | 'turnstile' | 'mcaptcha' | 'fc' | 'testcaptcha';
type CaptchaContainer = {
readonly [_ in CaptchaProvider]?: Captcha;
@@ -57,6 +68,9 @@ const available = ref(false);
const captchaEl = shallowRef<HTMLDivElement | undefined>();
+const testcaptchaInput = ref('');
+const testcaptchaPassed = ref(false);
+
const variable = computed(() => {
switch (props.provider) {
case 'hcaptcha': return 'hcaptcha';
@@ -64,6 +78,7 @@ const variable = computed(() => {
case 'turnstile': return 'turnstile';
case 'mcaptcha': return 'mcaptcha';
case 'fc': return 'friendlyChallenge';
+ case 'testcaptcha': return 'testcaptcha';
}
});
@@ -76,6 +91,7 @@ const src = computed(() => {
case 'turnstile': return 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit';
case 'fc': return 'https://cdn.jsdelivr.net/npm/friendly-challenge@0.9.18/widget.min.js';
case 'mcaptcha': return null;
+ case 'testcaptcha': return null;
}
});
@@ -83,7 +99,7 @@ const scriptId = computed(() => `script-${props.provider}`);
const captcha = computed<Captcha>(() => window[variable.value] || {} as unknown as Captcha);
-if (loaded || props.provider === 'mcaptcha') {
+if (loaded || props.provider === 'mcaptcha' || props.provider === 'testcaptcha') {
available.value = true;
} else if (src.value !== null) {
(document.getElementById(scriptId.value) ?? document.head.appendChild(Object.assign(document.createElement('script'), {
@@ -96,6 +112,8 @@ if (loaded || props.provider === 'mcaptcha') {
function reset() {
if (captcha.value.reset) captcha.value.reset();
+ testcaptchaPassed.value = false;
+ testcaptchaInput.value = '';
}
async function requestRender() {
@@ -140,6 +158,12 @@ function onReceivedMessage(message: MessageEvent) {
}
}
+function testcaptchaSubmit() {
+ testcaptchaPassed.value = testcaptchaInput.value === 'ai-chan-kawaii';
+ callback(testcaptchaPassed.value ? 'testcaptcha-passed' : undefined);
+ if (!testcaptchaPassed.value) testcaptchaInput.value = '';
+}
+
onMounted(() => {
if (available.value) {
window.addEventListener('message', onReceivedMessage);
diff --git a/packages/frontend/src/components/MkChannelFollowButton.vue b/packages/frontend/src/components/MkChannelFollowButton.vue
index 6dace43fde..99922ffbc8 100644
--- a/packages/frontend/src/components/MkChannelFollowButton.vue
+++ b/packages/frontend/src/components/MkChannelFollowButton.vue
@@ -68,9 +68,9 @@ async function onClick() {
position: relative;
display: inline-block;
font-weight: bold;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
background: transparent;
- border: solid 1px var(--accent);
+ border: solid 1px var(--MI_THEME-accent);
padding: 0;
height: 31px;
font-size: 16px;
@@ -99,17 +99,17 @@ async function onClick() {
}
&.active {
- color: var(--fgOnAccent);
- background: var(--accent);
+ color: var(--MI_THEME-fgOnAccent);
+ background: var(--MI_THEME-accent);
&:hover {
- background: var(--accentLighten);
- border-color: var(--accentLighten);
+ background: var(--MI_THEME-accentLighten);
+ border-color: var(--MI_THEME-accentLighten);
}
&:active {
- background: var(--accentDarken);
- border-color: var(--accentDarken);
+ background: var(--MI_THEME-accentDarken);
+ border-color: var(--MI_THEME-accentDarken);
}
}
diff --git a/packages/frontend/src/components/MkChannelPreview.vue b/packages/frontend/src/components/MkChannelPreview.vue
index 2f7ec34d44..a25c596975 100644
--- a/packages/frontend/src/components/MkChannelPreview.vue
+++ b/packages/frontend/src/components/MkChannelPreview.vue
@@ -100,7 +100,7 @@ const bannerStyle = computed(() => {
height: 100%;
border-radius: inherit;
pointer-events: none;
- box-shadow: inset 0 0 0 2px var(--focus);
+ box-shadow: inset 0 0 0 2px var(--MI_THEME-focus);
}
}
@@ -117,7 +117,7 @@ const bannerStyle = computed(() => {
left: 0;
width: 100%;
height: 64px;
- background: linear-gradient(0deg, var(--panel), color(from var(--panel) srgb r g b / 0));
+ background: linear-gradient(0deg, var(--MI_THEME-panel), color(from var(--MI_THEME-panel) srgb r g b / 0));
}
> .name {
@@ -148,7 +148,7 @@ const bannerStyle = computed(() => {
bottom: 16px;
left: 16px;
background: rgba(0, 0, 0, 0.7);
- color: var(--warn);
+ color: var(--MI_THEME-warn);
border-radius: var(--radius-sm);
font-weight: bold;
font-size: 1em;
@@ -167,7 +167,7 @@ const bannerStyle = computed(() => {
> footer {
padding: 12px 16px;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
> span {
opacity: 0.7;
@@ -213,8 +213,8 @@ const bannerStyle = computed(() => {
top: 0;
right: 0;
transform: translate(25%, -25%);
- background-color: var(--accent);
- border: solid var(--bg) 4px;
+ background-color: var(--MI_THEME-accent);
+ border: solid var(--MI_THEME-bg) 4px;
border-radius: var(--radius-full);
width: 1.5rem;
height: 1.5rem;
diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue
index 57d325b11a..d05f4921f6 100644
--- a/packages/frontend/src/components/MkChart.vue
+++ b/packages/frontend/src/components/MkChart.vue
@@ -863,8 +863,8 @@ onMounted(() => {
left: 0;
width: 100%;
height: 100%;
- -webkit-backdrop-filter: var(--blur, blur(12px));
- backdrop-filter: var(--blur, blur(12px));
+ -webkit-backdrop-filter: var(--MI-blur, blur(12px));
+ backdrop-filter: var(--MI-blur, blur(12px));
display: flex;
justify-content: center;
align-items: center;
diff --git a/packages/frontend/src/components/MkChartLegend.vue b/packages/frontend/src/components/MkChartLegend.vue
index 240c9c919e..1e402a617c 100644
--- a/packages/frontend/src/components/MkChartLegend.vue
+++ b/packages/frontend/src/components/MkChartLegend.vue
@@ -53,11 +53,11 @@ defineExpose({
> .item {
font-size: 85%;
padding: 4px 12px 4px 8px;
- border: solid 1px var(--divider);
+ border: solid 1px var(--MI_THEME-divider);
border-radius: var(--radius-ellipse);
&:hover {
- border-color: var(--inputBorderHover);
+ border-color: var(--MI_THEME-inputBorderHover);
}
&.disabled {
diff --git a/packages/frontend/src/components/MkClipPreview.vue b/packages/frontend/src/components/MkClipPreview.vue
index dd550733cb..5b09ec90dd 100644
--- a/packages/frontend/src/components/MkClipPreview.vue
+++ b/packages/frontend/src/components/MkClipPreview.vue
@@ -49,13 +49,13 @@ const remaining = computed(() => {
outline: none;
.root {
- box-shadow: inset 0 0 0 2px var(--focus);
+ box-shadow: inset 0 0 0 2px var(--MI_THEME-focus);
}
}
&:hover {
text-decoration: none;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
}
@@ -65,7 +65,7 @@ const remaining = computed(() => {
.divider {
height: 1px;
- background: var(--divider);
+ background: var(--MI_THEME-divider);
}
.description {
diff --git a/packages/frontend/src/components/MkCode.core.vue b/packages/frontend/src/components/MkCode.core.vue
index 9e54420034..ae80bf33ba 100644
--- a/packages/frontend/src/components/MkCode.core.vue
+++ b/packages/frontend/src/components/MkCode.core.vue
@@ -96,7 +96,7 @@ watch(() => props.lang, (to) => {
margin: .5em 0;
overflow: auto;
border-radius: var(--radius-sm);
- border: 1px solid var(--divider);
+ border: 1px solid var(--MI_THEME-divider);
font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;
color: var(--shiki-fallback);
diff --git a/packages/frontend/src/components/MkCode.vue b/packages/frontend/src/components/MkCode.vue
index 05cde89dd9..b4a04d4cc3 100644
--- a/packages/frontend/src/components/MkCode.vue
+++ b/packages/frontend/src/components/MkCode.vue
@@ -71,7 +71,7 @@ function copy() {
.codeBlockFallbackRoot {
display: block;
overflow-wrap: anywhere;
- background: var(--bg);
+ background: var(--MI_THEME-bg);
padding: 1em;
margin: .5em 0;
overflow: auto;
@@ -94,8 +94,8 @@ function copy() {
border-radius: var(--radius-sm);
padding: 24px;
margin-top: 4px;
- color: var(--fg);
- background: var(--bg);
+ color: var(--MI_THEME-fg);
+ background: var(--MI_THEME-bg);
}
.codePlaceholderContainer {
diff --git a/packages/frontend/src/components/MkCodeEditor.vue b/packages/frontend/src/components/MkCodeEditor.vue
index b233189ab0..1f9bd3e186 100644
--- a/packages/frontend/src/components/MkCodeEditor.vue
+++ b/packages/frontend/src/components/MkCodeEditor.vue
@@ -140,7 +140,7 @@ watch(v, newValue => {
.caption {
font-size: 0.85em;
padding: 8px 0 0 0;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
&:empty {
display: none;
@@ -160,17 +160,17 @@ watch(v, newValue => {
margin: 0;
border-radius: var(--radius-sm);
padding: 0;
- color: var(--fg);
- border: solid 1px var(--panel);
+ color: var(--MI_THEME-fg);
+ border: solid 1px var(--MI_THEME-panel);
transition: border-color 0.1s ease-out;
font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;
&:hover {
- border-color: var(--inputBorderHover) !important;
+ border-color: var(--MI_THEME-inputBorderHover) !important;
}
}
.focused.codeEditorRoot {
- border-color: var(--accent) !important;
+ border-color: var(--MI_THEME-accent) !important;
border-radius: var(--radius-sm);
}
@@ -196,7 +196,7 @@ watch(v, newValue => {
resize: none;
text-align: left;
color: transparent;
- caret-color: var(--fg);
+ caret-color: var(--MI_THEME-fg);
background-color: transparent;
border: 0;
border-radius: var(--radius-sm);
@@ -213,6 +213,6 @@ watch(v, newValue => {
}
.textarea::selection {
- color: var(--bg);
+ color: var(--MI_THEME-bg);
}
</style>
diff --git a/packages/frontend/src/components/MkCodeInline.vue b/packages/frontend/src/components/MkCodeInline.vue
index 6add80d1bc..04b6e54108 100644
--- a/packages/frontend/src/components/MkCodeInline.vue
+++ b/packages/frontend/src/components/MkCodeInline.vue
@@ -18,7 +18,7 @@ const props = defineProps<{
display: inline-block;
font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;
overflow-wrap: anywhere;
- background: var(--bg);
+ background: var(--MI_THEME-bg);
padding: .1em;
border-radius: .3em;
}
diff --git a/packages/frontend/src/components/MkColorInput.vue b/packages/frontend/src/components/MkColorInput.vue
index 99aa46d561..e83c0bccd4 100644
--- a/packages/frontend/src/components/MkColorInput.vue
+++ b/packages/frontend/src/components/MkColorInput.vue
@@ -60,7 +60,7 @@ const onInput = () => {
.caption {
font-size: 0.85em;
padding: 8px 0 0 0;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
&:empty {
display: none;
@@ -72,8 +72,8 @@ const onInput = () => {
&.focused {
> .inputCore {
- border-color: var(--accent) !important;
- //box-shadow: 0 0 0 4px var(--focus);
+ border-color: var(--MI_THEME-accent) !important;
+ //box-shadow: 0 0 0 4px var(--MI_THEME-focus);
}
}
@@ -98,9 +98,9 @@ const onInput = () => {
font: inherit;
font-weight: normal;
font-size: 1em;
- color: var(--fg);
- background: var(--panel);
- border: solid 1px var(--panel);
+ color: var(--MI_THEME-fg);
+ background: var(--MI_THEME-panel);
+ border: solid 1px var(--MI_THEME-panel);
border-radius: var(--radius-sm);
outline: none;
box-shadow: none;
@@ -108,7 +108,7 @@ const onInput = () => {
transition: border-color 0.1s ease-out;
&:hover {
- border-color: var(--inputBorderHover) !important;
+ border-color: var(--MI_THEME-inputBorderHover) !important;
}
}
</style>
diff --git a/packages/frontend/src/components/MkContainer.vue b/packages/frontend/src/components/MkContainer.vue
index 5f71e289b8..15d41baa7e 100644
--- a/packages/frontend/src/components/MkContainer.vue
+++ b/packages/frontend/src/components/MkContainer.vue
@@ -165,10 +165,11 @@ onUnmounted(() => {
.header {
position: sticky;
- top: var(--stickyTop, 0px);
+ top: var(--MI-stickyTop, 0px);
left: 0;
- color: var(--panelHeaderFg);
- border-bottom: solid 0.5px var(--panelHeaderDivider);
+ color: var(--MI_THEME-panelHeaderFg);
+ background: var(--MI_THEME-panelHeaderBg);
+ border-bottom: solid 0.5px var(--MI_THEME-panelHeaderDivider);
z-index: 2;
line-height: 1.4em;
background: color-mix(in srgb, var(--panelHeaderBg) 35%, transparent);
@@ -201,7 +202,7 @@ onUnmounted(() => {
}
.content {
- --stickyTop: 0px;
+ --MI-stickyTop: 0px;
&.omitted {
position: relative;
@@ -216,11 +217,11 @@ onUnmounted(() => {
left: 0;
width: 100%;
height: 64px;
- background: linear-gradient(0deg, var(--panel), color(from var(--panel) srgb r g b / 0));
+ background: linear-gradient(0deg, var(--MI_THEME-panel), color(from var(--MI_THEME-panel) srgb r g b / 0));
> .fadeLabel {
display: inline-block;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
padding: 6px 10px;
font-size: 0.8em;
border-radius: var(--radius-ellipse);
@@ -229,7 +230,7 @@ onUnmounted(() => {
&:hover {
> .fadeLabel {
- background: var(--panelHighlight);
+ background: var(--MI_THEME-panelHighlight);
}
}
}
diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue
index 2e1e92cbdf..c2a1aaf29a 100644
--- a/packages/frontend/src/components/MkCropperDialog.vue
+++ b/packages/frontend/src/components/MkCropperDialog.vue
@@ -125,7 +125,7 @@ onMounted(() => {
const computedStyle = getComputedStyle(document.documentElement);
const selection = cropper.getCropperSelection()!;
- selection.themeColor = tinycolor(computedStyle.getPropertyValue('--accent')).toHexString();
+ selection.themeColor = tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString();
selection.aspectRatio = props.aspectRatio;
selection.initialAspectRatio = props.aspectRatio;
selection.outlined = true;
@@ -170,8 +170,8 @@ onMounted(() => {
display: flex;
align-items: center;
justify-content: center;
- -webkit-backdrop-filter: var(--blur, blur(10px));
- backdrop-filter: var(--blur, blur(10px));
+ -webkit-backdrop-filter: var(--MI-blur, blur(10px));
+ backdrop-filter: var(--MI-blur, blur(10px));
background: rgba(0, 0, 0, 0.5);
}
diff --git a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
index c7f1288729..949adc6a8e 100644
--- a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
+++ b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
@@ -85,8 +85,8 @@ function cancel() {
.emojiImgWrapper {
max-width: 100%;
height: 40cqh;
- background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, var(--X5) 8px, var(--X5) 14px);
- border-radius: var(--radius);
+ background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, var(--MI_THEME-X5) 8px, var(--MI_THEME-X5) 14px);
+ border-radius: var(--MI-radius);
margin: auto;
overflow-y: hidden;
}
@@ -101,8 +101,8 @@ function cancel() {
display: inline-block;
word-break: break-all;
padding: 3px 10px;
- background-color: var(--X5);
- border: solid 1px var(--divider);
- border-radius: var(--radius);
+ background-color: var(--MI_THEME-X5);
+ border: solid 1px var(--MI_THEME-divider);
+ border-radius: var(--MI-radius);
}
</style>
diff --git a/packages/frontend/src/components/MkDateSeparatedList.vue b/packages/frontend/src/components/MkDateSeparatedList.vue
index 98bf5191f7..8fda097df6 100644
--- a/packages/frontend/src/components/MkDateSeparatedList.vue
+++ b/packages/frontend/src/components/MkDateSeparatedList.vue
@@ -9,6 +9,7 @@ import MkAd from '@/components/global/MkAd.vue';
import { isDebuggerEnabled, stackTraceInstances } from '@/debug.js';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
+import { instance } from '@/instance.js';
import { defaultStore } from '@/store.js';
import { MisskeyEntity } from '@/types/date-separated-list.js';
@@ -99,11 +100,13 @@ export default defineComponent({
return [el, separator];
} else {
- if (props.ad && item._shouldInsertAd_) {
- return [h(MkAd, {
+ if (props.ad && instance.ads.length > 0 && item._shouldInsertAd_) {
+ return [h('div', {
key: item.id + ':ad',
+ class: $style['ad-wrapper'],
+ }, [h(MkAd, {
prefer: ['horizontal', 'horizontal-big'],
- }), el];
+ })]), el];
} else {
return el;
}
@@ -182,7 +185,7 @@ export default defineComponent({
}
&:not(.date-separated-list-nogap) > *:not(:last-child) {
- margin-bottom: var(--margin);
+ margin-bottom: var(--MI-margin);
}
}
@@ -196,7 +199,7 @@ export default defineComponent({
box-shadow: none;
&:not(:last-child) {
- border-bottom: solid 0.5px var(--divider);
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
}
}
}
@@ -237,7 +240,7 @@ export default defineComponent({
line-height: 32px;
text-align: center;
font-size: 12px;
- color: var(--dateLabelFg);
+ color: var(--MI_THEME-dateLabelFg);
}
.date-1 {
@@ -255,5 +258,11 @@ export default defineComponent({
.date-2-icon {
margin-left: 8px;
}
+
+.ad-wrapper {
+ 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);
+}
</style>
diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue
index 7dc381b662..5af2816acf 100644
--- a/packages/frontend/src/components/MkDialog.vue
+++ b/packages/frontend/src/components/MkDialog.vue
@@ -185,7 +185,7 @@ function onInputKeydown(evt: KeyboardEvent) {
max-width: 480px;
box-sizing: border-box;
text-align: center;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
border-radius: var(--radius-md);
}
@@ -207,15 +207,15 @@ function onInputKeydown(evt: KeyboardEvent) {
}
.type_success {
- color: var(--success);
+ color: var(--MI_THEME-success);
}
.type_error {
- color: var(--error);
+ color: var(--MI_THEME-error);
}
.type_warning {
- color: var(--warn);
+ color: var(--MI_THEME-warn);
}
.title {
diff --git a/packages/frontend/src/components/MkDivider.vue b/packages/frontend/src/components/MkDivider.vue
index e4e3af99e4..f72f091383 100644
--- a/packages/frontend/src/components/MkDivider.vue
+++ b/packages/frontend/src/components/MkDivider.vue
@@ -27,6 +27,6 @@ defineProps<{
<style scoped lang="scss">
.default {
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
</style>
diff --git a/packages/frontend/src/components/MkDonation.vue b/packages/frontend/src/components/MkDonation.vue
index 1dfdebf0d4..9f413fc078 100644
--- a/packages/frontend/src/components/MkDonation.vue
+++ b/packages/frontend/src/components/MkDonation.vue
@@ -75,12 +75,12 @@ function neverShow() {
.root {
position: fixed;
z-index: v-bind(zIndex);
- bottom: var(--margin);
+ bottom: var(--MI-margin);
left: 0;
right: 0;
margin: auto;
box-sizing: border-box;
- width: calc(100% - (var(--margin) * 2));
+ width: calc(100% - (var(--MI-margin) * 2));
max-width: 500px;
display: flex;
backdrop-filter: var(--blur, blur(15px));
@@ -90,7 +90,7 @@ function neverShow() {
text-align: center;
padding-top: 25px;
width: 100px;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
@media (max-width: 500px) {
.icon {
diff --git a/packages/frontend/src/components/MkDrive.file.vue b/packages/frontend/src/components/MkDrive.file.vue
index 20ad2984d8..f7249f19fb 100644
--- a/packages/frontend/src/components/MkDrive.file.vue
+++ b/packages/frontend/src/components/MkDrive.file.vue
@@ -152,14 +152,14 @@ function onDragend() {
}
&.isSelected {
- background: var(--accent);
+ background: var(--MI_THEME-accent);
&:hover {
- background: var(--accentLighten);
+ background: var(--MI_THEME-accentLighten);
}
&:active {
- background: var(--accentDarken);
+ background: var(--MI_THEME-accentDarken);
}
> .label {
@@ -248,7 +248,7 @@ function onDragend() {
font-size: 0.8em;
text-align: center;
word-break: break-all;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
overflow: hidden;
}
</style>
diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue
index 44788a6ffb..a0693c3827 100644
--- a/packages/frontend/src/components/MkDrive.folder.vue
+++ b/packages/frontend/src/components/MkDrive.folder.vue
@@ -36,13 +36,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, defineAsyncComponent, ref } from 'vue';
import * as Misskey from 'misskey-js';
+import type { MenuItem } from '@/types/menu.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
import { defaultStore } from '@/store.js';
import { claimAchievement } from '@/scripts/achievements.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
-import type { MenuItem } from '@/types/menu.js';
const props = withDefaults(defineProps<{
folder: Misskey.entities.DriveFolder;
@@ -313,7 +313,7 @@ function onContextmenu(ev: MouseEvent) {
position: relative;
padding: 8px;
height: 64px;
- background: var(--driveFolderBg);
+ background: var(--MI_THEME-driveFolderBg);
border-radius: var(--radius-xs);
cursor: pointer;
@@ -326,7 +326,7 @@ function onContextmenu(ev: MouseEvent) {
right: -4px;
bottom: -4px;
left: -4px;
- border: 2px dashed var(--focus);
+ border: 2px dashed var(--MI_THEME-focus);
border-radius: var(--radius-xs);
}
}
@@ -345,13 +345,13 @@ function onContextmenu(ev: MouseEvent) {
width: 18px;
height: 18px;
background: #fff;
- border: solid 2px var(--divider);
+ border: solid 2px var(--MI_THEME-divider);
border-radius: 4px;
box-sizing: border-box;
&.checked {
- border-color: var(--accent);
- background: var(--accent);
+ border-color: var(--MI_THEME-accent);
+ background: var(--MI_THEME-accent);
&::after {
content: "\ea5e";
@@ -368,14 +368,13 @@ function onContextmenu(ev: MouseEvent) {
}
&:hover {
- background: var(--accentedBg);
+ background: var(--MI_THEME-accentedBg);
}
}
.name {
margin: 0;
font-size: 0.9em;
- color: var(--desktopDriveFolderFg);
}
.icon {
@@ -388,6 +387,5 @@ function onContextmenu(ev: MouseEvent) {
margin: 4px 4px;
font-size: 0.8em;
text-align: right;
- color: var(--desktopDriveFolderFg);
}
</style>
diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue
index a471457b44..4b8e5f27c2 100644
--- a/packages/frontend/src/components/MkDrive.vue
+++ b/packages/frontend/src/components/MkDrive.vue
@@ -735,7 +735,7 @@ onBeforeUnmount(() => {
box-sizing: border-box;
overflow: auto;
font-size: 0.9em;
- box-shadow: 0 1px 0 var(--divider);
+ box-shadow: 0 1px 0 var(--MI_THEME-divider);
user-select: none;
}
@@ -787,7 +787,7 @@ onBeforeUnmount(() => {
.main {
flex: 1;
overflow: auto;
- padding: var(--margin);
+ padding: var(--MI-margin);
user-select: none;
&.fetching {
@@ -834,7 +834,7 @@ onBeforeUnmount(() => {
top: 38px;
width: 100%;
height: calc(100% - 38px);
- border: dashed 2px var(--focus);
+ border: dashed 2px var(--MI_THEME-focus);
pointer-events: none;
}
</style>
diff --git a/packages/frontend/src/components/MkDriveFileThumbnail.vue b/packages/frontend/src/components/MkDriveFileThumbnail.vue
index 543cf24022..623c4895eb 100644
--- a/packages/frontend/src/components/MkDriveFileThumbnail.vue
+++ b/packages/frontend/src/components/MkDriveFileThumbnail.vue
@@ -69,7 +69,7 @@ const isThumbnailAvailable = computed(() => {
.root {
position: relative;
display: flex;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
border-radius: var(--radius-sm);
overflow: clip;
}
@@ -83,7 +83,7 @@ const isThumbnailAvailable = computed(() => {
height: 100%;
pointer-events: none;
border-radius: inherit;
- box-shadow: inset 0 0 0 4px var(--warn);
+ box-shadow: inset 0 0 0 4px var(--MI_THEME-warn);
}
.iconSub {
diff --git a/packages/frontend/src/components/MkEmbedCodeGenDialog.vue b/packages/frontend/src/components/MkEmbedCodeGenDialog.vue
index c060c3a659..c2bb516c7c 100644
--- a/packages/frontend/src/components/MkEmbedCodeGenDialog.vue
+++ b/packages/frontend/src/components/MkEmbedCodeGenDialog.vue
@@ -306,9 +306,9 @@ onUnmounted(() => {
.embedCodeGenPreviewRoot {
position: relative;
- background-color: var(--bg);
+ background-color: var(--MI_THEME-bg);
background-size: auto auto;
- background-image: repeating-linear-gradient(135deg, transparent, transparent 6px, var(--panel) 6px, var(--panel) 12px);
+ background-image: repeating-linear-gradient(135deg, transparent, transparent 6px, var(--MI_THEME-panel) 6px, var(--MI_THEME-panel) 12px);
cursor: not-allowed;
}
@@ -381,8 +381,8 @@ onUnmounted(() => {
.embedCodeGenResultHeadingIcon {
margin: 0 auto;
- background-color: var(--accentedBg);
- color: var(--accent);
+ background-color: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
text-align: center;
height: 64px;
width: 64px;
diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue
index 151843b18c..e2762eb3cb 100644
--- a/packages/frontend/src/components/MkEmojiPicker.section.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.section.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと -->
<!-- フォルダの中にはカスタム絵文字だけ(Unicode絵文字もこっち) -->
-<section v-if="!hasChildSection" v-panel style="border-radius: var(--radius-sm); border-bottom: 0.5px solid var(--divider);">
+<section v-if="!hasChildSection" v-panel style="border-radius: var(--radius-sm); border-bottom: 0.5px solid var(--MI_THEME-divider);">
<header class="_acrylic" @click="shown = !shown">
<i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ph-smiley-sticker ph-bold ph-lg"></i>:{{ emojis.length }})
</header>
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</section>
<!-- フォルダの中にはカスタム絵文字やフォルダがある -->
-<section v-else v-panel style="border-radius: var(--radius-sm); border-bottom: 0.5px solid var(--divider);">
+<section v-else v-panel style="border-radius: var(--radius-sm); border-bottom: 0.5px solid var(--MI_THEME-divider);">
<header class="_acrylic" @click="shown = !shown">
<i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ti ti-folder ti-fw"></i>:{{ customEmojiTree?.length }} <i class="ph-smiley-sticker ph-bold ph-lg ti-fw"></i>:{{ emojis.length }})
</header>
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 949ed4db91..667bb832a2 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -580,7 +580,7 @@ defineExpose({
&:disabled {
cursor: not-allowed;
- background: linear-gradient(-45deg, transparent 0% 48%, var(--X6) 48% 52%, transparent 52% 100%);
+ background: linear-gradient(-45deg, transparent 0% 48%, var(--MI_THEME-X6) 48% 52%, transparent 52% 100%);
opacity: 1;
> .emoji {
@@ -615,7 +615,7 @@ defineExpose({
&:disabled {
cursor: not-allowed;
- background: linear-gradient(-45deg, transparent 0% 48%, var(--X6) 48% 52%, transparent 52% 100%);
+ background: linear-gradient(-45deg, transparent 0% 48%, var(--MI_THEME-X6) 48% 52%, transparent 52% 100%);
opacity: 1;
> .emoji {
@@ -638,7 +638,7 @@ defineExpose({
outline: none;
border: none;
background: transparent;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
&:not(:focus):not(.filled) {
margin-bottom: env(safe-area-inset-bottom, 0px);
@@ -647,7 +647,7 @@ defineExpose({
&:not(.filled) {
order: 1;
z-index: 2;
- box-shadow: 0px -1px 0 0px var(--divider);
+ box-shadow: 0px -1px 0 0px var(--MI_THEME-divider);
}
}
@@ -658,11 +658,11 @@ defineExpose({
> .tab {
flex: 1;
height: 38px;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
&.active {
- border-top: solid 1px var(--accent);
- color: var(--accent);
+ border-top: solid 1px var(--MI_THEME-accent);
+ color: var(--MI_THEME-accent);
}
}
}
@@ -681,7 +681,7 @@ defineExpose({
> .group {
&:not(.index) {
padding: 4px 0 8px 0;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
> header {
@@ -708,7 +708,7 @@ defineExpose({
cursor: pointer;
&:hover {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
}
@@ -730,13 +730,13 @@ defineExpose({
}
&:active {
- background: var(--accent);
+ background: var(--MI_THEME-accent);
box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15);
}
&:disabled {
cursor: not-allowed;
- background: linear-gradient(-45deg, transparent 0% 48%, var(--X6) 48% 52%, transparent 52% 100%);
+ background: linear-gradient(-45deg, transparent 0% 48%, var(--MI_THEME-X6) 48% 52%, transparent 52% 100%);
opacity: 1;
> .emoji {
@@ -757,7 +757,7 @@ defineExpose({
}
&.result {
- border-bottom: solid 0.5px var(--divider);
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
&:empty {
display: none;
diff --git a/packages/frontend/src/components/MkExtensionInstaller.stories.impl.ts b/packages/frontend/src/components/MkExtensionInstaller.stories.impl.ts
new file mode 100644
index 0000000000..6763f7c546
--- /dev/null
+++ b/packages/frontend/src/components/MkExtensionInstaller.stories.impl.ts
@@ -0,0 +1,83 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { StoryObj } from '@storybook/vue3';
+import MkExtensionInstaller from './MkExtensionInstaller.vue';
+import lightTheme from '@@/themes/_light.json5';
+
+export const Plugin = {
+ render(args) {
+ return {
+ components: {
+ MkExtensionInstaller,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkExtensionInstaller v-bind="props" />',
+ };
+ },
+ args: {
+ extension: {
+ type: 'plugin',
+ raw: '"do nothing"',
+ meta: {
+ name: 'do nothing plugin',
+ version: '1.0',
+ author: 'syuilo and misskey-project',
+ description: 'a plugin that does nothing',
+ permissions: ['read:account'],
+ config: {
+ 'doNothing': true,
+ },
+ },
+ },
+ },
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkExtensionInstaller>;
+
+export const Theme = {
+ render(args) {
+ return {
+ components: {
+ MkExtensionInstaller,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkExtensionInstaller v-bind="props" />',
+ };
+ },
+ args: {
+ extension: {
+ type: 'theme',
+ raw: JSON.stringify(lightTheme),
+ meta: lightTheme,
+ },
+ },
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkExtensionInstaller>;
diff --git a/packages/frontend/src/components/MkExtensionInstaller.vue b/packages/frontend/src/components/MkExtensionInstaller.vue
new file mode 100644
index 0000000000..b41604b2c3
--- /dev/null
+++ b/packages/frontend/src/components/MkExtensionInstaller.vue
@@ -0,0 +1,146 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div class="_gaps_m" :class="$style.extInstallerRoot">
+ <div :class="$style.extInstallerIconWrapper">
+ <i v-if="isPlugin" class="ti ti-plug"></i>
+ <i v-else-if="isTheme" class="ti ti-palette"></i>
+ <!-- 拡張用? -->
+ <i v-else class="ti ti-download"></i>
+ </div>
+ <h2 :class="$style.extInstallerTitle">{{ i18n.ts._externalResourceInstaller[`_${extension.type}`].title }}</h2>
+ <div :class="$style.extInstallerNormDesc">{{ i18n.ts._externalResourceInstaller.checkVendorBeforeInstall }}</div>
+ <MkInfo v-if="isPlugin" :warn="true">{{ i18n.ts._plugin.installWarn }}</MkInfo>
+ <FormSection>
+ <template #label>{{ i18n.ts._externalResourceInstaller[`_${extension.type}`].metaTitle }}</template>
+ <div class="_gaps_s">
+ <FormSplit>
+ <MkKeyValue>
+ <template #key>{{ i18n.ts.name }}</template>
+ <template #value>{{ extension.meta.name }}</template>
+ </MkKeyValue>
+ <MkKeyValue>
+ <template #key>{{ i18n.ts.author }}</template>
+ <template #value>{{ extension.meta.author }}</template>
+ </MkKeyValue>
+ </FormSplit>
+ <MkKeyValue v-if="isPlugin">
+ <template #key>{{ i18n.ts.description }}</template>
+ <template #value>{{ extension.meta.description ?? i18n.ts.none }}</template>
+ </MkKeyValue>
+ <MkKeyValue v-if="isPlugin">
+ <template #key>{{ i18n.ts.version }}</template>
+ <template #value>{{ extension.meta.version }}</template>
+ </MkKeyValue>
+ <MkKeyValue v-if="isPlugin">
+ <template #key>{{ i18n.ts.permission }}</template>
+ <template #value>
+ <ul v-if="extension.meta.permissions && extension.meta.permissions.length > 0" :class="$style.extInstallerKVList">
+ <li v-for="permission in extension.meta.permissions" :key="permission">{{ i18n.ts._permissions[permission] }}</li>
+ </ul>
+ <template v-else>{{ i18n.ts.none }}</template>
+ </template>
+ </MkKeyValue>
+ <MkKeyValue v-if="isTheme">
+ <template #key>{{ i18n.ts._externalResourceInstaller._meta.base }}</template>
+ <template #value>{{ i18n.ts[extension.meta.base ?? 'none'] }}</template>
+ </MkKeyValue>
+ <MkFolder>
+ <template #icon><i class="ti ti-code"></i></template>
+ <template #label>{{ i18n.ts._plugin.viewSource }}</template>
+
+ <MkCode :code="extension.raw"/>
+ </MkFolder>
+ </div>
+ </FormSection>
+ <slot name="additionalInfo"/>
+ <div class="_buttonsCenter">
+ <MkButton primary @click="emits('confirm')"><i class="ti ti-check"></i> {{ i18n.ts.install }}</MkButton>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+export type Extension = {
+ type: 'plugin';
+ raw: string;
+ meta: {
+ name: string;
+ version: string;
+ author: string;
+ description?: string;
+ permissions?: string[];
+ config?: Record<string, any>;
+ };
+} | {
+ type: 'theme';
+ raw: string;
+ meta: {
+ name: string;
+ author: string;
+ base?: 'light' | 'dark';
+ };
+};
+</script>
+<script lang="ts" setup>
+import { computed } from 'vue';
+import MkButton from '@/components/MkButton.vue';
+import FormSection from '@/components/form/section.vue';
+import FormSplit from '@/components/form/split.vue';
+import MkCode from '@/components/MkCode.vue';
+import MkInfo from '@/components/MkInfo.vue';
+import MkFolder from '@/components/MkFolder.vue';
+import MkKeyValue from '@/components/MkKeyValue.vue';
+import { i18n } from '@/i18n.js';
+
+const isPlugin = computed(() => props.extension.type === 'plugin');
+const isTheme = computed(() => props.extension.type === 'theme');
+
+const props = defineProps<{
+ extension: Extension;
+}>();
+
+const emits = defineEmits<{
+ (ev: 'confirm'): void;
+}>();
+</script>
+
+<style lang="scss" module>
+.extInstallerRoot {
+ border-radius: var(--MI-radius);
+ background: var(--MI_THEME-panel);
+ padding: 1.5rem;
+}
+
+.extInstallerIconWrapper {
+ width: 48px;
+ height: 48px;
+ font-size: 24px;
+ line-height: 48px;
+ text-align: center;
+ border-radius: 50%;
+ margin-left: auto;
+ margin-right: auto;
+
+ background-color: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
+}
+
+.extInstallerTitle {
+ font-size: 1.2rem;
+ text-align: center;
+ margin: 0;
+}
+
+.extInstallerNormDesc {
+ text-align: center;
+}
+
+.extInstallerKVList {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+</style>
diff --git a/packages/frontend/src/components/MkFileListForAdmin.vue b/packages/frontend/src/components/MkFileListForAdmin.vue
index 7af68a32ba..5bc85a3a83 100644
--- a/packages/frontend/src/components/MkFileListForAdmin.vue
+++ b/packages/frontend/src/components/MkFileListForAdmin.vue
@@ -66,7 +66,7 @@ const props = defineProps<{
align-items: center;
&:hover {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
> .thumbnail {
diff --git a/packages/frontend/src/components/MkFlashPreview.vue b/packages/frontend/src/components/MkFlashPreview.vue
index 8a2a438624..b7278ac742 100644
--- a/packages/frontend/src/components/MkFlashPreview.vue
+++ b/packages/frontend/src/components/MkFlashPreview.vue
@@ -36,7 +36,7 @@ const props = defineProps<{
&:hover {
text-decoration: none;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
&:focus-visible {
@@ -83,7 +83,6 @@ const props = defineProps<{
> p {
display: inline-block;
margin: 0;
- color: var(--urlPreviewInfo);
font-size: 0.8em;
line-height: 16px;
vertical-align: top;
@@ -92,7 +91,7 @@ const props = defineProps<{
}
&:global(.gray) {
- --c: var(--bg);
+ --c: var(--MI_THEME-bg);
background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%);
background-size: 16px 16px;
}
diff --git a/packages/frontend/src/components/MkFoldableSection.vue b/packages/frontend/src/components/MkFoldableSection.vue
index f10d58b38a..1717f8fc98 100644
--- a/packages/frontend/src/components/MkFoldableSection.vue
+++ b/packages/frontend/src/components/MkFoldableSection.vue
@@ -83,7 +83,7 @@ function afterLeave(element: Element) {
onMounted(() => {
function getParentBg(el?: HTMLElement | null): string {
- if (el == null || el.tagName === 'BODY') return 'var(--bg)';
+ if (el == null || el.tagName === 'BODY') return 'var(--MI_THEME-bg)';
const background = el.style.background || el.style.backgroundColor;
if (background) {
return background;
@@ -118,9 +118,9 @@ onMounted(() => {
position: relative;
z-index: 10;
position: sticky;
- top: var(--stickyTop, 0px);
- -webkit-backdrop-filter: var(--blur, blur(8px));
- backdrop-filter: var(--blur, blur(20px));
+ top: var(--MI-stickyTop, 0px);
+ -webkit-backdrop-filter: var(--MI-blur, blur(8px));
+ backdrop-filter: var(--MI-blur, blur(20px));
}
.title {
@@ -134,7 +134,7 @@ onMounted(() => {
flex: 1;
margin: auto;
height: 1px;
- background: var(--divider);
+ background: var(--MI_THEME-divider);
}
.button {
diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue
index 392963fdb9..3715654b03 100644
--- a/packages/frontend/src/components/MkFolder.vue
+++ b/packages/frontend/src/components/MkFolder.vue
@@ -38,9 +38,12 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<KeepAlive>
<div v-show="opened">
- <MkSpacer :marginMin="14" :marginMax="22">
+ <MkSpacer v-if="withSpacer" :marginMin="14" :marginMax="22">
<slot></slot>
</MkSpacer>
+ <div v-else>
+ <slot></slot>
+ </div>
<div v-if="$slots.footer" :class="$style.footer">
<slot name="footer"></slot>
</div>
@@ -59,9 +62,11 @@ import { defaultStore } from '@/store.js';
const props = withDefaults(defineProps<{
defaultOpen?: boolean;
maxHeight?: number | null;
+ withSpacer?: boolean;
}>(), {
defaultOpen: false,
maxHeight: null,
+ withSpacer: true,
});
const getBgColor = (el: HTMLElement) => {
@@ -113,7 +118,7 @@ function toggle() {
onMounted(() => {
const computedStyle = getComputedStyle(document.documentElement);
const parentBg = getBgColor(rootEl.value!.parentElement!);
- const myBg = computedStyle.getPropertyValue('--panel');
+ const myBg = computedStyle.getPropertyValue('--MI_THEME-panel');
bgSame.value = parentBg === myBg;
});
</script>
@@ -139,15 +144,15 @@ onMounted(() => {
width: 100%;
box-sizing: border-box;
padding: 9px 12px 9px 12px;
- background: var(--folderHeaderBg);
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
+ background: var(--MI_THEME-folderHeaderBg);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
border-radius: var(--radius-sm);
transition: border-radius 0.3s;
&:hover {
text-decoration: none;
- background: var(--folderHeaderHoverBg);
+ background: var(--MI_THEME-folderHeaderHoverBg);
}
&:focus-within {
@@ -155,8 +160,8 @@ onMounted(() => {
}
&.active {
- color: var(--accent);
- background: var(--folderHeaderHoverBg);
+ color: var(--MI_THEME-accent);
+ background: var(--MI_THEME-folderHeaderHoverBg);
}
&.opened {
@@ -170,7 +175,7 @@ onMounted(() => {
}
.headerLower {
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
font-size: .85em;
padding-left: 4px;
}
@@ -204,13 +209,13 @@ onMounted(() => {
}
.headerTextSub {
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
font-size: .85em;
}
.headerRight {
margin-left: auto;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
white-space: nowrap;
}
@@ -219,26 +224,26 @@ onMounted(() => {
}
.body {
- background: var(--panel);
+ background: var(--MI_THEME-panel);
border-radius: 0 0 var(--radius-sm) var(--radius-sm);
container-type: inline-size;
&.bgSame {
- background: var(--bg);
+ background: var(--MI_THEME-bg);
}
}
.footer {
position: sticky !important;
z-index: 1;
- bottom: var(--stickyBottom, 0px);
+ bottom: var(--MI-stickyBottom, 0px);
left: 0;
padding: 12px;
- background: var(--acrylicBg);
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
+ background: var(--MI_THEME-acrylicBg);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
background-size: auto auto;
- background-image: repeating-linear-gradient(135deg, transparent, transparent 5px, var(--panel) 5px, var(--panel) 10px);
+ background-image: repeating-linear-gradient(135deg, transparent, transparent 5px, var(--MI_THEME-panel) 5px, var(--MI_THEME-panel) 10px);
border-radius: 0 0 6px 6px;
}
</style>
diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue
index 52497a2994..3733583192 100644
--- a/packages/frontend/src/components/MkFollowButton.vue
+++ b/packages/frontend/src/components/MkFollowButton.vue
@@ -165,8 +165,8 @@ onBeforeUnmount(() => {
position: relative;
display: inline-block;
font-weight: bold;
- color: var(--fgOnWhite);
- border: solid 1px var(--accent);
+ color: var(--MI_THEME-fgOnWhite);
+ border: solid 1px var(--MI_THEME-accent);
padding: 0;
height: 31px;
font-size: 16px;
@@ -201,17 +201,17 @@ onBeforeUnmount(() => {
}
&.active {
- color: var(--fgOnAccent);
- background: var(--accent);
+ color: var(--MI_THEME-fgOnAccent);
+ background: var(--MI_THEME-accent);
&:hover {
- background: var(--accentLighten);
- border-color: var(--accentLighten);
+ background: var(--MI_THEME-accentLighten);
+ border-color: var(--MI_THEME-accentLighten);
}
&:active {
- background: var(--accentDarken);
- border-color: var(--accentDarken);
+ background: var(--MI_THEME-accentDarken);
+ border-color: var(--MI_THEME-accentDarken);
}
}
diff --git a/packages/frontend/src/components/MkFormDialog.file.vue b/packages/frontend/src/components/MkFormDialog.file.vue
index 9360594236..ecb6cf882b 100644
--- a/packages/frontend/src/components/MkFormDialog.file.vue
+++ b/packages/frontend/src/components/MkFormDialog.file.vue
@@ -66,6 +66,6 @@ function selectButton(ev: MouseEvent) {
<style module>
.fileNotSelected {
font-weight: 700;
- color: var(--infoWarnFg);
+ color: var(--MI_THEME-infoWarnFg);
}
</style>
diff --git a/packages/frontend/src/components/MkFormFooter.vue b/packages/frontend/src/components/MkFormFooter.vue
index 1e88d59d8e..f409f6ce50 100644
--- a/packages/frontend/src/components/MkFormFooter.vue
+++ b/packages/frontend/src/components/MkFormFooter.vue
@@ -36,7 +36,7 @@ const props = defineProps<{
}
.text {
- color: var(--warn);
+ color: var(--MI_THEME-warn);
font-size: 90%;
animation: modified-blink 2s infinite;
}
diff --git a/packages/frontend/src/components/MkFukidashi.vue b/packages/frontend/src/components/MkFukidashi.vue
new file mode 100644
index 0000000000..8b1c56fca4
--- /dev/null
+++ b/packages/frontend/src/components/MkFukidashi.vue
@@ -0,0 +1,100 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div
+ :class="[
+ $style.root,
+ tail === 'left' ? $style.left : $style.right,
+ negativeMargin === true && $style.negativeMargin,
+ shadow === true && $style.shadow,
+ ]"
+>
+ <div :class="$style.bg">
+ <svg v-if="tail !== 'none'" :class="$style.tail" version="1.1" viewBox="0 0 14.597 14.58" xmlns="http://www.w3.org/2000/svg">
+ <g transform="translate(-173.71 -87.184)">
+ <path d="m188.19 87.657c-1.469 2.3218-3.9315 3.8312-6.667 4.0865-2.2309-1.7379-4.9781-2.6816-7.8061-2.6815h-5.1e-4v12.702h12.702v-5.1e-4c2e-5 -1.9998-0.47213-3.9713-1.378-5.754 2.0709-1.6834 3.2732-4.2102 3.273-6.8791-6e-5 -0.49375-0.0413-0.98662-0.1235-1.4735z" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke-width=".33225" style="paint-order:stroke fill markers"/>
+ </g>
+ </svg>
+ <div :class="$style.content">
+ <slot></slot>
+ </div>
+ </div>
+</div>
+</template>
+
+<script setup lang="ts">
+withDefaults(defineProps<{
+ tail?: 'left' | 'right' | 'none';
+ negativeMargin?: boolean;
+ shadow?: boolean;
+}>(), {
+ tail: 'right',
+ negativeMargin: false,
+ shadow: false,
+});
+</script>
+
+<style module lang="scss">
+.root {
+ --fukidashi-radius: var(--MI-radius);
+ --fukidashi-bg: var(--MI_THEME-panel);
+
+ position: relative;
+ display: inline-block;
+ min-height: calc(var(--fukidashi-radius) * 2);
+ padding-top: calc(var(--fukidashi-radius) * .13);
+
+ &.shadow {
+ filter: drop-shadow(0 4px 32px var(--MI_THEME-shadow));
+ }
+
+ &.left {
+ padding-left: calc(var(--fukidashi-radius) * .13);
+
+ &.negativeMargin {
+ margin-left: calc(calc(var(--fukidashi-radius) * .13) * -1);
+ }
+ }
+
+ &.right {
+ padding-right: calc(var(--fukidashi-radius) * .13);
+
+ &.negativeMargin {
+ margin-right: calc(calc(var(--fukidashi-radius) * .13) * -1);
+ }
+ }
+}
+
+.bg {
+ width: 100%;
+ height: 100%;
+ background: var(--fukidashi-bg);
+ border-radius: var(--fukidashi-radius);
+}
+
+.content {
+ position: relative;
+ padding: 8px 12px;
+}
+
+.tail {
+ position: absolute;
+ top: 0;
+ display: block;
+ width: calc(var(--fukidashi-radius) * 1.13);
+ height: auto;
+ fill: var(--fukidashi-bg);
+}
+
+.left .tail {
+ left: 0;
+ transform: rotateY(180deg);
+}
+
+.right .tail {
+ right: 0;
+}
+</style>
diff --git a/packages/frontend/src/components/MkGalleryPostPreview.vue b/packages/frontend/src/components/MkGalleryPostPreview.vue
index 2bb5b8762a..22f8355acf 100644
--- a/packages/frontend/src/components/MkGalleryPostPreview.vue
+++ b/packages/frontend/src/components/MkGalleryPostPreview.vue
@@ -75,7 +75,7 @@ function leaveHover(): void {
&:hover {
text-decoration: none;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
> .thumbnail {
transform: scale(1.1);
diff --git a/packages/frontend/src/components/MkGoogle.vue b/packages/frontend/src/components/MkGoogle.vue
index 54c7585f18..ad26030846 100644
--- a/packages/frontend/src/components/MkGoogle.vue
+++ b/packages/frontend/src/components/MkGoogle.vue
@@ -41,7 +41,7 @@ const search = () => {
width: 100%;
height: 40px;
font-size: 16px;
- border: solid 1px var(--divider);
+ border: solid 1px var(--MI_THEME-divider);
border-radius: var(--radius-xs) 0 0 var(--radius-xs);
-webkit-appearance: textfield;
}
@@ -50,7 +50,7 @@ const search = () => {
flex-shrink: 0;
margin: 0;
padding: 0 16px;
- border: solid 1px var(--divider);
+ border: solid 1px var(--MI_THEME-divider);
border-left: none;
border-radius: 0 var(--radius-xs) var(--radius-xs) 0;
diff --git a/packages/frontend/src/components/MkInfo.vue b/packages/frontend/src/components/MkInfo.vue
index 46c2db4b1f..bdc38f5142 100644
--- a/packages/frontend/src/components/MkInfo.vue
+++ b/packages/frontend/src/components/MkInfo.vue
@@ -36,15 +36,15 @@ function close() {
align-items: center;
padding: 12px 14px;
font-size: 90%;
- background: color-mix(in srgb, var(--infoBg) 65%, transparent);
- color: var(--infoFg);
- border-radius: var(--radius);
+ 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 {
- background: color-mix(in srgb, var(--infoWarnBg) 65%, transparent);
- color: var(--infoWarnFg);
+ background: color-mix(in srgb, var(--MI_THEME-infoWarnBg) 65%, transparent);
+ color: var(--MI_THEME-infoWarnFg);
}
}
diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue
index 42e1146e27..edaa605590 100644
--- a/packages/frontend/src/components/MkInput.vue
+++ b/packages/frontend/src/components/MkInput.vue
@@ -199,7 +199,7 @@ defineExpose({
.caption {
font-size: 0.85em;
padding: 8px 0 0 0;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
&:empty {
display: none;
@@ -216,8 +216,8 @@ defineExpose({
&.focused {
> .inputCore {
- border-color: var(--accent) !important;
- //box-shadow: 0 0 0 4px var(--focus);
+ border-color: var(--MI_THEME-accent) !important;
+ //box-shadow: 0 0 0 4px var(--MI_THEME-focus);
}
}
@@ -242,9 +242,9 @@ defineExpose({
font: inherit;
font-weight: normal;
font-size: 1em;
- color: var(--fg);
- background: var(--panel);
- border: solid 1px var(--panel);
+ color: var(--MI_THEME-fg);
+ background: var(--MI_THEME-panel);
+ border: solid 1px var(--MI_THEME-panel);
border-radius: var(--radius-sm);
outline: none;
box-shadow: none;
@@ -252,7 +252,7 @@ defineExpose({
transition: border-color 0.1s ease-out;
&:hover {
- border-color: var(--inputBorderHover) !important;
+ border-color: var(--MI_THEME-inputBorderHover) !important;
}
}
diff --git a/packages/frontend/src/components/MkInstanceCardMini.vue b/packages/frontend/src/components/MkInstanceCardMini.vue
index 10b390e7f9..4ba49be941 100644
--- a/packages/frontend/src/components/MkInstanceCardMini.vue
+++ b/packages/frontend/src/components/MkInstanceCardMini.vue
@@ -46,7 +46,7 @@ function getInstanceIcon(instance): string {
display: flex;
align-items: center;
padding: 16px;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
border-radius: var(--radius-sm);
> :global(.icon) {
@@ -62,7 +62,7 @@ function getInstanceIcon(instance): string {
flex: 1;
overflow: hidden;
font-size: 0.9em;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
padding-right: 8px;
> :global(.host) {
@@ -109,7 +109,7 @@ function getInstanceIcon(instance): string {
}
&:global(.gray) {
- --c: var(--bg);
+ --c: var(--MI_THEME-bg);
background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%);
background-size: 16px 16px;
}
diff --git a/packages/frontend/src/components/MkInstanceStats.vue b/packages/frontend/src/components/MkInstanceStats.vue
index d74c885041..8ccbf61e48 100644
--- a/packages/frontend/src/components/MkInstanceStats.vue
+++ b/packages/frontend/src/components/MkInstanceStats.vue
@@ -121,7 +121,7 @@ function createDoughnut(chartEl, tooltip, data) {
labels: data.map(x => x.name),
datasets: [{
backgroundColor: data.map(x => x.color),
- borderColor: getComputedStyle(document.documentElement).getPropertyValue('--panel'),
+ borderColor: getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel'),
borderWidth: 2,
hoverOffset: 0,
data: data.map(x => x.value),
@@ -256,8 +256,8 @@ onMounted(() => {
flex: 1;
min-width: 0;
position: relative;
- background: var(--panel);
- border-radius: var(--radius);
+ background: var(--MI_THEME-panel);
+ border-radius: var(--MI-radius);
padding: 24px;
max-height: 300px;
diff --git a/packages/frontend/src/components/MkInviteCode.vue b/packages/frontend/src/components/MkInviteCode.vue
index 4aee64f78e..1a71f6574f 100644
--- a/packages/frontend/src/components/MkInviteCode.vue
+++ b/packages/frontend/src/components/MkInviteCode.vue
@@ -8,8 +8,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ invite.code }}</template>
<template #suffix>
<span v-if="invite.used">{{ i18n.ts.used }}</span>
- <span v-else-if="isExpired" style="color: var(--error)">{{ i18n.ts.expired }}</span>
- <span v-else style="color: var(--success)">{{ i18n.ts.unused }}</span>
+ <span v-else-if="isExpired" style="color: var(--MI_THEME-error)">{{ i18n.ts.expired }}</span>
+ <span v-else style="color: var(--MI_THEME-success)">{{ i18n.ts.unused }}</span>
</template>
<template #footer>
<div class="_buttons">
diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue
index e0880ec3e7..0382dbe926 100644
--- a/packages/frontend/src/components/MkLaunchPad.vue
+++ b/packages/frontend/src/components/MkLaunchPad.vue
@@ -12,13 +12,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<i class="icon" :class="item.icon"></i>
<div class="text">{{ item.text }}</div>
<span v-if="item.indicate && item.indicateValue" class="_indicateCounter indicatorWithValue">{{ item.indicateValue }}</span>
- <span v-else-if="item.indicate" class="indicator"><i class="_indicatorCircle"></i></span>
+ <span v-else-if="item.indicate" class="indicator _blink"><i class="_indicatorCircle"></i></span>
</button>
<MkA v-else v-click-anime :to="item.to" class="item" @click.passive="close()">
<i class="icon" :class="item.icon"></i>
<div class="text">{{ item.text }}</div>
<span v-if="item.indicate && item.indicateValue" class="_indicateCounter indicatorWithValue">{{ item.indicateValue }}</span>
- <span v-else-if="item.indicate" class="indicator"><i class="_indicatorCircle"></i></span>
+ <span v-else-if="item.indicate" class="indicator _blink"><i class="_indicatorCircle"></i></span>
</MkA>
</template>
</div>
@@ -105,8 +105,8 @@ function close() {
box-sizing: border-box;
&:hover {
- color: var(--accent);
- background: var(--accentedBg);
+ color: var(--MI_THEME-accent);
+ background: var(--MI_THEME-accentedBg);
text-decoration: none;
}
@@ -137,9 +137,8 @@ function close() {
position: absolute;
top: 32px;
left: 32px;
- color: var(--indicator);
+ color: var(--MI_THEME-indicator);
font-size: 8px;
- animation: global-blink 1s infinite;
@media (max-width: 500px) {
top: 16px;
diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue
index 2d7cde1af2..10450fb621 100644
--- a/packages/frontend/src/components/MkMediaAudio.vue
+++ b/packages/frontend/src/components/MkMediaAudio.vue
@@ -394,8 +394,8 @@ onDeactivated(() => {
.audioContainer {
container-type: inline-size;
position: relative;
- border: .5px solid var(--divider);
- border-radius: var(--radius);
+ border: .5px solid var(--MI_THEME-divider);
+ border-radius: var(--MI-radius);
overflow: clip;
&:focus-visible {
@@ -415,7 +415,7 @@ onDeactivated(() => {
height: 100%;
pointer-events: none;
border-radius: inherit;
- box-shadow: inset 0 0 0 4px var(--warn);
+ box-shadow: inset 0 0 0 4px var(--MI_THEME-warn);
}
}
@@ -457,12 +457,12 @@ onDeactivated(() => {
.controlButton {
padding: 6px;
- border-radius: calc(var(--radius) / 2);
+ border-radius: calc(var(--MI-radius) / 2);
font-size: 1.05rem;
&:hover {
- color: var(--accent);
- background-color: var(--accentedBg);
+ color: var(--MI_THEME-accent);
+ background-color: var(--MI_THEME-accentedBg);
}
&:focus-visible {
diff --git a/packages/frontend/src/components/MkMediaBanner.vue b/packages/frontend/src/components/MkMediaBanner.vue
index 77a86ff2fb..e1714fb54d 100644
--- a/packages/frontend/src/components/MkMediaBanner.vue
+++ b/packages/frontend/src/components/MkMediaBanner.vue
@@ -68,7 +68,6 @@ async function show() {
}
.download {
- background: var(--noteAttachedFile);
}
.sensitive {
diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue
index 02c054956c..2aedaa4cd6 100644
--- a/packages/frontend/src/components/MkMediaImage.vue
+++ b/packages/frontend/src/components/MkMediaImage.vue
@@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.indicators">
<div v-if="['image/gif', 'image/apng'].includes(image.type)" :class="$style.indicator">GIF</div>
<div v-if="image.comment" :class="$style.indicator">ALT</div>
- <div v-if="image.isSensitive" :class="$style.indicator" style="color: var(--warn);" :title="i18n.ts.sensitive"><i class="ti ti-eye-exclamation"></i></div>
+ <div v-if="image.isSensitive" :class="$style.indicator" style="color: var(--MI_THEME-warn);" :title="i18n.ts.sensitive"><i class="ti ti-eye-exclamation"></i></div>
<div v-if="!image.comment" :class="$style.indicator" title="Image lacks descriptive text"><i class="ph-pencil-simple ph-bold ph-lg-off"></i></div>
</div>
<button :class="$style.menu" class="_button" @click.stop="showMenu"><i class="ti ti-dots" style="vertical-align: middle;"></i></button>
@@ -166,7 +166,7 @@ function showMenu(ev: MouseEvent) {
height: 100%;
pointer-events: none;
border-radius: inherit;
- box-shadow: inset 0 0 0 4px var(--warn);
+ box-shadow: inset 0 0 0 4px var(--MI_THEME-warn);
}
}
@@ -188,7 +188,7 @@ function showMenu(ev: MouseEvent) {
position: absolute;
border-radius: var(--radius-sm);
background-color: black;
- color: var(--accentLighten);
+ color: var(--MI_THEME-accentLighten);
font-size: 12px;
opacity: .5;
padding: 5px 8px;
@@ -207,19 +207,19 @@ function showMenu(ev: MouseEvent) {
.visible {
position: relative;
- //box-shadow: 0 0 0 1px var(--divider) inset;
- background: var(--bg);
+ //box-shadow: 0 0 0 1px var(--MI_THEME-divider) inset;
+ background: var(--MI_THEME-bg);
background-size: 16px 16px;
}
html[data-color-scheme=dark] .visible {
--c: rgb(255 255 255 / 2%);
- background-image: linear-gradient(45deg, var(--c) 16.67%, var(--bg) 16.67%, var(--bg) 50%, var(--c) 50%, var(--c) 66.67%, var(--bg) 66.67%, var(--bg) 100%);
+ background-image: linear-gradient(45deg, var(--c) 16.67%, var(--MI_THEME-bg) 16.67%, var(--MI_THEME-bg) 50%, var(--c) 50%, var(--c) 66.67%, var(--MI_THEME-bg) 66.67%, var(--MI_THEME-bg) 100%);
}
html[data-color-scheme=light] .visible {
--c: rgb(0 0 0 / 2%);
- background-image: linear-gradient(45deg, var(--c) 16.67%, var(--bg) 16.67%, var(--bg) 50%, var(--c) 50%, var(--c) 66.67%, var(--bg) 66.67%, var(--bg) 100%);
+ background-image: linear-gradient(45deg, var(--c) 16.67%, var(--MI_THEME-bg) 16.67%, var(--MI_THEME-bg) 50%, var(--c) 50%, var(--c) 66.67%, var(--MI_THEME-bg) 66.67%, var(--MI_THEME-bg) 100%);
}
.menu {
@@ -227,8 +227,8 @@ html[data-color-scheme=light] .visible {
position: absolute;
border-radius: var(--radius-ellipse);
background-color: rgba(0, 0, 0, 0.3);
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
color: #fff;
font-size: 0.8em;
width: 28px;
@@ -259,10 +259,10 @@ html[data-color-scheme=light] .visible {
}
.indicator {
- /* Hardcode to black because either --bg or --fg makes it hard to read in dark/light mode */
+ /* Hardcode to black because either --MI_THEME-bg or --MI_THEME-fg makes it hard to read in dark/light mode */
background-color: black;
border-radius: var(--radius-sm);
- color: var(--accentLighten);
+ color: var(--MI_THEME-accentLighten);
display: inline-block;
font-weight: bold;
font-size: 0.8em;
diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue
index 39fa6ff012..7f8033bcb6 100644
--- a/packages/frontend/src/components/MkMediaList.vue
+++ b/packages/frontend/src/components/MkMediaList.vue
@@ -329,14 +329,14 @@ defineExpose({
:global(.pswp) {
--pswp-root-z-index: var(--mk-pswp-root-z-index, 2000700) !important;
- --pswp-bg: var(--modalBg) !important;
+ --pswp-bg: var(--MI_THEME-modalBg) !important;
}
</style>
<style lang="scss">
.pswp__bg {
- background: var(--modalBg);
- backdrop-filter: var(--modalBgFilter);
+ background: var(--MI_THEME-modalBg);
+ backdrop-filter: var(--MI-modalBgFilter);
}
.pswp__alt-text-container {
@@ -354,14 +354,14 @@ defineExpose({
}
.pswp__alt-text {
- color: var(--fg);
+ color: var(--MI_THEME-fg);
margin: 0 auto;
text-align: center;
- padding: var(--margin);
- border-radius: var(--radius);
+ padding: var(--MI-margin);
+ border-radius: var(--MI-radius);
max-height: 8em;
overflow-y: auto;
- text-shadow: var(--bg) 0 0 10px, var(--bg) 0 0 3px, var(--bg) 0 0 3px;
+ text-shadow: var(--MI_THEME-bg) 0 0 10px, var(--MI_THEME-bg) 0 0 3px, var(--MI_THEME-bg) 0 0 3px;
white-space: pre-line;
}
</style>
diff --git a/packages/frontend/src/components/MkMediaRange.vue b/packages/frontend/src/components/MkMediaRange.vue
index 86ed8ba2cf..df7505b0c3 100644
--- a/packages/frontend/src/components/MkMediaRange.vue
+++ b/packages/frontend/src/components/MkMediaRange.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<!-- Media系専用のinput range -->
<template>
-<div :style="sliderBgWhite ? '--sliderBg: rgba(255,255,255,.25);' : '--sliderBg: var(--scrollbarHandle);'">
+<div :style="sliderBgWhite ? '--sliderBg: rgba(255,255,255,.25);' : '--sliderBg: var(--MI_THEME-scrollbarHandle);'">
<div :class="$style.controlsSeekbar">
<progress v-if="buffer !== undefined" :class="$style.buffer" :value="isNaN(buffer) ? 0 : buffer" min="0" max="1">{{ Math.round(buffer * 100) }}% buffered</progress>
<input v-model="model" :class="$style.seek" :style="`--value: ${modelValue * 100}%;`" type="range" min="0" max="1" step="any" @change="emit('dragEnded', modelValue)"/>
@@ -48,7 +48,7 @@ const modelValue = computed({
background: transparent;
border: 0;
border-radius: 26px;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
display: block;
height: 19px;
margin: 0;
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index 0502bdd401..c3cecba7b7 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<i class="ti ti-eye-off" :class="$style.hide" @click="hide = true"></i>
<div :class="$style.indicators">
<div v-if="video.comment" :class="$style.indicator">ALT</div>
- <div v-if="video.isSensitive" :class="$style.indicator" style="color: var(--warn);" :title="i18n.ts.sensitive"><i class="ti ti-eye-exclamation"></i></div>
+ <div v-if="video.isSensitive" :class="$style.indicator" style="color: var(--MI_THEME-warn);" :title="i18n.ts.sensitive"><i class="ti ti-eye-exclamation"></i></div>
</div>
</div>
@@ -67,7 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<i class="ti ti-eye-off" :class="$style.hide" @click="hide = true"></i>
<div :class="$style.indicators">
<div v-if="video.comment" :class="$style.indicator">ALT</div>
- <div v-if="video.isSensitive" :class="$style.indicator" style="color: var(--warn);" :title="i18n.ts.sensitive"><i class="ti ti-eye-exclamation"></i></div>
+ <div v-if="video.isSensitive" :class="$style.indicator" style="color: var(--MI_THEME-warn);" :title="i18n.ts.sensitive"><i class="ti ti-eye-exclamation"></i></div>
</div>
<div :class="$style.videoControls" @click.self="togglePlayPause">
<div :class="[$style.controlsChild, $style.controlsLeft]">
@@ -511,7 +511,7 @@ onDeactivated(() => {
height: 100%;
pointer-events: none;
border-radius: inherit;
- box-shadow: inset 0 0 0 4px var(--warn);
+ box-shadow: inset 0 0 0 4px var(--MI_THEME-warn);
}
}
@@ -526,10 +526,10 @@ onDeactivated(() => {
}
.indicator {
- /* Hardcode to black because either --bg or --fg makes it hard to read in dark/light mode */
+ /* Hardcode to black because either --MI_THEME-bg or --MI_THEME-fg makes it hard to read in dark/light mode */
background-color: black;
border-radius: 6px;
- color: var(--accentLighten);
+ color: var(--MI_THEME-accentLighten);
display: inline-block;
font-weight: bold;
font-size: 0.8em;
@@ -541,7 +541,7 @@ onDeactivated(() => {
position: absolute;
border-radius: var(--radius-sm);
background-color: black;
- color: var(--accentLighten);
+ color: var(--MI_THEME-accentLighten);
font-size: 12px;
opacity: .5;
padding: 5px 8px;
@@ -595,7 +595,7 @@ onDeactivated(() => {
opacity: 0;
transition: opacity .4s ease-in-out;
- background: var(--accent);
+ background: var(--MI_THEME-accent);
color: #fff;
padding: 1rem;
border-radius: 99rem;
@@ -661,12 +661,12 @@ onDeactivated(() => {
.controlButton {
padding: 6px;
- border-radius: calc(var(--radius) / 2);
+ border-radius: calc(var(--MI-radius) / 2);
transition: background-color .2s ease-in-out;
font-size: 1.05rem;
&:hover {
- background-color: var(--accent);
+ background-color: var(--MI_THEME-accent);
}
&:focus-visible {
diff --git a/packages/frontend/src/components/MkMention.vue b/packages/frontend/src/components/MkMention.vue
index de2048b6f2..0391c6bc39 100644
--- a/packages/frontend/src/components/MkMention.vue
+++ b/packages/frontend/src/components/MkMention.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkA v-user-preview="canonical" :class="[$style.root, { [$style.isMe]: isMe }]" :to="url" :style="{ background: bgCss }" :behavior="navigationBehavior">
+<MkA v-user-preview="canonical" :class="[$style.root, { [$style.isMe]: isMe }]" :to="url" :behavior="navigationBehavior">
<img :class="$style.icon" :src="avatarUrl" alt="">
<span>
<span>@{{ username }}</span>
@@ -16,7 +16,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { toUnicode } from 'punycode';
import { computed } from 'vue';
-import tinycolor from 'tinycolor2';
import { host as localHost } from '@@/js/config.js';
import { $i } from '@/account.js';
import { defaultStore } from '@/store.js';
@@ -37,11 +36,7 @@ const isMe = $i && (
`@${props.username}@${toUnicode(props.host)}` === `@${$i.username}@${toUnicode(localHost)}`.toLowerCase()
);
-const bg = tinycolor(getComputedStyle(document.documentElement).getPropertyValue(isMe ? '--mentionMe' : '--mention'));
-bg.setAlpha(0.1);
-const bgCss = bg.toRgbString();
-
-const avatarUrl = computed(() => defaultStore.state.disableShowingAnimatedImages
+const avatarUrl = computed(() => defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar
? getStaticImageUrl(`/avatar/@${props.username}@${props.host}`)
: `/avatar/@${props.username}@${props.host}`,
);
@@ -52,11 +47,13 @@ const avatarUrl = computed(() => defaultStore.state.disableShowingAnimatedImages
display: inline-block;
padding: 4px 8px 4px 4px;
border-radius: var(--radius-ellipse);
- color: var(--mention);
+ color: var(--MI_THEME-mention);
+ background: color(from var(--MI_THEME-mention) srgb r g b / 0.1);
white-space: nowrap;
&.isMe {
- color: var(--mentionMe);
+ color: var(--MI_THEME-mentionMe);
+ background: color(from var(--MI_THEME-mentionMe) srgb r g b / 0.1);
}
}
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index fe6df7090c..ff5f9b9a5d 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/>
<div :class="$style.item_content">
<span :class="$style.item_content_text">{{ item.text }}</span>
- <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
+ <span v-if="item.indicate" :class="$style.indicator" class="_blink"><i class="_indicatorCircle"></i></span>
</div>
</MkA>
<a
@@ -68,7 +68,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
<div :class="$style.item_content">
<span :class="$style.item_content_text">{{ item.text }}</span>
- <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
+ <span v-if="item.indicate" :class="$style.indicator" class="_blink"><i class="_indicatorCircle"></i></span>
</div>
</a>
<button
@@ -82,7 +82,7 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<MkAvatar :user="item.user" :class="$style.avatar"/><MkUserName :user="item.user"/>
<div v-if="item.indicate" :class="$style.item_content">
- <span :class="$style.indicator"><i class="_indicatorCircle"></i></span>
+ <span :class="$style.indicator" class="_blink"><i class="_indicatorCircle"></i></span>
</div>
</button>
<button
@@ -161,7 +161,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/>
<div :class="$style.item_content">
<span :class="$style.item_content_text">{{ item.text }}</span>
- <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
+ <span v-if="item.indicate" :class="$style.indicator" class="_blink"><i class="_indicatorCircle"></i></span>
</div>
</button>
</template>
@@ -437,9 +437,11 @@ onBeforeUnmount(() => {
&.big:not(.asDrawer) {
> .menu {
+ min-width: 230px;
+
> .item {
padding: 6px 20px;
- font-size: 1em;
+ font-size: 0.95em;
line-height: 24px;
}
}
@@ -505,7 +507,7 @@ onBeforeUnmount(() => {
overflow: hidden;
text-overflow: ellipsis;
text-decoration: none !important;
- color: var(--menuFg, var(--fg));
+ color: var(--menuFg, var(--MI_THEME-fg));
&::before {
content: "";
@@ -525,7 +527,7 @@ onBeforeUnmount(() => {
outline: none;
&:not(:hover):not(:active)::before {
- outline: var(--focus) solid 2px;
+ outline: var(--MI_THEME-focus) solid 2px;
outline-offset: -2px;
}
}
@@ -534,19 +536,19 @@ onBeforeUnmount(() => {
&:hover,
&:focus-visible:active,
&:focus-visible.active {
- color: var(--menuHoverFg, var(--accent));
+ color: var(--menuHoverFg, var(--MI_THEME-accent));
&::before {
- background-color: var(--menuHoverBg, var(--accentedBg));
+ background-color: var(--menuHoverBg, var(--MI_THEME-accentedBg));
}
}
&:not(:focus-visible):active,
&:not(:focus-visible).active {
- color: var(--menuActiveFg, var(--fgOnAccent));
+ color: var(--menuActiveFg, var(--MI_THEME-fgOnAccent));
&::before {
- background-color: var(--menuActiveBg, var(--accent));
+ background-color: var(--menuActiveBg, var(--MI_THEME-accent));
}
}
}
@@ -564,13 +566,13 @@ onBeforeUnmount(() => {
}
&.radio {
- --menuActiveFg: var(--accent);
- --menuActiveBg: var(--accentedBg);
+ --menuActiveFg: var(--MI_THEME-accent);
+ --menuActiveBg: var(--MI_THEME-accentedBg);
}
&.parent {
- --menuActiveFg: var(--accent);
- --menuActiveBg: var(--accentedBg);
+ --menuActiveFg: var(--MI_THEME-accent);
+ --menuActiveBg: var(--MI_THEME-accentedBg);
}
&.label {
@@ -635,14 +637,13 @@ onBeforeUnmount(() => {
.indicator {
display: flex;
align-items: center;
- color: var(--indicator);
+ color: var(--MI_THEME-indicator);
font-size: 12px;
- animation: global-blink 1s infinite;
}
.divider {
margin: 8px 0;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
.radioIcon {
@@ -652,11 +653,11 @@ onBeforeUnmount(() => {
height: 1em;
vertical-align: -0.125em;
border-radius: 50%;
- border: solid 2px var(--divider);
- background-color: var(--panel);
+ border: solid 2px var(--MI_THEME-divider);
+ background-color: var(--MI_THEME-panel);
&.radioChecked {
- border-color: var(--accent);
+ border-color: var(--MI_THEME-accent);
&::after {
content: "";
@@ -668,7 +669,7 @@ onBeforeUnmount(() => {
width: 50%;
height: 50%;
border-radius: 50%;
- background-color: var(--accent);
+ background-color: var(--MI_THEME-accent);
}
}
}
diff --git a/packages/frontend/src/components/MkMiniChart.vue b/packages/frontend/src/components/MkMiniChart.vue
index 1b6f6cef31..7ea585ecc2 100644
--- a/packages/frontend/src/components/MkMiniChart.vue
+++ b/packages/frontend/src/components/MkMiniChart.vue
@@ -48,7 +48,7 @@ const polygonPoints = ref('');
const headX = ref<number | null>(null);
const headY = ref<number | null>(null);
const clock = ref<number | null>(null);
-const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent'));
+const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-accent'));
const color = accent.toRgbString();
function draw(): void {
diff --git a/packages/frontend/src/components/MkModalWindow.vue b/packages/frontend/src/components/MkModalWindow.vue
index f26959888b..fe9e1ce088 100644
--- a/packages/frontend/src/components/MkModalWindow.vue
+++ b/packages/frontend/src/components/MkModalWindow.vue
@@ -90,12 +90,12 @@ defineExpose({
display: flex;
flex-direction: column;
contain: content;
- border-radius: var(--radius);
+ border-radius: var(--MI-radius);
--root-margin: 24px;
- --headerHeight: 46px;
- --headerHeightNarrow: 42px;
+ --MI_THEME-headerHeight: 46px;
+ --MI_THEME-headerHeightNarrow: 42px;
@media (max-width: 500px) {
--root-margin: 16px;
@@ -105,24 +105,24 @@ defineExpose({
.header {
display: flex;
flex-shrink: 0;
- background: var(--windowHeader);
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
+ background: var(--MI_THEME-windowHeader);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
}
.headerButton {
- height: var(--headerHeight);
- width: var(--headerHeight);
+ height: var(--MI_THEME-headerHeight);
+ width: var(--MI_THEME-headerHeight);
@media (max-width: 500px) {
- height: var(--headerHeightNarrow);
- width: var(--headerHeightNarrow);
+ height: var(--MI_THEME-headerHeightNarrow);
+ width: var(--MI_THEME-headerHeightNarrow);
}
}
.title {
flex: 1;
- line-height: var(--headerHeight);
+ line-height: var(--MI_THEME-headerHeight);
padding-left: 32px;
font-weight: bold;
white-space: nowrap;
@@ -131,7 +131,7 @@ defineExpose({
pointer-events: none;
@media (max-width: 500px) {
- line-height: var(--headerHeightNarrow);
+ line-height: var(--MI_THEME-headerHeightNarrow);
padding-left: 16px;
}
}
@@ -143,7 +143,7 @@ defineExpose({
.body {
flex: 1;
overflow: auto;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
container-type: size;
}
</style>
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 7ba4f0b9d9..55259406f8 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
v-show="!isDeleted"
ref="rootEl"
v-hotkey="keymap"
- :class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover }]"
+ :class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover, [$style.skipRender]: defaultStore.state.skipNoteRender }]"
:tabindex="isDeleted ? '-1' : '0'"
>
<div v-if="appearNote.reply && inReplyToCollapsed" :class="$style.collapsedInReplyTo">
@@ -156,8 +156,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<i class="ph-heart ph-bold ph-lg"></i>
</button>
<button ref="reactButton" :class="$style.footerButton" class="_button" @click="toggleReact()" @click.stop>
- <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReaction != null" class="ti ti-heart-filled" style="color: var(--love);"></i>
- <i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--accent);"></i>
+ <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReaction != null" class="ti ti-heart-filled" style="color: var(--MI_THEME-love);"></i>
+ <i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--MI_THEME-accent);"></i>
<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
<i v-else class="ph-smiley ph-bold ph-lg"></i>
<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.reactionCount) }}</p>
@@ -201,6 +201,9 @@ import { computed, inject, onMounted, ref, shallowRef, Ref, watch, provide } fro
import * as mfm from '@transfem-org/sfm-js';
import * as Misskey from 'misskey-js';
import { isLink } from '@@/js/is-link.js';
+import { shouldCollapsed } from '@@/js/collapsed.js';
+import { host } from '@@/js/config.js';
+import type { MenuItem } from '@/types/menu.js';
import MkNoteSub from '@/components/MkNoteSub.vue';
import MkNoteHeader from '@/components/MkNoteHeader.vue';
import MkNoteSimple from '@/components/MkNoteSimple.vue';
@@ -233,13 +236,10 @@ import { deepClone } from '@/scripts/clone.js';
import { useTooltip } from '@/scripts/use-tooltip.js';
import { claimAchievement } from '@/scripts/achievements.js';
import { getNoteSummary } from '@/scripts/get-note-summary.js';
-import type { MenuItem } from '@/types/menu.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
import { useRouter } from '@/router/supplier.js';
import { boostMenuItems, type Visibility } from '@/scripts/boost-quote.js';
-import { shouldCollapsed } from '@@/js/collapsed.js';
-import { host } from '@@/js/config.js';
import { isEnabledUrlPreview } from '@/instance.js';
import { type Keymap } from '@/scripts/hotkey.js';
import { focusPrev, focusNext } from '@/scripts/focus.js';
@@ -877,14 +877,6 @@ function emitUpdReaction(emoji: string, delta: number) {
overflow: clip;
contain: content;
- // これらの指定はパフォーマンス向上には有効だが、ノートの高さは一定でないため、
- // 下の方までスクロールすると上のノートの高さがここで決め打ちされたものに変化し、表示しているノートの位置が変わってしまう
- // ノートがマウントされたときに自身の高さを取得し contain-intrinsic-size を設定しなおせばほぼ解決できそうだが、
- // 今度はその処理自体がパフォーマンス低下の原因にならないか懸念される。また、被リアクションでも高さは変化するため、やはり多少のズレは生じる
- // 一度レンダリングされた要素はブラウザがよしなにサイズを覚えておいてくれるような実装になるまで待った方が良さそう(なるのか?)
- //content-visibility: auto;
- //contain-intrinsic-size: 0 128px;
-
&:focus-visible {
outline: none;
@@ -901,8 +893,8 @@ function emitUpdReaction(emoji: string, delta: number) {
margin: auto;
width: calc(100% - 8px);
height: calc(100% - 8px);
- border: dashed 2px var(--focus);
- border-radius: var(--radius);
+ border: dashed 2px var(--MI_THEME-focus);
+ border-radius: var(--MI-radius);
box-sizing: border-box;
}
}
@@ -929,9 +921,9 @@ function emitUpdReaction(emoji: string, delta: number) {
right: 12px;
padding: 0 4px;
margin-bottom: 0 !important;
- background: var(--popup);
+ background: var(--MI_THEME-popup);
border-radius: var(--radius-sm);
- box-shadow: 0px 4px 32px var(--shadow);
+ box-shadow: 0px 4px 32px var(--MI_THEME-shadow);
}
.footerButton {
@@ -950,6 +942,11 @@ function emitUpdReaction(emoji: string, delta: number) {
}
}
+.skipRender {
+ content-visibility: auto;
+ contain-intrinsic-size: 0 150px;
+}
+
.tip {
display: flex;
align-items: center;
@@ -976,7 +973,7 @@ function emitUpdReaction(emoji: string, delta: number) {
padding: 16px 32px 8px 32px;
line-height: 28px;
white-space: pre;
- color: var(--renote);
+ color: var(--MI_THEME-renote);
& + .article {
padding-top: 8px;
@@ -1081,7 +1078,7 @@ function emitUpdReaction(emoji: string, delta: number) {
width: var(--avatar);
height: var(--avatar);
position: sticky !important;
- top: calc(22px + var(--stickyTop, 0px));
+ top: calc(22px + var(--MI-stickyTop, 0px));
left: 0;
}
@@ -1101,12 +1098,12 @@ function emitUpdReaction(emoji: string, delta: number) {
width: 100%;
margin-top: 14px;
position: sticky;
- bottom: calc(var(--stickyBottom, 0px) - 100px);
+ bottom: calc(var(--MI-stickyBottom, 0px) - 100px);
}
.showLessLabel {
display: inline-block;
- background: var(--popup);
+ background: var(--MI_THEME-popup);
padding: 6px 10px;
font-size: 0.8em;
border-radius: var(--radius-ellipse);
@@ -1127,16 +1124,16 @@ function emitUpdReaction(emoji: string, delta: number) {
z-index: 2;
width: 100%;
height: 64px;
- //background: linear-gradient(0deg, var(--panel), color(from var(--panel) srgb r g b / 0));
+ //background: linear-gradient(0deg, var(--MI_THEME-panel), color(from var(--MI_THEME-panel) srgb r g b / 0));
&:hover > .collapsedLabel {
- background: var(--panelHighlight);
+ background: var(--MI_THEME-panelHighlight);
}
}
.collapsedLabel {
display: inline-block;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
padding: 6px 10px;
font-size: 0.8em;
border-radius: var(--radius-ellipse);
@@ -1149,13 +1146,13 @@ function emitUpdReaction(emoji: string, delta: number) {
}
.replyIcon {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
margin-right: 0.5em;
}
.translation {
- border: solid 0.5px var(--divider);
- border-radius: var(--radius);
+ border: solid 0.5px var(--MI_THEME-divider);
+ border-radius: var(--MI-radius);
padding: 12px;
margin-top: 8px;
}
@@ -1178,7 +1175,7 @@ function emitUpdReaction(emoji: string, delta: number) {
.quoteNote {
padding: 16px;
- border: dashed 1px var(--renote);
+ border: dashed 1px var(--MI_THEME-renote);
border-radius: var(--radius-sm);
overflow: clip;
}
@@ -1202,7 +1199,7 @@ function emitUpdReaction(emoji: string, delta: number) {
}
&:hover {
- color: var(--fgHighlighted);
+ color: var(--MI_THEME-fgHighlighted);
}
}
@@ -1277,7 +1274,7 @@ function emitUpdReaction(emoji: string, delta: number) {
margin: 0 10px 0 0;
width: 46px;
height: 46px;
- top: calc(14px + var(--stickyTop, 0px));
+ top: calc(14px + var(--MI-stickyTop, 0px));
}
}
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 5acb18c871..1e0c78e82e 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -157,8 +157,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<i class="ph-heart ph-bold ph-lg"></i>
</button>
<button ref="reactButton" :class="$style.noteFooterButton" class="_button" @click="toggleReact()">
- <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReaction != null" class="ti ti-heart-filled" style="color: var(--love);"></i>
- <i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--accent);"></i>
+ <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReaction != null" class="ti ti-heart-filled" style="color: var(--MI_THEME-love);"></i>
+ <i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--MI_THEME-accent);"></i>
<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
<i v-else class="ph-smiley ph-bold ph-lg"></i>
<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p>
@@ -843,8 +843,8 @@ function animatedMFM() {
margin: auto;
width: calc(100% - 8px);
height: calc(100% - 8px);
- border: dashed 2px var(--focus);
- border-radius: var(--radius);
+ border: dashed 2px var(--MI_THEME-focus);
+ border-radius: var(--MI-radius);
box-sizing: border-box;
}
}
@@ -874,7 +874,7 @@ function animatedMFM() {
padding: 16px 32px 8px 32px;
line-height: 28px;
white-space: pre;
- color: var(--renote);
+ color: var(--MI_THEME-renote);
}
.renoteAvatar {
@@ -956,7 +956,7 @@ function animatedMFM() {
padding: 4px 6px;
font-size: 80%;
line-height: 1;
- border: solid 0.5px var(--divider);
+ border: solid 0.5px var(--MI_THEME-divider);
border-radius: var(--radius-xs);
}
@@ -989,19 +989,19 @@ function animatedMFM() {
}
.noteReplyTarget {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
margin-right: 0.5em;
}
.rn {
margin-left: 4px;
font-style: oblique;
- color: var(--renote);
+ color: var(--MI_THEME-renote);
}
.translation {
- border: solid 0.5px var(--divider);
- border-radius: var(--radius);
+ border: solid 0.5px var(--MI_THEME-divider);
+ border-radius: var(--MI-radius);
padding: 12px;
margin-top: 8px;
}
@@ -1016,7 +1016,7 @@ function animatedMFM() {
.quoteNote {
padding: 16px;
- border: dashed 1px var(--renote);
+ border: dashed 1px var(--MI_THEME-renote);
border-radius: var(--radius-sm);
overflow: clip;
}
@@ -1042,7 +1042,7 @@ function animatedMFM() {
}
&:hover {
- color: var(--fgHighlighted);
+ color: var(--MI_THEME-fgHighlighted);
}
}
@@ -1052,17 +1052,17 @@ function animatedMFM() {
opacity: 0.7;
&.reacted {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
}
.reply:not(:first-child) {
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
.tabs {
- border-top: solid 0.5px var(--divider);
- border-bottom: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
display: flex;
}
@@ -1074,7 +1074,7 @@ function animatedMFM() {
}
.tabActive {
- border-bottom: solid 2px var(--accent);
+ border-bottom: solid 2px var(--MI_THEME-accent);
}
.tab_renotes {
@@ -1094,12 +1094,12 @@ function animatedMFM() {
.reactionTab {
padding: 4px 6px;
- border: solid 1px var(--divider);
+ border: solid 1px var(--MI_THEME-divider);
border-radius: var(--radius-sm);
}
.reactionTabActive {
- border-color: var(--accent);
+ border-color: var(--MI_THEME-accent);
}
@container (max-width: 500px) {
diff --git a/packages/frontend/src/components/MkNoteHeader.vue b/packages/frontend/src/components/MkNoteHeader.vue
index cd6fdf576c..10107ba0b1 100644
--- a/packages/frontend/src/components/MkNoteHeader.vue
+++ b/packages/frontend/src/components/MkNoteHeader.vue
@@ -5,18 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<header :class="$style.root">
- <component :is="defaultStore.state.enableCondensedLine ? 'MkCondensedLine' : 'div'" :minScale="0.5" style="min-width: 0;">
- <div style="display: flex; white-space: nowrap; align-items: baseline;">
- <div v-if="mock" :class="$style.name">
- <MkUserName :user="note.user"/>
- </div>
- <MkA v-else v-user-preview="note.user.id" :class="$style.name" :to="userPage(note.user)">
- <MkUserName :user="note.user"/>
- </MkA>
- <div v-if="note.user.isBot" :class="$style.isBot">bot</div>
- <div :class="$style.username"><MkAcct :user="note.user"/></div>
- </div>
- </component>
+ <div v-if="mock" :class="$style.name">
+ <MkUserName :user="note.user"/>
+ </div>
+ <MkA v-else v-user-preview="note.user.id" :class="$style.name" :to="userPage(note.user)">
+ <MkUserName :user="note.user"/>
+ </MkA>
+ <div v-if="note.user.isBot" :class="$style.isBot">bot</div>
+ <div :class="$style.username"><MkAcct :user="note.user"/></div>
<div v-if="note.user.badgeRoles" :class="$style.badgeRoles">
<img v-for="(role, i) in note.user.badgeRoles" :key="i" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl!"/>
</div>
@@ -95,7 +91,7 @@ const mock = inject<boolean>('mock', false);
margin: 0 .5em 0 0;
padding: 1px 6px;
font-size: 80%;
- border: solid 0.5px var(--divider);
+ border: solid 0.5px var(--MI_THEME-divider);
border-radius: var(--radius-xs);
}
diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue
index 542e3e79ea..4a4cdef679 100644
--- a/packages/frontend/src/components/MkNoteSimple.vue
+++ b/packages/frontend/src/components/MkNoteSimple.vue
@@ -58,7 +58,7 @@ watch(() => props.expandAllCws, (expandAllCws) => {
height: 34px;
border-radius: var(--radius-sm);
position: sticky !important;
- top: calc(16px + var(--stickyTop, 0px));
+ top: calc(16px + var(--MI-stickyTop, 0px));
left: 0;
}
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 45276839ad..c0be406893 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -510,7 +510,7 @@ if (props.detail) {
}
.reply, .more {
- border-left: solid 0.5px var(--divider);
+ border-left: solid 0.5px var(--MI_THEME-divider);
margin-top: 10px;
}
@@ -531,7 +531,7 @@ if (props.detail) {
.muted {
text-align: center;
padding: 8px !important;
- border: 1px solid var(--divider);
+ border: 1px solid var(--MI_THEME-divider);
margin: 8px 8px 0 8px;
border-radius: var(--radius-sm);
}
diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotes.vue
index 15173fbd99..4144e69d1e 100644
--- a/packages/frontend/src/components/MkNotes.vue
+++ b/packages/frontend/src/components/MkNotes.vue
@@ -64,17 +64,17 @@ defineExpose({
border-radius: var(--radius);
> .notes {
- background: color-mix(in srgb, var(--panel) 65%, transparent);
+ background: color-mix(in srgb, var(--MI_THEME-panel) 65%, transparent);
}
}
&:not(.noGap) {
> .notes {
- background: var(--bg);
+ background: var(--MI_THEME-bg);
.note {
- background: color-mix(in srgb, var(--panel) 65%, transparent);
- border-radius: var(--radius);
+ background: color-mix(in srgb, var(--MI_THEME-panel) 65%, transparent);
+ border-radius: var(--MI-radius);
}
}
}
diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue
index 7bec9bdc65..ed66360d0e 100644
--- a/packages/frontend/src/components/MkNotification.vue
+++ b/packages/frontend/src/components/MkNotification.vue
@@ -7,13 +7,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.root">
<div :class="$style.head">
<MkAvatar v-if="['pollEnded', 'note', 'edited'].includes(notification.type) && 'note' in notification" :class="$style.icon" :user="notification.note.user" link preview/>
- <MkAvatar v-else-if="['roleAssigned', 'achievementEarned'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
- <div v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ph-smiley ph-bold ph-lg" style="line-height: 1;"></i></div>
+ <MkAvatar v-else-if="['roleAssigned', 'achievementEarned', 'exportCompleted', 'login'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
+ <div v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'" :class="[$style.icon, $style.icon_reactionGroupHeart]"><i class="ph-smiley ph-bold ph-lg" style="line-height: 1;"></i></div>
<div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ph-smiley ph-bold ph-lg" style="line-height: 1;"></i></div>
<div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ti ti-repeat" style="line-height: 1;"></i></div>
<img v-else-if="notification.type === 'test'" :class="$style.icon" :src="infoImageUrl"/>
<MkAvatar v-else-if="'user' in notification" :class="$style.icon" :user="notification.user" link preview/>
- <MkAvatar v-else-if="notification.type === 'exportCompleted'" :class="$style.icon" :user="$i" link preview/>
<img v-else-if="'icon' in notification && notification.icon != null" :class="[$style.icon, $style.icon_app]" :src="notification.icon" alt=""/>
<div
:class="[$style.subIcon, {
@@ -27,6 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
[$style.t_pollEnded]: notification.type === 'pollEnded',
[$style.t_achievementEarned]: notification.type === 'achievementEarned',
[$style.t_exportCompleted]: notification.type === 'exportCompleted',
+ [$style.t_login]: notification.type === 'login',
[$style.t_roleAssigned]: notification.type === 'roleAssigned' && notification.role.iconUrl == null,
[$style.t_pollEnded]: notification.type === 'edited',
}]"
@@ -41,6 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<i v-else-if="notification.type === 'pollEnded'" class="ti ti-chart-arrows"></i>
<i v-else-if="notification.type === 'achievementEarned'" class="ti ti-medal"></i>
<i v-else-if="notification.type === 'exportCompleted'" class="ti ti-archive"></i>
+ <i v-else-if="notification.type === 'login'" class="ti ti-login-2"></i>
<template v-else-if="notification.type === 'roleAssigned'">
<img v-if="notification.role.iconUrl" style="height: 1.3em; vertical-align: -22%;" :src="notification.role.iconUrl" alt=""/>
<i v-else class="ti ti-badges"></i>
@@ -62,6 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<span v-else-if="notification.type === 'note'">{{ i18n.ts._notification.newNote }}: <MkUserName :user="notification.note.user"/></span>
<span v-else-if="notification.type === 'roleAssigned'">{{ i18n.ts._notification.roleAssigned }}</span>
<span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span>
+ <span v-else-if="notification.type === 'login'">{{ i18n.ts._notification.login }}</span>
<span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</span>
<span v-else-if="notification.type === 'exportCompleted'">{{ i18n.tsx._notification.exportOfXCompleted({ x: exportEntityName[notification.exportedEntity] }) }}</span>
<MkA v-else-if="notification.type === 'follow' || notification.type === 'mention' || notification.type === 'reply' || notification.type === 'renote' || notification.type === 'quote' || notification.type === 'reaction' || notification.type === 'receiveFollowRequest' || notification.type === 'followRequestAccepted'" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA>
@@ -228,13 +230,16 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification)
overflow-wrap: break-word;
display: flex;
contain: content;
+ content-visibility: auto;
+ contain-intrinsic-size: 0 100px;
--eventFollow: #36aed2;
--eventRenote: #36d298;
--eventReply: #007aff;
- --eventReactionHeart: var(--love);
+ --eventReactionHeart: var(--MI_THEME-love);
--eventReaction: #e99a0b;
--eventAchievement: #cb9a11;
+ --eventLogin: #007aff;
--eventOther: #88a6b7;
}
@@ -291,8 +296,8 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification)
height: 20px;
box-sizing: border-box;
border-radius: var(--radius-full);
- background: var(--panel);
- box-shadow: 0 0 0 3px var(--panel);
+ background: var(--MI_THEME-panel);
+ box-shadow: 0 0 0 3px var(--MI_THEME-panel);
font-size: 11px;
text-align: center;
color: #fff;
@@ -356,6 +361,12 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification)
pointer-events: none;
}
+.t_login {
+ padding: 3px;
+ background: var(--eventLogin);
+ pointer-events: none;
+}
+
.tail {
flex: 1;
min-width: 0;
@@ -438,8 +449,8 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification)
height: 20px;
box-sizing: border-box;
border-radius: var(--radius-full);
- background: var(--panel);
- box-shadow: 0 0 0 3px var(--panel);
+ background: var(--MI_THEME-panel);
+ box-shadow: 0 0 0 3px var(--MI_THEME-panel);
font-size: 11px;
text-align: center;
color: #fff;
diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue
index a395734add..51c4ea7ce4 100644
--- a/packages/frontend/src/components/MkNotifications.vue
+++ b/packages/frontend/src/components/MkNotifications.vue
@@ -111,6 +111,6 @@ defineExpose({
<style lang="scss" module>
.list {
- background: var(--panel);
+ background: var(--MI_THEME-panel);
}
</style>
diff --git a/packages/frontend/src/components/MkNumberDiff.vue b/packages/frontend/src/components/MkNumberDiff.vue
index 1825cc5405..80c634fdce 100644
--- a/packages/frontend/src/components/MkNumberDiff.vue
+++ b/packages/frontend/src/components/MkNumberDiff.vue
@@ -24,11 +24,11 @@ const isZero = computed(() => props.value === 0);
<style lang="scss" module>
.isPlus {
- color: var(--success);
+ color: var(--MI_THEME-success);
}
.isMinus {
- color: var(--error);
+ color: var(--MI_THEME-error);
}
.isZero {
diff --git a/packages/frontend/src/components/MkObjectView.value.vue b/packages/frontend/src/components/MkObjectView.value.vue
index 870599aa94..dabdd324fd 100644
--- a/packages/frontend/src/components/MkObjectView.value.vue
+++ b/packages/frontend/src/components/MkObjectView.value.vue
@@ -78,7 +78,7 @@ function collapsable(v): boolean {
> .boolean {
display: inline;
- color: var(--codeBoolean);
+ color: var(--MI_THEME-codeBoolean);
&.true {
font-weight: bold;
@@ -91,12 +91,12 @@ function collapsable(v): boolean {
> .string {
display: inline;
- color: var(--codeString);
+ color: var(--MI_THEME-codeString);
}
> .number {
display: inline;
- color: var(--codeNumber);
+ color: var(--MI_THEME-codeNumber);
}
> .array.empty {
@@ -127,7 +127,7 @@ function collapsable(v): boolean {
> .toggle {
width: 16px;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
visibility: hidden;
&.visible {
diff --git a/packages/frontend/src/components/MkOmit.vue b/packages/frontend/src/components/MkOmit.vue
index 94cbaf5c91..e19c34ba87 100644
--- a/packages/frontend/src/components/MkOmit.vue
+++ b/packages/frontend/src/components/MkOmit.vue
@@ -47,7 +47,7 @@ onUnmounted(() => {
<style lang="scss" module>
.content {
- --stickyTop: 0px;
+ --MI-stickyTop: 0px;
&.omitted {
position: relative;
@@ -62,11 +62,11 @@ onUnmounted(() => {
left: 0;
width: 100%;
height: 64px;
- //background: linear-gradient(0deg, var(--panel), color(from var(--panel) srgb r g b / 0));
+ //background: linear-gradient(0deg, var(--MI_THEME-panel), color(from var(--MI_THEME-panel) srgb r g b / 0));
> .fadeLabel {
display: inline-block;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
padding: 6px 10px;
font-size: 0.8em;
border-radius: var(--radius-ellipse);
@@ -75,7 +75,7 @@ onUnmounted(() => {
&:hover {
> .fadeLabel {
- background: var(--panelHighlight);
+ background: var(--MI_THEME-panelHighlight);
}
}
}
diff --git a/packages/frontend/src/components/MkPagePreview.vue b/packages/frontend/src/components/MkPagePreview.vue
index 8559d4b96e..35a37a1f7d 100644
--- a/packages/frontend/src/components/MkPagePreview.vue
+++ b/packages/frontend/src/components/MkPagePreview.vue
@@ -42,7 +42,7 @@ const props = defineProps<{
.eyeCatchingImageRoot {
width: 100%;
height: 200px;
- border-radius: var(--radius) var(--radius) 0 0;
+ border-radius: var(--MI-radius) var(--MI-radius) 0 0;
overflow: hidden;
}
</style>
@@ -54,7 +54,7 @@ const props = defineProps<{
&:hover {
text-decoration: none;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
&:focus-within {
@@ -67,22 +67,22 @@ const props = defineProps<{
left: 0;
width: 100%;
height: 100%;
- border-radius: var(--radius);
+ border-radius: var(--MI-radius);
pointer-events: none;
- box-shadow: inset 0 0 0 2px var(--focus);
+ box-shadow: inset 0 0 0 2px var(--MI_THEME-focus);
}
}
> .thumbnail {
& + article {
- border-radius: 0 0 var(--radius) var(--radius);
+ border-radius: 0 0 var(--MI-radius) var(--MI-radius);
}
}
> article {
- background-color: var(--panel);
+ background-color: var(--MI_THEME-panel);
padding: 16px;
- border-radius: var(--radius);
+ border-radius: var(--MI-radius);
> header {
margin-bottom: 8px;
@@ -115,7 +115,6 @@ const props = defineProps<{
> p {
display: inline-block;
margin: 0;
- color: var(--urlPreviewInfo);
font-size: 0.8em;
line-height: 16px;
vertical-align: top;
diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue
index f67a1e5b63..4aac283ecd 100644
--- a/packages/frontend/src/components/MkPageWindow.vue
+++ b/packages/frontend/src/components/MkPageWindow.vue
@@ -181,8 +181,8 @@ defineExpose({
overscroll-behavior: contain;
min-height: 100%;
- background: var(--bg);
+ background: var(--MI_THEME-bg);
- --margin: var(--marginHalf);
+ --MI-margin: var(--MI-marginHalf);
}
</style>
diff --git a/packages/frontend/src/components/MkPoll.vue b/packages/frontend/src/components/MkPoll.vue
index 592a511fb0..e11fb4fc99 100644
--- a/packages/frontend/src/components/MkPoll.vue
+++ b/packages/frontend/src/components/MkPoll.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<li v-for="(choice, i) in props.poll.choices" :key="i" :class="$style.choice" @click="vote(i)">
<div :class="$style.bg" :style="{ 'width': `${showResult ? (choice.votes / total * 100) : 0}%` }"></div>
<span :class="$style.fg">
- <template v-if="choice.isVoted"><i class="ti ti-check" style="margin-right: 4px; color: var(--accent);"></i></template>
+ <template v-if="choice.isVoted"><i class="ti ti-check" style="margin-right: 4px; color: var(--MI_THEME-accent);"></i></template>
<Mfm :text="choice.text" :plain="true"/>
<span v-if="showResult" style="margin-left: 4px; opacity: 0.7;">({{ i18n.tsx._poll.votesCount({ n: choice.votes }) }})</span>
</span>
@@ -139,8 +139,8 @@ const refreshVotes = async () => {
position: relative;
margin: 4px 0;
padding: 4px;
- //border: solid 0.5px var(--divider);
- background: var(--accentedBg);
+ //border: solid 0.5px var(--MI_THEME-divider);
+ background: var(--MI_THEME-accentedBg);
border-radius: var(--radius-xs);
overflow: clip;
cursor: pointer;
@@ -151,8 +151,8 @@ const refreshVotes = async () => {
top: 0;
left: 0;
height: 100%;
- background: var(--accent);
- background: linear-gradient(90deg,var(--buttonGradateA),var(--buttonGradateB));
+ background: var(--MI_THEME-accent);
+ background: linear-gradient(90deg,var(--MI_THEME-buttonGradateA),var(--MI_THEME-buttonGradateB));
transition: width 1s ease;
}
@@ -160,12 +160,12 @@ const refreshVotes = async () => {
position: relative;
display: inline-block;
padding: 3px 5px;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
border-radius: var(--radius-xs);
}
.info {
- color: var(--fg);
+ color: var(--MI_THEME-fg);
}
.done {
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 4a29b27ac4..b7d67f19ad 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -1175,7 +1175,7 @@ defineExpose({
outline: none;
.submitInner {
- outline: 2px solid var(--fgOnAccent);
+ outline: 2px solid var(--MI_THEME-fgOnAccent);
outline-offset: -4px;
}
}
@@ -1190,13 +1190,13 @@ defineExpose({
&:not(:disabled):hover {
> .inner {
- background: linear-gradient(90deg, hsl(from var(--accent) h s calc(l + 5)), hsl(from var(--accent) h s calc(l + 5)));
+ background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5)));
}
}
&:not(:disabled):active {
> .inner {
- background: linear-gradient(90deg, hsl(from var(--accent) h s calc(l + 5)), hsl(from var(--accent) h s calc(l + 5)));
+ background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5)));
}
}
}
@@ -1218,8 +1218,8 @@ defineExpose({
border-radius: var(--radius-sm);
min-width: 90px;
box-sizing: border-box;
- color: var(--fgOnAccent);
- background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
+ color: var(--MI_THEME-fgOnAccent);
+ background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
}
.headerRightItem {
@@ -1228,7 +1228,7 @@ defineExpose({
border-radius: var(--radius-sm);
&:hover {
- background: var(--X5);
+ background: var(--MI_THEME-X5);
}
&:disabled {
@@ -1272,7 +1272,7 @@ defineExpose({
.withQuote {
margin: 0 0 8px 0;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
.toSpecified {
@@ -1292,7 +1292,7 @@ defineExpose({
margin-right: 14px;
padding: 8px 0 8px 8px;
border-radius: var(--radius-sm);
- background: var(--X4);
+ background: var(--MI_THEME-X4);
}
.hasNotSpecifiedMentions {
@@ -1311,7 +1311,7 @@ defineExpose({
border: none;
border-radius: 0;
background: transparent;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
font-family: inherit;
&:focus {
@@ -1326,7 +1326,7 @@ defineExpose({
.cwFrame {
z-index: 1;
padding-bottom: 8px;
- border-bottom: solid 0.5px var(--divider);
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
width: 100%;
position: relative;
@@ -1336,7 +1336,7 @@ defineExpose({
z-index: 1;
padding-top: 8px;
padding-bottom: 8px;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
.textOuter {
@@ -1362,7 +1362,7 @@ defineExpose({
right: 2px;
padding: 4px 6px;
font-size: .9em;
- color: var(--warn);
+ color: var(--MI_THEME-warn);
border-radius: var(--radius-sm);
min-width: 1.6em;
text-align: center;
@@ -1406,16 +1406,16 @@ defineExpose({
border-radius: var(--radius-sm);
&:hover {
- background: var(--X5);
+ background: var(--MI_THEME-X5);
}
&.footerButtonActive {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
}
.previewButtonActive {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
@container (max-width: 500px) {
diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue
index f90fcfef33..a601a110fa 100644
--- a/packages/frontend/src/components/MkPostFormAttaches.vue
+++ b/packages/frontend/src/components/MkPostFormAttaches.vue
@@ -216,7 +216,7 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent | Keyboar
width: 100%;
height: 100%;
z-index: 1;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
}
.sensitive {
diff --git a/packages/frontend/src/components/MkRadio.vue b/packages/frontend/src/components/MkRadio.vue
index e02f76a58f..838eaee292 100644
--- a/packages/frontend/src/components/MkRadio.vue
+++ b/packages/frontend/src/components/MkRadio.vue
@@ -53,9 +53,9 @@ function toggle(): void {
cursor: pointer;
padding: 7px 10px;
min-width: 60px;
- background-color: var(--panel);
+ background-color: var(--MI_THEME-panel);
background-clip: padding-box !important;
- border: solid 1px var(--panel);
+ border: solid 1px var(--MI_THEME-panel);
border-radius: var(--radius-sm);
font-size: 90%;
transition: all 0.2s;
@@ -67,25 +67,25 @@ function toggle(): void {
}
&:hover {
- border-color: var(--inputBorderHover) !important;
+ border-color: var(--MI_THEME-inputBorderHover) !important;
}
&:focus-within {
outline: none;
- box-shadow: 0 0 0 2px var(--focus);
+ box-shadow: 0 0 0 2px var(--MI_THEME-focus);
}
&.checked {
- background-color: var(--accentedBg) !important;
- border-color: var(--accentedBg) !important;
- color: var(--accent);
+ background-color: var(--MI_THEME-accentedBg) !important;
+ border-color: var(--MI_THEME-accentedBg) !important;
+ color: var(--MI_THEME-accent);
cursor: default !important;
> .button {
- border-color: var(--accent);
+ border-color: var(--MI_THEME-accent);
&::after {
- background-color: var(--accent);
+ background-color: var(--MI_THEME-accent);
transform: scale(1);
opacity: 1;
}
@@ -106,7 +106,7 @@ function toggle(): void {
width: 14px;
height: 14px;
background: none;
- border: solid 2px var(--inputBorder);
+ border: solid 2px var(--MI_THEME-inputBorder);
border-radius: var(--radius-full);
transition: inherit;
diff --git a/packages/frontend/src/components/MkRadios.vue b/packages/frontend/src/components/MkRadios.vue
index 705c93f770..af81eb814d 100644
--- a/packages/frontend/src/components/MkRadios.vue
+++ b/packages/frontend/src/components/MkRadios.vue
@@ -77,7 +77,7 @@ export default defineComponent({
> .caption {
font-size: 0.85em;
padding: 8px 0 0 0;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
&:empty {
display: none;
diff --git a/packages/frontend/src/components/MkRange.vue b/packages/frontend/src/components/MkRange.vue
index 22c187c357..72e9aa6c0b 100644
--- a/packages/frontend/src/components/MkRange.vue
+++ b/packages/frontend/src/components/MkRange.vue
@@ -212,7 +212,7 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
> .caption {
font-size: 0.85em;
padding: 8px 0 0 0;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
&:empty {
display: none;
@@ -224,8 +224,8 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
> .body {
padding: 7px 12px;
- background: var(--panel);
- border: solid 1px var(--panel);
+ background: var(--MI_THEME-panel);
+ border: solid 1px var(--MI_THEME-panel);
border-radius: var(--radius-sm);
> .container {
@@ -250,7 +250,7 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
top: 0;
left: 0;
height: 100%;
- background: var(--accent);
+ background: var(--MI_THEME-accent);
opacity: 0.5;
}
}
@@ -272,7 +272,7 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
width: $tickWidth;
height: 3px;
margin-left: - math.div($tickWidth, 2);
- background: var(--divider);
+ background: var(--MI_THEME-divider);
border-radius: var(--radius-ellipse);
}
}
@@ -282,11 +282,11 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
width: $thumbWidth;
height: $thumbHeight;
cursor: grab;
- background: var(--accent);
+ background: var(--MI_THEME-accent);
border-radius: var(--radius-ellipse);
&:hover {
- background: var(--accentLighten);
+ background: var(--MI_THEME-accentLighten);
}
}
}
diff --git a/packages/frontend/src/components/MkReactionEffect.vue b/packages/frontend/src/components/MkReactionEffect.vue
index 361e246e9f..5a59a5e055 100644
--- a/packages/frontend/src/components/MkReactionEffect.vue
+++ b/packages/frontend/src/components/MkReactionEffect.vue
@@ -60,7 +60,7 @@ onMounted(() => {
right: 0;
bottom: 0;
margin: auto;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
font-size: 18px;
font-weight: bold;
transform: translateY(-30px);
diff --git a/packages/frontend/src/components/MkReactionsViewer.details.vue b/packages/frontend/src/components/MkReactionsViewer.details.vue
index 6fdeb3a3ab..a12bb55fa3 100644
--- a/packages/frontend/src/components/MkReactionsViewer.details.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.details.vue
@@ -57,7 +57,7 @@ function getReactionName(reaction: string): string {
max-width: 100px;
padding-right: 10px;
text-align: center;
- border-right: solid 0.5px var(--divider);
+ border-right: solid 0.5px var(--MI_THEME-divider);
}
.reactionIcon {
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index 957ee0e76b..32ab8ac3c3 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -180,7 +180,7 @@ if (!mock) {
justify-content: center;
&.canToggle {
- background: var(--buttonBg);
+ background: var(--MI_THEME-buttonBg);
&:hover {
background: rgba(0, 0, 0, 0.1);
@@ -214,12 +214,12 @@ if (!mock) {
}
&.reacted, &.reacted:hover {
- background: var(--accentedBg);
- color: var(--accent);
- box-shadow: 0 0 0 1px var(--accent) inset;
+ background: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
+ box-shadow: 0 0 0 1px var(--MI_THEME-accent) inset;
> .count {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
> .icon {
diff --git a/packages/frontend/src/components/MkRemoteCaution.vue b/packages/frontend/src/components/MkRemoteCaution.vue
index 2b59eab9d9..6391468204 100644
--- a/packages/frontend/src/components/MkRemoteCaution.vue
+++ b/packages/frontend/src/components/MkRemoteCaution.vue
@@ -19,15 +19,15 @@ defineProps<{
.root {
font-size: 0.8em;
padding: 16px;
- background: color-mix(in srgb, var(--infoWarnBg) 65%, transparent);
- color: var(--infoWarnFg);
- border-radius: var(--radius);
+ background: color-mix(in srgb, var(--MI_THEME-infoWarnBg) 65%, transparent);
+ color: var(--MI_THEME-infoWarnFg);
+ border-radius: var(--MI-radius);
overflow: clip;
z-index: 1;
}
.link {
margin-left: 4px;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
</style>
diff --git a/packages/frontend/src/components/MkRetentionLineChart.vue b/packages/frontend/src/components/MkRetentionLineChart.vue
index c3daa9c9a4..d41793b0fa 100644
--- a/packages/frontend/src/components/MkRetentionLineChart.vue
+++ b/packages/frontend/src/components/MkRetentionLineChart.vue
@@ -44,7 +44,7 @@ onMounted(async () => {
const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
- const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent'));
+ const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-accent'));
const color = accent.toHex();
if (chartEl.value == null) return;
diff --git a/packages/frontend/src/components/MkRippleEffect.vue b/packages/frontend/src/components/MkRippleEffect.vue
index ee5bb73ebf..2949cf156d 100644
--- a/packages/frontend/src/components/MkRippleEffect.vue
+++ b/packages/frontend/src/components/MkRippleEffect.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="$style.root" :style="{ zIndex, top: `${y - 64}px`, left: `${x - 64}px` }">
<svg width="128" height="128" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg">
- <circle fill="none" cx="64" cy="64" style="stroke: var(--accent);">
+ <circle fill="none" cx="64" cy="64" style="stroke: var(--MI_THEME-accent);">
<animate
attributeName="r"
begin="0s" dur="0.5s"
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
/>
</circle>
<g fill="none" fill-rule="evenodd">
- <circle v-for="(particle, i) in particles" :key="i" :fill="particle.color" style="stroke: var(--accent);">
+ <circle v-for="(particle, i) in particles" :key="i" :fill="particle.color" style="stroke: var(--MI_THEME-accent);">
<animate
attributeName="r"
begin="0s" dur="0.8s"
diff --git a/packages/frontend/src/components/MkRolePreview.vue b/packages/frontend/src/components/MkRolePreview.vue
index ce17ae08e0..3f14c5b5e0 100644
--- a/packages/frontend/src/components/MkRolePreview.vue
+++ b/packages/frontend/src/components/MkRolePreview.vue
@@ -6,8 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<MkA :to="forModeration ? `/admin/roles/${role.id}` : `/roles/${role.id}`" :class="$style.root" tabindex="-1" :style="{ '--color': role.color }">
<template v-if="forModeration">
- <i v-if="role.isPublic" class="ti ti-world" :class="$style.icon" style="color: var(--success)"></i>
- <i v-else class="ti ti-lock" :class="$style.icon" style="color: var(--warn)"></i>
+ <i v-if="role.isPublic" class="ti ti-world" :class="$style.icon" style="color: var(--MI_THEME-success)"></i>
+ <i v-else class="ti ti-lock" :class="$style.icon" style="color: var(--MI_THEME-warn)"></i>
</template>
<div v-adaptive-bg class="_panel" :class="$style.body">
@@ -17,8 +17,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<img :class="$style.bodyBadge" :src="role.iconUrl"/>
</template>
<template v-else>
- <i v-if="role.isAdministrator" class="ti ti-crown" style="color: var(--accent);"></i>
- <i v-else-if="role.isModerator" class="ti ti-shield" style="color: var(--accent);"></i>
+ <i v-if="role.isAdministrator" class="ti ti-crown" style="color: var(--MI_THEME-accent);"></i>
+ <i v-else-if="role.isModerator" class="ti ti-shield" style="color: var(--MI_THEME-accent);"></i>
<i v-else class="ti ti-user" style="opacity: 0.7;"></i>
</template>
</span>
diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue
index 150a5c6d54..154fff6d2f 100644
--- a/packages/frontend/src/components/MkSelect.vue
+++ b/packages/frontend/src/components/MkSelect.vue
@@ -202,7 +202,7 @@ function show() {
.caption {
font-size: 0.85em;
padding: 8px 0 0 0;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
&:empty {
display: none;
@@ -220,8 +220,8 @@ function show() {
&.focused {
> .inputCore {
- border-color: var(--accent) !important;
- //box-shadow: 0 0 0 4px var(--focus);
+ border-color: var(--MI_THEME-accent) !important;
+ //box-shadow: 0 0 0 4px var(--MI_THEME-focus);
}
}
@@ -240,7 +240,7 @@ function show() {
&:hover {
> .inputCore {
- border-color: var(--inputBorderHover) !important;
+ border-color: var(--MI_THEME-inputBorderHover) !important;
}
}
}
@@ -256,9 +256,9 @@ function show() {
font: inherit;
font-weight: normal;
font-size: 1em;
- color: var(--fg);
- background: var(--panel);
- border: solid 1px var(--panel);
+ color: var(--MI_THEME-fg);
+ background: var(--MI_THEME-panel);
+ border: solid 1px var(--MI_THEME-panel);
border-radius: var(--radius-sm);
outline: none;
box-shadow: none;
diff --git a/packages/frontend/src/components/MkSignin.input.vue b/packages/frontend/src/components/MkSignin.input.vue
new file mode 100644
index 0000000000..34c22abc31
--- /dev/null
+++ b/packages/frontend/src/components/MkSignin.input.vue
@@ -0,0 +1,206 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :class="$style.wrapper" data-cy-signin-page-input>
+ <div :class="$style.root">
+ <div :class="$style.avatar">
+ <i class="ti ti-user"></i>
+ </div>
+
+ <!-- ログイン画面メッセージ -->
+ <MkInfo v-if="message">
+ {{ message }}
+ </MkInfo>
+
+ <!-- 外部サーバーへの転送 -->
+ <div v-if="openOnRemote" class="_gaps_m">
+ <div class="_gaps_s">
+ <MkButton type="button" rounded primary style="margin: 0 auto;" @click="openRemote(openOnRemote)">
+ {{ i18n.ts.continueOnRemote }} <i class="ti ti-external-link"></i>
+ </MkButton>
+ <button type="button" class="_button" :class="$style.instanceManualSelectButton" @click="specifyHostAndOpenRemote(openOnRemote)">
+ {{ i18n.ts.specifyServerHost }}
+ </button>
+ </div>
+ <div :class="$style.orHr">
+ <p :class="$style.orMsg">{{ i18n.ts.or }}</p>
+ </div>
+ </div>
+
+ <!-- username入力 -->
+ <form class="_gaps_s" @submit.prevent="emit('usernameSubmitted', username)">
+ <MkInput v-model="username" :placeholder="i18n.ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autocomplete="username webauthn" autofocus required data-cy-signin-username>
+ <template #prefix>@</template>
+ <template #suffix>@{{ host }}</template>
+ </MkInput>
+ <MkButton type="submit" large primary rounded style="margin: 0 auto;" data-cy-signin-page-input-continue>{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
+ </form>
+
+ <!-- パスワードレスログイン -->
+ <div :class="$style.orHr">
+ <p :class="$style.orMsg">{{ i18n.ts.or }}</p>
+ </div>
+ <div>
+ <MkButton type="submit" style="margin: auto auto;" large rounded primary gradate @click="emit('passkeyClick', $event)">
+ <i class="ti ti-device-usb" style="font-size: medium;"></i>{{ i18n.ts.signinWithPasskey }}
+ </MkButton>
+ </div>
+ </div>
+</div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue';
+import { toUnicode } from 'punycode/';
+
+import { query, extractDomain } from '@@/js/url.js';
+import { host as configHost } from '@@/js/config.js';
+import type { OpenOnRemoteOptions } from '@/scripts/please-login.js';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
+
+import MkButton from '@/components/MkButton.vue';
+import MkInput from '@/components/MkInput.vue';
+import MkInfo from '@/components/MkInfo.vue';
+
+const props = withDefaults(defineProps<{
+ message?: string,
+ openOnRemote?: OpenOnRemoteOptions,
+}>(), {
+ message: '',
+ openOnRemote: undefined,
+});
+
+const emit = defineEmits<{
+ (ev: 'usernameSubmitted', v: string): void;
+ (ev: 'passkeyClick', v: MouseEvent): void;
+}>();
+
+const host = toUnicode(configHost);
+
+const username = ref('');
+
+//#region Open on remote
+function openRemote(options: OpenOnRemoteOptions, targetHost?: string): void {
+ switch (options.type) {
+ case 'web':
+ case 'lookup': {
+ let _path: string;
+
+ if (options.type === 'lookup') {
+ // TODO: v2024.7.0以降が浸透してきたら正式なURLに変更する▼
+ // _path = `/lookup?uri=${encodeURIComponent(_path)}`;
+ _path = `/authorize-follow?acct=${encodeURIComponent(options.url)}`;
+ } else {
+ _path = options.path;
+ }
+
+ if (targetHost) {
+ window.open(`https://${targetHost}${_path}`, '_blank', 'noopener');
+ } else {
+ window.open(`https://misskey-hub.net/mi-web/?path=${encodeURIComponent(_path)}`, '_blank', 'noopener');
+ }
+ break;
+ }
+ case 'share': {
+ const params = query(options.params);
+ if (targetHost) {
+ window.open(`https://${targetHost}/share?${params}`, '_blank', 'noopener');
+ } else {
+ window.open(`https://misskey-hub.net/share/?${params}`, '_blank', 'noopener');
+ }
+ break;
+ }
+ }
+}
+
+async function specifyHostAndOpenRemote(options: OpenOnRemoteOptions): Promise<void> {
+ const { canceled, result: hostTemp } = await os.inputText({
+ title: i18n.ts.inputHostName,
+ placeholder: 'misskey.example.com',
+ });
+
+ if (canceled) return;
+
+ let targetHost: string | null = hostTemp;
+
+ // ドメイン部分だけを取り出す
+ targetHost = extractDomain(targetHost ?? '');
+ if (targetHost == null) {
+ os.alert({
+ type: 'error',
+ title: i18n.ts.invalidValue,
+ text: i18n.ts.tryAgain,
+ });
+ return;
+ }
+ openRemote(options, targetHost);
+}
+//#endregion
+</script>
+
+<style lang="scss" module>
+.root {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+
+.wrapper {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ min-height: 336px;
+
+ > .root {
+ width: 100%;
+ }
+}
+
+.avatar {
+ margin: 0 auto;
+ background-color: color-mix(in srgb, var(--MI_THEME-fg), transparent 85%);
+ color: color-mix(in srgb, var(--MI_THEME-fg), transparent 25%);
+ text-align: center;
+ height: 64px;
+ width: 64px;
+ font-size: 24px;
+ line-height: 64px;
+ border-radius: 50%;
+}
+
+.instanceManualSelectButton {
+ display: block;
+ text-align: center;
+ opacity: .7;
+ font-size: .8em;
+
+ &:hover {
+ text-decoration: underline;
+ }
+}
+
+.orHr {
+ position: relative;
+ margin: .4em auto;
+ width: 100%;
+ height: 1px;
+ background: var(--MI_THEME-divider);
+}
+
+.orMsg {
+ position: absolute;
+ top: -.6em;
+ display: inline-block;
+ padding: 0 1em;
+ background: var(--MI_THEME-panel);
+ font-size: 0.8em;
+ color: var(--MI_THEME-fgOnPanel);
+ margin: 0;
+ left: 50%;
+ transform: translateX(-50%);
+}
+</style>
diff --git a/packages/frontend/src/components/MkSignin.passkey.vue b/packages/frontend/src/components/MkSignin.passkey.vue
new file mode 100644
index 0000000000..e5a56ab66d
--- /dev/null
+++ b/packages/frontend/src/components/MkSignin.passkey.vue
@@ -0,0 +1,92 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :class="$style.wrapper">
+ <div class="_gaps" :class="$style.root">
+ <div class="_gaps_s">
+ <div :class="$style.passkeyIcon">
+ <i class="ti ti-fingerprint"></i>
+ </div>
+ <div :class="$style.passkeyDescription">{{ i18n.ts.useSecurityKey }}</div>
+ </div>
+
+ <MkButton large primary rounded :disabled="queryingKey" style="margin: 0 auto;" @click="queryKey">{{ i18n.ts.retry }}</MkButton>
+
+ <MkButton v-if="isPerformingPasswordlessLogin !== true" transparent rounded :disabled="queryingKey" style="margin: 0 auto;" @click="emit('useTotp')">{{ i18n.ts.useTotp }}</MkButton>
+ </div>
+</div>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted } from 'vue';
+import { get as webAuthnRequest } from '@github/webauthn-json/browser-ponyfill';
+
+import { i18n } from '@/i18n.js';
+
+import MkButton from '@/components/MkButton.vue';
+
+import type { AuthenticationPublicKeyCredential } from '@github/webauthn-json/browser-ponyfill';
+
+const props = defineProps<{
+ credentialRequest: CredentialRequestOptions;
+ isPerformingPasswordlessLogin?: boolean;
+}>();
+
+const emit = defineEmits<{
+ (ev: 'done', credential: AuthenticationPublicKeyCredential): void;
+ (ev: 'useTotp'): void;
+}>();
+
+const queryingKey = ref(true);
+
+async function queryKey() {
+ queryingKey.value = true;
+ await webAuthnRequest(props.credentialRequest)
+ .catch(() => {
+ return Promise.reject(null);
+ })
+ .then((credential) => {
+ emit('done', credential);
+ })
+ .finally(() => {
+ queryingKey.value = false;
+ });
+}
+
+onMounted(() => {
+ queryKey();
+});
+</script>
+
+<style lang="scss" module>
+.wrapper {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ min-height: 336px;
+
+ > .root {
+ width: 100%;
+ }
+}
+
+.passkeyIcon {
+ margin: 0 auto;
+ background-color: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
+ text-align: center;
+ height: 64px;
+ width: 64px;
+ font-size: 24px;
+ line-height: 64px;
+ border-radius: 50%;
+}
+
+.passkeyDescription {
+ text-align: center;
+ font-size: 1.1em;
+}
+</style>
diff --git a/packages/frontend/src/components/MkSignin.password.vue b/packages/frontend/src/components/MkSignin.password.vue
new file mode 100644
index 0000000000..5608122a39
--- /dev/null
+++ b/packages/frontend/src/components/MkSignin.password.vue
@@ -0,0 +1,188 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :class="$style.wrapper" data-cy-signin-page-password>
+ <div class="_gaps" :class="$style.root">
+ <div :class="$style.avatar" :style="{ backgroundImage: user ? `url('${user.avatarUrl}')` : undefined }"></div>
+ <div :class="$style.welcomeBackMessage">
+ <I18n :src="i18n.ts.welcomeBackWithName" tag="span">
+ <template #name><Mfm :text="user.name ?? user.username" :plain="true"/></template>
+ </I18n>
+ </div>
+
+ <!-- password入力 -->
+ <form class="_gaps_s" @submit.prevent="onSubmit">
+ <!-- ブラウザ オートコンプリート用 -->
+ <input type="hidden" name="username" autocomplete="username" :value="user.username">
+
+ <MkInput v-model="password" :placeholder="i18n.ts.password" type="password" autocomplete="current-password webauthn" :withPasswordToggle="true" required autofocus data-cy-signin-password>
+ <template #prefix><i class="ti ti-lock"></i></template>
+ <template #caption><button class="_textButton" type="button" @click="resetPassword">{{ i18n.ts.forgotPassword }}</button></template>
+ </MkInput>
+
+ <div v-if="needCaptcha">
+ <MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :class="$style.captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/>
+ <MkCaptcha v-if="instance.enableMcaptcha" ref="mcaptcha" v-model="mCaptchaResponse" :class="$style.captcha" provider="mcaptcha" :sitekey="instance.mcaptchaSiteKey" :instanceUrl="instance.mcaptchaInstanceUrl"/>
+ <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" :class="$style.captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/>
+ <MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" :class="$style.captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/>
+ <MkCaptcha v-if="instance.enableTestcaptcha" ref="testcaptcha" v-model="testcaptchaResponse" :class="$style.captcha" provider="testcaptcha"/>
+ </div>
+
+ <MkButton type="submit" :disabled="needCaptcha && captchaFailed" large primary rounded style="margin: 0 auto;" data-cy-signin-page-password-continue>{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
+ </form>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+export type PwResponse = {
+ password: string;
+ captcha: {
+ hCaptchaResponse: string | null;
+ mCaptchaResponse: string | null;
+ reCaptchaResponse: string | null;
+ turnstileResponse: string | null;
+ testcaptchaResponse: string | null;
+ };
+};
+</script>
+
+<script setup lang="ts">
+import { ref, computed, useTemplateRef, defineAsyncComponent } from 'vue';
+import * as Misskey from 'misskey-js';
+
+import { instance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
+
+import MkButton from '@/components/MkButton.vue';
+import MkInput from '@/components/MkInput.vue';
+import MkCaptcha from '@/components/MkCaptcha.vue';
+
+const props = defineProps<{
+ user: Misskey.entities.UserDetailed;
+ needCaptcha: boolean;
+}>();
+
+const emit = defineEmits<{
+ (ev: 'passwordSubmitted', v: PwResponse): void;
+}>();
+
+const password = ref('');
+
+const hCaptcha = useTemplateRef('hcaptcha');
+const mCaptcha = useTemplateRef('mcaptcha');
+const reCaptcha = useTemplateRef('recaptcha');
+const turnstile = useTemplateRef('turnstile');
+const testcaptcha = useTemplateRef('testcaptcha');
+
+const hCaptchaResponse = ref<string | null>(null);
+const mCaptchaResponse = ref<string | null>(null);
+const reCaptchaResponse = ref<string | null>(null);
+const turnstileResponse = ref<string | null>(null);
+const testcaptchaResponse = ref<string | null>(null);
+
+const captchaFailed = computed((): boolean => {
+ return (
+ (instance.enableHcaptcha && !hCaptchaResponse.value) ||
+ (instance.enableMcaptcha && !mCaptchaResponse.value) ||
+ (instance.enableRecaptcha && !reCaptchaResponse.value) ||
+ (instance.enableTurnstile && !turnstileResponse.value) ||
+ (instance.enableTestcaptcha && !testcaptchaResponse.value)
+ );
+});
+
+function resetPassword(): void {
+ const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkForgotPassword.vue')), {}, {
+ closed: () => dispose(),
+ });
+}
+
+function onSubmit() {
+ emit('passwordSubmitted', {
+ password: password.value,
+ captcha: {
+ hCaptchaResponse: hCaptchaResponse.value,
+ mCaptchaResponse: mCaptchaResponse.value,
+ reCaptchaResponse: reCaptchaResponse.value,
+ turnstileResponse: turnstileResponse.value,
+ testcaptchaResponse: testcaptchaResponse.value,
+ },
+ });
+}
+
+function resetCaptcha() {
+ hCaptcha.value?.reset();
+ mCaptcha.value?.reset();
+ reCaptcha.value?.reset();
+ turnstile.value?.reset();
+ testcaptcha.value?.reset();
+}
+
+defineExpose({
+ resetCaptcha,
+});
+</script>
+
+<style lang="scss" module>
+.wrapper {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ min-height: 336px;
+
+ > .root {
+ width: 100%;
+ }
+}
+
+.avatar {
+ margin: 0 auto 0 auto;
+ width: 64px;
+ height: 64px;
+ background: #ddd;
+ background-position: center;
+ background-size: cover;
+ border-radius: 100%;
+}
+
+.welcomeBackMessage {
+ text-align: center;
+ font-size: 1.1em;
+}
+
+.instanceManualSelectButton {
+ display: block;
+ text-align: center;
+ opacity: .7;
+ font-size: .8em;
+
+ &:hover {
+ text-decoration: underline;
+ }
+}
+
+.orHr {
+ position: relative;
+ margin: .4em auto;
+ width: 100%;
+ height: 1px;
+ background: var(--MI_THEME-divider);
+}
+
+.orMsg {
+ position: absolute;
+ top: -.6em;
+ display: inline-block;
+ padding: 0 1em;
+ background: var(--MI_THEME-panel);
+ font-size: 0.8em;
+ color: var(--MI_THEME-fgOnPanel);
+ margin: 0;
+ left: 50%;
+ transform: translateX(-50%);
+}
+</style>
diff --git a/packages/frontend/src/components/MkSignin.totp.vue b/packages/frontend/src/components/MkSignin.totp.vue
new file mode 100644
index 0000000000..670b8057c2
--- /dev/null
+++ b/packages/frontend/src/components/MkSignin.totp.vue
@@ -0,0 +1,74 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :class="$style.wrapper">
+ <div class="_gaps" :class="$style.root">
+ <div class="_gaps_s">
+ <div :class="$style.totpIcon">
+ <i class="ti ti-key"></i>
+ </div>
+ <div :class="$style.totpDescription">{{ i18n.ts['2fa'] }}</div>
+ </div>
+
+ <!-- totp入力 -->
+ <form class="_gaps_s" @submit.prevent="emit('totpSubmitted', token)">
+ <MkInput v-model="token" type="text" :pattern="isBackupCode ? '^[A-Z0-9]{32}$' :'^[0-9]{6}$'" autocomplete="one-time-code" required autofocus :spellcheck="false" :inputmode="isBackupCode ? undefined : 'numeric'">
+ <template #label>{{ i18n.ts.token }} ({{ i18n.ts['2fa'] }})</template>
+ <template #prefix><i v-if="isBackupCode" class="ti ti-key"></i><i v-else class="ti ti-123"></i></template>
+ <template #caption><button class="_textButton" type="button" @click="isBackupCode = !isBackupCode">{{ isBackupCode ? i18n.ts.useTotp : i18n.ts.useBackupCode }}</button></template>
+ </MkInput>
+
+ <MkButton type="submit" large primary rounded style="margin: 0 auto;">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
+ </form>
+ </div>
+</div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue';
+
+import { i18n } from '@/i18n.js';
+
+import MkButton from '@/components/MkButton.vue';
+import MkInput from '@/components/MkInput.vue';
+
+const emit = defineEmits<{
+ (ev: 'totpSubmitted', token: string): void;
+}>();
+
+const token = ref('');
+const isBackupCode = ref(false);
+</script>
+
+<style lang="scss" module>
+.wrapper {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ min-height: 336px;
+
+ > .root {
+ width: 100%;
+ }
+}
+
+.totpIcon {
+ margin: 0 auto;
+ background-color: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
+ text-align: center;
+ height: 64px;
+ width: 64px;
+ font-size: 24px;
+ line-height: 64px;
+ border-radius: 50%;
+}
+
+.totpDescription {
+ text-align: center;
+ font-size: 1.1em;
+}
+</style>
diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue
index 82e0df8a01..4a6219071b 100644
--- a/packages/frontend/src/components/MkSignin.vue
+++ b/packages/frontend/src/components/MkSignin.vue
@@ -4,245 +4,290 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<form :class="{ signing, totpLogin }" @submit.prevent="onSubmit">
- <div class="_gaps_m">
- <div v-show="withAvatar" :class="$style.avatar" :style="{ backgroundImage: user ? `url('${user.avatarUrl}')` : undefined, marginBottom: message ? '1.5em' : undefined }"></div>
- <MkInfo v-if="message">
- {{ message }}
- </MkInfo>
- <div v-if="openOnRemote" class="_gaps_m">
- <div class="_gaps_s">
- <MkButton type="button" rounded primary style="margin: 0 auto;" @click="openRemote(openOnRemote)">
- {{ i18n.ts.continueOnRemote }} <i class="ti ti-external-link"></i>
- </MkButton>
- <button type="button" class="_button" :class="$style.instanceManualSelectButton" @click="specifyHostAndOpenRemote(openOnRemote)">
- {{ i18n.ts.specifyServerHost }}
- </button>
- </div>
- <div :class="$style.orHr">
- <p :class="$style.orMsg">{{ i18n.ts.or }}</p>
- </div>
- </div>
- <div v-if="!totpLogin" class="normal-signin _gaps_m">
- <MkInput v-model="username" :placeholder="i18n.ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autocomplete="username webauthn" autofocus required data-cy-signin-username @update:modelValue="onUsernameChange">
- <template #prefix>@</template>
- <template #suffix>@{{ host }}</template>
- </MkInput>
- <MkInput v-model="password" :placeholder="i18n.ts.password" type="password" autocomplete="current-password webauthn" :withPasswordToggle="true" required data-cy-signin-password>
- <template #prefix><i class="ti ti-lock"></i></template>
- <template #caption><button class="_textButton" type="button" @click="resetPassword">{{ i18n.ts.forgotPassword }}</button></template>
- </MkInput>
- <MkButton type="submit" large primary rounded :disabled="signing" style="margin: 0 auto;">{{ signing ? i18n.ts.loggingIn : i18n.ts.login }}</MkButton>
- </div>
- <div v-if="totpLogin" class="2fa-signin" :class="{ securityKeys: user && user.securityKeys }">
- <div v-if="user && user.securityKeys" class="twofa-group tap-group">
- <p>{{ i18n.ts.useSecurityKey }}</p>
- <MkButton v-if="!queryingKey" @click="query2FaKey">
- {{ i18n.ts.retry }}
- </MkButton>
- </div>
- <div v-if="user && user.securityKeys" :class="$style.orHr">
- <p :class="$style.orMsg">{{ i18n.ts.or }}</p>
- </div>
- <div class="twofa-group totp-group _gaps">
- <MkInput v-model="token" type="text" :pattern="isBackupCode ? '^[A-Z0-9]{32}$' :'^[0-9]{6}$'" autocomplete="one-time-code" required :spellcheck="false" :inputmode="isBackupCode ? undefined : 'numeric'">
- <template #label>{{ i18n.ts.token }} ({{ i18n.ts['2fa'] }})</template>
- <template #prefix><i v-if="isBackupCode" class="ti ti-key"></i><i v-else class="ti ti-123"></i></template>
- <template #caption><button class="_textButton" type="button" @click="isBackupCode = !isBackupCode">{{ isBackupCode ? i18n.ts.useTotp : i18n.ts.useBackupCode }}</button></template>
- </MkInput>
- <MkButton type="submit" :disabled="signing" large primary rounded style="margin: 0 auto;">{{ signing ? i18n.ts.loggingIn : i18n.ts.login }}</MkButton>
- </div>
- </div>
- <div v-if="!totpLogin && usePasswordLessLogin" :class="$style.orHr">
- <p :class="$style.orMsg">{{ i18n.ts.or }}</p>
- </div>
- <div v-if="!totpLogin && usePasswordLessLogin" class="twofa-group tap-group">
- <MkButton v-if="!queryingKey" type="submit" :disabled="signing" style="margin: auto auto;" rounded large primary @click="onPasskeyLogin">
- <i class="ti ti-device-usb" style="font-size: medium;"></i>
- {{ signing ? i18n.ts.loggingIn : i18n.ts.signinWithPasskey }}
- </MkButton>
- <p v-if="queryingKey">{{ i18n.ts.useSecurityKey }}</p>
- </div>
+<div :class="$style.signinRoot">
+ <Transition
+ mode="out-in"
+ :enterActiveClass="$style.transition_enterActive"
+ :leaveActiveClass="$style.transition_leaveActive"
+ :enterFromClass="$style.transition_enterFrom"
+ :leaveToClass="$style.transition_leaveTo"
+
+ :inert="waiting"
+ >
+ <!-- 1. 外部サーバーへの転送・username入力・パスキー -->
+ <XInput
+ v-if="page === 'input'"
+ key="input"
+ :message="message"
+ :openOnRemote="openOnRemote"
+
+ @usernameSubmitted="onUsernameSubmitted"
+ @passkeyClick="onPasskeyLogin"
+ />
+
+ <!-- 2. パスワード入力 -->
+ <XPassword
+ v-else-if="page === 'password'"
+ key="password"
+ ref="passwordPageEl"
+
+ :user="userInfo!"
+ :needCaptcha="needCaptcha"
+
+ @passwordSubmitted="onPasswordSubmitted"
+ />
+
+ <!-- 3. ワンタイムパスワード -->
+ <XTotp
+ v-else-if="page === 'totp'"
+ key="totp"
+
+ @totpSubmitted="onTotpSubmitted"
+ />
+
+ <!-- 4. パスキー -->
+ <XPasskey
+ v-else-if="page === 'passkey'"
+ key="passkey"
+
+ :credentialRequest="credentialRequest!"
+ :isPerformingPasswordlessLogin="doingPasskeyFromInputPage"
+
+ @done="onPasskeyDone"
+ @useTotp="onUseTotp"
+ />
+ </Transition>
+ <div v-if="waiting" :class="$style.waitingRoot">
+ <MkLoading/>
</div>
-</form>
+</div>
</template>
-<script lang="ts" setup>
-import { defineAsyncComponent, ref } from 'vue';
-import { toUnicode } from 'punycode/';
+<script setup lang="ts">
+import { nextTick, onBeforeUnmount, ref, shallowRef, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js';
-import { supported as webAuthnSupported, get as webAuthnRequest, parseRequestOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill';
-import { SigninWithPasskeyResponse } from 'misskey-js/entities.js';
-import { query, extractDomain } from '@@/js/url.js';
-import { host as configHost } from '@@/js/config.js';
-import MkDivider from './MkDivider.vue';
+import { supported as webAuthnSupported, parseRequestOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill';
+
+import type { AuthenticationPublicKeyCredential } from '@github/webauthn-json/browser-ponyfill';
import type { OpenOnRemoteOptions } from '@/scripts/please-login.js';
-import { showSuspendedDialog } from '@/scripts/show-suspended-dialog.js';
-import MkButton from '@/components/MkButton.vue';
-import MkInput from '@/components/MkInput.vue';
-import MkInfo from '@/components/MkInfo.vue';
-import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
+import { showSuspendedDialog } from '@/scripts/show-suspended-dialog.js';
import { login } from '@/account.js';
import { i18n } from '@/i18n.js';
import { showSystemAccountDialog } from '@/scripts/show-system-account-dialog.js';
+import * as os from '@/os.js';
-const signing = ref(false);
-const user = ref<Misskey.entities.UserDetailed | null>(null);
-const usePasswordLessLogin = ref<Misskey.entities.UserDetailed['usePasswordLessLogin']>(true);
-const username = ref('');
-const password = ref('');
-const token = ref('');
-const host = ref(toUnicode(configHost));
-const totpLogin = ref(false);
-const isBackupCode = ref(false);
-const queryingKey = ref(false);
-let credentialRequest: CredentialRequestOptions | null = null;
-const passkey_context = ref('');
+import XInput from '@/components/MkSignin.input.vue';
+import XPassword, { type PwResponse } from '@/components/MkSignin.password.vue';
+import XTotp from '@/components/MkSignin.totp.vue';
+import XPasskey from '@/components/MkSignin.passkey.vue';
const emit = defineEmits<{
- (ev: 'login', v: any): void;
+ (ev: 'login', v: Misskey.entities.SigninFlowResponse & { finished: true }): void;
}>();
const props = withDefaults(defineProps<{
- withAvatar?: boolean;
autoSet?: boolean;
message?: string,
openOnRemote?: OpenOnRemoteOptions,
}>(), {
- withAvatar: true,
autoSet: false,
message: '',
openOnRemote: undefined,
});
-function onUsernameChange(): void {
- const usernameRequested = username.value;
- misskeyApi('users/show', {
- username: usernameRequested,
- }).then(userResponse => {
- if (userResponse.username === username.value) {
- user.value = userResponse;
- usePasswordLessLogin.value = userResponse.usePasswordLessLogin;
- }
- }, () => {
- if (usernameRequested === username.value) {
- user.value = null;
- usePasswordLessLogin.value = true;
- }
- });
-}
+const page = ref<'input' | 'password' | 'totp' | 'passkey'>('input');
+const waiting = ref(false);
-function onLogin(res: any): Promise<void> | void {
- if (props.autoSet) {
- return login(res.i);
- }
-}
+const passwordPageEl = useTemplateRef('passwordPageEl');
+const needCaptcha = ref(false);
-async function query2FaKey(): Promise<void> {
- if (credentialRequest == null) return;
- queryingKey.value = true;
- await webAuthnRequest(credentialRequest)
- .catch(() => {
- queryingKey.value = false;
- return Promise.reject(null);
- }).then(credential => {
- credentialRequest = null;
- queryingKey.value = false;
- signing.value = true;
- return misskeyApi('signin', {
- username: username.value,
- password: password.value,
- credential: credential.toJSON(),
- });
- }).then(res => {
- emit('login', res);
- return onLogin(res);
- }).catch(err => {
- if (err === null) return;
- os.alert({
- type: 'error',
- text: i18n.ts.signinFailed,
- });
- signing.value = false;
- });
-}
+const userInfo = ref<null | Misskey.entities.UserDetailed>(null);
+const password = ref('');
+
+//#region Passkey Passwordless
+const credentialRequest = shallowRef<CredentialRequestOptions | null>(null);
+const passkeyContext = ref('');
+const doingPasskeyFromInputPage = ref(false);
function onPasskeyLogin(): void {
- signing.value = true;
if (webAuthnSupported()) {
+ doingPasskeyFromInputPage.value = true;
+ waiting.value = true;
misskeyApi('signin-with-passkey', {})
- .then((res: SigninWithPasskeyResponse) => {
- totpLogin.value = false;
- signing.value = false;
- queryingKey.value = true;
- passkey_context.value = res.context ?? '';
- credentialRequest = parseRequestOptionsFromJSON({
+ .then((res) => {
+ passkeyContext.value = res.context ?? '';
+ credentialRequest.value = parseRequestOptionsFromJSON({
publicKey: res.option,
});
+
+ page.value = 'passkey';
+ waiting.value = false;
})
- .then(() => queryPasskey())
- .catch(loginFailed);
+ .catch(onSigninApiError);
}
}
-async function queryPasskey(): Promise<void> {
- if (credentialRequest == null) return;
- queryingKey.value = true;
- console.log('Waiting passkey auth...');
- await webAuthnRequest(credentialRequest)
- .catch((err) => {
- console.warn('Passkey Auth fail!: ', err);
- queryingKey.value = false;
- return Promise.reject(null);
- }).then(credential => {
- credentialRequest = null;
- queryingKey.value = false;
- signing.value = true;
- return misskeyApi('signin-with-passkey', {
- credential: credential.toJSON(),
- context: passkey_context.value,
- });
- }).then((res: SigninWithPasskeyResponse) => {
+function onPasskeyDone(credential: AuthenticationPublicKeyCredential): void {
+ waiting.value = true;
+
+ if (doingPasskeyFromInputPage.value) {
+ misskeyApi('signin-with-passkey', {
+ credential: credential.toJSON(),
+ context: passkeyContext.value,
+ }).then((res) => {
+ if (res.signinResponse == null) {
+ onSigninApiError();
+ return;
+ }
emit('login', res.signinResponse);
- return onLogin(res.signinResponse);
+ }).catch(onSigninApiError);
+ } else if (userInfo.value != null) {
+ tryLogin({
+ username: userInfo.value.username,
+ password: password.value,
+ credential: credential.toJSON(),
});
+ }
}
-function onSubmit(): void {
- signing.value = true;
- if (!totpLogin.value && user.value && user.value.twoFactorEnabled) {
- if (webAuthnSupported() && user.value.securityKeys) {
- misskeyApi('signin', {
- username: username.value,
- password: password.value,
- }).then(res => {
- totpLogin.value = true;
- signing.value = false;
- credentialRequest = parseRequestOptionsFromJSON({
- publicKey: res,
- });
- })
- .then(() => query2FaKey())
- .catch(loginFailed);
- } else {
- totpLogin.value = true;
- signing.value = false;
- }
+function onUseTotp(): void {
+ page.value = 'totp';
+}
+//#endregion
+
+async function onUsernameSubmitted(username: string) {
+ waiting.value = true;
+
+ userInfo.value = await misskeyApi('users/show', {
+ username,
+ }).catch(() => null);
+
+ await tryLogin({
+ username,
+ });
+}
+
+async function onPasswordSubmitted(pw: PwResponse) {
+ waiting.value = true;
+ password.value = pw.password;
+
+ if (userInfo.value == null) {
+ await os.alert({
+ type: 'error',
+ title: i18n.ts.noSuchUser,
+ text: i18n.ts.signinFailed,
+ });
+ waiting.value = false;
+ return;
+ } else {
+ await tryLogin({
+ username: userInfo.value.username,
+ password: pw.password,
+ 'hcaptcha-response': pw.captcha.hCaptchaResponse,
+ 'm-captcha-response': pw.captcha.mCaptchaResponse,
+ 'g-recaptcha-response': pw.captcha.reCaptchaResponse,
+ 'frc-captcha-solution': pw.captcha.fcResponse,
+ 'turnstile-response': pw.captcha.turnstileResponse,
+ 'testcaptcha-response': pw.captcha.testcaptchaResponse,
+ });
+ }
+}
+
+async function onTotpSubmitted(token: string) {
+ waiting.value = true;
+
+ if (userInfo.value == null) {
+ await os.alert({
+ type: 'error',
+ title: i18n.ts.noSuchUser,
+ text: i18n.ts.signinFailed,
+ });
+ waiting.value = false;
+ return;
} else {
- misskeyApi('signin', {
- username: username.value,
+ await tryLogin({
+ username: userInfo.value.username,
password: password.value,
- token: user.value?.twoFactorEnabled ? token.value : undefined,
- }).then(res => {
+ token,
+ });
+ }
+}
+
+async function tryLogin(req: Partial<Misskey.entities.SigninFlowRequest>): Promise<Misskey.entities.SigninFlowResponse> {
+ const _req = {
+ username: req.username ?? userInfo.value?.username,
+ ...req,
+ };
+
+ function assertIsSigninFlowRequest(x: Partial<Misskey.entities.SigninFlowRequest>): x is Misskey.entities.SigninFlowRequest {
+ return x.username != null;
+ }
+
+ if (!assertIsSigninFlowRequest(_req)) {
+ throw new Error('Invalid request');
+ }
+
+ return await misskeyApi('signin-flow', _req).then(async (res) => {
+ if (res.finished) {
emit('login', res);
- onLogin(res);
- }).catch(loginFailed);
+ await onLoginSucceeded(res);
+ } else {
+ switch (res.next) {
+ case 'captcha': {
+ needCaptcha.value = true;
+ page.value = 'password';
+ break;
+ }
+ case 'password': {
+ needCaptcha.value = false;
+ page.value = 'password';
+ break;
+ }
+ case 'totp': {
+ page.value = 'totp';
+ break;
+ }
+ case 'passkey': {
+ if (webAuthnSupported()) {
+ credentialRequest.value = parseRequestOptionsFromJSON({
+ publicKey: res.authRequest,
+ });
+ page.value = 'passkey';
+ } else {
+ page.value = 'totp';
+ }
+ break;
+ }
+ }
+
+ if (doingPasskeyFromInputPage.value === true) {
+ doingPasskeyFromInputPage.value = false;
+ page.value = 'input';
+ password.value = '';
+ }
+ passwordPageEl.value?.resetCaptcha();
+ nextTick(() => {
+ waiting.value = false;
+ });
+ }
+ return res;
+ }).catch((err) => {
+ onSigninApiError(err);
+ return Promise.reject(err);
+ });
+}
+
+async function onLoginSucceeded(res: Misskey.entities.SigninFlowResponse & { finished: true }) {
+ if (props.autoSet) {
+ await login(res.i);
}
}
-function loginFailed(err: any): void {
- switch (err.id) {
+function onSigninApiError(err?: any): void {
+ const id = err?.id ?? null;
+
+ switch (id) {
case '6cc579cc-885d-43d8-95c2-b8c7fc963280': {
os.alert({
type: 'error',
@@ -275,6 +320,14 @@ function loginFailed(err: any): void {
});
break;
}
+ case 'cdf1235b-ac71-46d4-a3a6-84ccce48df6f': {
+ os.alert({
+ type: 'error',
+ title: i18n.ts.loginFailed,
+ text: i18n.ts.incorrectTotp,
+ });
+ break;
+ }
case '36b96a7d-b547-412d-aeed-2d611cdc8cdc': {
os.alert({
type: 'error',
@@ -283,6 +336,14 @@ function loginFailed(err: any): void {
});
break;
}
+ case '93b86c4b-72f9-40eb-9815-798928603d1e': {
+ os.alert({
+ type: 'error',
+ title: i18n.ts.loginFailed,
+ text: i18n.ts.passkeyVerificationFailed,
+ });
+ break;
+ }
case 'b18c89a7-5b5e-4cec-bb5b-0419f332d430': {
os.alert({
type: 'error',
@@ -309,113 +370,55 @@ function loginFailed(err: any): void {
}
}
- totpLogin.value = false;
- signing.value = false;
-}
-
-function resetPassword(): void {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkForgotPassword.vue')), {}, {
- closed: () => dispose(),
- });
-}
-
-function openRemote(options: OpenOnRemoteOptions, targetHost?: string): void {
- switch (options.type) {
- case 'web':
- case 'lookup': {
- let _path: string;
-
- if (options.type === 'lookup') {
- // TODO: v2024.7.0以降が浸透してきたら正式なURLに変更する▼
- // _path = `/lookup?uri=${encodeURIComponent(_path)}`;
- _path = `/authorize-follow?acct=${encodeURIComponent(options.url)}`;
- } else {
- _path = options.path;
- }
-
- if (targetHost) {
- window.open(`https://${targetHost}${_path}`, '_blank', 'noopener');
- } else {
- window.open(`https://misskey-hub.net/mi-web/?path=${encodeURIComponent(_path)}`, '_blank', 'noopener');
- }
- break;
- }
- case 'share': {
- const params = query(options.params);
- if (targetHost) {
- window.open(`https://${targetHost}/share?${params}`, '_blank', 'noopener');
- } else {
- window.open(`https://misskey-hub.net/share/?${params}`, '_blank', 'noopener');
- }
- break;
- }
+ if (doingPasskeyFromInputPage.value === true) {
+ doingPasskeyFromInputPage.value = false;
+ page.value = 'input';
+ password.value = '';
}
-}
-
-async function specifyHostAndOpenRemote(options: OpenOnRemoteOptions): Promise<void> {
- const { canceled, result: hostTemp } = await os.inputText({
- title: i18n.ts.inputHostName,
- placeholder: 'misskey.example.com',
+ passwordPageEl.value?.resetCaptcha();
+ nextTick(() => {
+ waiting.value = false;
});
-
- if (canceled) return;
-
- let targetHost: string | null = hostTemp;
-
- // ドメイン部分だけを取り出す
- targetHost = extractDomain(targetHost);
- if (targetHost == null) {
- os.alert({
- type: 'error',
- title: i18n.ts.invalidValue,
- text: i18n.ts.tryAgain,
- });
- return;
- }
- openRemote(options, targetHost);
}
+
+onBeforeUnmount(() => {
+ password.value = '';
+ needCaptcha.value = false;
+ userInfo.value = null;
+});
</script>
<style lang="scss" module>
-.avatar {
- margin: 0 auto 0 auto;
- width: 64px;
- height: 64px;
- background: #ddd;
- background-position: center;
- background-size: cover;
- border-radius: var(--radius-full);
+.transition_enterActive,
+.transition_leaveActive {
+ transition: opacity 0.3s cubic-bezier(0,0,.35,1), transform 0.3s cubic-bezier(0,0,.35,1);
}
-
-.instanceManualSelectButton {
- display: block;
- text-align: center;
- opacity: .7;
- font-size: .8em;
-
- &:hover {
- text-decoration: underline;
- }
+.transition_enterFrom {
+ opacity: 0;
+ transform: translateX(50px);
+}
+.transition_leaveTo {
+ opacity: 0;
+ transform: translateX(-50px);
}
-.orHr {
+.signinRoot {
+ overflow-x: hidden;
+ overflow-x: clip;
+
position: relative;
- margin: .4em auto;
- width: 100%;
- height: 1px;
- background: var(--divider);
}
-.orMsg {
+.waitingRoot {
position: absolute;
- top: -.6em;
- display: inline-block;
- padding: 0 1em;
- background: var(--panel);
- font-size: 0.8em;
- color: var(--fgOnPanel);
- margin: 0;
- left: 50%;
- transform: translateX(-50%);
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: color-mix(in srgb, var(--MI_THEME-panel), transparent 50%);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 1;
}
</style>
diff --git a/packages/frontend/src/components/MkSigninDialog.vue b/packages/frontend/src/components/MkSigninDialog.vue
index d48780e9de..676a336ec7 100644
--- a/packages/frontend/src/components/MkSigninDialog.vue
+++ b/packages/frontend/src/components/MkSigninDialog.vue
@@ -4,26 +4,30 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkModalWindow
- ref="dialog"
- :width="400"
- :height="450"
- @close="onClose"
+<MkModal
+ ref="modal"
+ :preferType="'dialog'"
+ @click="onClose"
@closed="emit('closed')"
>
- <template #header>{{ i18n.ts.login }}</template>
-
- <MkSpacer :marginMin="20" :marginMax="28">
- <MkSignin :autoSet="autoSet" :message="message" :openOnRemote="openOnRemote" @login="onLogin"/>
- </MkSpacer>
-</MkModalWindow>
+ <div :class="$style.root">
+ <div :class="$style.header">
+ <div :class="$style.headerText"><i class="ti ti-login-2"></i> {{ i18n.ts.login }}</div>
+ <button :class="$style.closeButton" class="_button" @click="onClose"><i class="ti ti-x"></i></button>
+ </div>
+ <div :class="$style.content">
+ <MkSignin :autoSet="autoSet" :message="message" :openOnRemote="openOnRemote" @login="onLogin"/>
+ </div>
+ </div>
+</MkModal>
</template>
<script lang="ts" setup>
+import * as Misskey from 'misskey-js';
import { shallowRef } from 'vue';
import type { OpenOnRemoteOptions } from '@/scripts/please-login.js';
import MkSignin from '@/components/MkSignin.vue';
-import MkModalWindow from '@/components/MkModalWindow.vue';
+import MkModal from '@/components/MkModal.vue';
import { i18n } from '@/i18n.js';
withDefaults(defineProps<{
@@ -37,20 +41,67 @@ withDefaults(defineProps<{
});
const emit = defineEmits<{
- (ev: 'done', v: any): void;
+ (ev: 'done', v: Misskey.entities.SigninFlowResponse & { finished: true }): void;
(ev: 'closed'): void;
(ev: 'cancelled'): void;
}>();
-const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
+const modal = shallowRef<InstanceType<typeof MkModal>>();
function onClose() {
emit('cancelled');
- if (dialog.value) dialog.value.close();
+ if (modal.value) modal.value.close();
}
-function onLogin(res) {
+function onLogin(res: Misskey.entities.SigninFlowResponse & { finished: true }) {
emit('done', res);
- if (dialog.value) dialog.value.close();
+ if (modal.value) modal.value.close();
}
</script>
+
+<style lang="scss" module>
+.root {
+ overflow: auto;
+ margin: auto;
+ position: relative;
+ width: 100%;
+ max-width: 400px;
+ height: 100%;
+ max-height: 450px;
+ box-sizing: border-box;
+ background: var(--MI_THEME-panel);
+ border-radius: var(--MI-radius);
+}
+
+.header {
+ position: sticky;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 50px;
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ font-weight: bold;
+ backdrop-filter: var(--MI-blur, blur(15px));
+ background: var(--MI_THEME-acrylicBg);
+ z-index: 1;
+}
+
+.headerText {
+ padding: 0 20px;
+ box-sizing: border-box;
+}
+
+.closeButton {
+ margin-left: auto;
+ padding: 16px;
+ font-size: 16px;
+ line-height: 16px;
+}
+
+.content {
+ padding: 32px;
+ box-sizing: border-box;
+}
+</style>
diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue
index 4c55831a3a..f0b440d2ef 100644
--- a/packages/frontend/src/components/MkSignupDialog.form.vue
+++ b/packages/frontend/src/components/MkSignupDialog.form.vue
@@ -21,12 +21,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #caption>
<div><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.cannotBeChangedLater }}</div>
<span v-if="usernameState === 'wait'" style="color:#999"><MkLoading :em="true"/> {{ i18n.ts.checking }}</span>
- <span v-else-if="usernameState === 'ok'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.available }}</span>
- <span v-else-if="usernameState === 'unavailable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.unavailable }}</span>
- <span v-else-if="usernameState === 'error'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.error }}</span>
- <span v-else-if="usernameState === 'invalid-format'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.usernameInvalidFormat }}</span>
- <span v-else-if="usernameState === 'min-range'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.tooShort }}</span>
- <span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.tooLong }}</span>
+ <span v-else-if="usernameState === 'ok'" style="color: var(--MI_THEME-success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.available }}</span>
+ <span v-else-if="usernameState === 'unavailable'" style="color: var(--MI_THEME-error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.unavailable }}</span>
+ <span v-else-if="usernameState === 'error'" style="color: var(--MI_THEME-error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.error }}</span>
+ <span v-else-if="usernameState === 'invalid-format'" style="color: var(--MI_THEME-error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.usernameInvalidFormat }}</span>
+ <span v-else-if="usernameState === 'min-range'" style="color: var(--MI_THEME-error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.tooShort }}</span>
+ <span v-else-if="usernameState === 'max-range'" style="color: var(--MI_THEME-error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.tooLong }}</span>
</template>
</MkInput>
<MkInput v-if="instance.emailRequiredForSignup" v-model="email" :debounce="true" type="email" :spellcheck="false" required data-cy-signup-email @update:modelValue="onChangeEmail">
@@ -34,32 +34,32 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #prefix><i class="ti ti-mail"></i></template>
<template #caption>
<span v-if="emailState === 'wait'" style="color:#999"><MkLoading :em="true"/> {{ i18n.ts.checking }}</span>
- <span v-else-if="emailState === 'ok'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.available }}</span>
- <span v-else-if="emailState === 'unavailable:used'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.used }}</span>
- <span v-else-if="emailState === 'unavailable:format'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.format }}</span>
- <span v-else-if="emailState === 'unavailable:disposable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.disposable }}</span>
- <span v-else-if="emailState === 'unavailable:banned'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.banned }}</span>
- <span v-else-if="emailState === 'unavailable:mx'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.mx }}</span>
- <span v-else-if="emailState === 'unavailable:smtp'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.smtp }}</span>
- <span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.unavailable }}</span>
- <span v-else-if="emailState === 'error'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.error }}</span>
+ <span v-else-if="emailState === 'ok'" style="color: var(--MI_THEME-success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.available }}</span>
+ <span v-else-if="emailState === 'unavailable:used'" style="color: var(--MI_THEME-error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.used }}</span>
+ <span v-else-if="emailState === 'unavailable:format'" style="color: var(--MI_THEME-error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.format }}</span>
+ <span v-else-if="emailState === 'unavailable:disposable'" style="color: var(--MI_THEME-error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.disposable }}</span>
+ <span v-else-if="emailState === 'unavailable:banned'" style="color: var(--MI_THEME-error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.banned }}</span>
+ <span v-else-if="emailState === 'unavailable:mx'" style="color: var(--MI_THEME-error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.mx }}</span>
+ <span v-else-if="emailState === 'unavailable:smtp'" style="color: var(--MI_THEME-error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.smtp }}</span>
+ <span v-else-if="emailState === 'unavailable'" style="color: var(--MI_THEME-error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.unavailable }}</span>
+ <span v-else-if="emailState === 'error'" style="color: var(--MI_THEME-error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.error }}</span>
</template>
</MkInput>
<MkInput v-model="password" type="password" autocomplete="new-password" required data-cy-signup-password @update:modelValue="onChangePassword">
<template #label>{{ i18n.ts.password }}</template>
<template #prefix><i class="ti ti-lock"></i></template>
<template #caption>
- <span v-if="passwordStrength == 'low'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.weakPassword }}</span>
- <span v-if="passwordStrength == 'medium'" style="color: var(--warn)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.normalPassword }}</span>
- <span v-if="passwordStrength == 'high'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.strongPassword }}</span>
+ <span v-if="passwordStrength == 'low'" style="color: var(--MI_THEME-error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.weakPassword }}</span>
+ <span v-if="passwordStrength == 'medium'" style="color: var(--MI_THEME-warn)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.normalPassword }}</span>
+ <span v-if="passwordStrength == 'high'" style="color: var(--MI_THEME-success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.strongPassword }}</span>
</template>
</MkInput>
<MkInput v-model="retypedPassword" type="password" autocomplete="new-password" required data-cy-signup-password-retype @update:modelValue="onChangePasswordRetype">
<template #label>{{ i18n.ts.password }} ({{ i18n.ts.retype }})</template>
<template #prefix><i class="ti ti-lock"></i></template>
<template #caption>
- <span v-if="passwordRetypeState == 'match'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.passwordMatched }}</span>
- <span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.passwordNotMatched }}</span>
+ <span v-if="passwordRetypeState == 'match'" style="color: var(--MI_THEME-success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.passwordMatched }}</span>
+ <span v-if="passwordRetypeState == 'not-match'" style="color: var(--MI_THEME-error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.passwordNotMatched }}</span>
</template>
</MkInput>
<MkInput v-if="instance.approvalRequiredForSignup" v-model="reason" type="text" :spellcheck="false" required data-cy-signup-reason>
@@ -71,6 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" :class="$style.captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/>
<MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" :class="$style.captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/>
<MkCaptcha v-if="instance.enableFC" ref="fc" v-model="fcResponse" :class="$style.captcha" provider="fc" :sitekey="instance.fcSiteKey"/>
+ <MkCaptcha v-if="instance.enableTestcaptcha" ref="testcaptcha" v-model="testcaptchaResponse" :class="$style.captcha" provider="testcaptcha"/>
<MkButton type="submit" :disabled="shouldDisableSubmitting" large gradate rounded data-cy-signup-submit style="margin: 0 auto;">
<template v-if="submitting">
<MkLoading :em="true" :colored="false"/>
@@ -86,10 +87,10 @@ SPDX-License-Identifier: AGPL-3.0-only
import { ref, computed } from 'vue';
import { toUnicode } from 'punycode/';
import * as Misskey from 'misskey-js';
+import * as config from '@@/js/config.js';
import MkButton from './MkButton.vue';
import MkInput from './MkInput.vue';
import MkCaptcha, { type Captcha } from '@/components/MkCaptcha.vue';
-import * as config from '@@/js/config.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { login } from '@/account.js';
@@ -103,7 +104,7 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (ev: 'signup', user: Misskey.entities.SigninResponse): void;
+ (ev: 'signup', user: Misskey.entities.SignupResponse): void;
(ev: 'signupEmailPending'): void;
(ev: 'approvalPending'): void;
}>();
@@ -111,9 +112,11 @@ const emit = defineEmits<{
const host = toUnicode(config.host);
const hcaptcha = ref<Captcha | undefined>();
+const mcaptcha = ref<Captcha | undefined>();
const recaptcha = ref<Captcha | undefined>();
const turnstile = ref<Captcha | undefined>();
const fc = ref<Captcha | undefined>();
+const testcaptcha = ref<Captcha | undefined>();
const username = ref<string>('');
const password = ref<string>('');
@@ -131,6 +134,7 @@ const mCaptchaResponse = ref<string | null>(null);
const reCaptchaResponse = ref<string | null>(null);
const turnstileResponse = ref<string | null>(null);
const fcResponse = ref<string | null>(null);
+const testcaptchaResponse = ref<string | null>(null);
const usernameAbortController = ref<null | AbortController>(null);
const emailAbortController = ref<null | AbortController>(null);
@@ -141,6 +145,7 @@ const shouldDisableSubmitting = computed((): boolean => {
instance.enableRecaptcha && !reCaptchaResponse.value ||
instance.enableTurnstile && !turnstileResponse.value ||
instance.enableFC && !fcResponse.value ||
+ instance.enableTestcaptcha && !testcaptchaResponse.value ||
instance.emailRequiredForSignup && emailState.value !== 'ok' ||
usernameState.value !== 'ok' ||
passwordRetypeState.value !== 'match';
@@ -259,20 +264,33 @@ async function onSubmit(): Promise<void> {
if (submitting.value) return;
submitting.value = true;
- try {
- await misskeyApi('signup', {
- username: username.value,
- password: password.value,
- emailAddress: email.value,
- invitationCode: invitationCode.value,
- reason: reason.value,
- 'hcaptcha-response': hCaptchaResponse.value,
- 'm-captcha-response': mCaptchaResponse.value,
- 'g-recaptcha-response': reCaptchaResponse.value,
- 'turnstile-response': turnstileResponse.value,
- 'frc-captcha-solution': fcResponse.value,
- });
- if (instance.emailRequiredForSignup) {
+ const signupPayload: Misskey.entities.SignupRequest = {
+ username: username.value,
+ password: password.value,
+ emailAddress: email.value,
+ invitationCode: invitationCode.value,
+ reason: reason.value,
+ 'hcaptcha-response': hCaptchaResponse.value,
+ 'm-captcha-response': mCaptchaResponse.value,
+ 'g-recaptcha-response': reCaptchaResponse.value,
+ 'turnstile-response': turnstileResponse.value,
+ 'frc-captcha-solution': fcResponse.value,
+ 'testcaptcha-response': testcaptchaResponse.value,
+ };
+
+ const res = await fetch(`${config.apiUrl}/signup`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(signupPayload),
+ }).catch(() => {
+ onSignupApiError();
+ return null;
+ });
+
+ if (res) {
+ if (res.status === 204 || instance.emailRequiredForSignup) {
os.alert({
type: 'success',
title: i18n.ts._signup.almostThere,
@@ -287,28 +305,33 @@ async function onSubmit(): Promise<void> {
});
emit('approvalPending');
} else {
- const res = await misskeyApi('signin', {
- username: username.value,
- password: password.value,
- });
- emit('signup', res);
+ const resJson = (await res.json()) as Misskey.entities.SignupResponse;
+ if (_DEV_) console.log(resJson);
+
+ emit('signup', resJson);
if (props.autoSet) {
- return login(res.i);
+ await login(resJson.token);
}
}
- } catch {
- submitting.value = false;
- hcaptcha.value?.reset?.();
- recaptcha.value?.reset?.();
- turnstile.value?.reset?.();
- fc.value?.reset?.();
-
- os.alert({
- type: 'error',
- text: i18n.ts.somethingHappened,
- });
}
+
+ submitting.value = false;
+}
+
+function onSignupApiError() {
+ submitting.value = false;
+ hcaptcha.value?.reset?.();
+ mcaptcha.value?.reset?.();
+ recaptcha.value?.reset?.();
+ turnstile.value?.reset?.();
+ fc.value?.reset?.();
+ testcaptcha.value?.reset?.();
+
+ os.alert({
+ type: 'error',
+ text: i18n.ts.somethingHappened,
+ });
}
</script>
@@ -317,8 +340,8 @@ async function onSubmit(): Promise<void> {
padding: 16px;
text-align: center;
font-size: 26px;
- background-color: var(--accentedBg);
- color: var(--accent);
+ background-color: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
}
.captcha {
diff --git a/packages/frontend/src/components/MkSignupDialog.rules.vue b/packages/frontend/src/components/MkSignupDialog.rules.vue
index 251c805401..12f9621fda 100644
--- a/packages/frontend/src/components/MkSignupDialog.rules.vue
+++ b/packages/frontend/src/components/MkSignupDialog.rules.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkFolder v-if="availableServerRules" :defaultOpen="true">
<template #label>{{ i18n.ts.serverRules }}</template>
- <template #suffix><i v-if="agreeServerRules" class="ti ti-check" style="color: var(--success)"></i></template>
+ <template #suffix><i v-if="agreeServerRules" class="ti ti-check" style="color: var(--MI_THEME-success)"></i></template>
<ol class="_gaps_s" :class="$style.rules">
<li v-for="item in instance.serverRules" :class="$style.rule"><div :class="$style.ruleText" v-html="sanitizeHtml(item)"></div></li>
@@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkFolder v-if="availableTos || availablePrivacyPolicy" :defaultOpen="true">
<template #label>{{ tosPrivacyPolicyLabel }}</template>
- <template #suffix><i v-if="agreeTosAndPrivacyPolicy" class="ti ti-check" style="color: var(--success)"></i></template>
+ <template #suffix><i v-if="agreeTosAndPrivacyPolicy" class="ti ti-check" style="color: var(--MI_THEME-success)"></i></template>
<div class="_gaps_s">
<div v-if="availableTos"><a :href="instance.tosUrl ?? undefined" class="_link" target="_blank">{{ i18n.ts.termsOfService }} <i class="ti ti-external-link"></i></a></div>
<div v-if="availablePrivacyPolicy"><a :href="instance.privacyPolicyUrl ?? undefined" class="_link" target="_blank">{{ i18n.ts.privacyPolicy }} <i class="ti ti-external-link"></i></a></div>
@@ -43,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkFolder :defaultOpen="true">
<template #label>{{ i18n.ts.basicNotesBeforeCreateAccount }}</template>
- <template #suffix><i v-if="agreeNote" class="ti ti-check" style="color: var(--success)"></i></template>
+ <template #suffix><i v-if="agreeNote" class="ti ti-check" style="color: var(--MI_THEME-success)"></i></template>
<a href="https://activitypub.software/TransFem-org/Sharkey/-/blob/stable/IMPORTANT_NOTES.md" class="_link" target="_blank">{{ i18n.ts.basicNotesBeforeCreateAccount }} <i class="ti ti-external-link"></i></a>
@@ -151,8 +151,8 @@ async function updateAgreeNote(v: boolean) {
padding: 16px;
text-align: center;
font-size: 26px;
- background-color: var(--accentedBg);
- color: var(--accent);
+ background-color: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
}
.rules {
@@ -171,14 +171,14 @@ async function updateAgreeNote(v: boolean) {
flex-shrink: 0;
display: flex;
position: sticky;
- top: calc(var(--stickyTop, 0px) + 8px);
+ top: calc(var(--MI-stickyTop, 0px) + 8px);
counter-increment: item;
content: counter(item);
width: 32px;
height: 32px;
line-height: 32px;
- background-color: var(--accentedBg);
- color: var(--accent);
+ background-color: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
font-size: 13px;
font-weight: bold;
align-items: center;
diff --git a/packages/frontend/src/components/MkSignupDialog.vue b/packages/frontend/src/components/MkSignupDialog.vue
index 91e7d5dd53..b8e6318d17 100644
--- a/packages/frontend/src/components/MkSignupDialog.vue
+++ b/packages/frontend/src/components/MkSignupDialog.vue
@@ -47,7 +47,7 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (ev: 'done', res: Misskey.entities.SigninResponse): void;
+ (ev: 'done', res: Misskey.entities.SignupResponse): void;
(ev: 'closed'): void;
}>();
@@ -55,7 +55,7 @@ const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
const isAcceptedServerRule = ref(false);
-function onSignup(res: Misskey.entities.SigninResponse) {
+function onSignup(res: Misskey.entities.SignupResponse) {
emit('done', res);
dialog.value?.close();
}
diff --git a/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue b/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue
index 7743a89242..9bfa2789af 100644
--- a/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue
+++ b/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue
@@ -63,12 +63,12 @@ function close() {
.root {
position: fixed;
z-index: v-bind(zIndex);
- bottom: var(--margin);
+ bottom: var(--MI-margin);
left: 0;
right: 0;
margin: auto;
box-sizing: border-box;
- width: calc(100% - (var(--margin) * 2));
+ width: calc(100% - (var(--MI-margin) * 2));
max-width: 500px;
display: flex;
backdrop-filter: var(--blur, blur(15px));
@@ -78,7 +78,7 @@ function close() {
text-align: center;
padding-top: 25px;
width: 100px;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
@media (max-width: 500px) {
.icon {
diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue
index 6bd00fcc2a..46ef575c23 100644
--- a/packages/frontend/src/components/MkSubNoteContent.vue
+++ b/packages/frontend/src/components/MkSubNoteContent.vue
@@ -110,11 +110,11 @@ watch(() => props.expandAllCws, (expandAllCws) => {
left: 0;
width: 100%;
height: 64px;
- // background: linear-gradient(0deg, var(--panel), color(from var(--panel) srgb r g b / 0));
+ // background: linear-gradient(0deg, var(--MI_THEME-panel), color(from var(--MI_THEME-panel) srgb r g b / 0));
> .fadeLabel {
display: inline-block;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
padding: 6px 10px;
font-size: 0.8em;
border-radius: var(--radius-ellipse);
@@ -123,7 +123,7 @@ watch(() => props.expandAllCws, (expandAllCws) => {
&:hover {
> .fadeLabel {
- background: var(--panelHighlight);
+ background: var(--MI_THEME-panelHighlight);
}
}
}
@@ -132,13 +132,13 @@ watch(() => props.expandAllCws, (expandAllCws) => {
.reply {
margin-right: 6px;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
.rp {
margin-left: 4px;
font-style: oblique;
- color: var(--renote);
+ color: var(--MI_THEME-renote);
}
.translation {
@@ -152,7 +152,7 @@ watch(() => props.expandAllCws, (expandAllCws) => {
width: 100%;
margin-top: 14px;
position: sticky;
- bottom: calc(var(--stickyBottom, 0px) - 100px);
+ bottom: calc(var(--MI-stickyBottom, 0px) - 100px);
}
.playMFMButton {
@@ -161,7 +161,7 @@ watch(() => props.expandAllCws, (expandAllCws) => {
.showLessLabel {
display: inline-block;
- background: var(--popup);
+ background: var(--MI_THEME-popup);
padding: 6px 10px;
font-size: 0.8em;
border-radius: var(--radius-ellipse);
diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue
index 430e3c7958..e938da8e57 100644
--- a/packages/frontend/src/components/MkSuperMenu.vue
+++ b/packages/frontend/src/components/MkSuperMenu.vue
@@ -43,7 +43,7 @@ defineProps<{
& + .group {
margin-top: 16px;
padding-top: 16px;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
> .title {
@@ -64,7 +64,7 @@ defineProps<{
&:hover {
text-decoration: none;
- background: var(--panelHighlight);
+ background: var(--MI_THEME-panelHighlight);
}
&:focus-visible {
@@ -72,12 +72,12 @@ defineProps<{
}
&.active {
- color: var(--accent);
- background: var(--accentedBg);
+ color: var(--MI_THEME-accent);
+ background: var(--MI_THEME-accentedBg);
}
&.danger {
- color: var(--error);
+ color: var(--MI_THEME-error);
}
> .icon {
@@ -128,10 +128,10 @@ defineProps<{
&:hover {
text-decoration: none;
background: none;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
> .icon {
- background: var(--accentedBg);
+ background: var(--MI_THEME-accentedBg);
}
}
@@ -144,7 +144,7 @@ defineProps<{
width: 60px;
height: 60px;
aspect-ratio: 1;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
border-radius: var(--radius-full);
}
diff --git a/packages/frontend/src/components/MkSwitch.button.vue b/packages/frontend/src/components/MkSwitch.button.vue
index f7c413e1d3..a06a407de1 100644
--- a/packages/frontend/src/components/MkSwitch.button.vue
+++ b/packages/frontend/src/components/MkSwitch.button.vue
@@ -51,9 +51,9 @@ const toggle = () => {
width: calc(var(--height) * 1.6);
height: calc(var(--height) + 2px); // 枠線
outline: none;
- background: var(--switchOffBg);
+ background: var(--MI_THEME-switchOffBg);
background-clip: content-box;
- border: solid 1px var(--switchOffBg);
+ border: solid 1px var(--MI_THEME-switchOffBg);
border-radius: var(--radius-ellipse);
cursor: pointer;
transition: inherit;
@@ -61,8 +61,8 @@ const toggle = () => {
}
.buttonChecked {
- background-color: var(--switchOnBg) !important;
- border-color: var(--switchOnBg) !important;
+ background-color: var(--MI_THEME-switchOnBg) !important;
+ border-color: var(--MI_THEME-switchOnBg) !important;
}
.buttonDisabled {
@@ -80,12 +80,12 @@ const toggle = () => {
&:not(.knobChecked) {
left: 3px;
- background: var(--switchOffFg);
+ background: var(--MI_THEME-switchOffFg);
}
}
.knobChecked {
left: calc(calc(100% - var(--height)) + 3px);
- background: var(--switchOnFg);
+ background: var(--MI_THEME-switchOnFg);
}
</style>
diff --git a/packages/frontend/src/components/MkSwitch.vue b/packages/frontend/src/components/MkSwitch.vue
index a0994d9cc9..5e6029ee40 100644
--- a/packages/frontend/src/components/MkSwitch.vue
+++ b/packages/frontend/src/components/MkSwitch.vue
@@ -59,7 +59,7 @@ const toggle = () => {
&:hover {
> .button {
- border-color: var(--inputBorderHover) !important;
+ border-color: var(--MI_THEME-inputBorderHover) !important;
}
}
@@ -77,7 +77,7 @@ const toggle = () => {
margin: 0;
&:focus-visible ~ .toggle {
- outline: 2px solid var(--focus);
+ outline: 2px solid var(--MI_THEME-focus);
outline-offset: 2px;
}
}
@@ -87,7 +87,7 @@ const toggle = () => {
margin-top: 2px;
display: block;
transition: inherit;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
}
.label {
@@ -99,7 +99,7 @@ const toggle = () => {
.caption {
margin: 8px 0 0 0;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
font-size: 0.85em;
&:empty {
diff --git a/packages/frontend/src/components/MkSystemWebhookEditor.vue b/packages/frontend/src/components/MkSystemWebhookEditor.vue
index ec3b1c90ca..485d003f93 100644
--- a/packages/frontend/src/components/MkSystemWebhookEditor.vue
+++ b/packages/frontend/src/components/MkSystemWebhookEditor.vue
@@ -55,6 +55,18 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch>
<MkButton v-show="mode === 'edit'" transparent :class="$style.testButton" :disabled="!(isActive && events.userCreated)" @click="test('userCreated')"><i class="ti ti-send"></i></MkButton>
</div>
+ <div :class="$style.switchBox">
+ <MkSwitch v-model="events.inactiveModeratorsWarning" :disabled="disabledEvents.inactiveModeratorsWarning">
+ <template #label>{{ i18n.ts._webhookSettings._systemEvents.inactiveModeratorsWarning }}</template>
+ </MkSwitch>
+ <MkButton v-show="mode === 'edit'" transparent :class="$style.testButton" :disabled="!(isActive && events.inactiveModeratorsWarning)" @click="test('inactiveModeratorsWarning')"><i class="ti ti-send"></i></MkButton>
+ </div>
+ <div :class="$style.switchBox">
+ <MkSwitch v-model="events.inactiveModeratorsInvitationOnlyChanged" :disabled="disabledEvents.inactiveModeratorsInvitationOnlyChanged">
+ <template #label>{{ i18n.ts._webhookSettings._systemEvents.inactiveModeratorsInvitationOnlyChanged }}</template>
+ </MkSwitch>
+ <MkButton v-show="mode === 'edit'" transparent :class="$style.testButton" :disabled="!(isActive && events.inactiveModeratorsInvitationOnlyChanged)" @click="test('inactiveModeratorsInvitationOnlyChanged')"><i class="ti ti-send"></i></MkButton>
+ </div>
</div>
<div v-show="mode === 'edit'" :class="$style.description">
@@ -100,6 +112,8 @@ type EventType = {
abuseReport: boolean;
abuseReportResolved: boolean;
userCreated: boolean;
+ inactiveModeratorsWarning: boolean;
+ inactiveModeratorsInvitationOnlyChanged: boolean;
}
const emit = defineEmits<{
@@ -123,6 +137,8 @@ const events = ref<EventType>({
abuseReport: true,
abuseReportResolved: true,
userCreated: true,
+ inactiveModeratorsWarning: true,
+ inactiveModeratorsInvitationOnlyChanged: true,
});
const isActive = ref<boolean>(true);
@@ -130,6 +146,8 @@ const disabledEvents = ref<EventType>({
abuseReport: false,
abuseReportResolved: false,
userCreated: false,
+ inactiveModeratorsWarning: false,
+ inactiveModeratorsInvitationOnlyChanged: false,
});
const disableSubmitButton = computed(() => {
@@ -261,10 +279,10 @@ onMounted(async () => {
bottom: 0;
left: 0;
padding: 12px;
- border-top: solid 0.5px var(--divider);
- background: var(--acrylicBg);
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
+ border-top: solid 0.5px var(--MI_THEME-divider);
+ background: var(--MI_THEME-acrylicBg);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
}
.switchBox {
@@ -289,6 +307,6 @@ onMounted(async () => {
.description {
font-size: 0.85em;
padding: 8px 0 0 0;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
}
</style>
diff --git a/packages/frontend/src/components/MkTab.vue b/packages/frontend/src/components/MkTab.vue
index 54ab8fc663..07ab007482 100644
--- a/packages/frontend/src/components/MkTab.vue
+++ b/packages/frontend/src/components/MkTab.vue
@@ -47,13 +47,13 @@ export default defineComponent({
}
&.active {
- color: var(--accent);
- background: var(--accentedBg);
+ color: var(--MI_THEME-accent);
+ background: var(--MI_THEME-accentedBg);
}
&:not(.active):hover {
- color: var(--fgHighlighted);
- background: var(--panelHighlight);
+ color: var(--MI_THEME-fgHighlighted);
+ background: var(--MI_THEME-panelHighlight);
}
&:not(:first-child) {
diff --git a/packages/frontend/src/components/MkTagCloud.vue b/packages/frontend/src/components/MkTagCloud.vue
index 6b9c181597..87aa046963 100644
--- a/packages/frontend/src/components/MkTagCloud.vue
+++ b/packages/frontend/src/components/MkTagCloud.vue
@@ -33,7 +33,7 @@ watch(available, () => {
try {
window.TagCanvas.Start(idForCanvas, idForTags, {
textColour: '#ffffff',
- outlineColour: tinycolor(computedStyle.getPropertyValue('--accent')).toHexString(),
+ outlineColour: tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString(),
outlineRadius: 10,
initial: [-0.030, -0.010],
frontSelect: true,
diff --git a/packages/frontend/src/components/MkTextarea.vue b/packages/frontend/src/components/MkTextarea.vue
index 72d6e12656..9a003d9db8 100644
--- a/packages/frontend/src/components/MkTextarea.vue
+++ b/packages/frontend/src/components/MkTextarea.vue
@@ -159,7 +159,7 @@ onUnmounted(() => {
.caption {
font-size: 0.85em;
padding: 8px 0 0 0;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
&:empty {
display: none;
@@ -179,9 +179,9 @@ onUnmounted(() => {
font: inherit;
font-weight: normal;
font-size: 1em;
- color: var(--fg);
- background: var(--panel);
- border: solid 1px var(--panel);
+ color: var(--MI_THEME-fg);
+ background: var(--MI_THEME-panel);
+ border: solid 1px var(--MI_THEME-panel);
border-radius: var(--radius-sm);
outline: none;
box-shadow: none;
@@ -189,13 +189,13 @@ onUnmounted(() => {
transition: border-color 0.1s ease-out;
&:hover {
- border-color: var(--inputBorderHover) !important;
+ border-color: var(--MI_THEME-inputBorderHover) !important;
}
}
.focused {
> .textarea {
- border-color: var(--accent) !important;
+ border-color: var(--MI_THEME-accent) !important;
}
}
@@ -226,7 +226,7 @@ onUnmounted(() => {
.mfmPreview {
padding: 12px;
- border-radius: var(--radius);
+ border-radius: var(--MI-radius);
box-sizing: border-box;
min-height: 130px;
pointer-events: none;
diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue
index b32066c950..a7bc3f37f1 100644
--- a/packages/frontend/src/components/MkTokenGenerateWindow.vue
+++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue
@@ -136,15 +136,15 @@ function enableAll(): void {
.adminPermissions {
margin: 8px -6px 0;
padding: 24px 6px 6px;
- border: 2px solid var(--error);
- border-radius: calc(var(--radius) / 2);
+ border: 2px solid var(--MI_THEME-error);
+ border-radius: calc(var(--MI-radius) / 2);
}
.adminPermissionsHeader {
margin: -34px 0 6px 12px;
padding: 0 4px;
width: fit-content;
- color: var(--error);
- background: var(--panel);
+ color: var(--MI_THEME-error);
+ background: var(--MI_THEME-panel);
}
</style>
diff --git a/packages/frontend/src/components/MkTooltip.vue b/packages/frontend/src/components/MkTooltip.vue
index aac07008a4..25350a8a40 100644
--- a/packages/frontend/src/components/MkTooltip.vue
+++ b/packages/frontend/src/components/MkTooltip.vue
@@ -110,7 +110,7 @@ onUnmounted(() => {
box-sizing: border-box;
text-align: center;
border-radius: var(--radius-xs);
- border: solid 0.5px var(--divider);
+ border: solid 0.5px var(--MI_THEME-divider);
pointer-events: none;
transform-origin: center center;
}
diff --git a/packages/frontend/src/components/MkTutorialDialog.Note.vue b/packages/frontend/src/components/MkTutorialDialog.Note.vue
index cec7d69943..53b8db38b2 100644
--- a/packages/frontend/src/components/MkTutorialDialog.Note.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.Note.vue
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</I18n>
<MkNote :class="$style.exampleNoteRoot" :note="exampleNote" :mock="true" @reaction="addReaction" @removeReaction="removeReaction"/>
<div v-if="onceReacted">
- <b style="color: var(--accent);"><i class="ti ti-check"></i> {{ i18n.ts._initialTutorial.wellDone }}</b> {{ i18n.ts._initialTutorial._reaction.reactNotification }}<br>
+ <b style="color: var(--MI_THEME-accent);"><i class="ti ti-check"></i> {{ i18n.ts._initialTutorial.wellDone }}</b> {{ i18n.ts._initialTutorial._reaction.reactNotification }}<br>
<I18n :src="i18n.ts._initialTutorial._reaction.reactDone">
<template #undo>
<i class="ph-minus ph-bold ph-lg"></i>
@@ -116,13 +116,13 @@ function removeReaction(emoji) {
<style lang="scss" module>
.exampleNoteRoot {
- border-radius: var(--radius);
- border: var(--panelBorder);
- background: var(--panel);
+ border-radius: var(--MI-radius);
+ border: var(--MI_THEME-panelBorder);
+ background: var(--MI_THEME-panel);
}
.divider {
height: 1px;
- background: var(--divider);
+ background: var(--MI_THEME-divider);
}
</style>
diff --git a/packages/frontend/src/components/MkTutorialDialog.PostNote.vue b/packages/frontend/src/components/MkTutorialDialog.PostNote.vue
index a9014d4202..0d210acbae 100644
--- a/packages/frontend/src/components/MkTutorialDialog.PostNote.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.PostNote.vue
@@ -81,14 +81,14 @@ const exampleCWNote = reactive<Misskey.entities.Note>({
<style lang="scss" module>
.exampleRoot {
max-width: none!important;
- border-radius: var(--radius);
- border: var(--panelBorder);
- background: var(--panel);
+ border-radius: var(--MI-radius);
+ border: var(--MI_THEME-panelBorder);
+ background: var(--MI_THEME-panel);
}
.divider {
height: 1px;
- background: var(--divider);
+ background: var(--MI_THEME-divider);
}
.image {
@@ -101,7 +101,7 @@ const exampleCWNote = reactive<Misskey.entities.Note>({
display: block;
width: 100%;
height: 40px;
- color: var(--fgOnAccent);
+ color: var(--MI_THEME-fgOnAccent);
font-weight: bold;
text-align: left;
@@ -117,7 +117,7 @@ const exampleCWNote = reactive<Misskey.entities.Note>({
right: 0;
bottom: 0;
border-radius: var(--radius-ellipse);
- background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
+ background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
}
}
diff --git a/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue b/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue
index 322082f5a0..3ac58163c5 100644
--- a/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:initialNote="exampleNote"
@fileChangeSensitive="doSucceeded"
></MkPostForm>
- <div v-if="onceSucceeded"><b style="color: var(--accent);"><i class="ti ti-check"></i> {{ i18n.ts._initialTutorial.wellDone }}</b> {{ i18n.ts._initialTutorial._howToMakeAttachmentsSensitive.sensitiveSucceeded }}</div>
+ <div v-if="onceSucceeded"><b style="color: var(--MI_THEME-accent);"><i class="ti ti-check"></i> {{ i18n.ts._initialTutorial.wellDone }}</b> {{ i18n.ts._initialTutorial._howToMakeAttachmentsSensitive.sensitiveSucceeded }}</div>
<MkFolder>
<template #label>{{ i18n.ts.previewNoteText }}</template>
<MkNote :mock="true" :note="exampleNote" :class="$style.exampleRoot"></MkNote>
@@ -91,14 +91,14 @@ const exampleNote = reactive<Misskey.entities.Note>({
<style lang="scss" module>
.exampleRoot {
- border-radius: var(--radius);
- border: var(--panelBorder);
- background: var(--panel);
+ border-radius: var(--MI-radius);
+ border: var(--MI_THEME-panelBorder);
+ background: var(--MI_THEME-panel);
}
.divider {
height: 1px;
- background: var(--divider);
+ background: var(--MI_THEME-divider);
}
.image {
@@ -111,7 +111,7 @@ const exampleNote = reactive<Misskey.entities.Note>({
display: block;
width: 100%;
height: 40px;
- color: var(--fgOnAccent);
+ color: var(--MI_THEME-fgOnAccent);
font-weight: bold;
text-align: left;
@@ -127,7 +127,7 @@ const exampleNote = reactive<Misskey.entities.Note>({
right: 0;
bottom: 0;
border-radius: var(--radius-ellipse);
- background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
+ background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
}
}
diff --git a/packages/frontend/src/components/MkTutorialDialog.Timeline.vue b/packages/frontend/src/components/MkTutorialDialog.Timeline.vue
index b900a30c85..328f7e95d1 100644
--- a/packages/frontend/src/components/MkTutorialDialog.Timeline.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.Timeline.vue
@@ -31,14 +31,14 @@ import { basicTimelineIconClass, basicTimelineTypes } from '@/timelines.js';
<style lang="scss" module>
.exampleNoteRoot {
- border-radius: var(--radius);
- border: var(--panelBorder);
- background: var(--panel);
+ border-radius: var(--MI-radius);
+ border: var(--MI_THEME-panelBorder);
+ background: var(--MI_THEME-panel);
}
.divider {
height: 1px;
- background: var(--divider);
+ background: var(--MI_THEME-divider);
}
.image {
@@ -51,7 +51,7 @@ import { basicTimelineIconClass, basicTimelineTypes } from '@/timelines.js';
display: block;
width: 100%;
height: 40px;
- color: var(--fgOnAccent);
+ color: var(--MI_THEME-fgOnAccent);
font-weight: bold;
text-align: left;
@@ -67,7 +67,7 @@ import { basicTimelineIconClass, basicTimelineTypes } from '@/timelines.js';
right: 0;
bottom: 0;
border-radius: var(--radius-ellipse);
- background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
+ background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
}
}
diff --git a/packages/frontend/src/components/MkTutorialDialog.vue b/packages/frontend/src/components/MkTutorialDialog.vue
index 1f5a2b9381..11d7c8dc4d 100644
--- a/packages/frontend/src/components/MkTutorialDialog.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.vue
@@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkAnimBg style="position: absolute; top: 0;" :scale="1.5"/>
<MkSpacer :marginMin="20" :marginMax="28">
<div class="_gaps" style="text-align: center;">
- <i class="ti ti-confetti" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i>
+ <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>
<div>{{ i18n.ts._initialTutorial._landing.description }}</div>
<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" @click="page++">{{ i18n.ts._initialTutorial.launchTutorial }} <i class="ti ti-arrow-right"></i></MkButton>
@@ -126,7 +126,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkAnimBg style="position: absolute; top: 0;" :scale="1.5"/>
<MkSpacer :marginMin="20" :marginMax="28">
<div class="_gaps" style="text-align: center;">
- <i class="ti ti-check" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i>
+ <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>
<I18n :src="i18n.ts._initialTutorial._done.description" tag="div" style="padding: 0 16px;">
<template #link>
@@ -223,7 +223,7 @@ async function close(skip: boolean) {
.progressBarValue {
height: 100%;
- background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
+ background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
transition: all 0.5s cubic-bezier(0,.5,.5,1);
}
@@ -253,7 +253,7 @@ async function close(skip: boolean) {
left: 0;
flex-shrink: 0;
padding: 12px;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
-webkit-backdrop-filter: blur(15px);
backdrop-filter: blur(15px);
}
diff --git a/packages/frontend/src/components/MkUpdated.vue b/packages/frontend/src/components/MkUpdated.vue
index 91f5b86c2d..7cafb1b0af 100644
--- a/packages/frontend/src/components/MkUpdated.vue
+++ b/packages/frontend/src/components/MkUpdated.vue
@@ -46,8 +46,8 @@ onMounted(() => {
max-width: 480px;
box-sizing: border-box;
text-align: center;
- background: var(--panel);
- border-radius: var(--radius);
+ background: var(--MI_THEME-panel);
+ border-radius: var(--MI-radius);
}
.title {
diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue
index 04f5314463..be12304ae6 100644
--- a/packages/frontend/src/components/MkUrlPreview.vue
+++ b/packages/frontend/src/components/MkUrlPreview.vue
@@ -84,13 +84,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { defineAsyncComponent, onDeactivated, onUnmounted, ref } from 'vue';
-import type { summaly } from '@misskey-dev/summaly';
import { url as local } from '@@/js/config.js';
+import { versatileLang } from '@@/js/intl-const.js';
+import type { summaly } from '@misskey-dev/summaly';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import { deviceKind } from '@/scripts/device-kind.js';
import MkButton from '@/components/MkButton.vue';
-import { versatileLang } from '@@/js/intl-const.js';
import { transformPlayerUrl } from '@/scripts/player-url-transform.js';
import { defaultStore } from '@/store.js';
@@ -219,7 +219,7 @@ onUnmounted(() => {
height: 1.5em;
padding: 0;
margin: 0;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
background: rgba(128, 128, 128, 0.2);
opacity: 0.7;
@@ -240,7 +240,7 @@ onUnmounted(() => {
position: relative;
display: block;
font-size: 14px;
- box-shadow: 0 0 0 1px var(--divider);
+ box-shadow: 0 0 0 1px var(--MI_THEME-divider);
border-radius: var(--radius-sm);
overflow: clip;
@@ -270,7 +270,7 @@ onUnmounted(() => {
height: 100%;
background-position: center;
background-size: cover;
- background-color: var(--bg);
+ background-color: var(--MI_THEME-bg);
display: flex;
justify-content: center;
align-items: center;
@@ -317,7 +317,6 @@ onUnmounted(() => {
.siteName {
display: inline-block;
margin: 0;
- color: var(--urlPreviewInfo);
font-size: 0.8em;
line-height: 16px;
vertical-align: top;
diff --git a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
index 3c5f563aa0..7a2b5f5ddc 100644
--- a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
+++ b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
@@ -25,9 +25,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkRadios v-model="icon">
<template #label>{{ i18n.ts.icon }}</template>
<option value="info"><i class="ti ti-info-circle"></i></option>
- <option value="warning"><i class="ti ti-alert-triangle" style="color: var(--warn);"></i></option>
- <option value="error"><i class="ti ti-circle-x" style="color: var(--error);"></i></option>
- <option value="success"><i class="ti ti-check" style="color: var(--success);"></i></option>
+ <option value="warning"><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i></option>
+ <option value="error"><i class="ti ti-circle-x" style="color: var(--MI_THEME-error);"></i></option>
+ <option value="success"><i class="ti ti-check" style="color: var(--MI_THEME-success);"></i></option>
</MkRadios>
<MkRadios v-model="display">
<template #label>{{ i18n.ts.display }}</template>
@@ -141,8 +141,8 @@ async function del() {
bottom: 0;
left: 0;
padding: 12px;
- border-top: solid 0.5px var(--divider);
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
+ border-top: solid 0.5px var(--MI_THEME-divider);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
}
</style>
diff --git a/packages/frontend/src/components/MkUserCardMini.vue b/packages/frontend/src/components/MkUserCardMini.vue
index 603f9f2435..f3c3625c3a 100644
--- a/packages/frontend/src/components/MkUserCardMini.vue
+++ b/packages/frontend/src/components/MkUserCardMini.vue
@@ -23,7 +23,7 @@ import { acct } from '@/filters/user.js';
const props = withDefaults(defineProps<{
user: Misskey.entities.User;
- withChart: boolean;
+ withChart?: boolean;
}>(), {
withChart: true,
});
@@ -49,7 +49,7 @@ $bodyInfoHieght: 16px;
display: flex;
align-items: center;
padding: 16px;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
border-radius: var(--radius-sm);
}
@@ -64,7 +64,7 @@ $bodyInfoHieght: 16px;
flex: 1;
overflow: hidden;
font-size: 0.9em;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
padding-right: 8px;
}
diff --git a/packages/frontend/src/components/MkUserInfo.vue b/packages/frontend/src/components/MkUserInfo.vue
index 73cdd9ce00..64a3867d33 100644
--- a/packages/frontend/src/components/MkUserInfo.vue
+++ b/packages/frontend/src/components/MkUserInfo.vue
@@ -77,7 +77,7 @@ defineProps<{
z-index: 2;
width: var(--avatar);
height: var(--avatar);
- border: solid 4px var(--panel);
+ border: solid 4px var(--MI_THEME-panel);
}
.title {
@@ -98,7 +98,7 @@ defineProps<{
margin: 0;
line-height: 16px;
font-size: 0.8em;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
opacity: 0.7;
}
@@ -116,7 +116,7 @@ defineProps<{
.description {
padding: 16px;
font-size: 0.8em;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
.mfm {
@@ -128,7 +128,7 @@ defineProps<{
.status {
padding: 10px 16px;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
.statusItem {
@@ -139,12 +139,12 @@ defineProps<{
.statusItemLabel {
margin: 0;
font-size: 0.7em;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
}
.statusItemValue {
font-size: 1em;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
.follow {
diff --git a/packages/frontend/src/components/MkUserList.vue b/packages/frontend/src/components/MkUserList.vue
index ac82ecc3d6..8dc01a08ab 100644
--- a/packages/frontend/src/components/MkUserList.vue
+++ b/packages/frontend/src/components/MkUserList.vue
@@ -39,6 +39,6 @@ const props = withDefaults(defineProps<{
.root {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
- grid-gap: var(--margin);
+ grid-gap: var(--MI-margin);
}
</style>
diff --git a/packages/frontend/src/components/MkUserOnlineIndicator.vue b/packages/frontend/src/components/MkUserOnlineIndicator.vue
index 9f04353f62..a4fee52367 100644
--- a/packages/frontend/src/components/MkUserOnlineIndicator.vue
+++ b/packages/frontend/src/components/MkUserOnlineIndicator.vue
@@ -36,7 +36,7 @@ const text = computed(() => {
<style lang="scss" module>
.root {
- box-shadow: 0 0 0 3px var(--panel);
+ box-shadow: 0 0 0 3px var(--MI_THEME-panel);
// sharkey: the comment mentions something about 100% radius not behaving correctly on blink.
// couldn't reproduce, assuming the 120% here was just an old workaround
diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue
index c6f4699b3e..9de8639fe4 100644
--- a/packages/frontend/src/components/MkUserPopup.vue
+++ b/packages/frontend/src/components/MkUserPopup.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<svg viewBox="0 0 128 128" :class="$style.avatarBack">
<g transform="matrix(1.6,0,0,1.6,-38.4,-51.2)">
- <path d="M64,32C81.661,32 96,46.339 96,64C95.891,72.184 104,72 104,72C104,72 74.096,80 64,80C52.755,80 24,72 24,72C24,72 31.854,72.018 32,64C32,46.339 46.339,32 64,32Z" style="fill: var(--popup);"/>
+ <path d="M64,32C81.661,32 96,46.339 96,64C95.891,72.184 104,72 104,72C104,72 74.096,80 64,80C52.755,80 24,72 24,72C24,72 31.854,72.018 32,64C32,46.339 46.339,32 64,32Z" style="fill: var(--MI_THEME-popup);"/>
</g>
</svg>
<MkAvatar :class="$style.avatar" :user="user" indicator/>
@@ -231,8 +231,8 @@ onMounted(() => {
padding: 16px 26px;
font-size: 0.8em;
text-align: center;
- border-top: solid 1px var(--divider);
- border-bottom: solid 1px var(--divider);
+ border-top: solid 1px var(--MI_THEME-divider);
+ border-bottom: solid 1px var(--MI_THEME-divider);
}
.fields {
@@ -296,7 +296,7 @@ onMounted(() => {
.statusItemLabel {
font-size: 0.7em;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
}
.menu {
@@ -304,7 +304,7 @@ onMounted(() => {
top: 8px;
right: 44px;
padding: 6px;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
border-radius: var(--radius-ellipse);
}
diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue
index a5b48c8ce2..7c11744368 100644
--- a/packages/frontend/src/components/MkUserSelectDialog.vue
+++ b/packages/frontend/src/components/MkUserSelectDialog.vue
@@ -196,11 +196,11 @@ onMounted(() => {
font-size: 14px;
&:hover {
- background: var(--X7);
+ background: var(--MI_THEME-X7);
}
&.selected {
- background: var(--accent);
+ background: var(--MI_THEME-accent);
color: #fff;
}
}
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Follow.vue b/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
index 1524ea0ec9..5153c06139 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
@@ -62,7 +62,7 @@ const popularUsers: Paging = {
.users {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
- grid-gap: var(--margin);
+ grid-gap: var(--MI-margin);
justify-content: center;
}
</style>
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
index 3194641cdb..7cb48f6afb 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
@@ -51,6 +51,11 @@ watch(name, () => {
// 空文字列をnullにしたいので??は使うな
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
name: name.value || null,
+ }, undefined, {
+ '0b3f9f6a-2f4d-4b1f-9fb4-49d3a2fd7191': {
+ title: i18n.ts.yourNameContainsProhibitedWords,
+ text: i18n.ts.yourNameContainsProhibitedWordsDescription,
+ },
});
});
diff --git a/packages/frontend/src/components/MkUserSetupDialog.User.vue b/packages/frontend/src/components/MkUserSetupDialog.User.vue
index c80349d034..c7732af808 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.User.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.User.vue
@@ -61,7 +61,7 @@ async function follow() {
z-index: 2;
width: var(--avatar);
height: var(--avatar);
- border: solid 4px var(--panel);
+ border: solid 4px var(--MI_THEME-panel);
}
.title {
@@ -82,7 +82,7 @@ async function follow() {
margin: 0;
line-height: 16px;
font-size: 0.8em;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
opacity: 0.7;
}
@@ -99,7 +99,7 @@ async function follow() {
}
.footer {
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
padding: 16px;
}
</style>
diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue
index 1fb1eda039..b7261129ef 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.vue
@@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkAnimBg style="position: absolute; top: 0;" :scale="1.5"/>
<MkSpacer :marginMin="20" :marginMax="28">
<div class="_gaps" style="text-align: center;">
- <i class="ti ti-confetti" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i>
+ <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>
<div>{{ i18n.ts._initialAccountSetting.letsStartAccountSetup }}</div>
<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" data-cy-user-setup-continue @click="page++">{{ i18n.ts._initialAccountSetting.profileSetting }} <i class="ti ti-arrow-right"></i></MkButton>
@@ -91,7 +91,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.centerPage">
<MkSpacer :marginMin="20" :marginMax="28">
<div class="_gaps" style="text-align: center;">
- <i class="ti ti-bell-ringing-2" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i>
+ <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>
<div style="padding: 0 16px;">{{ i18n.tsx._initialAccountSetting.pushNotificationDescription({ name: instance.name ?? host }) }}</div>
<MkPushNotificationAllowButton primary showOnlyToRegister style="margin: 0 auto;"/>
@@ -108,7 +108,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkAnimBg style="position: absolute; top: 0;" :scale="1.5"/>
<MkSpacer :marginMin="20" :marginMax="28">
<div class="_gaps" style="text-align: center;">
- <i class="ti ti-check" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i>
+ <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>
<div>{{ i18n.tsx._initialAccountSetting.youCanContinueTutorial({ name: instance.name ?? host }) }}</div>
<div class="_buttonsCenter" style="margin-top: 16px;">
@@ -223,7 +223,7 @@ async function later(later: boolean) {
.progressBarValue {
height: 100%;
- background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
+ background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
transition: all 0.5s cubic-bezier(0,.5,.5,1);
}
@@ -252,7 +252,7 @@ async function later(later: boolean) {
left: 0;
flex-shrink: 0;
padding: 12px;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
-webkit-backdrop-filter: blur(15px);
backdrop-filter: blur(15px);
}
diff --git a/packages/frontend/src/components/MkVisibilityPicker.vue b/packages/frontend/src/components/MkVisibilityPicker.vue
index 3c3f9e94b6..465204d7a2 100644
--- a/packages/frontend/src/components/MkVisibilityPicker.vue
+++ b/packages/frontend/src/components/MkVisibilityPicker.vue
@@ -124,7 +124,7 @@ function choose(visibility: typeof Misskey.noteVisibilities[number]): void {
}
&.active {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
}
diff --git a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
index cab42cd59d..d098dad9a1 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
@@ -62,7 +62,7 @@ async function renderChart() {
const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
const computedStyle = getComputedStyle(document.documentElement);
- const accent = tinycolor(computedStyle.getPropertyValue('--accent')).toHexString();
+ const accent = tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString();
const colorRead = accent;
const colorWrite = '#2ecc71';
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index 874eff6c79..688340c6b1 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -110,8 +110,8 @@ function showMenu(ev: MouseEvent) {
.panel {
position: relative;
- background: var(--panel);
- border-radius: var(--radius);
+ background: var(--MI_THEME-panel);
+ border-radius: var(--MI-radius);
box-shadow: 0 12px 32px rgb(0 0 0 / 25%);
}
@@ -191,14 +191,14 @@ function showMenu(ev: MouseEvent) {
}
.statsItemLabel {
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
font-size: 0.9em;
}
.statsItemCount {
font-weight: bold;
font-size: 1.2em;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
.tl {
@@ -207,7 +207,7 @@ function showMenu(ev: MouseEvent) {
.tlHeader {
padding: 12px 16px;
- border-bottom: solid 1px var(--divider);
+ border-bottom: solid 1px var(--MI_THEME-divider);
}
.tlBody {
diff --git a/packages/frontend/src/components/MkWaitingDialog.vue b/packages/frontend/src/components/MkWaitingDialog.vue
index 60b75b6d30..34fa6b0723 100644
--- a/packages/frontend/src/components/MkWaitingDialog.vue
+++ b/packages/frontend/src/components/MkWaitingDialog.vue
@@ -47,8 +47,8 @@ watch(() => props.showing, () => {
padding: 32px;
box-sizing: border-box;
text-align: center;
- background: var(--panel);
- border-radius: var(--radius);
+ background: var(--MI_THEME-panel);
+ border-radius: var(--MI-radius);
width: 250px;
&.iconOnly {
@@ -65,7 +65,7 @@ watch(() => props.showing, () => {
font-size: 32px;
&.success {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
&.waiting {
diff --git a/packages/frontend/src/components/MkWidgets.vue b/packages/frontend/src/components/MkWidgets.vue
index 99840bf8d7..f3d0bcd58f 100644
--- a/packages/frontend/src/components/MkWidgets.vue
+++ b/packages/frontend/src/components/MkWidgets.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.root">
<template v-if="edit">
<header :class="$style.editHeader">
- <MkSelect v-model="widgetAdderSelected" style="margin-bottom: var(--margin)" data-cy-widget-select>
+ <MkSelect v-model="widgetAdderSelected" style="margin-bottom: var(--MI-margin)" data-cy-widget-select>
<template #label>{{ i18n.ts.selectWidget }}</template>
<option v-for="widget in widgetDefs" :key="widget" :value="widget">{{ i18n.ts._widgets[widget] }}</option>
</MkSelect>
@@ -123,7 +123,7 @@ function onContextmenu(widget: Widget, ev: MouseEvent) {
.widget {
contain: content;
- margin: var(--margin) 0;
+ margin: var(--MI-margin) 0;
&:first-of-type {
margin-top: 0;
diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue
index 08906a1205..056b6a37ed 100644
--- a/packages/frontend/src/components/MkWindow.vue
+++ b/packages/frontend/src/components/MkWindow.vue
@@ -54,9 +54,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onBeforeUnmount, onMounted, provide, shallowRef, ref } from 'vue';
+import type { MenuItem } from '@/types/menu.js';
import contains from '@/scripts/contains.js';
import * as os from '@/os.js';
-import type { MenuItem } from '@/types/menu.js';
import { i18n } from '@/i18n.js';
import { defaultStore } from '@/store.js';
@@ -484,6 +484,10 @@ defineExpose({
}
.root {
+ // universal.vueとかで直接--MI-stickyBottomが定義されていたりするのでリセット
+ --MI-stickyTop: 0;
+ --MI-stickyBottom: 0;
+
position: fixed;
top: 0;
left: 0;
@@ -502,7 +506,7 @@ defineExpose({
contain: content;
width: 100%;
height: 100%;
- border-radius: var(--radius);
+ border-radius: var(--MI-radius);
}
.header {
@@ -514,10 +518,10 @@ defineExpose({
flex-shrink: 0;
user-select: none;
height: var(--height);
- background: var(--windowHeader);
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
- //border-bottom: solid 1px var(--divider);
+ background: var(--MI_THEME-windowHeader);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
+ //border-bottom: solid 1px var(--MI_THEME-divider);
font-size: 90%;
font-weight: bold;
@@ -531,11 +535,11 @@ defineExpose({
width: var(--height);
&:hover {
- color: var(--fgHighlighted);
+ color: var(--MI_THEME-fgHighlighted);
}
&.highlighted {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
}
@@ -560,7 +564,7 @@ defineExpose({
.content {
flex: 1;
overflow: auto;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
container-type: size;
}
diff --git a/packages/frontend/src/components/form/link.vue b/packages/frontend/src/components/form/link.vue
index f5546edf1e..1cfde0c903 100644
--- a/packages/frontend/src/components/form/link.vue
+++ b/packages/frontend/src/components/form/link.vue
@@ -60,18 +60,18 @@ const props = defineProps<{
width: 100%;
box-sizing: border-box;
padding: 10px 14px;
- background: var(--folderHeaderBg);
+ background: var(--MI_THEME-folderHeaderBg);
border-radius: var(--radius-sm);
font-size: 0.9em;
&:hover {
text-decoration: none;
- background: var(--folderHeaderHoverBg);
+ background: var(--MI_THEME-folderHeaderHoverBg);
}
&.active {
- color: var(--accent);
- background: var(--folderHeaderHoverBg);
+ color: var(--MI_THEME-accent);
+ background: var(--MI_THEME-folderHeaderHoverBg);
}
}
@@ -79,7 +79,7 @@ const props = defineProps<{
margin-right: 0.75em;
flex-shrink: 0;
text-align: center;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
&:empty {
display: none;
diff --git a/packages/frontend/src/components/form/section.vue b/packages/frontend/src/components/form/section.vue
index ad37daa265..5fca3acc31 100644
--- a/packages/frontend/src/components/form/section.vue
+++ b/packages/frontend/src/components/form/section.vue
@@ -21,8 +21,8 @@ defineProps<{
<style lang="scss" module>
.root {
- border-top: solid 0.5px var(--divider);
- //border-bottom: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
+ //border-bottom: solid 0.5px var(--MI_THEME-divider);
}
.rootFirst {
@@ -49,7 +49,7 @@ defineProps<{
.description {
font-size: 0.85em;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
margin: 0 0 8px 0;
}
</style>
diff --git a/packages/frontend/src/components/form/slot.vue b/packages/frontend/src/components/form/slot.vue
index f54db0ca82..da94b7abbb 100644
--- a/packages/frontend/src/components/form/slot.vue
+++ b/packages/frontend/src/components/form/slot.vue
@@ -35,7 +35,7 @@ function focus() {
.caption {
font-size: 0.85em;
padding: 8px 0 0 0;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
&:empty {
display: none;
diff --git a/packages/frontend/src/components/global/MkAd.vue b/packages/frontend/src/components/global/MkAd.vue
index 1238e6ef23..57af034354 100644
--- a/packages/frontend/src/components/global/MkAd.vue
+++ b/packages/frontend/src/components/global/MkAd.vue
@@ -30,12 +30,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</component>
</div>
<div v-else :class="$style.menu">
- <div :class="$style.menuContainer">
- <div>Ads by {{ host }}</div>
- <!--<MkButton class="button" primary>{{ i18n.ts._ad.like }}</MkButton>-->
- <MkButton v-if="chosen.ratio !== 0" :class="$style.menuButton" @click="reduceFrequency">{{ i18n.ts._ad.reduceFrequencyOfThisAd }}</MkButton>
- <button class="_textButton" @click="toggleMenu">{{ i18n.ts._ad.back }}</button>
- </div>
+ <div>Ads by {{ host }}</div>
+ <!--<MkButton class="button" primary>{{ i18n.ts._ad.like }}</MkButton>-->
+ <MkButton v-if="chosen.ratio !== 0" :class="$style.menuButton" @click="reduceFrequency">{{ i18n.ts._ad.reduceFrequencyOfThisAd }}</MkButton>
+ <button class="_textButton" @click="toggleMenu">{{ i18n.ts._ad.back }}</button>
</div>
</div>
<div v-else></div>
@@ -43,9 +41,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, computed } from 'vue';
+import { url as local, host } from '@@/js/config.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
-import { url as local, host } from '@@/js/config.js';
import MkButton from '@/components/MkButton.vue';
import { defaultStore } from '@/store.js';
import * as os from '@/os.js';
@@ -123,8 +121,7 @@ function reduceFrequency(): void {
<style lang="scss" module>
.root {
- background-size: auto auto;
- background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, var(--ad) 8px, var(--ad) 14px );
+
}
.main {
@@ -139,8 +136,6 @@ function reduceFrequency(): void {
}
&.form_horizontal {
- padding: 8px;
-
> .link,
> .link > .img {
max-width: min(600px, 100%);
@@ -149,8 +144,6 @@ function reduceFrequency(): void {
}
&.form_horizontalBig {
- padding: 8px;
-
> .link,
> .link > .img {
max-width: min(600px, 100%);
@@ -191,7 +184,7 @@ function reduceFrequency(): void {
right: 1px;
display: grid;
place-content: center;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
border-radius: var(--radius-full);
padding: 2px;
}
@@ -202,15 +195,12 @@ function reduceFrequency(): void {
}
.menu {
- padding: 8px;
text-align: center;
-}
-
-.menuContainer {
padding: 8px;
margin: 0 auto;
max-width: 400px;
- border: solid 1px var(--divider);
+ background: var(--MI_THEME-panel);
+ border: solid 1px var(--MI_THEME-divider);
}
.menuButton {
diff --git a/packages/frontend/src/components/global/MkLoading.vue b/packages/frontend/src/components/global/MkLoading.vue
index 49d8ace37b..47d797606b 100644
--- a/packages/frontend/src/components/global/MkLoading.vue
+++ b/packages/frontend/src/components/global/MkLoading.vue
@@ -56,7 +56,7 @@ const props = withDefaults(defineProps<{
--size: 38px;
&.colored {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
&.inline {
diff --git a/packages/frontend/src/components/global/MkMfm.ts b/packages/frontend/src/components/global/MkMfm.ts
index 9bf9f4a872..90b3999092 100644
--- a/packages/frontend/src/components/global/MkMfm.ts
+++ b/packages/frontend/src/components/global/MkMfm.ts
@@ -7,6 +7,7 @@ import { VNode, h, defineAsyncComponent, SetupContext, provide } from 'vue';
import * as mfm from '@transfem-org/sfm-js';
import * as Misskey from 'misskey-js';
import CkFollowMouse from '../CkFollowMouse.vue';
+import { host } from '@@/js/config.js';
import MkUrl from '@/components/global/MkUrl.vue';
import MkTime from '@/components/global/MkTime.vue';
import MkLink from '@/components/MkLink.vue';
@@ -18,7 +19,6 @@ import MkCodeInline from '@/components/MkCodeInline.vue';
import MkGoogle from '@/components/MkGoogle.vue';
import MkSparkle from '@/components/MkSparkle.vue';
import MkA, { MkABehavior } from '@/components/global/MkA.vue';
-import { host } from '@@/js/config.js';
import { defaultStore } from '@/store.js';
function safeParseFloat(str: unknown): number | null {
@@ -32,8 +32,8 @@ const QUOTE_STYLE = `
display: block;
margin: 8px;
padding: 6px 0 6px 12px;
-color: var(--fg);
-border-left: solid 3px var(--fg);
+color: var(--MI_THEME-fg);
+border-left: solid 3px var(--MI_THEME-fg);
opacity: 0.7;
`.split('\n').join(' ');
@@ -60,7 +60,8 @@ type MfmEvents = {
// eslint-disable-next-line import/no-default-export
export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEvents>['emit'] }) {
- provide('linkNavigationBehavior', props.linkNavigationBehavior);
+ // こうしたいところだけど functional component 内では provide は使えない
+ //provide('linkNavigationBehavior', props.linkNavigationBehavior);
const isNote = props.isNote ?? true;
const shouldNyaize = props.nyaize === 'respect' && props.author?.isCat && props.author?.speakAsCat && !defaultStore.state.disableCatSpeak;
@@ -328,7 +329,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
}
case 'border': {
let color = validColor(token.props.args.color);
- color = color ? `#${color}` : 'var(--accent)';
+ color = color ? `#${color}` : 'var(--MI_THEME-accent)';
let b_style = token.props.args.style;
if (
typeof b_style !== 'string' ||
@@ -361,7 +362,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
const child = token.children[0];
const unixtime = parseInt(child.type === 'text' ? child.props.text : '');
return h('span', {
- style: 'display: inline-block; font-size: 90%; border: solid 1px var(--divider); border-radius: var(--radius-ellipse); padding: 4px 10px 4px 6px;',
+ style: 'display: inline-block; font-size: 90%; border: solid 1px var(--MI_THEME-divider); border-radius: var(--radius-ellipse); padding: 4px 10px 4px 6px;',
}, [
h('i', {
class: 'ti ti-clock',
@@ -409,6 +410,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
key: Math.random(),
url: token.props.url,
rel: 'nofollow noopener',
+ navigationBehavior: props.linkNavigationBehavior,
}))];
}
@@ -417,6 +419,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
key: Math.random(),
url: token.props.url,
rel: 'nofollow noopener',
+ navigationBehavior: props.linkNavigationBehavior,
}, genEl(token.children, scale, true)))];
}
@@ -425,6 +428,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
key: Math.random(),
host: (token.props.host == null && props.author && props.author.host != null ? props.author.host : token.props.host) ?? host,
username: token.props.username,
+ navigationBehavior: props.linkNavigationBehavior,
}))];
}
@@ -432,7 +436,8 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
return [h('bdi', h(MkA, {
key: Math.random(),
to: isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/user-tags/${encodeURIComponent(token.props.hashtag)}`,
- style: 'color:var(--hashtag);',
+ style: 'color:var(--MI_THEME-hashtag);',
+ behavior: props.linkNavigationBehavior,
}, `#${token.props.hashtag}`))];
}
diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
index 7d13fb9279..20be1b8725 100644
--- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
@@ -248,7 +248,7 @@ onUnmounted(() => {
position: absolute;
bottom: 0;
height: 3px;
- background: var(--accent);
+ background: var(--MI_THEME-accent);
border-radius: var(--radius-ellipse);
transition: none;
pointer-events: none;
diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index bb20f54fa4..cc7633dcff 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -115,7 +115,7 @@ function goBack(): void {
}
const calcBg = () => {
- const rawBg = 'var(--bg)';
+ const rawBg = 'var(--MI_THEME-bg)';
const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
tinyBg.setAlpha(0.85);
bg.value = tinyBg.toRgbString();
@@ -146,9 +146,9 @@ onUnmounted(() => {
<style lang="scss" module>
.root {
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
- border-bottom: solid 0.5px var(--divider);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
width: 100%;
}
@@ -161,7 +161,7 @@ onUnmounted(() => {
.upper {
--height: 50px;
display: flex;
- gap: var(--margin);
+ gap: var(--MI-margin);
height: var(--height);
.tabs:first-child {
@@ -246,7 +246,7 @@ onUnmounted(() => {
}
&.highlighted {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
}
diff --git a/packages/frontend/src/components/global/MkStickyContainer.vue b/packages/frontend/src/components/global/MkStickyContainer.vue
index 72993991ce..1aebf487bb 100644
--- a/packages/frontend/src/components/global/MkStickyContainer.vue
+++ b/packages/frontend/src/components/global/MkStickyContainer.vue
@@ -5,32 +5,30 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div ref="rootEl">
- <div ref="headerEl">
+ <div ref="headerEl" :class="$style.header">
<slot name="header"></slot>
</div>
<div
- ref="bodyEl"
+ :class="$style.body"
:data-sticky-container-header-height="headerHeight"
:data-sticky-container-footer-height="footerHeight"
- style="position: relative; z-index: 0;"
>
<slot></slot>
</div>
- <div ref="footerEl">
+ <div ref="footerEl" :class="$style.footer">
<slot name="footer"></slot>
</div>
</div>
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, provide, inject, Ref, ref, watch, shallowRef } from 'vue';
+import { onMounted, onUnmounted, provide, inject, Ref, ref, watch, useTemplateRef } from 'vue';
import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@@/js/const.js';
-const rootEl = shallowRef<HTMLElement>();
-const headerEl = shallowRef<HTMLElement>();
-const footerEl = shallowRef<HTMLElement>();
-const bodyEl = shallowRef<HTMLElement>();
+const rootEl = useTemplateRef('rootEl');
+const headerEl = useTemplateRef('headerEl');
+const footerEl = useTemplateRef('footerEl');
const headerHeight = ref<string | undefined>();
const childStickyTop = ref(0);
@@ -67,31 +65,11 @@ onMounted(() => {
watch([parentStickyTop, parentStickyBottom], calc);
- watch(childStickyTop, () => {
- if (bodyEl.value == null) return;
- bodyEl.value.style.setProperty('--stickyTop', `${childStickyTop.value}px`);
- }, {
- immediate: true,
- });
-
- watch(childStickyBottom, () => {
- if (bodyEl.value == null) return;
- bodyEl.value.style.setProperty('--stickyBottom', `${childStickyBottom.value}px`);
- }, {
- immediate: true,
- });
-
if (headerEl.value != null) {
- headerEl.value.style.position = 'sticky';
- headerEl.value.style.top = 'var(--stickyTop, 0)';
- headerEl.value.style.zIndex = '1';
observer.observe(headerEl.value);
}
if (footerEl.value != null) {
- footerEl.value.style.position = 'sticky';
- footerEl.value.style.bottom = 'var(--stickyBottom, 0)';
- footerEl.value.style.zIndex = '1';
observer.observe(footerEl.value);
}
});
@@ -101,6 +79,27 @@ onUnmounted(() => {
});
defineExpose({
- rootEl: rootEl,
+ rootEl,
});
</script>
+
+<style lang='scss' module>
+.body {
+ position: relative;
+ z-index: 0;
+ --MI-stickyTop: v-bind("childStickyTop + 'px'");
+ --MI-stickyBottom: v-bind("childStickyBottom + 'px'");
+}
+
+.header {
+ position: sticky;
+ top: var(--MI-stickyTop, 0);
+ z-index: 1;
+}
+
+.footer {
+ position: sticky;
+ bottom: var(--MI-stickyBottom, 0);
+ z-index: 1;
+}
+</style>
diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue
index 50bec990a1..f600f7eed2 100644
--- a/packages/frontend/src/components/global/MkTime.vue
+++ b/packages/frontend/src/components/global/MkTime.vue
@@ -99,10 +99,10 @@ if (!invalid && props.origin === null && (props.mode === 'relative' || props.mod
<style lang="scss" module>
.old1 {
- color: var(--warn);
+ color: var(--MI_THEME-warn);
}
.old1.old2 {
- color: var(--error);
+ color: var(--MI_THEME-error);
}
</style>
diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue
index 19bd794a5d..38bdfc52d4 100644
--- a/packages/frontend/src/components/global/RouterView.vue
+++ b/packages/frontend/src/components/global/RouterView.vue
@@ -27,6 +27,7 @@ import MkLoadingPage from '@/pages/_loading_.vue';
const props = defineProps<{
router?: IRouter;
+ nested?: boolean;
}>();
const router = props.router ?? inject('router');
@@ -39,6 +40,8 @@ const currentDepth = inject('routerCurrentDepth', 0);
provide('routerCurrentDepth', currentDepth + 1);
function resolveNested(current: Resolved, d = 0): Resolved | null {
+ if (!props.nested) return current;
+
if (d === currentDepth) {
return current;
} else {
diff --git a/packages/frontend/src/components/page/page.dynamic.vue b/packages/frontend/src/components/page/page.dynamic.vue
index 8c511a690d..c2449931c1 100644
--- a/packages/frontend/src/components/page/page.dynamic.vue
+++ b/packages/frontend/src/components/page/page.dynamic.vue
@@ -27,9 +27,9 @@ const props = defineProps<{
<style lang="scss" module>
.root {
- border: 1px solid var(--divider);
- border-radius: var(--radius);
- padding: var(--margin);
+ border: 1px solid var(--MI_THEME-divider);
+ border-radius: var(--MI-radius);
+ padding: var(--MI-margin);
text-align: center;
}
diff --git a/packages/frontend/src/components/page/page.image.vue b/packages/frontend/src/components/page/page.image.vue
index fc1ce9fc7b..69443ce7dd 100644
--- a/packages/frontend/src/components/page/page.image.vue
+++ b/packages/frontend/src/components/page/page.image.vue
@@ -28,8 +28,8 @@ onMounted(() => {
<style lang="scss" module>
.root {
- border: 1px solid var(--divider);
- border-radius: var(--radius);
+ border: 1px solid var(--MI_THEME-divider);
+ border-radius: var(--MI-radius);
overflow: hidden;
}
.mediaList {
diff --git a/packages/frontend/src/components/page/page.note.vue b/packages/frontend/src/components/page/page.note.vue
index b5ba407806..84436e7adb 100644
--- a/packages/frontend/src/components/page/page.note.vue
+++ b/packages/frontend/src/components/page/page.note.vue
@@ -35,7 +35,7 @@ onMounted(() => {
<style lang="scss" module>
.root {
- border: 1px solid var(--divider);
- border-radius: var(--radius);
+ border: 1px solid var(--MI_THEME-divider);
+ border-radius: var(--MI-radius);
}
</style>
diff --git a/packages/frontend/src/directives/adaptive-bg.ts b/packages/frontend/src/directives/adaptive-bg.ts
index 23fd1bddf4..45891de889 100644
--- a/packages/frontend/src/directives/adaptive-bg.ts
+++ b/packages/frontend/src/directives/adaptive-bg.ts
@@ -21,7 +21,7 @@ export default {
const myBg = window.getComputedStyle(src).backgroundColor;
if (parentBg === myBg) {
- src.style.backgroundColor = 'var(--bg)';
+ src.style.backgroundColor = 'var(--MI_THEME-bg)';
} else {
src.style.backgroundColor = myBg;
}
diff --git a/packages/frontend/src/directives/adaptive-border.ts b/packages/frontend/src/directives/adaptive-border.ts
index b436075fcd..685ca38e96 100644
--- a/packages/frontend/src/directives/adaptive-border.ts
+++ b/packages/frontend/src/directives/adaptive-border.ts
@@ -21,7 +21,7 @@ export default {
const myBg = window.getComputedStyle(src).backgroundColor;
if (parentBg === myBg) {
- src.style.borderColor = 'var(--divider)';
+ src.style.borderColor = 'var(--MI_THEME-divider)';
} else {
src.style.borderColor = myBg;
}
diff --git a/packages/frontend/src/directives/panel.ts b/packages/frontend/src/directives/panel.ts
index bbcc220e09..7b5969c679 100644
--- a/packages/frontend/src/directives/panel.ts
+++ b/packages/frontend/src/directives/panel.ts
@@ -18,12 +18,12 @@ export default {
const parentBg = getBgColor(src.parentElement);
- const myBg = getComputedStyle(document.documentElement).getPropertyValue('--panel');
+ const myBg = getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel');
if (parentBg === myBg) {
- src.style.backgroundColor = 'var(--bg)';
+ src.style.backgroundColor = 'var(--MI_THEME-bg)';
} else {
- src.style.backgroundColor = 'var(--panel)';
+ src.style.backgroundColor = 'var(--MI_THEME-panel)';
}
},
} as Directive;
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index e73aa77a3c..5d3d4c0a55 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -10,6 +10,7 @@ import { EventEmitter } from 'eventemitter3';
import * as Misskey from 'misskey-js';
import type { ComponentProps as CP } from 'vue-component-type-helpers';
import type { Form, GetFormResultType } from '@/scripts/form.js';
+import type { MenuItem } from '@/types/menu.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js';
@@ -22,7 +23,6 @@ import MkPasswordDialog from '@/components/MkPasswordDialog.vue';
import MkEmojiPickerDialog from '@/components/MkEmojiPickerDialog.vue';
import MkPopupMenu from '@/components/MkPopupMenu.vue';
import MkContextMenu from '@/components/MkContextMenu.vue';
-import type { MenuItem } from '@/types/menu.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { pleaseLogin } from '@/scripts/please-login.js';
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
@@ -35,6 +35,7 @@ export const apiWithDialog = (<E extends keyof Misskey.Endpoints = keyof Misskey
endpoint: E,
data: P = {} as any,
token?: string | null | undefined,
+ customErrors?: Record<string, { title?: string; text: string; }>,
) => {
const promise = misskeyApi(endpoint, data, token);
promiseDialog(promise, null, async (err) => {
@@ -77,6 +78,9 @@ export const apiWithDialog = (<E extends keyof Misskey.Endpoints = keyof Misskey
} else if (err.message.startsWith('Unexpected token')) {
title = i18n.ts.gotInvalidResponseError;
text = i18n.ts.gotInvalidResponseErrorDescription;
+ } else if (customErrors && customErrors[err.id] != null) {
+ title = customErrors[err.id].title;
+ text = customErrors[err.id].text;
}
alert({
type: 'error',
@@ -86,7 +90,7 @@ export const apiWithDialog = (<E extends keyof Misskey.Endpoints = keyof Misskey
});
return promise;
-}) as typeof misskeyApi;
+});
export function promiseDialog<T extends Promise<any>>(
promise: T,
diff --git a/packages/frontend/src/pages/about.federation.vue b/packages/frontend/src/pages/about.federation.vue
index 71353c7dfa..2190be8bec 100644
--- a/packages/frontend/src/pages/about.federation.vue
+++ b/packages/frontend/src/pages/about.federation.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #prefix><i class="ti ti-search"></i></template>
<template #label>{{ i18n.ts.host }}</template>
</MkInput>
- <FormSplit style="margin-top: var(--margin);">
+ <FormSplit style="margin-top: var(--MI-margin);">
<MkSelect v-model="state">
<template #label>{{ i18n.ts.state }}</template>
<option value="all">{{ i18n.ts.all }}</option>
diff --git a/packages/frontend/src/pages/about.overview.vue b/packages/frontend/src/pages/about.overview.vue
index ca070c65a4..42898b3447 100644
--- a/packages/frontend/src/pages/about.overview.vue
+++ b/packages/frontend/src/pages/about.overview.vue
@@ -172,7 +172,7 @@ await misskeyApi('sponsors', { instance: true }).then((res) => sponsors.value.pu
text-align: center;
border-radius: var(--radius);
overflow: clip;
- background-color: var(--panel);
+ background-color: var(--MI_THEME-panel);
background-size: cover;
background-position: center center;
}
@@ -208,14 +208,14 @@ await misskeyApi('sponsors', { instance: true }).then((res) => sponsors.value.pu
flex-shrink: 0;
display: flex;
position: sticky;
- top: calc(var(--stickyTop, 0px) + 8px);
+ top: calc(var(--MI-stickyTop, 0px) + 8px);
counter-increment: item;
content: counter(item);
width: 32px;
height: 32px;
line-height: 32px;
- background-color: var(--accentedBg);
- color: var(--accent);
+ background-color: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
font-size: 13px;
font-weight: bold;
align-items: center;
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index 22e16effe0..f63b81c393 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -55,6 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkTextarea v-model="moderationNote" manualSave>
<template #label>{{ i18n.ts.moderationNote }}</template>
+ <template #caption>{{ i18n.ts.moderationNoteDescription }}</template>
</MkTextarea>
<FormSection v-if="user.host">
@@ -140,15 +141,21 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-else-if="tab === 'announcements'" class="_gaps">
<MkButton primary rounded @click="createAnnouncement"><i class="ti ti-plus"></i> {{ i18n.ts._announcement.new }}</MkButton>
+ <MkSelect v-model="announcementsStatus">
+ <template #label>{{ i18n.ts.filter }}</template>
+ <option value="active">{{ i18n.ts.active }}</option>
+ <option value="archived">{{ i18n.ts.archived }}</option>
+ </MkSelect>
+
<MkPagination :pagination="announcementsPagination">
<template #default="{ items }">
<div class="_gaps_s">
<div v-for="announcement in items" :key="announcement.id" v-panel :class="$style.announcementItem" @click="editAnnouncement(announcement)">
<span style="margin-right: 0.5em;">
<i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i>
- <i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--warn);"></i>
- <i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--error);"></i>
- <i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--success);"></i>
+ <i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i>
+ <i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--MI_THEME-error);"></i>
+ <i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--MI_THEME-success);"></i>
</span>
<span>{{ announcement.title }}</span>
<span v-if="announcement.reads > 0" style="margin-left: auto; opacity: 0.7;">{{ i18n.ts.messageRead }}</span>
@@ -193,6 +200,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, defineAsyncComponent, watch, ref } from 'vue';
import * as Misskey from 'misskey-js';
+import { url } from '@@/js/config.js';
import MkChart from '@/components/MkChart.vue';
import MkObjectView from '@/components/MkObjectView.vue';
import MkTextarea from '@/components/MkTextarea.vue';
@@ -208,7 +216,6 @@ import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
import MkInfo from '@/components/MkInfo.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
-import { url } from '@@/js/config.js';
import { acct } from '@/filters/user.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { i18n } from '@/i18n.js';
@@ -244,11 +251,15 @@ const filesPagination = {
userId: props.userId,
})),
};
+
+const announcementsStatus = ref<'active' | 'archived'>('active');
+
const announcementsPagination = {
endpoint: 'admin/announcements/list' as const,
limit: 10,
params: computed(() => ({
userId: props.userId,
+ status: announcementsStatus.value,
})),
};
const expandedRoles = ref([]);
@@ -600,18 +611,18 @@ definePageMetadata(() => ({
}
> .suspended {
- color: var(--error);
- border-color: var(--error);
+ color: var(--MI_THEME-error);
+ border-color: var(--MI_THEME-error);
}
> .silenced {
- color: var(--warn);
- border-color: var(--warn);
+ color: var(--MI_THEME-warn);
+ border-color: var(--MI_THEME-warn);
}
> .moderator {
- color: var(--success);
- border-color: var(--success);
+ color: var(--MI_THEME-success);
+ border-color: var(--MI_THEME-success);
}
}
}
@@ -670,7 +681,7 @@ definePageMetadata(() => ({
.roleItemSub {
padding: 6px 12px;
font-size: 85%;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
}
.roleUnassign {
diff --git a/packages/frontend/src/pages/admin/RolesEditorFormula.vue b/packages/frontend/src/pages/admin/RolesEditorFormula.vue
index f001a4ac20..4762ef3f97 100644
--- a/packages/frontend/src/pages/admin/RolesEditorFormula.vue
+++ b/packages/frontend/src/pages/admin/RolesEditorFormula.vue
@@ -155,12 +155,12 @@ function removeSelf() {
}
.item {
- border: solid 2px var(--divider);
- border-radius: var(--radius);
+ border: solid 2px var(--MI_THEME-divider);
+ border-radius: var(--MI-radius);
padding: 12px;
&:hover {
- border-color: var(--accent);
+ border-color: var(--MI_THEME-accent);
}
}
</style>
diff --git a/packages/frontend/src/pages/admin/_header_.vue b/packages/frontend/src/pages/admin/_header_.vue
index 2a71e3efab..b061b2fa0c 100644
--- a/packages/frontend/src/pages/admin/_header_.vue
+++ b/packages/frontend/src/pages/admin/_header_.vue
@@ -119,7 +119,7 @@ function onTabClick(tab: Tab, ev: MouseEvent): void {
}
const calcBg = () => {
- const rawBg = pageMetadata.value?.bg ?? 'var(--bg)';
+ const rawBg = pageMetadata.value?.bg ?? 'var(--MI_THEME-bg)';
const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
tinyBg.setAlpha(0.85);
bg.value = tinyBg.toRgbString();
@@ -156,8 +156,8 @@ onUnmounted(() => {
--height: 60px;
display: flex;
width: 100%;
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
> .buttons {
--margin: 8px;
@@ -189,7 +189,7 @@ onUnmounted(() => {
}
&.highlighted {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
}
@@ -286,7 +286,7 @@ onUnmounted(() => {
position: absolute;
bottom: 0;
height: 3px;
- background: var(--accent);
+ background: var(--MI_THEME-accent);
border-radius: var(--radius-ellipse);
transition: all 0.2s ease;
pointer-events: none;
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 827e22e8ae..eef24afd32 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
@@ -294,10 +294,10 @@ onMounted(async () => {
bottom: 0;
left: 0;
padding: 12px;
- border-top: solid 0.5px var(--divider);
- background: var(--acrylicBg);
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
+ border-top: solid 0.5px var(--MI_THEME-divider);
+ background: var(--MI_THEME-acrylicBg);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
}
.systemWebhook {
diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue
index 0b86808faf..36d586bd23 100644
--- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue
+++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue
@@ -87,7 +87,7 @@ function onDeleteButtonClicked() {
}
.rightDivider {
- border-right: 0.5px solid var(--divider);
+ border-right: 0.5px solid var(--MI_THEME-divider);
}
.recipientButtons {
@@ -108,7 +108,7 @@ function onDeleteButtonClicked() {
padding: 8px;
&:hover {
- background-color: var(--buttonBg);
+ background-color: var(--MI_THEME-buttonBg);
}
}
</style>
diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue
index c8a9ca7112..a164ecb1fe 100644
--- a/packages/frontend/src/pages/admin/abuses.vue
+++ b/packages/frontend/src/pages/admin/abuses.vue
@@ -12,6 +12,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton link to="/admin/abuse-report-notification-recipient" primary>{{ i18n.ts.notificationSetting }}</MkButton>
</div>
+ <MkInfo v-if="!defaultStore.reactiveState.abusesTutorial.value" closable @close="closeTutorial()">
+ {{ i18n.ts._abuseUserReport.resolveTutorial }}
+ </MkInfo>
+
<div :class="$style.inputs" class="_gaps">
<MkSelect v-model="state" style="margin: 0; flex: 1;">
<template #label>{{ i18n.ts.state }}</template>
@@ -44,8 +48,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
-->
- <MkPagination v-slot="{items}" ref="reports" :pagination="pagination" :displayLimit="50" style="margin-top: var(--margin);">
- <XAbuseReport v-for="report in items" :key="report.id" :report="report" @resolved="resolved"/>
+ <MkPagination v-slot="{items}" ref="reports" :pagination="pagination" :displayLimit="50">
+ <div class="_gaps">
+ <XAbuseReport v-for="report in items" :key="report.id" :report="report" @resolved="resolved"/>
+ </div>
</MkPagination>
</div>
</MkSpacer>
@@ -54,7 +60,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, shallowRef, ref } from 'vue';
-
import XHeader from './_header_.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkPagination from '@/components/MkPagination.vue';
@@ -62,6 +67,8 @@ import XAbuseReport from '@/components/MkAbuseReport.vue';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkButton from '@/components/MkButton.vue';
+import MkInfo from '@/components/MkInfo.vue';
+import { defaultStore } from '@/store.js';
const reports = shallowRef<InstanceType<typeof MkPagination>>();
@@ -85,6 +92,10 @@ function resolved(reportId) {
reports.value?.removeItem(reportId);
}
+function closeTutorial() {
+ defaultStore.set('abusesTutorial', false);
+}
+
const headerActions = computed(() => []);
const headerTabs = computed(() => []);
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue
index 6c8901b10b..0d67359e47 100644
--- a/packages/frontend/src/pages/admin/ads.vue
+++ b/packages/frontend/src/pages/admin/ads.vue
@@ -266,7 +266,7 @@ definePageMetadata(() => ({
padding: 32px;
&:not(:last-child) {
- margin-bottom: var(--margin);
+ margin-bottom: var(--MI-margin);
}
}
.input {
diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue
index fd37311b21..e420586017 100644
--- a/packages/frontend/src/pages/admin/announcements.vue
+++ b/packages/frontend/src/pages/admin/announcements.vue
@@ -24,9 +24,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ announcement.title }}</template>
<template #icon>
<i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i>
- <i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--warn);"></i>
- <i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--error);"></i>
- <i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--success);"></i>
+ <i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i>
+ <i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--MI_THEME-error);"></i>
+ <i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--MI_THEME-success);"></i>
</template>
<template #caption>{{ announcement.text }}</template>
<template #footer>
@@ -51,9 +51,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkRadios v-model="announcement.icon">
<template #label>{{ i18n.ts.icon }}</template>
<option value="info"><i class="ti ti-info-circle"></i></option>
- <option value="warning"><i class="ti ti-alert-triangle" style="color: var(--warn);"></i></option>
- <option value="error"><i class="ti ti-circle-x" style="color: var(--error);"></i></option>
- <option value="success"><i class="ti ti-check" style="color: var(--success);"></i></option>
+ <option value="warning"><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i></option>
+ <option value="error"><i class="ti ti-circle-x" style="color: var(--MI_THEME-error);"></i></option>
+ <option value="success"><i class="ti ti-check" style="color: var(--MI_THEME-success);"></i></option>
</MkRadios>
<MkRadios v-model="announcement.display">
<template #label>{{ i18n.ts.display }}</template>
diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue
index 644436cde6..2f6dac8097 100644
--- a/packages/frontend/src/pages/admin/bot-protection.vue
+++ b/packages/frontend/src/pages/admin/bot-protection.vue
@@ -12,6 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-else-if="botProtectionForm.savedState.provider === 'recaptcha'" #suffix>reCAPTCHA</template>
<template v-else-if="botProtectionForm.savedState.provider === 'turnstile'" #suffix>Turnstile</template>
<template v-else-if="botProtectionForm.savedState.provider === 'fc'" #suffix>FriendlyCaptcha</template>
+ <template v-else-if="botProtectionForm.savedState.provider === 'testcaptcha'" #suffix>testCaptcha</template>
<template v-else #suffix>{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</template>
<template v-if="botProtectionForm.modified.value" #footer>
<MkFormFooter :form="botProtectionForm"/>
@@ -25,6 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<option value="recaptcha">reCAPTCHA</option>
<option value="turnstile">Turnstile</option>
<option value="fc">FriendlyCaptcha</option>
+ <option value="testcaptcha">testCaptcha</option>
</MkRadios>
<template v-if="botProtectionForm.state.provider === 'hcaptcha'">
@@ -101,6 +103,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkCaptcha provider="fc" :sitekey="botProtectionForm.state.fcSiteKey"/>
</FormSlot>
</template>
+ <template v-else-if="botProtectionForm.state.provider === 'testcaptcha'">
+ <MkInfo warn><span v-html="i18n.ts.testCaptchaWarning"></span></MkInfo>
+ <FormSlot>
+ <template #label>{{ i18n.ts.preview }}</template>
+ <MkCaptcha provider="testcaptcha"/>
+ </FormSlot>
+ </template>
</div>
</MkFolder>
</template>
@@ -117,6 +126,7 @@ import { i18n } from '@/i18n.js';
import { useForm } from '@/scripts/use-form.js';
import MkFormFooter from '@/components/MkFormFooter.vue';
import MkFolder from '@/components/MkFolder.vue';
+import MkInfo from '@/components/MkInfo.vue';
const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));
@@ -133,6 +143,8 @@ const botProtectionForm = useForm({
? 'mcaptcha'
: meta.enableFC
? 'fc'
+ : meta.enableTestcaptcha
+ ? 'testcaptcha'
: null,
hcaptchaSiteKey: meta.hcaptchaSiteKey,
hcaptchaSecretKey: meta.hcaptchaSecretKey,
@@ -163,6 +175,7 @@ const botProtectionForm = useForm({
enableFC: state.provider === 'fc',
fcSiteKey: state.fcSiteKey,
fcSecretKey: state.fcSecretKey,
+ enableTestcaptcha: state.provider === 'testcaptcha',
});
fetchInstance(true);
});
diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue
index d3d52002fe..cc05466832 100644
--- a/packages/frontend/src/pages/admin/branding.vue
+++ b/packages/frontend/src/pages/admin/branding.vue
@@ -218,7 +218,7 @@ definePageMetadata(() => ({
<style lang="scss" module>
.footer {
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
}
</style>
diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue
index ddfe5ae81f..2f1d12ebb5 100644
--- a/packages/frontend/src/pages/admin/email-settings.vue
+++ b/packages/frontend/src/pages/admin/email-settings.vue
@@ -138,7 +138,7 @@ definePageMetadata(() => ({
<style lang="scss" module>
.footer {
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
}
</style>
diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue
index 1902c97724..ef6bbb865b 100644
--- a/packages/frontend/src/pages/admin/federation.vue
+++ b/packages/frontend/src/pages/admin/federation.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #prefix><i class="ti ti-search"></i></template>
<template #label>{{ i18n.ts.host }}</template>
</MkInput>
- <FormSplit style="margin-top: var(--margin);">
+ <FormSplit style="margin-top: var(--MI-margin);">
<MkSelect v-model="state">
<template #label>{{ i18n.ts.state }}</template>
<option value="all">{{ i18n.ts.all }}</option>
diff --git a/packages/frontend/src/pages/admin/files.vue b/packages/frontend/src/pages/admin/files.vue
index 5132b85c64..4cc859227f 100644
--- a/packages/frontend/src/pages/admin/files.vue
+++ b/packages/frontend/src/pages/admin/files.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #header><XHeader :actions="headerActions"/></template>
<MkSpacer :contentMax="900">
<div class="_gaps">
- <div class="inputs" style="display: flex; gap: var(--margin); flex-wrap: wrap;">
+ <div class="inputs" style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;">
<MkSelect v-model="origin" style="margin: 0; flex: 1;">
<template #label>{{ i18n.ts.instance }}</template>
<option value="combined">{{ i18n.ts.all }}</option>
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.host }}</template>
</MkInput>
</div>
- <div class="inputs" style="display: flex; gap: var(--margin); flex-wrap: wrap;">
+ <div class="inputs" style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;">
<MkInput v-model="userId" :debounce="true" type="search" style="margin: 0; flex: 1;">
<template #label>User ID</template>
</MkInput>
diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index e8d123060a..1fb49d51d9 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSpacer>
</div>
<div v-if="!(narrow && currentPage?.route.name == null)" class="main">
- <RouterView/>
+ <RouterView nested/>
</div>
</div>
</template>
@@ -346,7 +346,7 @@ defineExpose({
width: 32%;
max-width: 280px;
box-sizing: border-box;
- border-right: solid 0.5px var(--divider);
+ border-right: solid 0.5px var(--MI_THEME-divider);
overflow: auto;
height: 100%;
}
diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue
index bbcf2a6f77..03d1731a74 100644
--- a/packages/frontend/src/pages/admin/moderation.vue
+++ b/packages/frontend/src/pages/admin/moderation.vue
@@ -12,6 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_m">
<MkSwitch v-model="enableRegistration" @change="onChange_enableRegistration">
<template #label>{{ i18n.ts.enableRegistration }}</template>
+ <template #caption>{{ i18n.ts._serverSettings.thisSettingWillAutomaticallyOffWhenModeratorsInactive }}</template>
</MkSwitch>
<MkSwitch v-model="emailRequiredForSignup" @change="onChange_emailRequiredForSignup">
@@ -85,6 +86,18 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkFolder>
<MkFolder>
+ <template #icon><i class="ti ti-user-x"></i></template>
+ <template #label>{{ i18n.ts.prohibitedWordsForNameOfUser }}</template>
+
+ <div class="_gaps">
+ <MkTextarea v-model="prohibitedWordsForNameOfUser">
+ <template #caption>{{ i18n.ts.prohibitedWordsForNameOfUserDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template>
+ </MkTextarea>
+ <MkButton primary @click="save_prohibitedWordsForNameOfUser">{{ i18n.ts.save }}</MkButton>
+ </div>
+ </MkFolder>
+
+ <MkFolder>
<template #icon><i class="ti ti-eye-off"></i></template>
<template #label>{{ i18n.ts.hiddenTags }}</template>
@@ -160,6 +173,7 @@ const approvalRequiredForSignup = ref<boolean>(false);
const bubbleTimelineEnabled = ref<boolean>(false);
const sensitiveWords = ref<string>('');
const prohibitedWords = ref<string>('');
+const prohibitedWordsForNameOfUser = ref<string>('');
const hiddenTags = ref<string>('');
const preservedUsernames = ref<string>('');
const bubbleTimeline = ref<string>('');
@@ -175,13 +189,14 @@ async function init() {
approvalRequiredForSignup.value = meta.approvalRequiredForSignup;
sensitiveWords.value = meta.sensitiveWords.join('\n');
prohibitedWords.value = meta.prohibitedWords.join('\n');
+ prohibitedWordsForNameOfUser.value = meta.prohibitedWordsForNameOfUser.join('\n');
hiddenTags.value = meta.hiddenTags.join('\n');
preservedUsernames.value = meta.preservedUsernames.join('\n');
bubbleTimeline.value = meta.bubbleInstances.join('\n');
bubbleTimelineEnabled.value = meta.policies.btlAvailable;
trustedLinkUrlPatterns.value = meta.trustedLinkUrlPatterns.join('\n');
blockedHosts.value = meta.blockedHosts.join('\n');
- silencedHosts.value = meta.silencedHosts.join('\n');
+ silencedHosts.value = meta.silencedHosts?.join('\n') ?? '';
mediaSilencedHosts.value = meta.mediaSilencedHosts.join('\n');
}
@@ -249,6 +264,14 @@ function save_prohibitedWords() {
});
}
+function save_prohibitedWordsForNameOfUser() {
+ os.apiWithDialog('admin/update-meta', {
+ prohibitedWordsForNameOfUser: prohibitedWordsForNameOfUser.value.split('\n'),
+ }).then(() => {
+ fetchInstance(true);
+ });
+}
+
function save_hiddenTags() {
os.apiWithDialog('admin/update-meta', {
hiddenTags: hiddenTags.value.split('\n'),
diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue
index 6c81155c51..8af8d29e1a 100644
--- a/packages/frontend/src/pages/admin/modlog.ModLog.vue
+++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue
@@ -101,7 +101,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<div>
- <div style="display: flex; gap: var(--margin); flex-wrap: wrap;">
+ <div style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;">
<div style="flex: 1;">{{ i18n.ts.moderator }}: <MkA :to="`/admin/user/${log.userId}`" class="_link">@{{ log.user?.username }}</MkA></div>
<div style="flex: 1;">{{ i18n.ts.dateAndTime }}: <MkTime :time="log.createdAt" mode="detail"/></div>
</div>
@@ -180,6 +180,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
</div>
</template>
+ <template v-else-if="log.type === 'updateAbuseReportNote'">
+ <div :class="$style.diff">
+ <CodeDiff :context="5" :hideHeader="true" :oldString="log.info.before ?? ''" :newString="log.info.after ?? ''" maxHeight="300px"/>
+ </div>
+ </template>
<details>
<summary>raw</summary>
@@ -215,14 +220,14 @@ const props = defineProps<{
}
.logYellow {
- color: var(--warn);
+ color: var(--MI_THEME-warn);
}
.logRed {
- color: var(--error);
+ color: var(--MI_THEME-error);
}
.logGreen {
- color: var(--success);
+ color: var(--MI_THEME-success);
}
</style>
diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue
index 4e0d0f941e..35f939f1be 100644
--- a/packages/frontend/src/pages/admin/modlog.vue
+++ b/packages/frontend/src/pages/admin/modlog.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :contentMax="900">
<div>
- <div style="display: flex; gap: var(--margin); flex-wrap: wrap;">
+ <div style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;">
<MkSelect v-model="type" style="margin: 0; flex: 1;">
<template #label>{{ i18n.ts.type }}</template>
<option :value="null">{{ i18n.ts.all }}</option>
@@ -19,10 +19,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkInput>
</div>
- <MkPagination v-slot="{items}" ref="logs" :pagination="pagination" :displayLimit="50" style="margin-top: var(--margin);">
- <div class="_gaps_s">
- <XModLog v-for="item in items" :key="item.id" :log="item"/>
- </div>
+ <MkPagination v-slot="{items}" ref="logs" :pagination="pagination" :displayLimit="50" style="margin-top: var(--MI-margin);">
+ <MkDateSeparatedList v-slot="{ item }" :items="items" :noGap="false" style="--MI-margin: 8px;">
+ <XModLog :key="item.id" :log="item"/>
+ </MkDateSeparatedList>
</MkPagination>
</div>
</MkSpacer>
@@ -39,6 +39,7 @@ import MkInput from '@/components/MkInput.vue';
import MkPagination from '@/components/MkPagination.vue';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
+import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
const logs = shallowRef<InstanceType<typeof MkPagination>>();
diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue
index 5fddb715cd..d5a664934c 100644
--- a/packages/frontend/src/pages/admin/object-storage.vue
+++ b/packages/frontend/src/pages/admin/object-storage.vue
@@ -157,7 +157,7 @@ definePageMetadata(() => ({
<style lang="scss" module>
.footer {
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
}
</style>
diff --git a/packages/frontend/src/pages/admin/overview.ap-requests.vue b/packages/frontend/src/pages/admin/overview.ap-requests.vue
index 4bbb9210af..570fcddc07 100644
--- a/packages/frontend/src/pages/admin/overview.ap-requests.vue
+++ b/packages/frontend/src/pages/admin/overview.ap-requests.vue
@@ -278,7 +278,7 @@ onMounted(async () => {
padding: 16px;
&:first-child {
- border-bottom: solid 0.5px var(--divider);
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
}
}
}
diff --git a/packages/frontend/src/pages/admin/overview.federation.vue b/packages/frontend/src/pages/admin/overview.federation.vue
index ea01d073ea..1ada4a5251 100644
--- a/packages/frontend/src/pages/admin/overview.federation.vue
+++ b/packages/frontend/src/pages/admin/overview.federation.vue
@@ -151,8 +151,8 @@ onMounted(async () => {
height: 100%;
aspect-ratio: 1;
margin-right: 12px;
- background: var(--accentedBg);
- color: var(--accent);
+ background: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
border-radius: var(--radius);
}
diff --git a/packages/frontend/src/pages/admin/overview.pie.vue b/packages/frontend/src/pages/admin/overview.pie.vue
index c7a9f2a702..a21ec6c464 100644
--- a/packages/frontend/src/pages/admin/overview.pie.vue
+++ b/packages/frontend/src/pages/admin/overview.pie.vue
@@ -41,7 +41,7 @@ onMounted(() => {
labels: props.data.map(x => x.name),
datasets: [{
backgroundColor: props.data.map(x => x.color),
- borderColor: getComputedStyle(document.documentElement).getPropertyValue('--panel'),
+ borderColor: getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel'),
borderWidth: 2,
hoverOffset: 0,
data: props.data.map(x => x.value),
diff --git a/packages/frontend/src/pages/admin/overview.queue.vue b/packages/frontend/src/pages/admin/overview.queue.vue
index fb190f5325..de6b254412 100644
--- a/packages/frontend/src/pages/admin/overview.queue.vue
+++ b/packages/frontend/src/pages/admin/overview.queue.vue
@@ -119,8 +119,8 @@ onUnmounted(() => {
> .chart {
min-width: 0;
padding: 16px;
- background: var(--panel);
- border-radius: var(--radius);
+ background: var(--MI_THEME-panel);
+ border-radius: var(--MI-radius);
> .title {
font-size: 0.85em;
diff --git a/packages/frontend/src/pages/admin/overview.stats.vue b/packages/frontend/src/pages/admin/overview.stats.vue
index 37399a5724..12f2c82ff7 100644
--- a/packages/frontend/src/pages/admin/overview.stats.vue
+++ b/packages/frontend/src/pages/admin/overview.stats.vue
@@ -114,8 +114,8 @@ onMounted(async () => {
height: 100%;
aspect-ratio: 1;
margin-right: 12px;
- background: var(--accentedBg);
- color: var(--accent);
+ background: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
border-radius: var(--radius);
}
diff --git a/packages/frontend/src/pages/admin/performance.vue b/packages/frontend/src/pages/admin/performance.vue
index 7e0a932f82..12338f0bf9 100644
--- a/packages/frontend/src/pages/admin/performance.vue
+++ b/packages/frontend/src/pages/admin/performance.vue
@@ -30,6 +30,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div class="_panel" style="padding: 16px;">
+ <MkSwitch v-model="enableStatsForFederatedInstances" @change="onChange_enableStatsForFederatedInstances">
+ <template #label>{{ i18n.ts.enableStatsForFederatedInstances }}</template>
+ <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
+ </MkSwitch>
+ </div>
+
+ <div class="_panel" style="padding: 16px;">
<MkSwitch v-model="enableChartsForFederatedInstances" @change="onChange_enableChartsForFederatedInstances">
<template #label>{{ i18n.ts.enableChartsForFederatedInstances }}</template>
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
@@ -120,6 +127,7 @@ const meta = await misskeyApi('admin/meta');
const enableServerMachineStats = ref(meta.enableServerMachineStats);
const enableIdenticonGeneration = ref(meta.enableIdenticonGeneration);
const enableChartsForRemoteUser = ref(meta.enableChartsForRemoteUser);
+const enableStatsForFederatedInstances = ref(meta.enableStatsForFederatedInstances);
const enableChartsForFederatedInstances = ref(meta.enableChartsForFederatedInstances);
function onChange_enableServerMachineStats(value: boolean) {
@@ -146,6 +154,14 @@ function onChange_enableChartsForRemoteUser(value: boolean) {
});
}
+function onChange_enableStatsForFederatedInstances(value: boolean) {
+ os.apiWithDialog('admin/update-meta', {
+ enableStatsForFederatedInstances: value,
+ }).then(() => {
+ fetchInstance(true);
+ });
+}
+
function onChange_enableChartsForFederatedInstances(value: boolean) {
os.apiWithDialog('admin/update-meta', {
enableChartsForFederatedInstances: value,
diff --git a/packages/frontend/src/pages/admin/queue.chart.vue b/packages/frontend/src/pages/admin/queue.chart.vue
index 960a263a86..7c171ba0e1 100644
--- a/packages/frontend/src/pages/admin/queue.chart.vue
+++ b/packages/frontend/src/pages/admin/queue.chart.vue
@@ -135,8 +135,8 @@ onUnmounted(() => {
.chart {
min-width: 0;
padding: 16px;
- background: var(--panel);
- border-radius: var(--radius);
+ background: var(--MI_THEME-panel);
+ border-radius: var(--MI-radius);
}
.chartTitle {
diff --git a/packages/frontend/src/pages/admin/relays.vue b/packages/frontend/src/pages/admin/relays.vue
index 04982eea1f..17e99e6593 100644
--- a/packages/frontend/src/pages/admin/relays.vue
+++ b/packages/frontend/src/pages/admin/relays.vue
@@ -11,8 +11,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel" style="padding: 16px;">
<div>{{ relay.inbox }}</div>
<div style="margin: 8px 0;">
- <i v-if="relay.status === 'accepted'" class="ti ti-check" :class="$style.icon" style="color: var(--success);"></i>
- <i v-else-if="relay.status === 'rejected'" class="ti ti-ban" :class="$style.icon" style="color: var(--error);"></i>
+ <i v-if="relay.status === 'accepted'" class="ti ti-check" :class="$style.icon" style="color: var(--MI_THEME-success);"></i>
+ <i v-else-if="relay.status === 'rejected'" class="ti ti-ban" :class="$style.icon" style="color: var(--MI_THEME-error);"></i>
<i v-else class="ti ti-clock" :class="$style.icon"></i>
<span>{{ i18n.ts._relayStatus[relay.status] }}</span>
</div>
diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue
index 60f06d50ba..2b4006c3f7 100644
--- a/packages/frontend/src/pages/admin/roles.edit.vue
+++ b/packages/frontend/src/pages/admin/roles.edit.vue
@@ -95,7 +95,7 @@ definePageMetadata(() => ({
<style lang="scss" module>
.footer {
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
}
</style>
diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue
index 6bce60cfb9..d1c7be39d6 100644
--- a/packages/frontend/src/pages/admin/roles.role.vue
+++ b/packages/frontend/src/pages/admin/roles.role.vue
@@ -184,7 +184,7 @@ definePageMetadata(() => ({
.userItemSub {
padding: 6px 12px;
font-size: 85%;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
}
.userItemMainBody {
diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue
index 4788c16830..1ce7190d9c 100644
--- a/packages/frontend/src/pages/admin/server-rules.vue
+++ b/packages/frontend/src/pages/admin/server-rules.vue
@@ -76,7 +76,7 @@ definePageMetadata(() => ({
<style lang="scss" module>
.item {
display: block;
- color: var(--navFg);
+ color: var(--MI_THEME-navFg);
}
.itemHeader {
@@ -96,8 +96,8 @@ definePageMetadata(() => ({
.itemNumber {
display: flex;
- background-color: var(--accentedBg);
- color: var(--accent);
+ background-color: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
font-size: 14px;
font-weight: bold;
width: 28px;
@@ -117,12 +117,12 @@ definePageMetadata(() => ({
.itemRemove {
width: 40px;
height: 40px;
- color: var(--error);
+ color: var(--MI_THEME-error);
margin-left: auto;
border-radius: var(--radius-sm);
&:hover {
- background: var(--X5);
+ background: var(--MI_THEME-X5);
}
}
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index 04975dcbf3..68f211de5c 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -438,6 +438,6 @@ definePageMetadata(() => ({
<style lang="scss" module>
.subCaption {
font-size: 0.85em;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
}
</style>
diff --git a/packages/frontend/src/pages/admin/system-webhook.item.vue b/packages/frontend/src/pages/admin/system-webhook.item.vue
index 4e767fba16..45f0fff107 100644
--- a/packages/frontend/src/pages/admin/system-webhook.item.vue
+++ b/packages/frontend/src/pages/admin/system-webhook.item.vue
@@ -6,15 +6,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<MkFolder>
<template #label>{{ entity.name || entity.url }}</template>
+ <template v-if="entity.name != null && entity.name != ''" #caption>{{ entity.url }}</template>
<template #icon>
<i v-if="!entity.isActive" class="ti ti-player-pause"/>
<i v-else-if="entity.latestStatus === null" class="ti ti-circle"/>
<i
v-else-if="[200, 201, 204].includes(entity.latestStatus)"
class="ti ti-check"
- :style="{ color: 'var(--success)' }"
+ :style="{ color: 'var(--MI_THEME-success)' }"
/>
- <i v-else class="ti ti-alert-triangle" :style="{ color: 'var(--error)' }"/>
+ <i v-else class="ti ti-alert-triangle" :style="{ color: 'var(--MI_THEME-error)' }"/>
</template>
<template #suffix>
<MkTime v-if="entity.latestSentAt" :time="entity.latestSentAt" style="margin-right: 8px"/>
@@ -74,6 +75,6 @@ function onDeleteClick() {
margin-right: 0.75em;
flex-shrink: 0;
text-align: center;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
}
</style>
diff --git a/packages/frontend/src/pages/announcement.vue b/packages/frontend/src/pages/announcement.vue
index 802a6bf399..3840e6a494 100644
--- a/packages/frontend/src/pages/announcement.vue
+++ b/packages/frontend/src/pages/announcement.vue
@@ -20,9 +20,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<span v-if="$i && !announcement.silence && !announcement.isRead" style="margin-right: 0.5em;">🆕</span>
<span style="margin-right: 0.5em;">
<i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i>
- <i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--warn);"></i>
- <i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--error);"></i>
- <i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--success);"></i>
+ <i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i>
+ <i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--MI_THEME-error);"></i>
+ <i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--MI_THEME-success);"></i>
</span>
<Mfm :text="announcement.title"/>
</div>
diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue
index a9bcfee803..4eee275f07 100644
--- a/packages/frontend/src/pages/announcements.vue
+++ b/packages/frontend/src/pages/announcements.vue
@@ -17,9 +17,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<span v-if="$i && !announcement.silence && !announcement.isRead" style="margin-right: 0.5em;">🆕</span>
<span style="margin-right: 0.5em;">
<i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i>
- <i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--warn);"></i>
- <i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--error);"></i>
- <i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--success);"></i>
+ <i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i>
+ <i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--MI_THEME-error);"></i>
+ <i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--MI_THEME-success);"></i>
</span>
<MkA :to="`/announcements/${announcement.id}`"><span>{{ announcement.title }}</span></MkA>
</div>
diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue
index e57e212b60..fbfbb6edf7 100644
--- a/packages/frontend/src/pages/antenna-timeline.vue
+++ b/packages/frontend/src/pages/antenna-timeline.vue
@@ -97,26 +97,26 @@ definePageMetadata(() => ({
<style lang="scss" module>
.new {
position: sticky;
- top: calc(var(--stickyTop, 0px) + 16px);
+ top: calc(var(--MI-stickyTop, 0px) + 16px);
z-index: 1000;
width: 100%;
margin: calc(-0.675em - 8px) 0;
&:first-child {
- margin-top: calc(-0.675em - 8px - var(--margin));
+ margin-top: calc(-0.675em - 8px - var(--MI-margin));
}
}
.newButton {
display: block;
- margin: var(--margin) auto 0 auto;
+ margin: var(--MI-margin) auto 0 auto;
padding: 8px 16px;
border-radius: var(--radius-xl);
}
.tl {
- background: var(--bg);
- border-radius: var(--radius);
+ background: var(--MI_THEME-bg);
+ border-radius: var(--MI-radius);
overflow: clip;
}
</style>
diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue
index b377314856..b97e7c0eea 100644
--- a/packages/frontend/src/pages/avatar-decorations.vue
+++ b/packages/frontend/src/pages/avatar-decorations.vue
@@ -124,7 +124,7 @@ definePageMetadata(() => ({
display: grid;
grid-template-columns: 1fr;
grid-template-rows: auto auto;
- gap: var(--margin);
+ gap: var(--MI-margin);
}
.preview {
@@ -132,7 +132,7 @@ definePageMetadata(() => ({
place-items: center;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
- gap: var(--margin);
+ gap: var(--MI-margin);
}
.previewItem {
@@ -142,7 +142,7 @@ definePageMetadata(() => ({
display: flex;
align-items: center;
justify-content: center;
- border-radius: var(--radius);
+ border-radius: var(--MI-radius);
&.light {
background: #eee;
@@ -157,7 +157,7 @@ definePageMetadata(() => ({
.editorWrapper {
grid-template-columns: 200px 1fr;
grid-template-rows: 1fr;
- gap: calc(var(--margin) * 2);
+ gap: calc(var(--MI-margin) * 2);
}
.preview {
diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue
index d3f4a65b89..6d8274a55c 100644
--- a/packages/frontend/src/pages/channel-editor.vue
+++ b/packages/frontend/src/pages/channel-editor.vue
@@ -216,7 +216,7 @@ definePageMetadata(() => ({
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
- color: var(--navFg);
+ color: var(--MI_THEME-navFg);
}
.pinnedNoteRemove {
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue
index 790e16e471..6193e8857e 100644
--- a/packages/frontend/src/pages/channel.vue
+++ b/packages/frontend/src/pages/channel.vue
@@ -300,14 +300,14 @@ definePageMetadata(() => ({
<style lang="scss" module>
.main {
- min-height: calc(100cqh - (var(--stickyTop, 0px) + var(--stickyBottom, 0px)));
+ min-height: calc(100cqh - (var(--MI-stickyTop, 0px) + var(--MI-stickyBottom, 0px)));
}
.footer {
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
- background: var(--acrylicBg);
- border-top: solid 0.5px var(--divider);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
+ background: var(--MI_THEME-acrylicBg);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
.bannerContainer {
@@ -341,7 +341,7 @@ definePageMetadata(() => ({
left: 0;
width: 100%;
height: 64px;
- background: linear-gradient(0deg, var(--panel), color(from var(--panel) srgb r g b / 0));
+ background: linear-gradient(0deg, var(--MI_THEME-panel), color(from var(--MI_THEME-panel) srgb r g b / 0));
}
.bannerStatus {
@@ -366,7 +366,7 @@ definePageMetadata(() => ({
bottom: 16px;
left: 16px;
background: rgba(0, 0, 0, 0.7);
- color: var(--warn);
+ color: var(--MI_THEME-warn);
border-radius: var(--radius-sm);
font-weight: bold;
font-size: 1em;
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index 52b852ad17..9d8ba71a2d 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -198,7 +198,7 @@ definePageMetadata(() => ({
.user {
--height: 32px;
padding: 16px;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
line-height: var(--height);
}
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index 8904096875..48a64e5a02 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -330,28 +330,28 @@ definePageMetadata(() => ({
.ogwlenmc {
> .local {
.empty {
- margin: var(--margin);
+ margin: var(--MI-margin);
}
.ldhfsamy {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
grid-gap: 12px;
- margin: var(--margin) 0;
+ margin: var(--MI-margin) 0;
> .emoji {
display: flex;
align-items: center;
padding: 11px;
text-align: left;
- border: solid 1px var(--panel);
+ border: solid 1px var(--MI_THEME-panel);
&:hover {
- border-color: var(--inputBorderHover);
+ border-color: var(--MI_THEME-inputBorderHover);
}
&.selected {
- border-color: var(--accent);
+ border-color: var(--MI_THEME-accent);
}
> .img {
@@ -382,14 +382,14 @@ definePageMetadata(() => ({
> .remote {
.empty {
- margin: var(--margin);
+ margin: var(--MI-margin);
}
.ldhfsamy {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
grid-gap: 12px;
- margin: var(--margin) 0;
+ margin: var(--MI-margin) 0;
> .emoji {
display: flex;
@@ -398,7 +398,7 @@ definePageMetadata(() => ({
text-align: left;
&:hover {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
> .img {
diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue
index 36e95cec2d..604abba562 100644
--- a/packages/frontend/src/pages/drive.file.info.vue
+++ b/packages/frontend/src/pages/drive.file.info.vue
@@ -231,8 +231,8 @@ onMounted(async () => {
<style lang="scss" module>
.filePreviewRoot {
- background: var(--panel);
- border-radius: var(--radius);
+ background: var(--MI_THEME-panel);
+ border-radius: var(--MI-radius);
// MkMediaList 内の上部マージン 4px
padding: calc(1rem - 4px) 1rem 1rem;
}
@@ -262,8 +262,8 @@ onMounted(async () => {
&:hover,
&:focus-visible {
- background-color: var(--accentedBg);
- color: var(--accent);
+ background-color: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
text-decoration: none;
outline: none;
}
@@ -285,7 +285,7 @@ onMounted(async () => {
align-items: center;
min-width: 0;
font-weight: 700;
- border-radius: var(--radius);
+ border-radius: var(--MI-radius);
font-size: .8rem;
>.fileNameEditIcon {
@@ -299,12 +299,12 @@ onMounted(async () => {
}
&:hover {
- background-color: var(--accentedBg);
+ background-color: var(--MI_THEME-accentedBg);
>.fileName,
>.fileNameEditIcon {
visibility: visible;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
}
}
@@ -322,7 +322,7 @@ onMounted(async () => {
display: block;
width: 100%;
padding: .5rem 1rem;
- border-radius: var(--radius);
+ border-radius: var(--MI-radius);
.kvEditIcon {
display: inline-block;
@@ -332,11 +332,11 @@ onMounted(async () => {
}
&:hover {
- color: var(--accent);
- background-color: var(--accentedBg);
+ color: var(--MI_THEME-accent);
+ background-color: var(--MI_THEME-accentedBg);
.kvEditIcon {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
visibility: visible;
}
}
diff --git a/packages/frontend/src/pages/drop-and-fusion.game.vue b/packages/frontend/src/pages/drop-and-fusion.game.vue
index 4db952eac2..fb4d599c28 100644
--- a/packages/frontend/src/pages/drop-and-fusion.game.vue
+++ b/packages/frontend/src/pages/drop-and-fusion.game.vue
@@ -111,7 +111,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="replaying" class="_woodenFrame">
<div class="_woodenFrameInner">
<div style="background: #0004;">
- <div style="height: 10px; background: var(--accent); will-change: width;" :style="{ width: `${(currentFrame / endedAtFrame) * 100}%` }"></div>
+ <div style="height: 10px; background: var(--MI_THEME-accent); will-change: width;" :style="{ width: `${(currentFrame / endedAtFrame) * 100}%` }"></div>
</div>
</div>
<div class="_woodenFrameInner">
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index c5f0dde878..7bcd5addef 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -243,9 +243,9 @@ async function del() {
bottom: 0;
left: 0;
padding: 12px;
- border-top: solid 0.5px var(--divider);
- background: var(--acrylicBg);
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
+ border-top: solid 0.5px var(--MI_THEME-divider);
+ background: var(--MI_THEME-acrylicBg);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
}
</style>
diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue
index 03a3b8f1c0..458642699e 100644
--- a/packages/frontend/src/pages/emojis.emoji.vue
+++ b/packages/frontend/src/pages/emojis.emoji.vue
@@ -58,11 +58,11 @@ function menu(ev) {
align-items: center;
padding: 12px;
text-align: left;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
border-radius: var(--radius-sm);
&:hover {
- border-color: var(--accent);
+ border-color: var(--MI_THEME-accent);
}
}
diff --git a/packages/frontend/src/pages/explore.featured.vue b/packages/frontend/src/pages/explore.featured.vue
index cfdb235d3a..8b16a88ff3 100644
--- a/packages/frontend/src/pages/explore.featured.vue
+++ b/packages/frontend/src/pages/explore.featured.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<MkSpacer :contentMax="800">
- <MkTab v-model="tab" style="margin-bottom: var(--margin);">
+ <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>
diff --git a/packages/frontend/src/pages/explore.users.vue b/packages/frontend/src/pages/explore.users.vue
index 805d826946..2550100a42 100644
--- a/packages/frontend/src/pages/explore.users.vue
+++ b/packages/frontend/src/pages/explore.users.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<MkSpacer :contentMax="1200">
- <MkTab v-model="origin" style="margin-bottom: var(--margin);">
+ <MkTab v-model="origin" style="margin-bottom: var(--MI-margin);">
<option value="local">{{ i18n.ts.local }}</option>
<option value="remote">{{ i18n.ts.remote }}</option>
</MkTab>
diff --git a/packages/frontend/src/pages/favorites.vue b/packages/frontend/src/pages/favorites.vue
index cb1feef016..ab26573b19 100644
--- a/packages/frontend/src/pages/favorites.vue
+++ b/packages/frontend/src/pages/favorites.vue
@@ -53,7 +53,7 @@ definePageMetadata(() => ({
<style lang="scss" module>
.note {
- background: var(--panel);
- border-radius: var(--radius);
+ background: var(--MI_THEME-panel);
+ border-radius: var(--MI-radius);
}
</style>
diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index fd6fadd0b3..d84ec4873b 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -467,8 +467,8 @@ definePageMetadata(() => ({
</script>
<style lang="scss" module>
.footer {
- backdrop-filter: var(--blur, blur(15px));
- background: var(--acrylicBg);
- border-top: solid .5px var(--divider);
+ backdrop-filter: var(--MI-blur, blur(15px));
+ background: var(--MI_THEME-acrylicBg);
+ border-top: solid .5px var(--MI_THEME-divider);
}
</style>
diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue
index f63a799365..2b85489706 100644
--- a/packages/frontend/src/pages/flash/flash-index.vue
+++ b/packages/frontend/src/pages/flash/flash-index.vue
@@ -55,7 +55,8 @@ const tab = ref('featured');
const featuredFlashsPagination = {
endpoint: 'flash/featured' as const,
- noPaging: true,
+ limit: 5,
+ offsetMode: true,
};
const myFlashsPagination = {
endpoint: 'flash/my' as const,
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index 1229fcfd4e..7a7d52691c 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div><i class="ti ti-clock"></i> {{ i18n.ts.createdAt }}: <MkTime :time="flash.createdAt" mode="detail"/></div>
</div>
</div>
- <MkA v-if="$i && $i.id === flash.userId" :to="`/play/${flash.id}/edit`" style="color: var(--accent);">{{ i18n.ts._play.editThisPage }}</MkA>
+ <MkA v-if="$i && $i.id === flash.userId" :to="`/play/${flash.id}/edit`" style="color: var(--MI_THEME-accent);">{{ i18n.ts._play.editThisPage }}</MkA>
<MkAd :prefer="['horizontal', 'horizontal-big']"/>
</div>
<MkError v-else-if="error" @retry="fetchFlash()"/>
@@ -367,7 +367,7 @@ definePageMetadata(() => ({
justify-content: center;
gap: 12px;
padding: 16px;
- border-bottom: 1px solid var(--divider);
+ border-bottom: 1px solid var(--MI_THEME-divider);
&:last-child {
border-bottom: none;
diff --git a/packages/frontend/src/pages/gallery/edit.vue b/packages/frontend/src/pages/gallery/edit.vue
index a68a7e5c41..70f8b2c31d 100644
--- a/packages/frontend/src/pages/gallery/edit.vue
+++ b/packages/frontend/src/pages/gallery/edit.vue
@@ -141,7 +141,7 @@ definePageMetadata(() => ({
top: 8px;
left: 9px;
padding: 8px;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
}
> .remove {
@@ -149,7 +149,7 @@ definePageMetadata(() => ({
top: 8px;
right: 9px;
padding: 8px;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
}
}
</style>
diff --git a/packages/frontend/src/pages/gallery/index.vue b/packages/frontend/src/pages/gallery/index.vue
index e0b137ed28..2162e269bf 100644
--- a/packages/frontend/src/pages/gallery/index.vue
+++ b/packages/frontend/src/pages/gallery/index.vue
@@ -130,6 +130,6 @@ definePageMetadata(() => ({
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
grid-gap: 12px;
- margin: 0 var(--margin);
+ margin: 0 var(--MI-margin);
}
</style>
diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue
index 6ed119c0c4..539a6a9a7b 100644
--- a/packages/frontend/src/pages/gallery/post.vue
+++ b/packages/frontend/src/pages/gallery/post.vue
@@ -262,14 +262,14 @@ definePageMetadata(() => ({
align-items: center;
margin-top: 16px;
padding: 16px 0 0 0;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
> .like {
> .button {
- --accent: rgb(241 97 132);
- --X8: rgb(241 92 128);
- --buttonBg: rgb(216 71 106 / 5%);
- --buttonHoverBg: rgb(216 71 106 / 10%);
+ --MI_THEME-accent: rgb(241 97 132);
+ --MI_THEME-X8: rgb(241 92 128);
+ --MI_THEME-buttonBg: rgb(216 71 106 / 5%);
+ --MI_THEME-buttonHoverBg: rgb(216 71 106 / 10%);
color: #ff002f;
::v-deep(.count) {
@@ -286,7 +286,7 @@ definePageMetadata(() => ({
margin: 0 8px;
&:hover {
- color: var(--fgHighlighted);
+ color: var(--MI_THEME-fgHighlighted);
}
}
}
@@ -295,7 +295,7 @@ definePageMetadata(() => ({
> .user {
margin-top: 16px;
padding: 16px 0 0 0;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
display: flex;
align-items: center;
flex-wrap: wrap;
@@ -321,7 +321,7 @@ definePageMetadata(() => ({
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
grid-gap: 12px;
- margin: var(--margin);
+ margin: var(--MI-margin);
> .post {
diff --git a/packages/frontend/src/pages/games.vue b/packages/frontend/src/pages/games.vue
index b52f4decaa..998b8be0f3 100644
--- a/packages/frontend/src/pages/games.vue
+++ b/packages/frontend/src/pages/games.vue
@@ -35,7 +35,7 @@ definePageMetadata(() => ({
<style module>
.link:focus-within {
- outline: 2px solid var(--focus);
+ outline: 2px solid var(--MI_THEME-focus);
outline-offset: -2px;
}
</style>
diff --git a/packages/frontend/src/pages/install-extensions.vue b/packages/frontend/src/pages/install-extensions.vue
index 4bee437f65..6d68ed83b4 100644
--- a/packages/frontend/src/pages/install-extensions.vue
+++ b/packages/frontend/src/pages/install-extensions.vue
@@ -8,76 +8,26 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :contentMax="500">
<MkLoading v-if="uiPhase === 'fetching'"/>
- <div v-else-if="uiPhase === 'confirm' && data" class="_gaps_m" :class="$style.extInstallerRoot">
- <div :class="$style.extInstallerIconWrapper">
- <i v-if="data.type === 'plugin'" class="ti ti-plug"></i>
- <i v-else-if="data.type === 'theme'" class="ti ti-palette"></i>
- <i v-else class="ti ti-download"></i>
- </div>
- <h2 :class="$style.extInstallerTitle">{{ i18n.ts._externalResourceInstaller[`_${data.type}`].title }}</h2>
- <div :class="$style.extInstallerNormDesc">{{ i18n.ts._externalResourceInstaller.checkVendorBeforeInstall }}</div>
- <MkInfo v-if="data.type === 'plugin'" :warn="true">{{ i18n.ts._plugin.installWarn }}</MkInfo>
- <FormSection>
- <template #label>{{ i18n.ts._externalResourceInstaller[`_${data.type}`].metaTitle }}</template>
- <div class="_gaps_s">
- <FormSplit>
+ <MkExtensionInstaller v-else-if="uiPhase === 'confirm' && data" :extension="data" @confirm="install()">
+ <template #additionalInfo>
+ <FormSection>
+ <template #label>{{ i18n.ts._externalResourceInstaller._vendorInfo.title }}</template>
+ <div class="_gaps_s">
<MkKeyValue>
- <template #key>{{ i18n.ts.name }}</template>
- <template #value>{{ data.meta?.name }}</template>
+ <template #key>{{ i18n.ts._externalResourceInstaller._vendorInfo.endpoint }}</template>
+ <template #value><MkUrl :url="url" :showUrlPreview="false"></MkUrl></template>
</MkKeyValue>
<MkKeyValue>
- <template #key>{{ i18n.ts.author }}</template>
- <template #value>{{ data.meta?.author }}</template>
+ <template #key>{{ i18n.ts._externalResourceInstaller._vendorInfo.hashVerify }}</template>
+ <template #value>
+ <!-- この画面が出ている時点でハッシュの検証には成功している -->
+ <i class="ti ti-check" style="color: var(--MI_THEME-accent)"></i>
+ </template>
</MkKeyValue>
- </FormSplit>
- <MkKeyValue v-if="data.type === 'plugin'">
- <template #key>{{ i18n.ts.description }}</template>
- <template #value>{{ data.meta?.description }}</template>
- </MkKeyValue>
- <MkKeyValue v-if="data.type === 'plugin'">
- <template #key>{{ i18n.ts.version }}</template>
- <template #value>{{ data.meta?.version }}</template>
- </MkKeyValue>
- <MkKeyValue v-if="data.type === 'plugin'">
- <template #key>{{ i18n.ts.permission }}</template>
- <template #value>
- <ul :class="$style.extInstallerKVList">
- <li v-for="permission in data.meta?.permissions" :key="permission">{{ i18n.ts._permissions[permission] }}</li>
- </ul>
- </template>
- </MkKeyValue>
- <MkKeyValue v-if="data.type === 'theme' && data.meta?.base">
- <template #key>{{ i18n.ts._externalResourceInstaller._meta.base }}</template>
- <template #value>{{ i18n.ts[data.meta.base] }}</template>
- </MkKeyValue>
- <MkFolder>
- <template #icon><i class="ti ti-code"></i></template>
- <template #label>{{ i18n.ts._plugin.viewSource }}</template>
-
- <MkCode :code="data.raw ?? ''"/>
- </MkFolder>
- </div>
- </FormSection>
- <FormSection>
- <template #label>{{ i18n.ts._externalResourceInstaller._vendorInfo.title }}</template>
- <div class="_gaps_s">
- <MkKeyValue>
- <template #key>{{ i18n.ts._externalResourceInstaller._vendorInfo.endpoint }}</template>
- <template #value><MkUrl :url="url ?? ''" :showUrlPreview="false"></MkUrl></template>
- </MkKeyValue>
- <MkKeyValue>
- <template #key>{{ i18n.ts._externalResourceInstaller._vendorInfo.hashVerify }}</template>
- <template #value>
- <!--この画面が出ている時点でハッシュの検証には成功している-->
- <i class="ti ti-check" style="color: var(--accent)"></i>
- </template>
- </MkKeyValue>
- </div>
- </FormSection>
- <div class="_buttonsCenter">
- <MkButton primary @click="install()"><i class="ti ti-check"></i> {{ i18n.ts.install }}</MkButton>
- </div>
- </div>
+ </div>
+ </FormSection>
+ </template>
+ </MkExtensionInstaller>
<div v-else-if="uiPhase === 'error'" class="_gaps_m" :class="[$style.extInstallerRoot, $style.error]">
<div :class="$style.extInstallerIconWrapper">
<i class="ti ti-circle-x"></i>
@@ -96,14 +46,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, computed, onActivated, onDeactivated, nextTick } from 'vue';
import MkLoading from '@/components/global/MkLoading.vue';
+import MkExtensionInstaller, { type Extension } from '@/components/MkExtensionInstaller.vue';
import MkButton from '@/components/MkButton.vue';
-import FormSection from '@/components/form/section.vue';
-import FormSplit from '@/components/form/split.vue';
-import MkCode from '@/components/MkCode.vue';
-import MkUrl from '@/components/global/MkUrl.vue';
-import MkInfo from '@/components/MkInfo.vue';
-import MkFolder from '@/components/MkFolder.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
+import MkUrl from '@/components/global/MkUrl.vue';
+import FormSection from '@/components/form/section.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { AiScriptPluginMeta, parsePluginMeta, installPlugin } from '@/scripts/install-plugin.js';
@@ -124,24 +71,7 @@ const errorKV = ref<{
const url = ref<string | null>(null);
const hash = ref<string | null>(null);
-const data = ref<{
- type: 'plugin' | 'theme';
- raw: string;
- meta?: {
- // Plugin & Theme Common
- name: string;
- author: string;
-
- // Plugin
- description?: string;
- version?: string;
- permissions?: string[];
- config?: Record<string, any>;
-
- // Theme
- base?: 'light' | 'dark';
- };
-} | null>(null);
+const data = ref<Extension | null>(null);
function goBack(): void {
history.back();
@@ -227,7 +157,7 @@ async function fetch() {
data.value = {
type: 'theme',
meta: {
- description,
+ // description, // 使用されていない
...meta,
},
raw: res.data,
@@ -320,8 +250,8 @@ definePageMetadata(() => ({
<style lang="scss" module>
.extInstallerRoot {
- border-radius: var(--radius);
- background: var(--panel);
+ border-radius: var(--MI-radius);
+ background: var(--MI_THEME-panel);
padding: 1.5rem;
}
@@ -335,8 +265,8 @@ definePageMetadata(() => ({
margin-left: auto;
margin-right: auto;
- background-color: var(--accentedBg);
- color: var(--accent);
+ background-color: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
}
.error .extInstallerIconWrapper {
@@ -353,9 +283,4 @@ definePageMetadata(() => ({
.extInstallerNormDesc {
text-align: center;
}
-
-.extInstallerKVList {
- margin-top: 0;
- margin-bottom: 0;
-}
</style>
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index a5e6e5ac33..78d765df80 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -59,6 +59,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton>
<MkTextarea v-model="moderationNote" manualSave>
<template #label>{{ i18n.ts.moderationNote }}</template>
+ <template #caption>{{ i18n.ts.moderationNoteDescription }}</template>
</MkTextarea>
</div>
</FormSection>
diff --git a/packages/frontend/src/pages/list.vue b/packages/frontend/src/pages/list.vue
index 03ab804d05..cab807d610 100644
--- a/packages/frontend/src/pages/list.vue
+++ b/packages/frontend/src/pages/list.vue
@@ -108,7 +108,7 @@ definePageMetadata(() => ({
</script>
<style lang="scss" module>
.main {
- min-height: calc(100cqh - (var(--stickyTop, 0px) + var(--stickyBottom, 0px)));
+ min-height: calc(100cqh - (var(--MI-stickyTop, 0px) + var(--MI-stickyBottom, 0px)));
}
.userItem {
diff --git a/packages/frontend/src/pages/my-antennas/index.vue b/packages/frontend/src/pages/my-antennas/index.vue
index 9233695900..607843892e 100644
--- a/packages/frontend/src/pages/my-antennas/index.vue
+++ b/packages/frontend/src/pages/my-antennas/index.vue
@@ -73,11 +73,11 @@ onActivated(() => {
.antenna {
display: block;
padding: 16px;
- border: solid 1px var(--divider);
+ border: solid 1px var(--MI_THEME-divider);
border-radius: var(--radius-sm);
&:hover {
- border: solid 1px var(--accent);
+ border: solid 1px var(--MI_THEME-accent);
text-decoration: none;
}
}
diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue
index cd68be4ac3..a6e1f12d8a 100644
--- a/packages/frontend/src/pages/my-lists/index.vue
+++ b/packages/frontend/src/pages/my-lists/index.vue
@@ -85,12 +85,12 @@ onActivated(() => {
.list {
display: block;
padding: 16px;
- border: solid 1px var(--divider);
+ border: solid 1px var(--MI_THEME-divider);
border-radius: var(--radius-sm);
margin-bottom: 8px;
&:hover {
- border: solid 1px var(--accent);
+ border: solid 1px var(--MI_THEME-accent);
text-decoration: none;
}
}
diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue
index 5f195693cc..804a5ae8f8 100644
--- a/packages/frontend/src/pages/my-lists/list.vue
+++ b/packages/frontend/src/pages/my-lists/list.vue
@@ -134,7 +134,7 @@ async function removeUser(item, ev) {
async function showMembershipMenu(item, ev) {
const withRepliesRef = ref(item.withReplies);
-
+
os.popupMenu([{
type: 'switch',
text: i18n.ts.showRepliesToOthersInTimeline,
@@ -199,7 +199,7 @@ definePageMetadata(() => ({
<style lang="scss" module>
.main {
- min-height: calc(100cqh - (var(--stickyTop, 0px) + var(--stickyBottom, 0px)));
+ min-height: calc(100cqh - (var(--MI-stickyTop, 0px) + var(--MI-stickyBottom, 0px)));
}
.userItem {
@@ -234,8 +234,8 @@ definePageMetadata(() => ({
}
.footer {
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
- border-top: solid 0.5px var(--divider);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
</style>
diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue
index d0bbaeddd9..f6eef52153 100644
--- a/packages/frontend/src/pages/note.vue
+++ b/packages/frontend/src/pages/note.vue
@@ -182,11 +182,11 @@ definePageMetadata(() => ({
}
.loadNext {
- margin-bottom: var(--margin);
+ margin-bottom: var(--MI-margin);
}
.loadPrev {
- margin-top: var(--margin);
+ margin-top: var(--MI-margin);
}
.loadButton {
@@ -194,7 +194,7 @@ definePageMetadata(() => ({
}
.note {
- border-radius: var(--radius);
- background: var(--panel);
+ border-radius: var(--MI-radius);
+ background: var(--MI_THEME-panel);
}
</style>
diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue
index bd93fc8369..46ee501c76 100644
--- a/packages/frontend/src/pages/notifications.vue
+++ b/packages/frontend/src/pages/notifications.vue
@@ -102,7 +102,7 @@ definePageMetadata(() => ({
<style module lang="scss">
.notifications {
- border-radius: var(--radius);
+ border-radius: var(--MI-radius);
overflow: clip;
}
</style>
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
index 14c3e6845e..f09f7e1acd 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
@@ -63,7 +63,7 @@ onUnmounted(() => {
box-shadow: none;
padding: 16px;
background: transparent;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
font-size: 14px;
box-sizing: border-box;
}
diff --git a/packages/frontend/src/pages/page-editor/page-editor.container.vue b/packages/frontend/src/pages/page-editor/page-editor.container.vue
index 2531de0e62..28b224f752 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.container.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.container.vue
@@ -60,12 +60,12 @@ function remove() {
.cpjygsrt {
position: relative;
overflow: hidden;
- background: var(--panel);
- border: solid 2px var(--X12);
+ background: var(--MI_THEME-panel);
+ border: solid 2px var(--MI_THEME-X12);
border-radius: var(--radius-sm);
&:hover {
- border: solid 2px var(--X13);
+ border: solid 2px var(--MI_THEME-X13);
}
&.warn {
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index 12b70fa64f..544e112111 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -357,24 +357,24 @@ definePageMetadata(() => ({
&:hover,
&:focus-visible {
- background-color: var(--accentedBg);
- color: var(--accent);
+ background-color: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
text-decoration: none;
outline: none;
}
}
.pageMain {
- border-radius: var(--radius);
+ border-radius: var(--MI-radius);
padding: 2rem;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
box-sizing: border-box;
}
.pageBanner {
width: calc(100% + 4rem);
margin: -2rem -2rem 1.5rem;
- border-radius: var(--radius) var(--radius) 0 0;
+ border-radius: var(--MI-radius) var(--MI-radius) 0 0;
overflow: hidden;
position: relative;
@@ -399,7 +399,7 @@ definePageMetadata(() => ({
}
.pageBannerBgFallback2 {
- background-color: var(--accentedBg);
+ background-color: var(--MI_THEME-accentedBg);
}
&::after {
@@ -409,7 +409,7 @@ definePageMetadata(() => ({
bottom: 0;
width: 100%;
height: 100px;
- background: linear-gradient(0deg, var(--panel), transparent);
+ background: linear-gradient(0deg, var(--MI_THEME-panel), transparent);
}
}
@@ -433,7 +433,7 @@ definePageMetadata(() => ({
h1 {
font-size: 2rem;
font-weight: 700;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
margin: 0;
}
@@ -458,7 +458,7 @@ definePageMetadata(() => ({
flex-shrink: 0;
display: flex;
align-items: center;
- gap: var(--marginHalf);
+ gap: var(--MI-marginHalf);
margin-left: auto;
}
}
@@ -472,14 +472,14 @@ definePageMetadata(() => ({
display: flex;
align-items: center;
- border-top: 1px solid var(--divider);
+ border-top: 1px solid var(--MI_THEME-divider);
padding-top: 1.5rem;
margin-bottom: 1.5rem;
> .other {
margin-left: auto;
display: flex;
- gap: var(--marginHalf);
+ gap: var(--MI-marginHalf);
}
}
@@ -487,7 +487,7 @@ definePageMetadata(() => ({
display: flex;
align-items: center;
- border-top: 1px solid var(--divider);
+ border-top: 1px solid var(--MI_THEME-divider);
padding-top: 1.5rem;
margin-bottom: 1.5rem;
@@ -526,14 +526,14 @@ definePageMetadata(() => ({
display: flex;
align-items: center;
flex-wrap: wrap;
- gap: var(--marginHalf);
+ gap: var(--MI-marginHalf);
}
.relatedPagesRoot {
- padding: var(--margin);
+ padding: var(--MI-margin);
}
.relatedPagesItem > article {
- background-color: var(--panelHighlight) !important;
+ background-color: var(--MI_THEME-panelHighlight) !important;
}
</style>
diff --git a/packages/frontend/src/pages/reversi/game.board.vue b/packages/frontend/src/pages/reversi/game.board.vue
index 54e66f6e16..429f502133 100644
--- a/packages/frontend/src/pages/reversi/game.board.vue
+++ b/packages/frontend/src/pages/reversi/game.board.vue
@@ -504,7 +504,7 @@ $gap: 4px;
.boardInner {
padding: 32px;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410;
border-radius: 8px;
}
@@ -574,34 +574,34 @@ $gap: 4px;
transition: border 0.25s ease, opacity 0.25s ease;
&.boardCell_empty {
- border: solid 2px var(--divider);
+ border: solid 2px var(--MI_THEME-divider);
}
&.boardCell_empty.boardCell_can {
- border-color: var(--accent);
+ border-color: var(--MI_THEME-accent);
opacity: 0.5;
}
&.boardCell_empty.boardCell_myTurn {
- border-color: var(--divider);
+ border-color: var(--MI_THEME-divider);
opacity: 1;
&.boardCell_can {
- border-color: var(--accent);
+ border-color: var(--MI_THEME-accent);
cursor: pointer;
&:hover {
- background: var(--accent);
+ background: var(--MI_THEME-accent);
}
}
}
&.boardCell_prev {
- box-shadow: 0 0 0 4px var(--accent);
+ box-shadow: 0 0 0 4px var(--MI_THEME-accent);
}
&.boardCell_isEnded {
- border-color: var(--divider);
+ border-color: var(--MI_THEME-divider);
}
&.boardCell_none {
diff --git a/packages/frontend/src/pages/reversi/game.setting.vue b/packages/frontend/src/pages/reversi/game.setting.vue
index 08bb3cb76c..dfb6e3f53e 100644
--- a/packages/frontend/src/pages/reversi/game.setting.vue
+++ b/packages/frontend/src/pages/reversi/game.setting.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<template v-else>
<div class="_panel">
- <div style="display: flex; align-items: center; padding: 16px; border-bottom: solid 1px var(--divider);">
+ <div style="display: flex; align-items: center; padding: 16px; border-bottom: solid 1px var(--MI_THEME-divider);">
<div>{{ mapName }}</div>
<MkButton style="margin-left: auto;" @click="chooseMap">{{ i18n.ts._reversi.chooseBoard }}</MkButton>
</div>
@@ -87,7 +87,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.footer">
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="16">
<div style="text-align: center;" class="_gaps_s">
- <div v-if="opponentHasSettingsChanged" style="color: var(--warn);">{{ i18n.ts._reversi.opponentHasSettingsChanged }}</div>
+ <div v-if="opponentHasSettingsChanged" style="color: var(--MI_THEME-warn);">{{ i18n.ts._reversi.opponentHasSettingsChanged }}</div>
<div>
<template v-if="isReady && isOpReady">{{ i18n.ts._reversi.thisGameIsStartedSoon }}<MkEllipsis/></template>
<template v-if="isReady && !isOpReady">{{ i18n.ts._reversi.waitingForOther }}<MkEllipsis/></template>
@@ -273,14 +273,14 @@ onUnmounted(() => {
width: 300px;
height: 300px;
margin: 0 auto;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
}
.boardCell {
display: grid;
place-items: center;
background: transparent;
- border: solid 2px var(--divider);
+ border: solid 2px var(--MI_THEME-divider);
border-radius: 6px;
overflow: clip;
cursor: pointer;
@@ -290,9 +290,9 @@ onUnmounted(() => {
}
.footer {
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
- background: var(--acrylicBg);
- border-top: solid 0.5px var(--divider);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
+ background: var(--MI_THEME-acrylicBg);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
</style>
diff --git a/packages/frontend/src/pages/reversi/index.vue b/packages/frontend/src/pages/reversi/index.vue
index d823861b4a..d608a2411c 100644
--- a/packages/frontend/src/pages/reversi/index.vue
+++ b/packages/frontend/src/pages/reversi/index.vue
@@ -36,13 +36,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.gamePreviews">
<MkA v-for="g in items" :key="g.id" v-panel :class="[$style.gamePreview, !g.isStarted && !g.isEnded && $style.gamePreviewWaiting, g.isStarted && !g.isEnded && $style.gamePreviewActive]" tabindex="-1" :to="`/reversi/g/${g.id}`">
<div :class="$style.gamePreviewPlayers">
- <span v-if="g.winnerId === g.user1Id" style="margin-right: 0.75em; color: var(--accent); font-weight: bold;"><i class="ti ti-trophy"></i></span>
+ <span v-if="g.winnerId === g.user1Id" style="margin-right: 0.75em; color: var(--MI_THEME-accent); font-weight: bold;"><i class="ti ti-trophy"></i></span>
<span v-if="g.winnerId === g.user2Id" style="margin-right: 0.75em; visibility: hidden;"><i class="ti ti-x"></i></span>
<MkAvatar :class="$style.gamePreviewPlayersAvatar" :user="g.user1"/>
<span style="margin: 0 1em;">vs</span>
<MkAvatar :class="$style.gamePreviewPlayersAvatar" :user="g.user2"/>
<span v-if="g.winnerId === g.user1Id" style="margin-left: 0.75em; visibility: hidden;"><i class="ti ti-x"></i></span>
- <span v-if="g.winnerId === g.user2Id" style="margin-left: 0.75em; color: var(--accent); font-weight: bold;"><i class="ti ti-trophy"></i></span>
+ <span v-if="g.winnerId === g.user2Id" style="margin-left: 0.75em; color: var(--MI_THEME-accent); font-weight: bold;"><i class="ti ti-trophy"></i></span>
</div>
<div :class="$style.gamePreviewFooter">
<span v-if="g.isStarted && !g.isEnded" :class="$style.gamePreviewStatusActive">{{ i18n.ts._reversi.playing }}</span>
@@ -63,13 +63,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.gamePreviews">
<MkA v-for="g in items" :key="g.id" v-panel :class="[$style.gamePreview, !g.isStarted && !g.isEnded && $style.gamePreviewWaiting, g.isStarted && !g.isEnded && $style.gamePreviewActive]" tabindex="-1" :to="`/reversi/g/${g.id}`">
<div :class="$style.gamePreviewPlayers">
- <span v-if="g.winnerId === g.user1Id" style="margin-right: 0.75em; color: var(--accent); font-weight: bold;"><i class="ti ti-trophy"></i></span>
+ <span v-if="g.winnerId === g.user1Id" style="margin-right: 0.75em; color: var(--MI_THEME-accent); font-weight: bold;"><i class="ti ti-trophy"></i></span>
<span v-if="g.winnerId === g.user2Id" style="margin-right: 0.75em; visibility: hidden;"><i class="ti ti-x"></i></span>
<MkAvatar :class="$style.gamePreviewPlayersAvatar" :user="g.user1"/>
<span style="margin: 0 1em;">vs</span>
<MkAvatar :class="$style.gamePreviewPlayersAvatar" :user="g.user2"/>
<span v-if="g.winnerId === g.user1Id" style="margin-left: 0.75em; visibility: hidden;"><i class="ti ti-x"></i></span>
- <span v-if="g.winnerId === g.user2Id" style="margin-left: 0.75em; color: var(--accent); font-weight: bold;"><i class="ti ti-trophy"></i></span>
+ <span v-if="g.winnerId === g.user2Id" style="margin-left: 0.75em; color: var(--MI_THEME-accent); font-weight: bold;"><i class="ti ti-trophy"></i></span>
</div>
<div :class="$style.gamePreviewFooter">
<span v-if="g.isStarted && !g.isEnded" :class="$style.gamePreviewStatusActive">{{ i18n.ts._reversi.playing }}</span>
@@ -285,7 +285,7 @@ definePageMetadata(() => ({
.gamePreviews {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
- grid-gap: var(--margin);
+ grid-gap: var(--MI-margin);
}
.gamePreview {
@@ -295,11 +295,11 @@ definePageMetadata(() => ({
}
.gamePreviewActive {
- box-shadow: inset 0 0 8px 0px var(--accent);
+ box-shadow: inset 0 0 8px 0px var(--MI_THEME-accent);
}
.gamePreviewWaiting {
- box-shadow: inset 0 0 8px 0px var(--warn);
+ box-shadow: inset 0 0 8px 0px var(--MI_THEME-warn);
}
.gamePreviewPlayers {
@@ -324,19 +324,19 @@ definePageMetadata(() => ({
.gamePreviewFooter {
display: flex;
align-items: baseline;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
padding: 6px 10px;
font-size: 0.9em;
}
.gamePreviewStatusActive {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
font-weight: bold;
animation: blink 2s infinite;
}
.gamePreviewStatusWaiting {
- color: var(--warn);
+ color: var(--MI_THEME-warn);
font-weight: bold;
animation: blink 2s infinite;
}
diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue
index 155d8b82d7..2250e1ce60 100644
--- a/packages/frontend/src/pages/scratchpad.vue
+++ b/packages/frontend/src/pages/scratchpad.vue
@@ -204,7 +204,7 @@ definePageMetadata(() => ({
.root {
display: flex;
flex-direction: column;
- gap: var(--margin);
+ gap: var(--MI-margin);
}
.editor {
@@ -242,14 +242,14 @@ definePageMetadata(() => ({
}
.uiInspectorUnShown {
- color: var(--fgTransparent);
+ color: var(--MI_THEME-fgTransparent);
}
.uiInspectorType {
display: inline-block;
border: hidden;
border-radius: 10px;
- background-color: var(--panelHighlight);
+ background-color: var(--MI_THEME-panelHighlight);
padding: 2px 8px;
font-size: 12px;
}
diff --git a/packages/frontend/src/pages/search.note.vue b/packages/frontend/src/pages/search.note.vue
index 66c5c92480..d64537d289 100644
--- a/packages/frontend/src/pages/search.note.vue
+++ b/packages/frontend/src/pages/search.note.vue
@@ -225,12 +225,12 @@ async function search() {
justify-content: center;
}
.addMeButton {
- border: 2px dashed var(--fgTransparent);
+ border: 2px dashed var(--MI_THEME-fgTransparent);
padding: 12px;
margin-right: 16px;
}
.addUserButton {
- border: 2px dashed var(--fgTransparent);
+ border: 2px dashed var(--MI_THEME-fgTransparent);
padding: 12px;
flex-grow: 1;
}
diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue
index 6a9a1e16e2..a76b748ac1 100644
--- a/packages/frontend/src/pages/settings/2fa.vue
+++ b/packages/frontend/src/pages/settings/2fa.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #icon><i class="ti ti-shield-lock"></i></template>
<template #label>{{ i18n.ts.totp }}</template>
<template #caption>{{ i18n.ts.totpDescription }}</template>
- <template #suffix><i v-if="$i.twoFactorEnabled" class="ti ti-check" style="color: var(--success)"></i></template>
+ <template #suffix><i v-if="$i.twoFactorEnabled" class="ti ti-check" style="color: var(--MI_THEME-success)"></i></template>
<div v-if="$i.twoFactorEnabled" class="_gaps_s">
<div v-text="i18n.ts._2fa.alreadyRegistered"/>
diff --git a/packages/frontend/src/pages/settings/accounts.vue b/packages/frontend/src/pages/settings/accounts.vue
index 08c9261dcf..1bbedb817e 100644
--- a/packages/frontend/src/pages/settings/accounts.vue
+++ b/packages/frontend/src/pages/settings/accounts.vue
@@ -45,7 +45,7 @@ const init = async () => {
});
};
-function menu(account, ev) {
+function menu(account: Misskey.entities.UserDetailed, ev: MouseEvent) {
os.popupMenu([{
text: i18n.ts.switch,
icon: 'ti ti-switch-horizontal',
@@ -58,7 +58,7 @@ function menu(account, ev) {
}], ev.currentTarget ?? ev.target);
}
-function addAccount(ev) {
+function addAccount(ev: MouseEvent) {
os.popupMenu([{
text: i18n.ts.existingAccount,
action: () => { addExistingAccount(); },
@@ -68,14 +68,14 @@ function addAccount(ev) {
}], ev.currentTarget ?? ev.target);
}
-async function removeAccount(account) {
+async function removeAccount(account: Misskey.entities.UserDetailed) {
await _removeAccount(account.id);
accounts.value = accounts.value.filter(x => x.id !== account.id);
}
function addExistingAccount() {
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {}, {
- done: async res => {
+ done: async (res: Misskey.entities.SigninFlowResponse & { finished: true }) => {
await addAccounts(res.id, res.i);
os.success();
init();
@@ -86,17 +86,17 @@ function addExistingAccount() {
function createAccount() {
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkSignupDialog.vue')), {}, {
- done: async res => {
- await addAccounts(res.id, res.i);
- switchAccountWithToken(res.i);
+ done: async (res: Misskey.entities.SignupResponse) => {
+ await addAccounts(res.id, res.token);
+ switchAccountWithToken(res.token);
},
closed: () => dispose(),
});
}
async function switchAccount(account: any) {
- const fetchedAccounts: any[] = await getAccounts();
- const token = fetchedAccounts.find(x => x.id === account.id).token;
+ const fetchedAccounts = await getAccounts();
+ const token = fetchedAccounts.find(x => x.id === account.id)!.token;
switchAccountWithToken(token);
}
diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue
index c58ea5d378..68e36ef1bb 100644
--- a/packages/frontend/src/pages/settings/apps.vue
+++ b/packages/frontend/src/pages/settings/apps.vue
@@ -14,30 +14,39 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<template #default="{items}">
<div class="_gaps">
- <div v-for="token in items" :key="token.id" class="_panel" :class="$style.app">
- <img v-if="token.iconUrl" :class="$style.appIcon" :src="token.iconUrl" alt=""/>
- <div :class="$style.appBody">
- <div :class="$style.appName">{{ token.name }}</div>
- <div>{{ token.description }}</div>
- <MkKeyValue oneline>
- <template #key>{{ i18n.ts.installedDate }}</template>
- <template #value><MkTime :time="token.createdAt"/></template>
- </MkKeyValue>
- <MkKeyValue oneline>
- <template #key>{{ i18n.ts.lastUsedDate }}</template>
- <template #value><MkTime :time="token.lastUsedAt"/></template>
- </MkKeyValue>
- <details>
- <summary>{{ i18n.ts.details }}</summary>
+ <MkFolder v-for="token in items" :key="token.id" :defaultOpen="true">
+ <template #icon>
+ <img v-if="token.iconUrl" :class="$style.appIcon" :src="token.iconUrl" alt=""/>
+ <i v-else class="ti ti-plug"/>
+ </template>
+ <template #label>{{ token.name }}</template>
+ <template #caption>{{ token.description }}</template>
+ <template #suffix><MkTime :time="token.lastUsedAt"/></template>
+ <template #footer>
+ <MkButton danger @click="revoke(token)"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
+ </template>
+
+ <div class="_gaps_s">
+ <div v-if="token.description">{{ token.description }}</div>
+ <div>
+ <MkKeyValue oneline>
+ <template #key>{{ i18n.ts.installedDate }}</template>
+ <template #value><MkTime :time="token.createdAt" :mode="'detail'"/></template>
+ </MkKeyValue>
+ <MkKeyValue oneline>
+ <template #key>{{ i18n.ts.lastUsedDate }}</template>
+ <template #value><MkTime :time="token.lastUsedAt" :mode="'detail'"/></template>
+ </MkKeyValue>
+ </div>
+ <MkFolder>
+ <template #label>{{ i18n.ts.permission }}</template>
+ <template #suffix>{{ Object.keys(token.permission).length === 0 ? i18n.ts.none : Object.keys(token.permission).length }}</template>
<ul>
<li v-for="p in token.permission" :key="p">{{ i18n.ts._permissions[p] }}</li>
</ul>
- </details>
- <div>
- <MkButton inline danger @click="revoke(token)"><i class="ti ti-trash"></i></MkButton>
- </div>
+ </MkFolder>
</div>
- </div>
+ </MkFolder>
</div>
</template>
</FormPagination>
@@ -52,6 +61,7 @@ import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkKeyValue from '@/components/MkKeyValue.vue';
import MkButton from '@/components/MkButton.vue';
+import MkFolder from '@/components/MkFolder.vue';
import { infoImageUrl } from '@/instance.js';
const list = ref<InstanceType<typeof FormPagination>>();
@@ -82,26 +92,9 @@ definePageMetadata(() => ({
</script>
<style lang="scss" module>
-.app {
- display: flex;
- padding: 16px;
-}
-
.appIcon {
- display: block;
- flex-shrink: 0;
- margin: 0 12px 0 0;
- width: 50px;
- height: 50px;
- border-radius: var(--radius-sm);
-}
-
-.appBody {
- width: calc(100% - 62px);
- position: relative;
-}
-
-.appName {
- font-weight: bold;
+ width: 20px;
+ height: 20px;
+ border-radius: 4px;
}
</style>
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
index 9f7852a71d..5c59f4cbe4 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
@@ -44,7 +44,7 @@ const emit = defineEmits<{
.root {
cursor: pointer;
padding: 16px 16px 28px 16px;
- border: solid 2px var(--divider);
+ border: solid 2px var(--MI_THEME-divider);
border-radius: var(--radius-sm);
text-align: center;
font-size: 90%;
@@ -53,8 +53,8 @@ const emit = defineEmits<{
}
.active {
- background-color: var(--accentedBg);
- border-color: var(--accent);
+ background-color: var(--MI_THEME-accentedBg);
+ border-color: var(--MI_THEME-accent);
}
.name {
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
index 4ec4610279..b84665b111 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
@@ -159,8 +159,8 @@ async function detach() {
bottom: 0;
left: 0;
padding: 12px;
- border-top: solid 0.5px var(--divider);
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
+ border-top: solid 0.5px var(--MI_THEME-divider);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
}
</style>
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue
index 5324a6b7f7..efcd2a5f3f 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.vue
@@ -148,7 +148,7 @@ definePageMetadata(() => ({
.current {
padding: 16px;
- border-radius: var(--radius);
+ border-radius: var(--MI-radius);
}
.decorations {
diff --git a/packages/frontend/src/pages/settings/drive-cleaner.vue b/packages/frontend/src/pages/settings/drive-cleaner.vue
index 432295aa7f..4054cd810a 100644
--- a/packages/frontend/src/pages/settings/drive-cleaner.vue
+++ b/packages/frontend/src/pages/settings/drive-cleaner.vue
@@ -177,7 +177,7 @@ definePageMetadata(() => ({
align-items: center;
&:hover {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
}
diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue
index f226647569..d452f249b6 100644
--- a/packages/frontend/src/pages/settings/email.vue
+++ b/packages/frontend/src/pages/settings/email.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInput v-model="emailAddress" type="email" manualSave>
<template #prefix><i class="ti ti-mail"></i></template>
<template v-if="$i.email && !$i.emailVerified" #caption>{{ i18n.ts.verificationEmailSent }}</template>
- <template v-else-if="emailAddress === $i.email && $i.emailVerified" #caption><i class="ti ti-check" style="color: var(--success);"></i> {{ i18n.ts.emailVerified }}</template>
+ <template v-else-if="emailAddress === $i.email && $i.emailVerified" #caption><i class="ti ti-check" style="color: var(--MI_THEME-success);"></i> {{ i18n.ts.emailVerified }}</template>
</MkInput>
</FormSection>
diff --git a/packages/frontend/src/pages/settings/emoji-picker.vue b/packages/frontend/src/pages/settings/emoji-picker.vue
index bb919c6c53..eb5424ea6c 100644
--- a/packages/frontend/src/pages/settings/emoji-picker.vue
+++ b/packages/frontend/src/pages/settings/emoji-picker.vue
@@ -287,9 +287,9 @@ definePageMetadata(() => ({
<style lang="scss" module>
.tab {
- margin: calc(var(--margin) / 2) 0;
- padding: calc(var(--margin) / 2) 0;
- background: var(--bg);
+ margin: calc(var(--MI-margin) / 2) 0;
+ padding: calc(var(--MI-margin) / 2) 0;
+ background: var(--MI_THEME-bg);
}
.emojis {
@@ -311,6 +311,6 @@ definePageMetadata(() => ({
.editorCaption {
font-size: 0.85em;
padding: 8px 0 0 0;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
}
</style>
diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue
index f6f2ffc553..552b4ee028 100644
--- a/packages/frontend/src/pages/settings/index.vue
+++ b/packages/frontend/src/pages/settings/index.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div v-if="!(narrow && currentPage?.route.name == null)" class="main">
<div class="bkzroven" style="container-type: inline-size;">
- <RouterView/>
+ <RouterView nested/>
</div>
</div>
</div>
diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue
index a5c381200f..82aeb6063f 100644
--- a/packages/frontend/src/pages/settings/mute-block.vue
+++ b/packages/frontend/src/pages/settings/mute-block.vue
@@ -243,7 +243,7 @@ definePageMetadata(() => ({
.userItemSub {
padding: 6px 12px;
font-size: 85%;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
}
.userItemMainBody {
diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue
index a0e6cad9c8..b189db0f8f 100644
--- a/packages/frontend/src/pages/settings/navbar.vue
+++ b/packages/frontend/src/pages/settings/navbar.vue
@@ -122,7 +122,7 @@ definePageMetadata(() => ({
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
- color: var(--navFg);
+ color: var(--MI_THEME-navFg);
}
.itemIcon {
diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue
index eb9d158c1e..abe4524126 100644
--- a/packages/frontend/src/pages/settings/other.vue
+++ b/packages/frontend/src/pages/settings/other.vue
@@ -65,6 +65,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="enableCondensedLine">
<template #label>Enable condensed line</template>
</MkSwitch>
+ <MkSwitch v-model="skipNoteRender">
+ <template #label>Enable note render skipping</template>
+ </MkSwitch>
</div>
</MkFolder>
@@ -116,9 +119,14 @@ const $i = signinRequired();
const reportError = computed(defaultStore.makeGetterSetter('reportError'));
const enableCondensedLine = computed(defaultStore.makeGetterSetter('enableCondensedLine'));
+const skipNoteRender = computed(defaultStore.makeGetterSetter('skipNoteRender'));
const devMode = computed(defaultStore.makeGetterSetter('devMode'));
const defaultWithReplies = computed(defaultStore.makeGetterSetter('defaultWithReplies'));
+watch(skipNoteRender, async () => {
+ await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
+});
+
async function deleteAccount() {
{
const { canceled } = await os.confirm({
diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue
index 8036ef5555..f7dcc7b139 100644
--- a/packages/frontend/src/pages/settings/preferences-backups.vue
+++ b/packages/frontend/src/pages/settings/preferences-backups.vue
@@ -469,7 +469,7 @@ definePageMetadata(() => ({
<style lang="scss" module>
.buttons {
display: flex;
- gap: var(--margin);
+ gap: var(--MI-margin);
flex-wrap: wrap;
}
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index c94cd512f3..9ff4a63e09 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -52,14 +52,17 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkFolder>
<template #icon><i class="ti ti-list"></i></template>
<template #label>{{ i18n.ts._profile.metadataEdit }}</template>
-
- <div :class="$style.metadataRoot">
- <div :class="$style.metadataMargin">
- <MkButton :disabled="fields.length >= 16" inline style="margin-right: 8px;" @click="addField"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
- <MkButton v-if="!fieldEditMode" :disabled="fields.length <= 1" inline danger style="margin-right: 8px;" @click="fieldEditMode = !fieldEditMode"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
- <MkButton v-else inline style="margin-right: 8px;" @click="fieldEditMode = !fieldEditMode"><i class="ti ti-arrows-sort"></i> {{ i18n.ts.rearrange }}</MkButton>
- <MkButton inline primary @click="saveFields"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
+ <template #footer>
+ <div class="_buttons">
+ <MkButton primary @click="saveFields"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
+ <MkButton :disabled="fields.length >= 16" @click="addField"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
+ <MkButton v-if="!fieldEditMode" :disabled="fields.length <= 1" danger @click="fieldEditMode = !fieldEditMode"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
+ <MkButton v-else @click="fieldEditMode = !fieldEditMode"><i class="ti ti-arrows-sort"></i> {{ i18n.ts.rearrange }}</MkButton>
</div>
+ </template>
+
+ <div :class="$style.metadataRoot" class="_gaps_s">
+ <MkInfo>{{ i18n.ts._profile.verifiedLinkDescription }}</MkInfo>
<Sortable
v-model="fields"
@@ -71,24 +74,20 @@ SPDX-License-Identifier: AGPL-3.0-only
@end="e => e.item.classList.remove('active')"
>
<template #item="{element, index}">
- <div :class="$style.fieldDragItem">
+ <div v-panel :class="$style.fieldDragItem">
<button v-if="!fieldEditMode" class="_button" :class="$style.dragItemHandle" tabindex="-1"><i class="ti ti-menu"></i></button>
<button v-if="fieldEditMode" :disabled="fields.length <= 1" class="_button" :class="$style.dragItemRemove" @click="deleteField(index)"><i class="ti ti-x"></i></button>
<div :class="$style.dragItemForm">
<FormSplit :minWidth="200">
- <MkInput v-model="element.name" small>
- <template #label>{{ i18n.ts._profile.metadataLabel }}</template>
+ <MkInput v-model="element.name" small :placeholder="i18n.ts._profile.metadataLabel">
</MkInput>
- <MkInput v-model="element.value" small>
- <template #label>{{ i18n.ts._profile.metadataContent }}</template>
+ <MkInput v-model="element.value" small :placeholder="i18n.ts._profile.metadataContent">
</MkInput>
</FormSplit>
</div>
</div>
</template>
</Sortable>
-
- <MkInfo>{{ i18n.ts._profile.verifiedLinkDescription }}</MkInfo>
</div>
</MkFolder>
<template #caption>{{ i18n.ts._profile.metadataDescription }}</template>
@@ -158,6 +157,10 @@ const setMaxBirthDate = () => {
return `${y}-12-31`;
};
+function assertVaildLang(lang: string | null): lang is keyof typeof langmap {
+ return lang != null && lang in langmap;
+}
+
const profile = reactive({
name: $i.name,
description: $i.description,
@@ -165,7 +168,7 @@ const profile = reactive({
location: $i.location,
birthday: $i.birthday,
listenbrainz: $i.listenbrainz,
- lang: $i.lang,
+ lang: assertVaildLang($i.lang) ? $i.lang : null,
isBot: $i.isBot ?? false,
isCat: $i.isCat ?? false,
speakAsCat: $i.speakAsCat ?? false,
@@ -229,6 +232,11 @@ function save() {
isBot: !!profile.isBot,
isCat: !!profile.isCat,
speakAsCat: !!profile.speakAsCat,
+ }, undefined, {
+ '0b3f9f6a-2f4d-4b1f-9fb4-49d3a2fd7191': {
+ title: i18n.ts.yourNameContainsProhibitedWords,
+ text: i18n.ts.yourNameContainsProhibitedWordsDescription,
+ },
});
globalEvents.emit('requestClearPageCache');
claimAchievement('profileFilled');
@@ -417,7 +425,7 @@ definePageMetadata(() => ({
height: 130px;
background-size: cover;
background-position: center;
- border-bottom: solid 1px var(--divider);
+ border-bottom: solid 1px var(--MI_THEME-divider);
overflow: clip;
}
@@ -449,19 +457,11 @@ definePageMetadata(() => ({
container-type: inline-size;
}
-.metadataMargin {
- margin-bottom: 1.5em;
-}
-
.fieldDragItem {
display: flex;
- padding-bottom: .75em;
+ padding: 10px;
align-items: flex-end;
- border-bottom: solid 0.5px var(--divider);
-
- &:last-child {
- border-bottom: 0;
- }
+ border-radius: 6px;
/* (drag button) 32px + (drag button margin) 8px + (input width) 200px * 2 + (input gap) 12px = 452px */
@container (max-width: 452px) {
diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue
index de0f63630e..8f9d4f858b 100644
--- a/packages/frontend/src/pages/settings/security.vue
+++ b/packages/frontend/src/pages/settings/security.vue
@@ -124,7 +124,7 @@ definePageMetadata(() => ({
}
&:not(:last-child) {
- border-bottom: solid 0.5px var(--divider);
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
}
> header {
@@ -136,11 +136,11 @@ definePageMetadata(() => ({
margin-right: 0.75em;
&.succ {
- color: var(--success);
+ color: var(--MI_THEME-success);
}
&.fail {
- color: var(--error);
+ color: var(--MI_THEME-error);
}
}
diff --git a/packages/frontend/src/pages/settings/sounds.sound.vue b/packages/frontend/src/pages/settings/sounds.sound.vue
index 81478fede5..56f65e2309 100644
--- a/packages/frontend/src/pages/settings/sounds.sound.vue
+++ b/packages/frontend/src/pages/settings/sounds.sound.vue
@@ -194,7 +194,7 @@ function save() {
flex-grow: 1;
min-width: 0;
font-weight: 700;
- color: var(--error);
+ color: var(--MI_THEME-error);
}
.fileSelectorButton {
@@ -203,6 +203,6 @@ function save() {
.fileNotSelected {
font-weight: 700;
- color: var(--infoWarnFg);
+ color: var(--MI_THEME-infoWarnFg);
}
</style>
diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue
index e7aef55a53..34a0245406 100644
--- a/packages/frontend/src/pages/settings/theme.vue
+++ b/packages/frontend/src/pages/settings/theme.vue
@@ -204,7 +204,7 @@ definePageMetadata(() => ({
}
.dn:focus-visible ~ .toggle {
- outline: 2px solid var(--focus);
+ outline: 2px solid var(--MI_THEME-focus);
outline-offset: 2px;
}
@@ -227,12 +227,12 @@ definePageMetadata(() => ({
> .before {
left: -70px;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
> .after {
right: -68px;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
}
}
@@ -350,11 +350,11 @@ definePageMetadata(() => ({
background-color: #749DD6;
> .before {
- color: var(--fg);
+ color: var(--MI_THEME-fg);
}
> .after {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
.toggle__handler {
@@ -405,14 +405,14 @@ definePageMetadata(() => ({
> .sync {
padding: 14px 16px;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
}
.rsljpzjq {
> .selects {
display: flex;
- gap: 1.5em var(--margin);
+ gap: 1.5em var(--MI-margin);
flex-wrap: wrap;
> .select {
diff --git a/packages/frontend/src/pages/settings/webhook.edit.vue b/packages/frontend/src/pages/settings/webhook.edit.vue
index adeaf8550c..40d23e36c5 100644
--- a/packages/frontend/src/pages/settings/webhook.edit.vue
+++ b/packages/frontend/src/pages/settings/webhook.edit.vue
@@ -184,6 +184,6 @@ definePageMetadata(() => ({
.description {
font-size: 0.85em;
padding: 8px 0 0 0;
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
}
</style>
diff --git a/packages/frontend/src/pages/settings/webhook.vue b/packages/frontend/src/pages/settings/webhook.vue
index 0d11b00c97..af8b7ca945 100644
--- a/packages/frontend/src/pages/settings/webhook.vue
+++ b/packages/frontend/src/pages/settings/webhook.vue
@@ -17,8 +17,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #icon>
<i v-if="webhook.active === false" class="ti ti-player-pause"></i>
<i v-else-if="webhook.latestStatus === null" class="ti ti-circle"></i>
- <i v-else-if="[200, 201, 204].includes(webhook.latestStatus)" class="ti ti-check" :style="{ color: 'var(--success)' }"></i>
- <i v-else class="ti ti-alert-triangle" :style="{ color: 'var(--error)' }"></i>
+ <i v-else-if="[200, 201, 204].includes(webhook.latestStatus)" class="ti ti-check" :style="{ color: 'var(--MI_THEME-success)' }"></i>
+ <i v-else class="ti ti-alert-triangle" :style="{ color: 'var(--MI_THEME-error)' }"></i>
</template>
{{ webhook.name || webhook.url }}
<template #suffix>
diff --git a/packages/frontend/src/pages/signup-complete.vue b/packages/frontend/src/pages/signup-complete.vue
index 226f08bf8e..503e6e0f73 100644
--- a/packages/frontend/src/pages/signup-complete.vue
+++ b/packages/frontend/src/pages/signup-complete.vue
@@ -78,7 +78,7 @@ place-content: center;
.form {
position: relative;
z-index: 10;
- border-radius: var(--radius);
+ border-radius: var(--MI-radius);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
overflow: clip;
max-width: 500px;
@@ -88,7 +88,7 @@ place-content: center;
padding: 16px;
text-align: center;
font-size: 26px;
- background-color: var(--accentedBg);
- color: var(--accent);
+ background-color: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
}
</style>
diff --git a/packages/frontend/src/pages/tag.vue b/packages/frontend/src/pages/tag.vue
index 0d261b1af3..a45b61fb10 100644
--- a/packages/frontend/src/pages/tag.vue
+++ b/packages/frontend/src/pages/tag.vue
@@ -76,10 +76,10 @@ definePageMetadata(() => ({
<style lang="scss" module>
.footer {
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
- background: var(--acrylicBg);
- border-top: solid 0.5px var(--divider);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
+ background: var(--MI_THEME-acrylicBg);
+ border-top: solid 0.5px var(--MI_THEME-divider);
display: flex;
}
diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue
index 2fa6eb81ba..21f4548d2e 100644
--- a/packages/frontend/src/pages/theme-editor.vue
+++ b/packages/frontend/src/pages/theme-editor.vue
@@ -259,7 +259,7 @@ definePageMetadata(() => ({
}
&.active {
- box-shadow: 0 0 0 2px var(--divider) inset;
+ box-shadow: 0 0 0 2px var(--MI_THEME-divider) inset;
}
&.rounded {
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index 60974da971..92bfd9cdf1 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -9,10 +9,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSpacer :contentMax="800">
<MkHorizontalSwipe v-model:tab="src" :tabs="$i ? headerTabs : headerTabsWhenNotLogin">
<div :key="src" ref="rootEl">
- <MkInfo v-if="isBasicTimeline(src) && !defaultStore.reactiveState.timelineTutorials.value[src]" style="margin-bottom: var(--margin);" closable @close="closeTutorial()">
+ <MkInfo v-if="isBasicTimeline(src) && !defaultStore.reactiveState.timelineTutorials.value[src]" style="margin-bottom: var(--MI-margin);" closable @close="closeTutorial()">
{{ i18n.ts._timelineDescription[src] }}
</MkInfo>
- <MkPostForm v-if="defaultStore.reactiveState.showFixedPostForm.value" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/>
+ <MkPostForm v-if="defaultStore.reactiveState.showFixedPostForm.value" :class="$style.postForm" class="post-form _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>
<div :class="$style.tl">
<MkTimeline
@@ -363,30 +363,30 @@ definePageMetadata(() => ({
<style lang="scss" module>
.new {
position: sticky;
- top: calc(var(--stickyTop, 0px) + 16px);
+ top: calc(var(--MI-stickyTop, 0px) + 16px);
z-index: 1000;
width: 100%;
margin: calc(-0.675em - 8px) 0;
&:first-child {
- margin-top: calc(-0.675em - 8px - var(--margin));
+ margin-top: calc(-0.675em - 8px - var(--MI-margin));
}
}
.newButton {
display: block;
- margin: var(--margin) auto 0 auto;
+ margin: var(--MI-margin) auto 0 auto;
padding: 8px 16px;
border-radius: var(--radius-xl);
}
.postForm {
- border-radius: var(--radius);
+ border-radius: var(--MI-radius);
}
.tl {
- background: var(--bg);
- border-radius: var(--radius);
+ background: var(--MI_THEME-bg);
+ border-radius: var(--MI-radius);
overflow: clip;
}
</style>
diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue
index 396e6eb14a..d7326f0195 100644
--- a/packages/frontend/src/pages/user-list-timeline.vue
+++ b/packages/frontend/src/pages/user-list-timeline.vue
@@ -114,26 +114,26 @@ definePageMetadata(() => ({
<style lang="scss" module>
.new {
position: sticky;
- top: calc(var(--stickyTop, 0px) + 16px);
+ top: calc(var(--MI-stickyTop, 0px) + 16px);
z-index: 1000;
width: 100%;
margin: calc(-0.675em - 8px) 0;
&:first-child {
- margin-top: calc(-0.675em - 8px - var(--margin));
+ margin-top: calc(-0.675em - 8px - var(--MI-margin));
}
}
.newButton {
display: block;
- margin: var(--margin) auto 0 auto;
+ margin: var(--MI-margin) auto 0 auto;
padding: 8px 16px;
border-radius: var(--radius-xl);
}
.tl {
- background: var(--bg);
- border-radius: var(--radius);
+ background: var(--MI_THEME-bg);
+ border-radius: var(--MI-radius);
overflow: clip;
}
</style>
diff --git a/packages/frontend/src/pages/user/clips.vue b/packages/frontend/src/pages/user/clips.vue
index ac01cff8cd..38ce78e8d5 100644
--- a/packages/frontend/src/pages/user/clips.vue
+++ b/packages/frontend/src/pages/user/clips.vue
@@ -43,6 +43,6 @@ const pagination = {
.description {
margin-top: 8px;
padding-top: 8px;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
</style>
diff --git a/packages/frontend/src/pages/user/follow-list.vue b/packages/frontend/src/pages/user/follow-list.vue
index e60dccec17..868767e8f4 100644
--- a/packages/frontend/src/pages/user/follow-list.vue
+++ b/packages/frontend/src/pages/user/follow-list.vue
@@ -45,6 +45,6 @@ const followersPagination = {
.users {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
- grid-gap: var(--margin);
+ grid-gap: var(--MI-margin);
}
</style>
diff --git a/packages/frontend/src/pages/user/gallery.vue b/packages/frontend/src/pages/user/gallery.vue
index 9ba81322ba..0bc5628528 100644
--- a/packages/frontend/src/pages/user/gallery.vue
+++ b/packages/frontend/src/pages/user/gallery.vue
@@ -38,6 +38,6 @@ const pagination = {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
grid-gap: 12px;
- margin: var(--margin);
+ margin: var(--MI-margin);
}
</style>
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index 279f301d78..30ecbd5100 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkUserName class="name" :user="user" :nowrap="true"/>
<div class="bottom">
<span class="username"><MkAcct :user="user" :detail="true"/></span>
- <span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--badge);"><i class="ti ti-shield"></i></span>
+ <span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--MI_THEME-badge);"><i class="ti ti-shield"></i></span>
<span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="ti ti-lock"></i></span>
<span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ti ti-robot"></i></span>
<button v-if="$i && !isEditingMemo && !memoDraft" class="_button add-note-button" @click="showMemoTextarea">
@@ -49,15 +49,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkUserName :user="user" :nowrap="false" class="name"/>
<div class="bottom">
<span class="username"><MkAcct :user="user" :detail="true"/></span>
- <span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--badge);"><i class="ti ti-shield"></i></span>
+ <span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--MI_THEME-badge);"><i class="ti ti-shield"></i></span>
<span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="ti ti-lock"></i></span>
<span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ti ti-robot"></i></span>
</div>
</div>
<div v-if="user.followedMessage != null" class="followedMessage">
- <div style="border: solid 1px var(--love); border-radius: 6px; background: color-mix(in srgb, var(--love), transparent 90%); padding: 6px 8px;">
- <Mfm :text="user.followedMessage" :author="user"/>
- </div>
+ <MkFukidashi class="fukidashi" :tail="narrow ? 'none' : 'left'" negativeMargin shadow>
+ <div class="messageHeader">{{ i18n.ts.messageToFollower }}</div>
+ <div><MkSparkle><Mfm :plain="true" :text="user.followedMessage" :author="user"/></MkSparkle></div>
+ </MkFukidashi>
</div>
<div v-if="user.roles.length > 0" class="roles">
<span v-for="role in user.roles" :key="role.id" v-tooltip="role.description" class="role" :style="{ '--color': role.color }">
@@ -70,6 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="iAmModerator" class="moderationNote">
<MkTextarea v-if="editModerationNote || (moderationNote != null && moderationNote !== '')" v-model="moderationNote" manualSave>
<template #label>{{ i18n.ts.moderationNote }}</template>
+ <template #caption>{{ i18n.ts.moderationNoteDescription }}</template>
</MkTextarea>
<div v-else>
<MkButton small @click="editModerationNote = true">{{ i18n.ts.addModerationNote }}</MkButton>
@@ -190,15 +192,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { defineAsyncComponent, computed, onMounted, onUnmounted, nextTick, watch, ref } from 'vue';
import * as Misskey from 'misskey-js';
+import { getScrollPosition } from '@@/js/scroll.js';
import MkTab from '@/components/MkTab.vue';
import MkNotes from '@/components/MkNotes.vue';
import MkFollowButton from '@/components/MkFollowButton.vue';
import MkAccountMoved from '@/components/MkAccountMoved.vue';
+import MkFukidashi from '@/components/MkFukidashi.vue';
import MkRemoteCaution from '@/components/MkRemoteCaution.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkInfo from '@/components/MkInfo.vue';
import MkButton from '@/components/MkButton.vue';
-import { getScrollPosition } from '@@/js/scroll.js';
import { getUserMenu } from '@/scripts/get-user-menu.js';
import number from '@/filters/number.js';
import { userPage } from '@/filters/user.js';
@@ -213,11 +216,12 @@ import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFf
import { useRouter } from '@/router/supplier.js';
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
import { infoImageUrl } from '@/instance.js';
+import MkSparkle from '@/components/MkSparkle.vue';
const MkNote = defineAsyncComponent(() =>
defaultStore.state.noteDesign === 'sharkey'
- ? import('@/components/SkNote.vue')
- : import('@/components/MkNote.vue'),
+ ? import('@/components/SkNote.vue')
+ : import('@/components/MkNote.vue'),
);
function calcAge(birthdate: string): number {
@@ -473,8 +477,8 @@ onUnmounted(() => {
position: absolute;
top: 12px;
right: 12px;
- -webkit-backdrop-filter: var(--blur, blur(8px));
- backdrop-filter: var(--blur, blur(8px));
+ -webkit-backdrop-filter: var(--MI-blur, blur(8px));
+ backdrop-filter: var(--MI-blur, blur(8px));
background: rgba(0, 0, 0, 0.2);
padding: 8px;
border-radius: var(--radius-lg);
@@ -528,8 +532,8 @@ onUnmounted(() => {
> .add-note-button {
background: rgba(0, 0, 0, 0.2);
color: #fff;
- -webkit-backdrop-filter: var(--blur, blur(8px));
- backdrop-filter: var(--blur, blur(8px));
+ -webkit-backdrop-filter: var(--MI-blur, blur(8px));
+ backdrop-filter: var(--MI-blur, blur(8px));
border-radius: var(--radius-lg);
padding: 4px 8px;
font-size: 80%;
@@ -543,7 +547,7 @@ onUnmounted(() => {
text-align: center;
padding: 50px 8px 16px 8px;
font-weight: bold;
- border-bottom: solid 0.5px var(--divider);
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
> .bottom {
> * {
@@ -567,7 +571,18 @@ onUnmounted(() => {
> .followedMessage {
padding: 24px 24px 0 154px;
- font-size: 0.9em;
+
+ > .fukidashi {
+ display: block;
+ --fukidashi-bg: color-mix(in srgb, var(--MI_THEME-accent), var(--MI_THEME-panel) 85%);
+ --fukidashi-radius: 16px;
+ font-size: 0.9em;
+
+ .messageHeader {
+ opacity: 0.7;
+ font-size: 0.85em;
+ }
+ }
}
> .roles {
@@ -578,7 +593,7 @@ onUnmounted(() => {
gap: 8px;
> .role {
- border: solid 1px var(--color, var(--divider));
+ border: solid 1px var(--color, var(--MI_THEME-divider));
border-radius: var(--radius-ellipse);
margin-right: 4px;
padding: 3px 8px;
@@ -592,15 +607,15 @@ onUnmounted(() => {
> .memo {
margin: 12px 24px 0 154px;
background: transparent;
- color: var(--fg);
- border: 1px solid var(--divider);
+ color: var(--MI_THEME-fg);
+ border: 1px solid var(--MI_THEME-divider);
border-radius: var(--radius-sm);
padding: 8px;
line-height: 0;
> .heading {
text-align: left;
- color: var(--fgTransparent);
+ color: var(--MI_THEME-fgTransparent);
line-height: 1.5;
font-size: 85%;
}
@@ -615,7 +630,7 @@ onUnmounted(() => {
height: auto;
min-height: 0;
line-height: 1.5;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
overflow: hidden;
background: transparent;
font-family: inherit;
@@ -635,7 +650,7 @@ onUnmounted(() => {
> .fields {
padding: 24px;
font-size: 0.9em;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
> .field {
display: flex;
@@ -672,14 +687,14 @@ onUnmounted(() => {
> .status {
display: flex;
padding: 24px;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
> a {
flex: 1;
text-align: center;
&.active {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
&:hover {
@@ -701,7 +716,7 @@ onUnmounted(() => {
> .contents {
> .content {
- margin-bottom: var(--margin);
+ margin-bottom: var(--MI-margin);
}
}
}
@@ -718,7 +733,7 @@ onUnmounted(() => {
> .sub {
max-width: 350px;
min-width: 350px;
- margin-left: var(--margin);
+ margin-left: var(--MI-margin);
}
}
}
@@ -796,7 +811,7 @@ onUnmounted(() => {
<style lang="scss" module>
.tl {
background-color: rgba(0, 0, 0, 0);
- border-radius: var(--radius);
+ border-radius: var(--MI-radius);
overflow: clip;
z-index: 0;
}
@@ -817,7 +832,7 @@ onUnmounted(() => {
.verifiedLink {
margin-left: 4px;
- color: var(--success);
+ color: var(--MI_THEME-success);
}
.pinnedNote:not(:last-child) {
diff --git a/packages/frontend/src/pages/user/index.timeline.vue b/packages/frontend/src/pages/user/index.timeline.vue
index 8dbf90f344..49d015a530 100644
--- a/packages/frontend/src/pages/user/index.timeline.vue
+++ b/packages/frontend/src/pages/user/index.timeline.vue
@@ -51,13 +51,13 @@ const pagination = computed(() => tab.value === 'featured' ? {
<style lang="scss" module>
.tab {
- padding: calc(var(--margin) / 2) 0;
- background: var(--bg);
+ padding: calc(var(--MI-margin) / 2) 0;
+ background: var(--MI_THEME-bg);
}
.tl {
- background: var(--bg);
- border-radius: var(--radius);
+ background: var(--MI_THEME-bg);
+ border-radius: var(--MI-radius);
overflow: clip;
}
</style>
diff --git a/packages/frontend/src/pages/user/lists.vue b/packages/frontend/src/pages/user/lists.vue
index 8f95ce2dc9..46af3f7f37 100644
--- a/packages/frontend/src/pages/user/lists.vue
+++ b/packages/frontend/src/pages/user/lists.vue
@@ -44,12 +44,12 @@ const pagination = {
.list {
display: block;
padding: 16px;
- border: solid 1px var(--divider);
+ border: solid 1px var(--MI_THEME-divider);
border-radius: var(--radius-sm);
margin-bottom: 8px;
&:hover {
- border: solid 1px var(--accent);
+ border: solid 1px var(--MI_THEME-accent);
text-decoration: none;
}
}
diff --git a/packages/frontend/src/pages/user/raw.vue b/packages/frontend/src/pages/user/raw.vue
index ac18ad9392..4499eec10a 100644
--- a/packages/frontend/src/pages/user/raw.vue
+++ b/packages/frontend/src/pages/user/raw.vue
@@ -113,18 +113,18 @@ const suspended = computed(() => props.user.isSuspended ?? false);
}
> .suspended {
- color: var(--error);
- border-color: var(--error);
+ color: var(--MI_THEME-error);
+ border-color: var(--MI_THEME-error);
}
> .silenced {
- color: var(--warn);
- border-color: var(--warn);
+ color: var(--MI_THEME-warn);
+ border-color: var(--MI_THEME-warn);
}
> .moderator {
- color: var(--success);
- border-color: var(--success);
+ color: var(--MI_THEME-success);
+ border-color: var(--MI_THEME-success);
}
}
</style>
diff --git a/packages/frontend/src/pages/user/reactions.vue b/packages/frontend/src/pages/user/reactions.vue
index 3671decc18..7168778e12 100644
--- a/packages/frontend/src/pages/user/reactions.vue
+++ b/packages/frontend/src/pages/user/reactions.vue
@@ -44,7 +44,7 @@ const pagination = {
align-items: center;
padding: 8px 16px;
margin-bottom: 8px;
- border-bottom: solid 2px var(--divider);
+ border-bottom: solid 2px var(--MI_THEME-divider);
}
.avatar {
diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue
index 0d132e6a86..f6b7557e6d 100644
--- a/packages/frontend/src/pages/welcome.entrance.a.vue
+++ b/packages/frontend/src/pages/welcome.entrance.a.vue
@@ -98,7 +98,7 @@ misskeyApiGet('federation/instances', {
left: 0;
width: 100vw;
height: 100vh;
- background: var(--accent);
+ background: var(--MI_THEME-accent);
clip-path: polygon(0% 0%, 45% 0%, 20% 100%, 0% 100%);
}
> .shape2 {
@@ -107,7 +107,7 @@ misskeyApiGet('federation/instances', {
left: 0;
width: 100vw;
height: 100vh;
- background: var(--accent);
+ background: var(--MI_THEME-accent);
clip-path: polygon(0% 0%, 25% 0%, 35% 100%, 0% 100%);
opacity: 0.5;
}
@@ -164,9 +164,9 @@ misskeyApiGet('federation/instances', {
left: 0;
right: 0;
margin: auto;
- background: var(--acrylicPanel);
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
+ background: var(--MI_THEME-acrylicPanel);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
border-radius: var(--radius-ellipse);
overflow: clip;
width: 800px;
@@ -186,7 +186,7 @@ misskeyApiGet('federation/instances', {
vertical-align: bottom;
padding: 6px 12px 6px 6px;
margin: 0 10px 0 0;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
border-radius: var(--radius-ellipse);
> :global(.icon) {
diff --git a/packages/frontend/src/pages/welcome.setup.vue b/packages/frontend/src/pages/welcome.setup.vue
index 5a41100bf1..58e815d89c 100644
--- a/packages/frontend/src/pages/welcome.setup.vue
+++ b/packages/frontend/src/pages/welcome.setup.vue
@@ -14,6 +14,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div class="_gaps_m" style="padding: 32px;">
<div>{{ i18n.ts.intro }}</div>
+ <MkInput v-model="setupPassword" type="password" data-cy-admin-initial-password>
+ <template #label>{{ i18n.ts.initialPasswordForSetup }} <div v-tooltip:dialog="i18n.ts.initialPasswordForSetupDescription" class="_button _help"><i class="ti ti-help-circle"></i></div></template>
+ <template #prefix><i class="ti ti-lock"></i></template>
+ </MkInput>
<MkInput v-model="username" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" required data-cy-admin-username>
<template #label>{{ i18n.ts.username }}</template>
<template #prefix>@</template>
@@ -36,9 +40,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
+import { host, version } from '@@/js/config.js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
-import { host, version } from '@@/js/config.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { login } from '@/account.js';
@@ -47,6 +51,7 @@ import MkAnimBg from '@/components/MkAnimBg.vue';
const username = ref('');
const password = ref('');
+const setupPassword = ref('');
const submitting = ref(false);
function submit() {
@@ -56,14 +61,27 @@ function submit() {
misskeyApi('admin/accounts/create', {
username: username.value,
password: password.value,
+ setupPassword: setupPassword.value === '' ? null : setupPassword.value,
}).then(res => {
return login(res.token);
- }).catch(() => {
+ }).catch((err) => {
submitting.value = false;
+ let title = i18n.ts.somethingHappened;
+ let text = err.message + '\n' + err.id;
+
+ if (err.code === 'ACCESS_DENIED') {
+ title = i18n.ts.permissionDeniedError;
+ text = i18n.ts.operationForbidden;
+ } else if (err.code === 'INCORRECT_INITIAL_PASSWORD') {
+ title = i18n.ts.permissionDeniedError;
+ text = i18n.ts.incorrectPassword;
+ }
+
os.alert({
type: 'error',
- text: i18n.ts.somethingHappened,
+ title,
+ text,
});
});
}
@@ -74,14 +92,14 @@ function submit() {
min-height: 100svh;
padding: 32px 32px 64px 32px;
box-sizing: border-box;
-display: grid;
-place-content: center;
+ display: grid;
+ place-content: center;
}
.form {
position: relative;
z-index: 10;
- border-radius: var(--radius);
+ border-radius: var(--MI-radius);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
overflow: clip;
max-width: 500px;
@@ -92,8 +110,8 @@ place-content: center;
font-size: 1.5em;
text-align: center;
padding: 32px;
- background: var(--accentedBg);
- color: var(--accent);
+ background: var(--MI_THEME-accentedBg);
+ color: var(--MI_THEME-accent);
font-weight: bold;
}
diff --git a/packages/frontend/src/pages/welcome.timeline.note.vue b/packages/frontend/src/pages/welcome.timeline.note.vue
index ee8d4e1d62..5a03483d0c 100644
--- a/packages/frontend/src/pages/welcome.timeline.note.vue
+++ b/packages/frontend/src/pages/welcome.timeline.note.vue
@@ -84,7 +84,7 @@ onUpdated(() => {
left: 0;
width: 100%;
height: 64px;
- background: linear-gradient(0deg, var(--panel), color(from var(--panel) srgb r g b / 0));
+ background: linear-gradient(0deg, var(--MI_THEME-panel), color(from var(--MI_THEME-panel) srgb r g b / 0));
}
}
@@ -100,7 +100,7 @@ onUpdated(() => {
margin: 8px -16px -8px;
padding: 8px 16px 0;
width: calc(100% + 32px);
- border-top: 1px solid var(--divider);
+ border-top: 1px solid var(--MI_THEME-divider);
}
.richcontent {
diff --git a/packages/frontend/src/pages/welcome.timeline.vue b/packages/frontend/src/pages/welcome.timeline.vue
index 16d558cc91..2964b15164 100644
--- a/packages/frontend/src/pages/welcome.timeline.vue
+++ b/packages/frontend/src/pages/welcome.timeline.vue
@@ -60,7 +60,7 @@ onUpdated(() => {
transform: translate3d(0, 0, 0);
}
100% {
- transform: translate3d(0, calc(calc(-100% - 128px) - var(--margin)), 0);
+ transform: translate3d(0, calc(calc(-100% - 128px) - var(--MI-margin)), 0);
}
}
@@ -69,7 +69,7 @@ onUpdated(() => {
transform: translate3d(0, -128px, 0);
}
100% {
- transform: translate3d(0, calc(calc(-100% - 128px) - var(--margin)), 0);
+ transform: translate3d(0, calc(calc(-100% - 128px) - var(--MI-margin)), 0);
}
}
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index 4f3fb65665..c56fd185b6 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -267,13 +267,10 @@ export function getNoteMenu(props: {
function togglePin(pin: boolean): void {
os.apiWithDialog(pin ? 'i/pin' : 'i/unpin', {
noteId: appearNote.id,
- }, undefined, null, res => {
- if (res.id === '72dab508-c64d-498f-8740-a8eec1ba385a') {
- os.alert({
- type: 'error',
- text: i18n.ts.pinLimitExceeded,
- });
- }
+ }, undefined, {
+ '72dab508-c64d-498f-8740-a8eec1ba385a': {
+ text: i18n.ts.pinLimitExceeded,
+ },
});
}
diff --git a/packages/frontend/src/scripts/init-chart.ts b/packages/frontend/src/scripts/init-chart.ts
index 2465a14703..41e1636aa7 100644
--- a/packages/frontend/src/scripts/init-chart.ts
+++ b/packages/frontend/src/scripts/init-chart.ts
@@ -50,7 +50,7 @@ export function initChart() {
);
// フォントカラー
- Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--fg');
+ Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-fg');
Chart.defaults.borderColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)';
diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts
index bd3cddde67..8242e7d2e4 100644
--- a/packages/frontend/src/scripts/theme.ts
+++ b/packages/frontend/src/scripts/theme.ts
@@ -123,7 +123,7 @@ export function applyTheme(theme: Theme, persist = true) {
for (const [k, v] of Object.entries(props)) {
if (k.startsWith('font')) continue;
- document.documentElement.style.setProperty(`--${k}`, v.toString());
+ document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
}
document.documentElement.style.setProperty('color-scheme', colorScheme);
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index fd84ad2e4d..986ee9bf94 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -80,6 +80,10 @@ export const defaultStore = markRaw(new Storage('base', {
global: false,
},
},
+ abusesTutorial: {
+ where: 'account',
+ default: false,
+ },
keepCw: {
where: 'account',
default: true,
@@ -277,7 +281,7 @@ export const defaultStore = markRaw(new Storage('base', {
},
animatedMfm: {
where: 'device',
- default: false,
+ default: !window.matchMedia('(prefers-reduced-motion)').matches,
},
advancedMfm: {
where: 'device',
@@ -559,6 +563,10 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device',
default: 'app' as 'app' | 'appWithShift' | 'native',
},
+ skipNoteRender: {
+ where: 'device',
+ default: true,
+ },
sound_masterVolume: {
where: 'device',
diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss
index d990a706b3..116e650666 100644
--- a/packages/frontend/src/style.scss
+++ b/packages/frontend/src/style.scss
@@ -24,19 +24,18 @@
--radius-ellipse: 5px;
--radius-full: 5px;
- --marginFull: 16px;
- --marginHalf: 10px;
+ --MI-radius: 12px;
+ --MI-marginFull: 16px;
+ --MI-marginHalf: 10px;
- --margin: var(--marginFull);
+ --MI-margin: var(--MI-marginFull);
// switch dynamically
- --minBottomSpacingMobile: calc(72px + max(12px, env(safe-area-inset-bottom, 0px)));
- --minBottomSpacing: var(--minBottomSpacingMobile);
-
- //--ad: rgb(255 169 0 / 10%);
+ --MI-minBottomSpacingMobile: calc(72px + max(12px, env(safe-area-inset-bottom, 0px)));
+ --MI-minBottomSpacing: var(--MI-minBottomSpacingMobile);
@media (max-width: 500px) {
- --margin: var(--marginHalf);
+ --MI-margin: var(--MI-marginHalf);
}
--avatar: 48px;
@@ -55,14 +54,14 @@ html.radius-misskey {
}
::selection {
- color: var(--fgOnAccent);
- background-color: var(--accent);
+ color: var(--MI_THEME-fgOnAccent);
+ background-color: var(--MI_THEME-accent);
}
html {
- background-color: var(--bg);
- color: var(--fg);
- accent-color: var(--accent);
+ background-color: var(--MI_THEME-bg);
+ color: var(--MI_THEME-fg);
+ accent-color: var(--MI_THEME-accent);
overflow: auto;
overflow-wrap: break-word;
font-family: 'sharkey-theme-font-face', 'Lexend', 'Hiragino Maru Gothic Pro', "BIZ UDGothic", Roboto, HelveticaNeue, Arial, sans-serif;
@@ -73,7 +72,7 @@ html {
-webkit-text-size-adjust: 100%;
&, * {
- scrollbar-color: var(--scrollbarHandle) transparent;
+ scrollbar-color: var(--MI_THEME-scrollbarHandle) transparent;
scrollbar-width: thin;
&::-webkit-scrollbar {
@@ -86,14 +85,14 @@ html {
}
&::-webkit-scrollbar-thumb {
- background: var(--scrollbarHandle);
+ background: var(--MI_THEME-scrollbarHandle);
&:hover {
- background: var(--scrollbarHandleHover);
+ background: var(--MI_THEME-scrollbarHandleHover);
}
&:active {
- background: var(--accent);
+ background: var(--MI_THEME-accent);
}
}
}
@@ -155,15 +154,15 @@ textarea, input {
}
optgroup, option {
- background: var(--panel);
- color: var(--fg);
+ background: var(--MI_THEME-panel);
+ color: var(--MI_THEME-fg);
}
hr {
- margin: var(--margin) 0 var(--margin) 0;
+ margin: var(--MI-margin) 0 var(--MI-margin) 0;
border: none;
height: 1px;
- background: var(--divider);
+ background: var(--MI_THEME-divider);
}
rt {
@@ -171,7 +170,7 @@ rt {
}
:focus-visible {
- outline: var(--focus) solid 2px;
+ outline: var(--MI_THEME-focus) solid 2px;
outline-offset: -2px;
&:hover {
@@ -204,9 +203,9 @@ rt {
._indicateCounter {
display: inline-flex;
- color: var(--fgOnAccent);
+ color: var(--MI_THEME-fgOnAccent);
font-weight: 700;
- background: var(--indicator);
+ background: var(--MI_THEME-indicator);
height: 1.5em;
min-width: 1em;
align-items: center;
@@ -239,13 +238,13 @@ rt {
left: 0;
width: 100%;
height: 100%;
- background: var(--modalBg);
- -webkit-backdrop-filter: var(--modalBgFilter);
- backdrop-filter: var(--modalBgFilter);
+ background: var(--MI_THEME-modalBg);
+ -webkit-backdrop-filter: var(--MI-modalBgFilter);
+ backdrop-filter: var(--MI-modalBgFilter);
}
._shadow {
- box-shadow: 0px 4px 32px var(--shadow) !important;
+ box-shadow: 0px 4px 32px var(--MI_THEME-shadow) !important;
}
._button {
@@ -278,40 +277,40 @@ rt {
._buttonPrimary {
@extend ._button;
- color: var(--fgOnAccent);
- background: var(--accent);
+ color: var(--MI_THEME-fgOnAccent);
+ background: var(--MI_THEME-accent);
&:not(:disabled):hover {
- background: hsl(from var(--accent) h s calc(l + 5));
+ background: hsl(from var(--MI_THEME-accent) h s calc(l + 5));
}
&:not(:disabled):active {
- background: hsl(from var(--accent) h s calc(l - 5));
+ background: hsl(from var(--MI_THEME-accent) h s calc(l - 5));
}
}
._buttonGradate {
@extend ._buttonPrimary;
- color: var(--fgOnAccent);
- background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
+ color: var(--MI_THEME-fgOnAccent);
+ background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
&:not(:disabled):hover {
- background: linear-gradient(90deg, hsl(from var(--accent) h s calc(l + 5)), hsl(from var(--accent) h s calc(l + 5)));
+ background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5)));
}
&:not(:disabled):active {
- background: linear-gradient(90deg, hsl(from var(--accent) h s calc(l + 5)), hsl(from var(--accent) h s calc(l + 5)));
+ background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5)));
}
}
._help {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
cursor: help;
}
._textButton {
@extend ._button;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
&:focus-visible {
outline-offset: 2px;
@@ -323,13 +322,13 @@ rt {
}
._panel {
- background: color-mix(in srgb, var(--panel) 65%, transparent);
- border-radius: var(--radius);
+ background: color-mix(in srgb, var(--MI_THEME-panel) 65%, transparent);
+ border-radius: var(--MI-radius);
overflow: clip;
}
._margin {
- margin: var(--margin) 0;
+ margin: var(--MI-margin) 0;
}
._gaps_m {
@@ -347,7 +346,7 @@ rt {
._gaps {
display: flex;
flex-direction: column;
- gap: var(--margin);
+ gap: var(--MI-margin);
}
._buttons {
@@ -369,24 +368,24 @@ rt {
padding: 10px;
box-sizing: border-box;
text-align: center;
- border: solid 0.5px var(--divider);
- border-radius: var(--radius);
+ border: solid 0.5px var(--MI_THEME-divider);
+ border-radius: var(--MI-radius);
&:active {
- border-color: var(--accent);
+ border-color: var(--MI_THEME-accent);
}
}
._popup {
- background: var(--popup);
- border-radius: var(--radius);
+ background: var(--MI_THEME-popup);
+ border-radius: var(--MI-radius);
contain: content;
}
._acrylic {
- background: var(--acrylicPanel);
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
+ background: var(--MI_THEME-acrylicPanel);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
}
._formLinksGrid {
@@ -399,8 +398,8 @@ rt {
margin-left: 0.7em;
font-size: 65%;
padding: 2px 3px;
- color: var(--accent);
- border: solid 1px var(--accent);
+ color: var(--MI_THEME-accent);
+ border: solid 1px var(--MI_THEME-accent);
border-radius: var(--radius-xs);
vertical-align: top;
}
@@ -409,8 +408,8 @@ rt {
margin-left: 0.7em;
font-size: 65%;
padding: 2px 3px;
- color: var(--warn);
- border: solid 1px var(--warn);
+ color: var(--MI_THEME-warn);
+ border: solid 1px var(--MI_THEME-warn);
border-radius: 4px;
vertical-align: top;
}
@@ -456,7 +455,7 @@ rt {
}
._link {
- color: var(--link);
+ color: var(--MI_THEME-link);
}
._caption {
@@ -480,14 +479,14 @@ rt {
box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c;
border-radius: 10px;
- --bg: #F1E8DC;
- --fg: #693410;
+ --MI_THEME-bg: #F1E8DC;
+ --MI_THEME-fg: #693410;
}
html[data-color-scheme=dark] ._woodenFrame {
- --bg: #1d0c02;
- --fg: #F1E8DC;
- --panel: #192320;
+ --MI_THEME-bg: #1d0c02;
+ --MI_THEME-fg: #F1E8DC;
+ --MI_THEME-panel: #192320;
}
._woodenFrameH {
@@ -498,10 +497,10 @@ html[data-color-scheme=dark] ._woodenFrame {
._woodenFrameInner {
padding: 8px;
margin-top: 8px;
- background: var(--bg);
+ background: var(--MI_THEME-bg);
box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410;
border-radius: 6px;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
&:first-child {
margin-top: 0;
@@ -516,7 +515,11 @@ html[data-color-scheme=dark] ._woodenFrame {
transform: scale(0.9);
}
-@keyframes global-blink {
+._blink {
+ animation: blink 1s infinite;
+}
+
+@keyframes blink {
0% { opacity: 1; transform: scale(1); }
30% { opacity: 1; transform: scale(1); }
90% { opacity: 0; transform: scale(0.5); }
diff --git a/packages/frontend/src/ui/_common_/announcements.vue b/packages/frontend/src/ui/_common_/announcements.vue
index 374bc20b54..d153dc8726 100644
--- a/packages/frontend/src/ui/_common_/announcements.vue
+++ b/packages/frontend/src/ui/_common_/announcements.vue
@@ -13,9 +13,9 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<span :class="$style.icon">
<i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i>
- <i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--warn);"></i>
- <i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--error);"></i>
- <i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--success);"></i>
+ <i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i>
+ <i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--MI_THEME-error);"></i>
+ <i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--MI_THEME-success);"></i>
</span>
<span :class="$style.title">{{ announcement.title }}</span>
<span :class="$style.body">{{ announcement.text }}</span>
@@ -30,7 +30,7 @@ import { $i } from '@/account.js';
<style lang="scss" module>
.root {
font-size: 15px;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
}
.item {
@@ -44,8 +44,8 @@ import { $i } from '@/account.js';
height: var(--height);
overflow: clip;
contain: strict;
- background: var(--accent);
- color: var(--fgOnAccent);
+ background: var(--MI_THEME-accent);
+ color: var(--MI_THEME-fgOnAccent);
@container (max-width: 1000px) {
display: block;
diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue
index a8ff2a4c8d..7e29a5eeff 100644
--- a/packages/frontend/src/ui/_common_/common.vue
+++ b/packages/frontend/src/ui/_common_/common.vue
@@ -129,26 +129,26 @@ function getPointerEvents() {
.notifications {
position: fixed;
z-index: 3900000;
- padding: 0 var(--margin);
+ padding: 0 var(--MI-margin);
display: flex;
&.notificationsPosition_leftTop {
- top: var(--margin);
+ top: var(--MI-margin);
left: 0;
}
&.notificationsPosition_rightTop {
- top: var(--margin);
+ top: var(--MI-margin);
right: 0;
}
&.notificationsPosition_leftBottom {
- bottom: calc(var(--minBottomSpacing) + var(--margin));
+ bottom: calc(var(--MI-minBottomSpacing) + var(--MI-margin));
left: 0;
}
&.notificationsPosition_rightBottom {
- bottom: calc(var(--minBottomSpacing) + var(--margin));
+ bottom: calc(var(--MI-minBottomSpacing) + var(--MI-margin));
right: 0;
}
@@ -246,8 +246,8 @@ function getPointerEvents() {
height: 18px;
box-sizing: border-box;
border: solid 2px transparent;
- border-top-color: var(--accent);
- border-left-color: var(--accent);
+ border-top-color: var(--MI_THEME-accent);
+ border-left-color: var(--MI_THEME-accent);
border-radius: 50%;
animation: progress-spinner 400ms linear infinite;
}
diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
index f3244b5697..c8c0915dc9 100644
--- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
+++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="item === '-'" :class="$style.divider"></div>
<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" class="_button" :class="[$style.item, { [$style.active]: navbarItemDef[item].active }]" :activeClass="$style.active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
<i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item].icon]"></i><span :class="$style.itemText">{{ navbarItemDef[item].title }}</span>
- <span v-if="navbarItemDef[item].indicated" :class="$style.itemIndicator">
+ <span v-if="navbarItemDef[item].indicated" :class="$style.itemIndicator" class="_blink">
<span v-if="navbarItemDef[item].indicateValue" class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ navbarItemDef[item].indicateValue }}</span>
<i v-else class="_indicatorCircle"></i>
</span>
@@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA>
<button :class="$style.item" class="_button" @click="more">
<i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.more }}</span>
- <span v-if="otherMenuItemIndicated" :class="$style.itemIndicator"><i class="_indicatorCircle"></i></span>
+ <span v-if="otherMenuItemIndicated" :class="$style.itemIndicator" class="_blink"><i class="_indicatorCircle"></i></span>
</button>
<MkA :class="$style.item" :activeClass="$style.active" to="/settings">
<i :class="$style.itemIcon" class="ti ti-settings ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.settings }}</span>
@@ -82,7 +82,7 @@ function more() {
<style lang="scss" module>
.root {
- --nav-bg-transparent: color(from var(--navBg) srgb r g b / 0.5);
+ --nav-bg-transparent: color(from var(--MI_THEME-navBg) srgb r g b / 0.5);
display: flex;
flex-direction: column;
@@ -94,8 +94,8 @@ function more() {
z-index: 1;
padding: 20px 0;
background: var(--nav-bg-transparent);
- -webkit-backdrop-filter: var(--blur, blur(8px));
- backdrop-filter: var(--blur, blur(8px));
+ -webkit-backdrop-filter: var(--MI-blur, blur(8px));
+ backdrop-filter: var(--MI-blur, blur(8px));
}
.banner {
@@ -135,8 +135,8 @@ function more() {
bottom: 0;
padding: 20px 0;
background: var(--nav-bg-transparent);
- -webkit-backdrop-filter: var(--blur, blur(8px));
- backdrop-filter: var(--blur, blur(8px));
+ -webkit-backdrop-filter: var(--MI-blur, blur(8px));
+ backdrop-filter: var(--MI-blur, blur(8px));
}
.post {
@@ -144,7 +144,7 @@ function more() {
display: block;
width: 100%;
height: 40px;
- color: var(--fgOnAccent);
+ color: var(--MI_THEME-fgOnAccent);
font-weight: bold;
text-align: left;
@@ -160,12 +160,12 @@ function more() {
right: 0;
bottom: 0;
border-radius: var(--radius-ellipse);
- background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
+ background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
}
&:hover, &.active {
&::before {
- background: var(--accentLighten);
+ background: var(--MI_THEME-accentLighten);
}
}
}
@@ -209,7 +209,7 @@ function more() {
.divider {
margin: 16px 16px;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
.item {
@@ -223,15 +223,15 @@ function more() {
width: 100%;
text-align: left;
box-sizing: border-box;
- color: var(--navFg);
+ color: var(--MI_THEME-navFg);
&:hover {
text-decoration: none;
- color: var(--navHoverFg);
+ color: var(--MI_THEME-navHoverFg);
}
&.active {
- color: var(--navActive);
+ color: var(--MI_THEME-navActive);
}
&:hover, &.active {
@@ -247,7 +247,7 @@ function more() {
right: 0;
bottom: 0;
border-radius: var(--radius-ellipse);
- background: var(--accentedBg);
+ background: var(--MI_THEME-accentedBg);
}
}
}
@@ -262,9 +262,8 @@ function more() {
position: absolute;
top: 0;
left: 20px;
- color: var(--navIndicator);
+ color: var(--MI_THEME-navIndicator);
font-size: 8px;
- animation: global-blink 1s infinite;
&:has(.itemIndicateValueIcon) {
animation: none;
diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue
index 17690df412..9481a99231 100644
--- a/packages/frontend/src/ui/_common_/navbar.vue
+++ b/packages/frontend/src/ui/_common_/navbar.vue
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}"
>
<i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item].icon]"></i><span :class="$style.itemText">{{ navbarItemDef[item].title }}</span>
- <span v-if="navbarItemDef[item].indicated" :class="$style.itemIndicator">
+ <span v-if="navbarItemDef[item].indicated" :class="$style.itemIndicator" class="_blink">
<span v-if="navbarItemDef[item].indicateValue" class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ navbarItemDef[item].indicateValue }}</span>
<i v-else class="_indicatorCircle"></i>
</span>
@@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA>
<button class="_button" :class="$style.item" @click="more">
<i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.more }}</span>
- <span v-if="otherMenuItemIndicated" :class="$style.itemIndicator"><i class="_indicatorCircle"></i></span>
+ <span v-if="otherMenuItemIndicated" :class="$style.itemIndicator" class="_blink"><i class="_indicatorCircle"></i></span>
</button>
<MkA v-tooltip.noDelay.right="i18n.ts.settings" :class="$style.item" :activeClass="$style.active" to="/settings">
<i :class="$style.itemIcon" class="ti ti-settings ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.settings }}</span>
@@ -111,7 +111,7 @@ function more(ev: MouseEvent) {
.root {
--nav-width: 250px;
--nav-icon-only-width: 80px;
- --nav-bg-transparent: color(from var(--navBg) srgb r g b / 0.5);
+ --nav-bg-transparent: color(from var(--MI_THEME-navBg) srgb r g b / 0.5);
flex: 0 0 var(--nav-width);
width: var(--nav-width);
@@ -129,7 +129,7 @@ function more(ev: MouseEvent) {
overflow: auto;
overflow-x: clip;
overscroll-behavior: contain;
- background: var(--navBg);
+ background: var(--MI_THEME-navBg);
contain: strict;
display: flex;
flex-direction: column;
@@ -146,8 +146,8 @@ function more(ev: MouseEvent) {
z-index: 1;
padding: 20px 0;
background: var(--nav-bg-transparent);
- -webkit-backdrop-filter: var(--blur, blur(8px));
- backdrop-filter: var(--blur, blur(8px));
+ -webkit-backdrop-filter: var(--MI-blur, blur(8px));
+ backdrop-filter: var(--MI-blur, blur(8px));
}
.banner {
@@ -172,7 +172,7 @@ function more(ev: MouseEvent) {
outline: none;
> .instanceIcon {
- outline: 2px solid var(--focus);
+ outline: 2px solid var(--MI_THEME-focus);
outline-offset: 2px;
}
}
@@ -196,8 +196,8 @@ function more(ev: MouseEvent) {
bottom: 0;
padding-top: 20px;
background: var(--nav-bg-transparent);
- -webkit-backdrop-filter: var(--blur, blur(8px));
- backdrop-filter: var(--blur, blur(8px));
+ -webkit-backdrop-filter: var(--MI-blur, blur(8px));
+ backdrop-filter: var(--MI-blur, blur(8px));
}
.post {
@@ -205,7 +205,7 @@ function more(ev: MouseEvent) {
display: block;
width: 100%;
height: 40px;
- color: var(--fgOnAccent);
+ color: var(--MI_THEME-fgOnAccent);
font-weight: bold;
text-align: left;
@@ -221,21 +221,21 @@ function more(ev: MouseEvent) {
right: 0;
bottom: 0;
border-radius: var(--radius-ellipse);
- background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
+ background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
}
&:focus-visible {
outline: none;
&::before {
- outline: 2px solid var(--fgOnAccent);
+ outline: 2px solid var(--MI_THEME-fgOnAccent);
outline-offset: -4px;
}
}
&:hover, &.active {
&::before {
- background: var(--accentLighten);
+ background: var(--MI_THEME-accentLighten);
}
}
}
@@ -265,7 +265,7 @@ function more(ev: MouseEvent) {
outline: none;
> .avatar {
- box-shadow: 0 0 0 4px var(--focus);
+ box-shadow: 0 0 0 4px var(--MI_THEME-focus);
}
}
}
@@ -291,7 +291,7 @@ function more(ev: MouseEvent) {
.divider {
margin: 16px 16px;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
.item {
@@ -305,28 +305,28 @@ function more(ev: MouseEvent) {
width: 100%;
text-align: left;
box-sizing: border-box;
- color: var(--navFg);
+ color: var(--MI_THEME-navFg);
&:hover {
text-decoration: none;
- color: var(--navHoverFg);
+ color: var(--MI_THEME-navHoverFg);
}
&.active {
- color: var(--navActive);
+ color: var(--MI_THEME-navActive);
}
&:focus-visible {
outline: none;
&::before {
- outline: 2px solid var(--focus);
+ outline: 2px solid var(--MI_THEME-focus);
outline-offset: -2px;
}
}
&:hover, &.active, &:focus {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
&::before {
content: "";
@@ -340,7 +340,7 @@ function more(ev: MouseEvent) {
right: 0;
bottom: 0;
border-radius: var(--radius-ellipse);
- background: var(--accentedBg);
+ background: var(--MI_THEME-accentedBg);
}
}
}
@@ -355,9 +355,8 @@ function more(ev: MouseEvent) {
position: absolute;
top: 0;
left: 20px;
- color: var(--navIndicator);
+ color: var(--MI_THEME-navIndicator);
font-size: 8px;
- animation: global-blink 1s infinite;
&:has(.itemIndicateValueIcon) {
animation: none;
@@ -387,8 +386,8 @@ function more(ev: MouseEvent) {
z-index: 1;
padding: 20px 0;
background: var(--nav-bg-transparent);
- -webkit-backdrop-filter: var(--blur, blur(8px));
- backdrop-filter: var(--blur, blur(8px));
+ -webkit-backdrop-filter: var(--MI-blur, blur(8px));
+ backdrop-filter: var(--MI-blur, blur(8px));
}
.instance {
@@ -400,7 +399,7 @@ function more(ev: MouseEvent) {
outline: none;
> .instanceIcon {
- outline: 2px solid var(--focus);
+ outline: 2px solid var(--MI_THEME-focus);
outline-offset: 2px;
}
}
@@ -417,8 +416,8 @@ function more(ev: MouseEvent) {
bottom: 0;
padding-top: 20px;
background: var(--nav-bg-transparent);
- -webkit-backdrop-filter: var(--blur, blur(8px));
- backdrop-filter: var(--blur, blur(8px));
+ -webkit-backdrop-filter: var(--MI-blur, blur(8px));
+ backdrop-filter: var(--MI-blur, blur(8px));
}
.post {
@@ -440,28 +439,28 @@ function more(ev: MouseEvent) {
width: 52px;
aspect-ratio: 1/1;
border-radius: var(--radius-full);
- background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
+ background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
}
&:focus-visible {
outline: none;
&::before {
- outline: 2px solid var(--fgOnAccent);
+ outline: 2px solid var(--MI_THEME-fgOnAccent);
outline-offset: -4px;
}
}
&:hover, &.active {
&::before {
- background: var(--accentLighten);
+ background: var(--MI_THEME-accentLighten);
}
}
}
.postIcon {
position: relative;
- color: var(--fgOnAccent);
+ color: var(--MI_THEME-fgOnAccent);
}
.postText {
@@ -479,7 +478,7 @@ function more(ev: MouseEvent) {
outline: none;
> .avatar {
- box-shadow: 0 0 0 4px var(--focus);
+ box-shadow: 0 0 0 4px var(--MI_THEME-focus);
}
}
}
@@ -501,7 +500,7 @@ function more(ev: MouseEvent) {
.divider {
margin: 8px auto;
width: calc(100% - 32px);
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
.item {
@@ -515,14 +514,14 @@ function more(ev: MouseEvent) {
outline: none;
&::before {
- outline: 2px solid var(--focus);
+ outline: 2px solid var(--MI_THEME-focus);
outline-offset: -2px;
}
}
&:hover, &.active, &:focus {
text-decoration: none;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
&::before {
content: "";
@@ -536,7 +535,7 @@ function more(ev: MouseEvent) {
right: 0;
bottom: 0;
border-radius: var(--radius-ellipse);
- background: var(--accentedBg);
+ background: var(--MI_THEME-accentedBg);
}
> .icon,
@@ -560,9 +559,8 @@ function more(ev: MouseEvent) {
position: absolute;
top: 6px;
left: 24px;
- color: var(--navIndicator);
+ color: var(--MI_THEME-navIndicator);
font-size: 8px;
- animation: global-blink 1s infinite;
&:has(.itemIndicateValueIcon) {
animation: none;
diff --git a/packages/frontend/src/ui/_common_/statusbars.vue b/packages/frontend/src/ui/_common_/statusbars.vue
index 690366307b..5f9a938017 100644
--- a/packages/frontend/src/ui/_common_/statusbars.vue
+++ b/packages/frontend/src/ui/_common_/statusbars.vue
@@ -32,7 +32,7 @@ const XUserList = defineAsyncComponent(() => import('./statusbar-user-list.vue')
<style lang="scss" module>
.root {
font-size: 15px;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
}
.item {
@@ -81,7 +81,7 @@ const XUserList = defineAsyncComponent(() => import('./statusbar-user-list.vue')
.name {
padding: 0 var(--nameMargin);
font-weight: bold;
- color: var(--accent);
+ color: var(--MI_THEME-accent);
&:empty {
display: none;
diff --git a/packages/frontend/src/ui/_common_/stream-indicator.vue b/packages/frontend/src/ui/_common_/stream-indicator.vue
index ad93b7e61c..cc62a28b14 100644
--- a/packages/frontend/src/ui/_common_/stream-indicator.vue
+++ b/packages/frontend/src/ui/_common_/stream-indicator.vue
@@ -48,8 +48,8 @@ onUnmounted(() => {
.root {
position: fixed;
z-index: v-bind(zIndex);
- bottom: calc(var(--minBottomSpacing) + var(--margin));
- right: var(--margin);
+ bottom: calc(var(--MI-minBottomSpacing) + var(--MI-margin));
+ right: var(--MI-margin);
margin: 0;
padding: 12px;
font-size: 0.9em;
diff --git a/packages/frontend/src/ui/_common_/upload.vue b/packages/frontend/src/ui/_common_/upload.vue
index 96030c7897..d15c497645 100644
--- a/packages/frontend/src/ui/_common_/upload.vue
+++ b/packages/frontend/src/ui/_common_/upload.vue
@@ -125,10 +125,10 @@ const zIndex = os.claimZIndex('high');
height: 8px;
}
.mk-uploader > ol > li > progress::-webkit-progress-value {
- background: var(--accent);
+ background: var(--MI_THEME-accent);
}
.mk-uploader > ol > li > progress::-webkit-progress-bar {
- //background: var(--accentAlpha01);
+ //background: var(--MI_THEME-accentAlpha01);
background: transparent;
}
</style>
diff --git a/packages/frontend/src/ui/classic.header.vue b/packages/frontend/src/ui/classic.header.vue
index c03afd6cd6..f4633314ae 100644
--- a/packages/frontend/src/ui/classic.header.vue
+++ b/packages/frontend/src/ui/classic.header.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="item === '-'" class="divider"></div>
<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime v-tooltip="navbarItemDef[item].title" class="item _button" :class="item" activeClass="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
<i class="ti-fw" :class="navbarItemDef[item].icon"></i>
- <span v-if="navbarItemDef[item].indicated" class="indicator"><i class="_indicatorCircle"></i></span>
+ <span v-if="navbarItemDef[item].indicated" class="indicator _blink"><i class="_indicatorCircle"></i></span>
</component>
</template>
<div class="divider"></div>
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA>
<button v-click-anime class="item _button" @click="more">
<i class="ti ti-dots ti-fw"></i>
- <span v-if="otherNavItemIndicated" class="indicator"><i class="_indicatorCircle"></i></span>
+ <span v-if="otherNavItemIndicated" class="indicator _blink"><i class="_indicatorCircle"></i></span>
</button>
</div>
<div class="right">
@@ -104,7 +104,7 @@ onMounted(() => {
z-index: 1000;
width: 100%;
height: $height;
- background-color: var(--bg);
+ background-color: var(--MI_THEME-bg);
> .body {
max-width: 1380px;
@@ -140,18 +140,17 @@ onMounted(() => {
position: absolute;
top: 0;
left: 0;
- color: var(--navIndicator);
+ color: var(--MI_THEME-navIndicator);
font-size: 8px;
- animation: global-blink 1s infinite;
}
&:hover {
text-decoration: none;
- color: var(--navHoverFg);
+ color: var(--MI_THEME-navHoverFg);
}
&.active {
- color: var(--navActive);
+ color: var(--MI_THEME-navActive);
}
}
@@ -159,7 +158,7 @@ onMounted(() => {
display: inline-block;
height: 16px;
margin: 0 10px;
- border-right: solid 0.5px var(--divider);
+ border-right: solid 0.5px var(--MI_THEME-divider);
}
> .instance {
diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue
index 96cc24c9b9..f17027bcde 100644
--- a/packages/frontend/src/ui/classic.sidebar.vue
+++ b/packages/frontend/src/ui/classic.sidebar.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="item === '-'" class="divider"></div>
<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime class="item _button" :class="item" activeClass="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
<i class="ti-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ navbarItemDef[item].title }}</span>
- <span v-if="navbarItemDef[item].indicated" class="indicator">
+ <span v-if="navbarItemDef[item].indicated" class="indicator _blink">
<span v-if="navbarItemDef[item].indicateValue" class="_indicateCounter itemIndicateValueIcon">{{ navbarItemDef[item].indicateValue }}</span>
<i v-else class="_indicatorCircle"></i>
</span>
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA>
<button v-click-anime class="item _button" @click="more">
<i class="ti ti-dots ti-fw"></i><span class="text">{{ i18n.ts.more }}</span>
- <span v-if="otherNavItemIndicated" class="indicator"><i class="_indicatorCircle"></i></span>
+ <span v-if="otherNavItemIndicated" class="indicator _blink"><i class="_indicatorCircle"></i></span>
</button>
<MkA v-click-anime class="item" activeClass="active" to="/settings" :behavior="settingsWindowed ? 'window' : null">
<i class="ti ti-settings ti-fw"></i><span class="text">{{ i18n.ts.settings }}</span>
@@ -159,7 +159,7 @@ watch(defaultStore.reactiveState.menuDisplay, () => {
> .divider {
margin: 10px 0;
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
> .post {
@@ -224,9 +224,8 @@ watch(defaultStore.reactiveState.menuDisplay, () => {
position: absolute;
top: 0;
left: 0;
- color: var(--navIndicator);
+ color: var(--MI_THEME-navIndicator);
font-size: 8px;
- animation: global-blink 1s infinite;
&:has(.itemIndicateValueIcon) {
animation: none;
@@ -237,11 +236,11 @@ watch(defaultStore.reactiveState.menuDisplay, () => {
&:hover {
text-decoration: none;
- color: var(--navHoverFg);
+ color: var(--MI_THEME-navHoverFg);
}
&.active {
- color: var(--navActive);
+ color: var(--MI_THEME-navActive);
}
}
}
diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue
index 31bb1ddc14..ded945dda1 100644
--- a/packages/frontend/src/ui/classic.vue
+++ b/packages/frontend/src/ui/classic.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<XSidebar/>
</div>
<div v-else-if="!pageMetadata?.needWideArea" ref="widgetsLeft" class="widgets left">
- <XWidgets place="left" :marginTop="'var(--margin)'" @mounted="attachSticky(widgetsLeft)"/>
+ <XWidgets place="left" :marginTop="'var(--MI-margin)'" @mounted="attachSticky(widgetsLeft)"/>
</div>
<main class="main" @contextmenu.stop="onContextmenu">
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</main>
<div v-if="isDesktop && !pageMetadata?.needWideArea" ref="widgetsRight" class="widgets right">
- <XWidgets :place="showMenuOnTop ? 'right' : null" :marginTop="showMenuOnTop ? '0' : 'var(--margin)'" @mounted="attachSticky(widgetsRight)"/>
+ <XWidgets :place="showMenuOnTop ? 'right' : null" :marginTop="showMenuOnTop ? '0' : 'var(--MI-margin)'" @mounted="attachSticky(widgetsRight)"/>
</div>
</div>
@@ -216,8 +216,8 @@ onMounted(() => {
box-sizing: border-box;
&.wallpaper {
- background: var(--wallpaperOverlay);
- //backdrop-filter: var(--blur, blur(4px));
+ background: var(--MI_THEME-wallpaperOverlay);
+ //backdrop-filter: var(--MI-blur, blur(4px));
}
> .columns {
@@ -249,17 +249,16 @@ onMounted(() => {
min-width: 0;
width: 750px;
margin: 0 16px 0 0;
- border-left: solid 1px var(--divider);
- border-right: solid 1px var(--divider);
+ border-left: solid 1px var(--MI_THEME-divider);
+ border-right: solid 1px var(--MI_THEME-divider);
border-radius: 0;
overflow: clip;
- --margin: 12px;
+ --MI-margin: 12px;
}
> .widgets {
position: sticky;
top: 0;
- width: 300px;
height: 100%;
padding-top: 16px;
box-sizing: border-box;
@@ -281,13 +280,13 @@ onMounted(() => {
&.withGlobalHeader {
> .main {
margin-top: 0;
- border: solid 1px var(--divider);
- border-radius: var(--radius);
- --stickyTop: var(--globalHeaderHeight);
+ border: solid 1px var(--MI_THEME-divider);
+ border-radius: var(--MI-radius);
+ --MI-stickyTop: var(--globalHeaderHeight);
}
> .widgets {
- --stickyTop: var(--globalHeaderHeight);
+ --MI-stickyTop: var(--globalHeaderHeight);
margin-top: 0;
}
}
@@ -296,7 +295,7 @@ onMounted(() => {
margin: 0;
> .sidebar {
- border-right: solid 0.5px var(--divider);
+ border-right: solid 0.5px var(--MI_THEME-divider);
}
> .main {
@@ -318,10 +317,10 @@ onMounted(() => {
right: 0;
z-index: 1001;
height: 100dvh;
- padding: var(--margin) var(--margin) calc(var(--margin) + env(safe-area-inset-bottom, 0px));
+ padding: var(--MI-margin) var(--MI-margin) calc(var(--MI-margin) + env(safe-area-inset-bottom, 0px));
box-sizing: border-box;
overflow: auto;
- background: var(--bg);
+ background: var(--MI_THEME-bg);
}
> .ivnzpscs {
diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index cd4e256056..dfd6c7bf6f 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -50,11 +50,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div v-if="isMobile" :class="$style.nav">
- <button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i :class="$style.navButtonIcon" class="ti ti-menu-2"></i><span v-if="menuIndicated" :class="$style.navButtonIndicator"><i class="_indicatorCircle"></i></span></button>
+ <button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i :class="$style.navButtonIcon" class="ti ti-menu-2"></i><span v-if="menuIndicated" :class="$style.navButtonIndicator" class="_blink"><i class="_indicatorCircle"></i></span></button>
<button :class="$style.navButton" class="_button" @click="mainRouter.push('/')"><i :class="$style.navButtonIcon" class="ti ti-home"></i></button>
<button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')">
<i :class="$style.navButtonIcon" class="ti ti-bell"></i>
- <span v-if="$i?.hasUnreadNotification" :class="$style.navButtonIndicator">
+ <span v-if="$i?.hasUnreadNotification" :class="$style.navButtonIndicator" class="_blink">
<span class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ $i.unreadNotificationsCount > 99 ? '99+' : $i.unreadNotificationsCount }}</span>
</span>
</button>
@@ -97,6 +97,7 @@ import { v4 as uuid } from 'uuid';
import XCommon from './_common_/common.vue';
import { deckStore, columnTypes, addColumn as addColumnToStore, loadDeck, getProfiles, deleteProfile as deleteProfile_ } from './deck/deck-store.js';
import type { ColumnType } from './deck/deck-store.js';
+import type { MenuItem } from '@/types/menu.js';
import XSidebar from '@/ui/_common_/navbar.vue';
import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue';
import MkButton from '@/components/MkButton.vue';
@@ -118,7 +119,6 @@ import XMentionsColumn from '@/ui/deck/mentions-column.vue';
import XDirectColumn from '@/ui/deck/direct-column.vue';
import XRoleTimelineColumn from '@/ui/deck/role-timeline-column.vue';
import { mainRouter } from '@/router/main.js';
-import type { MenuItem } from '@/types/menu.js';
const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue'));
const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announcements.vue'));
@@ -305,7 +305,7 @@ body {
.root {
$nav-hide-threshold: 650px; // TODO: どこかに集約したい
- --margin: var(--marginHalf);
+ --MI-margin: var(--MI-marginHalf);
--columnGap: 6px;
@@ -332,7 +332,7 @@ body {
overflow-x: auto;
overflow-y: clip;
overscroll-behavior: contain;
- background: var(--deckBg);
+ background: var(--MI_THEME-deckBg);
&.center {
> .section:first-of-type {
@@ -414,7 +414,7 @@ body {
contain: strict;
overflow: auto;
overscroll-behavior: contain;
- background: var(--navBg);
+ background: var(--MI_THEME-navBg);
}
.nav {
@@ -428,10 +428,10 @@ body {
grid-gap: 8px;
width: 100%;
box-sizing: border-box;
- -webkit-backdrop-filter: var(--blur, blur(32px));
- backdrop-filter: var(--blur, blur(32px));
- background-color: var(--header);
- border-top: solid 0.5px var(--divider);
+ -webkit-backdrop-filter: var(--MI-blur, blur(32px));
+ backdrop-filter: var(--MI-blur, blur(32px));
+ background-color: var(--MI_THEME-header);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
.navButton {
@@ -443,30 +443,30 @@ body {
margin: auto;
border-radius: var(--radius-lg);
background: transparent;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
&:hover {
- color: var(--accent);
+ color: var(--MI_THEME-accent);
}
&:active {
- color: var(--accent);
- background: hsl(from var(--panel) h s calc(l - 2));
+ color: var(--MI_THEME-accent);
+ background: hsl(from var(--MI_THEME-panel) h s calc(l - 2));
}
}
.postButton {
composes: navButton;
- background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
- color: var(--fgOnAccent);
+ background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
+ color: var(--MI_THEME-fgOnAccent);
&:hover {
- background: linear-gradient(90deg, hsl(from var(--accent) h s calc(l + 5)), hsl(from var(--accent) h s calc(l + 5)));
- color: var(--fgOnAccent);
+ background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5)));
+ color: var(--MI_THEME-fgOnAccent);
}
&:active {
- background: linear-gradient(90deg, hsl(from var(--accent) h s calc(l + 5)), hsl(from var(--accent) h s calc(l + 5)));
+ background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5)));
color: var(--fgOnAccent);
}
}
@@ -480,9 +480,8 @@ body {
position: absolute;
top: 0;
left: 0;
- color: var(--indicator);
+ color: var(--MI_THEME-indicator);
font-size: 16px;
- animation: global-blink 1s infinite;
&:has(.itemIndicateValueIcon) {
animation: none;
diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue
index 5ed3aa754f..d89c322c72 100644
--- a/packages/frontend/src/ui/deck/column.vue
+++ b/packages/frontend/src/ui/deck/column.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<svg viewBox="0 0 256 128" :class="$style.tabShape">
<g transform="matrix(6.2431,0,0,6.2431,-677.417,-29.3839)">
- <path d="M149.512,4.707L108.507,4.707C116.252,4.719 118.758,14.958 118.758,14.958C118.758,14.958 121.381,25.283 129.009,25.209L149.512,25.209L149.512,4.707Z" style="fill:var(--deckBg);"/>
+ <path d="M149.512,4.707L108.507,4.707C116.252,4.719 118.758,14.958 118.758,14.958C118.758,14.958 121.381,25.283 129.009,25.209L149.512,25.209L149.512,4.707Z" style="fill:var(--MI_THEME-deckBg);"/>
</g>
</svg>
<div :class="$style.color"></div>
@@ -299,7 +299,7 @@ function onDrop(ev) {
left: 0;
width: 100%;
height: 100%;
- background: var(--focus);
+ background: var(--MI_THEME-focus);
}
}
@@ -313,7 +313,7 @@ function onDrop(ev) {
left: 0;
width: 100%;
height: 100%;
- background: var(--focus);
+ background: var(--MI_THEME-focus);
opacity: 0.5;
}
}
@@ -331,19 +331,19 @@ function onDrop(ev) {
}
&.naked {
- background: var(--acrylicBg) !important;
- -webkit-backdrop-filter: var(--blur, blur(10px));
- backdrop-filter: var(--blur, blur(10px));
+ background: var(--MI_THEME-acrylicBg) !important;
+ -webkit-backdrop-filter: var(--MI-blur, blur(10px));
+ backdrop-filter: var(--MI-blur, blur(10px));
> .header {
background: transparent;
box-shadow: none;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
}
> .body {
background: transparent !important;
- scrollbar-color: var(--scrollbarHandle) transparent;
+ scrollbar-color: var(--MI_THEME-scrollbarHandle) transparent;
&::-webkit-scrollbar-track {
background: transparent;
@@ -352,12 +352,12 @@ function onDrop(ev) {
}
&.paged {
- background: var(--bg) !important;
+ background: var(--MI_THEME-bg) !important;
> .body {
- background: var(--bg) !important;
+ background: var(--MI_THEME-bg) !important;
overflow-y: scroll !important;
- scrollbar-color: var(--scrollbarHandle) transparent;
+ scrollbar-color: var(--MI_THEME-scrollbarHandle) transparent;
&::-webkit-scrollbar-track {
background: inherit;
@@ -374,9 +374,9 @@ function onDrop(ev) {
height: var(--deckColumnHeaderHeight);
padding: 0 16px 0 30px;
font-size: 0.9em;
- color: var(--panelHeaderFg);
- background: var(--panelHeaderBg);
- box-shadow: 0 1px 0 0 var(--panelHeaderDivider);
+ color: var(--MI_THEME-panelHeaderFg);
+ background: var(--MI_THEME-panelHeaderBg);
+ box-shadow: 0 1px 0 0 var(--MI_THEME-panelHeaderDivider);
cursor: pointer;
user-select: none;
}
@@ -387,7 +387,7 @@ function onDrop(ev) {
left: 12px;
width: 3px;
height: calc(100% - 24px);
- background: var(--accent);
+ background: var(--MI_THEME-accent);
border-radius: var(--radius-ellipse);
}
@@ -441,11 +441,11 @@ function onDrop(ev) {
overscroll-behavior-y: contain;
box-sizing: border-box;
container-type: size;
- background-color: var(--bg);
- scrollbar-color: var(--scrollbarHandle) var(--panel);
+ background-color: var(--MI_THEME-bg);
+ scrollbar-color: var(--MI_THEME-scrollbarHandle) var(--MI_THEME-panel);
&::-webkit-scrollbar-track {
- background: var(--panel);
+ background: var(--MI_THEME-panel);
}
}
</style>
diff --git a/packages/frontend/src/ui/deck/widgets-column.vue b/packages/frontend/src/ui/deck/widgets-column.vue
index 9995996771..a0e62c8264 100644
--- a/packages/frontend/src/ui/deck/widgets-column.vue
+++ b/packages/frontend/src/ui/deck/widgets-column.vue
@@ -57,10 +57,10 @@ const menu = [{
<style lang="scss" module>
.root {
- --margin: 8px;
- --panelBorder: none;
+ --MI-margin: 8px;
+ --MI_THEME-panelBorder: none;
- padding: 0 var(--margin);
+ padding: 0 var(--MI-margin);
}
.intro {
diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue
index 2fdaca775b..0b3f5875f2 100644
--- a/packages/frontend/src/ui/universal.vue
+++ b/packages/frontend/src/ui/universal.vue
@@ -25,11 +25,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<button v-if="(!isDesktop || pageMetadata?.needWideArea) && !isMobile" :class="$style.widgetButton" class="_button" @click="widgetsShowing = true"><i class="ti ti-apps"></i></button>
<div v-if="isMobile" ref="navFooter" :class="$style.nav">
- <button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i :class="$style.navButtonIcon" class="ti ti-menu-2"></i><span v-if="menuIndicated" :class="$style.navButtonIndicator"><i class="_indicatorCircle"></i></span></button>
+ <button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i :class="$style.navButtonIcon" class="ti ti-menu-2"></i><span v-if="menuIndicated" :class="$style.navButtonIndicator" class="_blink"><i class="_indicatorCircle"></i></span></button>
<button :class="$style.navButton" class="_button" @click="isRoot ? top() : mainRouter.push('/')"><i :class="$style.navButtonIcon" class="ti ti-home"></i></button>
<button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')">
<i :class="$style.navButtonIcon" class="ti ti-bell"></i>
- <span v-if="$i?.hasUnreadNotification" :class="$style.navButtonIndicator">
+ <span v-if="$i?.hasUnreadNotification" :class="$style.navButtonIndicator" class="_blink">
<span class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ $i.unreadNotificationsCount > 99 ? '99+' : $i.unreadNotificationsCount }}</span>
</span>
</button>
@@ -96,9 +96,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { defineAsyncComponent, provide, onMounted, computed, ref, watch, shallowRef, Ref } from 'vue';
+import { instanceName } from '@@/js/config.js';
+import { CURRENT_STICKY_BOTTOM } from '@@/js/const.js';
+import { isLink } from '@@/js/is-link.js';
import XCommon from './_common_/common.vue';
import type MkStickyContainer from '@/components/global/MkStickyContainer.vue';
-import { instanceName } from '@@/js/config.js';
import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue';
import * as os from '@/os.js';
import { defaultStore } from '@/store.js';
@@ -108,10 +110,8 @@ import { $i } from '@/account.js';
import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js';
import { deviceKind } from '@/scripts/device-kind.js';
import { miLocalStorage } from '@/local-storage.js';
-import { CURRENT_STICKY_BOTTOM } from '@@/js/const.js';
import { useScrollPositionManager } from '@/nirax.js';
import { mainRouter } from '@/router/main.js';
-import { isLink } from '@@/js/is-link.js';
const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue'));
const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/navbar.vue'));
@@ -225,12 +225,12 @@ provide<Ref<number>>(CURRENT_STICKY_BOTTOM, navFooterHeight);
watch(navFooter, () => {
if (navFooter.value) {
navFooterHeight.value = navFooter.value.offsetHeight;
- document.body.style.setProperty('--stickyBottom', `${navFooterHeight.value}px`);
- document.body.style.setProperty('--minBottomSpacing', 'var(--minBottomSpacingMobile)');
+ document.body.style.setProperty('--MI-stickyBottom', `${navFooterHeight.value}px`);
+ document.body.style.setProperty('--MI-minBottomSpacing', 'var(--MI-minBottomSpacingMobile)');
} else {
navFooterHeight.value = 0;
- document.body.style.setProperty('--stickyBottom', '0px');
- document.body.style.setProperty('--minBottomSpacing', '0px');
+ document.body.style.setProperty('--MI-stickyBottom', '0px');
+ document.body.style.setProperty('--MI-minBottomSpacing', '0px');
}
}, {
immediate: true,
@@ -318,7 +318,7 @@ $widgets-hide-threshold: 1090px;
}
.sidebar {
- border-right: solid 0.5px var(--divider);
+ border-right: solid 0.5px var(--MI_THEME-divider);
}
.contents {
@@ -328,7 +328,7 @@ $widgets-hide-threshold: 1090px;
overflow: auto;
overflow-y: scroll;
overscroll-behavior: unset;
- background: var(--bg);
+ background: var(--MI_THEME-bg);
}
.widgets {
@@ -336,9 +336,9 @@ $widgets-hide-threshold: 1090px;
height: 100%;
box-sizing: border-box;
overflow: auto;
- padding: var(--margin) var(--margin) calc(var(--margin) + env(safe-area-inset-bottom, 0px));
- border-left: solid 0.5px var(--divider);
- background: var(--bg);
+ padding: var(--MI-margin) var(--MI-margin) calc(var(--MI-margin) + env(safe-area-inset-bottom, 0px));
+ border-left: solid 0.5px var(--MI_THEME-divider);
+ background: var(--MI_THEME-bg);
@media (max-width: $widgets-hide-threshold) {
display: none;
@@ -356,7 +356,7 @@ $widgets-hide-threshold: 1090px;
border-radius: var(--radius-full);
box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.2), 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12);
font-size: 22px;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
}
.widgetsDrawerBg {
@@ -370,11 +370,11 @@ $widgets-hide-threshold: 1090px;
z-index: 1001;
width: 310px;
height: 100dvh;
- padding: var(--margin) var(--margin) calc(var(--margin) + env(safe-area-inset-bottom, 0px)) !important;
+ padding: var(--MI-margin) var(--MI-margin) calc(var(--MI-margin) + env(safe-area-inset-bottom, 0px)) !important;
box-sizing: border-box;
overflow: auto;
overscroll-behavior: contain;
- background: var(--bg);
+ background: var(--MI_THEME-bg);
}
.widgetsCloseButton {
@@ -400,10 +400,10 @@ $widgets-hide-threshold: 1090px;
grid-gap: 8px;
width: 100%;
box-sizing: border-box;
- -webkit-backdrop-filter: var(--blur, blur(24px));
- backdrop-filter: var(--blur, blur(24px));
- background-color: var(--header);
- border-top: solid 0.5px var(--divider);
+ -webkit-backdrop-filter: var(--MI-blur, blur(24px));
+ backdrop-filter: var(--MI-blur, blur(24px));
+ background-color: var(--MI_THEME-header);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
.navButton {
@@ -415,32 +415,32 @@ $widgets-hide-threshold: 1090px;
margin: auto;
border-radius: var(--radius-lg);
background: transparent;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
&:hover {
- background: var(--panelHighlight);
- color: var(--accent);
+ background: var(--MI_THEME-panelHighlight);
+ color: var(--MI_THEME-accent);
}
&:active {
- background: hsl(from var(--panel) h s calc(l - 2));
- color: var(--accent);
+ background: hsl(from var(--MI_THEME-panel) h s calc(l - 2));
+ color: var(--MI_THEME-accent);
}
}
.postButton {
composes: navButton;
- background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
- color: var(--fgOnAccent);
+ background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
+ color: var(--MI_THEME-fgOnAccent);
&:hover {
- background: linear-gradient(90deg, hsl(from var(--accent) h s calc(l + 5)), hsl(from var(--accent) h s calc(l + 5)));
- color: var(--fgOnAccent);
+ background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5)));
+ color: var(--MI_THEME-fgOnAccent);
}
&:active {
- background: linear-gradient(90deg, hsl(from var(--accent) h s calc(l + 5)), hsl(from var(--accent) h s calc(l + 5)));
- color: var(--fgOnAccent);
+ color: var(--MI_THEME-fgOnAccent);
+ background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5)));
}
}
@@ -453,9 +453,8 @@ $widgets-hide-threshold: 1090px;
position: absolute;
top: 0;
left: 0;
- color: var(--indicator);
+ color: var(--MI_THEME-indicator);
font-size: 16px;
- animation: global-blink 1s infinite;
&:has(.itemIndicateValueIcon) {
animation: none;
@@ -478,7 +477,7 @@ $widgets-hide-threshold: 1090px;
contain: strict;
overflow: auto;
overscroll-behavior: contain;
- background: var(--navBg);
+ background: var(--MI_THEME-navBg);
}
.statusbars {
@@ -488,6 +487,6 @@ $widgets-hide-threshold: 1090px;
}
.spacer {
- height: calc(var(--minBottomSpacing));
+ height: calc(var(--MI-minBottomSpacing));
}
</style>
diff --git a/packages/frontend/src/ui/visitor.vue b/packages/frontend/src/ui/visitor.vue
index 510b2a4342..371eba4540 100644
--- a/packages/frontend/src/ui/visitor.vue
+++ b/packages/frontend/src/ui/visitor.vue
@@ -189,7 +189,7 @@ defineExpose({
left: 0;
width: 500px;
height: 100vh;
- background: var(--accent);
+ background: var(--MI_THEME-accent);
z-index: 1;
> .banner {
@@ -218,7 +218,7 @@ defineExpose({
min-width: 0;
> .header {
- background: var(--panel);
+ background: var(--MI_THEME-panel);
position: relative;
z-index: 1;
@@ -255,7 +255,7 @@ defineExpose({
left: 0;
width: 240px;
height: 100vh;
- background: var(--panel);
+ background: var(--MI_THEME-panel);
> .link {
display: block;
@@ -269,7 +269,7 @@ defineExpose({
> .divider {
margin: 8px auto;
width: calc(100% - 32px);
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
> .action {
@@ -284,7 +284,7 @@ defineExpose({
border-radius: var(--radius-ellipse);
&._button {
- background: var(--panel);
+ background: var(--MI_THEME-panel);
}
&:first-child {
diff --git a/packages/frontend/src/ui/zen.vue b/packages/frontend/src/ui/zen.vue
index ac13d7822f..7c2a6cc729 100644
--- a/packages/frontend/src/ui/zen.vue
+++ b/packages/frontend/src/ui/zen.vue
@@ -63,12 +63,12 @@ document.documentElement.style.overflowY = 'scroll';
}
.rootWithBottom {
- min-height: calc(100dvh - (60px + (var(--margin) * 2) + env(safe-area-inset-bottom, 0px)));
+ min-height: calc(100dvh - (60px + (var(--MI-margin) * 2) + env(safe-area-inset-bottom, 0px)));
box-sizing: border-box;
}
.bottom {
- height: calc(60px + (var(--margin) * 2) + env(safe-area-inset-bottom, 0px));
+ height: calc(60px + (var(--MI-margin) * 2) + env(safe-area-inset-bottom, 0px));
width: 100%;
margin-top: auto;
}
@@ -81,9 +81,9 @@ document.documentElement.style.overflowY = 'scroll';
max-width: 60px;
margin: auto;
border-radius: var(--radius-full);
- background: var(--panel);
- color: var(--fg);
- right: var(--margin);
- bottom: calc(var(--margin) + env(safe-area-inset-bottom, 0px));
+ background: var(--MI_THEME-panel);
+ color: var(--MI_THEME-fg);
+ right: var(--MI-margin);
+ bottom: calc(var(--MI-margin) + env(safe-area-inset-bottom, 0px));
}
</style>
diff --git a/packages/frontend/src/widgets/WidgetAiscript.vue b/packages/frontend/src/widgets/WidgetAiscript.vue
index 8e29254819..6a1fcfa1a6 100644
--- a/packages/frontend/src/widgets/WidgetAiscript.vue
+++ b/packages/frontend/src/widgets/WidgetAiscript.vue
@@ -126,10 +126,10 @@ defineExpose<WidgetComponentExpose>({
max-width: 100%;
min-width: 100%;
padding: 16px;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
background: transparent;
border: none;
- border-bottom: solid 0.5px var(--divider);
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
border-radius: 0;
box-sizing: border-box;
font: inherit;
@@ -154,7 +154,7 @@ defineExpose<WidgetComponentExpose>({
}
> .logs {
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
text-align: left;
padding: 16px;
diff --git a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
index bcfaaf00ab..c2bda85ac7 100644
--- a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
+++ b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
@@ -115,7 +115,7 @@ defineExpose<WidgetComponentExpose>({
<style lang="scss" module>
.bdayFRoot {
overflow: hidden;
- min-height: calc(calc(calc(50px * 3) - 8px) + calc(var(--margin) * 2));
+ min-height: calc(calc(calc(50px * 3) - 8px) + calc(var(--MI-margin) * 2));
}
.bdayFGrid {
display: grid;
@@ -123,7 +123,7 @@ defineExpose<WidgetComponentExpose>({
grid-template-rows: repeat(3, 42px);
place-content: center;
gap: 8px;
- margin: var(--margin) auto;
+ margin: var(--MI-margin) auto;
}
.bdayFFallback {
@@ -139,6 +139,6 @@ defineExpose<WidgetComponentExpose>({
width: auto;
max-width: 90%;
margin-bottom: 8px;
- border-radius: var(--radius);
+ border-radius: var(--MI-radius);
}
</style>
diff --git a/packages/frontend/src/widgets/WidgetCalendar.vue b/packages/frontend/src/widgets/WidgetCalendar.vue
index e4ac1acfc7..2387cb1ab4 100644
--- a/packages/frontend/src/widgets/WidgetCalendar.vue
+++ b/packages/frontend/src/widgets/WidgetCalendar.vue
@@ -207,7 +207,7 @@ defineExpose<WidgetComponentExpose>({
.meter {
width: 100%;
overflow: hidden;
- background: var(--X11);
+ background: var(--MI_THEME-X11);
border-radius: var(--radius-sm);
}
diff --git a/packages/frontend/src/widgets/WidgetFederation.vue b/packages/frontend/src/widgets/WidgetFederation.vue
index e91a77beab..3632975048 100644
--- a/packages/frontend/src/widgets/WidgetFederation.vue
+++ b/packages/frontend/src/widgets/WidgetFederation.vue
@@ -105,7 +105,7 @@ defineExpose<WidgetComponentExpose>({
display: flex;
align-items: center;
padding: 14px 16px;
- border-bottom: solid 0.5px var(--divider);
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
> img {
display: block;
@@ -120,7 +120,7 @@ defineExpose<WidgetComponentExpose>({
flex: 1;
overflow: hidden;
font-size: 0.9em;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
padding-right: 8px;
> .a {
diff --git a/packages/frontend/src/widgets/WidgetJobQueue.vue b/packages/frontend/src/widgets/WidgetJobQueue.vue
index edf6622a13..0ee6b863dc 100644
--- a/packages/frontend/src/widgets/WidgetJobQueue.vue
+++ b/packages/frontend/src/widgets/WidgetJobQueue.vue
@@ -173,14 +173,14 @@ defineExpose<WidgetComponentExpose>({
padding: 16px;
&:not(:first-child) {
- border-top: solid 0.5px var(--divider);
+ border-top: solid 0.5px var(--MI_THEME-divider);
}
> .label {
display: flex;
> .icon {
- color: var(--warn);
+ color: var(--MI_THEME-warn);
margin-left: auto;
animation: warnBlink 1s infinite;
}
@@ -198,11 +198,11 @@ defineExpose<WidgetComponentExpose>({
> div:last-child {
&.inc {
- color: var(--warn);
+ color: var(--MI_THEME-warn);
}
&.dec {
- color: var(--success);
+ color: var(--MI_THEME-success);
}
}
}
diff --git a/packages/frontend/src/widgets/WidgetMemo.vue b/packages/frontend/src/widgets/WidgetMemo.vue
index ee89beb944..82e03c1c65 100644
--- a/packages/frontend/src/widgets/WidgetMemo.vue
+++ b/packages/frontend/src/widgets/WidgetMemo.vue
@@ -84,10 +84,10 @@ defineExpose<WidgetComponentExpose>({
max-width: 100%;
min-width: 100%;
padding: 16px;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
background: transparent;
border: none;
- border-bottom: solid 0.5px var(--divider);
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
border-radius: 0;
box-sizing: border-box;
font: inherit;
diff --git a/packages/frontend/src/widgets/WidgetOnlineUsers.vue b/packages/frontend/src/widgets/WidgetOnlineUsers.vue
index d56ee96ac1..d8c4e259c8 100644
--- a/packages/frontend/src/widgets/WidgetOnlineUsers.vue
+++ b/packages/frontend/src/widgets/WidgetOnlineUsers.vue
@@ -72,6 +72,6 @@ defineExpose<WidgetComponentExpose>({
}
.text {
- color: var(--fgTransparentWeak);
+ color: var(--MI_THEME-fgTransparentWeak);
}
</style>
diff --git a/packages/frontend/src/widgets/WidgetRss.vue b/packages/frontend/src/widgets/WidgetRss.vue
index 511777a570..3e43687709 100644
--- a/packages/frontend/src/widgets/WidgetRss.vue
+++ b/packages/frontend/src/widgets/WidgetRss.vue
@@ -113,7 +113,7 @@ defineExpose<WidgetComponentExpose>({
.item {
display: block;
padding: 8px 16px;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
diff --git a/packages/frontend/src/widgets/WidgetRssTicker.vue b/packages/frontend/src/widgets/WidgetRssTicker.vue
index b393ecd74b..4f594b720f 100644
--- a/packages/frontend/src/widgets/WidgetRssTicker.vue
+++ b/packages/frontend/src/widgets/WidgetRssTicker.vue
@@ -171,7 +171,7 @@ defineExpose<WidgetComponentExpose>({
display: inline-flex;
align-items: center;
vertical-align: bottom;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
}
.divider {
@@ -179,6 +179,6 @@ defineExpose<WidgetComponentExpose>({
width: 0.5px;
height: 16px;
margin: 0 1em;
- background: var(--divider);
+ background: var(--MI_THEME-divider);
}
</style>
diff --git a/packages/frontend/src/widgets/WidgetTrends.vue b/packages/frontend/src/widgets/WidgetTrends.vue
index a41db513e8..47a4efc106 100644
--- a/packages/frontend/src/widgets/WidgetTrends.vue
+++ b/packages/frontend/src/widgets/WidgetTrends.vue
@@ -91,13 +91,13 @@ defineExpose<WidgetComponentExpose>({
display: flex;
align-items: center;
padding: 14px 16px;
- border-bottom: solid 0.5px var(--divider);
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
> .tag {
flex: 1;
overflow: hidden;
font-size: 0.9em;
- color: var(--fg);
+ color: var(--MI_THEME-fg);
> .a {
display: block;