fix(frontend): drop classic ui
This commit is contained in:
parent
c03f9bff0a
commit
4f4cb6738c
|
@ -51,6 +51,9 @@
|
|||
- Enhance 全体的なパフォーマンス向上
|
||||
- Fix: 読み込み直後にスクロールしようとすると途中で止まる場合があるのを修正
|
||||
- Fix: テーマ切り替え時に一部の色が変わらない問題を修正
|
||||
- NOTE: 構造上クラシックUIを新しいデザインシステムに移行することが困難なため、クラシックUIが削除されました
|
||||
- デッキUIでカラムを中央寄せにし、メインカラムの左右にウィジェットカラムを配置することである程度クラシックUIを再現できます
|
||||
- また、デッキでナビゲーションバーを上部に表示するオプションを実装予定です
|
||||
|
||||
### Server
|
||||
- Enhance 全体的なパフォーマンス向上
|
||||
|
|
|
@ -54,9 +54,6 @@ export async function mainBoot() {
|
|||
case 'visitor':
|
||||
rootComponent = await import('@/ui/visitor.vue').then(x => x.default);
|
||||
break;
|
||||
case 'classic':
|
||||
rootComponent = await import('@/ui/classic.vue').then(x => x.default);
|
||||
break;
|
||||
default:
|
||||
rootComponent = await import('@/ui/universal.vue').then(x => x.default);
|
||||
break;
|
||||
|
|
|
@ -145,13 +145,6 @@ export const navbarItemDef = reactive({
|
|||
miLocalStorage.setItem('ui', 'deck');
|
||||
unisonReload();
|
||||
},
|
||||
}, {
|
||||
text: i18n.ts.classic,
|
||||
active: ui === 'classic',
|
||||
action: () => {
|
||||
miLocalStorage.setItem('ui', 'classic');
|
||||
unisonReload();
|
||||
},
|
||||
}], ev.currentTarget ?? ev.target);
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,212 +0,0 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="azykntjl">
|
||||
<div class="body">
|
||||
<div class="left">
|
||||
<button v-click-anime class="item _button instance" @click="openInstanceMenu">
|
||||
<img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" draggable="false"/>
|
||||
</button>
|
||||
<MkA v-click-anime v-tooltip="i18n.ts.timeline" class="item index" activeClass="active" to="/" exact>
|
||||
<i class="ti ti-home ti-fw"></i>
|
||||
</MkA>
|
||||
<template v-for="item in menu">
|
||||
<div v-if="item === '-'" class="divider"></div>
|
||||
<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime v-tooltip="navbarItemDef[item].title" class="item _button" :class="item" activeClass="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
|
||||
<i class="ti-fw" :class="navbarItemDef[item].icon"></i>
|
||||
<span v-if="navbarItemDef[item].indicated" class="indicator _blink"><i class="_indicatorCircle"></i></span>
|
||||
</component>
|
||||
</template>
|
||||
<div class="divider"></div>
|
||||
<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime v-tooltip="i18n.ts.controlPanel" class="item" activeClass="active" to="/admin" :behavior="settingsWindowed ? 'window' : null">
|
||||
<i class="ti ti-dashboard ti-fw"></i>
|
||||
</MkA>
|
||||
<button v-click-anime class="item _button" @click="more">
|
||||
<i class="ti ti-dots ti-fw"></i>
|
||||
<span v-if="otherNavItemIndicated" class="indicator _blink"><i class="_indicatorCircle"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="right">
|
||||
<MkA v-click-anime v-tooltip="i18n.ts.settings" class="item" activeClass="active" to="/settings" :behavior="settingsWindowed ? 'window' : null">
|
||||
<i class="ti ti-settings ti-fw"></i>
|
||||
</MkA>
|
||||
<button v-click-anime class="item _button account" @click="openAccountMenu">
|
||||
<MkAvatar :user="$i" class="avatar"/><MkAcct class="acct" :user="$i"/>
|
||||
</button>
|
||||
<div class="post" @click="os.post()">
|
||||
<MkButton class="button" gradate full rounded>
|
||||
<i class="ti ti-pencil ti-fw"></i>
|
||||
</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, defineAsyncComponent, onMounted, ref } from 'vue';
|
||||
import { openInstanceMenu } from './_common_/common.js';
|
||||
import * as os from '@/os.js';
|
||||
import { navbarItemDef } from '@/navbar.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import { instance } from '@/instance.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
|
||||
import { $i } from '@/i.js';
|
||||
|
||||
const WINDOW_THRESHOLD = 1400;
|
||||
|
||||
const settingsWindowed = ref(window.innerWidth > WINDOW_THRESHOLD);
|
||||
const menu = ref(prefer.s.menu);
|
||||
// const menuDisplay = computed(store.makeGetterSetter('menuDisplay'));
|
||||
const otherNavItemIndicated = computed<boolean>(() => {
|
||||
for (const def in navbarItemDef) {
|
||||
if (menu.value.includes(def)) continue;
|
||||
if (navbarItemDef[def].indicated) return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
function more(ev: MouseEvent) {
|
||||
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {
|
||||
src: ev.currentTarget ?? ev.target,
|
||||
anchor: { x: 'center', y: 'bottom' },
|
||||
}, {
|
||||
closed: () => dispose(),
|
||||
});
|
||||
}
|
||||
|
||||
function openAccountMenu(ev: MouseEvent) {
|
||||
openAccountMenu_({
|
||||
withExtraOperation: true,
|
||||
}, ev);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', () => {
|
||||
settingsWindowed.value = (window.innerWidth >= WINDOW_THRESHOLD);
|
||||
}, { passive: true });
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.azykntjl {
|
||||
$height: 60px;
|
||||
$avatar-size: 32px;
|
||||
$avatar-margin: 8px;
|
||||
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
width: 100%;
|
||||
height: $height;
|
||||
background-color: var(--MI_THEME-bg);
|
||||
|
||||
> .body {
|
||||
max-width: 1380px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
|
||||
> .right,
|
||||
> .left {
|
||||
|
||||
> .item {
|
||||
position: relative;
|
||||
font-size: 0.9em;
|
||||
display: inline-block;
|
||||
padding: 0 12px;
|
||||
line-height: $height;
|
||||
|
||||
> i,
|
||||
> .avatar {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
> i {
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
> .avatar {
|
||||
width: $avatar-size;
|
||||
height: $avatar-size;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
> .indicator {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
color: var(--MI_THEME-navIndicator);
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: var(--MI_THEME-navHoverFg);
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: var(--MI_THEME-navActive);
|
||||
}
|
||||
}
|
||||
|
||||
> .divider {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
margin: 0 10px;
|
||||
border-right: solid 0.5px var(--MI_THEME-divider);
|
||||
}
|
||||
|
||||
> .instance {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 56px;
|
||||
height: 100%;
|
||||
vertical-align: bottom;
|
||||
|
||||
> img {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
||||
> .post {
|
||||
display: inline-block;
|
||||
|
||||
> .button {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
padding: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
> .account {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
vertical-align: top;
|
||||
margin-right: 8px;
|
||||
|
||||
> .acct {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .right {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,245 +0,0 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="npcljfve" :class="{ iconOnly }">
|
||||
<button v-click-anime class="item _button account" @click="openAccountMenu">
|
||||
<MkAvatar :user="$i" class="avatar"/><MkAcct class="text" :user="$i"/>
|
||||
</button>
|
||||
<div class="post" data-cy-open-post-form @click="os.post">
|
||||
<MkButton class="button" gradate full rounded>
|
||||
<i class="ti ti-pencil ti-fw"></i><span v-if="!iconOnly" class="text">{{ i18n.ts.note }}</span>
|
||||
</MkButton>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<MkA v-click-anime class="item index" activeClass="active" to="/" exact>
|
||||
<i class="ti ti-home ti-fw"></i><span class="text">{{ i18n.ts.timeline }}</span>
|
||||
</MkA>
|
||||
<template v-for="item in menu">
|
||||
<div v-if="item === '-'" class="divider"></div>
|
||||
<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime class="item _button" :class="item" activeClass="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
|
||||
<i class="ti-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ navbarItemDef[item].title }}</span>
|
||||
<span v-if="navbarItemDef[item].indicated" class="indicator _blink">
|
||||
<span v-if="navbarItemDef[item].indicateValue" class="_indicateCounter itemIndicateValueIcon">{{ navbarItemDef[item].indicateValue }}</span>
|
||||
<i v-else class="_indicatorCircle"></i>
|
||||
</span>
|
||||
</component>
|
||||
</template>
|
||||
<div class="divider"></div>
|
||||
<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime class="item" activeClass="active" to="/admin" :behavior="settingsWindowed ? 'window' : null">
|
||||
<i class="ti ti-dashboard ti-fw"></i><span class="text">{{ i18n.ts.controlPanel }}</span>
|
||||
</MkA>
|
||||
<button v-click-anime class="item _button" @click="more">
|
||||
<i class="ti ti-dots ti-fw"></i><span class="text">{{ i18n.ts.more }}</span>
|
||||
<span v-if="otherNavItemIndicated" class="indicator _blink"><i class="_indicatorCircle"></i></span>
|
||||
</button>
|
||||
<MkA v-click-anime class="item" activeClass="active" to="/settings" :behavior="settingsWindowed ? 'window' : null">
|
||||
<i class="ti ti-settings ti-fw"></i><span class="text">{{ i18n.ts.settings }}</span>
|
||||
</MkA>
|
||||
<div class="divider"></div>
|
||||
<div class="about">
|
||||
<button v-click-anime class="item _button" @click="openInstanceMenu">
|
||||
<img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" draggable="false"/>
|
||||
</button>
|
||||
</div>
|
||||
<!--<MisskeyLogo class="misskey"/>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent, computed, watch, ref, useTemplateRef } from 'vue';
|
||||
import { openInstanceMenu } from './_common_/common.js';
|
||||
// import { host } from '@@/js/config.js';
|
||||
import * as os from '@/os.js';
|
||||
import { navbarItemDef } from '@/navbar.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
// import { StickySidebar } from '@/utility/sticky-sidebar.js';
|
||||
// import { mainRouter } from '@/router.js';
|
||||
//import MisskeyLogo from '@assets/client/misskey.svg';
|
||||
import { store } from '@/store.js';
|
||||
import { instance } from '@/instance.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
|
||||
import { $i } from '@/i.js';
|
||||
|
||||
const WINDOW_THRESHOLD = 1400;
|
||||
|
||||
const menu = ref(prefer.s.menu);
|
||||
const menuDisplay = computed(store.makeGetterSetter('menuDisplay'));
|
||||
const otherNavItemIndicated = computed<boolean>(() => {
|
||||
for (const def in navbarItemDef) {
|
||||
if (menu.value.includes(def)) continue;
|
||||
if (navbarItemDef[def].indicated) return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
const el = useTemplateRef('el');
|
||||
// let accounts = $ref([]);
|
||||
// let connection = $ref(null);
|
||||
const iconOnly = ref(false);
|
||||
const settingsWindowed = ref(false);
|
||||
|
||||
function calcViewState() {
|
||||
iconOnly.value = (window.innerWidth <= WINDOW_THRESHOLD) || (menuDisplay.value === 'sideIcon');
|
||||
settingsWindowed.value = (window.innerWidth > WINDOW_THRESHOLD);
|
||||
}
|
||||
|
||||
function more(ev: MouseEvent) {
|
||||
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {
|
||||
src: ev.currentTarget ?? ev.target,
|
||||
}, {
|
||||
closed: () => dispose(),
|
||||
});
|
||||
}
|
||||
|
||||
function openAccountMenu(ev: MouseEvent) {
|
||||
openAccountMenu_({
|
||||
withExtraOperation: true,
|
||||
}, ev);
|
||||
}
|
||||
|
||||
watch(store.r.menuDisplay, () => {
|
||||
calcViewState();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.npcljfve {
|
||||
$ui-font-size: 1em; // TODO: どこかに集約したい
|
||||
$nav-icon-only-width: 78px; // TODO: どこかに集約したい
|
||||
$avatar-size: 32px;
|
||||
$avatar-margin: 8px;
|
||||
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
width: 260px;
|
||||
|
||||
&.iconOnly {
|
||||
flex: 0 0 $nav-icon-only-width;
|
||||
width: $nav-icon-only-width !important;
|
||||
|
||||
> .divider {
|
||||
margin: 8px auto;
|
||||
width: calc(100% - 32px);
|
||||
}
|
||||
|
||||
> .post {
|
||||
> .button {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
> .item {
|
||||
padding-left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: $ui-font-size * 1.1;
|
||||
line-height: 3.7rem;
|
||||
|
||||
> i,
|
||||
> .avatar {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
> i {
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
> .text {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .divider {
|
||||
margin: 10px 0;
|
||||
border-top: solid 0.5px var(--MI_THEME-divider);
|
||||
}
|
||||
|
||||
> .post {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
padding: 16px 0;
|
||||
background: var(--MI_THEME-bg);
|
||||
|
||||
> .button {
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
> .about {
|
||||
fill: currentColor;
|
||||
padding: 8px 0 16px 0;
|
||||
text-align: center;
|
||||
|
||||
> .item {
|
||||
display: block;
|
||||
width: 32px;
|
||||
margin: 0 auto;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .item {
|
||||
position: relative;
|
||||
display: block;
|
||||
font-size: $ui-font-size;
|
||||
line-height: 2.6rem;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
box-sizing: border-box;
|
||||
|
||||
> i {
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
> i,
|
||||
> .avatar {
|
||||
margin-right: $avatar-margin;
|
||||
}
|
||||
|
||||
> .avatar {
|
||||
width: $avatar-size;
|
||||
height: $avatar-size;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
> .indicator {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
color: var(--MI_THEME-navIndicator);
|
||||
font-size: 8px;
|
||||
|
||||
&:has(.itemIndicateValueIcon) {
|
||||
animation: none;
|
||||
left: auto;
|
||||
right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: var(--MI_THEME-navHoverFg);
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: var(--MI_THEME-navActive);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,326 +0,0 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="gbhvwtnk" :class="{ wallpaper }" :style="`--globalHeaderHeight:${globalHeaderHeight}px`">
|
||||
<XHeaderMenu v-if="showMenuOnTop" v-get-size="(w, h) => globalHeaderHeight = h"/>
|
||||
|
||||
<div class="columns" :class="{ fullView, withGlobalHeader: showMenuOnTop }">
|
||||
<div v-if="!showMenuOnTop" class="sidebar">
|
||||
<XSidebar/>
|
||||
</div>
|
||||
<div v-else-if="!pageMetadata?.needWideArea" ref="widgetsLeft" class="widgets left">
|
||||
<XWidgets place="left" :marginTop="'var(--MI-margin)'" @mounted="attachSticky(widgetsLeft)"/>
|
||||
</div>
|
||||
|
||||
<main class="main" @contextmenu.stop="onContextmenu">
|
||||
<div class="content">
|
||||
<StackingRouterView v-if="prefer.s['experimental.stackingRouterView']"/>
|
||||
<RouterView v-else/>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div v-if="isDesktop && !pageMetadata?.needWideArea" ref="widgetsRight" class="widgets right">
|
||||
<XWidgets :place="showMenuOnTop ? 'right' : null" :marginTop="showMenuOnTop ? '0' : 'var(--MI-margin)'" @mounted="attachSticky(widgetsRight)"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Transition :name="prefer.s.animation ? 'tray-back' : ''">
|
||||
<div
|
||||
v-if="widgetsShowing"
|
||||
class="tray-back _modalBg"
|
||||
@click="widgetsShowing = false"
|
||||
@touchstart.passive="widgetsShowing = false"
|
||||
></div>
|
||||
</Transition>
|
||||
|
||||
<Transition :name="prefer.s.animation ? 'tray' : ''">
|
||||
<XWidgets v-if="widgetsShowing" class="tray"/>
|
||||
</Transition>
|
||||
|
||||
<iframe v-if="prefer.s.aiChanMode" ref="live2d" class="ivnzpscs" src="https://misskey-dev.github.io/mascot-web/?scale=2&y=1.4"></iframe>
|
||||
|
||||
<XCommon/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent, onMounted, provide, ref, computed, useTemplateRef } from 'vue';
|
||||
import { instanceName } from '@@/js/config.js';
|
||||
import { isLink } from '@@/js/is-link.js';
|
||||
import XSidebar from './classic.sidebar.vue';
|
||||
import XCommon from './_common_/common.vue';
|
||||
import type { PageMetadata } from '@/page.js';
|
||||
import { StickySidebar } from '@/utility/sticky-sidebar.js';
|
||||
import * as os from '@/os.js';
|
||||
import { provideMetadataReceiver, provideReactiveMetadata } from '@/page.js';
|
||||
import { store } from '@/store.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { miLocalStorage } from '@/local-storage.js';
|
||||
import { mainRouter } from '@/router.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { DI } from '@/di.js';
|
||||
|
||||
const XHeaderMenu = defineAsyncComponent(() => import('./classic.header.vue'));
|
||||
const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue'));
|
||||
|
||||
const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index');
|
||||
|
||||
const DESKTOP_THRESHOLD = 1100;
|
||||
|
||||
const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD);
|
||||
|
||||
const pageMetadata = ref<null | PageMetadata>(null);
|
||||
const widgetsShowing = ref(false);
|
||||
const fullView = ref(false);
|
||||
const globalHeaderHeight = ref(0);
|
||||
const wallpaper = miLocalStorage.getItem('wallpaper') != null;
|
||||
const showMenuOnTop = computed(() => store.s.menuDisplay === 'top');
|
||||
const live2d = useTemplateRef('live2d');
|
||||
const widgetsLeft = ref<HTMLElement>();
|
||||
const widgetsRight = ref<HTMLElement>();
|
||||
|
||||
provide(DI.router, mainRouter);
|
||||
provideMetadataReceiver((metadataGetter) => {
|
||||
const info = metadataGetter();
|
||||
pageMetadata.value = info;
|
||||
if (pageMetadata.value) {
|
||||
if (isRoot.value && pageMetadata.value.title === instanceName) {
|
||||
window.document.title = pageMetadata.value.title;
|
||||
} else {
|
||||
window.document.title = `${pageMetadata.value.title} | ${instanceName}`;
|
||||
}
|
||||
}
|
||||
});
|
||||
provideReactiveMetadata(pageMetadata);
|
||||
provide('shouldHeaderThin', showMenuOnTop.value);
|
||||
provide('forceSpacerMin', true);
|
||||
|
||||
function attachSticky(el: HTMLElement) {
|
||||
const sticky = new StickySidebar(el, 0, store.s.menuDisplay === 'top' ? 60 : 0); // TODO: ヘッダーの高さを60pxと決め打ちしているのを直す
|
||||
window.addEventListener('scroll', () => {
|
||||
sticky.calc(window.scrollY);
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
function top() {
|
||||
window.scroll({ top: 0, behavior: 'smooth' });
|
||||
}
|
||||
|
||||
function onContextmenu(ev: MouseEvent) {
|
||||
if (isLink(ev.target)) return;
|
||||
if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(ev.target.tagName) || ev.target.attributes['contenteditable']) return;
|
||||
if (window.getSelection().toString() !== '') return;
|
||||
const path = mainRouter.getCurrentFullPath();
|
||||
os.contextMenu([{
|
||||
type: 'label',
|
||||
text: path,
|
||||
}, {
|
||||
icon: fullView.value ? 'ti ti-minimize' : 'ti ti-maximize',
|
||||
text: fullView.value ? i18n.ts.quitFullView : i18n.ts.fullView,
|
||||
action: () => {
|
||||
fullView.value = !fullView.value;
|
||||
},
|
||||
}, {
|
||||
icon: 'ti ti-window-maximize',
|
||||
text: i18n.ts.openInWindow,
|
||||
action: () => {
|
||||
os.pageWindow(path);
|
||||
},
|
||||
}], ev);
|
||||
}
|
||||
|
||||
function onAiClick(ev) {
|
||||
//if (this.live2d) this.live2d.click(ev);
|
||||
}
|
||||
|
||||
if (window.innerWidth < 1024) {
|
||||
const currentUI = miLocalStorage.getItem('ui');
|
||||
miLocalStorage.setItem('ui_temp', currentUI ?? 'default');
|
||||
miLocalStorage.setItem('ui', 'default');
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
window.document.documentElement.style.overflowY = 'scroll';
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', () => {
|
||||
isDesktop.value = (window.innerWidth >= DESKTOP_THRESHOLD);
|
||||
}, { passive: true });
|
||||
|
||||
if (prefer.s.aiChanMode) {
|
||||
const iframeRect = live2d.value.getBoundingClientRect();
|
||||
window.addEventListener('mousemove', ev => {
|
||||
live2d.value.contentWindow.postMessage({
|
||||
type: 'moveCursor',
|
||||
body: {
|
||||
x: ev.clientX - iframeRect.left,
|
||||
y: ev.clientY - iframeRect.top,
|
||||
},
|
||||
}, '*');
|
||||
}, { passive: true });
|
||||
window.addEventListener('touchmove', ev => {
|
||||
live2d.value.contentWindow.postMessage({
|
||||
type: 'moveCursor',
|
||||
body: {
|
||||
x: ev.touches[0].clientX - iframeRect.left,
|
||||
y: ev.touches[0].clientY - iframeRect.top,
|
||||
},
|
||||
}, '*');
|
||||
}, { passive: true });
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tray-enter-active,
|
||||
.tray-leave-active {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
|
||||
}
|
||||
.tray-enter-from,
|
||||
.tray-leave-active {
|
||||
opacity: 0;
|
||||
transform: translateX(240px);
|
||||
}
|
||||
|
||||
.tray-back-enter-active,
|
||||
.tray-back-leave-active {
|
||||
opacity: 1;
|
||||
transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
|
||||
}
|
||||
.tray-back-enter-from,
|
||||
.tray-back-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.gbhvwtnk {
|
||||
$ui-font-size: 1em;
|
||||
$widgets-hide-threshold: 1200px;
|
||||
|
||||
min-height: 100dvh;
|
||||
box-sizing: border-box;
|
||||
|
||||
&.wallpaper {
|
||||
background: var(--MI_THEME-wallpaperOverlay);
|
||||
//backdrop-filter: var(--MI-blur, blur(4px));
|
||||
}
|
||||
|
||||
> .columns {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
max-width: 100%;
|
||||
//margin: 32px 0;
|
||||
|
||||
&.fullView {
|
||||
margin: 0;
|
||||
|
||||
> .sidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> .widgets {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> .main {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
> .main {
|
||||
min-width: 0;
|
||||
width: 750px;
|
||||
margin: 0 16px 0 0;
|
||||
border-left: solid 1px var(--MI_THEME-divider);
|
||||
border-right: solid 1px var(--MI_THEME-divider);
|
||||
border-radius: 0;
|
||||
overflow: clip;
|
||||
--MI-margin: 12px;
|
||||
}
|
||||
|
||||
> .widgets {
|
||||
//--MI_THEME-panelBorder: none;
|
||||
width: 300px;
|
||||
padding-bottom: calc(var(--MI-margin) + env(safe-area-inset-bottom, 0px));
|
||||
|
||||
@media (max-width: $widgets-hide-threshold) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.left {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
> .sidebar {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&.withGlobalHeader {
|
||||
> .main {
|
||||
margin-top: 0;
|
||||
border: solid 1px var(--MI_THEME-divider);
|
||||
border-radius: var(--MI-radius);
|
||||
--MI-stickyTop: var(--globalHeaderHeight);
|
||||
}
|
||||
|
||||
> .widgets {
|
||||
--MI-stickyTop: var(--globalHeaderHeight);
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 850px) {
|
||||
margin: 0;
|
||||
|
||||
> .sidebar {
|
||||
border-right: solid 0.5px var(--MI_THEME-divider);
|
||||
}
|
||||
|
||||
> .main {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .tray-back {
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
> .tray {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 1001;
|
||||
height: 100dvh;
|
||||
padding: var(--MI-margin) var(--MI-margin) calc(var(--MI-margin) + env(safe-area-inset-bottom, 0px));
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
background: var(--MI_THEME-bg);
|
||||
}
|
||||
|
||||
> .ivnzpscs {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 300px;
|
||||
height: 600px;
|
||||
border: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue