feat(notebook): load notebook space via token and pre-select docs/sources
- NotebookAIWritingPage reads `token` from URL, fetches space data from notebook-token API (search_space_id, doc_ids, user_token) and space name from searchspaces API; keep-alive ping every 50s resets Redis TTL - Pass user_token as studioToken through AIWritingPage → AIWritingLayout → WritingFormPanel → AIWritingPanel → AIWritingForm to skip getTokenByEmail when studio token is already available - useStudioNotebooks accepts enabled flag; useNotebookSources accepts overrideToken to bypass the internal token fetch - AIWritingForm hides notebook selector when studioToken is provided (notebook is pre-locked) and pre-selects initialDocumentIds Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
e53c1a0f7b
commit
0e7149eec3
6 files changed
+140
-85
No files matched your search
@@ -24,6 +24,10 @@ interface AIWritingFormProps {
|
||||
hideAdvancedFields?: boolean;
|
||||
/** 预选笔记本 ID(传入时自动切换素材来源为「我的笔记」) */
|
||||
initialNotebookId?: number;
|
||||
/** 预选文档 ID 列表 */
|
||||
initialDocumentIds?: number[];
|
||||
/** 外部直接提供的 studio bearer token,跳过 getTokenByEmail */
|
||||
studioToken?: string;
|
||||
}
|
||||
|
||||
const INPUT_CLASS =
|
||||
@@ -53,6 +57,8 @@ export function AIWritingForm({
|
||||
disabled,
|
||||
hideAdvancedFields = false,
|
||||
initialNotebookId,
|
||||
initialDocumentIds,
|
||||
studioToken,
|
||||
}: AIWritingFormProps) {
|
||||
const {
|
||||
intent, setIntent,
|
||||
@@ -69,11 +75,11 @@ export function AIWritingForm({
|
||||
const [selectedNotebookId, setSelectedNotebookId] = useState<number | null>(
|
||||
initialNotebookId ?? null,
|
||||
);
|
||||
const [selectedDocumentIds, setSelectedDocumentIds] = useState<number[]>([]);
|
||||
const [selectedDocumentIds, setSelectedDocumentIds] = useState<number[]>(initialDocumentIds ?? []);
|
||||
|
||||
const { models, isLoading: modelsLoading } = useModels();
|
||||
const { notebooks, isLoading: notebooksLoading } = useStudioNotebooks();
|
||||
const { sources, isLoading: sourcesLoading } = useNotebookSources(selectedNotebookId);
|
||||
const { notebooks, isLoading: notebooksLoading } = useStudioNotebooks(!studioToken);
|
||||
const { sources, isLoading: sourcesLoading } = useNotebookSources(selectedNotebookId, studioToken);
|
||||
|
||||
const { articleTypes, loading: typesLoading, refresh: refreshTypes } = useArticleTypes();
|
||||
|
||||
@@ -291,22 +297,25 @@ export function AIWritingForm({
|
||||
{/* 选了"我的笔记"才展示笔记本 / 文档选择器 */}
|
||||
{materialSource === 'notebook' && (
|
||||
<div className="mt-2 space-y-2">
|
||||
<Select
|
||||
value={selectedNotebookId !== null ? String(selectedNotebookId) : ''}
|
||||
onValueChange={handleNotebookChange}
|
||||
disabled={disabled || notebooksLoading}
|
||||
>
|
||||
<SelectTrigger className={FIELD_TRIGGER_CLASS}>
|
||||
<SelectValue placeholder={notebooksLoading ? '加载中…' : '选择笔记本'} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{notebooks.map((nb) => (
|
||||
<SelectItem key={nb.id} value={String(nb.id)} className="text-sm">
|
||||
{nb.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{/* studioToken 模式:笔记本已锁定,不展示选择器 */}
|
||||
{!studioToken && (
|
||||
<Select
|
||||
value={selectedNotebookId !== null ? String(selectedNotebookId) : ''}
|
||||
onValueChange={handleNotebookChange}
|
||||
disabled={disabled || notebooksLoading}
|
||||
>
|
||||
<SelectTrigger className={FIELD_TRIGGER_CLASS}>
|
||||
<SelectValue placeholder={notebooksLoading ? '加载中…' : '选择笔记本'} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{notebooks.map((nb) => (
|
||||
<SelectItem key={nb.id} value={String(nb.id)} className="text-sm">
|
||||
{nb.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
|
||||
{selectedNotebookId !== null && (
|
||||
<div className="space-y-1">
|
||||
|
||||
@@ -71,11 +71,16 @@ const STATUS_LABEL: Record<string, string> = {
|
||||
export function AIWritingPanel({
|
||||
hideAdvancedFields = false,
|
||||
initialNotebookId,
|
||||
initialDocumentIds,
|
||||
studioToken,
|
||||
}: {
|
||||
hideAdvancedFields?: boolean;
|
||||
initialNotebookId?: number;
|
||||
initialDocumentIds?: number[];
|
||||
studioToken?: string;
|
||||
} = {}) {
|
||||
const navigate = useNavigate();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const { state, startWriting, submitIntervention, pauseWriting, resumeWriting, reset } = useAIWriting();
|
||||
const { historySession, isHistoryMode, openHistory, closeHistory } = useAIWritingHistory();
|
||||
const { sessions, loading: sessionsLoading, refresh, rename, remove } = useAIWritingSessions();
|
||||
@@ -354,6 +359,8 @@ export function AIWritingPanel({
|
||||
onSubmit={startWriting}
|
||||
hideAdvancedFields={hideAdvancedFields}
|
||||
initialNotebookId={initialNotebookId}
|
||||
initialDocumentIds={initialDocumentIds}
|
||||
studioToken={studioToken}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -115,11 +115,17 @@ const SETUP_LAYOUT = { [SETUP_CHAT_ID]: 65, [SETUP_FORM_ID]: 35 };
|
||||
/**
|
||||
* 写作配置表单面板(写作助手回填的目标),抽出复用:idle 时在右栏,开始写作后在左栏。
|
||||
*/
|
||||
function WritingFormPanel({ hideAdvancedFields }: { hideAdvancedFields: boolean }) {
|
||||
interface NotebookProps {
|
||||
initialNotebookId?: number;
|
||||
initialDocumentIds?: number[];
|
||||
studioToken?: string;
|
||||
}
|
||||
|
||||
function WritingFormPanel({ hideAdvancedFields, initialNotebookId, initialDocumentIds, studioToken }: { hideAdvancedFields: boolean } & NotebookProps) {
|
||||
return (
|
||||
// 与原布局保持相同的 flex-1 min-h-0 包裹,确保内部 h-full 正确解析
|
||||
<div className="flex-1 min-h-0 overflow-hidden">
|
||||
<AIWritingPanel hideAdvancedFields={hideAdvancedFields} />
|
||||
<AIWritingPanel hideAdvancedFields={hideAdvancedFields} initialNotebookId={initialNotebookId} initialDocumentIds={initialDocumentIds} studioToken={studioToken} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -129,7 +135,7 @@ function WritingFormPanel({ hideAdvancedFields }: { hideAdvancedFields: boolean
|
||||
* - idle(初始问答阶段):左=「写作助手 Q&A」,右=「写作配置选项」表单;
|
||||
* - 开始写作后:回到原布局——左=写作配置/时间线面板,右=草稿预览。
|
||||
*/
|
||||
function AIWritingLayout({ hideAdvancedFields }: { hideAdvancedFields: boolean }) {
|
||||
function AIWritingLayout({ hideAdvancedFields, initialNotebookId, initialDocumentIds, studioToken }: { hideAdvancedFields: boolean } & NotebookProps) {
|
||||
const layoutRef = useRef<GroupImperativeHandle>(null);
|
||||
const { state } = useAIWriting();
|
||||
const { isHistoryMode } = useAIWritingHistory();
|
||||
@@ -162,7 +168,7 @@ function AIWritingLayout({ hideAdvancedFields }: { hideAdvancedFields: boolean }
|
||||
minSize={20}
|
||||
className="overflow-hidden flex flex-col"
|
||||
>
|
||||
<WritingFormPanel hideAdvancedFields={hideAdvancedFields} />
|
||||
<WritingFormPanel hideAdvancedFields={hideAdvancedFields} initialNotebookId={initialNotebookId} initialDocumentIds={initialDocumentIds} studioToken={studioToken} />
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
);
|
||||
@@ -203,9 +209,13 @@ function AIWritingLayout({ hideAdvancedFields }: { hideAdvancedFields: boolean }
|
||||
export default function AIWritingPage({
|
||||
hideAdvancedFields = false,
|
||||
initialNotebookId,
|
||||
initialDocumentIds,
|
||||
studioToken,
|
||||
}: {
|
||||
hideAdvancedFields?: boolean;
|
||||
initialNotebookId?: number;
|
||||
initialDocumentIds?: number[];
|
||||
studioToken?: string;
|
||||
} = {}) {
|
||||
return (
|
||||
<CanvasThreadProvider>
|
||||
@@ -217,7 +227,7 @@ export default function AIWritingPage({
|
||||
<TooltipProvider delayDuration={300}>
|
||||
<div className="h-full flex flex-col overflow-hidden bg-background">
|
||||
<div className="flex-1 min-h-0 overflow-hidden">
|
||||
<AIWritingLayout hideAdvancedFields={hideAdvancedFields} />
|
||||
<AIWritingLayout hideAdvancedFields={hideAdvancedFields} initialNotebookId={initialNotebookId} initialDocumentIds={initialDocumentIds} studioToken={studioToken} />
|
||||
</div>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
|
||||
Reference in new issue
Block a user