style: add board light mode
This commit is contained in:
@@ -4,7 +4,7 @@ import { type ReactNode, type RefObject, useEffect, useMemo, useRef, useState }
|
||||
import { createPortal } from "react-dom"
|
||||
import {
|
||||
AlertTriangle, Check, ChevronDown, Circle, Film, FileText, Image as ImageIcon, Info, Link2, Loader2,
|
||||
Mic, Package, PanelRight, Play, Plus, RefreshCw, Scissors, Sparkles, Trash2, Upload, Wand2,
|
||||
Mic, Moon, Package, PanelRight, Play, Plus, RefreshCw, Scissors, Sparkles, Sun, Trash2, Upload, Wand2,
|
||||
} from "lucide-react"
|
||||
import { toast } from "sonner"
|
||||
import {
|
||||
@@ -78,6 +78,9 @@ const VIDEO_MODELS = [
|
||||
] as const
|
||||
|
||||
type VideoModel = (typeof VIDEO_MODELS)[number]["value"]
|
||||
type BoardThemeMode = "dark" | "light"
|
||||
|
||||
const BOARD_THEME_STORAGE_KEY = "skg-board-theme"
|
||||
|
||||
type DraftSegment = {
|
||||
id: string
|
||||
@@ -1268,6 +1271,7 @@ export function AdRecreationBoard({
|
||||
const [sixViewBusyKey, setSixViewBusyKey] = useState<string | null>(null)
|
||||
const [generatingAll, setGeneratingAll] = useState(false)
|
||||
const [runtimeModels, setRuntimeModels] = useState<RuntimeModels | undefined>()
|
||||
const [boardTheme, setBoardTheme] = useState<BoardThemeMode>("dark")
|
||||
const fileRef = useRef<HTMLInputElement | null>(null)
|
||||
const selectedFrames = job
|
||||
? job.frames.filter((frame) => data.selectedFrames.has(frame.index)).sort((a, b) => a.timestamp - b.timestamp)
|
||||
@@ -1307,6 +1311,15 @@ export function AdRecreationBoard({
|
||||
setSelectedVideoIds(new Set())
|
||||
}, [activeJobId])
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
const saved = window.localStorage.getItem(BOARD_THEME_STORAGE_KEY)
|
||||
if (saved === "light" || saved === "dark") setBoardTheme(saved)
|
||||
} catch {
|
||||
// Ignore storage failures; dark mode remains the product default.
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
getRuntimeHealth()
|
||||
@@ -1334,6 +1347,18 @@ export function AdRecreationBoard({
|
||||
if (trimmed) setUrl("")
|
||||
}
|
||||
|
||||
const toggleBoardTheme = () => {
|
||||
setBoardTheme((current) => {
|
||||
const next: BoardThemeMode = current === "dark" ? "light" : "dark"
|
||||
try {
|
||||
window.localStorage.setItem(BOARD_THEME_STORAGE_KEY, next)
|
||||
} catch {
|
||||
// Ignore storage failures; the in-memory theme still switches.
|
||||
}
|
||||
return next
|
||||
})
|
||||
}
|
||||
|
||||
const selectAllFrames = () => {
|
||||
if (!job) return
|
||||
for (const frame of job.frames) {
|
||||
@@ -1477,7 +1502,7 @@ export function AdRecreationBoard({
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="skg-board-theme relative z-20 h-screen w-screen overflow-hidden bg-black text-white">
|
||||
<section className={`skg-board-theme ${boardTheme === "light" ? "skg-board-theme--light" : ""} relative z-20 h-screen w-screen overflow-hidden bg-black text-white`}>
|
||||
<div className="skg-board-ambient pointer-events-none absolute inset-0" />
|
||||
<div className="relative z-10 flex h-full flex-col px-4 py-4">
|
||||
<header className="skg-board-topbar mb-3 flex items-center justify-between gap-4 rounded-lg border border-white/10 bg-white/[0.04] px-4 py-3">
|
||||
@@ -1485,12 +1510,23 @@ export function AdRecreationBoard({
|
||||
<div className="text-[11px] font-medium uppercase tracking-[0.18em] text-white/40">feed ad recreation worksheet</div>
|
||||
<h1 className="mt-1 text-[22px] font-semibold leading-tight text-white">信息流广告复刻工作表</h1>
|
||||
</div>
|
||||
<div className="grid min-w-[520px] grid-cols-5 gap-2 text-[11px] text-white/48">
|
||||
<Metric label="素材" value={`${jobs.length}`} />
|
||||
<Metric label="当前" value={shortId(activeJobId)} />
|
||||
<Metric label="视频" value={job?.video_url ? "ready" : "-"} />
|
||||
<Metric label="文案段" value={`${transcriptCount}`} />
|
||||
<Metric label="背景音" value={backgroundReady ? "ready" : "-"} />
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleBoardTheme}
|
||||
className="skg-board-theme-toggle inline-flex h-10 items-center gap-1.5 rounded-md border border-white/10 bg-black/24 px-3 text-[11px] font-semibold text-white/62 transition hover:border-[#d6b36a]/45 hover:text-white"
|
||||
title={boardTheme === "dark" ? "切换到明亮模式" : "切换到暗色模式"}
|
||||
>
|
||||
{boardTheme === "dark" ? <Sun className="h-3.5 w-3.5" /> : <Moon className="h-3.5 w-3.5" />}
|
||||
{boardTheme === "dark" ? "明亮" : "暗色"}
|
||||
</button>
|
||||
<div className="grid min-w-[520px] grid-cols-5 gap-2 text-[11px] text-white/48">
|
||||
<Metric label="素材" value={`${jobs.length}`} />
|
||||
<Metric label="当前" value={shortId(activeJobId)} />
|
||||
<Metric label="视频" value={job?.video_url ? "ready" : "-"} />
|
||||
<Metric label="文案段" value={`${transcriptCount}`} />
|
||||
<Metric label="背景音" value={backgroundReady ? "ready" : "-"} />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user