feat(assets): add initial fonts and image assets

This commit is contained in:
2026-06-09 15:47:20 +08:00
parent cda55c8f9b
commit e2f60bc4f2
41 changed files with 207 additions and 70 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 KiB

+35 -1
View File
@@ -1,5 +1,6 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import localFont from "next/font/local";
import Script from "next/script";
import "./globals.css";
@@ -15,6 +16,39 @@ const geistMono = Geist_Mono({
subsets: ["latin"],
});
/**
* Athelas 字体加载
* 原始资源:/Users/chase/Documents/cozsweet/fonts/Athelas-*.ttf
* 放置位置:/public/fonts/Athelas-*.ttf
*
* 路径计算(`next/font/local` 要求相对路径从调用文件出发):
* src/app/layout.tsx → ../../public/fonts/...
*
* 通过 `next/font/local` 自动 preload、压缩、inline。
* 输出 CSS 变量 `--font-athelas`,由 `globals.css` 桥接到 Tailwind `font-athelas`。
*/
const athelas = localFont({
src: [
{
path: "../../public/fonts/Athelas-Regular.ttf",
weight: "400",
style: "normal",
},
{
path: "../../public/fonts/Athelas-Bold.ttf",
weight: "700",
style: "normal",
},
{
path: "../../public/fonts/Athelas-BoldItalic.ttf",
weight: "700",
style: "italic",
},
],
variable: "--font-athelas",
display: "swap",
});
export const metadata: Metadata = {
title: "cozsweet",
description: "cozsweet frontend (Next.js)",
@@ -31,7 +65,7 @@ export default function RootLayout({
<html
lang="en"
suppressHydrationWarning
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
className={`${geistSans.variable} ${geistMono.variable} ${athelas.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col">
{/* Facebook SDK JSauth + splash 入口会用到 FB.login */}
@@ -1,8 +1,12 @@
.bg {
position: absolute;
inset: 0;
background:
radial-gradient(circle at 80% 20%, rgba(248, 77, 150, 0.25), transparent 60%),
linear-gradient(180deg, #1a0f2a 0%, #0d0b14 100%);
z-index: 0;
background: var(--color-sidebar-background);
overflow: hidden;
}
.image {
object-fit: cover;
object-position: center;
}
@@ -1,14 +1,18 @@
"use client";
/**
* Splash 背景
*
* 原始 Dart: lib/ui/splash/widgets/splash_background.dart
* 使用 `Assets.images.picBgHome.image(fit: BoxFit.cover)` 渲染全屏背景图。
*
* 当前实现:纯色 + CSS 渐变占位(待 `public/splash/bg.png` 资源到位后可切换为 `<Image>`)。
*/
import Image from "next/image";
import styles from "./splash-background.module.css";
export function SplashBackground() {
return <div className={styles.bg} aria-hidden="true" />;
return (
<div className={styles.bg}>
<Image
src="/images/splash/pic-bg-home.png"
alt=""
fill
priority
sizes="100vw"
className={styles.image}
/>
</div>
);
}
@@ -1,7 +1,61 @@
.wrapper {
display: flex;
flex-direction: column;
gap: var(--spacing-md);
flex-direction: row;
align-items: center;
justify-content: center;
gap: var(--spacing-26);
width: 100%;
z-index: 2;
}
.skip {
font-family: var(--font-athelas);
font-size: var(--font-size-xxl);
font-weight: 700;
color: #ffffff;
background: transparent;
border: 0;
cursor: pointer;
padding: 0;
}
.skip:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.separator {
width: var(--spacing-26);
height: 1px;
}
.facebookButton {
height: 48px;
padding: 0 30px;
border: 0;
border-radius: 24px;
background: linear-gradient(
to right,
#fff96ade,
#fff657a0
);
color: #ffffff;
font-weight: 700;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 10px rgba(248, 89, 168, 0.3);
}
.facebookButton:disabled {
cursor: not-allowed;
opacity: 0.7;
}
.facebookLabel {
font-family: var(--font-athelas);
font-size: var(--font-size-xxl);
font-weight: 700;
color: #ffffff;
}
+45 -17
View File
@@ -1,35 +1,63 @@
"use client";
/**
* Splash 底部按钮组Skip + Facebook 登录)
* Splash 底部按钮组
*
* 原始 Dart: lib/ui/splash/widgets/splash_button.dart
* `Row(children: [Skip 文本, SizedBox 26, Facebook 登录按钮])`
*
* 关键对齐点:
* - 仅两个元素:Skip 文本 + Facebook 登录按钮
* - 居中布局 (MainAxisAlignment.center)
* - Facebook 按钮:带渐变(primaryGradient+ 圆角 24 + 高度 48
* - 加载中:按钮内显示 spinner
*/
import { useAuthState } from "@/contexts/auth/auth-context";
import { AuthSocialButtons } from "@/app/auth/components/auth-social-buttons";
import { AuthPrimaryButton } from "@/app/auth/components/auth-primary-button";
import { AuthDivider } from "@/app/auth/components/auth-divider";
import { useAuthDispatch } from "@/contexts/auth/auth-context";
import { useAuthState, useAuthDispatch } from "@/contexts/auth/auth-context";
import { LoadingIndicator } from "@/app/_components/core/loading-indicator";
import styles from "./splash-button.module.css";
export interface SplashButtonProps {
onSkip: () => void;
onFacebookLogin: () => void;
}
export function SplashButton({ onSkip }: SplashButtonProps) {
export function SplashButton({
onSkip,
onFacebookLogin,
}: SplashButtonProps) {
const state = useAuthState();
const dispatch = useAuthDispatch();
const isLoading = state.isLoading;
return (
<div className={styles.wrapper}>
<AuthSocialButtons
loadingProvider={state.isLoading ? "facebook" : null}
onFacebook={() => dispatch({ type: "AuthFacebookLoginSubmitted" })}
onGoogle={() => dispatch({ type: "AuthGoogleLoginSubmitted" })}
/>
<AuthDivider label="or" />
<AuthPrimaryButton onClick={onSkip} variant="primary">
Skip Continue as Guest
</AuthPrimaryButton>
{/* Skip 文本按钮 */}
<button
type="button"
onClick={onSkip}
className={styles.skip}
disabled={isLoading}
>
Skip
</button>
<div className={styles.separator} aria-hidden="true" />
{/* Facebook 登录按钮(带渐变) */}
<button
type="button"
onClick={() => {
dispatch({ type: "AuthFacebookLoginSubmitted" });
onFacebookLogin();
}}
disabled={isLoading}
className={styles.facebookButton}
>
{isLoading ? (
<LoadingIndicator color="#ffffff" />
) : (
<span className={styles.facebookLabel}>Login with Facebook</span>
)}
</button>
</div>
);
}
@@ -2,21 +2,17 @@
display: flex;
flex-direction: column;
gap: var(--spacing-md);
color: var(--color-text-primary);
z-index: 2;
}
.title {
margin: 0;
font-family: var(--font-athelas);
font-size: var(--font-size-26);
font-weight: 600;
line-height: 1.3;
}
.subtitle {
font-size: var(--font-size-title);
font-weight: 700;
font-style: italic;
line-height: 1.4;
color: #ffffff;
text-align: left;
white-space: pre-line;
margin: 0;
font-size: var(--font-size-md);
line-height: 1.5;
color: rgba(255, 255, 255, 0.8);
}
+14 -5
View File
@@ -1,18 +1,27 @@
"use client";
/**
* Splash 内容文案
* Splash 内容文案
*
* 原始 Dart: lib/ui/splash/widgets/splash_content.dart
*
* 关键差异:
* - 使用 Athelas 字体(衬线体)
* - 标题加粗 + 斜体(与原 Dart 一致)
* - 颜色:白色(背景图上需高对比度)
*/
import styles from "./splash-content.module.css";
const APOSTROPHE = "&#39;";
export function SplashContent() {
return (
<div className={styles.content}>
<h1 className={styles.title}>Chat with Elio, anytime, anywhere</h1>
<p className={styles.subtitle}>
A safe, private space to talk, share, and unwind your AI companion who&apos;s always there.
</p>
<h1 className={styles.title}>
Welcome to my secret hideout~
{"\n"}It{APOSTROPHE}s just the two of us now.
{"\n"}Feel free to whisper your
{"\n"}little secrets.
</h1>
</div>
);
}
@@ -1,20 +1,12 @@
.logo {
display: flex;
flex-direction: column;
gap: var(--spacing-xs);
color: var(--color-text-primary);
display: inline-flex;
z-index: 2;
}
.brand {
font-family: var(--font-athelas);
font-size: var(--font-size-title-x-large);
font-weight: 700;
letter-spacing: -0.02em;
line-height: 1;
}
.tagline {
font-size: var(--font-size-md);
color: var(--color-text-secondary);
.image {
display: block;
width: auto;
height: auto;
/* Apple 平台 Athelas 渲染时仍可能需要 fallback */
font-family: var(--font-athelas, serif);
}
+12 -5
View File
@@ -3,17 +3,24 @@
* Splash Logo
*
* 原始 Dart: lib/ui/splash/widgets/splash_logo.dart
*
* 当前实现:纯文字 logo"cozsweet" + tagline)。
* 资源:`Assets.images.icLogoHome`(待替换为 `<Image src="/splash/logo.svg" />`)。
* 资源: /public/images/splash/ic-logo-home.png (kebab-case 重命名后)
* 原名: ic_logo_home.png (snake_case)
* 渲染: Assets.images.icLogoHome.image() → next/image <Image>
*/
import Image from "next/image";
import styles from "./splash-logo.module.css";
export function SplashLogo() {
return (
<div className={styles.logo}>
<span className={styles.brand}>cozsweet</span>
<span className={styles.tagline}>Your exclusive AI boyfriend</span>
<Image
src="/images/splash/ic-logo-home.png"
alt="cozsweet"
width={120}
height={40}
priority
className={styles.image}
/>
</div>
);
}
@@ -8,6 +8,7 @@
overflow: hidden;
}
/* 渐变:accent (bottom-left) → transparent (center-right) */
.gradientOverlay {
position: absolute;
inset: 0;
@@ -16,7 +17,7 @@
var(--color-accent),
transparent 60%
);
opacity: 0.4;
opacity: 0.5;
z-index: 1;
pointer-events: none;
}
@@ -40,7 +41,9 @@
.bottom {
margin: var(--spacing-xxxl) 0 0 0;
font-family: var(--font-athelas);
font-size: var(--font-size-md);
font-weight: 400;
line-height: 1.5;
color: rgba(255, 255, 255, 0.9);
text-align: center;
+7 -1
View File
@@ -42,13 +42,19 @@ export function SplashScreen() {
<MobileShell background="var(--color-sidebar-background)">
<div className={styles.wrapper}>
<SplashBackground />
{/* 渐变叠加层:accent → transparent (bottom-left → center-right) */}
<div className={styles.gradientOverlay} aria-hidden="true" />
<div className={styles.content}>
<SplashLogo />
<div className={styles.spacer} />
<SplashContent />
<div className={styles.buttonArea}>
<SplashButton onSkip={() => router.push(ROUTES.chat)} />
<SplashButton
onSkip={() => router.push(ROUTES.chat)}
onFacebookLogin={() => {
/* Facebook 登录触发由 SplashButton 内部 dispatch */
}}
/>
</div>
<p className={styles.bottom}>
Elio Silvestri, Your exclusive AI boyfriend