diff --git a/apps/web/app/templates/page.tsx b/apps/web/app/templates/page.tsx new file mode 100644 index 0000000..596d083 --- /dev/null +++ b/apps/web/app/templates/page.tsx @@ -0,0 +1,32 @@ +import { AppShell } from "@/components/AppShell"; +import { TemplatesManager } from "@/components/templates/TemplatesManager"; +import { fetchTemplates } from "@/lib/api/server"; +import type { TemplateResponse } from "@/lib/api/types"; + +// 提示词/模板库(F3,全局单用户本地版)。Server Component 预取列表;CRUD 在客户端组件。 +export default async function TemplatesPage() { + let initial: TemplateResponse[] = []; + let loadError = false; + try { + initial = await fetchTemplates(); + } catch { + loadError = true; + } + + return ( + +
+

+ 保存常用提示词,复用时一键填入生成器的 brief / 原文输入。仅本地、单用户,不做分享。 +

+ {loadError ? ( +

+ 无法连接后端服务,请确认 API 已启动后刷新。 +

+ ) : ( + + )} +
+
+ ); +} diff --git a/apps/web/components/chain/ChainPage.tsx b/apps/web/components/chain/ChainPage.tsx index 0ba9178..33716b8 100644 --- a/apps/web/components/chain/ChainPage.tsx +++ b/apps/web/components/chain/ChainPage.tsx @@ -5,7 +5,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { AppShell } from "@/components/AppShell"; import { useToast } from "@/components/Toast"; import { api } from "@/lib/api/client"; -import { chainPhase, chainResultView } from "@/lib/chain/chain"; +import { chainPhase, chainResultView, type ChainKind } from "@/lib/chain/chain"; import { friendlyError } from "@/lib/errors/messages"; import { useJobPoll } from "@/lib/jobs/useJobPoll"; import type { ProjectResponse } from "@/lib/api/types"; @@ -17,8 +17,6 @@ interface ChainPageProps { project: ProjectResponse; } -const CHAIN_KEY = "draft_volume"; - // 多章工作流链页(Scope B B1):发起链(POST run)→job 轮询进度→interrupt 命中则读该章冲突裁决→ // POST resume 续跑。复用 useJobPoll(轮询)+ ConflictCard/decisions(裁决)+ friendlyError(文案)。 // 链是后台任务,走 job 轮询而非 SSE。awaiting 时停轮询(后端把 job 置 awaiting_input, @@ -43,14 +41,18 @@ export function ChainPage({ project }: ChainPageProps) { }, [phase, poll]); const onStart = useCallback( - async (startChapterNo: number, count: number): Promise => { + async ( + chainKey: ChainKind, + startChapterNo: number, + count: number, + ): Promise => { setStarting(true); try { const { data, error } = await api.POST( "/projects/{project_id}/chains/{chain_key}/run", { params: { - path: { project_id: project.id, chain_key: CHAIN_KEY }, + path: { project_id: project.id, chain_key: chainKey }, }, body: { start_chapter_no: startChapterNo, count }, }, @@ -89,7 +91,10 @@ export function ChainPage({ project }: ChainPageProps) { activeNav="chains" >
- void onStart(s, c)} disabled={busy} /> + void onStart(k, s, c)} + disabled={busy} + /> {showProgress && result ? ( diff --git a/apps/web/components/chain/ChainStarter.tsx b/apps/web/components/chain/ChainStarter.tsx index 5ae691d..cc0fd52 100644 --- a/apps/web/components/chain/ChainStarter.tsx +++ b/apps/web/components/chain/ChainStarter.tsx @@ -2,9 +2,11 @@ import { useState } from "react"; +import { CHAIN_KINDS, type ChainKind } from "@/lib/chain/chain"; + interface ChainStarterProps { - // 发起一条链(起始章 + 章数);父层负责 POST run + 轮询。 - onStart: (startChapterNo: number, count: number) => void; + // 发起一条链(链类型 + 起始章 + 章数);父层负责 POST run + 轮询。 + onStart: (chainKey: ChainKind, startChapterNo: number, count: number) => void; // 链正在运行/等待裁决时禁用(避免重复发起)。 disabled: boolean; } @@ -12,10 +14,12 @@ interface ChainStarterProps { const DEFAULT_START = 1; const DEFAULT_COUNT = 3; const MAX_COUNT = 50; +const DEFAULT_CHAIN: ChainKind = "draft_volume"; // 链发起表单(净新):选起始章号 + 连续写几章 → 调 onStart。 // count 1..50(对齐后端 ChainRunRequest Field 约束;前端先拦一道,越界后端 422 兜底)。 export function ChainStarter({ onStart, disabled }: ChainStarterProps) { + const [chainKey, setChainKey] = useState(DEFAULT_CHAIN); const [start, setStart] = useState(String(DEFAULT_START)); const [count, setCount] = useState(String(DEFAULT_COUNT)); @@ -34,7 +38,7 @@ export function ChainStarter({ onStart, disabled }: ChainStarterProps) { aria-label="发起多章链" onSubmit={(e) => { e.preventDefault(); - if (valid && !disabled) onStart(startNo, countNo); + if (valid && !disabled) onStart(chainKey, startNo, countNo); }} >
@@ -43,6 +47,33 @@ export function ChainStarter({ onStart, disabled }: ChainStarterProps) { 从指定章起循环「写章 → 四审 → 验收」;遇未决冲突会暂停等你裁决再续跑。

+
+ 链类型 +
+ {CHAIN_KINDS.map((kind) => ( + + ))} +
+