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('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 __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
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.error = (...args) => { window.electronAPI?.log('error', ...args); _origError.apply(console, args); };
|
||||||
console.warn = (...args) => { window.electronAPI?.log('warn', ...args); _origWarn.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';
|
type Page = 'docs' | 'blueprint';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -33,7 +48,7 @@ declare global {
|
|||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [currentPage, setCurrentPage] = useState<Page>('docs');
|
const [currentPage, setCurrentPage] = useState<Page>('docs');
|
||||||
const [docsPath, setDocsPath] = useState<string>('');
|
const [docsPath, setDocsPath] = useState<string>(loadStoredDocsPath());
|
||||||
const [showAddModal, setShowAddModal] = useState(false);
|
const [showAddModal, setShowAddModal] = useState(false);
|
||||||
|
|
||||||
// Docs state lifted from ApiDocViewer to persist across view changes
|
// Docs state lifted from ApiDocViewer to persist across view changes
|
||||||
@@ -89,7 +104,8 @@ function App() {
|
|||||||
<main className="flex-1 overflow-hidden">
|
<main className="flex-1 overflow-hidden">
|
||||||
{currentPage === 'docs' ? (
|
{currentPage === 'docs' ? (
|
||||||
<ApiDocViewer
|
<ApiDocViewer
|
||||||
onDocsPathChange={setDocsPath}
|
docsPath={docsPath}
|
||||||
|
setDocsPath={setDocsPath}
|
||||||
showAddModal={showAddModal}
|
showAddModal={showAddModal}
|
||||||
onCloseAddModal={() => setShowAddModal(false)}
|
onCloseAddModal={() => setShowAddModal(false)}
|
||||||
externalDocs={externalDocs}
|
externalDocs={externalDocs}
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ interface ExternalDoc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ApiDocViewerProps {
|
interface ApiDocViewerProps {
|
||||||
onDocsPathChange?: (path: string) => void;
|
docsPath?: string;
|
||||||
|
setDocsPath?: (path: string) => void;
|
||||||
showAddModal?: boolean;
|
showAddModal?: boolean;
|
||||||
onCloseAddModal?: () => void;
|
onCloseAddModal?: () => void;
|
||||||
externalDocs: ExternalDoc[];
|
externalDocs: ExternalDoc[];
|
||||||
@@ -45,7 +46,8 @@ function saveStoredConfig(docsPath: string, externalDocs: ExternalDoc[], selecte
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ApiDocViewer = ({
|
export const ApiDocViewer = ({
|
||||||
onDocsPathChange,
|
docsPath: docsPathProp,
|
||||||
|
setDocsPath: setDocsPathProp,
|
||||||
showAddModal,
|
showAddModal,
|
||||||
onCloseAddModal,
|
onCloseAddModal,
|
||||||
externalDocs,
|
externalDocs,
|
||||||
@@ -59,10 +61,17 @@ export const ApiDocViewer = ({
|
|||||||
}: ApiDocViewerProps) => {
|
}: ApiDocViewerProps) => {
|
||||||
const stored = loadStoredConfig();
|
const stored = loadStoredConfig();
|
||||||
const [showModal, setShowModal] = useState(false)
|
const [showModal, setShowModal] = useState(false)
|
||||||
const [docsPath, setDocsPath] = useState(stored.docsPath)
|
const [docsPath, setDocsPath] = useState(docsPathProp ?? stored.docsPath)
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [errorMsg, setErrorMsg] = useState<string | null>(null)
|
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(() => {
|
useEffect(() => {
|
||||||
if (showAddModal) {
|
if (showAddModal) {
|
||||||
setShowModal(true)
|
setShowModal(true)
|
||||||
@@ -94,8 +103,9 @@ export const ApiDocViewer = ({
|
|||||||
}
|
}
|
||||||
}, [externalDocs, fileTree, selectedPath, stored.selectedPath, setSelectedPath, setCurrentContent])
|
}, [externalDocs, fileTree, selectedPath, stored.selectedPath, setSelectedPath, setCurrentContent])
|
||||||
|
|
||||||
|
// Save to localStorage when docsPath changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (docsPath && externalDocs.length > 0) {
|
if (docsPath) {
|
||||||
saveStoredConfig(docsPath, externalDocs, selectedPath)
|
saveStoredConfig(docsPath, externalDocs, selectedPath)
|
||||||
}
|
}
|
||||||
}, [docsPath, externalDocs, selectedPath])
|
}, [docsPath, externalDocs, selectedPath])
|
||||||
@@ -212,7 +222,7 @@ export const ApiDocViewer = ({
|
|||||||
if (success) {
|
if (success) {
|
||||||
setShowModal(false)
|
setShowModal(false)
|
||||||
onCloseAddModal?.()
|
onCloseAddModal?.()
|
||||||
onDocsPathChange?.(docsPath.trim())
|
setDocsPathProp?.(docsPath.trim())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,20 @@
|
|||||||
import Scene3D from './Scene3D';
|
import Scene3D from './Scene3D';
|
||||||
import DetailPanel from './DetailPanel';
|
import DetailPanel from './DetailPanel';
|
||||||
import { useBlueprintStore } from '../../store/blueprintStore';
|
import { useBlueprintStore } from '../../store/blueprintStore';
|
||||||
import { useEffect, useCallback } from 'react';
|
import { useEffect, useCallback, Component, ReactNode } from 'react';
|
||||||
import { parseBlueprintFromMd } from '../../data/blueprintParser';
|
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 {
|
interface BlueprintPageProps {
|
||||||
docsPath: string;
|
docsPath: string;
|
||||||
@@ -12,7 +24,11 @@ export default function BlueprintPage({ docsPath }: BlueprintPageProps) {
|
|||||||
const setBlueprintData = useBlueprintStore(state => state.setBlueprintData);
|
const setBlueprintData = useBlueprintStore(state => state.setBlueprintData);
|
||||||
|
|
||||||
const loadBlueprint = useCallback(async () => {
|
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';
|
const blueprintPath = docsPath.replace(/\\/g, '/') + '/blueprint.md';
|
||||||
let content: string | null = null;
|
let content: string | null = null;
|
||||||
@@ -31,6 +47,9 @@ export default function BlueprintPage({ docsPath }: BlueprintPageProps) {
|
|||||||
if (content) {
|
if (content) {
|
||||||
const data = parseBlueprintFromMd(content);
|
const data = parseBlueprintFromMd(content);
|
||||||
setBlueprintData(data);
|
setBlueprintData(data);
|
||||||
|
} else {
|
||||||
|
// Fallback to default if file not found
|
||||||
|
setBlueprintData(blueprintData);
|
||||||
}
|
}
|
||||||
}, [docsPath, setBlueprintData]);
|
}, [docsPath, setBlueprintData]);
|
||||||
|
|
||||||
@@ -41,7 +60,9 @@ export default function BlueprintPage({ docsPath }: BlueprintPageProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="flex h-full">
|
<div className="flex h-full">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<Scene3D />
|
<ErrorBoundary>
|
||||||
|
<Scene3D />
|
||||||
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
<DetailPanel />
|
<DetailPanel />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,15 @@ import SystemStructure from './SystemStructure';
|
|||||||
export default function Scene3D() {
|
export default function Scene3D() {
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full bg-black">
|
<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} />
|
<PerspectiveCamera makeDefault position={[12, 10, 12]} fov={50} />
|
||||||
<OrbitControls enablePan enableZoom enableRotate />
|
<OrbitControls enablePan enableZoom enableRotate />
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user