feat: lift docs state to App for persistence across view changes

Move externalDocs, fileTree, selectedPath, and currentContent state from
ApiDocViewer to App component so that docs state persists when switching
between docs and blueprint views.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-19 22:06:53 +08:00
parent ad58bed418
commit 4a8220652e
2 changed files with 48 additions and 9 deletions

View File

@@ -3,6 +3,14 @@ import { FileText, Box, Plus } from 'lucide-react';
import { ApiDocViewer } from './components/ApiDocViewer';
import BlueprintPage from './components/blueprint/BlueprintPage';
import { config } from './config';
import type { DocFile } from '@/lib/types';
interface ExternalDoc {
name: string
path: string
relativePath: string
content: string
}
const _origLog = console.log;
const _origError = console.error;
@@ -27,6 +35,12 @@ function App() {
const [currentPage, setCurrentPage] = useState<Page>('docs');
const [docsPath, setDocsPath] = useState<string>('');
const [showAddModal, setShowAddModal] = useState(false);
// Docs state lifted from ApiDocViewer to persist across view changes
const [externalDocs, setExternalDocs] = useState<ExternalDoc[]>([]);
const [fileTree, setFileTree] = useState<DocFile[]>([]);
const [selectedPath, setSelectedPath] = useState<string | undefined>(undefined);
const [currentContent, setCurrentContent] = useState<string>('');
useEffect(() => {
document.title = config.projectName;
@@ -74,10 +88,18 @@ function App() {
</header>
<main className="flex-1 overflow-hidden">
{currentPage === 'docs' ? (
<ApiDocViewer
<ApiDocViewer
onDocsPathChange={setDocsPath}
showAddModal={showAddModal}
onCloseAddModal={() => setShowAddModal(false)}
externalDocs={externalDocs}
setExternalDocs={setExternalDocs}
fileTree={fileTree}
setFileTree={setFileTree}
selectedPath={selectedPath}
setSelectedPath={setSelectedPath}
currentContent={currentContent}
setCurrentContent={setCurrentContent}
/>
) : (
<BlueprintPage docsPath={docsPath} />

View File

@@ -16,6 +16,14 @@ interface ApiDocViewerProps {
onDocsPathChange?: (path: string) => void;
showAddModal?: boolean;
onCloseAddModal?: () => void;
externalDocs: ExternalDoc[];
setExternalDocs: React.Dispatch<React.SetStateAction<ExternalDoc[]>>;
fileTree: DocFile[];
setFileTree: React.Dispatch<React.SetStateAction<DocFile[]>>;
selectedPath: string | undefined;
setSelectedPath: React.Dispatch<React.SetStateAction<string | undefined>>;
currentContent: string;
setCurrentContent: React.Dispatch<React.SetStateAction<string>>;
}
const STORAGE_KEY = 'xc-docs-config';
@@ -36,14 +44,22 @@ function saveStoredConfig(docsPath: string, externalDocs: ExternalDoc[], selecte
} catch {}
}
export const ApiDocViewer = ({ onDocsPathChange, showAddModal, onCloseAddModal }: ApiDocViewerProps) => {
export const ApiDocViewer = ({
onDocsPathChange,
showAddModal,
onCloseAddModal,
externalDocs,
setExternalDocs,
fileTree,
setFileTree,
selectedPath,
setSelectedPath,
currentContent,
setCurrentContent
}: ApiDocViewerProps) => {
const stored = loadStoredConfig();
const [fileTree, setFileTree] = useState<DocFile[]>([])
const [selectedPath, setSelectedPath] = useState<string | undefined>(stored.selectedPath)
const [currentContent, setCurrentContent] = useState<string>('')
const [showModal, setShowModal] = useState(false)
const [docsPath, setDocsPath] = useState(stored.docsPath)
const [externalDocs, setExternalDocs] = useState<ExternalDoc[]>(stored.externalDocs)
const [isLoading, setIsLoading] = useState(false)
const [errorMsg, setErrorMsg] = useState<string | null>(null)
@@ -54,6 +70,7 @@ export const ApiDocViewer = ({ onDocsPathChange, showAddModal, onCloseAddModal }
}
}, [showAddModal])
// Auto-select first document when externalDocs and fileTree are loaded
useEffect(() => {
if (externalDocs.length > 0 && fileTree.length > 0 && !selectedPath) {
const fileList = externalDocs
@@ -65,17 +82,17 @@ export const ApiDocViewer = ({ onDocsPathChange, showAddModal, onCloseAddModal }
return filename !== parentFolder
})
.map(d => d.relativePath)
const targetPath = stored.selectedPath && externalDocs.find(d => d.relativePath === stored.selectedPath)
? stored.selectedPath
: fileList[0]
if (targetPath) {
setSelectedPath(targetPath)
setCurrentContent(externalDocs.find(d => d.relativePath === targetPath)?.content || '')
}
}
}, [externalDocs, fileTree, selectedPath])
}, [externalDocs, fileTree, selectedPath, stored.selectedPath, setSelectedPath, setCurrentContent])
useEffect(() => {
if (docsPath && externalDocs.length > 0) {