Compare commits

..

No commits in common. "v0.2.2" and "v0.2.1" have entirely different histories.

8 changed files with 28 additions and 42 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 358 KiB

View File

@ -35,7 +35,7 @@ export default function HeroBanner({
if (!v || !videoSrc) return; if (!v || !videoSrc) return;
v.muted = isMuted; v.muted = isMuted;
v.play().catch(() => {}); v.play().catch(() => {});
// 仅在 videoSrc 变化时执行 · 不依赖 isMuted(mute 切换由按钮处理) // 仅在 videoSrc 变化时执行 · 不依赖 isMutedmute 切换由按钮处理)
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [videoSrc]); }, [videoSrc]);
@ -93,7 +93,7 @@ export default function HeroBanner({
{/* Eyebrow 左上 · 紧贴导航下方 */} {/* Eyebrow 左上 · 紧贴导航下方 */}
<div className="absolute top-[6.5rem] sm:top-[7.5rem] left-4 sm:left-6 lg:left-8 z-10"> <div className="absolute top-[6.5rem] sm:top-[7.5rem] left-4 sm:left-6 lg:left-8 z-10">
<p className="font-label text-[10px] sm:text-xs tracking-[0.4em] uppercase text-purple-200/90"> <p className="font-label text-[10px] sm:text-xs tracking-[0.4em] uppercase text-purple-200/90">
Top 12 · Cyber Star Debut Survival Top 12 · Virtual Idol Debut Project
</p> </p>
</div> </div>

View File

@ -25,10 +25,9 @@ export default function Logo({
// 用原生 <img> 绕开 Next/Image 的格式转换 —— 某些环境下 sharp 把透明 PNG // 用原生 <img> 绕开 Next/Image 的格式转换 —— 某些环境下 sharp 把透明 PNG
// 转 webp/avif 时会铺白底,导致 logo 在深色 nav 上出现白色矩形。 // 转 webp/avif 时会铺白底,导致 logo 在深色 nav 上出现白色矩形。
// ?v=2 缓存破坏:logo 改版时 +1,浏览器立刻拉新版而不读老缓存。
const inner = ( const inner = (
<img <img
src="/logo.png?v=3" src="/logo.png"
alt="CYBER STAR" alt="CYBER STAR"
decoding="async" decoding="async"
draggable={false} draggable={false}
@ -36,6 +35,8 @@ export default function Logo({
height: `${h}px`, height: `${h}px`,
width: "auto", width: "auto",
background: "transparent", background: "transparent",
// 保留紫色辉光,但 drop-shadow 不会引入白底
filter: "drop-shadow(0 0 14px rgba(139,92,246,0.4))",
}} }}
className={`block select-none ${className}`} className={`block select-none ${className}`}
/> />

View File

@ -2,6 +2,7 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import Logo from "./Logo";
import NavLinks from "./NavLinks"; import NavLinks from "./NavLinks";
import SearchTrigger from "./SearchTrigger"; import SearchTrigger from "./SearchTrigger";
import AuthMenu from "./auth/AuthMenu"; import AuthMenu from "./auth/AuthMenu";
@ -81,7 +82,9 @@ export default function Navigation() {
)} )}
/> />
<nav className="relative max-w-[1500px] mx-auto h-20 px-4 sm:px-6 lg:px-8 flex items-center gap-8"> <nav className="relative max-w-[1500px] mx-auto h-20 px-4 sm:px-6 lg:px-8 flex items-center gap-8">
{/* 左侧:首页 / 排行榜 / 我的(logo 已移除) */} <Logo size="md" />
{/* 中部:首页 / 排行榜 / 我的 */}
<NavLinks className="hidden md:flex" /> <NavLinks className="hidden md:flex" />
{/* 右侧:搜索 + 今日余票 + 登录/注册 (或 头像+下拉) */} {/* 右侧:搜索 + 今日余票 + 登录/注册 (或 头像+下拉) */}

View File

@ -40,8 +40,8 @@ export default function ArtistCard({
className="block" className="block"
aria-label={`查看 ${artist.name} 详情`} aria-label={`查看 ${artist.name} 详情`}
> >
{/* 立绘区 · Top12 区分仅靠紫色边框 + 辉光,不再降低非 Top12 卡片亮度 */} {/* 立绘区13+ 卡片轻度暗化) */}
<div className="relative aspect-[4/5]"> <div className={cn("relative aspect-[4/5]", !inTop12 && "opacity-[0.78]")}>
<ArtistPortrait <ArtistPortrait
artist={artist} artist={artist}
rounded="rounded-none" rounded="rounded-none"
@ -59,6 +59,9 @@ export default function ArtistCard({
> >
{artist.rank} {artist.rank}
</div> </div>
{/* 顶部轻微渐变蒙层 */}
<div className="absolute inset-x-0 top-0 h-12 bg-gradient-to-b from-black/40 to-transparent pointer-events-none" />
</div> </div>
{/* 信息区(黑色背景明显分隔) */} {/* 信息区(黑色背景明显分隔) */}

View File

@ -324,7 +324,7 @@ export const ARTIST_SEEDS: ArtistSeed[] = [
{ {
no: `021`, no: `021`,
name: `温景然`, name: `温景然`,
enName: `KINGSTON`, enName: `RYAN`,
age: 27, age: 27,
gender: `M`, gender: `M`,
height: 178, height: 178,

View File

@ -11,34 +11,22 @@ import { tosUrl } from "./tos";
* / store * / store
*/ */
/** solo.mp4 (docx "") /** 没有 solo.mp4 的艺人编号docx 标注"缺视频" */
003/010/017/027 v2 ,033 , */ const MISSING_VIDEO: ReadonlySet<string> = new Set(["003", "010", "017", "027"]);
const MISSING_VIDEO: ReadonlySet<string> = new Set<string>();
/** /** 缺氛围图3 的艺人编号资料文件夹里实际只到氛围图2 */
* 自定义封面:这些艺人的卡片/ cover , const MISSING_ATMOSPHERE_3: ReadonlySet<string> = new Set(["036"]);
* 11/2/3
* (.xlsx "封面图用氛围图N" webp
* portraits/{no}-cover.webp, portrait )
*/
const CUSTOM_COVERS: ReadonlySet<string> = new Set([
"002",
"003",
"005",
"012",
"013",
"014",
"019",
"025",
]);
/** 画廊 = 三张氛围图1/2/3。不包含三视图因为长宽比与卡片不一致。 */ /** 画廊 = 三张氛围图1/2/3。不包含三视图因为长宽比与卡片不一致。 */
function buildGallery(no: string): string[] { function buildGallery(no: string): string[] {
return [ const items = [
tosUrl(`portraits/${no}.webp`), tosUrl(`portraits/${no}.webp`),
tosUrl(`portraits/${no}-2.webp`), tosUrl(`portraits/${no}-2.webp`),
tosUrl(`portraits/${no}-3.webp`),
]; ];
if (!MISSING_ATMOSPHERE_3.has(no)) {
items.push(tosUrl(`portraits/${no}-3.webp`));
}
return items;
} }
function buildArtists(): Artist[] { function buildArtists(): Artist[] {
@ -51,11 +39,7 @@ function buildArtists(): Artist[] {
age: seed.age, age: seed.age,
gender: seed.gender, gender: seed.gender,
bio: seed.bio, bio: seed.bio,
portrait: tosUrl( portrait: tosUrl(`portraits/${seed.no}.webp`),
CUSTOM_COVERS.has(seed.no)
? `portraits/${seed.no}-cover.webp`
: `portraits/${seed.no}.webp`,
),
avatar: "", avatar: "",
gallery: buildGallery(seed.no), gallery: buildGallery(seed.no),
videoUrl: MISSING_VIDEO.has(seed.no) videoUrl: MISSING_VIDEO.has(seed.no)

View File

@ -3,21 +3,16 @@
* *
* : * :
* tosUrl("portraits/001.webp") * tosUrl("portraits/001.webp")
* https://cyberstar.tos-cn-shanghai.volces.com/cyber-star/portraits/001.webp?v=2 * https://cyberstar.tos-cn-shanghai.volces.com/cyber-star/portraits/001.webp
* *
* NEXT_PUBLIC_TOS_DOMAIN : * NEXT_PUBLIC_TOS_DOMAIN :
* .env.local / .env.production + ( scheme, /) * .env.local / .env.production + ( scheme, /)
* fallback (/path/...), public/ * fallback (/path/...), public/
*
* TOS_VERSION:每次有 TOS (/),
* +1 CDN URL ,
* , TTL invalidate
*/ */
const TOS_BASE = (process.env.NEXT_PUBLIC_TOS_DOMAIN ?? "").replace(/\/+$/, ""); const TOS_BASE = (process.env.NEXT_PUBLIC_TOS_DOMAIN ?? "").replace(/\/+$/, "");
const TOS_VERSION = "7";
export function tosUrl(path: string): string { export function tosUrl(path: string): string {
const clean = path.replace(/^\/+/, ""); const clean = path.replace(/^\/+/, "");
const base = TOS_BASE ? `${TOS_BASE}/${clean}` : `/${clean}`; if (!TOS_BASE) return `/${clean}`;
return `${base}?v=${TOS_VERSION}`; return `${TOS_BASE}/${clean}`;
} }