diff --git a/apps/web/components/codex/CodexPage.tsx b/apps/web/components/codex/CodexPage.tsx index 050e36a..6124627 100644 --- a/apps/web/components/codex/CodexPage.tsx +++ b/apps/web/components/codex/CodexPage.tsx @@ -11,6 +11,7 @@ import { Button } from "@/components/ui/Button"; import { Card } from "@/components/ui/Card"; import { EmptyState } from "@/components/ui/EmptyState"; import { PageHeader } from "@/components/ui/PageHeader"; +import { SegmentedControl } from "@/components/ui/SegmentedControl"; import type { CharacterCardView, ProjectResponse, @@ -70,24 +71,13 @@ export function CodexPage({ title="设定库" description="人物、世界观与时间线共同构成写作时的真相源。AI 生成内容也要先预览、再确认入库。" /> -
- {TABS.map((t) => ( - - ))} -
+ ({ value: t.key, label: t.label }))} + />
{tab === "characters" ? ( @@ -119,7 +109,7 @@ export function CodexPage({ {c.relations.map((r, j) => (
  • {r.kind} · {r.name} diff --git a/apps/web/components/generation/CharacterGenerator.tsx b/apps/web/components/generation/CharacterGenerator.tsx index 06af9e8..ae978d5 100644 --- a/apps/web/components/generation/CharacterGenerator.tsx +++ b/apps/web/components/generation/CharacterGenerator.tsx @@ -3,6 +3,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { Database, Sparkles } from "lucide-react"; +import { ThinkingIndicator } from "@/components/ThinkingIndicator"; import { Button } from "@/components/ui/Button"; import { Field } from "@/components/ui/Field"; import { SectionHeader } from "@/components/ui/SectionHeader"; @@ -10,6 +11,7 @@ import { StatusNote } from "@/components/ui/StatusNote"; import { TextInput } from "@/components/ui/TextInput"; import type { CharacterCardView } from "@/lib/api/types"; import { + clampCharacterCount, MAX_CHARACTER_COUNT, MIN_CHARACTER_COUNT, updateCard, @@ -95,13 +97,19 @@ export function CharacterGenerator({ />
    - + setCount(Number(e.target.value))} + onChange={(e) => + setCount(clampCharacterCount(Number(e.target.value))) + } /> @@ -113,7 +121,12 @@ export function CharacterGenerator({
  • + {generating ? ( +
    + +
    + ) : null} + {gen.genStatus === "preview" && drafts.length > 0 ? (

    diff --git a/apps/web/components/generation/ConflictAdjudication.tsx b/apps/web/components/generation/ConflictAdjudication.tsx index 908fab5..a34250e 100644 --- a/apps/web/components/generation/ConflictAdjudication.tsx +++ b/apps/web/components/generation/ConflictAdjudication.tsx @@ -1,5 +1,6 @@ "use client"; +import { ThinkingIndicator } from "@/components/ThinkingIndicator"; import { Button } from "@/components/ui/Button"; import type { IngestConflicts } from "@/lib/generation/cards"; @@ -74,6 +75,11 @@ export function ConflictAdjudication({ 取消,回去改卡

    + {busy ? ( +
    + +
    + ) : null} ); } diff --git a/apps/web/components/generation/WorldGenerator.tsx b/apps/web/components/generation/WorldGenerator.tsx index 677cea4..3cb27dc 100644 --- a/apps/web/components/generation/WorldGenerator.tsx +++ b/apps/web/components/generation/WorldGenerator.tsx @@ -1,10 +1,12 @@ "use client"; import { useCallback, useState } from "react"; -import { Copy, Sparkles } from "lucide-react"; +import { Copy, Globe2, RotateCcw, Sparkles } from "lucide-react"; import { useToast } from "@/components/Toast"; +import { ThinkingIndicator } from "@/components/ThinkingIndicator"; import { Button } from "@/components/ui/Button"; +import { EmptyState } from "@/components/ui/EmptyState"; import { Field } from "@/components/ui/Field"; import { SectionHeader } from "@/components/ui/SectionHeader"; import { StatusNote } from "@/components/ui/StatusNote"; @@ -72,6 +74,49 @@ export function WorldGenerator({ projectId }: WorldGeneratorProps) { + {generating ? ( +
    + +
    + ) : null} + + {gen.status === "error" ? ( + + + 生成失败,请稍后重试。 + + + + ) : null} + + {gen.status === "done" && gen.entities.length === 0 ? ( + void onGenerate()} + disabled={brief.trim().length === 0} + > +