feat(web): 生成器 ThinkingIndicator/空结果态/数量校验 + 文风页 PageHeader + Codex 分段控件 + RefineView role 修正
This commit is contained in:
@@ -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({
|
||||
/>
|
||||
</Field>
|
||||
<div className="flex flex-wrap items-end gap-3">
|
||||
<Field label="数量" className="w-24">
|
||||
<Field
|
||||
label="数量"
|
||||
className="w-28"
|
||||
help={`${MIN_CHARACTER_COUNT}–${MAX_CHARACTER_COUNT} 张`}
|
||||
>
|
||||
<TextInput
|
||||
type="number"
|
||||
min={MIN_CHARACTER_COUNT}
|
||||
max={MAX_CHARACTER_COUNT}
|
||||
value={count}
|
||||
onChange={(e) => setCount(Number(e.target.value))}
|
||||
onChange={(e) =>
|
||||
setCount(clampCharacterCount(Number(e.target.value)))
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="定位(可选)" className="min-w-[12rem] flex-1">
|
||||
@@ -113,7 +121,12 @@ export function CharacterGenerator({
|
||||
</Field>
|
||||
<Button
|
||||
onClick={() => void onGenerate()}
|
||||
disabled={generating || brief.trim().length === 0}
|
||||
disabled={
|
||||
generating ||
|
||||
brief.trim().length === 0 ||
|
||||
count < MIN_CHARACTER_COUNT ||
|
||||
count > MAX_CHARACTER_COUNT
|
||||
}
|
||||
variant="primary"
|
||||
>
|
||||
<Sparkles className="h-4 w-4" aria-hidden="true" />
|
||||
@@ -122,6 +135,12 @@ export function CharacterGenerator({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{generating ? (
|
||||
<div className="rounded border border-line bg-panel/50 p-4 text-cinnabar">
|
||||
<ThinkingIndicator label="生成中" />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{gen.genStatus === "preview" && drafts.length > 0 ? (
|
||||
<div className="flex flex-col gap-3">
|
||||
<p className="text-xs text-ink-soft">
|
||||
|
||||
@@ -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({
|
||||
取消,回去改卡
|
||||
</Button>
|
||||
</div>
|
||||
{busy ? (
|
||||
<div className="mt-3 text-cinnabar">
|
||||
<ThinkingIndicator label="入库中" />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{generating ? (
|
||||
<div className="rounded border border-line bg-panel/50 p-4 text-cinnabar">
|
||||
<ThinkingIndicator label="生成中" />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{gen.status === "error" ? (
|
||||
<StatusNote variant="danger">
|
||||
<span className="flex flex-wrap items-center gap-3">
|
||||
<span>生成失败,请稍后重试。</span>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => void onGenerate()}
|
||||
>
|
||||
<RotateCcw className="h-4 w-4" aria-hidden="true" />
|
||||
重试
|
||||
</Button>
|
||||
</span>
|
||||
</StatusNote>
|
||||
) : null}
|
||||
|
||||
{gen.status === "done" && gen.entities.length === 0 ? (
|
||||
<EmptyState
|
||||
icon={Globe2}
|
||||
title="本次未生成可用实体"
|
||||
description="可换一个更具体的设定方向,或补充世界的硬规则与边界后再试。"
|
||||
action={
|
||||
<Button
|
||||
type="button"
|
||||
variant="primary"
|
||||
size="sm"
|
||||
onClick={() => void onGenerate()}
|
||||
disabled={brief.trim().length === 0}
|
||||
>
|
||||
<Sparkles className="h-4 w-4" aria-hidden="true" />
|
||||
重新生成
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{gen.status === "done" && gen.entities.length > 0 ? (
|
||||
<div className="flex flex-col gap-3">
|
||||
<StatusNote variant="info">
|
||||
|
||||
Reference in New Issue
Block a user