merge upstream
This commit is contained in:
commit
d8908ef2d8
1065 changed files with 32953 additions and 20092 deletions
|
|
@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</div>
|
||||
<div :class="$style.headerRight">
|
||||
<template v-if="!(channel != null && fixed)">
|
||||
<button v-if="channel == null" ref="visibilityButton" v-click-anime v-tooltip="i18n.ts.visibility" :class="['_button', $style.headerRightItem, $style.visibility]" :disabled="editId != null" @click="setVisibility">
|
||||
<button v-if="channel == null" ref="visibilityButton" v-tooltip="i18n.ts.visibility" :class="['_button', $style.headerRightItem, $style.visibility]" :disabled="editId != null" @click="setVisibility">
|
||||
<span v-if="visibility === 'public'"><i class="ti ti-world"></i></span>
|
||||
<span v-if="visibility === 'home'"><i class="ti ti-home"></i></span>
|
||||
<span v-if="visibility === 'followers'"><i class="ti ti-lock"></i></span>
|
||||
|
|
@ -32,11 +32,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<span :class="$style.headerRightButtonText">{{ channel.name }}</span>
|
||||
</button>
|
||||
</template>
|
||||
<button v-click-anime v-tooltip="i18n.ts._visibility.disableFederation" class="_button" :class="[$style.headerRightItem, { [$style.danger]: localOnly }]" :disabled="channel != null || visibility === 'specified' || editId != null" @click="toggleLocalOnly">
|
||||
<button v-tooltip="i18n.ts._visibility.disableFederation" class="_button" :class="[$style.headerRightItem, { [$style.danger]: localOnly }]" :disabled="channel != null || visibility === 'specified' || editId != null" @click="toggleLocalOnly">
|
||||
<span v-if="!localOnly"><i class="ti ti-rocket"></i></span>
|
||||
<span v-else><i class="ti ti-rocket-off"></i></span>
|
||||
</button>
|
||||
<button v-click-anime v-tooltip="i18n.ts.reactionAcceptance" class="_button" :class="[$style.headerRightItem, { [$style.danger]: reactionAcceptance === 'likeOnly' }]" @click="toggleReactionAcceptance">
|
||||
<button ref="otherSettingsButton" v-tooltip="i18n.ts.other" class="_button" :class="$style.headerRightItem" @click="showOtherSettings"><i class="ti ti-dots"></i></button>
|
||||
<button v-tooltip="i18n.ts.reactionAcceptance" class="_button" :class="[$style.headerRightItem, { [$style.danger]: reactionAcceptance === 'likeOnly' }]" @click="toggleReactionAcceptance">
|
||||
<span v-if="reactionAcceptance === 'likeOnly'"><i class="ti ti-heart"></i></span>
|
||||
<span v-else-if="reactionAcceptance === 'likeOnlyForRemote'"><i class="ti ti-heart-plus"></i></span>
|
||||
<span v-else><i class="ph-smiley ph-bold ph-lg"></i></span>
|
||||
|
|
@ -65,9 +66,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</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">
|
||||
<div v-show="useCw" :class="$style.cwOuter">
|
||||
<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 v-if="maxCwTextLength - cwTextLength < 20" :class="['_acrylic', $style.cwTextCount, { [$style.cwTextOver]: cwTextLength > maxCwTextLength }]">{{ maxCwTextLength - cwTextLength }}</div>
|
||||
</div>
|
||||
<div :class="[$style.textOuter, { [$style.withCw]: useCw }]">
|
||||
<div v-if="channel" :class="$style.colorBar" :style="{ background: channel.color }"></div>
|
||||
|
|
@ -106,41 +107,48 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed, toRaw, type ShallowRef } from 'vue';
|
||||
import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed, useTemplateRef, toRaw } from 'vue';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import insertTextAtCursor from 'insert-text-at-cursor';
|
||||
import { toASCII } from 'punycode.js';
|
||||
import { host, url } from '@@/js/config.js';
|
||||
import type { ShallowRef } from 'vue';
|
||||
import { appendContentWarning } from '@@/js/append-content-warning.js';
|
||||
import type { MenuItem } from '@/types/menu.js';
|
||||
import type { PostFormProps } from '@/types/post-form.js';
|
||||
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
||||
import type { PollEditorModelValue } from '@/components/MkPollEditor.vue';
|
||||
import MkNotePreview from '@/components/MkNotePreview.vue';
|
||||
import XPostFormAttaches from '@/components/MkPostFormAttaches.vue';
|
||||
import MkPollEditor, { type PollEditorModelValue } from '@/components/MkPollEditor.vue';
|
||||
import { erase, unique } from '@/scripts/array.js';
|
||||
import { extractMentions } from '@/scripts/extract-mentions.js';
|
||||
import { formatTimeString } from '@/scripts/format-time-string.js';
|
||||
import { Autocomplete } from '@/scripts/autocomplete.js';
|
||||
import XTextCounter from '@/components/MkPostForm.TextCounter.vue';
|
||||
import MkPollEditor from '@/components/MkPollEditor.vue';
|
||||
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
||||
import { erase, unique } from '@/utility/array.js';
|
||||
import { extractMentions } from '@/utility/extract-mentions.js';
|
||||
import { formatTimeString } from '@/utility/format-time-string.js';
|
||||
import { Autocomplete } from '@/utility/autocomplete.js';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||
import { selectFiles } from '@/scripts/select-file.js';
|
||||
import { defaultStore, notePostInterruptors, postFormActions } from '@/store.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { selectFiles } from '@/utility/select-file.js';
|
||||
import { store } from '@/store.js';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { instance } from '@/instance.js';
|
||||
import { signinRequired, notesCount, incNotesCount, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account.js';
|
||||
import { uploadFile } from '@/scripts/upload.js';
|
||||
import { deepClone } from '@/scripts/clone.js';
|
||||
import { ensureSignin, notesCount, incNotesCount } from '@/i.js';
|
||||
import { getAccounts, openAccountMenu as openAccountMenu_ } from '@/accounts.js';
|
||||
import { uploadFile } from '@/utility/upload.js';
|
||||
import { deepClone } from '@/utility/clone.js';
|
||||
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
||||
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 MkScheduleEditor from '@/components/MkScheduleEditor.vue';
|
||||
import { miLocalStorage } from '@/local-storage.js';
|
||||
import { claimAchievement } from '@/utility/achievements.js';
|
||||
import { emojiPicker } from '@/utility/emoji-picker.js';
|
||||
import { mfmFunctionPicker } from '@/utility/mfm-function-picker.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { getPluginHandlers } from '@/plugin.js';
|
||||
import { DI } from '@/di.js';
|
||||
|
||||
const $i = signinRequired();
|
||||
const $i = ensureSignin();
|
||||
|
||||
const modal = inject('modal');
|
||||
|
||||
|
|
@ -157,7 +165,7 @@ const props = withDefaults(defineProps<PostFormProps & {
|
|||
initialLocalOnly: undefined,
|
||||
});
|
||||
|
||||
provide('mock', props.mock);
|
||||
provide(DI.mock, props.mock);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'posted'): void;
|
||||
|
|
@ -168,10 +176,11 @@ const emit = defineEmits<{
|
|||
(ev: 'fileChangeSensitive', fileId: string, to: boolean): void;
|
||||
}>();
|
||||
|
||||
const textareaEl = shallowRef<HTMLTextAreaElement | null>(null);
|
||||
const cwInputEl = shallowRef<HTMLInputElement | null>(null);
|
||||
const hashtagsInputEl = shallowRef<HTMLInputElement | null>(null);
|
||||
const visibilityButton = shallowRef<HTMLElement>();
|
||||
const textareaEl = useTemplateRef('textareaEl');
|
||||
const cwInputEl = useTemplateRef('cwInputEl');
|
||||
const hashtagsInputEl = useTemplateRef('hashtagsInputEl');
|
||||
const visibilityButton = useTemplateRef('visibilityButton');
|
||||
const otherSettingsButton = useTemplateRef('otherSettingsButton');
|
||||
|
||||
const posting = ref(false);
|
||||
const posted = ref(false);
|
||||
|
|
@ -179,19 +188,18 @@ const text = ref(props.initialText ?? '');
|
|||
const files = ref(props.initialFiles ?? []);
|
||||
const poll = ref<PollEditorModelValue | null>(null);
|
||||
const useCw = ref<boolean>(!!props.initialCw);
|
||||
const showPreview = ref(defaultStore.state.showPreview);
|
||||
watch(showPreview, () => defaultStore.set('showPreview', showPreview.value));
|
||||
const showAddMfmFunction = ref(defaultStore.state.enableQuickAddMfmFunction);
|
||||
watch(showAddMfmFunction, () => defaultStore.set('enableQuickAddMfmFunction', showAddMfmFunction.value));
|
||||
const showPreview = ref(store.s.showPreview);
|
||||
watch(showPreview, () => store.set('showPreview', showPreview.value));
|
||||
const showAddMfmFunction = ref(prefer.s.enableQuickAddMfmFunction);
|
||||
watch(showAddMfmFunction, () => prefer.commit('enableQuickAddMfmFunction', showAddMfmFunction.value));
|
||||
const cw = ref<string | null>(props.initialCw ?? null);
|
||||
const localOnly = ref(props.initialLocalOnly ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly));
|
||||
const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility));
|
||||
const localOnly = ref(props.initialLocalOnly ?? (prefer.s.rememberNoteVisibility ? store.s.localOnly : prefer.s.defaultNoteLocalOnly));
|
||||
const visibility = ref(props.initialVisibility ?? (prefer.s.rememberNoteVisibility ? store.s.visibility : prefer.s.defaultNoteVisibility));
|
||||
const visibleUsers = ref<Misskey.entities.UserDetailed[]>([]);
|
||||
if (props.initialVisibleUsers) {
|
||||
props.initialVisibleUsers.forEach(u => pushVisibleUser(u));
|
||||
}
|
||||
const reactionAcceptance = ref(defaultStore.state.reactionAcceptance);
|
||||
const autocomplete = ref(null);
|
||||
const reactionAcceptance = ref(store.s.reactionAcceptance);
|
||||
const draghover = ref(false);
|
||||
const quoteId = ref<string | null>(null);
|
||||
const hasNotSpecifiedMentions = ref(false);
|
||||
|
|
@ -204,6 +212,7 @@ const scheduleNote = ref<{
|
|||
scheduledAt: number | null;
|
||||
} | null>(null);
|
||||
const renoteTargetNote: ShallowRef<PostFormProps['renote'] | null> = shallowRef(props.renote);
|
||||
const postFormActions = getPluginHandlers('post_form_action');
|
||||
|
||||
const draftKey = computed((): string => {
|
||||
let key = props.channel ? `channel:${props.channel.id}` : '';
|
||||
|
|
@ -255,8 +264,11 @@ const maxTextLength = computed((): number => {
|
|||
return instance ? instance.maxNoteTextLength : 1000;
|
||||
});
|
||||
|
||||
const cwLength = computed(() => cw.value?.length ?? 0);
|
||||
const maxCwLength = computed(() => instance.maxCwLength);
|
||||
const cwTextLength = computed((): number => {
|
||||
return cw.value?.length ?? 0;
|
||||
});
|
||||
|
||||
const maxCwTextLength = computed(() => instance.maxCwLength);
|
||||
|
||||
const canPost = computed((): boolean => {
|
||||
return !props.mock && !posting.value && !posted.value &&
|
||||
|
|
@ -268,13 +280,19 @@ const canPost = computed((): boolean => {
|
|||
quoteId.value != null
|
||||
) &&
|
||||
(textLength.value <= maxTextLength.value) &&
|
||||
(cwLength.value <= maxCwLength.value) &&
|
||||
(
|
||||
useCw.value ?
|
||||
(
|
||||
cw.value != null && cw.value.trim() !== '' &&
|
||||
cwTextLength.value <= maxCwTextLength.value
|
||||
) : true
|
||||
) &&
|
||||
(files.value.length <= 16) &&
|
||||
(!poll.value || poll.value.choices.length >= 2);
|
||||
});
|
||||
|
||||
const withHashtags = computed(defaultStore.makeGetterSetter('postFormWithHashtags'));
|
||||
const hashtags = computed(defaultStore.makeGetterSetter('postFormHashtags'));
|
||||
const withHashtags = computed(store.makeGetterSetter('postFormWithHashtags'));
|
||||
const hashtags = computed(store.makeGetterSetter('postFormHashtags'));
|
||||
|
||||
watch(text, () => {
|
||||
checkMissingMention();
|
||||
|
|
@ -362,7 +380,7 @@ if (props.specified) {
|
|||
}
|
||||
|
||||
// keep cw when reply
|
||||
if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
|
||||
if (prefer.s.keepCw && props.reply && props.reply.cw) {
|
||||
useCw.value = true;
|
||||
cw.value = props.reply.cw;
|
||||
}
|
||||
|
|
@ -484,7 +502,7 @@ function replaceFile(file: Misskey.entities.DriveFile, newFile: Misskey.entities
|
|||
function upload(file: File, name?: string): void {
|
||||
if (props.mock) return;
|
||||
|
||||
uploadFile(file, defaultStore.state.uploadFolder, name).then(res => {
|
||||
uploadFile(file, prefer.s.uploadFolder, name).then(res => {
|
||||
files.value.push(res);
|
||||
});
|
||||
}
|
||||
|
|
@ -505,8 +523,8 @@ function setVisibility() {
|
|||
}, {
|
||||
changeVisibility: v => {
|
||||
visibility.value = v;
|
||||
if (defaultStore.state.rememberNoteVisibility) {
|
||||
defaultStore.set('visibility', visibility.value);
|
||||
if (prefer.s.rememberNoteVisibility) {
|
||||
store.set('visibility', visibility.value);
|
||||
}
|
||||
},
|
||||
closed: () => dispose(),
|
||||
|
|
@ -553,8 +571,8 @@ async function toggleLocalOnly() {
|
|||
}
|
||||
|
||||
localOnly.value = !localOnly.value;
|
||||
if (defaultStore.state.rememberNoteVisibility) {
|
||||
defaultStore.set('localOnly', localOnly.value);
|
||||
if (prefer.s.rememberNoteVisibility) {
|
||||
store.set('localOnly', localOnly.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -574,6 +592,47 @@ async function toggleReactionAcceptance() {
|
|||
reactionAcceptance.value = select.result;
|
||||
}
|
||||
|
||||
//#region その他の設定メニューpopup
|
||||
function showOtherSettings() {
|
||||
let reactionAcceptanceIcon = 'ti ti-icons';
|
||||
|
||||
if (reactionAcceptance.value === 'likeOnly') {
|
||||
reactionAcceptanceIcon = 'ti ti-heart _love';
|
||||
} else if (reactionAcceptance.value === 'likeOnlyForRemote') {
|
||||
reactionAcceptanceIcon = 'ti ti-heart-plus';
|
||||
}
|
||||
|
||||
const menuItems = [{
|
||||
type: 'component',
|
||||
component: XTextCounter,
|
||||
props: {
|
||||
textLength: textLength,
|
||||
},
|
||||
}, { type: 'divider' }, {
|
||||
icon: reactionAcceptanceIcon,
|
||||
text: i18n.ts.reactionAcceptance,
|
||||
action: () => {
|
||||
toggleReactionAcceptance();
|
||||
},
|
||||
}, { type: 'divider' }, {
|
||||
icon: 'ti ti-trash',
|
||||
text: i18n.ts.reset,
|
||||
danger: true,
|
||||
action: async () => {
|
||||
if (props.mock) return;
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'question',
|
||||
text: i18n.ts.resetAreYouSure,
|
||||
});
|
||||
if (canceled) return;
|
||||
clear();
|
||||
},
|
||||
}] satisfies MenuItem[];
|
||||
|
||||
os.popupMenu(menuItems, otherSettingsButton.value);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
function pushVisibleUser(user: Misskey.entities.UserDetailed) {
|
||||
if (!visibleUsers.value.some(u => u.username === user.username && u.host === user.host)) {
|
||||
visibleUsers.value.push(user);
|
||||
|
|
@ -623,6 +682,8 @@ function onCompositionEnd(ev: CompositionEvent) {
|
|||
justEndedComposition.value = true;
|
||||
}
|
||||
|
||||
const pastedFileName = 'yyyy-MM-dd HH-mm-ss [{{number}}]';
|
||||
|
||||
async function onPaste(ev: ClipboardEvent) {
|
||||
if (props.mock) return;
|
||||
if (!ev.clipboardData) return;
|
||||
|
|
@ -633,7 +694,7 @@ async function onPaste(ev: ClipboardEvent) {
|
|||
if (!file) continue;
|
||||
const lio = file.name.lastIndexOf('.');
|
||||
const ext = lio >= 0 ? file.name.slice(lio) : '';
|
||||
const formatted = `${formatTimeString(new Date(file.lastModified), defaultStore.state.pastedFileName).replace(/{{number}}/g, `${i + 1}`)}${ext}`;
|
||||
const formatted = `${formatTimeString(new Date(file.lastModified), pastedFileName).replace(/{{number}}/g, `${i + 1}`)}${ext}`;
|
||||
upload(file, formatted);
|
||||
}
|
||||
}
|
||||
|
|
@ -678,7 +739,7 @@ async function onPaste(ev: ClipboardEvent) {
|
|||
return;
|
||||
}
|
||||
|
||||
const fileName = formatTimeString(new Date(), defaultStore.state.pastedFileName).replace(/{{number}}/g, '0');
|
||||
const fileName = formatTimeString(new Date(), pastedFileName).replace(/{{number}}/g, '0');
|
||||
const file = new File([paste], `${fileName}.txt`, { type: 'text/plain' });
|
||||
upload(file, `${fileName}.txt`);
|
||||
});
|
||||
|
|
@ -772,19 +833,19 @@ function deleteDraft() {
|
|||
miLocalStorage.setItem('drafts', JSON.stringify(draftData));
|
||||
}
|
||||
|
||||
async function post(ev?: MouseEvent) {
|
||||
if (useCw.value && (cw.value == null || cw.value.trim() === '')) {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: i18n.ts.cwNotationRequired,
|
||||
});
|
||||
return;
|
||||
}
|
||||
function isAnnoying(text: string): boolean {
|
||||
return text.includes('$[x2') ||
|
||||
text.includes('$[x3') ||
|
||||
text.includes('$[x4') ||
|
||||
text.includes('$[scale') ||
|
||||
text.includes('$[position');
|
||||
}
|
||||
|
||||
async function post(ev?: MouseEvent) {
|
||||
if (ev) {
|
||||
const el = (ev.currentTarget ?? ev.target) as HTMLElement | null;
|
||||
|
||||
if (el) {
|
||||
if (el && prefer.s.animation) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
const x = rect.left + (el.offsetWidth / 2);
|
||||
const y = rect.top + (el.offsetHeight / 2);
|
||||
|
|
@ -796,14 +857,10 @@ async function post(ev?: MouseEvent) {
|
|||
|
||||
if (props.mock) return;
|
||||
|
||||
const annoying =
|
||||
text.value.includes('$[x2') ||
|
||||
text.value.includes('$[x3') ||
|
||||
text.value.includes('$[x4') ||
|
||||
text.value.includes('$[scale') ||
|
||||
text.value.includes('$[position');
|
||||
|
||||
if (annoying && visibility.value === 'public') {
|
||||
if (visibility.value === 'public' && (
|
||||
(useCw.value && cw.value != null && cw.value.trim() !== '' && isAnnoying(cw.value)) || // CWが迷惑になる場合
|
||||
((!useCw.value || cw.value == null || cw.value.trim() === '') && text.value != null && text.value.trim() !== '' && isAnnoying(text.value)) // CWが無い かつ 本文が迷惑になる場合
|
||||
)) {
|
||||
const { canceled, result } = await os.actions({
|
||||
type: 'warning',
|
||||
text: i18n.ts.thisPostMayBeAnnoying,
|
||||
|
|
@ -884,6 +941,7 @@ async function post(ev?: MouseEvent) {
|
|||
}
|
||||
|
||||
// plugin
|
||||
const notePostInterruptors = getPluginHandlers('note_post_interruptor');
|
||||
if (notePostInterruptors.length > 0) {
|
||||
for (const interruptor of notePostInterruptors) {
|
||||
try {
|
||||
|
|
@ -898,7 +956,7 @@ async function post(ev?: MouseEvent) {
|
|||
|
||||
if (postAccount.value) {
|
||||
const storedAccounts = await getAccounts();
|
||||
token = storedAccounts.find(x => x.id === postAccount.value?.id)?.token;
|
||||
token = storedAccounts.find(x => x.user.id === postAccount.value?.id)?.token;
|
||||
}
|
||||
|
||||
posting.value = true;
|
||||
|
|
@ -1182,6 +1240,8 @@ defineExpose({
|
|||
&.modal {
|
||||
width: 100%;
|
||||
max-width: 520px;
|
||||
overflow-x: clip;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1292,7 +1352,7 @@ defineExpose({
|
|||
border-radius: var(--MI-radius-sm);
|
||||
|
||||
&:hover {
|
||||
background: var(--MI_THEME-X5);
|
||||
background: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05));
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
|
|
@ -1356,7 +1416,7 @@ defineExpose({
|
|||
margin-right: 14px;
|
||||
padding: 8px 0 8px 8px;
|
||||
border-radius: var(--MI-radius-sm);
|
||||
background: var(--MI_THEME-X4);
|
||||
background: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1));
|
||||
}
|
||||
|
||||
.hasNotSpecifiedMentions {
|
||||
|
|
@ -1387,7 +1447,12 @@ defineExpose({
|
|||
}
|
||||
}
|
||||
|
||||
.cwFrame {
|
||||
.cwOuter {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.cw {
|
||||
z-index: 1;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: solid 0.5px var(--MI_THEME-divider);
|
||||
|
|
@ -1396,6 +1461,23 @@ defineExpose({
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.cwTextCount {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 2px;
|
||||
padding: 2px 6px;
|
||||
font-size: .9em;
|
||||
color: var(--MI_THEME-warn);
|
||||
border-radius: 6px;
|
||||
max-width: 100%;
|
||||
min-width: 1.6em;
|
||||
text-align: center;
|
||||
|
||||
&.cwTextOver {
|
||||
color: #ff2a2a;
|
||||
}
|
||||
}
|
||||
|
||||
.hashtags {
|
||||
z-index: 1;
|
||||
padding-top: 8px;
|
||||
|
|
@ -1470,7 +1552,7 @@ defineExpose({
|
|||
border-radius: var(--MI-radius-sm);
|
||||
|
||||
&:hover {
|
||||
background: var(--MI_THEME-X5);
|
||||
background: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05));
|
||||
}
|
||||
|
||||
&.footerButtonActive {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue