feat: 添加发现设备页面
This commit is contained in:
@@ -4,9 +4,43 @@ import fs from 'node:fs';
|
||||
import net from 'node:net';
|
||||
import { spawn } from 'node:child_process';
|
||||
import started from 'electron-squirrel-startup';
|
||||
import { Bonjour } from 'bonjour-service';
|
||||
|
||||
if (started) app.quit();
|
||||
|
||||
// ========== Bonjour 服务发现 ==========
|
||||
const bonjour = new Bonjour();
|
||||
let discoveredServices = new Map();
|
||||
|
||||
function getDiscoveredServices() {
|
||||
return Array.from(discoveredServices.values());
|
||||
}
|
||||
|
||||
function startBonjourDiscovery() {
|
||||
const browser = bonjour.find({});
|
||||
|
||||
browser.on('up', (service) => {
|
||||
console.log('[bonjour] Service up:', service.name);
|
||||
discoveredServices.set(service.fqdn, service);
|
||||
notifyServicesChanged();
|
||||
});
|
||||
|
||||
browser.on('down', (service) => {
|
||||
console.log('[bonjour] Service down:', service.name);
|
||||
discoveredServices.delete(service.fqdn);
|
||||
notifyServicesChanged();
|
||||
});
|
||||
|
||||
return browser;
|
||||
}
|
||||
|
||||
function notifyServicesChanged() {
|
||||
const services = getDiscoveredServices();
|
||||
BrowserWindow.getAllWindows().forEach((win) => {
|
||||
win.webContents.send('bonjour:services-updated', services);
|
||||
});
|
||||
}
|
||||
|
||||
// ========== OpenCode 服务管理 ==========
|
||||
const DEFAULT_PORT = 4096;
|
||||
let opencodeProcess = null;
|
||||
@@ -36,7 +70,10 @@ function waitForReady(port, timeout = 15000) {
|
||||
const start = Date.now();
|
||||
const check = () => {
|
||||
const socket = net.createConnection({ port, host: '127.0.0.1' });
|
||||
socket.once('connect', () => { socket.end(); resolve(); });
|
||||
socket.once('connect', () => {
|
||||
socket.end();
|
||||
resolve();
|
||||
});
|
||||
socket.once('error', () => {
|
||||
socket.destroy();
|
||||
if (Date.now() - start >= timeout) return reject(new Error('OpenCode 服务启动超时'));
|
||||
@@ -92,7 +129,9 @@ async function startOpencode() {
|
||||
});
|
||||
|
||||
opencodeProcess.stdout?.on('data', (d) => console.log(`[opencode] ${d.toString().trim()}`));
|
||||
opencodeProcess.stderr?.on('data', (d) => console.error(`[opencode error] ${d.toString().trim()}`));
|
||||
opencodeProcess.stderr?.on('data', (d) =>
|
||||
console.error(`[opencode error] ${d.toString().trim()}`)
|
||||
);
|
||||
opencodeProcess.once('error', (e) => console.error('[opencode spawn error]', e));
|
||||
opencodeProcess.once('close', (code) => {
|
||||
console.log(`[opencode exited] code=${code}`);
|
||||
@@ -126,7 +165,10 @@ function stopOpencode() {
|
||||
// ========== IPC Handlers ==========
|
||||
function registerIpcHandlers() {
|
||||
ipcMain.handle('opencode:start', () => startOpencode());
|
||||
ipcMain.handle('opencode:stop', () => { stopOpencode(); return buildInfo(); });
|
||||
ipcMain.handle('opencode:stop', () => {
|
||||
stopOpencode();
|
||||
return buildInfo();
|
||||
});
|
||||
ipcMain.handle('opencode:info', () => buildInfo());
|
||||
ipcMain.handle('opencode:port', () => opencodePort);
|
||||
|
||||
@@ -158,6 +200,9 @@ function registerIpcHandlers() {
|
||||
if (!res.ok) throw new Error(`发送消息失败: ${res.status}`);
|
||||
return res.json();
|
||||
});
|
||||
|
||||
// Bonjour
|
||||
ipcMain.handle('bonjour:get-services', () => getDiscoveredServices());
|
||||
}
|
||||
|
||||
// ========== 窗口 ==========
|
||||
@@ -195,14 +240,13 @@ const createWindow = () => {
|
||||
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
|
||||
mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
|
||||
} else {
|
||||
mainWindow.loadFile(
|
||||
path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`)
|
||||
);
|
||||
mainWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`));
|
||||
}
|
||||
};
|
||||
|
||||
app.whenReady().then(() => {
|
||||
registerIpcHandlers();
|
||||
startBonjourDiscovery();
|
||||
createWindow();
|
||||
|
||||
app.on('activate', () => {
|
||||
|
||||
@@ -16,3 +16,12 @@ contextBridge.exposeInMainWorld('opencode', {
|
||||
createSession: (data) => ipcRenderer.invoke('opencode:session:create', data),
|
||||
sendMessage: (sessionId, text) => ipcRenderer.invoke('opencode:session:send', sessionId, text),
|
||||
});
|
||||
|
||||
contextBridge.exposeInMainWorld('bonjour', {
|
||||
getServices: () => ipcRenderer.invoke('bonjour:get-services'),
|
||||
onServicesUpdated: (callback) => {
|
||||
const listener = (_event, services) => callback(services);
|
||||
ipcRenderer.on('bonjour:services-updated', listener);
|
||||
return () => ipcRenderer.removeListener('bonjour:services-updated', listener);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
<el-icon><ChatDotRound /></el-icon>
|
||||
<template #title>OpenCode 对话</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/bonjour">
|
||||
<el-icon><Search /></el-icon>
|
||||
<template #title>发现设备</template>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
|
||||
<!-- 折叠按钮 -->
|
||||
@@ -47,7 +51,9 @@
|
||||
<!-- 主内容区 -->
|
||||
<div class="flex flex-col flex-1 overflow-hidden">
|
||||
<!-- 顶部栏 -->
|
||||
<header class="flex items-center justify-between h-14 px-6 bg-white border-b border-gray-200 shrink-0">
|
||||
<header
|
||||
class="flex items-center justify-between h-14 px-6 bg-white border-b border-gray-200 shrink-0"
|
||||
>
|
||||
<h1 class="text-base font-medium text-gray-700">{{ currentTitle }}</h1>
|
||||
<div class="flex items-center gap-2">
|
||||
<el-avatar :size="32" class="bg-blue-500">U</el-avatar>
|
||||
@@ -68,7 +74,7 @@
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useAppStore } from '@/stores/app';
|
||||
import { House, Monitor, Expand, Fold, Edit, ChatDotRound } from '@element-plus/icons-vue';
|
||||
import { House, Monitor, Expand, Fold, Edit, ChatDotRound, Search } from '@element-plus/icons-vue';
|
||||
|
||||
const route = useRoute();
|
||||
const appStore = useAppStore();
|
||||
|
||||
@@ -17,6 +17,12 @@ const routes = [
|
||||
component: () => import('@/views/chat/ChatView.vue'),
|
||||
meta: { title: 'OpenCode 对话' },
|
||||
},
|
||||
{
|
||||
path: '/bonjour',
|
||||
name: 'Bonjour',
|
||||
component: () => import('@/views/bonjour/BonjourView.vue'),
|
||||
meta: { title: '发现设备' },
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user