diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue index 51f19faf29..eae2ccec4a 100644 --- a/packages/frontend/src/components/MkPageWindow.vue +++ b/packages/frontend/src/components/MkPageWindow.vue @@ -22,8 +22,10 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </template> - <div :class="$style.root" class="_pageContainer"> - <RouterView :key="reloadCount" :router="windowRouter"/> + <div :class="$style.root"> + <StackingRouterView :key="reloadCount" :router="windowRouter"/> + <StackingRouterView v-if="prefer.s['experimental.stackingRouterView']" :key="reloadCount" :router="windowRouter"/> + <RouterView v-else :key="reloadCount" :router="windowRouter"/> </div> </MkWindow> </template> @@ -44,6 +46,7 @@ import { useRouterFactory } from '@/router/supplier.js'; import { mainRouter } from '@/router/main.js'; import { analytics } from '@/analytics.js'; import { DI } from '@/di.js'; +import { prefer } from '@/preferences.js'; const props = defineProps<{ initialPath: string; diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index 69bbd88cb6..b353269fef 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -49,9 +49,9 @@ import type { Tab } from './MkPageHeader.tabs.vue'; import type { PageHeaderItem } from '@/types/page-header.js'; import type { PageMetadata } from '@/page.js'; import { globalEvents } from '@/events.js'; -import { injectReactiveMetadata } from '@/page.js'; import { openAccountMenu as openAccountMenu_ } from '@/accounts.js'; import { $i } from '@/i.js'; +import { DI } from '@/di.js'; const props = withDefaults(defineProps<{ overridePageMetadata?: PageMetadata; @@ -69,7 +69,7 @@ const emit = defineEmits<{ (ev: 'update:tab', key: string); }>(); -const injectedPageMetadata = injectReactiveMetadata(); +const injectedPageMetadata = inject(DI.pageMetadata); const pageMetadata = computed(() => props.overridePageMetadata ?? injectedPageMetadata.value); const hideTitle = computed(() => inject('shouldOmitHeaderTitle', false) || props.hideTitle); diff --git a/packages/frontend/src/components/global/NestedRouterView.vue b/packages/frontend/src/components/global/NestedRouterView.vue new file mode 100644 index 0000000000..eb7192d8e0 --- /dev/null +++ b/packages/frontend/src/components/global/NestedRouterView.vue @@ -0,0 +1,65 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<Suspense :timeout="0"> + <component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/> + + <template #fallback> + <MkLoading/> + </template> +</Suspense> +</template> + +<script lang="ts" setup> +import { inject, onBeforeUnmount, provide, ref, shallowRef } from 'vue'; +import type { IRouter, Resolved } from '@/nirax.js'; +import MkLoadingPage from '@/pages/_loading_.vue'; +import { DI } from '@/di.js'; + +const props = defineProps<{ + router?: IRouter; +}>(); + +const router = props.router ?? inject(DI.router); + +if (router == null) { + throw new Error('no router provided'); +} + +const currentDepth = inject(DI.routerCurrentDepth, 0); +provide(DI.routerCurrentDepth, currentDepth + 1); + +function resolveNested(current: Resolved, d = 0): Resolved | null { + if (d === currentDepth) { + return current; + } else { + if (current.child) { + return resolveNested(current.child, d + 1); + } else { + return null; + } + } +} + +const current = resolveNested(router.current)!; +const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage); +const currentPageProps = ref(current.props); +const key = ref(router.getCurrentKey() + JSON.stringify(Object.fromEntries(current.props))); + +function onChange({ resolved, key: newKey }) { + const current = resolveNested(resolved); + if (current == null || 'redirect' in current.route) return; + currentPageComponent.value = current.route.component; + currentPageProps.value = current.props; + key.value = newKey + JSON.stringify(Object.fromEntries(current.props)); +} + +router.addListener('change', onChange); + +onBeforeUnmount(() => { + router.removeListener('change', onChange); +}); +</script> diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue index 25a29a4ae7..b01e355c5e 100644 --- a/packages/frontend/src/components/global/RouterView.vue +++ b/packages/frontend/src/components/global/RouterView.vue @@ -4,18 +4,20 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<KeepAlive - :max="prefer.s.numberOfPageCache" - :exclude="pageCacheController" -> - <Suspense :timeout="0"> - <component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/> +<div class="_pageContainer" style="height: 100%;"> + <KeepAlive + :max="prefer.s.numberOfPageCache" + :exclude="pageCacheController" + > + <Suspense :timeout="0"> + <component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/> - <template #fallback> - <MkLoading/> - </template> - </Suspense> -</KeepAlive> + <template #fallback> + <MkLoading/> + </template> + </Suspense> + </KeepAlive> +</div> </template> <script lang="ts" setup> @@ -28,7 +30,6 @@ import { DI } from '@/di.js'; const props = defineProps<{ router?: IRouter; - nested?: boolean; }>(); const router = props.router ?? inject(DI.router); @@ -40,31 +41,16 @@ if (router == null) { const currentDepth = inject(DI.routerCurrentDepth, 0); provide(DI.routerCurrentDepth, currentDepth + 1); -function resolveNested(current: Resolved, d = 0): Resolved | null { - if (!props.nested) return current; - - if (d === currentDepth) { - return current; - } else { - if (current.child) { - return resolveNested(current.child, d + 1); - } else { - return null; - } - } -} - -const current = resolveNested(router.current)!; +const current = router.current!; const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage); const currentPageProps = ref(current.props); const key = ref(router.getCurrentKey() + JSON.stringify(Object.fromEntries(current.props))); function onChange({ resolved, key: newKey }) { - const current = resolveNested(resolved); - if (current == null || 'redirect' in current.route) return; - currentPageComponent.value = current.route.component; - currentPageProps.value = current.props; - key.value = newKey + JSON.stringify(Object.fromEntries(current.props)); + if (resolved == null || 'redirect' in resolved.route) return; + currentPageComponent.value = resolved.route.component; + currentPageProps.value = resolved.props; + key.value = newKey + JSON.stringify(Object.fromEntries(resolved.props)); nextTick(() => { // ページ遷移完了後に再びキャッシュを有効化 @@ -79,11 +65,11 @@ router.addListener('change', onChange); // #region キャッシュ制御 /** - * キャッシュクリアが有効になったら、全キャッシュをクリアする - * - * keepAlive側にwatcherがあるのですぐ消えるとはおもうけど、念のためページ遷移完了まではキャッシュを無効化しておく。 - * キャッシュ有効時向けにexcludeを使いたい場合は、pageCacheControllerに並列に突っ込むのではなく、下に追記すること - */ + * キャッシュクリアが有効になったら、全キャッシュをクリアする + * + * keepAlive側にwatcherがあるのですぐ消えるとはおもうけど、念のためページ遷移完了まではキャッシュを無効化しておく。 + * キャッシュ有効時向けにexcludeを使いたい場合は、pageCacheControllerに並列に突っ込むのではなく、下に追記すること + */ const pageCacheController = computed(() => clearCacheRequested.value ? /.*/ : undefined); const clearCacheRequested = ref(false); diff --git a/packages/frontend/src/components/global/StackingRouterView.vue b/packages/frontend/src/components/global/StackingRouterView.vue new file mode 100644 index 0000000000..c9364bdffa --- /dev/null +++ b/packages/frontend/src/components/global/StackingRouterView.vue @@ -0,0 +1,191 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<TransitionGroup + :enterActiveClass="prefer.s.animation ? $style.transition_x_enterActive : ''" + :leaveActiveClass="prefer.s.animation ? $style.transition_x_leaveActive : ''" + :enterFromClass="prefer.s.animation ? $style.transition_x_enterFrom : ''" + :leaveToClass="prefer.s.animation ? $style.transition_x_leaveTo : ''" + :moveClass="prefer.s.animation ? $style.transition_x_move : ''" + :duration="200" + tag="div" :class="$style.root" +> + <div v-for="(tab, i) in tabs" :key="tab.key" :class="$style.tab" :style="{ '--i': i }"> + <div v-if="i > 0" :class="$style.tabBg" @click="back()"></div> + <div :class="$style.tabFg"> + <div :class="$style.tabContent" class="_pageContainer" @click.stop=""> + <Suspense :timeout="0"> + <component :is="tab.component" v-bind="Object.fromEntries(tab.props)"/> + + <template #fallback> + <MkLoading/> + </template> + </Suspense> + </div> + </div> + </div> +</TransitionGroup> +</template> + +<script lang="ts" setup> +import { inject, onBeforeUnmount, provide, ref, shallowRef, computed, nextTick } from 'vue'; +import type { IRouter, Resolved, RouteDef } from '@/nirax.js'; +import { prefer } from '@/preferences.js'; +import { globalEvents } from '@/events.js'; +import MkLoadingPage from '@/pages/_loading_.vue'; +import { DI } from '@/di.js'; +import { deepEqual } from '@/utility/deep-equal.js'; + +const props = defineProps<{ + router?: IRouter; +}>(); + +const router = props.router ?? inject(DI.router); + +if (router == null) { + throw new Error('no router provided'); +} + +const currentDepth = inject(DI.routerCurrentDepth, 0); +provide(DI.routerCurrentDepth, currentDepth + 1); + +const current = router.current!; +const key = ref(router.getCurrentKey() + JSON.stringify(Object.fromEntries(current.props))); + +const tabs = shallowRef([{ + key: key.value, + path: router.getCurrentPath(), + component: 'component' in current.route ? current.route.component : MkLoadingPage, + props: current.props, +}]); + +function onChange({ resolved, key: newKey }) { + const currentTab = tabs.value[tabs.value.length - 1]; + if (resolved == null || 'redirect' in resolved.route) return; + if (resolved.route.path === currentTab.path && deepEqual(resolved.props, currentTab.props)) return; + key.value = newKey + JSON.stringify(Object.fromEntries(resolved.props)); + + if (tabs.value.some(tab => tab.key === key.value)) { + const newTabs = []; + for (const tab of tabs.value) { + newTabs.push(tab); + + if (tab.key === key.value) { + break; + } + } + tabs.value = newTabs; + return; + } + + tabs.value = tabs.value.length >= prefer.s.numberOfPageCache ? [ + ...tabs.value.slice(1), + { + key: key.value, + path: router.getCurrentPath(), + component: resolved.route.component, + props: resolved.props, + }, + ] : [...tabs.value, { + key: key.value, + path: router.getCurrentPath(), + component: resolved.route.component, + props: resolved.props, + }]; +} + +function back() { + const last = tabs.value[tabs.value.length - 1]; + router.replace(last.path, last.key); + tabs.value = [...tabs.value.slice(0, tabs.value.length - 1)]; +} + +router.addListener('change', onChange); + +onBeforeUnmount(() => { + router.removeListener('change', onChange); +}); +</script> + +<style lang="scss" module> +.transition_x_move, +.transition_x_enterActive, +.transition_x_leaveActive { + .tabBg { + transition: opacity 0.2s cubic-bezier(0,.5,.5,1), transform 0.2s cubic-bezier(0,.5,.5,1) !important; + } + + .tabFg { + transition: opacity 0.2s cubic-bezier(0,.5,.5,1), transform 0.2s cubic-bezier(0,.5,.5,1) !important; + } +} +.transition_x_enterFrom, +.transition_x_leaveTo { + .tabBg { + opacity: 0; + } + + .tabFg { + opacity: 0; + transform: translateY(100px); + } +} +.transition_x_leaveActive { + .tabFg { + //position: absolute; + } +} + +.root { + position: relative; + height: 100%; + overflow: clip; +} + +.tabBg { + position: absolute; + z-index: 1; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #0003; + -webkit-backdrop-filter: var(--MI-blur, blur(3px)); + backdrop-filter: var(--MI-blur, blur(3px)); +} + +.tab { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + box-sizing: border-box; + + &:not(:nth-child(1)) { + .tabFg { + position: absolute; + z-index: 1; + bottom: 0; + left: 0; + width: 100%; + height: calc(100% - 20px * var(--i)); + } + } +} + +.tabFg { + position: relative; + height: 100%; + background: var(--MI_THEME-bg); + border-radius: 16px 16px 0 0; + overflow: clip; +} + +.tabContent { + height: 100%; +} +</style> diff --git a/packages/frontend/src/components/index.ts b/packages/frontend/src/components/index.ts index 66ac871f7d..c28c457e33 100644 --- a/packages/frontend/src/components/index.ts +++ b/packages/frontend/src/components/index.ts @@ -16,6 +16,8 @@ import MkTime from './global/MkTime.vue'; import MkUrl from './global/MkUrl.vue'; import I18n from './global/I18n.vue'; import RouterView from './global/RouterView.vue'; +import NestedRouterView from './global/NestedRouterView.vue'; +import StackingRouterView from './global/StackingRouterView.vue'; import MkLoading from './global/MkLoading.vue'; import MkError from './global/MkError.vue'; import MkAd from './global/MkAd.vue'; @@ -38,6 +40,8 @@ export default function(app: App) { export const components = { I18n: I18n, RouterView: RouterView, + NestedRouterView: NestedRouterView, + StackingRouterView: StackingRouterView, Mfm: Mfm, MkA: MkA, MkAcct: MkAcct, @@ -65,6 +69,8 @@ declare module '@vue/runtime-core' { export interface GlobalComponents { I18n: typeof I18n; RouterView: typeof RouterView; + NestedRouterView: typeof NestedRouterView; + StackingRouterView: typeof StackingRouterView; Mfm: typeof Mfm; MkA: typeof MkA; MkAcct: typeof MkAcct; diff --git a/packages/frontend/src/di.ts b/packages/frontend/src/di.ts index 192242984c..2afe35550d 100644 --- a/packages/frontend/src/di.ts +++ b/packages/frontend/src/di.ts @@ -10,4 +10,5 @@ export const DI = { routerCurrentDepth: Symbol() as InjectionKey<number>, router: Symbol() as InjectionKey<IRouter>, mock: Symbol() as InjectionKey<boolean>, + pageMetadata: Symbol() as InjectionKey<Ref<Record<string, any>>>, }; diff --git a/packages/frontend/src/page.ts b/packages/frontend/src/page.ts index f3ec09a16f..0107f17be4 100644 --- a/packages/frontend/src/page.ts +++ b/packages/frontend/src/page.ts @@ -5,6 +5,7 @@ import * as Misskey from 'misskey-js'; import { inject, isRef, onActivated, onBeforeUnmount, provide, ref, toValue, watch } from 'vue'; +import { DI } from './di.js'; import type { MaybeRefOrGetter, Ref } from 'vue'; export type PageMetadata = { @@ -31,9 +32,6 @@ const METADATA_KEY = Symbol('MetadataKey'); const setMetadata = (v: Ref<PageMetadata | null>): void => { provide<Ref<PageMetadata | null>>(METADATA_KEY, v); }; -const getMetadata = (): Ref<PageMetadata | null> | undefined => { - return inject<Ref<PageMetadata | null>>(METADATA_KEY); -}; export const definePage = (maybeRefOrGetterMetadata: MaybeRefOrGetter<PageMetadata>): void => { const metadataRef = ref(toValue(maybeRefOrGetterMetadata)); @@ -55,6 +53,8 @@ export const definePage = (maybeRefOrGetterMetadata: MaybeRefOrGetter<PageMetada onActivated(() => { receiver?.(metadataGetter); }); + + provide(DI.pageMetadata, metadataRef); }; export const provideMetadataReceiver = (receiver: PageMetadataReceiver): void => { @@ -64,8 +64,3 @@ export const provideMetadataReceiver = (receiver: PageMetadataReceiver): void => export const provideReactiveMetadata = (metadataRef: Ref<PageMetadata | null>): void => { setMetadata(metadataRef); }; - -export const injectReactiveMetadata = (): Ref<PageMetadata | null> => { - const metadataRef = getMetadata(); - return isRef(metadataRef) ? metadataRef : ref(null); -}; diff --git a/packages/frontend/src/pages/admin/_header_.vue b/packages/frontend/src/pages/admin/_header_.vue index 1382cad9a4..f073a4af7e 100644 --- a/packages/frontend/src/pages/admin/_header_.vue +++ b/packages/frontend/src/pages/admin/_header_.vue @@ -33,13 +33,13 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, onMounted, onUnmounted, ref, shallowRef, watch, nextTick } from 'vue'; +import { computed, onMounted, onUnmounted, ref, shallowRef, watch, nextTick, inject } from 'vue'; import tinycolor from 'tinycolor2'; import { scrollToTop } from '@@/js/scroll.js'; import { popupMenu } from '@/os.js'; import MkButton from '@/components/MkButton.vue'; import { globalEvents } from '@/events.js'; -import { injectReactiveMetadata } from '@/page.js'; +import { DI } from '@/di.js'; type Tab = { key?: string | null; @@ -66,7 +66,7 @@ const emit = defineEmits<{ (ev: 'update:tab', key: string); }>(); -const pageMetadata = injectReactiveMetadata(); +const pageMetadata = inject(DI.pageMetadata); const el = shallowRef<HTMLElement>(null); const tabRefs = {}; @@ -119,7 +119,7 @@ function onTabClick(tab: Tab, ev: MouseEvent): void { } const calcBg = () => { - const rawBg = pageMetadata.value?.bg ?? 'var(--MI_THEME-bg)'; + const rawBg = pageMetadata.value.bg ?? 'var(--MI_THEME-bg)'; const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); tinyBg.setAlpha(0.85); bg.value = tinyBg.toRgbString(); diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index 5c47ea8ddc..7e7467859b 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -25,16 +25,17 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSpacer> </div> <div v-if="!(narrow && currentPage?.route.name == null)" class="main"> - <RouterView nested/> + <NestedRouterView/> </div> </div> </template> <script lang="ts" setup> import { onActivated, onMounted, onUnmounted, provide, watch, ref, computed } from 'vue'; +import type { SuperMenuDef } from '@/components/MkSuperMenu.vue'; +import type { PageMetadata } from '@/page.js'; import { i18n } from '@/i18n.js'; import MkSuperMenu from '@/components/MkSuperMenu.vue'; -import type { SuperMenuDef } from '@/components/MkSuperMenu.vue'; import MkInfo from '@/components/MkInfo.vue'; import { instance } from '@/instance.js'; import { lookup } from '@/utility/lookup.js'; @@ -42,7 +43,6 @@ import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { lookupUser, lookupUserByEmail, lookupFile } from '@/utility/admin-lookup.js'; import { definePage, provideMetadataReceiver, provideReactiveMetadata } from '@/page.js'; -import type { PageMetadata } from '@/page.js'; import { useRouter } from '@/router/supplier.js'; const isEmpty = (x: string | null) => x == null || x === ''; diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index ef148dd9af..ea1b714aed 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -20,8 +20,8 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> <div v-if="!(narrow && currentPage?.route.name == null)" class="main"> - <div class="bkzroven" style="container-type: inline-size;"> - <RouterView nested/> + <div style="container-type: inline-size;"> + <NestedRouterView/> </div> </div> </div> diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue index 3d425ff406..d81c6fc93d 100644 --- a/packages/frontend/src/pages/settings/other.vue +++ b/packages/frontend/src/pages/settings/other.vue @@ -91,6 +91,9 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="skipNoteRender"> <template #label>Enable note render skipping</template> </MkSwitch> + <MkSwitch v-model="stackingRouterView"> + <template #label>Enable stacking router view</template> + </MkSwitch> </div> </MkFolder> </SearchMarker> @@ -142,6 +145,7 @@ const reportError = prefer.model('reportError'); const enableCondensedLine = prefer.model('enableCondensedLine'); const skipNoteRender = prefer.model('skipNoteRender'); const devMode = prefer.model('devMode'); +const stackingRouterView = prefer.model('experimental.stackingRouterView'); watch(skipNoteRender, async () => { await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true }); diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index b8f5ece02b..68f4b7a26d 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { computed, watch, provide, shallowRef, ref, onMounted, onActivated } from 'vue'; -import { getScrollContainer, scroll } from '@@/js/scroll.js'; +import { scroll } from '@@/js/scroll.js'; import type { Tab } from '@/components/global/MkPageHeader.tabs.vue'; import type { MenuItem } from '@/types/menu.js'; import type { BasicTimelineType } from '@/timelines.js'; @@ -56,15 +56,9 @@ import { deepMerge } from '@/utility/merge.js'; import { miLocalStorage } from '@/local-storage.js'; import { availableBasicTimelines, hasWithReplies, isAvailableBasicTimeline, isBasicTimeline, basicTimelineIconClass } from '@/timelines.js'; import { prefer } from '@/preferences.js'; -import { useScrollPositionManager } from '@/nirax.js'; -import { useRouter } from '@/router/supplier.js'; provide('shouldOmitHeaderTitle', true); -const router = useRouter(); - -useScrollPositionManager(() => getScrollContainer(rootEl.value), router); - const tlComponent = shallowRef<InstanceType<typeof MkTimeline>>(); const rootEl = shallowRef<HTMLElement>(); diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts index 9ad604ae72..dd1e39ab06 100644 --- a/packages/frontend/src/preferences/def.ts +++ b/packages/frontend/src/preferences/def.ts @@ -367,4 +367,8 @@ export const PREF_DEF = { sfxVolume: 1, }, }, + + 'experimental.stackingRouterView': { + default: false, + }, } satisfies PreferencesDefinition; diff --git a/packages/frontend/src/ui/deck/main-column.vue b/packages/frontend/src/ui/deck/main-column.vue index 5de5d0a866..b4d494fb09 100644 --- a/packages/frontend/src/ui/deck/main-column.vue +++ b/packages/frontend/src/ui/deck/main-column.vue @@ -12,8 +12,9 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </template> - <div class="_pageContainer" style="height: 100%;"> - <RouterView @contextmenu.stop="onContextmenu"/> + <div style="height: 100%;"> + <StackingRouterView v-if="prefer.s['experimental.stackingRouterView']" @contextmenu.stop="onContextmenu"/> + <RouterView v-else @contextmenu.stop="onContextmenu"/> </div> </XColumn> </template> diff --git a/packages/frontend/src/ui/minimum.vue b/packages/frontend/src/ui/minimum.vue index ebaa46850c..7b58829ff5 100644 --- a/packages/frontend/src/ui/minimum.vue +++ b/packages/frontend/src/ui/minimum.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div :class="$style.root"> - <div class="_pageContainer" style="height: 100%;"> + <div style="height: 100%;"> <RouterView/> </div> diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index 28c05ec748..be933d5324 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -13,8 +13,9 @@ SPDX-License-Identifier: AGPL-3.0-only <XAnnouncements v-if="$i"/> <XStatusBars :class="$style.statusbars"/> </div> - <div :class="$style.content" class="_pageContainer"> - <RouterView/> + <div :class="$style.content"> + <StackingRouterView v-if="prefer.s['experimental.stackingRouterView']"/> + <RouterView v-else/> </div> <div v-if="isMobile" ref="navFooter" :class="$style.nav"> <button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i :class="$style.navButtonIcon" class="ti ti-menu-2"></i><span v-if="menuIndicated" :class="$style.navButtonIndicator" class="_blink"><i class="_indicatorCircle"></i></span></button> diff --git a/packages/frontend/src/ui/visitor.vue b/packages/frontend/src/ui/visitor.vue index c5b7e0663a..976c3584a7 100644 --- a/packages/frontend/src/ui/visitor.vue +++ b/packages/frontend/src/ui/visitor.vue @@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only <button v-if="!isRoot" :class="$style.homeButton" class="_button" @click="goHome"> <i class="ti ti-home"></i> </button> - <div :class="$style.content" class="_pageContainer"> + <div :class="$style.content"> <RouterView/> </div> </div> diff --git a/packages/frontend/src/ui/zen.vue b/packages/frontend/src/ui/zen.vue index 45524d1fbb..d60611a19c 100644 --- a/packages/frontend/src/ui/zen.vue +++ b/packages/frontend/src/ui/zen.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div :class="$style.root"> <div :class="$style.contents"> - <div class="_pageContainer" style="flex: 1; min-height: 0;"> + <div style="flex: 1; min-height: 0;"> <RouterView/> </div> diff --git a/packages/frontend/src/utility/autogen/settings-search-index.ts b/packages/frontend/src/utility/autogen/settings-search-index.ts index 0c83e72d82..fd92876880 100644 --- a/packages/frontend/src/utility/autogen/settings-search-index.ts +++ b/packages/frontend/src/utility/autogen/settings-search-index.ts @@ -572,7 +572,7 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['experimental', 'feature', 'flags'], }, { - id: '54wETGawJ', + id: 'zWbGKohZ2', label: i18n.ts.developer, keywords: ['developer', 'mode', 'debug'], },