lovart3/app/layout.tsx
Your Name 7526a0159c feat: 完善图片编辑功能和UI优化
- 修复浏览器favicon未更新问题(使用settings.favicon)
- 将'图片标注'组件名改为'图片编辑'(所有相关文本和注释)
- 在顶部导航栏添加'图片编辑'按钮,支持上传图片进行编辑
- 图片编辑完成后自动添加到聊天输入框(通过sessionStorage传递)
- 优化图片编辑组件下载按钮UI(更明显的样式,添加文字标签)
- 图片处理结果(变清晰/抠图/图像拓展)自动保存到相册和生图记录
- 优化图片编辑确认逻辑,优先使用处理后的图片
2025-12-29 11:24:47 +08:00

134 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import { Toaster } from "sonner";
import { ThemeProvider } from "@/components/theme-provider";
import "./globals.css";
import { SessionProvider } from "next-auth/react";
import { getSiteSettings } from "@/lib/db/queries";
export async function generateMetadata(): Promise<Metadata> {
const settings = await getSiteSettings();
return {
title: {
template: `%s | ${settings.siteName}`,
default: settings.siteName,
},
description: settings.siteName,
icons: {
icon: settings.favicon || "/favicon.ico",
apple: settings.favicon || "/favicon.ico",
},
};
}
export const viewport = {
maximumScale: 1, // Disable auto-zoom on mobile Safari
};
const geist = Geist({
subsets: ["latin"],
display: "swap",
variable: "--font-geist",
});
const geistMono = Geist_Mono({
subsets: ["latin"],
display: "swap",
variable: "--font-geist-mono",
});
const LIGHT_THEME_COLOR = "hsl(0 0% 100%)";
const DARK_THEME_COLOR = "hsl(240deg 10% 3.92%)";
const THEME_COLOR_SCRIPT = `\
(function() {
var html = document.documentElement;
var meta = document.querySelector('meta[name="theme-color"]');
if (!meta) {
meta = document.createElement('meta');
meta.setAttribute('name', 'theme-color');
document.head.appendChild(meta);
}
function updateThemeColor() {
var isDark = html.classList.contains('dark');
meta.setAttribute('content', isDark ? '${DARK_THEME_COLOR}' : '${LIGHT_THEME_COLOR}');
}
var observer = new MutationObserver(updateThemeColor);
observer.observe(html, { attributes: true, attributeFilter: ['class'] });
updateThemeColor();
})();`;
const CHUNK_ERROR_RECOVERY_SCRIPT = `\
(function() {
var hasReloaded = sessionStorage.getItem('chunk-reload-attempted');
window.addEventListener('error', function(event) {
var isChunkError = event.message && (
event.message.includes('Failed to load chunk') ||
event.message.includes('Loading chunk') ||
event.message.includes('/_next/static/chunks/')
);
if (isChunkError && !hasReloaded) {
console.warn('[ChunkError] 检测到chunk加载失败,自动刷新页面...');
sessionStorage.setItem('chunk-reload-attempted', 'true');
setTimeout(function() {
window.location.reload();
}, 1000);
}
}, true);
// 页面加载成功后清除标记
window.addEventListener('load', function() {
setTimeout(function() {
sessionStorage.removeItem('chunk-reload-attempted');
}, 2000);
});
})();`;
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html
className={`${geist.variable} ${geistMono.variable}`}
// `next-themes` injects an extra classname to the body element to avoid
// visual flicker before hydration. Hence the `suppressHydrationWarning`
// prop is necessary to avoid the React hydration mismatch warning.
// https://github.com/pacocoursey/next-themes?tab=readme-ov-file#with-app
lang="zh-CN"
suppressHydrationWarning
>
<head>
<script
// biome-ignore lint/security/noDangerouslySetInnerHtml: "Required"
dangerouslySetInnerHTML={{
__html: THEME_COLOR_SCRIPT,
}}
/>
<script
// biome-ignore lint/security/noDangerouslySetInnerHtml: "Required"
dangerouslySetInnerHTML={{
__html: CHUNK_ERROR_RECOVERY_SCRIPT,
}}
/>
</head>
<body className="antialiased">
<ThemeProvider
attribute="class"
defaultTheme="system"
disableTransitionOnChange
enableSystem
>
<Toaster position="top-center" />
<SessionProvider>{children}</SessionProvider>
</ThemeProvider>
</body>
</html>
);
}