From d6ae4c980ba0e7893af4a2c327cad367cda24e5c Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 29 Apr 2025 09:43:15 +0900
Subject: feat(frontend): タイトルバーを表示できるように
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/ui/deck.vue | 144 ++++++++++++++++++++------------------
 1 file changed, 76 insertions(+), 68 deletions(-)

(limited to 'packages/frontend/src/ui/deck.vue')

diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index 7556f513c2..f702e8ef44 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -4,71 +4,75 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div :class="[$style.root, { [$style.withWallpaper]: withWallpaper }]">
-	<XSidebar v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'left'"/>
-
-	<div :class="$style.main">
-		<XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'top'"/>
-
-		<XAnnouncements v-if="$i"/>
-		<XStatusBars/>
-		<div :class="$style.columnsWrapper">
-			<!-- passive: https://bugs.webkit.org/show_bug.cgi?id=281300 -->
-			<div ref="columnsEl" :class="[$style.columns, { [$style.center]: prefer.r['deck.columnAlign'].value === 'center', [$style.snapScroll]: snapScroll }]" @contextmenu.self.prevent="onContextmenu" @wheel.passive.self="onWheel">
-				<!-- sectionを利用しているのは、deck.vue側でcolumnに対してfirst-of-typeを効かせるため -->
-				<section
-					v-for="ids in layout"
-					:class="$style.section"
-					:style="columns.filter(c => ids.includes(c.id)).some(c => c.flexible) ? { flex: 1, minWidth: '350px' } : { width: Math.max(...columns.filter(c => ids.includes(c.id)).map(c => c.width)) + 'px' }"
-					@wheel.passive.self="onWheel"
-				>
-					<component
-						:is="columnComponents[columns.find(c => c.id === id)!.type] ?? XTlColumn"
-						v-for="id in ids"
-						:ref="id"
-						:key="id"
-						:class="[$style.column, { '_shadow': withWallpaper }]"
-						:column="columns.find(c => c.id === id)!"
-						:isStacked="ids.length > 1"
-						@headerWheel="onWheel"
-					/>
-				</section>
-				<div v-if="layout.length === 0" class="_panel" :class="$style.onboarding">
-					<div>{{ i18n.ts._deck.introduction }}</div>
-					<div>{{ i18n.ts._deck.introduction2 }}</div>
+<div :class="[$style.root]">
+	<XTitlebar v-if="prefer.r.showTitlebar.value" style="flex-shrink: 0;"/>
+
+	<div :class="$style.nonTitlebarArea">
+		<XSidebar v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'left'"/>
+
+		<div :class="[$style.main, { [$style.withWallpaper]: withWallpaper, [$style.withSidebarAndTitlebar]: !isMobile && prefer.r['deck.navbarPosition'].value === 'left' && prefer.r.showTitlebar.value }]" :style="{ backgroundImage: prefer.s['deck.wallpaper'] != null ? `url(${ prefer.s['deck.wallpaper'] })` : null }">
+			<XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'top'"/>
+
+			<XAnnouncements v-if="$i"/>
+			<XStatusBars/>
+			<div :class="$style.columnsWrapper">
+				<!-- passive: https://bugs.webkit.org/show_bug.cgi?id=281300 -->
+				<div ref="columnsEl" :class="[$style.columns, { [$style.center]: prefer.r['deck.columnAlign'].value === 'center', [$style.snapScroll]: snapScroll }]" @contextmenu.self.prevent="onContextmenu" @wheel.passive.self="onWheel">
+					<!-- sectionを利用しているのは、deck.vue側でcolumnに対してfirst-of-typeを効かせるため -->
+					<section
+						v-for="ids in layout"
+						:class="$style.section"
+						:style="columns.filter(c => ids.includes(c.id)).some(c => c.flexible) ? { flex: 1, minWidth: '350px' } : { width: Math.max(...columns.filter(c => ids.includes(c.id)).map(c => c.width)) + 'px' }"
+						@wheel.passive.self="onWheel"
+					>
+						<component
+							:is="columnComponents[columns.find(c => c.id === id)!.type] ?? XTlColumn"
+							v-for="id in ids"
+							:ref="id"
+							:key="id"
+							:class="[$style.column, { '_shadow': withWallpaper }]"
+							:column="columns.find(c => c.id === id)!"
+							:isStacked="ids.length > 1"
+							@headerWheel="onWheel"
+						/>
+					</section>
+					<div v-if="layout.length === 0" class="_panel" :class="$style.onboarding">
+						<div>{{ i18n.ts._deck.introduction }}</div>
+						<div>{{ i18n.ts._deck.introduction2 }}</div>
+					</div>
+				</div>
+
+				<div v-if="prefer.r['deck.menuPosition'].value === 'right'" :class="$style.sideMenu">
+					<div :class="$style.sideMenuTop">
+						<button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.sideMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button>
+						<button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.sideMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button>
+					</div>
+					<div :class="$style.sideMenuMiddle">
+						<button v-tooltip.noDelay.left="i18n.ts._deck.addColumn" :class="$style.sideMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button>
+					</div>
+					<div :class="$style.sideMenuBottom">
+						<button v-tooltip.noDelay.left="i18n.ts.settings" :class="$style.sideMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings-2"></i></button>
+					</div>
 				</div>
 			</div>
 
-			<div v-if="prefer.r['deck.menuPosition'].value === 'right'" :class="$style.sideMenu">
-				<div :class="$style.sideMenuTop">
-					<button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.sideMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button>
-					<button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.sideMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button>
+			<div v-if="prefer.r['deck.menuPosition'].value === 'bottom'" :class="$style.bottomMenu">
+				<div :class="$style.bottomMenuLeft">
+					<button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.bottomMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button>
+					<button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.bottomMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button>
 				</div>
-				<div :class="$style.sideMenuMiddle">
-					<button v-tooltip.noDelay.left="i18n.ts._deck.addColumn" :class="$style.sideMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button>
+				<div :class="$style.bottomMenuMiddle">
+					<button v-tooltip.noDelay.left="i18n.ts._deck.addColumn" :class="$style.bottomMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button>
 				</div>
-				<div :class="$style.sideMenuBottom">
-					<button v-tooltip.noDelay.left="i18n.ts.settings" :class="$style.sideMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings-2"></i></button>
+				<div :class="$style.bottomMenuRight">
+					<button v-tooltip.noDelay.left="i18n.ts.settings" :class="$style.bottomMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings-2"></i></button>
 				</div>
 			</div>
-		</div>
 
-		<div v-if="prefer.r['deck.menuPosition'].value === 'bottom'" :class="$style.bottomMenu">
-			<div :class="$style.bottomMenuLeft">
-				<button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.bottomMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button>
-				<button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.bottomMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button>
-			</div>
-			<div :class="$style.bottomMenuMiddle">
-				<button v-tooltip.noDelay.left="i18n.ts._deck.addColumn" :class="$style.bottomMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button>
-			</div>
-			<div :class="$style.bottomMenuRight">
-				<button v-tooltip.noDelay.left="i18n.ts.settings" :class="$style.bottomMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings-2"></i></button>
-			</div>
-		</div>
-
-		<XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'bottom'"/>
+			<XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'bottom'"/>
 
-		<XMobileFooterMenu v-if="isMobile" v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/>
+			<XMobileFooterMenu v-if="isMobile" v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/>
+		</div>
 	</div>
 
 	<XCommon v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/>
@@ -82,6 +86,7 @@ import XCommon from './_common_/common.vue';
 import XSidebar from '@/ui/_common_/navbar.vue';
 import XNavbarH from '@/ui/_common_/navbar-h.vue';
 import XMobileFooterMenu from '@/ui/_common_/mobile-footer-menu.vue';
+import XTitlebar from '@/ui/_common_/titlebar.vue';
 import * as os from '@/os.js';
 import { $i } from '@/i.js';
 import { i18n } from '@/i18n.js';
@@ -209,30 +214,25 @@ async function deleteProfile() {
 
 window.document.documentElement.style.overflowY = 'hidden';
 window.document.documentElement.style.scrollBehavior = 'auto';
-
-if (prefer.s['deck.wallpaper'] != null) {
-	window.document.documentElement.style.backgroundImage = `url(${prefer.s['deck.wallpaper']})`;
-}
 </script>
 
 <style lang="scss" module>
 .root {
-	$nav-hide-threshold: 650px; // TODO: どこかに集約したい
-
 	--MI-margin: var(--MI-marginHalf);
 
 	--columnGap: v-bind("gap + 'px'");
 
 	display: flex;
+	flex-direction: column;
 	height: 100dvh;
 	box-sizing: border-box;
 	flex: 1;
+	background: var(--MI_THEME-navBg);
+}
 
-	&.withWallpaper {
-		.main {
-			background: transparent;
-		}
-	}
+.nonTitlebarArea {
+	display: flex;
+	flex: 1;
 }
 
 .main {
@@ -240,7 +240,15 @@ if (prefer.s['deck.wallpaper'] != null) {
 	min-width: 0;
 	display: flex;
 	flex-direction: column;
-	background: var(--MI_THEME-deckBg);
+
+	&:not(.withWallpaper) {
+		background: var(--MI_THEME-deckBg);
+	}
+
+	&.withSidebarAndTitlebar {
+		border-radius: 12px 0 0 0;
+		overflow: clip;
+	}
 }
 
 .columnsWrapper {
-- 
cgit v1.2.3-freya

