1
0
mirror of https://git.boykissers.com/pawkey/pawkey-sk.git synced 2025-12-20 04:04:16 +00:00

fix: fix stuff after merge

This commit is contained in:
Leafus
2025-06-01 06:26:43 +02:00
committed by Bluey Heeler
parent 5a5914be8d
commit 238aeed753
10 changed files with 490 additions and 431 deletions

View File

@@ -7,13 +7,13 @@ import { defineAsyncComponent, reactive, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { apiUrl } from '@@/js/config.js'; import { apiUrl } from '@@/js/config.js';
import type { MenuItem, MenuButton } from '@/types/menu.js'; import type { MenuItem, MenuButton } from '@/types/menu.js';
import { showSuspendedDialog } from '@/scripts/show-suspended-dialog.js'; import { showSuspendedDialog } from '@/utility/show-suspended-dialog.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { miLocalStorage } from '@/local-storage.js'; import { miLocalStorage } from '@/local-storage.js';
import { del, get, set } from '@/scripts/idb-proxy.js'; import { del, get, set } from '@/utility/idb-proxy.js';
import { waiting, popup, popupMenu, success, alert } from '@/os.js'; import { waiting, popup, popupMenu, success, alert } from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js'; import { misskeyApi } from '@/utility/misskey-api.js';
import { unisonReload, reloadChannel } from '@/scripts/unison-reload.js'; import { unisonReload, reloadChannel } from '@/utility/unison-reload.js';
// TODO: 他のタブと永続化されたstateを同期 // TODO: 他のタブと永続化されたstateを同期

View File

@@ -76,7 +76,7 @@ import { ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { instanceName } from '@@/js/config.js'; import { instanceName } from '@@/js/config.js';
import type { MenuItem } from '@/types/menu.js'; import type { MenuItem } from '@/types/menu.js';
import sanitizeHtml from '@/scripts/sanitize-html.js'; import sanitizeHtml from '@/utility/sanitize-html.js';
import XSigninDialog from '@/components/MkSigninDialog.vue'; import XSigninDialog from '@/components/MkSigninDialog.vue';
import XSignupDialog from '@/components/MkSignupDialog.vue'; import XSignupDialog from '@/components/MkSignupDialog.vue';
import PwVisitorMusic from '@/components/PwVisitorMusic.vue'; import PwVisitorMusic from '@/components/PwVisitorMusic.vue';

View File

@@ -346,7 +346,7 @@ import { searchEngineMap } from '@/scripts/search-engine-map.js';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { instance } from '@/instance.js'; import { instance } from '@/instance.js';
import { misskeyApi } from '@/scripts/misskey-api.js'; import { misskeyApi } from '@/utility/misskey-api.js';
import { reloadAsk } from '@/scripts/reload-ask.js'; import { reloadAsk } from '@/scripts/reload-ask.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js'; import { definePageMetadata } from '@/scripts/page-metadata.js';

View File

@@ -35,9 +35,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkRadios v-model="overridedDeviceKind"> <MkRadios v-model="overridedDeviceKind">
<template #label><SearchLabel>{{ i18n.ts.overridedDeviceKind }}</SearchLabel></template> <template #label><SearchLabel>{{ i18n.ts.overridedDeviceKind }}</SearchLabel></template>
<option :value="null">{{ i18n.ts.auto }}</option> <option :value="null">{{ i18n.ts.auto }}</option>
<option value="smartphone"><i class="ti ti-device-mobile"/> {{ i18n.ts.smartphone }}</option> <option value="smartphone">{{ i18n.ts.smartphone }}</option>
<option value="tablet"><i class="ti ti-device-tablet"/> {{ i18n.ts.tablet }}</option> <option value="tablet">{{ i18n.ts.tablet }}</option>
<option value="desktop"><i class="ti ti-device-desktop"/> {{ i18n.ts.desktop }}</option> <option value="desktop">{{ i18n.ts.desktop }}</option>
</MkRadios> </MkRadios>
</SearchMarker> </SearchMarker>

View File

@@ -4,199 +4,232 @@ SPDX-License-Identifier: AGPL-3.0-only
--> -->
<template> <template>
<div class="_spacer" :style="{ '--MI_SPACER-w': narrow ? '800px' : '1100px', ...background }"> <div class="_spacer" :style="{ '--MI_SPACER-w': narrow ? '800px' : '1100px', ...background }">
<div ref="rootEl" class="ftskorzw" :class="{ wide: !narrow }" style="container-type: inline-size;"> <div ref="rootEl" class="ftskorzw" :class="{ wide: !narrow }" style="container-type: inline-size;">
<div class="main _gaps"> <div class="main _gaps">
<MkInfo v-if="user.isSuspended" :warn="true">{{ i18n.ts.userSuspended }}</MkInfo> <MkInfo v-if="user.isSuspended" :warn="true">{{ i18n.ts.userSuspended }}</MkInfo>
<MkInfo v-if="user.isSilenced" :warn="true">{{ i18n.ts.userSilenced }}</MkInfo> <MkInfo v-if="user.isSilenced" :warn="true">{{ i18n.ts.userSilenced }}</MkInfo>
<div class="profile _gaps"> <div class="profile _gaps">
<MkAccountMoved v-if="user.movedTo" :movedTo="user.movedTo"/> <MkAccountMoved v-if="user.movedTo" :movedTo="user.movedTo" />
<MkRemoteCaution v-if="user.host != null" :href="user.url ?? user.uri!"/> <MkRemoteCaution v-if="user.host != null" :href="user.url ?? user.uri!" />
<MkInfo v-if="user.host == null && user.username.includes('.')">{{ i18n.ts.isSystemAccount }}</MkInfo> <MkInfo v-if="user.host == null && user.username.includes('.')">{{ i18n.ts.isSystemAccount }}
</MkInfo>
<div :key="user.id" class="main _panel"> <div :key="user.id" class="main _panel">
<div class="banner-container" :class="{ [$style.bannerContainerTall]: useTallBanner }"> <div class="banner-container" :class="{ [$style.bannerContainerTall]: useTallBanner }">
<div ref="bannerEl" class="banner" :style="style"></div> <div ref="bannerEl" class="banner" :style="style"></div>
<div class="fade"></div> <div class="fade"></div>
<div class="title"> <div class="title">
<MkUserName class="name" :user="user" :nowrap="true"/> <MkUserName class="name" :user="user" :nowrap="true" />
<div class="bottom"> <div class="bottom">
<span class="username"><MkAcct :user="user" :detail="true"/></span> <span class="username">
<span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--MI_THEME-badge);"><i class="ti ti-shield"></i></span> <MkAcct :user="user" :detail="true" />
<span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="ti ti-lock"></i></span> </span>
<span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ti ti-robot"></i></span> <span v-if="user.isAdmin" :title="i18n.ts.isAdmin"
<button v-if="$i && !isEditingMemo && !memoDraft" class="_button add-note-button" @click="showMemoTextarea"> style="color: var(--MI_THEME-badge);"><i class="ti ti-shield"></i></span>
<i class="ti ti-edit"/> {{ i18n.ts.addMemo }} <span v-if="user.isLocked" :title="i18n.ts.isLocked"><i
</button> class="ti ti-lock"></i></span>
<span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ti ti-robot"></i></span>
<button v-if="$i && !isEditingMemo && !memoDraft" class="_button add-note-button"
@click="showMemoTextarea">
<i class="ti ti-edit" /> {{ i18n.ts.addMemo }}
</button>
</div>
</div>
<ul v-if="$i && $i.id != user.id" :class="$style.infoBadges">
<li v-if="user.isFollowed && user.isFollowing">{{ i18n.ts.mutuals }}</li>
<li v-else-if="user.isFollowing">{{ i18n.ts.following }}</li>
<li v-else-if="user.isFollowed">{{ i18n.ts.followsYou }}</li>
<li v-if="user.isMuted">{{ i18n.ts.muted }}</li>
<li v-if="user.isRenoteMuted">{{ i18n.ts.renoteMuted }}</li>
<li v-if="user.isBlocking">{{ i18n.ts.blocked }}</li>
<li v-if="user.isBlocked && $i.isModerator">{{ i18n.ts.blockingYou }}</li>
</ul>
<div :class="$style.actions" class="actions">
<button :class="$style.actionsMenu" class="menu _button" @click="menu"><i
class="ti ti-dots"></i></button>
<MkFollowButton v-if="$i?.id != user.id" v-model:user="user"
:class="$style.actionsFollow" :disabled="disableFollowControls" :inline="true"
:transparent="false" :full="true" class="koudoku"
@update:wait="onFollowButtonDisabledChanged" />
<div v-if="hasFollowRequest" :class="$style.actionsBanner">{{
i18n.ts.receiveFollowRequest }}</div>
<MkButton v-if="hasFollowRequest" :class="$style.actionsAccept"
:disabled="disableFollowControls" :inline="true" :transparent="false" :full="true"
rounded primary @click="acceptFollowRequest"><i class="ti ti-check" /> {{
i18n.ts.accept }}</MkButton>
<MkButton v-if="hasFollowRequest" :class="$style.actionsReject"
:disabled="disableFollowControls" :inline="true" :transparent="false" :full="true"
rounded danger @click="rejectFollowRequest"><i class="ti ti-x" /> {{ i18n.ts.reject
}}</MkButton>
</div> </div>
</div> </div>
<ul v-if="$i && $i.id != user.id" :class="$style.infoBadges"> <MkAvatar class="avatar" :class="{ [$style.avatarTall]: useTallBanner }" :user="user"
<li v-if="user.isFollowed && user.isFollowing">{{ i18n.ts.mutuals }}</li> indicator />
<li v-else-if="user.isFollowing">{{ i18n.ts.following }}</li> <div class="title">
<li v-else-if="user.isFollowed">{{ i18n.ts.followsYou }}</li> <MkUserName :user="user" :nowrap="false" class="name" />
<li v-if="user.isMuted">{{ i18n.ts.muted }}</li> <div class="bottom">
<li v-if="user.isRenoteMuted">{{ i18n.ts.renoteMuted }}</li> <span class="username">
<li v-if="user.isBlocking">{{ i18n.ts.blocked }}</li> <MkAcct :user="user" :detail="true" />
<li v-if="user.isBlocked && $i.isModerator">{{ i18n.ts.blockingYou }}</li> </span>
</ul> <span v-if="user.isAdmin" :title="i18n.ts.isAdmin"
<div :class="$style.actions" class="actions"> style="color: var(--MI_THEME-badge);"><i class="ti ti-shield"></i></span>
<button :class="$style.actionsMenu" class="menu _button" @click="menu"><i class="ti ti-dots"></i></button> <span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="ti ti-lock"></i></span>
<MkFollowButton v-if="$i?.id != user.id" v-model:user="user" :class="$style.actionsFollow" :disabled="disableFollowControls" :inline="true" :transparent="false" :full="true" class="koudoku" @update:wait="onFollowButtonDisabledChanged"/> <span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ti ti-robot"></i></span>
<div v-if="hasFollowRequest" :class="$style.actionsBanner">{{ i18n.ts.receiveFollowRequest }}</div> </div>
<MkButton v-if="hasFollowRequest" :class="$style.actionsAccept" :disabled="disableFollowControls" :inline="true" :transparent="false" :full="true" rounded primary @click="acceptFollowRequest"><i class="ti ti-check"/> {{ i18n.ts.accept }}</MkButton>
<MkButton v-if="hasFollowRequest" :class="$style.actionsReject" :disabled="disableFollowControls" :inline="true" :transparent="false" :full="true" rounded danger @click="rejectFollowRequest"><i class="ti ti-x"/> {{ i18n.ts.reject }}</MkButton>
</div> </div>
</div> <div v-if="user.followedMessage != null" class="followedMessage">
<MkAvatar class="avatar" :class="{ [$style.avatarTall]: useTallBanner }" :user="user" indicator/> <MkFukidashi class="fukidashi" :tail="narrow ? 'none' : 'left'" negativeMargin>
<div class="title"> <div class="messageHeader">{{ i18n.ts.messageToFollower }}</div>
<MkUserName :user="user" :nowrap="false" class="name"/> <div>
<div class="bottom"> <MkSparkle>
<span class="username"><MkAcct :user="user" :detail="true"/></span> <Mfm :plain="true" :text="user.followedMessage" :author="user"
<span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--MI_THEME-badge);"><i class="ti ti-shield"></i></span> class="_selectable" />
<span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="ti ti-lock"></i></span> </MkSparkle>
<span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ti ti-robot"></i></span> </div>
</MkFukidashi>
</div> </div>
</div> <div v-if="user.roles.length > 0" class="roles">
<div v-if="user.followedMessage != null" class="followedMessage"> <span v-for="role in user.roles" :key="role.id" v-tooltip="role.description" class="role"
<MkFukidashi class="fukidashi" :tail="narrow ? 'none' : 'left'" negativeMargin> :style="{ '--color': role.color }">
<div class="messageHeader">{{ i18n.ts.messageToFollower }}</div> <MkA v-adaptive-bg :to="`/roles/${role.id}`">
<div><MkSparkle><Mfm :plain="true" :text="user.followedMessage" :author="user" class="_selectable"/></MkSparkle></div> <img v-if="role.iconUrl" style="height: 1.3em; vertical-align: -22%;"
</MkFukidashi> :src="role.iconUrl" />
</div> {{ role.name }}
<div v-if="user.roles.length > 0" class="roles"> </MkA>
<span v-for="role in user.roles" :key="role.id" v-tooltip="role.description" class="role" :style="{ '--color': role.color }"> </span>
<MkA v-adaptive-bg :to="`/roles/${role.id}`">
<img v-if="role.iconUrl" style="height: 1.3em; vertical-align: -22%;" :src="role.iconUrl"/>
{{ role.name }}
</MkA>
</span>
</div>
<div v-if="iAmModerator" class="moderationNote">
<MkTextarea v-if="editModerationNote || (moderationNote != null && moderationNote !== '')" v-model="moderationNote" manualSave>
<template #label>{{ i18n.ts.moderationNote }}</template>
<template #caption>{{ i18n.ts.moderationNoteDescription }}</template>
</MkTextarea>
<div v-else>
<MkButton small @click="editModerationNote = true">{{ i18n.ts.addModerationNote }}</MkButton>
</div> </div>
</div> <div v-if="iAmModerator" class="moderationNote">
<div v-if="isEditingMemo || memoDraft" class="memo" :class="{'no-memo': !memoDraft}"> <MkTextarea v-if="editModerationNote || (moderationNote != null && moderationNote !== '')"
<div class="heading" v-text="i18n.ts.memo"/> v-model="moderationNote" manualSave>
<textarea <template #label>{{ i18n.ts.moderationNote }}</template>
ref="memoTextareaEl" <template #caption>{{ i18n.ts.moderationNoteDescription }}</template>
v-model="memoDraft" </MkTextarea>
rows="1" <div v-else>
@focus="isEditingMemo = true" <MkButton small @click="editModerationNote = true">{{ i18n.ts.addModerationNote }}
@blur="updateMemo" </MkButton>
@input="adjustMemoTextarea" </div>
/> </div>
</div> <div v-if="isEditingMemo || memoDraft" class="memo" :class="{ 'no-memo': !memoDraft }">
<div class="description"> <div class="heading" v-text="i18n.ts.memo" />
<MkOmit> <textarea ref="memoTextareaEl" v-model="memoDraft" rows="1" @focus="isEditingMemo = true"
<Mfm v-if="user.description" :text="user.description" :isBlock="true" :isNote="false" :author="user" class="_selectable"/> @blur="updateMemo" @input="adjustMemoTextarea" />
</div>
<div class="description">
<Mfm v-if="user.description" :text="user.description" :isBlock="true" :isNote="false"
:author="user" class="_selectable" />
<p v-else class="empty">{{ i18n.ts.noAccountDescription }}</p> <p v-else class="empty">{{ i18n.ts.noAccountDescription }}</p>
</MkOmit> </div>
</div> <div class="fields system">
<div class="fields system"> <dl v-if="user.location" class="field">
<dl v-if="user.location" class="field"> <dt class="name"><i class="ti ti-map-pin ti-fw"></i> {{ i18n.ts.location }}</dt>
<dt class="name"><i class="ti ti-map-pin ti-fw"></i> {{ i18n.ts.location }}</dt> <dd class="value">{{ user.location }}</dd>
<dd class="value">{{ user.location }}</dd> </dl>
</dl> <dl v-if="user.birthday" class="field">
<dl v-if="user.birthday" class="field"> <dt class="name"><i class="ti ti-cake ti-fw"></i> {{ i18n.ts.birthday }}</dt>
<dt class="name"><i class="ti ti-cake ti-fw"></i> {{ i18n.ts.birthday }}</dt> <dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{
<dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ i18n.tsx.yearsOld({ age }) }})</dd> i18n.tsx.yearsOld({ age }) }})
</dl> </dd>
<dl class="field"> </dl>
<dt class="name"><i class="ti ti-calendar ti-fw"></i> {{ i18n.ts.registeredDate }}</dt> <dl class="field">
<dd class="value">{{ dateString(user.createdAt) }} (<MkTime :time="user.createdAt"/>)</dd> <dt class="name"><i class="ti ti-calendar ti-fw"></i> {{ i18n.ts.registeredDate }}</dt>
</dl> <dd class="value">{{ dateString(user.createdAt) }} (
</div> <MkTime :time="user.createdAt" />)
<div v-if="user.fields.length > 0" class="fields"> </dd>
<dl v-for="(field, i) in user.fields" :key="i" class="field"> </dl>
<dt class="name"> </div>
<Mfm :text="field.name" :author="user" :plain="true" :colored="false" class="_selectable"/> <div v-if="user.fields.length > 0" class="fields">
</dt> <dl v-for="(field, i) in user.fields" :key="i" class="field">
<dd class="value"> <dt class="name">
<Mfm :text="field.value" :author="user" :colored="false" class="_selectable"/> <Mfm :text="field.name" :author="user" :plain="true" :colored="false"
<i v-if="user.verifiedLinks.includes(field.value)" v-tooltip:dialog="i18n.ts.verifiedLink" class="ti ti-circle-check" :class="$style.verifiedLink"></i> class="_selectable" />
</dd> </dt>
</dl> <dd class="value">
</div> <Mfm :text="field.value" :author="user" :colored="false" class="_selectable" />
<div class="status"> <i v-if="user.verifiedLinks.includes(field.value)"
<MkA :to="userPage(user)"> v-tooltip:dialog="i18n.ts.verifiedLink" class="ti ti-circle-check"
<b>{{ number(user.notesCount) }}</b> :class="$style.verifiedLink"></i>
<span>{{ i18n.ts.notes }}</span> </dd>
</MkA> </dl>
<MkA v-if="isFollowingVisibleForMe(user)" :to="userPage(user, 'following')"> </div>
<b>{{ number(user.followingCount) }}</b> <div class="status">
<span>{{ i18n.ts.following }}</span> <MkA :to="userPage(user)">
</MkA> <b>{{ number(user.notesCount) }}</b>
<MkA v-if="isFollowersVisibleForMe(user)" :to="userPage(user, 'followers')"> <span>{{ i18n.ts.notes }}</span>
<b>{{ number(user.followersCount) }}</b> </MkA>
<span>{{ i18n.ts.followers }}</span> <MkA v-if="isFollowingVisibleForMe(user)" :to="userPage(user, 'following')">
</MkA> <b>{{ number(user.followingCount) }}</b>
<span>{{ i18n.ts.following }}</span>
</MkA>
<MkA v-if="isFollowersVisibleForMe(user)" :to="userPage(user, 'followers')">
<b>{{ number(user.followersCount) }}</b>
<span>{{ i18n.ts.followers }}</span>
</MkA>
</div>
</div> </div>
</div> </div>
</div>
<div class="contents _gaps"> <div class="contents _gaps">
<MkInfo v-if="user.pinnedNotes.length === 0 && $i?.id === user.id">{{ i18n.ts.userPagePinTip }}</MkInfo> <MkInfo v-if="user.pinnedNotes.length === 0 && $i?.id === user.id">{{ i18n.ts.userPagePinTip }}
<template v-if="narrow"> </MkInfo>
<MkLazy v-if="user.musicUrl"> <template v-if="narrow">
<XProfileMusic :key="user.id" :user="user"/> <MkLazy v-if="user.musicUrl">
</MkLazy> <XProfileMusic :key="user.id" :user="user" />
<MkLazy> </MkLazy>
<XFiles :key="user.id" :user="user" :collapsed="true" @unfold="emit('unfoldFiles')"/> <MkLazy>
</MkLazy> <XFiles :key="user.id" :user="user" :collapsed="true" @unfold="emit('unfoldFiles')" />
<MkLazy> </MkLazy>
<XActivity :key="user.id" :user="user" :collapsed="true"/> <MkLazy>
</MkLazy> <XActivity :key="user.id" :user="user" :collapsed="true" />
<MkLazy v-if="user.listenbrainz && listenbrainzdata"> </MkLazy>
<XListenBrainz :key="user.id" :user="user" :collapsed="true"/> <MkLazy v-if="user.listenbrainz && listenbrainzdata">
</MkLazy> <XListenBrainz :key="user.id" :user="user" :collapsed="true" />
</template> </MkLazy>
<!-- <div v-if="!disableNotes"> </template>
<!-- <div v-if="!disableNotes">
<MkLazy> <MkLazy>
<XTimeline :user="user"/> <XTimeline :user="user"/>
</MkLazy> </MkLazy>
</div> --> </div> -->
<MkStickyContainer> <MkStickyContainer>
<template #header> <template #header>
<!-- You can't use v-if on these, as MkTab first *deletes* and replaces all children with native HTML elements. --> <!-- You can't use v-if on these, as MkTab first *deletes* and replaces all children with native HTML elements. -->
<!-- Instead, we add a "no notes" placeholder and default to null (all notes) if there's nothing pinned. --> <!-- Instead, we add a "no notes" placeholder and default to null (all notes) if there's nothing pinned. -->
<!-- It also converts all comments into text! --> <!-- It also converts all comments into text! -->
<MkTab v-model="noteview" :class="$style.tab"> <MkTab v-model="noteview" :class="$style.tab">
<option value="pinned">{{ i18n.ts.pinnedOnly }}</option> <option value="pinned">{{ i18n.ts.pinnedOnly }}</option>
<option :value="null">{{ i18n.ts.notes }}</option> <option :value="null">{{ i18n.ts.notes }}</option>
<option value="all">{{ i18n.ts.all }}</option> <option value="all">{{ i18n.ts.all }}</option>
<option value="files">{{ i18n.ts.withFiles }}</option> <option value="files">{{ i18n.ts.withFiles }}</option>
</MkTab> </MkTab>
</template> </template>
<MkLazy> <MkLazy>
<div v-if="noteview === 'pinned'" class="_gaps"> <div v-if="noteview === 'pinned'" class="_gaps">
<div v-if="user.pinnedNotes.length < 1" class="_fullinfo"> <div v-if="user.pinnedNotes.length < 1" class="_fullinfo">
<img :src="infoImageUrl" draggable="false" aria-hidden="true" :alt="i18n.ts.noNotes"/> <img :src="infoImageUrl" draggable="false" aria-hidden="true"
<div>{{ i18n.ts.noNotes }}</div> :alt="i18n.ts.noNotes" />
<div>{{ i18n.ts.noNotes }}</div>
</div>
<div v-else class="_panel">
<DynamicNote v-for="note of user.pinnedNotes" :key="note.id" class="note"
:class="$style.pinnedNote" :note="note" :pinned="true" />
</div>
</div> </div>
<div v-else class="_panel"> <MkNotes v-else :class="$style.tl" :noGap="true" :pagination="AllPagination" />
<DynamicNote v-for="note of user.pinnedNotes" :key="note.id" class="note" :class="$style.pinnedNote" :note="note" :pinned="true"/> </MkLazy>
</div> </MkStickyContainer>
</div> </div>
<MkNotes v-else :class="$style.tl" :noGap="true" :pagination="AllPagination"/> </div>
</MkLazy> <div v-if="!narrow" class="sub _gaps" style="container-type: inline-size;">
</MkStickyContainer> <XFiles :key="user.id" :user="user" @unfold="emit('unfoldFiles')" />
<XActivity :key="user.id" :user="user" />
<XListenBrainz v-if="user.listenbrainz && listenbrainzdata" :key="user.id" :user="user" />
<XProfileMusic v-if="user.musicUrl" :key="user.id" :user="user" :collapsed="true" />
</div> </div>
</div> </div>
<div v-if="!narrow" class="sub _gaps" style="container-type: inline-size;"> <div class="background"></div>
<XFiles :key="user.id" :user="user" @unfold="emit('unfoldFiles')"/>
<XActivity :key="user.id" :user="user"/>
<XListenBrainz v-if="user.listenbrainz && listenbrainzdata" :key="user.id" :user="user"/>
<XProfileMusic v-if="user.musicUrl" :key="user.id" :user="user" :collapsed="true"/>
</div>
</div> </div>
<div class="background"></div>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@@ -277,7 +310,7 @@ const noteview = ref<string | null>(props.user.pinnedNotes.length ? 'pinned' : n
const listenbrainzdata = ref(false); const listenbrainzdata = ref(false);
if (props.user.listenbrainz) { if (props.user.listenbrainz) {
(async function() { (async function () {
try { try {
const response = await window.fetch(`https://api.listenbrainz.org/1/user/${props.user.listenbrainz}/playing-now`, { const response = await window.fetch(`https://api.listenbrainz.org/1/user/${props.user.listenbrainz}/playing-now`, {
method: 'GET', method: 'GET',
@@ -336,11 +369,11 @@ const style = computed(() => {
if (props.user.bannerUrl == null) return {}; if (props.user.bannerUrl == null) return {};
if (prefer.s.disableShowingAnimatedImages) { if (prefer.s.disableShowingAnimatedImages) {
return { return {
backgroundImage: `url(${ getStaticImageUrl(props.user.bannerUrl) })`, backgroundImage: `url(${getStaticImageUrl(props.user.bannerUrl)})`,
}; };
} else { } else {
return { return {
backgroundImage: `url(${ props.user.bannerUrl })`, backgroundImage: `url(${props.user.bannerUrl})`,
}; };
} }
}); });
@@ -461,7 +494,7 @@ onUnmounted(() => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.background{ .background {
position: fixed; position: fixed;
z-index: -1; z-index: -1;
background: var(--backgroundImageStatic); background: var(--backgroundImageStatic);
@@ -479,28 +512,28 @@ onUnmounted(() => {
.ftskorzw { .ftskorzw {
> .main { >.main {
> .punished { >.punished {
font-size: 0.8em; font-size: 0.8em;
padding: 16px; padding: 16px;
} }
> .profile { >.profile {
> .main { >.main {
position: relative; position: relative;
overflow: clip; overflow: clip;
background: color-mix(in srgb, var(--MI_THEME-panel) 65%, transparent); background: color-mix(in srgb, var(--MI_THEME-panel) 65%, transparent);
> .banner-container { >.banner-container {
position: relative; position: relative;
height: 250px; height: 250px;
overflow: clip; overflow: clip;
background-size: cover; background-size: cover;
background-position: center; background-position: center;
> .banner { >.banner {
height: 100%; height: 100%;
background-color: #4c5e6d; background-color: #4c5e6d;
background-size: cover; background-size: cover;
@@ -509,7 +542,7 @@ onUnmounted(() => {
will-change: background-position; will-change: background-position;
} }
> .fade { >.fade {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
@@ -518,7 +551,7 @@ onUnmounted(() => {
background: linear-gradient(transparent, rgba(#000, 0.7)); background: linear-gradient(transparent, rgba(#000, 0.7));
} }
> .actions { >.actions {
position: absolute; position: absolute;
top: 12px; top: 12px;
right: 12px; right: 12px;
@@ -528,7 +561,7 @@ onUnmounted(() => {
padding: 8px; padding: 8px;
border-radius: var(--MI-radius-lg); border-radius: var(--MI-radius-lg);
> .menu { >.menu {
vertical-align: bottom; vertical-align: bottom;
height: 31px; height: 31px;
width: 31px; width: 31px;
@@ -537,13 +570,13 @@ onUnmounted(() => {
font-size: 16px; font-size: 16px;
} }
> .koudoku { >.koudoku {
margin-left: 4px; margin-left: 4px;
vertical-align: bottom; vertical-align: bottom;
} }
} }
> .title { >.title {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
@@ -552,7 +585,7 @@ onUnmounted(() => {
box-sizing: border-box; box-sizing: border-box;
color: #fff; color: #fff;
> .name { >.name {
display: block; display: block;
margin: -10px; margin: -10px;
padding: 10px; padding: 10px;
@@ -562,8 +595,8 @@ onUnmounted(() => {
filter: drop-shadow(0 0 4px #000); filter: drop-shadow(0 0 4px #000);
} }
> .bottom { >.bottom {
> * { >* {
display: inline-block; display: inline-block;
margin-right: 16px; margin-right: 16px;
line-height: 20px; line-height: 20px;
@@ -574,7 +607,7 @@ onUnmounted(() => {
} }
} }
> .add-note-button { >.add-note-button {
background: rgba(0, 0, 0, 0.2); background: rgba(0, 0, 0, 0.2);
color: #fff; color: #fff;
-webkit-backdrop-filter: var(--MI-blur, blur(8px)); -webkit-backdrop-filter: var(--MI-blur, blur(8px));
@@ -587,15 +620,15 @@ onUnmounted(() => {
} }
} }
> .title { >.title {
display: none; display: none;
text-align: center; text-align: center;
padding: 50px 8px 16px 8px; padding: 50px 8px 16px 8px;
font-weight: bold; font-weight: bold;
border-bottom: solid 0.5px var(--MI_THEME-divider); border-bottom: solid 0.5px var(--MI_THEME-divider);
> .bottom { >.bottom {
> * { >* {
display: inline-block; display: inline-block;
margin-right: 8px; margin-right: 8px;
opacity: 0.8; opacity: 0.8;
@@ -603,7 +636,7 @@ onUnmounted(() => {
} }
} }
> .avatar { >.avatar {
display: block; display: block;
position: absolute; position: absolute;
top: 170px; top: 170px;
@@ -614,10 +647,10 @@ onUnmounted(() => {
filter: drop-shadow(1px 1px 3px rgba(#000, 0.2)); filter: drop-shadow(1px 1px 3px rgba(#000, 0.2));
} }
> .followedMessage { >.followedMessage {
padding: 24px 24px 0 154px; padding: 24px 24px 0 154px;
> .fukidashi { >.fukidashi {
display: block; display: block;
--fukidashi-bg: color-mix(in srgb, var(--MI_THEME-accent), var(--MI_THEME-panel) 85%); --fukidashi-bg: color-mix(in srgb, var(--MI_THEME-accent), var(--MI_THEME-panel) 85%);
--fukidashi-radius: 16px; --fukidashi-radius: 16px;
@@ -630,14 +663,14 @@ onUnmounted(() => {
} }
} }
> .roles { >.roles {
padding: 24px 24px 0 154px; padding: 24px 24px 0 154px;
font-size: 0.95em; font-size: 0.95em;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 8px; gap: 8px;
> .role { >.role {
border: solid 1px var(--color, var(--MI_THEME-divider)); border: solid 1px var(--color, var(--MI_THEME-divider));
border-radius: var(--MI-radius-ellipse); border-radius: var(--MI-radius-ellipse);
margin-right: 4px; margin-right: 4px;
@@ -645,11 +678,11 @@ onUnmounted(() => {
} }
} }
> .moderationNote { >.moderationNote {
margin: 12px 24px 0 154px; margin: 12px 24px 0 154px;
} }
> .memo { >.memo {
margin: 12px 24px 0 154px; margin: 12px 24px 0 154px;
background: transparent; background: transparent;
color: var(--MI_THEME-fg); color: var(--MI_THEME-fg);
@@ -658,7 +691,7 @@ onUnmounted(() => {
padding: 8px; padding: 8px;
line-height: 0; line-height: 0;
> .heading { >.heading {
text-align: left; text-align: left;
color: color(from var(--MI_THEME-fg) srgb r g b / 0.5); color: color(from var(--MI_THEME-fg) srgb r g b / 0.5);
line-height: 1.5; line-height: 1.5;
@@ -682,22 +715,22 @@ onUnmounted(() => {
} }
} }
> .description { >.description {
padding: 24px 24px 24px 154px; padding: 24px 24px 24px 154px;
font-size: 0.95em; font-size: 0.95em;
> .empty { >.empty {
margin: 0; margin: 0;
opacity: 0.5; opacity: 0.5;
} }
} }
> .fields { >.fields {
padding: 24px; padding: 24px;
font-size: 0.9em; font-size: 0.9em;
border-top: solid 0.5px var(--MI_THEME-divider); border-top: solid 0.5px var(--MI_THEME-divider);
> .field { >.field {
display: flex; display: flex;
padding: 0; padding: 0;
margin: 0; margin: 0;
@@ -707,7 +740,7 @@ onUnmounted(() => {
margin-bottom: 8px; margin-bottom: 8px;
} }
> .name { >.name {
width: 30%; width: 30%;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
@@ -716,7 +749,7 @@ onUnmounted(() => {
text-align: center; text-align: center;
} }
> .value { >.value {
width: 70%; width: 70%;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
@@ -725,16 +758,15 @@ onUnmounted(() => {
} }
} }
&.system > .field > .name { &.system>.field>.name {}
}
} }
> .status { >.status {
display: flex; display: flex;
padding: 24px; padding: 24px;
border-top: solid 0.5px var(--MI_THEME-divider); border-top: solid 0.5px var(--MI_THEME-divider);
> a { >a {
flex: 1; flex: 1;
text-align: center; text-align: center;
@@ -746,12 +778,12 @@ onUnmounted(() => {
text-decoration: none; text-decoration: none;
} }
> b { >b {
display: block; display: block;
line-height: 16px; line-height: 16px;
} }
> span { >span {
font-size: 70%; font-size: 70%;
} }
} }
@@ -759,8 +791,8 @@ onUnmounted(() => {
} }
} }
> .contents { >.contents {
> .content { >.content {
margin-bottom: var(--MI-margin); margin-bottom: var(--MI-margin);
} }
} }
@@ -770,12 +802,12 @@ onUnmounted(() => {
display: flex; display: flex;
width: 100%; width: 100%;
> .main { >.main {
width: 100%; width: 100%;
min-width: 0; min-width: 0;
} }
> .sub { >.sub {
max-width: 350px; max-width: 350px;
min-width: 350px; min-width: 350px;
margin-left: var(--MI-margin); margin-left: var(--MI-margin);
@@ -785,25 +817,25 @@ onUnmounted(() => {
@container (max-width: 500px) { @container (max-width: 500px) {
.ftskorzw { .ftskorzw {
> .main { >.main {
> .profile > .main { >.profile>.main {
> .banner-container { >.banner-container {
height: 140px; height: 140px;
> .fade { >.fade {
display: none; display: none;
} }
> .title { >.title {
display: none; display: none;
} }
} }
> .title { >.title {
display: block; display: block;
} }
> .avatar { >.avatar {
top: 90px; top: 90px;
left: 0; left: 0;
right: 0; right: 0;
@@ -812,39 +844,39 @@ onUnmounted(() => {
margin: auto; margin: auto;
} }
> .followedMessage { >.followedMessage {
padding: 16px 16px 0 16px; padding: 16px 16px 0 16px;
} }
> .roles { >.roles {
padding: 16px 16px 0 16px; padding: 16px 16px 0 16px;
justify-content: center; justify-content: center;
} }
> .moderationNote { >.moderationNote {
margin: 16px 16px 0 16px; margin: 16px 16px 0 16px;
} }
> .memo { >.memo {
margin: 16px 16px 0 16px; margin: 16px 16px 0 16px;
} }
> .description { >.description {
padding: 16px; padding: 16px;
text-align: center; text-align: center;
} }
> .fields { >.fields {
padding: 16px; padding: 16px;
} }
> .status { >.status {
padding: 16px; padding: 16px;
} }
} }
> .contents { >.contents {
> .nav { >.nav {
font-size: 80%; font-size: 80%;
} }
} }
@@ -868,7 +900,7 @@ onUnmounted(() => {
backdrop-filter: var(--MI-blur, blur(15px)); backdrop-filter: var(--MI-blur, blur(15px));
border-radius: var(--MI-radius-sm); border-radius: var(--MI-radius-sm);
> button { >button {
border-radius: var(--MI-radius-sm); border-radius: var(--MI-radius-sm);
margin-left: 0.4rem; margin-left: 0.4rem;
margin-right: 0.4rem; margin-right: 0.4rem;
@@ -891,7 +923,7 @@ onUnmounted(() => {
padding: 0; padding: 0;
margin: 0; margin: 0;
> * { >* {
padding: 4px 8px; padding: 4px 8px;
color: #fff; color: #fff;
background: rgba(0, 0, 0, 0.7); background: rgba(0, 0, 0, 0.7);

View File

@@ -4,9 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only
--> -->
<template> <template>
<div v-if="meta" :class="$style.root"> <div v-if="meta" class="rsqzvsbo">
<MkFeaturedPhotos :class="$style.bg"/> <MkFeaturedPhotos class="bg"/>
<XTimeline v-if="meta.policies.ltlAvailable" :class="$style.tl"/>
<div class="logo-wrapper"> <div class="logo-wrapper">
<!-- <div class="powered-by">Powered by</div> --> <!-- <div class="powered-by">Powered by</div> -->
<img :src="misskeysvg" class="misskey"/> <img :src="misskeysvg" class="misskey"/>
@@ -37,7 +36,7 @@ import { ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue'; import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue';
import misskeysvg from '/client-assets/pawkey.png'; import misskeysvg from '/client-assets/pawkey.png';
import { misskeyApiGet } from '@/scripts/misskey-api.js'; import { misskeyApiGet } from '@/utility/misskey-api.js';
import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue'; import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue';
import { getProxiedImageUrl } from '@/utility/media-proxy.js'; import { getProxiedImageUrl } from '@/utility/media-proxy.js';
import { instance as meta } from '@/instance.js'; import { instance as meta } from '@/instance.js';
@@ -61,61 +60,68 @@ misskeyApiGet('federation/instances', {
}); });
</script> </script>
<style lang="scss" module> <style lang="scss" scoped>
.root { .rsqzvsbo {
height: 100cqh; > .bg {
overflow: auto; position: fixed;
overscroll-behavior: contain; top: 0;
} right: 0;
width: 100vw;
height: 100vh;
}
.bg { > .tl {
position: fixed; position: fixed;
top: 0; top: 0;
right: 0; bottom: 0;
width: 100%; right: 64px;
height: 100vh; margin: auto;
} padding: 128px 0;
width: 500px;
.tl { height: calc(100% - 256px);
position: fixed; overflow: hidden;
top: 0; -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
bottom: 0; mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
right: 64px;
margin: auto;
padding: 128px 0;
width: 500px;
height: calc(100% - 256px);
overflow: hidden;
-webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
@media (max-width: 1200px) { @media (max-width: 1200px) {
display: none; display: none;
} }
} }
.logoWrapper { > .logo-wrapper {
position: fixed; position: fixed;
top: 36px; top: 36px;
left: 36px; left: 36px;
flex: auto; flex: auto;
color: #fff; color: #fff;
user-select: none; user-select: none;
pointer-events: none; pointer-events: none;
}
.poweredBy { > .powered-by {
color: var(--MI_THEME-fgOnAccent); margin-bottom: 2px;
margin-bottom: 2px; }
}
.misskey { > .misskey {
width: 120px; width: 140px;
@media (max-width: 450px) {
@media (max-width: 450px) { width: 130px;
width: 100px; }
}
}
> .emojis {
position: fixed;
bottom: 32px;
left: 35px;
> * {
margin-right: 8px;
}
@media (max-width: 1200px) {
display: none;
}
} }
}
> .contents { > .contents {
position: relative; position: relative;
@@ -123,30 +129,33 @@ misskeyApiGet('federation/instances', {
margin: auto; margin: auto;
padding: 100px 0 100px 0; padding: 100px 0 100px 0;
@media (max-width: 1200px) { @media (max-width: 1200px) {
margin: auto;
}
}
> .federation {
position: fixed;
bottom: 16px;
left: 0;
right: 0;
margin: auto; margin: auto;
background: var(--MI_THEME-acrylicPanel);
-webkit-backdrop-filter: var(--MI-blur, blur(15px));
backdrop-filter: var(--MI-blur, blur(15px));
border-radius: var(--MI-radius-ellipse);
overflow: clip;
width: 800px;
padding: 8px 0;
@media (max-width: 900px) {
display: none;
}
} }
} }
</style>
.federation { <style lang="scss" module>
position: fixed;
bottom: 16px;
left: 0;
right: 0;
margin: auto;
background: color(from var(--MI_THEME-panel) srgb r g b / 0.5);
-webkit-backdrop-filter: var(--MI-blur, blur(15px));
backdrop-filter: var(--MI-blur, blur(15px));
border-radius: var(--MI-radius-ellipse);
overflow: clip;
width: 800px;
padding: 8px 0;
@media (max-width: 900px) {
display: none;
}
}
.federationInstance { .federationInstance {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@@ -155,13 +164,13 @@ misskeyApiGet('federation/instances', {
margin: 0 10px 0 0; margin: 0 10px 0 0;
background: var(--MI_THEME-panel); background: var(--MI_THEME-panel);
border-radius: var(--MI-radius-ellipse); border-radius: var(--MI-radius-ellipse);
}
.federationInstanceIcon { > :global(.icon) {
display: inline-block; display: inline-block;
width: 20px; width: 20px;
height: 20px; height: 20px;
margin-right: 5px; margin-right: 5px;
border-radius: var(--MI-radius-ellipse); border-radius: var(--MI-radius-ellipse);
}
} }
</style> </style>

View File

@@ -420,7 +420,7 @@ function getPointerEvents() {
#devTicker { #devTicker {
position: fixed; position: fixed;
bottom: 0; bottom: 10px;
left: 0; left: 0;
z-index: 2147483647; z-index: 2147483647;
border-radius: 10px; border-radius: 10px;

View File

@@ -83,10 +83,6 @@ const isVideo = computed(() => {
const url = instance.backgroundImageUrl.toLowerCase(); const url = instance.backgroundImageUrl.toLowerCase();
return url.endsWith('.mp4') || url.endsWith('.webm') || url.endsWith('.ogg'); return url.endsWith('.mp4') || url.endsWith('.webm') || url.endsWith('.ogg');
}); });
defineExpose({
showMenu: showMenu,
});
</script> </script>
<style> <style>
@@ -137,6 +133,11 @@ defineExpose({
min-width: 0; min-width: 0;
} }
.content {
flex: 1;
overflow: auto;
}
.homeButton { .homeButton {
position: fixed; position: fixed;
z-index: 1000; z-index: 1000;
@@ -159,119 +160,117 @@ defineExpose({
z-index: 1; z-index: 1;
overflow-y: scroll; overflow-y: scroll;
background: var(--MI_THEME-accent); background: var(--MI_THEME-accent);
>.banner {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-position: center;
background-size: cover;
-webkit-mask-image: linear-gradient(rgba(0, 0, 0, 1.0), transparent);
mask-image: linear-gradient(rgba(0, 0, 0, 1.0), transparent);
> video {
width: 500px;
height: 100%;
object-fit: cover;
}
> .image {
width: 500px;
height: 100%;
background-size: cover;
background-position: center;
}
}
>.dashboard {
position: relative;
padding: 32px;
box-sizing: border-box;
max-height: 100%;
overflow: auto;
}
} }
>.banner { .main {
position: absolute; flex: 1;
top: 0; min-width: 0;
left: 0;
width: 100%;
height: 100%;
background-position: center;
background-size: cover;
-webkit-mask-image: linear-gradient(rgba(0, 0, 0, 1.0), transparent);
mask-image: linear-gradient(rgba(0, 0, 0, 1.0), transparent);
> video { >.header {
width: 500px;
height: 100%;
object-fit: cover;
}
> .image {
width: 500px;
height: 100%;
background-size: cover;
background-position: center;
}
}
>.dashboard {
position: relative;
padding: 32px;
box-sizing: border-box;
max-height: 100%;
overflow: auto;
}
}
>.main {
flex: 1;
min-width: 0;
>.header {
background: var(--MI_THEME-panel);
position: relative;
z-index: 1;
>.wide {
line-height: 50px;
padding: 0 16px;
>.link {
padding: 0 16px;
}
}
>.narrow {
>.menu {
padding: 16px;
}
}
}
}
>.menu-back {
position: fixed;
z-index: 1001;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
}
>.menu {
position: fixed;
z-index: 1001;
top: 0;
left: 0;
width: 240px;
height: 100vh;
background: var(--MI_THEME-panel); background: var(--MI_THEME-panel);
position: relative;
z-index: 1;
>.link { >.wide {
display: block; line-height: 50px;
padding: 16px; padding: 0 16px;
>.icon { >.link {
margin-right: 1em; padding: 0 16px;
} }
} }
>.divider { >.narrow {
margin: 8px auto; >.menu {
width: calc(100% - 32px); padding: 16px;
border-top: solid 0.5px var(--MI_THEME-divider); }
} }
}
}
>.action { .menu-back {
padding: 16px; position: fixed;
z-index: 1001;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
}
>button { .menu {
display: block; position: fixed;
width: 100%; z-index: 1001;
padding: 10px; top: 0;
box-sizing: border-box; left: 0;
text-align: center; width: 240px;
border-radius: var(--MI-radius-ellipse); height: 100vh;
background: var(--MI_THEME-panel);
&._button { >.link {
background: var(--MI_THEME-panel); display: block;
} padding: 16px;
&:first-child { >.icon {
margin-bottom: 16px; margin-right: 1em;
} }
}
>.divider {
margin: 8px auto;
width: calc(100% - 32px);
border-top: solid 0.5px var(--MI_THEME-divider);
}
>.action {
padding: 16px;
> button {
display: block;
width: 100%;
padding: 10px;
box-sizing: border-box;
text-align: center;
border-radius: var(--MI-radius-ellipse);
&._button {
background: var(--MI_THEME-panel);
}
&:first-child {
margin-bottom: 16px;
} }
} }
} }

View File

@@ -0,0 +1,19 @@
/*
* SPDX-FileCopyrightText: dakkar and other Sharkey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import original from 'sanitize-html';
export default function sanitizeHtml(str: string | null): string | null {
if (str == null) return str;
return original(str, {
allowedTags: original.defaults.allowedTags.concat(['img', 'audio', 'video', 'center', 'details', 'summary']),
allowedAttributes: {
...original.defaults.allowedAttributes,
a: original.defaults.allowedAttributes.a.concat(['style']),
img: original.defaults.allowedAttributes.img.concat(['style']),
'*': (original.defaults.allowedAttributes['*'] || []).concat(['style']),
},
});
}

6
pnpm-lock.yaml generated
View File

@@ -675,7 +675,7 @@ importers:
version: 2.1.1 version: 2.1.1
'@iconify/vue': '@iconify/vue':
specifier: ^4.3.0 specifier: ^4.3.0
version: 4.3.0(vue@3.5.12(typescript@5.6.3)) version: 4.3.0(vue@3.5.14(typescript@5.8.3))
'@mcaptcha/vanilla-glue': '@mcaptcha/vanilla-glue':
specifier: 0.1.0-alpha-3 specifier: 0.1.0-alpha-3
version: 0.1.0-alpha-3 version: 0.1.0-alpha-3
@@ -12081,10 +12081,10 @@ snapshots:
'@iconify/types@2.0.0': {} '@iconify/types@2.0.0': {}
'@iconify/vue@4.3.0(vue@3.5.12(typescript@5.6.3))': '@iconify/vue@4.3.0(vue@3.5.14(typescript@5.8.3))':
dependencies: dependencies:
'@iconify/types': 2.0.0 '@iconify/types': 2.0.0
vue: 3.5.12(typescript@5.6.3) vue: 3.5.14(typescript@5.8.3)
'@img/sharp-darwin-arm64@0.34.1': '@img/sharp-darwin-arm64@0.34.1':
optionalDependencies: optionalDependencies: