feat(ux): B0 注入透明读端点 + F1 写作页右栏真面板
兑现「看到的=写章用的」信任牌(不变量 #6),替换写作页右栏过时假占位
(「M1 暂未接 / M2 开放」)。
后端:
- GET /projects/{id}/chapters/{no}/injection → InjectionResponse(selected
实体 + 入选理由 + recent_n);调既有 assemble() 回放确定性 SelectionTrace,
无 LLM / 无 commit / 无 DDL;项目不存在→404,无大纲→selected:[]。
- 新 schemas/injection.py;tests/test_injection.py(3 测)。
前端:
- gen:api 纳入端点;纯逻辑 lib/workbench/injection.ts(理由/类型→中文徽标
+ 4 vitest)+ useInjection 读 hook。
- ChapterAssistant 改 client 组件:列出选中实体 + 理由徽标 + 空/载/错三态,
四审段指向审稿页;Workbench 传 projectId/chapterNo。
门禁:后端 ruff/format/mypy 159/alembic 无漂移/pytest 454;
前端 lint/typecheck/vitest 167/build。
B0 可控版(PUT override pin/排除/recent_n + selection 加参 + draft 同读
override + 持久化)+ F1 可控控件待后续。
This commit is contained in:
50
apps/web/lib/workbench/useInjection.ts
Normal file
50
apps/web/lib/workbench/useInjection.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { api } from "@/lib/api/client";
|
||||
import type { InjectionResponse } from "./injection";
|
||||
|
||||
export interface UseInjection {
|
||||
data: InjectionResponse | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
// 本章注入透明(B0 读端点):挂载时拉一次确定性 SelectionTrace。
|
||||
// 只读、无 LLM、无 commit——展示「选了谁/为什么」。失败给可读文案不抛。
|
||||
export function useInjection(
|
||||
projectId: string,
|
||||
chapterNo: number,
|
||||
): UseInjection {
|
||||
const [data, setData] = useState<InjectionResponse | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
void (async () => {
|
||||
const { data: body, error: err } = await api.GET(
|
||||
"/projects/{project_id}/chapters/{chapter_no}/injection",
|
||||
{ params: { path: { project_id: projectId, chapter_no: chapterNo } } },
|
||||
);
|
||||
if (cancelled) return;
|
||||
if (err || !body) {
|
||||
setError("注入信息暂不可用");
|
||||
setData(null);
|
||||
} else {
|
||||
setData(body);
|
||||
}
|
||||
setLoading(false);
|
||||
})();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [projectId, chapterNo]);
|
||||
|
||||
return { data, loading, error };
|
||||
}
|
||||
Reference in New Issue
Block a user