mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-26 00:53:54 +08:00
@@ -904,17 +904,6 @@ body {
|
|||||||
|
|
||||||
.mcp-tools-panel {
|
.mcp-tools-panel {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
transition: max-height 0.3s ease;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mcp-tools-panel.expanded {
|
|
||||||
max-height: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mcp-tools-panel:not(.expanded) {
|
|
||||||
max-height: 0;
|
|
||||||
padding: 0 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mcp-tools-list {
|
.mcp-tools-list {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// 主应用入口
|
// 主应用入口
|
||||||
import { checkOpusLoaded, initOpusEncoder } from './core/audio/opus-codec.js?v=0127';
|
import { checkOpusLoaded, initOpusEncoder } from './core/audio/opus-codec.js?v=0205';
|
||||||
import { getAudioPlayer } from './core/audio/player.js?v=0127';
|
import { getAudioPlayer } from './core/audio/player.js?v=0205';
|
||||||
import { checkMicrophoneAvailability, isHttpNonLocalhost } from './core/audio/recorder.js?v=0127';
|
import { checkMicrophoneAvailability, isHttpNonLocalhost } from './core/audio/recorder.js?v=0205';
|
||||||
import { initMcpTools } from './core/mcp/tools.js?v=0127';
|
import { initMcpTools } from './core/mcp/tools.js?v=0205';
|
||||||
import { uiController } from './ui/controller.js?v=0127';
|
import { uiController } from './ui/controller.js?v=0205';
|
||||||
import { log } from './utils/logger.js?v=0127';
|
import { log } from './utils/logger.js?v=0205';
|
||||||
|
|
||||||
// 辅助函数:将Base64数据转换为Blob
|
// 辅助函数:将Base64数据转换为Blob
|
||||||
function dataURItoBlob(dataURI) {
|
function dataURItoBlob(dataURI) {
|
||||||
@@ -254,7 +254,7 @@ class App {
|
|||||||
if (!url || !token) {
|
if (!url || !token) {
|
||||||
throw new Error('视觉分析失败:配置缺少接口地址(url)或令牌(token)');
|
throw new Error('视觉分析失败:配置缺少接口地址(url)或令牌(token)');
|
||||||
}
|
}
|
||||||
|
|
||||||
log(`正在发送图片到视觉分析接口: ${url}`, 'info');
|
log(`正在发送图片到视觉分析接口: ${url}`, 'info');
|
||||||
|
|
||||||
const deviceId = document.getElementById('deviceMac')?.value || '';
|
const deviceId = document.getElementById('deviceMac')?.value || '';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { log } from '../../utils/logger.js?v=0127';
|
import { log } from '../../utils/logger.js?v=0205';
|
||||||
|
|
||||||
|
|
||||||
// 检查Opus库是否已加载
|
// 检查Opus库是否已加载
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// 音频播放模块
|
// 音频播放模块
|
||||||
import BlockingQueue from '../../utils/blocking-queue.js?v=0127';
|
import BlockingQueue from '../../utils/blocking-queue.js?v=0205';
|
||||||
import { log } from '../../utils/logger.js?v=0127';
|
import { log } from '../../utils/logger.js?v=0205';
|
||||||
import { createStreamingContext } from './stream-context.js?v=0127';
|
import { createStreamingContext } from './stream-context.js?v=0205';
|
||||||
|
|
||||||
// 音频播放器类
|
// 音频播放器类
|
||||||
export class AudioPlayer {
|
export class AudioPlayer {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Audio recording module
|
// Audio recording module
|
||||||
import { log } from '../../utils/logger.js?v=0127';
|
import { log } from '../../utils/logger.js?v=0205';
|
||||||
import { initOpusEncoder } from './opus-codec.js?v=0127';
|
import { initOpusEncoder } from './opus-codec.js?v=0205';
|
||||||
import { getAudioPlayer } from './player.js?v=0127';
|
import { getAudioPlayer } from './player.js?v=0205';
|
||||||
|
|
||||||
// Audio recorder class
|
// Audio recorder class
|
||||||
export class AudioRecorder {
|
export class AudioRecorder {
|
||||||
@@ -204,7 +204,7 @@ export class AudioRecorder {
|
|||||||
if (this.isRecording) return false;
|
if (this.isRecording) return false;
|
||||||
try {
|
try {
|
||||||
// Check if WebSocketHandler instance exists
|
// Check if WebSocketHandler instance exists
|
||||||
const { getWebSocketHandler } = await import('../network/websocket.js?v=0127');
|
const { getWebSocketHandler } = await import('../network/websocket.js?v=0205');
|
||||||
const wsHandler = getWebSocketHandler();
|
const wsHandler = getWebSocketHandler();
|
||||||
// If machine is speaking, send abort message
|
// If machine is speaking, send abort message
|
||||||
if (wsHandler && wsHandler.isRemoteSpeaking && wsHandler.currentSessionId) {
|
if (wsHandler && wsHandler.isRemoteSpeaking && wsHandler.currentSessionId) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import BlockingQueue from '../../utils/blocking-queue.js?v=0127';
|
import BlockingQueue from '../../utils/blocking-queue.js?v=0205';
|
||||||
import { log } from '../../utils/logger.js?v=0127';
|
import { log } from '../../utils/logger.js?v=0205';
|
||||||
|
|
||||||
// 音频流播放上下文类
|
// 音频流播放上下文类
|
||||||
export class StreamingContext {
|
export class StreamingContext {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { log } from '../../utils/logger.js?v=0127';
|
import { log } from '../../utils/logger.js?v=0205';
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// MCP 工具管理逻辑
|
// MCP 工具管理逻辑
|
||||||
@@ -45,10 +45,7 @@ export async function initMcpTools() {
|
|||||||
mcpTools = [...defaultMcpTools];
|
mcpTools = [...defaultMcpTools];
|
||||||
}
|
}
|
||||||
renderMcpTools();
|
renderMcpTools();
|
||||||
// Only setup event listeners if DOM elements exist
|
setupMcpEventListeners();
|
||||||
if (document.getElementById('toggleMcpTools')) {
|
|
||||||
setupMcpEventListeners();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -280,7 +277,6 @@ function deleteMcpProperty(index) {
|
|||||||
* 设置事件监听
|
* 设置事件监听
|
||||||
*/
|
*/
|
||||||
function setupMcpEventListeners() {
|
function setupMcpEventListeners() {
|
||||||
const toggleBtn = document.getElementById('toggleMcpTools');
|
|
||||||
const panel = document.getElementById('mcpToolsPanel');
|
const panel = document.getElementById('mcpToolsPanel');
|
||||||
const addBtn = document.getElementById('addMcpToolBtn');
|
const addBtn = document.getElementById('addMcpToolBtn');
|
||||||
const modal = document.getElementById('mcpToolModal');
|
const modal = document.getElementById('mcpToolModal');
|
||||||
@@ -297,32 +293,19 @@ function setupMcpEventListeners() {
|
|||||||
const propertyTypeSelect = document.getElementById('mcpPropertyType');
|
const propertyTypeSelect = document.getElementById('mcpPropertyType');
|
||||||
|
|
||||||
// Return early if required elements don't exist (e.g., in test environment)
|
// Return early if required elements don't exist (e.g., in test environment)
|
||||||
if (!toggleBtn || !panel || !addBtn || !modal || !closeBtn || !cancelBtn || !form || !addPropertyBtn) {
|
if (!panel || !addBtn || !modal || !closeBtn || !cancelBtn || !form || !addPropertyBtn) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
toggleBtn.addEventListener('click', () => {
|
|
||||||
const isExpanded = panel.classList.contains('expanded');
|
|
||||||
panel.classList.toggle('expanded');
|
|
||||||
toggleBtn.textContent = isExpanded ? '收起' : '展开';
|
|
||||||
});
|
|
||||||
// 确保面板默认展开
|
|
||||||
panel.classList.add('expanded');
|
|
||||||
addBtn.addEventListener('click', () => openMcpModal());
|
addBtn.addEventListener('click', () => openMcpModal());
|
||||||
closeBtn.addEventListener('click', closeMcpModal);
|
closeBtn.addEventListener('click', closeMcpModal);
|
||||||
cancelBtn.addEventListener('click', closeMcpModal);
|
cancelBtn.addEventListener('click', closeMcpModal);
|
||||||
addPropertyBtn.addEventListener('click', addMcpProperty);
|
addPropertyBtn.addEventListener('click', addMcpProperty);
|
||||||
modal.addEventListener('click', (e) => {
|
|
||||||
if (e.target === modal) closeMcpModal();
|
|
||||||
});
|
|
||||||
form.addEventListener('submit', handleMcpSubmit);
|
form.addEventListener('submit', handleMcpSubmit);
|
||||||
|
|
||||||
// 参数编辑模态框事件
|
// 参数编辑模态框事件
|
||||||
if (propertyModal && closePropertyBtn && cancelPropertyBtn && propertyForm && propertyTypeSelect) {
|
if (propertyModal && closePropertyBtn && cancelPropertyBtn && propertyForm && propertyTypeSelect) {
|
||||||
closePropertyBtn.addEventListener('click', closePropertyModal);
|
closePropertyBtn.addEventListener('click', closePropertyModal);
|
||||||
cancelPropertyBtn.addEventListener('click', closePropertyModal);
|
cancelPropertyBtn.addEventListener('click', closePropertyModal);
|
||||||
propertyModal.addEventListener('click', (e) => {
|
|
||||||
if (e.target === propertyModal) closePropertyModal();
|
|
||||||
});
|
|
||||||
propertyForm.addEventListener('submit', handlePropertySubmit);
|
propertyForm.addEventListener('submit', handlePropertySubmit);
|
||||||
propertyTypeSelect.addEventListener('change', updatePropertyRangeVisibility);
|
propertyTypeSelect.addEventListener('change', updatePropertyRangeVisibility);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { log } from '../../utils/logger.js?v=0127';
|
import { log } from '../../utils/logger.js?v=0205';
|
||||||
|
|
||||||
// WebSocket 连接
|
// WebSocket 连接
|
||||||
export async function webSocketConnect(otaUrl, config) {
|
export async function webSocketConnect(otaUrl, config) {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// WebSocket消息处理模块
|
// WebSocket消息处理模块
|
||||||
import { getConfig, saveConnectionUrls } from '../../config/manager.js?v=0127';
|
import { getConfig, saveConnectionUrls } from '../../config/manager.js?v=0205';
|
||||||
import { uiController } from '../../ui/controller.js?v=0127';
|
import { uiController } from '../../ui/controller.js?v=0205';
|
||||||
import { log } from '../../utils/logger.js?v=0127';
|
import { log } from '../../utils/logger.js?v=0205';
|
||||||
import { getAudioPlayer } from '../audio/player.js?v=0127';
|
import { getAudioPlayer } from '../audio/player.js?v=0205';
|
||||||
import { getAudioRecorder } from '../audio/recorder.js?v=0127';
|
import { getAudioRecorder } from '../audio/recorder.js?v=0205';
|
||||||
import { executeMcpTool, getMcpTools, setWebSocket as setMcpWebSocket } from '../mcp/tools.js?v=0127';
|
import { executeMcpTool, getMcpTools, setWebSocket as setMcpWebSocket } from '../mcp/tools.js?v=0205';
|
||||||
import { webSocketConnect } from './ota-connector.js?v=0127';
|
import { webSocketConnect } from './ota-connector.js?v=0205';
|
||||||
|
|
||||||
// WebSocket处理器类
|
// WebSocket处理器类
|
||||||
export class WebSocketHandler {
|
export class WebSocketHandler {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// UI controller module
|
// UI controller module
|
||||||
import { loadConfig, saveConfig } from '../config/manager.js?v=0127';
|
import { loadConfig, saveConfig } from '../config/manager.js?v=0205';
|
||||||
import { getAudioPlayer } from '../core/audio/player.js?v=0127';
|
import { getAudioPlayer } from '../core/audio/player.js?v=0205';
|
||||||
import { getAudioRecorder } from '../core/audio/recorder.js?v=0127';
|
import { getAudioRecorder } from '../core/audio/recorder.js?v=0205';
|
||||||
import { getWebSocketHandler } from '../core/network/websocket.js?v=0127';
|
import { getWebSocketHandler } from '../core/network/websocket.js?v=0205';
|
||||||
|
|
||||||
// UI controller class
|
// UI controller class
|
||||||
class UIController {
|
class UIController {
|
||||||
@@ -248,13 +248,15 @@ class UIController {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Click modal background to close
|
// 点击模态框背景关闭(仅对特定模态框禁用此功能)
|
||||||
const modals = document.querySelectorAll('.modal');
|
const modals = document.querySelectorAll('.modal');
|
||||||
modals.forEach(modal => {
|
modals.forEach(modal => {
|
||||||
modal.addEventListener('click', (e) => {
|
modal.addEventListener('click', (e) => {
|
||||||
if (e.target === modal) {
|
if (e.target === modal) {
|
||||||
if (modal.id === 'settingsModal') {
|
// settingsModal、mcpToolModal、mcpPropertyModal 只能通过点击X关闭
|
||||||
saveConfig();
|
const nonClosableModals = ['settingsModal', 'mcpToolModal', 'mcpPropertyModal'];
|
||||||
|
if (nonClosableModals.includes(modal.id)) {
|
||||||
|
return; // 禁止点击背景关闭
|
||||||
}
|
}
|
||||||
this.hideModal(modal.id);
|
this.hideModal(modal.id);
|
||||||
}
|
}
|
||||||
@@ -604,7 +606,7 @@ class UIController {
|
|||||||
|
|
||||||
if (isConnected) {
|
if (isConnected) {
|
||||||
// Check microphone availability (check again after connection)
|
// Check microphone availability (check again after connection)
|
||||||
const { checkMicrophoneAvailability } = await import('../core/audio/recorder.js?v=0127');
|
const { checkMicrophoneAvailability } = await import('../core/audio/recorder.js?v=0205');
|
||||||
const micAvailable = await checkMicrophoneAvailability();
|
const micAvailable = await checkMicrophoneAvailability();
|
||||||
|
|
||||||
if (!micAvailable) {
|
if (!micAvailable) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>小智服务器测试页面</title>
|
<title>小智服务器测试页面</title>
|
||||||
<link rel="stylesheet" href="css/test_page.css?v=0127">
|
<link rel="stylesheet" href="css/test_page.css?v=0205">
|
||||||
<script>
|
<script>
|
||||||
// 检测是否使用file://协议打开
|
// 检测是否使用file://协议打开
|
||||||
if (window.location.protocol === 'file:') {
|
if (window.location.protocol === 'file:') {
|
||||||
@@ -129,7 +129,7 @@
|
|||||||
<div class="settings-tabs">
|
<div class="settings-tabs">
|
||||||
<button class="tab-btn active" data-tab="device">设备配置</button>
|
<button class="tab-btn active" data-tab="device">设备配置</button>
|
||||||
<button class="tab-btn" data-tab="mcp">MCP工具</button>
|
<button class="tab-btn" data-tab="mcp">MCP工具</button>
|
||||||
<button class="tab-btn" data-tab="other">其他</button>
|
<button class="tab-btn" data-tab="other">数字人皮肤</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-content active" id="deviceTab">
|
<div class="tab-content active" id="deviceTab">
|
||||||
@@ -180,9 +180,6 @@
|
|||||||
<div class="mcp-tools-container">
|
<div class="mcp-tools-container">
|
||||||
<div class="mcp-tools-header">
|
<div class="mcp-tools-header">
|
||||||
<h3>MCP 工具管理</h3>
|
<h3>MCP 工具管理</h3>
|
||||||
<button class="btn-secondary" id="toggleMcpTools">
|
|
||||||
收起
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mcp-tools-panel" id="mcpToolsPanel">
|
<div class="mcp-tools-panel" id="mcpToolsPanel">
|
||||||
<div class="mcp-tools-list" id="mcpToolsContainer">
|
<div class="mcp-tools-list" id="mcpToolsContainer">
|
||||||
@@ -334,23 +331,23 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 背景加载 -->
|
<!-- 背景加载 -->
|
||||||
<script src="js/ui/background-load.js?v=0127"></script>
|
<script src="js/ui/background-load.js?v=0205"></script>
|
||||||
|
|
||||||
<!-- PIXI.js 2D渲染引擎 -->
|
<!-- PIXI.js 2D渲染引擎 -->
|
||||||
<script src="js/live2d/pixi.js?v=0127"></script>
|
<script src="js/live2d/pixi.js?v=0205"></script>
|
||||||
|
|
||||||
<!-- Live2D Cubism 4.0 SDK -->
|
<!-- Live2D Cubism 4.0 SDK -->
|
||||||
<script src="js/live2d/live2dcubismcore.min.js?v=0127"></script>
|
<script src="js/live2d/live2dcubismcore.min.js?v=0205"></script>
|
||||||
<script src="js/live2d/cubism4.min.js?v=0127"></script>
|
<script src="js/live2d/cubism4.min.js?v=0205"></script>
|
||||||
|
|
||||||
<!-- Live2D 管理器 -->
|
<!-- Live2D 管理器 -->
|
||||||
<script src="js/live2d/live2d.js?v=0127"></script>
|
<script src="js/live2d/live2d.js?v=0205"></script>
|
||||||
|
|
||||||
<!-- Opus解码库 -->
|
<!-- Opus解码库 -->
|
||||||
<script src="js/utils/libopus.js?v=0127"></script>
|
<script src="js/utils/libopus.js?v=0205"></script>
|
||||||
|
|
||||||
<!-- 主应用入口 -->
|
<!-- 主应用入口 -->
|
||||||
<script type="module" src="js/app.js?v=0127"></script>
|
<script type="module" src="js/app.js?v=0205"></script>
|
||||||
|
|
||||||
<!-- 全局错误处理 -->
|
<!-- 全局错误处理 -->
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
Reference in New Issue
Block a user