feat: add visual settings and readiness gate

This commit is contained in:
Codex
2026-08-01 22:14:20 +08:00
parent 6a15217d55
commit 3b4106e8cc
25 changed files with 2447 additions and 239 deletions
+42 -3
View File
@@ -11,6 +11,7 @@ import {
CubeIcon,
EyeIcon,
EyeSlashIcon,
GearSixIcon,
ImageIcon,
LockSimpleIcon,
PaperPlaneTiltIcon,
@@ -21,8 +22,9 @@ import {
WarningCircleIcon,
} from "@phosphor-icons/react";
import Image from "next/image";
import { FormEvent, useMemo, useState } from "react";
import { FormEvent, useEffect, useMemo, useState } from "react";
import { SettingsCenter } from "@/components/settings-center";
import {
initialLayers,
initialMessages,
@@ -30,8 +32,10 @@ import {
workflowStages,
} from "@/lib/demo";
import type { ChatMessage } from "@/types/workflow";
import type { SettingsReadiness } from "@/types/settings";
const iconWeight = "regular" as const;
const API_BASE = process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8000";
export function WorkflowStudio() {
const [layers, setLayers] = useState(initialLayers);
@@ -39,12 +43,32 @@ export function WorkflowStudio() {
const [messages, setMessages] = useState<ChatMessage[]>(initialMessages);
const [draft, setDraft] = useState("");
const [furnitureMode, setFurnitureMode] = useState<"reference" | "remove">("reference");
const [settingsOpen, setSettingsOpen] = useState(false);
const [readiness, setReadiness] = useState<SettingsReadiness | null>(null);
const currentDirection = useMemo(
() => styleDirections.find((item) => item.id === selectedDirection) ?? styleDirections[0],
[selectedDirection],
);
useEffect(() => {
async function loadReadiness() {
try {
const response = await fetch(`${API_BASE}/v1/readiness`, { cache: "no-store" });
if (!response.ok) return;
const current = await response.json() as SettingsReadiness;
setReadiness(current);
if (!current.ready && !window.sessionStorage.getItem("settings-intro-seen")) {
window.sessionStorage.setItem("settings-intro-seen", "true");
setSettingsOpen(true);
}
} catch {
setReadiness(null);
}
}
void loadReadiness();
}, []);
function toggleLayer(id: string) {
setLayers((items) =>
items.map((item) => (item.id === id ? { ...item, visible: !item.visible } : item)),
@@ -84,9 +108,19 @@ export function WorkflowStudio() {
<div className="topbar-actions">
<span className="save-state"><CheckIcon size={14} weight="bold" /> </span>
<button className="button button-secondary settings-button" type="button" onClick={() => setSettingsOpen(true)}>
<GearSixIcon size={16} />
{readiness && !readiness.ready ? <span className="settings-alert-dot" aria-label="有必备配置未完成" /> : null}
</button>
<button className="button button-secondary" type="button"></button>
<button className="button button-primary" type="button">
<ArrowRightIcon size={16} weight="bold" />
<button
className="button button-primary"
type="button"
disabled={!readiness?.ready}
onClick={() => { if (!readiness?.ready) setSettingsOpen(true); }}
title={!readiness?.ready ? "请先完成系统设置" : undefined}
>
{readiness?.ready ? "确认结构" : "配置后开始"} <ArrowRightIcon size={16} weight="bold" />
</button>
</div>
</header>
@@ -327,6 +361,11 @@ export function WorkflowStudio() {
</div>
</aside>
</div>
<SettingsCenter
open={settingsOpen}
onClose={() => setSettingsOpen(false)}
onReadinessChange={setReadiness}
/>
</main>
</Tooltip.Provider>
);