157 lines
6.8 KiB
TypeScript
157 lines
6.8 KiB
TypeScript
import {
|
|
createContext,
|
|
useCallback,
|
|
useContext,
|
|
useMemo,
|
|
useState,
|
|
type ReactNode,
|
|
} from 'react';
|
|
|
|
import type { SetupWritingApprovalPayload } from '@/components/workspace/messages';
|
|
|
|
/**
|
|
* 写作配置表单的共享状态。
|
|
*
|
|
* 上提到 Context 是为了让「右侧写作助手 Q&A」在用户批准模型建议后,能把建议字段
|
|
* 一次性回填到「左侧写作表单」的输入框(``applyProposal``)。表单与 Q&A 面板共用同一份
|
|
* state,因此 Provider 必须同时包住两侧面板(见 AIWritingPage)。
|
|
*/
|
|
export interface WritingFormValue {
|
|
intent: string;
|
|
setIntent: (v: string) => void;
|
|
/** 整个写作配置表单主体是否展开;初始收起,用户点表头或问答侧「应用到表单」时展开。 */
|
|
formExpanded: boolean;
|
|
setFormExpanded: (v: boolean) => void;
|
|
showOutline: boolean;
|
|
setShowOutline: (v: boolean) => void;
|
|
outlineText: string;
|
|
setOutlineText: (v: string) => void;
|
|
writingModeType: 'normal' | 'imitate';
|
|
setWritingModeType: (v: 'normal' | 'imitate') => void;
|
|
sampleText: string;
|
|
setSampleText: (v: string) => void;
|
|
sampleFilename: string;
|
|
setSampleFilename: (v: string) => void;
|
|
imitateStrength: 'light' | 'medium' | 'strong';
|
|
setImitateStrength: (v: 'light' | 'medium' | 'strong') => void;
|
|
preserveSampleStructure: boolean;
|
|
setPreserveSampleStructure: (v: boolean) => void;
|
|
articleType: string;
|
|
setArticleType: (v: string) => void;
|
|
wordCountInput: string;
|
|
setWordCountInput: (v: string) => void;
|
|
audience: string;
|
|
setAudience: (v: string) => void;
|
|
strictMode: boolean;
|
|
setStrictMode: (v: boolean) => void;
|
|
/** 是否在成稿里加参考文献(正文 [n] 角标 + 文末「参考文献」列表,像论文)。默认开。 */
|
|
addReferences: boolean;
|
|
setAddReferences: (v: boolean) => void;
|
|
materialSource: 'general' | 'knowledge_base' | 'notebook';
|
|
setMaterialSource: (v: 'general' | 'knowledge_base' | 'notebook') => void;
|
|
/** 不收集素材,直接用通用知识写作:勾选后跳过素材检索阶段(含素材确认暂停点)。默认关。 */
|
|
skipResearch: boolean;
|
|
setSkipResearch: (v: boolean) => void;
|
|
/** 我的空间(笔记本)来源:选中的 search_space_id;上提到 Context 以便左栏「笔记本」点击直接设为素材来源。 */
|
|
selectedNotebookId: number | null;
|
|
setSelectedNotebookId: (v: number | null) => void;
|
|
/** 选中笔记本下的文档 id 列表(缩小检索范围)。 */
|
|
selectedDocumentIds: number[];
|
|
setSelectedDocumentIds: (v: number[]) => void;
|
|
modelName: string;
|
|
setModelName: (v: string) => void;
|
|
/** 把模型建议(setup_writing 审批 payload)回填到表单各字段。 */
|
|
applyProposal: (payload: SetupWritingApprovalPayload) => void;
|
|
}
|
|
|
|
const WritingFormContext = createContext<WritingFormValue | null>(null);
|
|
|
|
export function useWritingForm(): WritingFormValue {
|
|
const ctx = useContext(WritingFormContext);
|
|
if (!ctx) {
|
|
throw new Error('useWritingForm must be used within WritingFormProvider');
|
|
}
|
|
return ctx;
|
|
}
|
|
|
|
export function useWritingFormOptional(): WritingFormValue | null {
|
|
return useContext(WritingFormContext);
|
|
}
|
|
|
|
export function WritingFormProvider({
|
|
children,
|
|
initialMaterialSource,
|
|
initialNotebookId,
|
|
initialDocumentIds,
|
|
}: {
|
|
children: ReactNode;
|
|
initialMaterialSource?: 'general' | 'knowledge_base' | 'notebook';
|
|
initialNotebookId?: number | null;
|
|
initialDocumentIds?: number[];
|
|
}) {
|
|
const [intent, setIntent] = useState('');
|
|
const [formExpanded, setFormExpanded] = useState(false);
|
|
const [showOutline, setShowOutline] = useState(true);
|
|
const [outlineText, setOutlineText] = useState('');
|
|
const [writingModeType, setWritingModeType] = useState<'normal' | 'imitate'>('normal');
|
|
const [sampleText, setSampleText] = useState('');
|
|
const [sampleFilename, setSampleFilename] = useState('');
|
|
const [imitateStrength, setImitateStrength] = useState<'light' | 'medium' | 'strong'>('medium');
|
|
const [preserveSampleStructure, setPreserveSampleStructure] = useState(true);
|
|
const [articleType, setArticleType] = useState('analysis');
|
|
const [wordCountInput, setWordCountInput] = useState('800');
|
|
const [audience, setAudience] = useState('普通读者');
|
|
const [strictMode, setStrictMode] = useState(false);
|
|
const [addReferences, setAddReferences] = useState(true);
|
|
const [materialSource, setMaterialSource] = useState<'general' | 'knowledge_base' | 'notebook'>(initialMaterialSource ?? 'general');
|
|
const [skipResearch, setSkipResearch] = useState(false);
|
|
const [selectedNotebookId, setSelectedNotebookId] = useState<number | null>(initialNotebookId ?? null);
|
|
const [selectedDocumentIds, setSelectedDocumentIds] = useState<number[]>(initialDocumentIds ?? []);
|
|
const [modelName, setModelName] = useState('');
|
|
|
|
const applyProposal = useCallback((payload: SetupWritingApprovalPayload) => {
|
|
// 问答侧识别出意图并点「应用到表单」时,自动展开表单让用户看到回填结果。
|
|
setFormExpanded(true);
|
|
if (payload.user_intent?.trim()) setIntent(payload.user_intent.trim());
|
|
if (payload.article_type?.trim()) setArticleType(payload.article_type.trim());
|
|
if (typeof payload.word_count_target === 'number' && payload.word_count_target > 0) {
|
|
setWordCountInput(String(payload.word_count_target));
|
|
}
|
|
if (payload.target_audience?.trim()) setAudience(payload.target_audience.trim());
|
|
if (payload.writing_mode === 'strict') setStrictMode(true);
|
|
else if (payload.writing_mode === 'loose') setStrictMode(false);
|
|
if (payload.outline?.trim()) {
|
|
setOutlineText(payload.outline);
|
|
setShowOutline(true);
|
|
}
|
|
}, []);
|
|
|
|
const value = useMemo<WritingFormValue>(
|
|
() => ({
|
|
intent, setIntent,
|
|
formExpanded, setFormExpanded,
|
|
showOutline, setShowOutline,
|
|
outlineText, setOutlineText,
|
|
writingModeType, setWritingModeType,
|
|
sampleText, setSampleText,
|
|
sampleFilename, setSampleFilename,
|
|
imitateStrength, setImitateStrength,
|
|
preserveSampleStructure, setPreserveSampleStructure,
|
|
articleType, setArticleType,
|
|
wordCountInput, setWordCountInput,
|
|
audience, setAudience,
|
|
strictMode, setStrictMode,
|
|
addReferences, setAddReferences,
|
|
materialSource, setMaterialSource,
|
|
skipResearch, setSkipResearch,
|
|
selectedNotebookId, setSelectedNotebookId,
|
|
selectedDocumentIds, setSelectedDocumentIds,
|
|
modelName, setModelName,
|
|
applyProposal,
|
|
}),
|
|
[intent, formExpanded, showOutline, outlineText, writingModeType, sampleText, sampleFilename, imitateStrength, preserveSampleStructure, articleType, wordCountInput, audience, strictMode, addReferences, materialSource, skipResearch, selectedNotebookId, selectedDocumentIds, modelName, applyProposal],
|
|
);
|
|
|
|
return <WritingFormContext.Provider value={value}>{children}</WritingFormContext.Provider>;
|
|
}
|