feat: update electron main and React components
This commit is contained in:
9
.claude/settings.local.json
Normal file
9
.claude/settings.local.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit:*)",
|
||||
"Bash(git push:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,9 @@ if (argsGlobal.headless) {
|
||||
}
|
||||
|
||||
app.commandLine.appendSwitch('disable-gpu-shader-disk-cache');
|
||||
app.commandLine.appendSwitch('enable-webgl');
|
||||
app.commandLine.appendSwitch('ignore-gpu-blacklist');
|
||||
app.commandLine.appendSwitch('enable-accelerated-2d-canvas');
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
20
src/App.tsx
20
src/App.tsx
@@ -19,6 +19,21 @@ console.log = (...args) => { window.electronAPI?.log('log', ...args); _origLog.a
|
||||
console.error = (...args) => { window.electronAPI?.log('error', ...args); _origError.apply(console, args); };
|
||||
console.warn = (...args) => { window.electronAPI?.log('warn', ...args); _origWarn.apply(console, args); };
|
||||
|
||||
const STORAGE_KEY = 'xc-docs-config';
|
||||
|
||||
function loadStoredDocsPath(): string {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored) {
|
||||
const config = JSON.parse(stored);
|
||||
return config.docsPath || '';
|
||||
}
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
type Page = 'docs' | 'blueprint';
|
||||
|
||||
declare global {
|
||||
@@ -33,7 +48,7 @@ declare global {
|
||||
|
||||
function App() {
|
||||
const [currentPage, setCurrentPage] = useState<Page>('docs');
|
||||
const [docsPath, setDocsPath] = useState<string>('');
|
||||
const [docsPath, setDocsPath] = useState<string>(loadStoredDocsPath());
|
||||
const [showAddModal, setShowAddModal] = useState(false);
|
||||
|
||||
// Docs state lifted from ApiDocViewer to persist across view changes
|
||||
@@ -89,7 +104,8 @@ function App() {
|
||||
<main className="flex-1 overflow-hidden">
|
||||
{currentPage === 'docs' ? (
|
||||
<ApiDocViewer
|
||||
onDocsPathChange={setDocsPath}
|
||||
docsPath={docsPath}
|
||||
setDocsPath={setDocsPath}
|
||||
showAddModal={showAddModal}
|
||||
onCloseAddModal={() => setShowAddModal(false)}
|
||||
externalDocs={externalDocs}
|
||||
|
||||
@@ -13,7 +13,8 @@ interface ExternalDoc {
|
||||
}
|
||||
|
||||
interface ApiDocViewerProps {
|
||||
onDocsPathChange?: (path: string) => void;
|
||||
docsPath?: string;
|
||||
setDocsPath?: (path: string) => void;
|
||||
showAddModal?: boolean;
|
||||
onCloseAddModal?: () => void;
|
||||
externalDocs: ExternalDoc[];
|
||||
@@ -45,7 +46,8 @@ function saveStoredConfig(docsPath: string, externalDocs: ExternalDoc[], selecte
|
||||
}
|
||||
|
||||
export const ApiDocViewer = ({
|
||||
onDocsPathChange,
|
||||
docsPath: docsPathProp,
|
||||
setDocsPath: setDocsPathProp,
|
||||
showAddModal,
|
||||
onCloseAddModal,
|
||||
externalDocs,
|
||||
@@ -59,10 +61,17 @@ export const ApiDocViewer = ({
|
||||
}: ApiDocViewerProps) => {
|
||||
const stored = loadStoredConfig();
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
const [docsPath, setDocsPath] = useState(stored.docsPath)
|
||||
const [docsPath, setDocsPath] = useState(docsPathProp ?? stored.docsPath)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [errorMsg, setErrorMsg] = useState<string | null>(null)
|
||||
|
||||
// Sync with prop when it changes (e.g., when switching views back to docs)
|
||||
useEffect(() => {
|
||||
if (docsPathProp !== undefined) {
|
||||
setDocsPath(docsPathProp)
|
||||
}
|
||||
}, [docsPathProp])
|
||||
|
||||
useEffect(() => {
|
||||
if (showAddModal) {
|
||||
setShowModal(true)
|
||||
@@ -94,8 +103,9 @@ export const ApiDocViewer = ({
|
||||
}
|
||||
}, [externalDocs, fileTree, selectedPath, stored.selectedPath, setSelectedPath, setCurrentContent])
|
||||
|
||||
// Save to localStorage when docsPath changes
|
||||
useEffect(() => {
|
||||
if (docsPath && externalDocs.length > 0) {
|
||||
if (docsPath) {
|
||||
saveStoredConfig(docsPath, externalDocs, selectedPath)
|
||||
}
|
||||
}, [docsPath, externalDocs, selectedPath])
|
||||
@@ -212,7 +222,7 @@ export const ApiDocViewer = ({
|
||||
if (success) {
|
||||
setShowModal(false)
|
||||
onCloseAddModal?.()
|
||||
onDocsPathChange?.(docsPath.trim())
|
||||
setDocsPathProp?.(docsPath.trim())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
import Scene3D from './Scene3D';
|
||||
import DetailPanel from './DetailPanel';
|
||||
import { useBlueprintStore } from '../../store/blueprintStore';
|
||||
import { useEffect, useCallback } from 'react';
|
||||
import { parseBlueprintFromMd } from '../../data/blueprintParser';
|
||||
import { useEffect, useCallback, Component, ReactNode } from 'react';
|
||||
import { parseBlueprintFromMd, blueprintData } from '../../data/blueprintParser';
|
||||
|
||||
class ErrorBoundary extends Component<{ children: ReactNode }, { hasError: boolean }> {
|
||||
state = { hasError: false };
|
||||
static getDerivedStateFromError() { return { hasError: true }; }
|
||||
componentDidCatch(e: Error, info: React.ErrorInfo) {
|
||||
console.error('[BlueprintPage] Error:', e, info);
|
||||
}
|
||||
render() {
|
||||
if (this.state.hasError) return <div className="text-white p-4">3D渲染出错</div>;
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
interface BlueprintPageProps {
|
||||
docsPath: string;
|
||||
@@ -12,7 +24,11 @@ export default function BlueprintPage({ docsPath }: BlueprintPageProps) {
|
||||
const setBlueprintData = useBlueprintStore(state => state.setBlueprintData);
|
||||
|
||||
const loadBlueprint = useCallback(async () => {
|
||||
if (!docsPath) return;
|
||||
// Use default blueprint data if no docsPath is set
|
||||
if (!docsPath) {
|
||||
setBlueprintData(blueprintData);
|
||||
return;
|
||||
}
|
||||
|
||||
const blueprintPath = docsPath.replace(/\\/g, '/') + '/blueprint.md';
|
||||
let content: string | null = null;
|
||||
@@ -31,6 +47,9 @@ export default function BlueprintPage({ docsPath }: BlueprintPageProps) {
|
||||
if (content) {
|
||||
const data = parseBlueprintFromMd(content);
|
||||
setBlueprintData(data);
|
||||
} else {
|
||||
// Fallback to default if file not found
|
||||
setBlueprintData(blueprintData);
|
||||
}
|
||||
}, [docsPath, setBlueprintData]);
|
||||
|
||||
@@ -41,7 +60,9 @@ export default function BlueprintPage({ docsPath }: BlueprintPageProps) {
|
||||
return (
|
||||
<div className="flex h-full">
|
||||
<div className="flex-1">
|
||||
<Scene3D />
|
||||
<ErrorBoundary>
|
||||
<Scene3D />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
<DetailPanel />
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,15 @@ import SystemStructure from './SystemStructure';
|
||||
export default function Scene3D() {
|
||||
return (
|
||||
<div className="w-full h-full bg-black">
|
||||
<Canvas>
|
||||
<Canvas
|
||||
gl={{
|
||||
antialias: true,
|
||||
alpha: false,
|
||||
powerPreference: 'high-performance',
|
||||
failIfMajorPerformanceCaveat: false,
|
||||
}}
|
||||
dpr={[1, 2]}
|
||||
>
|
||||
<PerspectiveCamera makeDefault position={[12, 10, 12]} fov={50} />
|
||||
<OrbitControls enablePan enableZoom enableRotate />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user