From 2503d8be6434048d85af3d7392be8b49c856be77 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Tue, 10 Mar 2026 16:20:32 +0800 Subject: [PATCH] =?UTF-8?q?refactor(home):=20=E5=B0=86=E9=A6=96=E9=A1=B5?= =?UTF-8?q?=E6=94=B9=E9=80=A0=E6=88=90opencode=E6=9C=8D=E5=8A=A1=E5=85=A5?= =?UTF-8?q?=E5=8F=A3=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除ChatGPT风格对话界面相关代码 - 添加在首页自动启动/停止opencode serve的IPC调用 - 首页使用webview加载opencode服务器界面 --- electron/main.ts | 60 ++++++++++ electron/preload.ts | 2 + src/modules/home/HomePage.tsx | 61 +++++++++- src/modules/home/components/ChatContent.tsx | 120 -------------------- src/modules/home/components/ChatLayout.tsx | 11 -- src/modules/home/components/ChatSidebar.tsx | 53 --------- src/modules/home/index.tsx | 2 - src/stores/chatStore.ts | 98 ---------------- src/types/electron.d.ts | 2 + 9 files changed, 123 insertions(+), 286 deletions(-) delete mode 100644 src/modules/home/components/ChatContent.tsx delete mode 100644 src/modules/home/components/ChatLayout.tsx delete mode 100644 src/modules/home/components/ChatSidebar.tsx delete mode 100644 src/stores/chatStore.ts diff --git a/electron/main.ts b/electron/main.ts index 28f4fbe..cdced87 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -6,6 +6,7 @@ import log from 'electron-log'; import { generatePdf } from './services/pdfGenerator'; import { selectHtmlFile } from './services/htmlImport'; import { electronState } from './state'; +import { spawn, ChildProcess } from 'child_process'; log.initialize(); @@ -24,6 +25,9 @@ if (!fs.existsSync(process.env.NOTEBOOK_ROOT)) { electronState.setDevelopment(!app.isPackaged); +let opencodeProcess: ChildProcess | null = null; +const OPENCODE_PORT = 4096; + let lastClipboardText = ''; function startClipboardWatcher() { @@ -358,6 +362,62 @@ ipcMain.handle('remote-download-file', async (_event, id: string, serverHost: st } }); +ipcMain.handle('opencode-start-server', async () => { + if (opencodeProcess) { + log.info('Opencode server already running'); + return { success: true, port: OPENCODE_PORT }; + } + + try { + log.info('Starting opencode server...'); + opencodeProcess = spawn('opencode', ['serve'], { + stdio: 'pipe', + shell: true, + detached: false, + }); + + opencodeProcess.stdout?.on('data', (data) => { + log.info(`[opencode] ${data}`); + }); + + opencodeProcess.stderr?.on('data', (data) => { + log.error(`[opencode error] ${data}`); + }); + + opencodeProcess.on('error', (err) => { + log.error('Opencode process error:', err); + opencodeProcess = null; + }); + + opencodeProcess.on('exit', (code) => { + log.info(`Opencode process exited with code ${code}`); + opencodeProcess = null; + }); + + return { success: true, port: OPENCODE_PORT }; + } catch (error: any) { + log.error('Failed to start opencode server:', error); + return { success: false, error: error.message }; + } +}); + +ipcMain.handle('opencode-stop-server', async () => { + if (!opencodeProcess) { + log.info('Opencode server not running'); + return { success: true }; + } + + try { + log.info('Stopping opencode server...'); + opencodeProcess.kill('SIGTERM'); + opencodeProcess = null; + return { success: true }; + } catch (error: any) { + log.error('Failed to stop opencode server:', error); + return { success: false, error: error.message }; + } +}); + async function startServer() { if (electronState.isDevelopment()) { log.info('In dev mode, assuming external servers are running.'); diff --git a/electron/preload.ts b/electron/preload.ts index cae8b8a..259651d 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -39,4 +39,6 @@ contextBridge.exposeInMainWorld('electronAPI', { ipcRenderer.invoke('remote-upload-file', id, serverHost, port, filePath, remotePath, password), remoteDownloadFile: (id: string, serverHost: string, port: number, fileName: string, remotePath: string, localPath: string, password?: string) => ipcRenderer.invoke('remote-download-file', id, serverHost, port, fileName, remotePath, localPath, password), + opencodeStartServer: () => ipcRenderer.invoke('opencode-start-server'), + opencodeStopServer: () => ipcRenderer.invoke('opencode-stop-server'), }) diff --git a/src/modules/home/HomePage.tsx b/src/modules/home/HomePage.tsx index b3dbc32..b13a5ae 100644 --- a/src/modules/home/HomePage.tsx +++ b/src/modules/home/HomePage.tsx @@ -1,5 +1,62 @@ -import { ChatLayout } from './components/ChatLayout' +import { useRef, useEffect, useState } from 'react' export const HomePage = () => { - return + const webviewRef = useRef(null) + const [isLoading, setIsLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + const startOpencodeServer = async () => { + try { + const result = await window.electronAPI?.opencodeStartServer() + if (!result?.success) { + setError(result?.error || 'Failed to start opencode server') + } + } catch (err) { + setError(err instanceof Error ? err.message : 'Unknown error') + } + } + + startOpencodeServer() + + return () => { + window.electronAPI?.opencodeStopServer() + } + }, []) + + useEffect(() => { + const webview = webviewRef.current + if (!webview) return + + webview.addEventListener('did-fail-load', (e) => { + console.error('[HomePage] Failed to load:', e) + setIsLoading(false) + }) + + webview.addEventListener('did-finish-load', () => { + setIsLoading(false) + }) + }, []) + + return ( +
+ {isLoading && ( +
+
正在启动 opencode 服务...
+
+ )} + {error && ( +
+
启动失败: {error}
+
+ )} + +
+ ) } diff --git a/src/modules/home/components/ChatContent.tsx b/src/modules/home/components/ChatContent.tsx deleted file mode 100644 index 1fc98b3..0000000 --- a/src/modules/home/components/ChatContent.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import { Send } from 'lucide-react' -import { useChatStore, type ChatMessage } from '@/stores/chatStore' -import { useState, useRef, useEffect } from 'react' - -const MessageItem = ({ message }: { message: ChatMessage }) => { - const isUser = message.role === 'user' - - return ( -
-
-
- {isUser ? ( - 👤 - ) : ( - 🤖 - )} -
-
- {message.content} -
-
-
- ) -} - -export const ChatContent = () => { - const { currentChatId, chats, addMessage, updateChatTitle } = useChatStore() - const [input, setInput] = useState('') - const messagesEndRef = useRef(null) - - const currentChat = chats.find((c) => c.id === currentChatId) - const messages = currentChat?.messages ?? [] - - useEffect(() => { - messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) - }, [messages.length]) - - const handleSend = () => { - if (!input.trim() || !currentChatId) return - - const userMessage: ChatMessage = { - id: Math.random().toString(36).substring(2, 15), - role: 'user', - content: input.trim(), - timestamp: Date.now(), - } - - addMessage(currentChatId, userMessage) - - if (messages.length === 0) { - const title = input.trim().slice(0, 20) + (input.trim().length > 20 ? '...' : '') - updateChatTitle(currentChatId, title) - } - - setInput('') - } - - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault() - handleSend() - } - } - - return ( -
-
- {messages.length === 0 ? ( -
-

- 开始今天的工作吧! -

-
- ) : ( -
- {messages.map((msg) => ( - - ))} -
-
- )} -
- -
-
-
-