feat: 添加 SDD (规范驱动开发) 模块

This commit is contained in:
2026-03-18 16:17:30 +08:00
parent 90517f2289
commit c83f23c319
7 changed files with 312 additions and 1 deletions

View File

@@ -0,0 +1,86 @@
import React, { useEffect, useState, useRef } from 'react'
export const SDDPage: React.FC = () => {
const [isHealthy, setIsHealthy] = useState(false)
const [port, setPort] = useState<number>(9998)
const startedRef = useRef(false)
const restartingRef = useRef(false)
const webviewRef = useRef<HTMLWebViewElement>(null)
useEffect(() => {
let mounted = true
const start = async () => {
try {
const portResult = await window.electronAPI.sddGetPort()
if (mounted) {
setPort(portResult.port)
}
const result = await window.electronAPI.sddStart()
if (!result.success && mounted) {
console.error('Failed to start SDD:', result.error)
}
restartingRef.current = false
} catch (err) {
console.error('Failed to start SDD:', err)
restartingRef.current = false
}
}
const checkStatus = async () => {
try {
const status = await window.electronAPI.sddGetStatus()
if (mounted) {
setIsHealthy(status.running)
if (!status.running && !restartingRef.current) {
restartingRef.current = true
start()
}
}
} catch (err) {
if (mounted) {
setIsHealthy(false)
if (!restartingRef.current) {
restartingRef.current = true
start()
}
}
}
}
if (!startedRef.current) {
startedRef.current = true
start()
}
const interval = setInterval(checkStatus, 2000)
return () => {
mounted = false
clearInterval(interval)
window.electronAPI.sddStop()
startedRef.current = false
}
}, [])
return (
<div className="h-full w-full relative">
<span className={`absolute bottom-2 right-2 z-10 w-1.5 h-1.5 rounded-full ${isHealthy ? 'bg-green-500' : 'bg-red-500'}`} />
{!isHealthy && (
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-8 h-8 border-2 border-gray-300 border-t-blue-500 rounded-full animate-spin" />
</div>
)}
{isHealthy && (
<webview
ref={webviewRef}
src={`http://localhost:${port}`}
style={{ width: '100%', height: '100%', border: 'none' }}
allowpopups={true}
webpreferences="contextIsolation=no"
/>
)}
</div>
)
}

11
src/modules/sdd/index.tsx Normal file
View File

@@ -0,0 +1,11 @@
import { FileCode } from 'lucide-react'
import { SDDPage } from './SDDPage'
import { SDD_MODULE } from '@shared/modules/sdd'
import { createFrontendModule } from '@/lib/module-registry'
export default createFrontendModule(SDD_MODULE, {
icon: FileCode,
component: SDDPage,
})
export { SDDPage }

View File

@@ -45,6 +45,10 @@ export interface ElectronAPI {
xcOpenCodeWebGetStatus: () => Promise<{ running: boolean; port: number }>
xcOpenCodeWebGetPort: () => Promise<{ port: number }>
xcOpenCodeWebCheckReady: () => Promise<{ ready: boolean }>
sddStart: () => Promise<{ success: boolean; error?: string }>
sddStop: () => Promise<{ success: boolean; error?: string }>
sddGetStatus: () => Promise<{ running: boolean; port: number }>
sddGetPort: () => Promise<{ port: number }>
}
declare global {