merge: upstream changes for 2024.11 (!742)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/742 Closes #645 and #646 Approved-by: Hazelnoot <acomputerdog@gmail.com> Approved-by: Marie <github@yuugi.dev>
This commit is contained in:
commit
e2352839e4
616 changed files with 16825 additions and 7991 deletions
|
|
@ -10,7 +10,7 @@ import '@/style.scss';
|
|||
import { mainBoot } from '@/boot/main-boot.js';
|
||||
import { subBoot } from '@/boot/sub-boot.js';
|
||||
|
||||
const subBootPaths = ['/share', '/auth', '/miauth', '/signup-complete'];
|
||||
const subBootPaths = ['/share', '/auth', '/miauth', '/oauth', '/signup-complete'];
|
||||
|
||||
if (subBootPaths.some(i => location.pathname === i || location.pathname.startsWith(i + '/'))) {
|
||||
subBoot();
|
||||
|
|
|
|||
|
|
@ -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') {
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
import { defineAsyncComponent, reactive, ref } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { apiUrl } from '@@/js/config.js';
|
||||
import type { MenuItem, MenuButton } from '@/types/menu.js';
|
||||
import { showSuspendedDialog } from '@/scripts/show-suspended-dialog.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { miLocalStorage } from '@/local-storage.js';
|
||||
import type { MenuItem, MenuButton } from '@/types/menu.js';
|
||||
import { del, get, set } from '@/scripts/idb-proxy.js';
|
||||
import { apiUrl } from '@@/js/config.js';
|
||||
import { waiting, popup, popupMenu, success, alert } from '@/os.js';
|
||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||
import { unisonReload, reloadChannel } from '@/scripts/unison-reload.js';
|
||||
|
|
@ -173,7 +173,18 @@ function fetchAccount(token: string, id?: string, forceShowDialog?: boolean): Pr
|
|||
});
|
||||
}
|
||||
|
||||
export function updateAccount(accountData: Partial<Account>) {
|
||||
export function updateAccount(accountData: Account) {
|
||||
if (!$i) return;
|
||||
for (const key of Object.keys($i)) {
|
||||
delete $i[key];
|
||||
}
|
||||
for (const [key, value] of Object.entries(accountData)) {
|
||||
$i[key] = value;
|
||||
}
|
||||
miLocalStorage.setItem('account', JSON.stringify($i));
|
||||
}
|
||||
|
||||
export function updateAccountPartial(accountData: Partial<Account>) {
|
||||
if (!$i) return;
|
||||
for (const [key, value] of Object.entries(accountData)) {
|
||||
$i[key] = value;
|
||||
|
|
@ -232,26 +243,6 @@ export async function openAccountMenu(opts: {
|
|||
}, ev: MouseEvent) {
|
||||
if (!$i) return;
|
||||
|
||||
function showSigninDialog() {
|
||||
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {}, {
|
||||
done: res => {
|
||||
addAccount(res.id, res.i);
|
||||
success();
|
||||
},
|
||||
closed: () => dispose(),
|
||||
});
|
||||
}
|
||||
|
||||
function createAccount() {
|
||||
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSignupDialog.vue')), {}, {
|
||||
done: res => {
|
||||
addAccount(res.id, res.i);
|
||||
switchAccountWithToken(res.i);
|
||||
},
|
||||
closed: () => dispose(),
|
||||
});
|
||||
}
|
||||
|
||||
async function switchAccount(account: Misskey.entities.UserDetailed) {
|
||||
const storedAccounts = await getAccounts();
|
||||
const found = storedAccounts.find(x => x.id === account.id);
|
||||
|
|
@ -320,10 +311,22 @@ export async function openAccountMenu(opts: {
|
|||
text: i18n.ts.addAccount,
|
||||
children: [{
|
||||
text: i18n.ts.existingAccount,
|
||||
action: () => { showSigninDialog(); },
|
||||
action: () => {
|
||||
getAccountWithSigninDialog().then(res => {
|
||||
if (res != null) {
|
||||
success();
|
||||
}
|
||||
});
|
||||
},
|
||||
}, {
|
||||
text: i18n.ts.createAccount,
|
||||
action: () => { createAccount(); },
|
||||
action: () => {
|
||||
getAccountWithSignupDialog().then(res => {
|
||||
if (res != null) {
|
||||
switchAccountWithToken(res.token);
|
||||
}
|
||||
});
|
||||
},
|
||||
}],
|
||||
}, {
|
||||
type: 'link',
|
||||
|
|
@ -349,6 +352,40 @@ export async function openAccountMenu(opts: {
|
|||
});
|
||||
}
|
||||
|
||||
export function getAccountWithSigninDialog(): Promise<{ id: string, token: string } | null> {
|
||||
return new Promise((resolve) => {
|
||||
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {}, {
|
||||
done: async (res: Misskey.entities.SigninFlowResponse & { finished: true }) => {
|
||||
await addAccount(res.id, res.i);
|
||||
resolve({ id: res.id, token: res.i });
|
||||
},
|
||||
cancelled: () => {
|
||||
resolve(null);
|
||||
},
|
||||
closed: () => {
|
||||
dispose();
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function getAccountWithSignupDialog(): Promise<{ id: string, token: string } | null> {
|
||||
return new Promise((resolve) => {
|
||||
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSignupDialog.vue')), {}, {
|
||||
done: async (res: Misskey.entities.SignupResponse) => {
|
||||
await addAccount(res.id, res.token);
|
||||
resolve({ id: res.id, token: res.token });
|
||||
},
|
||||
cancelled: () => {
|
||||
resolve(null);
|
||||
},
|
||||
closed: () => {
|
||||
dispose();
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (_DEV_) {
|
||||
(window as any).$i = $i;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ import directives from '@/directives/index.js';
|
|||
import components from '@/components/index.js';
|
||||
import { applyTheme } from '@/scripts/theme.js';
|
||||
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js';
|
||||
import { updateI18n } from '@/i18n.js';
|
||||
import { updateI18n, i18n } from '@/i18n.js';
|
||||
import { $i, refreshAccount, login } from '@/account.js';
|
||||
import { defaultStore, ColdDeviceStorage } from '@/store.js';
|
||||
import { fetchInstance, instance } from '@/instance.js';
|
||||
import { deviceKind } from '@/scripts/device-kind.js';
|
||||
import { deviceKind, updateDeviceKind } from '@/scripts/device-kind.js';
|
||||
import { reloadChannel } from '@/scripts/unison-reload.js';
|
||||
import { getUrlWithoutLoginId } from '@/scripts/login-id.js';
|
||||
import { getAccountFromId } from '@/scripts/get-account-from-id.js';
|
||||
|
|
@ -183,24 +183,22 @@ 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.overridedDeviceKind, (kind) => {
|
||||
updateDeviceKind(kind);
|
||||
}, { immediate: true });
|
||||
|
||||
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 });
|
||||
|
||||
|
|
@ -280,6 +278,27 @@ export async function common(createVue: () => App<Element>) {
|
|||
|
||||
removeSplash();
|
||||
|
||||
//#region Self-XSS 対策メッセージ
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.warning}`,
|
||||
'color: #f00; background-color: #ff0; font-size: 36px; padding: 4px;',
|
||||
);
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.title}`,
|
||||
'color: #f00; font-weight: 900; font-family: "Hiragino Sans W9", "Hiragino Kaku Gothic ProN", sans-serif; font-size: 24px;',
|
||||
);
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.description1}`,
|
||||
'font-size: 16px; font-weight: 700;',
|
||||
);
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.description2}`,
|
||||
'font-size: 16px;',
|
||||
'font-size: 20px; font-weight: 700; color: #f00;',
|
||||
);
|
||||
console.log(i18n.tsx._selfXssPrevention.description3({ link: 'https://misskey-hub.net/docs/for-users/resources/self-xss/' }));
|
||||
//#endregion
|
||||
|
||||
return {
|
||||
isClientUpdated,
|
||||
app,
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
*/
|
||||
|
||||
import { createApp, defineAsyncComponent, markRaw } from 'vue';
|
||||
import { ui } from '@@/js/config.js';
|
||||
import { common } from './common.js';
|
||||
import type * as Misskey from 'misskey-js';
|
||||
import { ui } from '@@/js/config.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { alert, confirm, popup, post, toast } from '@/os.js';
|
||||
import { useStream } from '@/stream.js';
|
||||
import * as sound from '@/scripts/sound.js';
|
||||
import { $i, signout, updateAccount } from '@/account.js';
|
||||
import { $i, signout, updateAccountPartial } from '@/account.js';
|
||||
import { instance } from '@/instance.js';
|
||||
import { ColdDeviceStorage, defaultStore } from '@/store.js';
|
||||
import { reactionPicker } from '@/scripts/reaction-picker.js';
|
||||
|
|
@ -230,11 +230,41 @@ export async function mainBoot() {
|
|||
}
|
||||
|
||||
if (!claimedAchievements.includes('justPlainLucky')) {
|
||||
window.setInterval(() => {
|
||||
let justPlainLuckyTimer: number | null = null;
|
||||
let lastVisibilityChangedAt = Date.now();
|
||||
|
||||
function claimPlainLucky() {
|
||||
if (document.visibilityState !== 'visible') {
|
||||
if (justPlainLuckyTimer != null) window.clearTimeout(justPlainLuckyTimer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.floor(Math.random() * 20000) === 0) {
|
||||
claimAchievement('justPlainLucky');
|
||||
} else {
|
||||
justPlainLuckyTimer = window.setTimeout(claimPlainLucky, 1000 * 10);
|
||||
}
|
||||
}, 1000 * 10);
|
||||
}
|
||||
|
||||
window.addEventListener('visibilitychange', () => {
|
||||
const now = Date.now();
|
||||
|
||||
if (document.visibilityState === 'visible') {
|
||||
// タブを高速で切り替えたら取得処理が何度も走るのを防ぐ
|
||||
if ((now - lastVisibilityChangedAt) < 1000 * 10) {
|
||||
justPlainLuckyTimer = window.setTimeout(claimPlainLucky, 1000 * 10);
|
||||
} else {
|
||||
claimPlainLucky();
|
||||
}
|
||||
} else if (justPlainLuckyTimer != null) {
|
||||
window.clearTimeout(justPlainLuckyTimer);
|
||||
justPlainLuckyTimer = null;
|
||||
}
|
||||
|
||||
lastVisibilityChangedAt = now;
|
||||
}, { passive: true });
|
||||
|
||||
claimPlainLucky();
|
||||
}
|
||||
|
||||
if (!claimedAchievements.includes('client30min')) {
|
||||
|
|
@ -298,13 +328,13 @@ export async function mainBoot() {
|
|||
|
||||
// 自分の情報が更新されたとき
|
||||
main.on('meUpdated', i => {
|
||||
updateAccount(i);
|
||||
updateAccountPartial(i);
|
||||
});
|
||||
|
||||
main.on('readAllNotifications', () => {
|
||||
setFavIconDot(false);
|
||||
|
||||
updateAccount({
|
||||
updateAccountPartial({
|
||||
hasUnreadNotification: false,
|
||||
unreadNotificationsCount: 0,
|
||||
});
|
||||
|
|
@ -314,39 +344,39 @@ export async function mainBoot() {
|
|||
attemptShowNotificationDot();
|
||||
|
||||
const unreadNotificationsCount = ($i?.unreadNotificationsCount ?? 0) + 1;
|
||||
updateAccount({
|
||||
updateAccountPartial({
|
||||
hasUnreadNotification: true,
|
||||
unreadNotificationsCount,
|
||||
});
|
||||
});
|
||||
|
||||
main.on('unreadMention', () => {
|
||||
updateAccount({ hasUnreadMentions: true });
|
||||
updateAccountPartial({ hasUnreadMentions: true });
|
||||
});
|
||||
|
||||
main.on('readAllUnreadMentions', () => {
|
||||
updateAccount({ hasUnreadMentions: false });
|
||||
updateAccountPartial({ hasUnreadMentions: false });
|
||||
});
|
||||
|
||||
main.on('unreadSpecifiedNote', () => {
|
||||
updateAccount({ hasUnreadSpecifiedNotes: true });
|
||||
updateAccountPartial({ hasUnreadSpecifiedNotes: true });
|
||||
});
|
||||
|
||||
main.on('readAllUnreadSpecifiedNotes', () => {
|
||||
updateAccount({ hasUnreadSpecifiedNotes: false });
|
||||
updateAccountPartial({ hasUnreadSpecifiedNotes: false });
|
||||
});
|
||||
|
||||
main.on('readAllAntennas', () => {
|
||||
updateAccount({ hasUnreadAntenna: false });
|
||||
updateAccountPartial({ hasUnreadAntenna: false });
|
||||
});
|
||||
|
||||
main.on('unreadAntenna', () => {
|
||||
updateAccount({ hasUnreadAntenna: true });
|
||||
updateAccountPartial({ hasUnreadAntenna: true });
|
||||
sound.playMisskeySfx('antenna');
|
||||
});
|
||||
|
||||
main.on('readAllAnnouncements', () => {
|
||||
updateAccount({ hasUnreadAnnouncement: false });
|
||||
updateAccountPartial({ hasUnreadAnnouncement: false });
|
||||
});
|
||||
|
||||
// 個人宛てお知らせが発行されたとき
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
@ -4,141 +4,151 @@ 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="_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();
|
||||
|
||||
function resolve() {
|
||||
os.apiWithDialog('admin/resolve-abuse-user-report', {
|
||||
forward: forward.value,
|
||||
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(resolvedAs) {
|
||||
os.apiWithDialog('admin/resolve-abuse-user-report', {
|
||||
reportId: props.report.id,
|
||||
resolvedAs,
|
||||
}).then(() => {
|
||||
emit('resolved', props.report.id);
|
||||
});
|
||||
}
|
||||
|
||||
function forward() {
|
||||
os.apiWithDialog('admin/forward-abuse-user-report', {
|
||||
reportId: props.report.id,
|
||||
}).then(() => {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
<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;
|
||||
}
|
||||
}
|
||||
|
||||
> .detail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0px 24px 24px 24px;
|
||||
|
||||
.assignee {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
<style lang="scss" module>
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -121,10 +121,10 @@ onMounted(() => {
|
|||
|
||||
.iconFrame {
|
||||
position: relative;
|
||||
width: var(--avatar);
|
||||
height: var(--avatar);
|
||||
width: var(--MI-avatar);
|
||||
height: var(--MI-avatar);
|
||||
padding: 6px;
|
||||
border-radius: var(--radius-full);
|
||||
border-radius: var(--MI-radius-full);
|
||||
box-sizing: border-box;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
|
|
@ -191,7 +191,7 @@ onMounted(() => {
|
|||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: var(--radius-full);
|
||||
border-radius: var(--MI-radius-full);
|
||||
box-shadow: 0 1px 0px #ffffff88 inset;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -29,7 +29,7 @@ import { misskeyApi } from '@/scripts/misskey-api.js';
|
|||
import MkModal from '@/components/MkModal.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { $i, updateAccount } from '@/account.js';
|
||||
import { $i, updateAccountPartial } from '@/account.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
announcement: Misskey.entities.Announcement;
|
||||
|
|
@ -51,7 +51,7 @@ async function ok() {
|
|||
|
||||
modal.value?.close();
|
||||
misskeyApi('i/read-announcement', { announcementId: props.announcement.id });
|
||||
updateAccount({
|
||||
updateAccountPartial({
|
||||
unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== props.announcement.id),
|
||||
});
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ async function deleteAntenna() {
|
|||
function addUser() {
|
||||
os.selectUser({ includeSelf: true }).then(user => {
|
||||
users.value = users.value.trim();
|
||||
users.value += '\n@' + Misskey.acct.toString(user as any);
|
||||
users.value += '\n@' + Misskey.acct.toString(user);
|
||||
users.value = users.value.trim();
|
||||
});
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--MI_THEME-bg);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import MkAuthConfirm from './MkAuthConfirm.vue';
|
||||
void MkAuthConfirm;
|
||||
450
packages/frontend/src/components/MkAuthConfirm.vue
Normal file
450
packages/frontend/src/components/MkAuthConfirm.vue
Normal file
|
|
@ -0,0 +1,450 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div :class="$style.wrapper">
|
||||
<Transition
|
||||
mode="out-in"
|
||||
:enterActiveClass="$style.transition_enterActive"
|
||||
:leaveActiveClass="$style.transition_leaveActive"
|
||||
:enterFromClass="$style.transition_enterFrom"
|
||||
:leaveToClass="$style.transition_leaveTo"
|
||||
|
||||
:inert="_waiting"
|
||||
>
|
||||
<div v-if="phase === 'accountSelect'" key="accountSelect" :class="$style.root" class="_gaps">
|
||||
<div :class="$style.header" class="_gaps_s">
|
||||
<div :class="$style.iconFallback">
|
||||
<i class="ti ti-user"></i>
|
||||
</div>
|
||||
<div :class="$style.headerText">{{ i18n.ts.pleaseSelectAccount }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div :class="$style.accountSelectorLabel">{{ i18n.ts.selectAccount }}</div>
|
||||
<div :class="$style.accountSelectorList">
|
||||
<template v-for="[id, user] in users">
|
||||
<input :id="'account-' + id" v-model="selectedUser" type="radio" name="accountSelector" :value="id" :class="$style.accountSelectorRadio"/>
|
||||
<label :for="'account-' + id" :class="$style.accountSelectorItem">
|
||||
<MkAvatar :user="user" :class="$style.accountSelectorAvatar"/>
|
||||
<div :class="$style.accountSelectorBody">
|
||||
<MkUserName :user="user" :class="$style.accountSelectorName"/>
|
||||
<MkAcct :user="user" :class="$style.accountSelectorAcct"/>
|
||||
</div>
|
||||
</label>
|
||||
</template>
|
||||
<button class="_button" :class="[$style.accountSelectorItem, $style.accountSelectorAddAccountRoot]" @click="clickAddAccount">
|
||||
<div :class="[$style.accountSelectorAvatar, $style.accountSelectorAddAccountAvatar]">
|
||||
<i class="ti ti-user-plus"></i>
|
||||
</div>
|
||||
<div :class="[$style.accountSelectorBody, $style.accountSelectorName]">{{ i18n.ts.addAccount }}</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_buttonsCenter">
|
||||
<MkButton rounded gradate :disabled="selectedUser === null" @click="clickChooseAccount">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="phase === 'consent'" key="consent" :class="$style.root" class="_gaps">
|
||||
<div :class="$style.header" class="_gaps_s">
|
||||
<img v-if="icon" :class="$style.icon" :src="getProxiedImageUrl(icon, 'preview')"/>
|
||||
<div v-else :class="$style.iconFallback">
|
||||
<i class="ti ti-apps"></i>
|
||||
</div>
|
||||
<div :class="$style.headerText">{{ name ? i18n.tsx._auth.shareAccess({ name }) : i18n.ts._auth.shareAccessAsk }}</div>
|
||||
</div>
|
||||
<div v-if="permissions && permissions.length > 0" class="_gaps_s" :class="$style.permissionRoot">
|
||||
<div>{{ name ? i18n.tsx._auth.permission({ name }) : i18n.ts._auth.permissionAsk }}</div>
|
||||
<div :class="$style.permissionListWrapper">
|
||||
<ul :class="$style.permissionList">
|
||||
<li v-for="p in permissions" :key="p">{{ i18n.ts._permissions[p] }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<slot name="consentAdditionalInfo"></slot>
|
||||
<div>
|
||||
<div :class="$style.accountSelectorLabel">
|
||||
{{ i18n.ts._auth.scopeUser }} <button class="_textButton" @click="clickBackToAccountSelect">{{ i18n.ts.switchAccount }}</button>
|
||||
</div>
|
||||
<div :class="$style.accountSelectorList">
|
||||
<div :class="[$style.accountSelectorItem, $style.static]">
|
||||
<MkAvatar :user="users.get(selectedUser!)!" :class="$style.accountSelectorAvatar"/>
|
||||
<div :class="$style.accountSelectorBody">
|
||||
<MkUserName :user="users.get(selectedUser!)!" :class="$style.accountSelectorName"/>
|
||||
<MkAcct :user="users.get(selectedUser!)!" :class="$style.accountSelectorAcct"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_buttonsCenter">
|
||||
<MkButton rounded @click="clickCancel">{{ i18n.ts.reject }}</MkButton>
|
||||
<MkButton rounded gradate @click="clickAccept">{{ i18n.ts.accept }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="phase === 'success'" key="success" :class="$style.root" class="_gaps_s">
|
||||
<div :class="$style.header" class="_gaps_s">
|
||||
<div :class="$style.iconFallback">
|
||||
<i class="ti ti-check"></i>
|
||||
</div>
|
||||
<div :class="$style.headerText">{{ i18n.ts._auth.accepted }}</div>
|
||||
<div :class="$style.headerTextSub">{{ i18n.ts._auth.pleaseGoBack }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="phase === 'denied'" key="denied" :class="$style.root" class="_gaps_s">
|
||||
<div :class="$style.header" class="_gaps_s">
|
||||
<div :class="$style.iconFallback">
|
||||
<i class="ti ti-x"></i>
|
||||
</div>
|
||||
<div :class="$style.headerText">{{ i18n.ts._auth.denied }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="phase === 'failed'" key="failed" :class="$style.root" class="_gaps_s">
|
||||
<div :class="$style.header" class="_gaps_s">
|
||||
<div :class="$style.iconFallback">
|
||||
<i class="ti ti-x"></i>
|
||||
</div>
|
||||
<div :class="$style.headerText">{{ i18n.ts.somethingHappened }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
<div v-if="_waiting" :class="$style.waitingRoot">
|
||||
<MkLoading/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
|
||||
import { $i, getAccounts, getAccountWithSigninDialog, getAccountWithSignupDialog } from '@/account.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import * as os from '@/os.js';
|
||||
import { getProxiedImageUrl } from '@/scripts/media-proxy.js';
|
||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||
|
||||
const props = defineProps<{
|
||||
name?: string;
|
||||
icon?: string;
|
||||
permissions?: (typeof Misskey.permissions[number])[];
|
||||
manualWaiting?: boolean;
|
||||
waitOnDeny?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'accept', token: string): void;
|
||||
(ev: 'deny', token: string): void;
|
||||
}>();
|
||||
|
||||
const waiting = ref(true);
|
||||
const _waiting = computed(() => waiting.value || props.manualWaiting);
|
||||
const phase = ref<'accountSelect' | 'consent' | 'success' | 'denied' | 'failed'>('accountSelect');
|
||||
|
||||
const selectedUser = ref<string | null>(null);
|
||||
|
||||
const users = ref(new Map<string, Misskey.entities.UserDetailed & { token: string; }>());
|
||||
|
||||
async function init() {
|
||||
waiting.value = true;
|
||||
|
||||
users.value.clear();
|
||||
|
||||
if ($i) {
|
||||
users.value.set($i.id, $i);
|
||||
}
|
||||
|
||||
const accounts = await getAccounts();
|
||||
|
||||
const accountIdsToFetch = accounts.map(a => a.id).filter(id => !users.value.has(id));
|
||||
|
||||
if (accountIdsToFetch.length > 0) {
|
||||
const usersRes = await misskeyApi('users/show', {
|
||||
userIds: accountIdsToFetch,
|
||||
});
|
||||
|
||||
for (const user of usersRes) {
|
||||
if (users.value.has(user.id)) continue;
|
||||
|
||||
users.value.set(user.id, {
|
||||
...user,
|
||||
token: accounts.find(a => a.id === user.id)!.token,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
waiting.value = false;
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
function clickAddAccount(ev: MouseEvent) {
|
||||
selectedUser.value = null;
|
||||
|
||||
os.popupMenu([{
|
||||
text: i18n.ts.existingAccount,
|
||||
action: () => {
|
||||
getAccountWithSigninDialog().then(async (res) => {
|
||||
if (res != null) {
|
||||
os.success();
|
||||
await init();
|
||||
if (users.value.has(res.id)) {
|
||||
selectedUser.value = res.id;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
}, {
|
||||
text: i18n.ts.createAccount,
|
||||
action: () => {
|
||||
getAccountWithSignupDialog().then(async (res) => {
|
||||
if (res != null) {
|
||||
os.success();
|
||||
await init();
|
||||
if (users.value.has(res.id)) {
|
||||
selectedUser.value = res.id;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
}], ev.currentTarget ?? ev.target);
|
||||
}
|
||||
|
||||
function clickChooseAccount() {
|
||||
if (selectedUser.value === null) return;
|
||||
|
||||
phase.value = 'consent';
|
||||
}
|
||||
|
||||
function clickBackToAccountSelect() {
|
||||
selectedUser.value = null;
|
||||
phase.value = 'accountSelect';
|
||||
}
|
||||
|
||||
function clickCancel() {
|
||||
if (selectedUser.value === null) return;
|
||||
|
||||
const user = users.value.get(selectedUser.value)!;
|
||||
|
||||
const token = user.token;
|
||||
|
||||
if (props.waitOnDeny) {
|
||||
waiting.value = true;
|
||||
}
|
||||
emit('deny', token);
|
||||
}
|
||||
|
||||
async function clickAccept() {
|
||||
if (selectedUser.value === null) return;
|
||||
|
||||
const user = users.value.get(selectedUser.value)!;
|
||||
|
||||
const token = user.token;
|
||||
|
||||
waiting.value = true;
|
||||
emit('accept', token);
|
||||
}
|
||||
|
||||
function showUI(state: 'success' | 'denied' | 'failed') {
|
||||
phase.value = state;
|
||||
waiting.value = false;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
showUI,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.transition_enterActive,
|
||||
.transition_leaveActive {
|
||||
transition: opacity 0.3s cubic-bezier(0,0,.35,1), transform 0.3s cubic-bezier(0,0,.35,1);
|
||||
}
|
||||
.transition_enterFrom {
|
||||
opacity: 0;
|
||||
transform: translateX(50px);
|
||||
}
|
||||
.transition_leaveTo {
|
||||
opacity: 0;
|
||||
transform: translateX(-50px);
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
overflow-x: hidden;
|
||||
overflow-x: clip;
|
||||
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.waitingRoot {
|
||||
position: absolute;
|
||||
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;
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
.root {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 48px 24px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin: 0 auto;
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
.icon,
|
||||
.iconFallback {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
border-radius: 50%;
|
||||
border: 1px solid var(--MI_THEME-divider);
|
||||
background-color: #fff;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.iconFallback {
|
||||
border-radius: 50%;
|
||||
background-color: var(--MI_THEME-accentedBg);
|
||||
color: var(--MI_THEME-accent);
|
||||
text-align: center;
|
||||
line-height: 54px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.headerText,
|
||||
.headerTextSub {
|
||||
text-align: center;
|
||||
word-break: normal;
|
||||
word-break: auto-phrase;
|
||||
}
|
||||
|
||||
.headerText {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.permissionRoot {
|
||||
padding: 16px;
|
||||
border-radius: var(--MI-radius);
|
||||
background-color: var(--MI_THEME-bg);
|
||||
}
|
||||
|
||||
.permissionListWrapper {
|
||||
max-height: 350px;
|
||||
overflow-y: auto;
|
||||
padding: 12px;
|
||||
border-radius: var(--MI-radius);
|
||||
background-color: var(--MI_THEME-panel);
|
||||
}
|
||||
|
||||
.permissionList {
|
||||
margin: 0 0 0 1.5em;
|
||||
padding: 0;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.accountSelectorLabel {
|
||||
font-size: 0.85em;
|
||||
opacity: 0.7;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.accountSelectorList {
|
||||
border-radius: var(--MI-radius);
|
||||
border: 1px solid var(--MI_THEME-divider);
|
||||
overflow: hidden;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
.accountSelectorRadio {
|
||||
position: absolute;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
pointer-events: none;
|
||||
|
||||
&:focus-visible + .accountSelectorItem {
|
||||
outline: 2px solid var(--MI_THEME-accent);
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
&:checked:focus-visible + .accountSelectorItem {
|
||||
outline-color: #fff;
|
||||
}
|
||||
|
||||
&:checked + .accountSelectorItem {
|
||||
background: var(--MI_THEME-accent);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.accountSelectorItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px;
|
||||
font-size: 14px;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: var(--MI_THEME-buttonHoverBg);
|
||||
}
|
||||
|
||||
&.static {
|
||||
cursor: unset;
|
||||
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.accountSelectorAddAccountRoot {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.accountSelectorBody {
|
||||
padding: 0 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.accountSelectorAvatar {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.accountSelectorAddAccountAvatar {
|
||||
background-color: var(--MI_THEME-accentedBg);
|
||||
color: var(--MI_THEME-accent);
|
||||
font-size: 16px;
|
||||
line-height: 45px;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.accountSelectorName {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.accountSelectorAcct {
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -427,7 +427,7 @@ onBeforeUnmount(() => {
|
|||
max-width: 28px;
|
||||
max-height: 28px;
|
||||
margin: 0 8px 0 0;
|
||||
border-radius: var(--radius-full);
|
||||
border-radius: var(--MI-radius-full);
|
||||
}
|
||||
|
||||
.userName {
|
||||
|
|
|
|||
|
|
@ -129,8 +129,8 @@ function onMousedown(evt: MouseEvent): void {
|
|||
font-size: 95%;
|
||||
box-shadow: none;
|
||||
text-decoration: none;
|
||||
background: var(--buttonBg);
|
||||
border-radius: var(--radius-xs);
|
||||
background: var(--MI_THEME-buttonBg);
|
||||
border-radius: var(--MI-radius-xs);
|
||||
overflow: clip;
|
||||
box-sizing: border-box;
|
||||
transition: background 0.1s ease;
|
||||
|
|
@ -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 {
|
||||
|
|
@ -162,20 +162,20 @@ function onMousedown(evt: MouseEvent): void {
|
|||
}
|
||||
|
||||
&.rounded {
|
||||
border-radius: var(--radius-ellipse);
|
||||
border-radius: var(--MI-radius-ellipse);
|
||||
}
|
||||
|
||||
&.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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -272,7 +272,7 @@ function onMousedown(evt: MouseEvent): void {
|
|||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
overflow: clip;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
|
@ -281,7 +281,7 @@ function onMousedown(evt: MouseEvent): void {
|
|||
position: absolute;
|
||||
width: 2px;
|
||||
height: 2px;
|
||||
border-radius: var(--radius-full);
|
||||
border-radius: var(--MI-radius-full);
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
@ -104,8 +122,8 @@ async function requestRender() {
|
|||
sitekey: props.sitekey,
|
||||
theme: defaultStore.state.darkMode ? 'dark' : 'light',
|
||||
callback: callback,
|
||||
'expired-callback': callback,
|
||||
'error-callback': callback,
|
||||
'expired-callback': () => callback(undefined),
|
||||
'error-callback': () => callback(undefined),
|
||||
});
|
||||
} else if (props.provider === 'mcaptcha' && props.instanceUrl && props.sitekey) {
|
||||
const { default: Widget } = await import('@mcaptcha/vanilla-glue');
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -68,13 +68,13 @@ 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;
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--MI-radius-xl);
|
||||
background: #fff;
|
||||
|
||||
&.full {
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,11 +47,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { miLocalStorage } from '@/local-storage.js';
|
||||
|
||||
const props = defineProps<{
|
||||
channel: Record<string, any>;
|
||||
channel: Misskey.entities.Channel;
|
||||
}>();
|
||||
|
||||
const getLastReadedAt = (): number | null => {
|
||||
|
|
@ -100,7 +101,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 +118,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 {
|
||||
|
|
@ -138,7 +139,7 @@ const bannerStyle = computed(() => {
|
|||
padding: 8px 12px;
|
||||
font-size: 80%;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
|
@ -148,8 +149,8 @@ const bannerStyle = computed(() => {
|
|||
bottom: 16px;
|
||||
left: 16px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
color: var(--warn);
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--MI_THEME-warn);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
padding: 4px 7px;
|
||||
|
|
@ -167,7 +168,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,9 +214,9 @@ const bannerStyle = computed(() => {
|
|||
top: 0;
|
||||
right: 0;
|
||||
transform: translate(25%, -25%);
|
||||
background-color: var(--accent);
|
||||
border: solid var(--bg) 4px;
|
||||
border-radius: var(--radius-full);
|
||||
background-color: var(--MI_THEME-accent);
|
||||
border: solid var(--MI_THEME-bg) 4px;
|
||||
border-radius: var(--MI-radius-full);
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
aspect-ratio: 1 / 1;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -53,11 +53,11 @@ defineExpose({
|
|||
> .item {
|
||||
font-size: 85%;
|
||||
padding: 4px 12px 4px 8px;
|
||||
border: solid 1px var(--divider);
|
||||
border-radius: var(--radius-ellipse);
|
||||
border: solid 1px var(--MI_THEME-divider);
|
||||
border-radius: var(--MI-radius-ellipse);
|
||||
|
||||
&:hover {
|
||||
border-color: var(--inputBorderHover);
|
||||
border-color: var(--MI_THEME-inputBorderHover);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
|
|
@ -69,7 +69,7 @@ defineExpose({
|
|||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: var(--radius-full);
|
||||
border-radius: var(--MI-radius-full);
|
||||
vertical-align: -10%;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -95,8 +95,8 @@ watch(() => props.lang, (to) => {
|
|||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--divider);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
border: 1px solid var(--MI_THEME-divider);
|
||||
font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;
|
||||
|
||||
color: var(--shiki-fallback);
|
||||
|
|
@ -140,7 +140,7 @@ watch(() => props.lang, (to) => {
|
|||
& :global(.shiki) {
|
||||
padding: 12px;
|
||||
margin: 0;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
border: none;
|
||||
min-height: 130px;
|
||||
pointer-events: none;
|
||||
|
|
|
|||
|
|
@ -71,11 +71,11 @@ function copy() {
|
|||
.codeBlockFallbackRoot {
|
||||
display: block;
|
||||
overflow-wrap: anywhere;
|
||||
background: var(--bg);
|
||||
background: var(--MI_THEME-bg);
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
}
|
||||
|
||||
.codeBlockFallbackCode {
|
||||
|
|
@ -91,11 +91,11 @@ function copy() {
|
|||
cursor: pointer;
|
||||
|
||||
box-sizing: border-box;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
padding: 24px;
|
||||
margin-top: 4px;
|
||||
color: var(--fg);
|
||||
background: var(--bg);
|
||||
color: var(--MI_THEME-fg);
|
||||
background: var(--MI_THEME-bg);
|
||||
}
|
||||
|
||||
.codePlaceholderContainer {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -158,20 +158,20 @@ watch(v, newValue => {
|
|||
overflow-y: hidden;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-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-radius: var(--radius-sm);
|
||||
border-color: var(--MI_THEME-accent) !important;
|
||||
border-radius: var(--MI-radius-sm);
|
||||
}
|
||||
|
||||
.codeEditorScroller {
|
||||
|
|
@ -196,10 +196,10 @@ 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);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
box-sizing: border-box;
|
||||
outline: 0;
|
||||
min-width: calc(100% - 24px);
|
||||
|
|
@ -213,6 +213,6 @@ watch(v, newValue => {
|
|||
}
|
||||
|
||||
.textarea::selection {
|
||||
color: var(--bg);
|
||||
color: var(--MI_THEME-bg);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,17 +98,17 @@ const onInput = () => {
|
|||
font: inherit;
|
||||
font-weight: normal;
|
||||
font-size: 1em;
|
||||
color: var(--fg);
|
||||
background: var(--panel);
|
||||
border: solid 1px var(--panel);
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--MI_THEME-fg);
|
||||
background: var(--MI_THEME-panel);
|
||||
border: solid 1px var(--MI_THEME-panel);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
box-sizing: border-box;
|
||||
transition: border-color 0.1s ease-out;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--inputBorderHover) !important;
|
||||
border-color: var(--MI_THEME-inputBorderHover) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -64,26 +64,30 @@ const showBody = ref(props.expanded);
|
|||
const ignoreOmit = ref(false);
|
||||
const omitted = ref(false);
|
||||
|
||||
function enter(el) {
|
||||
function enter(el: Element) {
|
||||
if (!(el instanceof HTMLElement)) return;
|
||||
const elementHeight = el.getBoundingClientRect().height;
|
||||
el.style.height = 0;
|
||||
el.style.height = '0';
|
||||
el.offsetHeight; // reflow
|
||||
el.style.height = Math.min(elementHeight, props.maxHeight ?? Infinity) + 'px';
|
||||
el.style.height = `${Math.min(elementHeight, props.maxHeight ?? Infinity)}px`;
|
||||
}
|
||||
|
||||
function afterEnter(el) {
|
||||
el.style.height = null;
|
||||
function afterEnter(el: Element) {
|
||||
if (!(el instanceof HTMLElement)) return;
|
||||
el.style.height = '';
|
||||
}
|
||||
|
||||
function leave(el) {
|
||||
function leave(el: Element) {
|
||||
if (!(el instanceof HTMLElement)) return;
|
||||
const elementHeight = el.getBoundingClientRect().height;
|
||||
el.style.height = elementHeight + 'px';
|
||||
el.style.height = `${elementHeight}px`;
|
||||
el.offsetHeight; // reflow
|
||||
el.style.height = 0;
|
||||
el.style.height = '0';
|
||||
}
|
||||
|
||||
function afterLeave(el) {
|
||||
el.style.height = null;
|
||||
function afterLeave(el: Element) {
|
||||
if (!(el instanceof HTMLElement)) return;
|
||||
el.style.height = '';
|
||||
}
|
||||
|
||||
const calcOmit = () => {
|
||||
|
|
@ -138,7 +142,7 @@ onUnmounted(() => {
|
|||
position: relative;
|
||||
overflow: clip;
|
||||
contain: content;
|
||||
background: color-mix(in srgb, var(--panel) 65%, transparent);
|
||||
background: color-mix(in srgb, var(--MI_THEME-panel) 65%, transparent);
|
||||
&.naked {
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
|
|
@ -165,13 +169,14 @@ 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);
|
||||
background: color-mix(in srgb, var(--MI_THEME-panelHeaderBg) 35%, transparent);
|
||||
}
|
||||
|
||||
.title {
|
||||
|
|
@ -201,7 +206,7 @@ onUnmounted(() => {
|
|||
}
|
||||
|
||||
.content {
|
||||
--stickyTop: 0px;
|
||||
--MI-stickyTop: 0px;
|
||||
|
||||
&.omitted {
|
||||
position: relative;
|
||||
|
|
@ -216,20 +221,20 @@ 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);
|
||||
border-radius: var(--MI-radius-ellipse);
|
||||
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
> .fadeLabel {
|
||||
background: var(--panelHighlight);
|
||||
background: var(--MI_THEME-panelHighlight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
:withOkButton="true"
|
||||
@close="cancel()"
|
||||
@ok="ok()"
|
||||
@closed="$emit('closed')"
|
||||
@closed="emit('closed')"
|
||||
>
|
||||
<template #header>{{ i18n.ts.cropImage }}</template>
|
||||
<template #default="{ width, height }">
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
-->
|
||||
|
||||
<template>
|
||||
<MkModalWindow ref="dialogEl" @close="cancel()" @closed="$emit('closed')">
|
||||
<MkModalWindow ref="dialogEl" @close="cancel()" @closed="emit('closed')">
|
||||
<template #header>:{{ emoji.name }}:</template>
|
||||
<template #default>
|
||||
<MkSpacer>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -125,14 +128,14 @@ export default defineComponent({
|
|||
return children;
|
||||
};
|
||||
|
||||
function onBeforeLeave(element: Element) {
|
||||
const el = element as HTMLElement;
|
||||
function onBeforeLeave(el: Element) {
|
||||
if (!(el instanceof HTMLElement)) return;
|
||||
el.style.top = `${el.offsetTop}px`;
|
||||
el.style.left = `${el.offsetLeft}px`;
|
||||
}
|
||||
|
||||
function onLeaveCancelled(element: Element) {
|
||||
const el = element as HTMLElement;
|
||||
function onLeaveCancelled(el: Element) {
|
||||
if (!(el instanceof HTMLElement)) return;
|
||||
el.style.top = '';
|
||||
el.style.left = '';
|
||||
}
|
||||
|
|
@ -182,12 +185,12 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
&:not(.date-separated-list-nogap) > *:not(:last-child) {
|
||||
margin-bottom: var(--margin);
|
||||
margin-bottom: var(--MI-margin);
|
||||
}
|
||||
}
|
||||
|
||||
.date-separated-list-nogap {
|
||||
border-radius: var(--radius);
|
||||
border-radius: var(--MI-radius);
|
||||
|
||||
> * {
|
||||
margin: 0 !important;
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
</MkSelect>
|
||||
<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons">
|
||||
<MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabledReason" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
|
||||
<MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabledReason != null" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
|
||||
<MkButton v-if="showCancelButton || input || select" data-cy-modal-dialog-cancel inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton>
|
||||
</div>
|
||||
<div v-if="actions" :class="$style.buttons">
|
||||
|
|
@ -98,7 +98,7 @@ const props = withDefaults(defineProps<{
|
|||
text: string;
|
||||
primary?: boolean,
|
||||
danger?: boolean,
|
||||
callback: (...args: any[]) => void;
|
||||
callback: (...args: unknown[]) => void;
|
||||
}[];
|
||||
showOkButton?: boolean;
|
||||
showCancelButton?: boolean;
|
||||
|
|
@ -185,8 +185,8 @@ function onInputKeydown(evt: KeyboardEvent) {
|
|||
max-width: 480px;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
background: var(--panel);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--MI_THEME-panel);
|
||||
border-radius: var(--MI-radius-md);
|
||||
}
|
||||
|
||||
.icon {
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -75,22 +75,22 @@ 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));
|
||||
backdrop-filter: var(--MI-blur, blur(15px));
|
||||
}
|
||||
|
||||
.icon {
|
||||
text-align: center;
|
||||
padding-top: 25px;
|
||||
width: 100px;
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.icon {
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ function onDragend() {
|
|||
position: relative;
|
||||
padding: 8px 0 0 0;
|
||||
min-height: 180px;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,8 +313,8 @@ function onContextmenu(ev: MouseEvent) {
|
|||
position: relative;
|
||||
padding: 8px;
|
||||
height: 64px;
|
||||
background: var(--driveFolderBg);
|
||||
border-radius: var(--radius-xs);
|
||||
background: var(--MI_THEME-driveFolderBg);
|
||||
border-radius: var(--MI-radius-xs);
|
||||
cursor: pointer;
|
||||
|
||||
&.draghover {
|
||||
|
|
@ -326,8 +326,8 @@ function onContextmenu(ev: MouseEvent) {
|
|||
right: -4px;
|
||||
bottom: -4px;
|
||||
left: -4px;
|
||||
border: 2px dashed var(--focus);
|
||||
border-radius: var(--radius-xs);
|
||||
border: 2px dashed var(--MI_THEME-focus);
|
||||
border-radius: var(--MI-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>
|
||||
|
|
|
|||
|
|
@ -167,7 +167,12 @@ const ilFilesObserver = new IntersectionObserver(
|
|||
(entries) => entries.some((entry) => entry.isIntersecting) && !fetching.value && moreFiles.value && fetchMoreFiles(),
|
||||
);
|
||||
|
||||
const sortModeSelect = ref<NonNullable<Misskey.entities.DriveFilesRequest['sort']>>('+createdAt');
|
||||
|
||||
watch(folder, () => emit('cd', folder.value));
|
||||
watch(sortModeSelect, () => {
|
||||
fetch();
|
||||
});
|
||||
|
||||
function onStreamDriveFileCreated(file: Misskey.entities.DriveFile) {
|
||||
addFile(file, true);
|
||||
|
|
@ -203,7 +208,7 @@ function onStreamDriveFolderDeleted(folderId: string) {
|
|||
removeFolder(folderId);
|
||||
}
|
||||
|
||||
function onDragover(ev: DragEvent): any {
|
||||
function onDragover(ev: DragEvent) {
|
||||
if (!ev.dataTransfer) return;
|
||||
|
||||
// ドラッグ元が自分自身の所有するアイテムだったら
|
||||
|
|
@ -248,7 +253,7 @@ function onDragleave() {
|
|||
draghover.value = false;
|
||||
}
|
||||
|
||||
function onDrop(ev: DragEvent): any {
|
||||
function onDrop(ev: DragEvent) {
|
||||
draghover.value = false;
|
||||
|
||||
if (!ev.dataTransfer) return;
|
||||
|
|
@ -337,7 +342,7 @@ function createFolder() {
|
|||
title: i18n.ts.createFolder,
|
||||
placeholder: i18n.ts.folderName,
|
||||
}).then(({ canceled, result: name }) => {
|
||||
if (canceled) return;
|
||||
if (canceled || name == null) return;
|
||||
misskeyApi('drive/folders/create', {
|
||||
name: name,
|
||||
parentId: folder.value ? folder.value.id : undefined,
|
||||
|
|
@ -570,6 +575,7 @@ async function fetch() {
|
|||
type: props.type,
|
||||
limit: filesMax + 1,
|
||||
searchQuery: searchQuery.value.toString().trim(),
|
||||
sort: sortModeSelect.value,
|
||||
}).then(fetchedFiles => {
|
||||
if (fetchedFiles.length === filesMax + 1) {
|
||||
moreFiles.value = true;
|
||||
|
|
@ -621,6 +627,7 @@ function fetchMoreFiles() {
|
|||
untilId: files.value.at(-1)?.id,
|
||||
limit: max + 1,
|
||||
searchQuery: searchQuery.value.toString().trim(),
|
||||
sort: sortModeSelect.value,
|
||||
}).then(files => {
|
||||
if (files.length === max + 1) {
|
||||
moreFiles.value = true;
|
||||
|
|
@ -656,6 +663,43 @@ function getMenu() {
|
|||
type: 'label',
|
||||
});
|
||||
|
||||
menu.push({
|
||||
type: 'parent',
|
||||
text: i18n.ts.sort,
|
||||
icon: 'ti ti-arrows-sort',
|
||||
children: [{
|
||||
text: `${i18n.ts.registeredDate} (${i18n.ts.descendingOrder})`,
|
||||
icon: 'ti ti-sort-descending-letters',
|
||||
action: () => { sortModeSelect.value = '+createdAt'; },
|
||||
active: sortModeSelect.value === '+createdAt',
|
||||
}, {
|
||||
text: `${i18n.ts.registeredDate} (${i18n.ts.ascendingOrder})`,
|
||||
icon: 'ti ti-sort-ascending-letters',
|
||||
action: () => { sortModeSelect.value = '-createdAt'; },
|
||||
active: sortModeSelect.value === '-createdAt',
|
||||
}, {
|
||||
text: `${i18n.ts.size} (${i18n.ts.descendingOrder})`,
|
||||
icon: 'ti ti-sort-descending-letters',
|
||||
action: () => { sortModeSelect.value = '+size'; },
|
||||
active: sortModeSelect.value === '+size',
|
||||
}, {
|
||||
text: `${i18n.ts.size} (${i18n.ts.ascendingOrder})`,
|
||||
icon: 'ti ti-sort-ascending-letters',
|
||||
action: () => { sortModeSelect.value = '-size'; },
|
||||
active: sortModeSelect.value === '-size',
|
||||
}, {
|
||||
text: `${i18n.ts.name} (${i18n.ts.descendingOrder})`,
|
||||
icon: 'ti ti-sort-descending-letters',
|
||||
action: () => { sortModeSelect.value = '+name'; },
|
||||
active: sortModeSelect.value === '+name',
|
||||
}, {
|
||||
text: `${i18n.ts.name} (${i18n.ts.ascendingOrder})`,
|
||||
icon: 'ti ti-sort-ascending-letters',
|
||||
action: () => { sortModeSelect.value = '-name'; },
|
||||
active: sortModeSelect.value === '-name',
|
||||
}],
|
||||
});
|
||||
|
||||
if (folder.value) {
|
||||
menu.push({
|
||||
text: i18n.ts.renameFolder,
|
||||
|
|
@ -735,7 +779,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 +831,7 @@ onBeforeUnmount(() => {
|
|||
.main {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: var(--margin);
|
||||
padding: var(--MI-margin);
|
||||
user-select: none;
|
||||
|
||||
&.fetching {
|
||||
|
|
@ -834,7 +878,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>
|
||||
|
|
|
|||
|
|
@ -69,8 +69,8 @@ const isThumbnailAvailable = computed(() => {
|
|||
.root {
|
||||
position: relative;
|
||||
display: flex;
|
||||
background: var(--panel);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--MI_THEME-panel);
|
||||
border-radius: var(--MI-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 {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
:scroll="false"
|
||||
:withOkButton="false"
|
||||
@close="cancel()"
|
||||
@closed="$emit('closed')"
|
||||
@closed="emit('closed')"
|
||||
>
|
||||
<template #header><i class="ti ti-code"></i> {{ i18n.ts._embedCodeGen.title }}</template>
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(--MI-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(--MI-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>
|
||||
|
|
@ -90,7 +90,7 @@ function computeButtonTitle(ev: MouseEvent): void {
|
|||
elm.title = getEmojiName(emoji);
|
||||
}
|
||||
|
||||
function nestedChosen(emoji: any, ev: MouseEvent) {
|
||||
function nestedChosen(emoji: string, ev: MouseEvent) {
|
||||
emit('chosen', emoji, ev);
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -409,7 +409,7 @@ function computeButtonTitle(ev: MouseEvent): void {
|
|||
elm.title = getEmojiName(emoji);
|
||||
}
|
||||
|
||||
function chosen(emoji: any, ev?: MouseEvent) {
|
||||
function chosen(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef, ev?: MouseEvent) {
|
||||
const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined;
|
||||
if (el) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
|
|
@ -426,7 +426,7 @@ function chosen(emoji: any, ev?: MouseEvent) {
|
|||
// 最近使った絵文字更新
|
||||
if (!pinned.value?.includes(key)) {
|
||||
let recents = defaultStore.state.recentlyUsedEmojis;
|
||||
recents = recents.filter((emoji: any) => emoji !== key);
|
||||
recents = recents.filter((emoji) => emoji !== key);
|
||||
recents.unshift(key);
|
||||
defaultStore.set('recentlyUsedEmojis', recents.splice(0, 32));
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -722,7 +722,7 @@ defineExpose({
|
|||
width: var(--eachSize);
|
||||
height: var(--eachSize);
|
||||
contain: strict;
|
||||
border-radius: var(--radius-xs);
|
||||
border-radius: var(--MI-radius-xs);
|
||||
font-size: 24px;
|
||||
|
||||
&:hover {
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ function opening() {
|
|||
|
||||
<style lang="scss" module>
|
||||
.drawer {
|
||||
border-radius: var(--radius-lg);
|
||||
border-radius: var(--MI-radius-lg);
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
146
packages/frontend/src/components/MkExtensionInstaller.vue
Normal file
146
packages/frontend/src/components/MkExtensionInstaller.vue
Normal file
|
|
@ -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, unknown>;
|
||||
};
|
||||
} | {
|
||||
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>
|
||||
|
|
@ -66,7 +66,7 @@ const props = defineProps<{
|
|||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
}
|
||||
|
||||
> .thumbnail {
|
||||
|
|
@ -108,7 +108,7 @@ const props = defineProps<{
|
|||
padding: 2px 4px;
|
||||
background: #ff0000bf;
|
||||
color: #fff;
|
||||
border-radius: var(--radius-xs);
|
||||
border-radius: var(--MI-radius-xs);
|
||||
font-size: 85%;
|
||||
animation: sensitive-blink 1s infinite;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ const props = defineProps<{
|
|||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
|
|
@ -82,7 +82,6 @@ const props = defineProps<{
|
|||
> p {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
color: var(--urlPreviewInfo);
|
||||
font-size: 0.8em;
|
||||
line-height: 16px;
|
||||
vertical-align: top;
|
||||
|
|
@ -91,7 +90,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<template>
|
||||
<div ref="rootEl" :class="$style.root">
|
||||
<header :class="$style.header" class="_button" :style="{ background: bg }" @click="showBody = !showBody">
|
||||
<header :class="$style.header" class="_button" @click="showBody = !showBody">
|
||||
<div :class="$style.title"><div><slot name="header"></slot></div></div>
|
||||
<div :class="$style.divider"></div>
|
||||
<button class="_button" :class="$style.button">
|
||||
|
|
@ -32,21 +32,23 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, shallowRef, watch } from 'vue';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { miLocalStorage } from '@/local-storage.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
import { getBgColor } from '@/scripts/get-bg-color.js';
|
||||
|
||||
const miLocalStoragePrefix = 'ui:folder:' as const;
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
expanded?: boolean;
|
||||
persistKey?: string;
|
||||
persistKey?: string | null;
|
||||
}>(), {
|
||||
expanded: true,
|
||||
persistKey: null,
|
||||
});
|
||||
|
||||
const rootEl = shallowRef<HTMLDivElement>();
|
||||
const bg = ref<string>();
|
||||
const rootEl = shallowRef<HTMLElement>();
|
||||
const parentBg = ref<string | null>(null);
|
||||
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
|
||||
const showBody = ref((props.persistKey && miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`)) ? (miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`) === 't') : props.expanded);
|
||||
|
||||
watch(showBody, () => {
|
||||
|
|
@ -55,47 +57,34 @@ watch(showBody, () => {
|
|||
}
|
||||
});
|
||||
|
||||
function enter(element: Element) {
|
||||
const el = element as HTMLElement;
|
||||
function enter(el: Element) {
|
||||
if (!(el instanceof HTMLElement)) return;
|
||||
const elementHeight = el.getBoundingClientRect().height;
|
||||
el.style.height = '0';
|
||||
el.offsetHeight; // reflow
|
||||
el.style.height = elementHeight + 'px';
|
||||
el.style.height = `${elementHeight}px`;
|
||||
}
|
||||
|
||||
function afterEnter(element: Element) {
|
||||
const el = element as HTMLElement;
|
||||
el.style.height = 'unset';
|
||||
function afterEnter(el: Element) {
|
||||
if (!(el instanceof HTMLElement)) return;
|
||||
el.style.height = '';
|
||||
}
|
||||
|
||||
function leave(element: Element) {
|
||||
const el = element as HTMLElement;
|
||||
function leave(el: Element) {
|
||||
if (!(el instanceof HTMLElement)) return;
|
||||
const elementHeight = el.getBoundingClientRect().height;
|
||||
el.style.height = elementHeight + 'px';
|
||||
el.style.height = `${elementHeight}px`;
|
||||
el.offsetHeight; // reflow
|
||||
el.style.height = '0';
|
||||
}
|
||||
|
||||
function afterLeave(element: Element) {
|
||||
const el = element as HTMLElement;
|
||||
el.style.height = 'unset';
|
||||
function afterLeave(el: Element) {
|
||||
if (!(el instanceof HTMLElement)) return;
|
||||
el.style.height = '';
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
function getParentBg(el?: HTMLElement | null): string {
|
||||
if (el == null || el.tagName === 'BODY') return 'var(--bg)';
|
||||
const background = el.style.background || el.style.backgroundColor;
|
||||
if (background) {
|
||||
return background;
|
||||
} else {
|
||||
return getParentBg(el.parentElement);
|
||||
}
|
||||
}
|
||||
|
||||
const rawBg = getParentBg(rootEl.value);
|
||||
const _bg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
|
||||
_bg.setAlpha(0.85);
|
||||
bg.value = _bg.toRgbString();
|
||||
parentBg.value = getBgColor(rootEl.value?.parentElement);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -118,9 +107,10 @@ 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));
|
||||
background-color: color(from v-bind("parentBg ?? 'var(--MI_THEME-bg)'") srgb r g b / 0.85);
|
||||
}
|
||||
|
||||
.title {
|
||||
|
|
@ -134,7 +124,7 @@ onMounted(() => {
|
|||
flex: 1;
|
||||
margin: auto;
|
||||
height: 1px;
|
||||
background: var(--divider);
|
||||
background: var(--MI_THEME-divider);
|
||||
}
|
||||
|
||||
.button {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -53,51 +56,49 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted, shallowRef, ref } from 'vue';
|
||||
import { nextTick, onMounted, ref, shallowRef } from 'vue';
|
||||
import { defaultStore } from '@/store.js';
|
||||
import { getBgColor } from '@/scripts/get-bg-color.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
defaultOpen?: boolean;
|
||||
maxHeight?: number | null;
|
||||
withSpacer?: boolean;
|
||||
}>(), {
|
||||
defaultOpen: false,
|
||||
maxHeight: null,
|
||||
withSpacer: true,
|
||||
});
|
||||
|
||||
const getBgColor = (el: HTMLElement) => {
|
||||
const style = window.getComputedStyle(el);
|
||||
if (style.backgroundColor && !['rgba(0, 0, 0, 0)', 'rgba(0,0,0,0)', 'transparent'].includes(style.backgroundColor)) {
|
||||
return style.backgroundColor;
|
||||
} else {
|
||||
return el.parentElement ? getBgColor(el.parentElement) : 'transparent';
|
||||
}
|
||||
};
|
||||
|
||||
const rootEl = shallowRef<HTMLElement>();
|
||||
const bgSame = ref(false);
|
||||
const opened = ref(props.defaultOpen);
|
||||
const openedAtLeastOnce = ref(props.defaultOpen);
|
||||
|
||||
function enter(el) {
|
||||
function enter(el: Element) {
|
||||
if (!(el instanceof HTMLElement)) return;
|
||||
const elementHeight = el.getBoundingClientRect().height;
|
||||
el.style.height = 0;
|
||||
el.style.height = '0';
|
||||
el.offsetHeight; // reflow
|
||||
el.style.height = Math.min(elementHeight, props.maxHeight ?? Infinity) + 'px';
|
||||
el.style.height = `${Math.min(elementHeight, props.maxHeight ?? Infinity)}px`;
|
||||
}
|
||||
|
||||
function afterEnter(el) {
|
||||
el.style.height = null;
|
||||
function afterEnter(el: Element) {
|
||||
if (!(el instanceof HTMLElement)) return;
|
||||
el.style.height = '';
|
||||
}
|
||||
|
||||
function leave(el) {
|
||||
function leave(el: Element) {
|
||||
if (!(el instanceof HTMLElement)) return;
|
||||
const elementHeight = el.getBoundingClientRect().height;
|
||||
el.style.height = elementHeight + 'px';
|
||||
el.style.height = `${elementHeight}px`;
|
||||
el.offsetHeight; // reflow
|
||||
el.style.height = 0;
|
||||
el.style.height = '0';
|
||||
}
|
||||
|
||||
function afterLeave(el) {
|
||||
el.style.height = null;
|
||||
function afterLeave(el: Element) {
|
||||
if (!(el instanceof HTMLElement)) return;
|
||||
el.style.height = '';
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
|
|
@ -112,8 +113,8 @@ function toggle() {
|
|||
|
||||
onMounted(() => {
|
||||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
const parentBg = getBgColor(rootEl.value!.parentElement!);
|
||||
const myBg = computedStyle.getPropertyValue('--panel');
|
||||
const parentBg = getBgColor(rootEl.value?.parentElement) ?? 'transparent';
|
||||
const myBg = computedStyle.getPropertyValue('--MI_THEME-panel');
|
||||
bgSame.value = parentBg === myBg;
|
||||
});
|
||||
</script>
|
||||
|
|
@ -139,15 +140,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));
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--MI_THEME-folderHeaderBg);
|
||||
-webkit-backdrop-filter: var(--MI-blur, blur(15px));
|
||||
backdrop-filter: var(--MI-blur, blur(15px));
|
||||
border-radius: var(--MI-radius-sm);
|
||||
transition: border-radius 0.3s;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
background: var(--folderHeaderHoverBg);
|
||||
background: var(--MI_THEME-folderHeaderHoverBg);
|
||||
}
|
||||
|
||||
&:focus-within {
|
||||
|
|
@ -155,12 +156,12 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
&.active {
|
||||
color: var(--accent);
|
||||
background: var(--folderHeaderHoverBg);
|
||||
color: var(--MI_THEME-accent);
|
||||
background: var(--MI_THEME-folderHeaderHoverBg);
|
||||
}
|
||||
|
||||
&.opened {
|
||||
border-radius: var(--radius-sm) var(--radius-sm) 0 0;
|
||||
border-radius: var(--MI-radius-sm) var(--MI-radius-sm) 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,7 +171,7 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
.headerLower {
|
||||
color: var(--fgTransparentWeak);
|
||||
color: var(--MI_THEME-fgTransparentWeak);
|
||||
font-size: .85em;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
|
@ -204,13 +205,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 +220,26 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
.body {
|
||||
background: var(--panel);
|
||||
border-radius: 0 0 var(--radius-sm) var(--radius-sm);
|
||||
background: var(--MI_THEME-panel);
|
||||
border-radius: 0 0 var(--MI-radius-sm) var(--MI-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>
|
||||
|
|
|
|||
|
|
@ -37,13 +37,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { host } from '@@/js/config.js';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||
import { useStream } from '@/stream.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { claimAchievement } from '@/scripts/achievements.js';
|
||||
import { pleaseLogin } from '@/scripts/please-login.js';
|
||||
import { host } from '@@/js/config.js';
|
||||
import { $i } from '@/account.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ function onFollowChange(user: Misskey.entities.UserDetailed) {
|
|||
}
|
||||
|
||||
async function onClick() {
|
||||
pleaseLogin(undefined, { type: 'web', path: `/@${props.user.username}@${props.user.host ?? host}` });
|
||||
pleaseLogin({ openOnRemote: { type: 'web', path: `/@${props.user.username}@${props.user.host ?? host}` } });
|
||||
|
||||
wait.value = true;
|
||||
|
||||
|
|
@ -91,7 +91,10 @@ async function onClick() {
|
|||
text: i18n.tsx.unfollowConfirm({ name: props.user.name || props.user.username }),
|
||||
});
|
||||
|
||||
if (canceled) return;
|
||||
if (canceled) {
|
||||
wait.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
await misskeyApi('following/delete', {
|
||||
userId: props.user.id,
|
||||
|
|
@ -125,7 +128,10 @@ async function onClick() {
|
|||
});
|
||||
hasPendingFollowRequestFromYou.value = true;
|
||||
|
||||
if ($i == null) return;
|
||||
if ($i == null) {
|
||||
wait.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
claimAchievement('following1');
|
||||
|
||||
|
|
@ -165,12 +171,12 @@ 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;
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--MI-radius-xl);
|
||||
background: #fff;
|
||||
|
||||
&.full {
|
||||
|
|
@ -201,17 +207,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,6 @@ function selectButton(ev: MouseEvent) {
|
|||
<style module>
|
||||
.fileNotSelected {
|
||||
font-weight: 700;
|
||||
color: var(--infoWarnFg);
|
||||
color: var(--MI_THEME-infoWarnFg);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
@click="cancel()"
|
||||
@ok="ok()"
|
||||
@close="cancel()"
|
||||
@closed="$emit('closed')"
|
||||
@closed="emit('closed')"
|
||||
>
|
||||
<template #header>
|
||||
{{ title }}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ const props = defineProps<{
|
|||
}
|
||||
|
||||
.text {
|
||||
color: var(--warn);
|
||||
color: var(--MI_THEME-warn);
|
||||
font-size: 90%;
|
||||
animation: modified-blink 2s infinite;
|
||||
}
|
||||
|
|
|
|||
100
packages/frontend/src/components/MkFukidashi.vue
Normal file
100
packages/frontend/src/components/MkFukidashi.vue
Normal file
|
|
@ -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>
|
||||
|
|
@ -75,7 +75,7 @@ function leaveHover(): void {
|
|||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
|
||||
> .thumbnail {
|
||||
transform: scale(1.1);
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ const search = () => {
|
|||
width: 100%;
|
||||
height: 40px;
|
||||
font-size: 16px;
|
||||
border: solid 1px var(--divider);
|
||||
border-radius: var(--radius-xs) 0 0 var(--radius-xs);
|
||||
border: solid 1px var(--MI_THEME-divider);
|
||||
border-radius: var(--MI-radius-xs) 0 0 var(--MI-radius-xs);
|
||||
-webkit-appearance: textfield;
|
||||
}
|
||||
|
||||
|
|
@ -50,9 +50,9 @@ 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;
|
||||
border-radius: 0 var(--MI-radius-xs) var(--MI-radius-xs) 0;
|
||||
|
||||
&:active {
|
||||
box-shadow: 0 2px 4px rgba(#000, 0.15) inset;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue';
|
||||
import { onMounted, onUnmounted, nextTick, ref, shallowRef, watch, computed, toRefs, InputHTMLAttributes } from 'vue';
|
||||
import { debounce } from 'throttle-debounce';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import { useInterval } from '@@/js/use-interval.js';
|
||||
|
|
@ -53,7 +53,7 @@ import { Autocomplete, SuggestionType } from '@/scripts/autocomplete.js';
|
|||
|
||||
const props = defineProps<{
|
||||
modelValue: string | number | null;
|
||||
type?: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local';
|
||||
type?: InputHTMLAttributes['type'];
|
||||
required?: boolean;
|
||||
readonly?: boolean;
|
||||
disabled?: boolean;
|
||||
|
|
@ -64,8 +64,8 @@ const props = defineProps<{
|
|||
mfmAutocomplete?: boolean | SuggestionType[],
|
||||
autocapitalize?: string;
|
||||
spellcheck?: boolean;
|
||||
inputmode?: 'none' | 'text' | 'search' | 'email' | 'url' | 'numeric' | 'tel' | 'decimal';
|
||||
step?: any;
|
||||
inputmode?: InputHTMLAttributes['inputmode'];
|
||||
step?: InputHTMLAttributes['step'];
|
||||
datalist?: string[];
|
||||
min?: number;
|
||||
max?: number | string;
|
||||
|
|
@ -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,17 +242,17 @@ defineExpose({
|
|||
font: inherit;
|
||||
font-weight: normal;
|
||||
font-size: 1em;
|
||||
color: var(--fg);
|
||||
background: var(--panel);
|
||||
border: solid 1px var(--panel);
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--MI_THEME-fg);
|
||||
background: var(--MI_THEME-panel);
|
||||
border: solid 1px var(--MI_THEME-panel);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
box-sizing: border-box;
|
||||
transition: border-color 0.1s ease-out;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--inputBorderHover) !important;
|
||||
border-color: var(--MI_THEME-inputBorderHover) !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,15 +46,15 @@ function getInstanceIcon(instance): string {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
background: var(--panel);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--MI_THEME-panel);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
|
||||
> :global(.icon) {
|
||||
display: block;
|
||||
width: ($bodyTitleHieght + $bodyInfoHieght);
|
||||
height: ($bodyTitleHieght + $bodyInfoHieght);
|
||||
object-fit: cover;
|
||||
border-radius: var(--radius-xs);
|
||||
border-radius: var(--MI-radius-xs);
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ $height: 2ex;
|
|||
display: flex;
|
||||
align-items: center;
|
||||
height: $height;
|
||||
border-radius: var(--radius-xs) 0 0 var(--radius-xs);
|
||||
border-radius: var(--MI-radius-xs) 0 0 var(--MI-radius-xs);
|
||||
overflow: clip;
|
||||
color: #fff;
|
||||
text-shadow: /* .866 ≈ sin(60deg) */
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -77,12 +77,12 @@ function close() {
|
|||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
text-align: left;
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--MI-radius-md);
|
||||
|
||||
&.asDrawer {
|
||||
width: 100%;
|
||||
padding: 16px 16px max(env(safe-area-inset-bottom, 0px), 16px) 16px;
|
||||
border-radius: var(--radius-lg);
|
||||
border-radius: var(--MI-radius-lg);
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
text-align: center;
|
||||
|
|
@ -100,13 +100,13 @@ function close() {
|
|||
justify-content: center;
|
||||
vertical-align: bottom;
|
||||
height: 100px;
|
||||
border-radius: var(--radius);
|
||||
border-radius: var(--MI-radius);
|
||||
padding: 10px;
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ async function show() {
|
|||
<style lang="scss" module>
|
||||
.root {
|
||||
width: 100%;
|
||||
border-radius: var(--radius-xs);
|
||||
border-radius: var(--MI-radius-xs);
|
||||
margin-top: 4px;
|
||||
overflow: clip;
|
||||
}
|
||||
|
|
@ -68,7 +68,6 @@ async function show() {
|
|||
}
|
||||
|
||||
.download {
|
||||
background: var(--noteAttachedFile);
|
||||
}
|
||||
|
||||
.sensitive {
|
||||
|
|
@ -77,7 +76,7 @@ async function show() {
|
|||
}
|
||||
|
||||
.audio {
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
overflow: clip;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -186,9 +186,9 @@ function showMenu(ev: MouseEvent) {
|
|||
.hide {
|
||||
display: block;
|
||||
position: absolute;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
background-color: black;
|
||||
color: var(--accentLighten);
|
||||
color: var(--MI_THEME-accentLighten);
|
||||
font-size: 12px;
|
||||
opacity: .5;
|
||||
padding: 5px 8px;
|
||||
|
|
@ -207,28 +207,28 @@ 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 {
|
||||
display: block;
|
||||
position: absolute;
|
||||
border-radius: var(--radius-ellipse);
|
||||
border-radius: var(--MI-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);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
color: var(--MI_THEME-accentLighten);
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
font-size: 0.8em;
|
||||
|
|
|
|||
|
|
@ -339,19 +339,19 @@ defineExpose({
|
|||
|
||||
.media {
|
||||
overflow: hidden; // clipにするとバグる
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
}
|
||||
|
||||
: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 {
|
||||
|
|
@ -369,14 +369,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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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]">
|
||||
|
|
@ -121,7 +121,7 @@ import { hms } from '@/filters/hms.js';
|
|||
import { defaultStore } from '@/store.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import * as os from '@/os.js';
|
||||
import { isFullscreenNotSupported } from '@/scripts/device-kind.js';
|
||||
import { exitFullscreen, requestFullscreen } from '@/scripts/fullscreen.js';
|
||||
import hasAudio from '@/scripts/media-has-audio.js';
|
||||
import MkMediaRange from '@/components/MkMediaRange.vue';
|
||||
import { $i, iAmModerator } from '@/account.js';
|
||||
|
|
@ -337,26 +337,21 @@ function togglePlayPause() {
|
|||
}
|
||||
|
||||
function toggleFullscreen() {
|
||||
if (isFullscreenNotSupported && videoEl.value) {
|
||||
if (isFullscreen.value) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
videoEl.value.webkitExitFullscreen();
|
||||
isFullscreen.value = false;
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
videoEl.value.webkitEnterFullscreen();
|
||||
isFullscreen.value = true;
|
||||
}
|
||||
} else if (playerEl.value) {
|
||||
if (isFullscreen.value) {
|
||||
document.exitFullscreen();
|
||||
isFullscreen.value = false;
|
||||
} else {
|
||||
playerEl.value.requestFullscreen({ navigationUI: 'hide' });
|
||||
isFullscreen.value = true;
|
||||
}
|
||||
if (playerEl.value == null || videoEl.value == null) return;
|
||||
if (isFullscreen.value) {
|
||||
exitFullscreen({
|
||||
videoEl: videoEl.value,
|
||||
});
|
||||
isFullscreen.value = false;
|
||||
} else {
|
||||
requestFullscreen({
|
||||
videoEl: videoEl.value,
|
||||
playerEl: playerEl.value,
|
||||
options: {
|
||||
navigationUI: 'hide',
|
||||
},
|
||||
});
|
||||
isFullscreen.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -457,8 +452,10 @@ watch(loop, (to) => {
|
|||
});
|
||||
|
||||
watch(hide, (to) => {
|
||||
if (to && isFullscreen.value) {
|
||||
document.exitFullscreen();
|
||||
if (videoEl.value && to && isFullscreen.value) {
|
||||
exitFullscreen({
|
||||
videoEl: videoEl.value,
|
||||
});
|
||||
isFullscreen.value = false;
|
||||
}
|
||||
});
|
||||
|
|
@ -511,7 +508,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 +523,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;
|
||||
|
|
@ -539,9 +536,9 @@ onDeactivated(() => {
|
|||
.hide {
|
||||
display: block;
|
||||
position: absolute;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
background-color: black;
|
||||
color: var(--accentLighten);
|
||||
color: var(--MI_THEME-accentLighten);
|
||||
font-size: 12px;
|
||||
opacity: .5;
|
||||
padding: 5px 8px;
|
||||
|
|
@ -595,7 +592,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 +658,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 {
|
||||
|
|
@ -728,10 +725,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: var(--radius-sm);
|
||||
color: var(--accentLighten);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
color: var(--MI_THEME-accentLighten);
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
font-size: 0.8em;
|
||||
|
|
|
|||
|
|
@ -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}`,
|
||||
);
|
||||
|
|
@ -51,12 +46,14 @@ const avatarUrl = computed(() => defaultStore.state.disableShowingAnimatedImages
|
|||
.root {
|
||||
display: inline-block;
|
||||
padding: 4px 8px 4px 4px;
|
||||
border-radius: var(--radius-ellipse);
|
||||
color: var(--mention);
|
||||
border-radius: var(--MI-radius-ellipse);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -66,7 +63,7 @@ const avatarUrl = computed(() => defaultStore.state.disableShowingAnimatedImages
|
|||
object-fit: cover;
|
||||
margin: 0 0.2em 0 0;
|
||||
vertical-align: bottom;
|
||||
border-radius: var(--radius-full);
|
||||
border-radius: var(--MI-radius-full);
|
||||
}
|
||||
|
||||
.host {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -452,7 +454,7 @@ onBeforeUnmount(() => {
|
|||
> .menu {
|
||||
padding: 12px 0 max(env(safe-area-inset-bottom, 0px), 12px) 0;
|
||||
width: 100%;
|
||||
border-radius: var(--radius-lg);
|
||||
border-radius: var(--MI-radius-lg);
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
|
||||
|
|
@ -462,7 +464,7 @@ onBeforeUnmount(() => {
|
|||
|
||||
&::before {
|
||||
width: calc(100% - 24px);
|
||||
border-radius: var(--radius);
|
||||
border-radius: var(--MI-radius);
|
||||
}
|
||||
|
||||
> .icon {
|
||||
|
|
@ -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: "";
|
||||
|
|
@ -518,14 +520,14 @@ onBeforeUnmount(() => {
|
|||
margin: auto;
|
||||
width: calc(100% - 16px);
|
||||
height: 100%;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@ import { onMounted, onUnmounted, shallowRef, ref } from 'vue';
|
|||
import MkModal from './MkModal.vue';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
withOkButton: boolean;
|
||||
withCloseButton: boolean;
|
||||
okButtonDisabled: boolean;
|
||||
width: number;
|
||||
height: number;
|
||||
withOkButton?: boolean;
|
||||
withCloseButton?: boolean;
|
||||
okButtonDisabled?: boolean;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}>(), {
|
||||
withOkButton: false,
|
||||
withCloseButton: true,
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
@ -132,7 +132,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
ref="renoteButton"
|
||||
:class="$style.footerButton"
|
||||
class="_button"
|
||||
:style="renoted ? 'color: var(--accent) !important;' : ''"
|
||||
:style="renoted ? 'color: var(--MI_THEME-accent) !important;' : ''"
|
||||
@click.stop
|
||||
@mousedown.prevent="renoted ? undoRenote(appearNote) : boostVisibility()"
|
||||
>
|
||||
|
|
@ -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';
|
||||
|
|
@ -215,6 +218,7 @@ import MkInstanceTicker from '@/components/MkInstanceTicker.vue';
|
|||
import MkButton from '@/components/MkButton.vue';
|
||||
import { pleaseLogin, type OpenOnRemoteOptions } from '@/scripts/please-login.js';
|
||||
import { checkWordMute } from '@/scripts/check-word-mute.js';
|
||||
import { notePage } from '@/filters/note.js';
|
||||
import { userPage } from '@/filters/user.js';
|
||||
import number from '@/filters/number.js';
|
||||
import * as os from '@/os.js';
|
||||
|
|
@ -233,13 +237,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';
|
||||
|
|
@ -264,6 +265,7 @@ const emit = defineEmits<{
|
|||
const router = useRouter();
|
||||
|
||||
const inTimeline = inject<boolean>('inTimeline', false);
|
||||
const tl_withSensitive = inject<Ref<boolean>>('tl_withSensitive', ref(true));
|
||||
const inChannel = inject('inChannel', null);
|
||||
const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null);
|
||||
|
||||
|
|
@ -343,15 +345,18 @@ function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string
|
|||
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: false): boolean | 'sensitiveMute';
|
||||
*/
|
||||
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly = false): boolean | 'sensitiveMute' {
|
||||
if (mutedWords == null) return false;
|
||||
|
||||
if (checkWordMute(noteToCheck, $i, mutedWords)) return true;
|
||||
if (noteToCheck.reply && checkWordMute(noteToCheck.reply, $i, mutedWords)) return true;
|
||||
if (noteToCheck.renote && checkWordMute(noteToCheck.renote, $i, mutedWords)) return true;
|
||||
if (mutedWords != null) {
|
||||
if (checkWordMute(noteToCheck, $i, mutedWords)) return true;
|
||||
if (noteToCheck.reply && checkWordMute(noteToCheck.reply, $i, mutedWords)) return true;
|
||||
if (noteToCheck.renote && checkWordMute(noteToCheck.renote, $i, mutedWords)) return true;
|
||||
}
|
||||
|
||||
if (checkOnly) return false;
|
||||
|
||||
if (inTimeline && !defaultStore.state.tl.filter.withSensitive && noteToCheck.files?.some((v) => v.isSensitive)) return 'sensitiveMute';
|
||||
if (inTimeline && tl_withSensitive.value === false && noteToCheck.files?.some((v) => v.isSensitive)) {
|
||||
return 'sensitiveMute';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -514,7 +519,7 @@ function boostVisibility() {
|
|||
}
|
||||
|
||||
function renote(visibility: Visibility, localOnly: boolean = false) {
|
||||
pleaseLogin(undefined, pleaseLoginContext.value);
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
showMovedDialog();
|
||||
|
||||
renoting = true;
|
||||
|
|
@ -564,7 +569,7 @@ function renote(visibility: Visibility, localOnly: boolean = false) {
|
|||
}
|
||||
|
||||
function quote() {
|
||||
pleaseLogin(undefined, pleaseLoginContext.value);
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
showMovedDialog();
|
||||
if (props.mock) {
|
||||
return;
|
||||
|
|
@ -625,7 +630,7 @@ function quote() {
|
|||
}
|
||||
|
||||
function reply(): void {
|
||||
pleaseLogin(undefined, pleaseLoginContext.value);
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
if (props.mock) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -638,7 +643,7 @@ function reply(): void {
|
|||
}
|
||||
|
||||
function like(): void {
|
||||
pleaseLogin(undefined, pleaseLoginContext.value);
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
showMovedDialog();
|
||||
sound.playMisskeySfx('reaction');
|
||||
if (props.mock) {
|
||||
|
|
@ -660,7 +665,7 @@ function like(): void {
|
|||
}
|
||||
|
||||
function react(viaKeyboard = false): void {
|
||||
pleaseLogin(undefined, pleaseLoginContext.value);
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
showMovedDialog();
|
||||
if (appearNote.value.reactionAcceptance === 'likeOnly') {
|
||||
sound.playMisskeySfx('reaction');
|
||||
|
|
@ -808,15 +813,24 @@ function showRenoteMenu(): void {
|
|||
};
|
||||
}
|
||||
|
||||
const renoteDetailsMenu: MenuItem = {
|
||||
type: 'link',
|
||||
text: i18n.ts.renoteDetails,
|
||||
icon: 'ti ti-info-circle',
|
||||
to: notePage(note.value),
|
||||
};
|
||||
|
||||
if (isMyRenote) {
|
||||
pleaseLogin(undefined, pleaseLoginContext.value);
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
os.popupMenu([
|
||||
renoteDetailsMenu,
|
||||
getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote),
|
||||
{ type: 'divider' },
|
||||
getUnrenote(),
|
||||
], renoteTime.value);
|
||||
} else {
|
||||
os.popupMenu([
|
||||
renoteDetailsMenu,
|
||||
getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote),
|
||||
{ type: 'divider' },
|
||||
getAbuseNoteMenu(note.value, i18n.ts.reportAbuseRenote),
|
||||
|
|
@ -877,14 +891,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 +907,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 +935,9 @@ function emitUpdReaction(emoji: string, delta: number) {
|
|||
right: 12px;
|
||||
padding: 0 4px;
|
||||
margin-bottom: 0 !important;
|
||||
background: var(--popup);
|
||||
border-radius: var(--radius-sm);
|
||||
box-shadow: 0px 4px 32px var(--shadow);
|
||||
background: var(--MI_THEME-popup);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
box-shadow: 0px 4px 32px var(--MI_THEME-shadow);
|
||||
}
|
||||
|
||||
.footerButton {
|
||||
|
|
@ -950,6 +956,11 @@ function emitUpdReaction(emoji: string, delta: number) {
|
|||
}
|
||||
}
|
||||
|
||||
.skipRender {
|
||||
content-visibility: auto;
|
||||
contain-intrinsic-size: 0 150px;
|
||||
}
|
||||
|
||||
.tip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -976,7 +987,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;
|
||||
|
|
@ -1070,7 +1081,7 @@ function emitUpdReaction(emoji: string, delta: number) {
|
|||
left: 8px;
|
||||
width: 5px;
|
||||
height: calc(100% - 16px);
|
||||
border-radius: var(--radius-ellipse);
|
||||
border-radius: var(--MI-radius-ellipse);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
|
@ -1078,10 +1089,10 @@ function emitUpdReaction(emoji: string, delta: number) {
|
|||
flex-shrink: 0;
|
||||
display: block !important;
|
||||
margin: 0 14px 0 0;
|
||||
width: var(--avatar);
|
||||
height: var(--avatar);
|
||||
width: var(--MI-avatar);
|
||||
height: var(--MI-avatar);
|
||||
position: sticky !important;
|
||||
top: calc(22px + var(--stickyTop, 0px));
|
||||
top: calc(22px + var(--MI-stickyTop, 0px));
|
||||
left: 0;
|
||||
}
|
||||
|
||||
|
|
@ -1101,15 +1112,15 @@ 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);
|
||||
border-radius: var(--MI-radius-ellipse);
|
||||
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
|
||||
}
|
||||
|
||||
|
|
@ -1127,19 +1138,19 @@ 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);
|
||||
border-radius: var(--MI-radius-ellipse);
|
||||
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
|
||||
}
|
||||
|
||||
|
|
@ -1149,13 +1160,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,8 +1189,8 @@ function emitUpdReaction(emoji: string, delta: number) {
|
|||
|
||||
.quoteNote {
|
||||
padding: 16px;
|
||||
border: dashed 1px var(--renote);
|
||||
border-radius: var(--radius-sm);
|
||||
border: dashed 1px var(--MI_THEME-renote);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
|
|
@ -1202,7 +1213,7 @@ function emitUpdReaction(emoji: string, delta: number) {
|
|||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--fgHighlighted);
|
||||
color: var(--MI_THEME-fgHighlighted);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1277,7 +1288,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<span v-if="appearNote.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="$style.noteHeaderUsername"><MkAcct :user="appearNote.user"/></div>
|
||||
<div :class="$style.noteHeaderUsernameAndBadgeRoles">
|
||||
<div :class="$style.noteHeaderUsername">
|
||||
<MkAcct :user="appearNote.user"/>
|
||||
</div>
|
||||
<div v-if="appearNote.user.badgeRoles" :class="$style.noteHeaderBadgeRoles">
|
||||
<img v-for="(role, i) in appearNote.user.badgeRoles" :key="i" v-tooltip="role.name" :class="$style.noteHeaderBadgeRole" :src="role.iconUrl!"/>
|
||||
</div>
|
||||
</div>
|
||||
<MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/>
|
||||
</div>
|
||||
</header>
|
||||
|
|
@ -135,7 +142,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
ref="renoteButton"
|
||||
class="_button"
|
||||
:class="$style.noteFooterButton"
|
||||
:style="renoted ? 'color: var(--accent) !important;' : ''"
|
||||
:style="renoted ? 'color: var(--MI_THEME-accent) !important;' : ''"
|
||||
@mousedown.prevent="renoted ? undoRenote() : boostVisibility()"
|
||||
>
|
||||
<i class="ti ti-repeat"></i>
|
||||
|
|
@ -157,8 +164,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>
|
||||
|
|
@ -236,6 +243,7 @@ import { computed, inject, onMounted, provide, ref, shallowRef, watch } from 'vu
|
|||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { isLink } from '@@/js/is-link.js';
|
||||
import { host } from '@@/js/config.js';
|
||||
import MkNoteSub from '@/components/MkNoteSub.vue';
|
||||
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
||||
import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
|
||||
|
|
@ -259,10 +267,8 @@ import { reactionPicker } from '@/scripts/reaction-picker.js';
|
|||
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
|
||||
import { $i } from '@/account.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { host } from '@@/js/config.js';
|
||||
import { getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu.js';
|
||||
import { getNoteVersionsMenu } from '@/scripts/get-note-versions-menu.js';
|
||||
|
||||
import { useNoteCapture } from '@/scripts/use-note-capture.js';
|
||||
import { deepClone } from '@/scripts/clone.js';
|
||||
import { useTooltip } from '@/scripts/use-tooltip.js';
|
||||
|
|
@ -507,7 +513,7 @@ if (appearNote.value.reactionAcceptance === 'likeOnly') {
|
|||
}
|
||||
|
||||
function renote(visibility: Visibility, localOnly: boolean = false) {
|
||||
pleaseLogin(undefined, pleaseLoginContext.value);
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
showMovedDialog();
|
||||
|
||||
renoting = true;
|
||||
|
|
@ -553,7 +559,7 @@ function renote(visibility: Visibility, localOnly: boolean = false) {
|
|||
}
|
||||
|
||||
function quote() {
|
||||
pleaseLogin(undefined, pleaseLoginContext.value);
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
showMovedDialog();
|
||||
|
||||
if (appearNote.value.channel) {
|
||||
|
|
@ -611,7 +617,7 @@ function quote() {
|
|||
}
|
||||
|
||||
function reply(): void {
|
||||
pleaseLogin(undefined, pleaseLoginContext.value);
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
showMovedDialog();
|
||||
os.post({
|
||||
reply: appearNote.value,
|
||||
|
|
@ -622,7 +628,7 @@ function reply(): void {
|
|||
}
|
||||
|
||||
function react(): void {
|
||||
pleaseLogin(undefined, pleaseLoginContext.value);
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
showMovedDialog();
|
||||
if (appearNote.value.reactionAcceptance === 'likeOnly') {
|
||||
sound.playMisskeySfx('reaction');
|
||||
|
|
@ -659,7 +665,7 @@ function react(): void {
|
|||
}
|
||||
|
||||
function like(): void {
|
||||
pleaseLogin(undefined, pleaseLoginContext.value);
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
showMovedDialog();
|
||||
sound.playMisskeySfx('reaction');
|
||||
misskeyApi('notes/like', {
|
||||
|
|
@ -741,7 +747,7 @@ async function clip(): Promise<void> {
|
|||
|
||||
function showRenoteMenu(): void {
|
||||
if (!isMyRenote) return;
|
||||
pleaseLogin(undefined, pleaseLoginContext.value);
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
os.popupMenu([{
|
||||
text: i18n.ts.unrenote,
|
||||
icon: 'ti ti-trash',
|
||||
|
|
@ -843,8 +849,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 +880,7 @@ function animatedMFM() {
|
|||
padding: 16px 32px 8px 32px;
|
||||
line-height: 28px;
|
||||
white-space: pre;
|
||||
color: var(--renote);
|
||||
color: var(--MI_THEME-renote);
|
||||
}
|
||||
|
||||
.renoteAvatar {
|
||||
|
|
@ -883,7 +889,7 @@ function animatedMFM() {
|
|||
width: 28px;
|
||||
height: 28px;
|
||||
margin: 0 8px 0 0;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
}
|
||||
|
||||
.renoteText {
|
||||
|
|
@ -932,8 +938,8 @@ function animatedMFM() {
|
|||
.noteHeaderAvatar {
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
width: var(--avatar);
|
||||
height: var(--avatar);
|
||||
width: var(--MI-avatar);
|
||||
height: var(--MI-avatar);
|
||||
}
|
||||
|
||||
.noteHeaderBody {
|
||||
|
|
@ -956,16 +962,21 @@ function animatedMFM() {
|
|||
padding: 4px 6px;
|
||||
font-size: 80%;
|
||||
line-height: 1;
|
||||
border: solid 0.5px var(--divider);
|
||||
border-radius: var(--radius-xs);
|
||||
border: solid 0.5px var(--MI_THEME-divider);
|
||||
border-radius: var(--MI-radius-xs);
|
||||
}
|
||||
|
||||
.noteHeaderInfo {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.noteHeaderUsernameAndBadgeRoles {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.noteHeaderUsername {
|
||||
margin-bottom: 2px;
|
||||
margin-right: 0.5em;
|
||||
line-height: 1.3;
|
||||
word-wrap: anywhere;
|
||||
}
|
||||
|
|
@ -974,6 +985,19 @@ function animatedMFM() {
|
|||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.noteHeaderBadgeRoles {
|
||||
margin: 0 .5em 0 0;
|
||||
}
|
||||
|
||||
.noteHeaderBadgeRole {
|
||||
height: 1.3em;
|
||||
vertical-align: -20%;
|
||||
|
||||
& + .noteHeaderBadgeRole {
|
||||
margin-left: 0.2em;
|
||||
}
|
||||
}
|
||||
|
||||
.noteContent {
|
||||
container-type: inline-size;
|
||||
overflow-wrap: break-word;
|
||||
|
|
@ -989,19 +1013,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,8 +1040,8 @@ function animatedMFM() {
|
|||
|
||||
.quoteNote {
|
||||
padding: 16px;
|
||||
border: dashed 1px var(--renote);
|
||||
border-radius: var(--radius-sm);
|
||||
border: dashed 1px var(--MI_THEME-renote);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
|
|
@ -1042,7 +1066,7 @@ function animatedMFM() {
|
|||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--fgHighlighted);
|
||||
color: var(--MI_THEME-fgHighlighted);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1052,17 +1076,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 +1098,7 @@ function animatedMFM() {
|
|||
}
|
||||
|
||||
.tabActive {
|
||||
border-bottom: solid 2px var(--accent);
|
||||
border-bottom: solid 2px var(--MI_THEME-accent);
|
||||
}
|
||||
|
||||
.tab_renotes {
|
||||
|
|
@ -1094,12 +1118,12 @@ function animatedMFM() {
|
|||
|
||||
.reactionTab {
|
||||
padding: 4px 6px;
|
||||
border: solid 1px var(--divider);
|
||||
border-radius: var(--radius-sm);
|
||||
border: solid 1px var(--MI_THEME-divider);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
}
|
||||
|
||||
.reactionTabActive {
|
||||
border-color: var(--accent);
|
||||
border-color: var(--MI_THEME-accent);
|
||||
}
|
||||
|
||||
@container (max-width: 500px) {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -98,8 +94,8 @@ const mock = inject<boolean>('mock', false);
|
|||
margin: 0 .5em 0 0;
|
||||
padding: 1px 6px;
|
||||
font-size: 80%;
|
||||
border: solid 0.5px var(--divider);
|
||||
border-radius: var(--radius-xs);
|
||||
border: solid 0.5px var(--MI_THEME-divider);
|
||||
border-radius: var(--MI-radius-xs);
|
||||
}
|
||||
|
||||
.username {
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ const props = defineProps<{
|
|||
margin: 0 10px 0 0 !important;
|
||||
width: 40px !important;
|
||||
height: 40px !important;
|
||||
border-radius: var(--radius-sm) !important;
|
||||
border-radius: var(--MI-radius-sm) !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -95,8 +95,8 @@ watch(() => props.expandAllCws, (expandAllCws) => {
|
|||
}
|
||||
|
||||
.button{
|
||||
margin-right: var(--margin);
|
||||
margin-bottom: var(--margin);
|
||||
margin-right: var(--MI-margin);
|
||||
margin-bottom: var(--MI-margin);
|
||||
}
|
||||
|
||||
.avatar {
|
||||
|
|
@ -105,9 +105,9 @@ watch(() => props.expandAllCws, (expandAllCws) => {
|
|||
margin: 0 10px 0 0;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
position: sticky !important;
|
||||
top: calc(16px + var(--stickyTop, 0px));
|
||||
top: calc(16px + var(--MI-stickyTop, 0px));
|
||||
left: 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
ref="renoteButton"
|
||||
class="_button"
|
||||
:class="$style.noteFooterButton"
|
||||
:style="renoted ? 'color: var(--accent) !important;' : ''"
|
||||
:style="renoted ? 'color: var(--MI_THEME-accent) !important;' : ''"
|
||||
@mousedown="renoted ? undoRenote() : boostVisibility()"
|
||||
>
|
||||
<i class="ph-rocket-launch ph-bold ph-lg"></i>
|
||||
|
|
@ -98,7 +98,8 @@ import { $i } from '@/account.js';
|
|||
import { userPage } from '@/filters/user.js';
|
||||
import { checkWordMute } from '@/scripts/check-word-mute.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
import { pleaseLogin } from '@/scripts/please-login.js';
|
||||
import { host } from '@@/js/config.js';
|
||||
import { pleaseLogin, type OpenOnRemoteOptions } from '@/scripts/please-login.js';
|
||||
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
|
||||
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
||||
import { reactionPicker } from '@/scripts/reaction-picker.js';
|
||||
|
|
@ -145,6 +146,11 @@ const isRenote = (
|
|||
props.note.poll == null
|
||||
);
|
||||
|
||||
const pleaseLoginContext = computed<OpenOnRemoteOptions>(() => ({
|
||||
type: 'lookup',
|
||||
url: `https://${host}/notes/${appearNote.value.id}`,
|
||||
}));
|
||||
|
||||
async function addReplyTo(replyNote: Misskey.entities.Note) {
|
||||
replies.value.unshift(replyNote);
|
||||
appearNote.value.repliesCount += 1;
|
||||
|
|
@ -182,7 +188,7 @@ function focus() {
|
|||
}
|
||||
|
||||
function reply(viaKeyboard = false): void {
|
||||
pleaseLogin();
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
showMovedDialog();
|
||||
os.post({
|
||||
reply: props.note,
|
||||
|
|
@ -194,7 +200,7 @@ function reply(viaKeyboard = false): void {
|
|||
}
|
||||
|
||||
function react(viaKeyboard = false): void {
|
||||
pleaseLogin();
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
showMovedDialog();
|
||||
sound.playMisskeySfx('reaction');
|
||||
if (props.note.reactionAcceptance === 'likeOnly') {
|
||||
|
|
@ -228,7 +234,7 @@ function react(viaKeyboard = false): void {
|
|||
}
|
||||
|
||||
function like(): void {
|
||||
pleaseLogin();
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
showMovedDialog();
|
||||
sound.playMisskeySfx('reaction');
|
||||
misskeyApi('notes/like', {
|
||||
|
|
@ -288,7 +294,7 @@ function boostVisibility() {
|
|||
}
|
||||
|
||||
function renote(visibility: Visibility, localOnly: boolean = false) {
|
||||
pleaseLogin();
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
showMovedDialog();
|
||||
|
||||
if (appearNote.value.channel) {
|
||||
|
|
@ -332,7 +338,7 @@ function renote(visibility: Visibility, localOnly: boolean = false) {
|
|||
}
|
||||
|
||||
function quote() {
|
||||
pleaseLogin();
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
showMovedDialog();
|
||||
|
||||
if (appearNote.value.channel) {
|
||||
|
|
@ -438,7 +444,7 @@ if (props.detail) {
|
|||
left: 8px;
|
||||
width: 5px;
|
||||
height: calc(100% - 8px);
|
||||
border-radius: var(--radius-ellipse);
|
||||
border-radius: var(--MI-radius-ellipse);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
|
@ -448,7 +454,7 @@ if (props.detail) {
|
|||
margin: 0 8px 0 0;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
}
|
||||
|
||||
.body {
|
||||
|
|
@ -475,7 +481,7 @@ if (props.detail) {
|
|||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--fgHighlighted);
|
||||
color: var(--MI_THEME-fgHighlighted);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -493,7 +499,7 @@ if (props.detail) {
|
|||
opacity: 0.7;
|
||||
|
||||
&.reacted {
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -510,7 +516,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,8 +537,8 @@ 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);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -61,20 +61,20 @@ defineExpose({
|
|||
<style lang="scss" module>
|
||||
.root {
|
||||
&.noGap {
|
||||
border-radius: var(--radius);
|
||||
border-radius: var(--MI-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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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', 'scheduledNotePosted'].includes(notification.type) && 'note' in notification" :class="$style.icon" :user="notification.note.user" link preview/>
|
||||
<MkAvatar v-else-if="['roleAssigned', 'achievementEarned', 'scheduledNoteFailed'].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', 'scheduledNoteFailed'].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',
|
||||
[$style.t_roleAssigned]: notification.type === 'scheduledNoteFailed',
|
||||
|
|
@ -43,6 +43,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>
|
||||
|
|
@ -66,6 +67,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>
|
||||
|
|
@ -243,13 +245,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;
|
||||
}
|
||||
|
||||
|
|
@ -277,7 +282,7 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification)
|
|||
width: 80%;
|
||||
height: 80%;
|
||||
font-size: 15px;
|
||||
border-radius: var(--radius-full);
|
||||
border-radius: var(--MI-radius-full);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
|
@ -294,7 +299,7 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification)
|
|||
}
|
||||
|
||||
.icon_app {
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
}
|
||||
|
||||
.subIcon {
|
||||
|
|
@ -305,9 +310,9 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification)
|
|||
width: 20px;
|
||||
height: 20px;
|
||||
box-sizing: border-box;
|
||||
border-radius: var(--radius-full);
|
||||
background: var(--panel);
|
||||
box-shadow: 0 0 0 3px var(--panel);
|
||||
border-radius: var(--MI-radius-full);
|
||||
background: var(--MI_THEME-panel);
|
||||
box-shadow: 0 0 0 3px var(--MI_THEME-panel);
|
||||
font-size: 11px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
|
|
@ -371,6 +376,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;
|
||||
|
|
@ -452,9 +463,9 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification)
|
|||
width: 20px;
|
||||
height: 20px;
|
||||
box-sizing: border-box;
|
||||
border-radius: var(--radius-full);
|
||||
background: var(--panel);
|
||||
box-shadow: 0 0 0 3px var(--panel);
|
||||
border-radius: var(--MI-radius-full);
|
||||
background: var(--MI_THEME-panel);
|
||||
box-shadow: 0 0 0 3px var(--MI_THEME-panel);
|
||||
font-size: 11px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ const props = withDefaults(defineProps<{
|
|||
|
||||
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||
|
||||
const typesMap: TypesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(!props.excludeTypes.includes(t)) }), {} as any);
|
||||
const typesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(!props.excludeTypes.includes(t)) }), {} as TypesMap);
|
||||
|
||||
function ok() {
|
||||
emit('done', {
|
||||
|
|
|
|||
|
|
@ -111,6 +111,6 @@ defineExpose({
|
|||
|
||||
<style lang="scss" module>
|
||||
.list {
|
||||
background: var(--panel);
|
||||
background: var(--MI_THEME-panel);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ import number from '@/filters/number.js';
|
|||
import XValue from '@/components/MkObjectView.value.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
value: any;
|
||||
value: unknown;
|
||||
}>();
|
||||
|
||||
const collapsed = reactive({});
|
||||
|
|
@ -50,19 +50,19 @@ if (isObject(props.value)) {
|
|||
}
|
||||
}
|
||||
|
||||
function isObject(v): boolean {
|
||||
function isObject(v: unknown): v is Record<PropertyKey, unknown> {
|
||||
return typeof v === 'object' && !Array.isArray(v) && v !== null;
|
||||
}
|
||||
|
||||
function isArray(v): boolean {
|
||||
function isArray(v: unknown): v is unknown[] {
|
||||
return Array.isArray(v);
|
||||
}
|
||||
|
||||
function isEmpty(v): boolean {
|
||||
function isEmpty(v: unknown): v is Record<PropertyKey, never> | never[] {
|
||||
return (isArray(v) && v.length === 0) || (isObject(v) && Object.keys(v).length === 0);
|
||||
}
|
||||
|
||||
function collapsable(v): boolean {
|
||||
function collapsable(v: unknown): boolean {
|
||||
return (isObject(v) || isArray(v)) && !isEmpty(v);
|
||||
}
|
||||
</script>
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ onUnmounted(() => {
|
|||
|
||||
<style lang="scss" module>
|
||||
.content {
|
||||
--stickyTop: 0px;
|
||||
--MI-stickyTop: 0px;
|
||||
|
||||
&.omitted {
|
||||
position: relative;
|
||||
|
|
@ -62,20 +62,20 @@ 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);
|
||||
border-radius: var(--MI-radius-ellipse);
|
||||
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
> .fadeLabel {
|
||||
background: var(--panelHighlight);
|
||||
background: var(--MI_THEME-panelHighlight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,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>
|
||||
|
|
@ -53,7 +53,7 @@ const props = defineProps<{
|
|||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
}
|
||||
|
||||
&:focus-within {
|
||||
|
|
@ -66,22 +66,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;
|
||||
|
|
@ -114,7 +114,6 @@ const props = defineProps<{
|
|||
> p {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
color: var(--urlPreviewInfo);
|
||||
font-size: 0.8em;
|
||||
line-height: 16px;
|
||||
vertical-align: top;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
:buttonsLeft="buttonsLeft"
|
||||
:buttonsRight="buttonsRight"
|
||||
:contextmenu="contextmenu"
|
||||
@closed="$emit('closed')"
|
||||
@closed="emit('closed')"
|
||||
>
|
||||
<template #header>
|
||||
<template v-if="pageMetadata">
|
||||
|
|
@ -30,17 +30,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, onUnmounted, provide, ref, shallowRef } from 'vue';
|
||||
import { url } from '@@/js/config.js';
|
||||
import { getScrollContainer } from '@@/js/scroll.js';
|
||||
import RouterView from '@/components/global/RouterView.vue';
|
||||
import MkWindow from '@/components/MkWindow.vue';
|
||||
import { popout as _popout } from '@/scripts/popout.js';
|
||||
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
|
||||
import { url } from '@@/js/config.js';
|
||||
import { useScrollPositionManager } from '@/nirax.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js';
|
||||
import { openingWindowsCount } from '@/os.js';
|
||||
import { claimAchievement } from '@/scripts/achievements.js';
|
||||
import { getScrollContainer } from '@@/js/scroll.js';
|
||||
import { useRouterFactory } from '@/router/supplier.js';
|
||||
import { mainRouter } from '@/router/main.js';
|
||||
import MkUserName from './global/MkUserName.vue';
|
||||
|
|
@ -49,7 +49,7 @@ const props = defineProps<{
|
|||
initialPath: string;
|
||||
}>();
|
||||
|
||||
defineEmits<{
|
||||
const emit = defineEmits<{
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ const windowRouter = routerFactory(props.initialPath);
|
|||
const contents = shallowRef<HTMLElement | null>(null);
|
||||
const pageMetadata = ref<null | PageMetadata>(null);
|
||||
const windowEl = shallowRef<InstanceType<typeof MkWindow>>();
|
||||
const history = ref<{ path: string; key: any; }[]>([{
|
||||
const history = ref<{ path: string; key: string; }[]>([{
|
||||
path: windowRouter.getCurrentPath(),
|
||||
key: windowRouter.getCurrentKey(),
|
||||
}]);
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<p v-if="!readOnly" :class="$style.info">
|
||||
<span>{{ i18n.tsx._poll.totalVotes({ n: total }) }}</span>
|
||||
<span v-if="poll.multiple"> · </span>
|
||||
<span v-if="poll.multiple" style="color: var(--accent); font-weight: bolder;">{{ i18n.ts._poll.multiple }}</span>
|
||||
<span v-if="poll.multiple" style="color: var(--MI_THEME-accent); font-weight: bolder;">{{ i18n.ts._poll.multiple }}</span>
|
||||
<span> · </span>
|
||||
<a v-if="!closed && !isVoted" style="color: inherit;" @click="showResult = !showResult">{{ showResult ? i18n.ts._poll.vote : i18n.ts._poll.showResult }}</a>
|
||||
<span v-if="isVoted">{{ i18n.ts._poll.voted }}</span>
|
||||
|
|
@ -33,14 +33,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { host } from '@@/js/config.js';
|
||||
import { useInterval } from '@@/js/use-interval.js';
|
||||
import type { OpenOnRemoteOptions } from '@/scripts/please-login.js';
|
||||
import { sum } from '@/scripts/array.js';
|
||||
import { pleaseLogin } from '@/scripts/please-login.js';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { host } from '@@/js/config.js';
|
||||
import { useInterval } from '@@/js/use-interval.js';
|
||||
import { $i } from '@/account.js';
|
||||
|
||||
const props = defineProps<{
|
||||
|
|
@ -91,7 +91,7 @@ if (props.poll.expiresAt) {
|
|||
const vote = async (id) => {
|
||||
if (props.readOnly || closed.value || isVoted.value) return;
|
||||
|
||||
pleaseLogin(undefined, pleaseLoginContext.value);
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
|
||||
if (!props.poll.multiple) {
|
||||
const { canceled } = await os.confirm({
|
||||
|
|
@ -115,7 +115,7 @@ const vote = async (id) => {
|
|||
};
|
||||
|
||||
const refreshVotes = async () => {
|
||||
pleaseLogin(undefined, pleaseLoginContext.value);
|
||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||
|
||||
if (props.readOnly || closed.value) return;
|
||||
await misskeyApi('notes/polls/refresh', {
|
||||
|
|
@ -139,9 +139,9 @@ const refreshVotes = async () => {
|
|||
position: relative;
|
||||
margin: 4px 0;
|
||||
padding: 4px;
|
||||
//border: solid 0.5px var(--divider);
|
||||
background: var(--accentedBg);
|
||||
border-radius: var(--radius-xs);
|
||||
//border: solid 0.5px var(--MI_THEME-divider);
|
||||
background: var(--MI_THEME-accentedBg);
|
||||
border-radius: var(--MI-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);
|
||||
border-radius: var(--radius-xs);
|
||||
background: var(--MI_THEME-panel);
|
||||
border-radius: var(--MI-radius-xs);
|
||||
}
|
||||
|
||||
.info {
|
||||
color: var(--fg);
|
||||
color: var(--MI_THEME-fg);
|
||||
}
|
||||
|
||||
.done {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ defineProps<{
|
|||
items: MenuItem[];
|
||||
align?: 'center' | string;
|
||||
width?: number;
|
||||
src?: any;
|
||||
src?: HTMLElement | null;
|
||||
returnFocusTo?: HTMLElement | null;
|
||||
}>();
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ function close() {
|
|||
|
||||
<style lang="scss" module>
|
||||
.drawer {
|
||||
border-radius: var(--radius-lg);
|
||||
border-radius: var(--MI-radius-lg);
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,17 +61,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkAcct :user="u"/>
|
||||
<button class="_button" style="padding: 4px 8px;" @click="removeVisibleUser(u)"><i class="ti ti-x"></i></button>
|
||||
</span>
|
||||
<button class="_buttonPrimary" style="padding: 4px; border-radius: var(--radius-sm);" @click="addVisibleUser"><i class="ti ti-plus ti-fw"></i></button>
|
||||
<button class="_buttonPrimary" style="padding: 4px; border-radius: var(--MI-radius-sm);" @click="addVisibleUser"><i class="ti ti-plus ti-fw"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<MkInfo v-if="hasNotSpecifiedMentions" warn :class="$style.hasNotSpecifiedMentions">{{ i18n.ts.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ i18n.ts.add }}</button></MkInfo>
|
||||
<div v-show="useCw" :class="$style.cwFrame">
|
||||
<input ref="cwInputEl" v-model="cw" :class="$style.cw" :placeholder="i18n.ts.annotation" @keydown="onKeydown">
|
||||
<input ref="cwInputEl" v-model="cw" :class="$style.cw" :placeholder="i18n.ts.annotation" @keydown="onKeydown" @keyup="onKeyup" @compositionend="onCompositionEnd">
|
||||
<div v-if="maxCwLength - cwLength < 100" :class="['_acrylic', $style.textCount, { [$style.textOver]: cwLength > maxCwLength }]">{{ maxCwLength - cwLength }}</div>
|
||||
</div>
|
||||
<div :class="[$style.textOuter, { [$style.withCw]: useCw }]">
|
||||
<div v-if="channel" :class="$style.colorBar" :style="{ background: channel.color }"></div>
|
||||
<textarea ref="textareaEl" v-model="text" :class="[$style.text]" :disabled="posting || posted" :readonly="textAreaReadOnly" :placeholder="placeholder" data-cy-post-form-text dir="auto" @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/>
|
||||
<textarea ref="textareaEl" v-model="text" :class="[$style.text]" :disabled="posting || posted" :readonly="textAreaReadOnly" :placeholder="placeholder" data-cy-post-form-text dir="auto" @keydown="onKeydown" @keyup="onKeyup" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/>
|
||||
<div v-if="maxTextLength - textLength < 100" :class="['_acrylic', $style.textCount, { [$style.textOver]: textLength > maxTextLength }]">{{ maxTextLength - textLength }}</div>
|
||||
</div>
|
||||
<input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" :placeholder="i18n.ts.hashtags" list="hashtags">
|
||||
|
|
@ -136,28 +136,14 @@ import { miLocalStorage } from '@/local-storage.js';
|
|||
import { claimAchievement } from '@/scripts/achievements.js';
|
||||
import { emojiPicker } from '@/scripts/emoji-picker.js';
|
||||
import { mfmFunctionPicker } from '@/scripts/mfm-function-picker.js';
|
||||
import type { PostFormProps } from '@/types/post-form.js';
|
||||
import MkScheduleEditor from '@/components/MkScheduleEditor.vue';
|
||||
|
||||
const $i = signinRequired();
|
||||
|
||||
const modal = inject('modal');
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
reply?: Misskey.entities.Note;
|
||||
renote?: Misskey.entities.Note;
|
||||
channel?: Misskey.entities.Channel; // TODO
|
||||
mention?: Misskey.entities.User;
|
||||
specified?: Misskey.entities.UserDetailed;
|
||||
initialText?: string;
|
||||
initialCw?: string;
|
||||
initialVisibility?: (typeof Misskey.noteVisibilities)[number];
|
||||
initialFiles?: Misskey.entities.DriveFile[];
|
||||
initialLocalOnly?: boolean;
|
||||
initialVisibleUsers?: Misskey.entities.UserDetailed[];
|
||||
initialNote?: Misskey.entities.Note & {
|
||||
isSchedule?: boolean,
|
||||
};
|
||||
instant?: boolean;
|
||||
const props = withDefaults(defineProps<PostFormProps & {
|
||||
fixed?: boolean;
|
||||
autofocus?: boolean;
|
||||
freezeAfterPosted?: boolean;
|
||||
|
|
@ -212,6 +198,7 @@ const recentHashtags = ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'
|
|||
const imeText = ref('');
|
||||
const showingOptions = ref(false);
|
||||
const textAreaReadOnly = ref(false);
|
||||
const justEndedComposition = ref(false);
|
||||
const scheduleNote = ref<{
|
||||
scheduledAt: number | null;
|
||||
} | null>(null);
|
||||
|
|
@ -602,7 +589,13 @@ function clear() {
|
|||
function onKeydown(ev: KeyboardEvent) {
|
||||
if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost.value) post();
|
||||
|
||||
if (ev.key === 'Escape') emit('esc');
|
||||
// justEndedComposition.value is for Safari, which keyDown occurs after compositionend.
|
||||
// ev.isComposing is for another browsers.
|
||||
if (ev.key === 'Escape' && !justEndedComposition.value && !ev.isComposing) emit('esc');
|
||||
}
|
||||
|
||||
function onKeyup(ev: KeyboardEvent) {
|
||||
justEndedComposition.value = false;
|
||||
}
|
||||
|
||||
function onCompositionUpdate(ev: CompositionEvent) {
|
||||
|
|
@ -611,6 +604,7 @@ function onCompositionUpdate(ev: CompositionEvent) {
|
|||
|
||||
function onCompositionEnd(ev: CompositionEvent) {
|
||||
imeText.value = '';
|
||||
justEndedComposition.value = true;
|
||||
}
|
||||
|
||||
async function onPaste(ev: ClipboardEvent) {
|
||||
|
|
@ -1015,8 +1009,8 @@ function showActions(ev: MouseEvent) {
|
|||
action.handler({
|
||||
text: text.value,
|
||||
cw: cw.value,
|
||||
}, (key, value: any) => {
|
||||
if (typeof key !== 'string') return;
|
||||
}, (key, value) => {
|
||||
if (typeof key !== 'string' || typeof value !== 'string') return;
|
||||
if (key === 'text') { text.value = value; }
|
||||
if (key === 'cw') { useCw.value = value !== null; cw.value = value; }
|
||||
});
|
||||
|
|
@ -1228,8 +1222,8 @@ defineExpose({
|
|||
&:focus-visible {
|
||||
outline: none;
|
||||
|
||||
.submitInner {
|
||||
outline: 2px solid var(--fgOnAccent);
|
||||
> .submitInner {
|
||||
outline: 2px solid var(--MI_THEME-fgOnAccent);
|
||||
outline-offset: -4px;
|
||||
}
|
||||
}
|
||||
|
|
@ -1243,14 +1237,14 @@ 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)));
|
||||
> .submitInner {
|
||||
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)));
|
||||
> .submitInner {
|
||||
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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1261,7 +1255,7 @@ defineExpose({
|
|||
left: 12px;
|
||||
width: 5px;
|
||||
height: 100% ;
|
||||
border-radius: var(--radius-ellipse);
|
||||
border-radius: var(--MI-radius-ellipse);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
|
@ -1269,20 +1263,20 @@ defineExpose({
|
|||
padding: 0 12px;
|
||||
line-height: 34px;
|
||||
font-weight: bold;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-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 {
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
|
||||
&:hover {
|
||||
background: var(--X5);
|
||||
background: var(--MI_THEME-X5);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
|
|
@ -1326,7 +1320,7 @@ defineExpose({
|
|||
|
||||
.withQuote {
|
||||
margin: 0 0 8px 0;
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
}
|
||||
|
||||
.toSpecified {
|
||||
|
|
@ -1345,8 +1339,8 @@ defineExpose({
|
|||
.visibleUser {
|
||||
margin-right: 14px;
|
||||
padding: 8px 0 8px 8px;
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--X4);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
background: var(--MI_THEME-X4);
|
||||
}
|
||||
|
||||
.hasNotSpecifiedMentions {
|
||||
|
|
@ -1365,7 +1359,7 @@ defineExpose({
|
|||
border: none;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
color: var(--MI_THEME-fg);
|
||||
font-family: inherit;
|
||||
|
||||
&:focus {
|
||||
|
|
@ -1380,7 +1374,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;
|
||||
|
|
@ -1390,7 +1384,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 {
|
||||
|
|
@ -1416,8 +1410,8 @@ defineExpose({
|
|||
right: 2px;
|
||||
padding: 4px 6px;
|
||||
font-size: .9em;
|
||||
color: var(--warn);
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--MI_THEME-warn);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
min-width: 1.6em;
|
||||
text-align: center;
|
||||
|
||||
|
|
@ -1457,19 +1451,19 @@ defineExpose({
|
|||
font-size: 1em;
|
||||
width: auto;
|
||||
height: 100%;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-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) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template>
|
||||
<div v-show="props.modelValue.length != 0" :class="$style.root">
|
||||
<Sortable :modelValue="props.modelValue" :class="$style.files" itemKey="id" :animation="150" :delay="100" :delayOnTouchOnly="true" @update:modelValue="v => emit('update:modelValue', v)">
|
||||
<template #item="{element}">
|
||||
<template #item="{ element }">
|
||||
<div
|
||||
:class="$style.file"
|
||||
role="button"
|
||||
|
|
@ -38,14 +38,14 @@ import type { MenuItem } from '@/types/menu.js';
|
|||
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: any[];
|
||||
modelValue: Misskey.entities.DriveFile[];
|
||||
detachMediaFn?: (id: string) => void;
|
||||
}>();
|
||||
|
||||
const mock = inject<boolean>('mock', false);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'update:modelValue', value: any[]): void;
|
||||
(ev: 'update:modelValue', value: Misskey.entities.DriveFile[]): void;
|
||||
(ev: 'detach', id: string): void;
|
||||
(ev: 'changeSensitive', file: Misskey.entities.DriveFile, isSensitive: boolean): void;
|
||||
(ev: 'changeName', file: Misskey.entities.DriveFile, newName: string): void;
|
||||
|
|
@ -113,7 +113,7 @@ async function rename(file) {
|
|||
});
|
||||
}
|
||||
|
||||
async function describe(file) {
|
||||
async function describe(file: Misskey.entities.DriveFile) {
|
||||
if (mock) return;
|
||||
|
||||
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), {
|
||||
|
|
@ -203,7 +203,7 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent | Keyboar
|
|||
width: 64px;
|
||||
height: 64px;
|
||||
margin-right: 4px;
|
||||
border-radius: var(--radius-xs);
|
||||
border-radius: var(--MI-radius-xs);
|
||||
overflow: hidden;
|
||||
cursor: move;
|
||||
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -11,23 +11,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { shallowRef } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkModal from '@/components/MkModal.vue';
|
||||
import MkPostForm from '@/components/MkPostForm.vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import type { PostFormProps } from '@/types/post-form.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
reply?: Misskey.entities.Note;
|
||||
renote?: Misskey.entities.Note;
|
||||
channel?: any; // TODO
|
||||
mention?: Misskey.entities.User;
|
||||
specified?: Misskey.entities.UserDetailed;
|
||||
initialText?: string;
|
||||
initialCw?: string;
|
||||
initialVisibility?: (typeof Misskey.noteVisibilities)[number];
|
||||
initialFiles?: Misskey.entities.DriveFile[];
|
||||
initialLocalOnly?: boolean;
|
||||
initialVisibleUsers?: Misskey.entities.UserDetailed[];
|
||||
initialNote?: Misskey.entities.Note;
|
||||
const props = withDefaults(defineProps<PostFormProps & {
|
||||
instant?: boolean;
|
||||
fixed?: boolean;
|
||||
autofocus?: boolean;
|
||||
|
|
|
|||
|
|
@ -24,17 +24,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
<script lang="ts" setup generic="T extends unknown">
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: any;
|
||||
value: any;
|
||||
modelValue: T;
|
||||
value: T;
|
||||
disabled?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'update:modelValue', value: any): void;
|
||||
(ev: 'update:modelValue', value: T): void;
|
||||
}>();
|
||||
|
||||
const checked = computed(() => props.modelValue === props.value);
|
||||
|
|
@ -53,10 +53,10 @@ 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-radius: var(--radius-sm);
|
||||
border: solid 1px var(--MI_THEME-panel);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
font-size: 90%;
|
||||
transition: all 0.2s;
|
||||
user-select: none;
|
||||
|
|
@ -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,8 +106,8 @@ function toggle(): void {
|
|||
width: 14px;
|
||||
height: 14px;
|
||||
background: none;
|
||||
border: solid 2px var(--inputBorder);
|
||||
border-radius: var(--radius-full);
|
||||
border: solid 2px var(--MI_THEME-inputBorder);
|
||||
border-radius: var(--MI-radius-full);
|
||||
transition: inherit;
|
||||
|
||||
&::after {
|
||||
|
|
@ -118,7 +118,7 @@ function toggle(): void {
|
|||
right: 3px;
|
||||
bottom: 3px;
|
||||
left: 3px;
|
||||
border-radius: var(--radius-full);
|
||||
border-radius: var(--MI-radius-full);
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
transition: 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,9 +224,9 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
|
|||
|
||||
> .body {
|
||||
padding: 7px 12px;
|
||||
background: var(--panel);
|
||||
border: solid 1px var(--panel);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--MI_THEME-panel);
|
||||
border: solid 1px var(--MI_THEME-panel);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
|
||||
> .container {
|
||||
position: relative;
|
||||
|
|
@ -242,7 +242,7 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
|
|||
width: calc(100% - #{$thumbWidth});
|
||||
height: 3px;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: var(--radius-ellipse);
|
||||
border-radius: var(--MI-radius-ellipse);
|
||||
overflow: clip;
|
||||
|
||||
> .highlight {
|
||||
|
|
@ -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,8 +272,8 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
|
|||
width: $tickWidth;
|
||||
height: 3px;
|
||||
margin-left: - math.div($tickWidth, 2);
|
||||
background: var(--divider);
|
||||
border-radius: var(--radius-ellipse);
|
||||
background: var(--MI_THEME-divider);
|
||||
border-radius: var(--MI-radius-ellipse);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -282,11 +282,11 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
|
|||
width: $thumbWidth;
|
||||
height: $thumbHeight;
|
||||
cursor: grab;
|
||||
background: var(--accent);
|
||||
border-radius: var(--radius-ellipse);
|
||||
background: var(--MI_THEME-accent);
|
||||
border-radius: var(--MI-radius-ellipse);
|
||||
|
||||
&:hover {
|
||||
background: var(--accentLighten);
|
||||
background: var(--MI_THEME-accentLighten);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { getEmojiName } from '@@/js/emojilist.js';
|
||||
import MkTooltip from './MkTooltip.vue';
|
||||
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
||||
|
|
@ -30,7 +31,7 @@ import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
|||
defineProps<{
|
||||
showing: boolean;
|
||||
reaction: string;
|
||||
users: any[]; // TODO
|
||||
users: Misskey.entities.UserLite[];
|
||||
count: number;
|
||||
targetElement: HTMLElement;
|
||||
}>();
|
||||
|
|
@ -57,7 +58,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 {
|
||||
|
|
|
|||
|
|
@ -175,12 +175,12 @@ if (!mock) {
|
|||
margin: 2px;
|
||||
padding: 0 6px;
|
||||
font-size: 1.5em;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&.canToggle {
|
||||
background: var(--buttonBg);
|
||||
background: var(--MI_THEME-buttonBg);
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
|
|
@ -194,7 +194,7 @@ if (!mock) {
|
|||
&.small {
|
||||
height: 32px;
|
||||
font-size: 1em;
|
||||
border-radius: var(--radius-xs);
|
||||
border-radius: var(--MI-radius-xs);
|
||||
|
||||
> .count {
|
||||
font-size: 0.9em;
|
||||
|
|
@ -205,7 +205,7 @@ if (!mock) {
|
|||
&.large {
|
||||
height: 52px;
|
||||
font-size: 2em;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--MI-radius-sm);
|
||||
|
||||
> .count {
|
||||
font-size: 0.6em;
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue