feat: light app center and iframe refactor
This commit is contained in:
1 parent
728a4b1170
commit
9d73f27146
35 files changed
+1878
-246
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { ChevronsDown, Play, Plus, RotateCcw, Settings2, Square, Trash2 } from 'lucide-react';
|
||||
import { ChevronsDown, PanelRightClose, Play, Plus, RotateCcw, Settings2, Square, Trash2 } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -15,6 +15,7 @@ import { useArticleTypes } from '../../hooks/useArticleTypes';
|
||||
|
||||
import { useAIWriting } from '../../contexts/AIWritingContext';
|
||||
import { useAIWritingHistory } from '../../contexts/AIWritingHistoryContext';
|
||||
import { useWritingFormOptional } from '../../contexts/WritingFormContext';
|
||||
import { useAIWritingSessions } from '../../hooks/useAIWritingSessions';
|
||||
import { adminCleanupAIWritingSessions, getAIWritingSession } from '../../api/ai-writing-sessions';
|
||||
|
||||
@@ -83,6 +84,7 @@ export function AIWritingPanel({
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const { state, startWriting, submitIntervention, pauseWriting, resumeWriting, reset } = useAIWriting();
|
||||
const { historySession, isHistoryMode, openHistory, closeHistory } = useAIWritingHistory();
|
||||
const writingForm = useWritingFormOptional();
|
||||
const { sessions, loading: sessionsLoading, refresh, rename, remove } = useAIWritingSessions();
|
||||
const { articleTypes, refresh: refreshArticleTypes } = useArticleTypes();
|
||||
|
||||
@@ -342,6 +344,18 @@ export function AIWritingPanel({
|
||||
onRename={rename}
|
||||
onRefresh={refresh}
|
||||
/>
|
||||
{/* 收起写作配置:仅在 idle 配置阶段、表单展开时出现(与 AIWritingPage 的整块收起联动) */}
|
||||
{writingForm && isIdle && !isHistoryMode && writingForm.formExpanded && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-7 w-7"
|
||||
onClick={() => writingForm.setFormExpanded(false)}
|
||||
title="收起写作配置"
|
||||
>
|
||||
<PanelRightClose className="size-3.5" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ import type { SetupWritingApprovalPayload } from '@/components/workspace/message
|
||||
export interface WritingFormValue {
|
||||
intent: string;
|
||||
setIntent: (v: string) => void;
|
||||
/** 整个写作配置表单主体是否展开;初始收起,用户点表头或问答侧「应用到表单」时展开。 */
|
||||
formExpanded: boolean;
|
||||
setFormExpanded: (v: boolean) => void;
|
||||
showOutline: boolean;
|
||||
setShowOutline: (v: boolean) => void;
|
||||
outlineText: string;
|
||||
@@ -55,6 +58,7 @@ export function useWritingFormOptional(): WritingFormValue | null {
|
||||
|
||||
export function WritingFormProvider({ children, initialMaterialSource }: { children: ReactNode; initialMaterialSource?: 'knowledge_base' | 'skill' | 'notebook' }) {
|
||||
const [intent, setIntent] = useState('');
|
||||
const [formExpanded, setFormExpanded] = useState(false);
|
||||
const [showOutline, setShowOutline] = useState(false);
|
||||
const [outlineText, setOutlineText] = useState('');
|
||||
const [articleType, setArticleType] = useState('analysis');
|
||||
@@ -65,6 +69,8 @@ export function WritingFormProvider({ children, initialMaterialSource }: { child
|
||||
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) {
|
||||
@@ -85,6 +91,7 @@ export function WritingFormProvider({ children, initialMaterialSource }: { child
|
||||
const value = useMemo<WritingFormValue>(
|
||||
() => ({
|
||||
intent, setIntent,
|
||||
formExpanded, setFormExpanded,
|
||||
showOutline, setShowOutline,
|
||||
outlineText, setOutlineText,
|
||||
articleType, setArticleType,
|
||||
@@ -95,7 +102,7 @@ export function WritingFormProvider({ children, initialMaterialSource }: { child
|
||||
modelName, setModelName,
|
||||
applyProposal,
|
||||
}),
|
||||
[intent, showOutline, outlineText, articleType, wordCountInput, audience, strictMode, materialSource, modelName, applyProposal],
|
||||
[intent, formExpanded, showOutline, outlineText, articleType, wordCountInput, audience, strictMode, materialSource, modelName, applyProposal],
|
||||
);
|
||||
|
||||
return <WritingFormContext.Provider value={value}>{children}</WritingFormContext.Provider>;
|
||||
|
||||
@@ -82,7 +82,9 @@
|
||||
* ──────────────────────────────────────────────────────────────────────────
|
||||
*/
|
||||
import { useRef } from 'react';
|
||||
import { ChevronLeft } from 'lucide-react';
|
||||
import { TooltipProvider } from '@/components/ui/tooltip';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { GroupImperativeHandle } from 'react-resizable-panels';
|
||||
|
||||
import { CanvasThreadProvider } from '../contexts/CanvasThreadContext';
|
||||
@@ -93,7 +95,7 @@ import { AIWritingDraftPanel } from '../components/ai-writing/AIWritingDraftPane
|
||||
import { WritingSetupChat } from '../components/ai-writing/WritingSetupChat';
|
||||
import { AIWritingProvider, useAIWriting } from '../contexts/AIWritingContext';
|
||||
import { AIWritingHistoryProvider, useAIWritingHistory } from '../contexts/AIWritingHistoryContext';
|
||||
import { WritingFormProvider } from '../contexts/WritingFormContext';
|
||||
import { WritingFormProvider, useWritingForm } from '../contexts/WritingFormContext';
|
||||
import {
|
||||
ResizableHandle,
|
||||
ResizablePanel,
|
||||
@@ -103,14 +105,9 @@ import {
|
||||
// Panel IDs used as keys in the v4 Layout map
|
||||
const WRITING_ID = 'aw-writing';
|
||||
const DRAFT_ID = 'aw-draft';
|
||||
// 初始问答阶段(idle)专用的两栏 ID:左=问答助手,右=写作配置表单
|
||||
const SETUP_CHAT_ID = 'aw-setup-chat';
|
||||
const SETUP_FORM_ID = 'aw-setup-form';
|
||||
|
||||
// Match the original canvas page proportions (chat=35, artifact=65)
|
||||
const DEFAULT_LAYOUT = { [WRITING_ID]: 35, [DRAFT_ID]: 65 };
|
||||
// idle 阶段镜像原比例:问答框在左占大头(65),配置表单在右占小头(35)
|
||||
const SETUP_LAYOUT = { [SETUP_CHAT_ID]: 65, [SETUP_FORM_ID]: 35 };
|
||||
|
||||
/**
|
||||
* 写作配置表单面板(写作助手回填的目标),抽出复用:idle 时在右栏,开始写作后在左栏。
|
||||
@@ -139,38 +136,45 @@ function AIWritingLayout({ hideAdvancedFields, initialNotebookId, initialDocumen
|
||||
const layoutRef = useRef<GroupImperativeHandle>(null);
|
||||
const { state } = useAIWriting();
|
||||
const { isHistoryMode } = useAIWritingHistory();
|
||||
const { formExpanded, setFormExpanded } = useWritingForm();
|
||||
|
||||
// 初始问答阶段:无历史查看;查看历史时与「正在写作」相同左右分栏(左时间线、右草稿)
|
||||
// 初始问答阶段:无历史查看;查看历史时与「正在写作」相同左右分栏(左时间线、右草稿)。
|
||||
//
|
||||
// 用稳定的 flex 结构(而非按 formExpanded 在两套容器间切换):左侧问答区永远是第一个
|
||||
// 子节点、位置不变,避免收起/展开时 WritingSetupChat 被重新挂载(它在内部 useMemo 生成
|
||||
// 独立 threadId,一旦重挂会换新线程、丢失对话)。右侧整块「写作配置表单」可收起为一条
|
||||
// 竖向展开栏;问答侧点「应用到表单」(applyProposal) 会自动把 formExpanded 置 true 展开。
|
||||
if (state.status === 'idle' && !isHistoryMode) {
|
||||
return (
|
||||
<ResizablePanelGroup
|
||||
orientation="horizontal"
|
||||
groupRef={layoutRef}
|
||||
defaultLayout={SETUP_LAYOUT}
|
||||
className="h-full"
|
||||
>
|
||||
{/* 左侧:写作助手 Q&A */}
|
||||
<ResizablePanel
|
||||
id={SETUP_CHAT_ID}
|
||||
defaultSize={65}
|
||||
minSize={40}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<div className="flex h-full">
|
||||
{/* 左侧:写作助手 Q&A —— 恒为第一个子节点,位置稳定不重挂 */}
|
||||
<div className="min-h-0 flex-1 overflow-hidden">
|
||||
<WritingSetupChat />
|
||||
</ResizablePanel>
|
||||
</div>
|
||||
|
||||
<ResizableHandle withHandle />
|
||||
|
||||
{/* 右侧:写作配置选项表单 */}
|
||||
<ResizablePanel
|
||||
id={SETUP_FORM_ID}
|
||||
defaultSize={35}
|
||||
minSize={20}
|
||||
className="overflow-hidden flex flex-col"
|
||||
{/* 右侧:写作配置表单整块。容器持久存在,宽度随 formExpanded 平滑过渡(展开/收起
|
||||
都有动画);收起时只剩一条竖向「展开」栏,收起按钮则在表单头部右上角。 */}
|
||||
<div
|
||||
className={cn(
|
||||
'relative flex min-h-0 shrink-0 flex-col overflow-hidden border-l transition-[width,min-width] duration-300 ease-in-out',
|
||||
formExpanded ? 'w-[36%] min-w-[340px] max-w-[600px]' : 'w-10 min-w-10',
|
||||
)}
|
||||
>
|
||||
<WritingFormPanel hideAdvancedFields={hideAdvancedFields} initialNotebookId={initialNotebookId} initialDocumentIds={initialDocumentIds} studioToken={studioToken} />
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
{formExpanded ? (
|
||||
<WritingFormPanel hideAdvancedFields={hideAdvancedFields} initialNotebookId={initialNotebookId} initialDocumentIds={initialDocumentIds} studioToken={studioToken} />
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setFormExpanded(true)}
|
||||
title="展开写作配置"
|
||||
className="flex h-full w-full flex-col items-center justify-center gap-3 bg-muted/30 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
|
||||
>
|
||||
<ChevronLeft className="size-4" />
|
||||
<span className="[writing-mode:vertical-rl] text-xs tracking-widest">写作配置</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user