Files
writer-work-flow/apps/web/lib/wizard/useProjectPlan.ts
Yaojia Wang 02d19019f6 feat(web): AI 立项方案生成+预填(种子门控 + 按字段接受)
向导阶段(project 未建)一键推演结构化书级蓝图预填立项向导。

- 新 SPEC project_plan_spec(analyst 档,纯预览 reads/writes=())+ schema
  ProjectPlanResult(书名候选 + 时空背景/叙事结构/故事核心/结局设计/基调,
  全字段默认值守解析韧性)+ 注册 SCHEMA_CATALOG;计数三处 21→22 + regen golden。
- 端点 POST /skills/project-plan/generate(不带 project 前缀,绕开 toolbox 404):
  请求体序列化向导草稿为 project_context,复用 build_brief_context + run_generator;
  种子门控(缺 genre/logline→422);请求 schema 加 max_length 上界。
- run_generator/_build_request 的 project_id 放宽 uuid.UUID|None(向导无 project)。
- 前端 wizard.ts 加 mapPlanResultToWizardForm(错配字段折进 premise 不静默丢)+
  applyPlanPatch(仅回填空字段、保护已编辑)+ useProjectPlan hook + PlanAssistant。
2026-07-06 16:26:08 +02:00

60 lines
1.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useCallback, useState } from "react";
import { useToast } from "@/components/Toast";
import { api } from "@/lib/api/client";
import { generationErrorMessage } from "@/lib/generation/cards";
import type {
ProjectPlanGenerateRequest,
ProjectPlanView,
} from "@/lib/api/types";
export type ProjectPlanStatus = "idle" | "generating" | "done" | "error";
export interface UseProjectPlan {
status: ProjectPlanStatus;
plan: ProjectPlanView | null;
generate: (seed: ProjectPlanGenerateRequest) => Promise<void>;
reset: () => void;
}
// AI 立项方案生成器POST /skills/project-plan/generate → 结构化方案预览(不入库)。
// 向导阶段调用project 未建);种子门控在后端亦校验(缺 genre/logline → 422
export function useProjectPlan(): UseProjectPlan {
const [status, setStatus] = useState<ProjectPlanStatus>("idle");
const [plan, setPlan] = useState<ProjectPlanView | null>(null);
const toast = useToast();
const generate = useCallback<UseProjectPlan["generate"]>(
async (seed) => {
setStatus("generating");
setPlan(null);
try {
const { data, error } = await api.POST(
"/skills/project-plan/generate",
{ body: seed },
);
if (error || !data) {
setStatus("error");
toast(generationErrorMessage(error), "error");
return;
}
setPlan(data);
setStatus("done");
} catch {
setStatus("error");
toast("生成请求异常,请检查网络。", "error");
}
},
[toast],
);
const reset = useCallback((): void => {
setStatus("idle");
setPlan(null);
}, []);
return { status, plan, generate, reset };
}