完善样式系统与调试链路底座

This commit is contained in:
2026-03-30 18:19:05 +08:00
parent 2c0fd4c549
commit 3b9117427e
40 changed files with 7526 additions and 389 deletions

View File

@@ -3,7 +3,9 @@
const DEFAULT_CONFIG_URL = 'https://oss-mbh5.colormaprun.com/wxmini/test/game.json'
const DEFAULT_TILE_URL = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
const PROXY_BASE_URL = `${location.origin}/proxy?url=`
const WS_URL = `ws://${location.hostname}:17865/mock-gps`
const GPS_WS_URL = `ws://${location.hostname}:17865/mock-gps`
const HEART_RATE_WS_URL = `ws://${location.hostname}:17865/mock-hr`
const DEBUG_LOG_WS_URL = `ws://${location.hostname}:17865/debug-log`
const DEFAULT_GATEWAY_BRIDGE_URL = 'ws://127.0.0.1:18080/ws'
const LEGACY_GATEWAY_BRIDGE_URLS = new Set([
'ws://127.0.0.1:8080/ws',
@@ -11,6 +13,7 @@
])
const BRIDGE_CONFIG_STORAGE_KEY = 'mock-gps-sim.bridge-config'
const BRIDGE_PRESETS_STORAGE_KEY = 'mock-gps-sim.bridge-presets'
const MAX_DEBUG_LOG_LINES = 400
const map = L.map('map').setView(DEFAULT_CENTER, 16)
let tileLayer = createTileLayer(DEFAULT_TILE_URL, {
@@ -37,8 +40,13 @@
const pathPoints = []
const state = {
socket: null,
heartRateSocket: null,
debugSocket: null,
connected: false,
heartRateConnected: false,
socketConnecting: false,
heartRateSocketConnecting: false,
debugSocketConnecting: false,
streaming: false,
heartRateStreaming: false,
heartRateSampleMode: false,
@@ -134,6 +142,8 @@
headingText: document.getElementById('headingText'),
pathCountText: document.getElementById('pathCountText'),
log: document.getElementById('log'),
debugLog: document.getElementById('debugLog'),
clearDebugLogBtn: document.getElementById('clearDebugLogBtn'),
}
elements.configUrlInput.value = DEFAULT_CONFIG_URL
@@ -150,6 +160,29 @@
elements.log.textContent = `[${time}] ${message}\n` + elements.log.textContent
}
function logDebug(entry) {
if (!elements.debugLog) {
return
}
const time = new Date(entry.timestamp || Date.now()).toLocaleTimeString()
const scope = String(entry.scope || 'app')
const level = String(entry.level || 'info').toUpperCase()
const message = String(entry.message || '')
const payloadText = entry.payload ? ` ${JSON.stringify(entry.payload)}` : ''
const nextText = `[${time}] [${scope}] [${level}] ${message}${payloadText}\n${elements.debugLog.textContent || ''}`
elements.debugLog.textContent = nextText
.split('\n')
.slice(0, MAX_DEBUG_LOG_LINES)
.join('\n')
}
function clearDebugLog() {
if (elements.debugLog) {
elements.debugLog.textContent = ''
}
}
function setResourceStatus(message, tone) {
elements.resourceStatus.textContent = message
elements.resourceStatus.className = 'hint'
@@ -191,10 +224,10 @@
elements.streamBtn.classList.toggle('is-active', state.streaming)
elements.streamBtn.disabled = !state.connected || state.streaming
elements.stopStreamBtn.disabled = !state.streaming
elements.sendHeartRateOnceBtn.disabled = !state.connected
elements.sendHeartRateOnceBtn.disabled = !state.heartRateConnected
elements.startHeartRateStreamBtn.textContent = state.heartRateStreaming ? '发送中' : '开始连续发送'
elements.startHeartRateStreamBtn.classList.toggle('is-active', state.heartRateStreaming)
elements.startHeartRateStreamBtn.disabled = !state.connected || state.heartRateStreaming
elements.startHeartRateStreamBtn.disabled = !state.heartRateConnected || state.heartRateStreaming
elements.stopHeartRateStreamBtn.disabled = !state.heartRateStreaming
elements.toggleHeartRateSampleBtn.textContent = state.heartRateSampleMode ? '关闭真实样本' : '模拟真实样本'
elements.toggleHeartRateSampleBtn.classList.toggle('is-active', state.heartRateSampleMode)
@@ -250,13 +283,13 @@
elements.realtimeStatus.textContent = '桥接未连接'
}
if (state.connected && state.heartRateStreaming) {
if (state.heartRateConnected && state.heartRateStreaming) {
elements.heartRateStatus.textContent = state.heartRateSampleMode
? `桥接已连接,正在以 ${elements.heartRateHzSelect.value} Hz 发送真实心率样本`
: `桥接已连接,正在以 ${elements.heartRateHzSelect.value} Hz 连续发送心率`
} else if (state.connected) {
} else if (state.heartRateConnected) {
elements.heartRateStatus.textContent = state.heartRateSampleMode ? '真实心率样本待命' : '心率模拟待命'
} else if (state.socketConnecting) {
} else if (state.heartRateSocketConnecting) {
elements.heartRateStatus.textContent = '桥接连接中'
} else {
elements.heartRateStatus.textContent = '桥接未连接'
@@ -476,12 +509,12 @@
return
}
const socket = new WebSocket(WS_URL)
const socket = new WebSocket(GPS_WS_URL)
state.socket = socket
state.socketConnecting = true
setSocketBadge(false)
updateUiState()
log(`连接 ${WS_URL}`)
log(`连接 ${GPS_WS_URL}`)
socket.addEventListener('open', () => {
state.connected = true
@@ -495,7 +528,6 @@
state.connected = false
state.socketConnecting = false
stopStream()
stopHeartRateStream()
setSocketBadge(false)
updateUiState()
log('桥接已断开')
@@ -505,13 +537,91 @@
state.connected = false
state.socketConnecting = false
stopStream()
stopHeartRateStream()
setSocketBadge(false)
updateUiState()
log('桥接连接失败')
})
}
function connectHeartRateSocket() {
if (state.heartRateSocket && (state.heartRateSocket.readyState === WebSocket.OPEN || state.heartRateSocket.readyState === WebSocket.CONNECTING)) {
return
}
const socket = new WebSocket(HEART_RATE_WS_URL)
state.heartRateSocket = socket
state.heartRateSocketConnecting = true
updateUiState()
log(`连接心率模拟 ${HEART_RATE_WS_URL}`)
socket.addEventListener('open', () => {
state.heartRateConnected = true
state.heartRateSocketConnecting = false
updateUiState()
log('心率模拟已连接')
})
socket.addEventListener('close', () => {
state.heartRateConnected = false
state.heartRateSocketConnecting = false
state.heartRateSocket = null
stopHeartRateStream()
updateUiState()
log('心率模拟已断开')
})
socket.addEventListener('error', () => {
state.heartRateConnected = false
state.heartRateSocketConnecting = false
state.heartRateSocket = null
stopHeartRateStream()
updateUiState()
log('心率模拟连接失败')
})
}
function connectDebugSocket() {
if (state.debugSocket && (state.debugSocket.readyState === WebSocket.OPEN || state.debugSocket.readyState === WebSocket.CONNECTING)) {
return
}
const socket = new WebSocket(DEBUG_LOG_WS_URL)
state.debugSocket = socket
state.debugSocketConnecting = true
log(`连接日志通道 ${DEBUG_LOG_WS_URL}`)
socket.addEventListener('message', (event) => {
let parsed = null
try {
parsed = JSON.parse(String(event.data || ''))
} catch (_error) {
return
}
if (parsed && parsed.type === 'debug-log') {
logDebug(parsed)
}
})
socket.addEventListener('open', () => {
state.debugSocketConnecting = false
log('日志通道已连接')
})
socket.addEventListener('close', () => {
state.debugSocketConnecting = false
state.debugSocket = null
log('日志通道已断开')
window.setTimeout(connectDebugSocket, 1500)
})
socket.addEventListener('error', () => {
state.debugSocketConnecting = false
state.debugSocket = null
log('日志通道连接失败')
})
}
async function refreshGatewayBridgeStatus() {
try {
const response = await fetch('/bridge-status', {
@@ -1159,8 +1269,8 @@
}
function sendCurrentHeartRate() {
if (!state.socket || state.socket.readyState !== WebSocket.OPEN) {
log('未连接桥接,无法发送心率')
if (!state.heartRateSocket || state.heartRateSocket.readyState !== WebSocket.OPEN) {
log('未连接心率模拟,无法发送心率')
return
}
@@ -1169,7 +1279,7 @@
timestamp: Date.now(),
bpm: state.heartRateSampleMode ? getSampleHeartRateBpm() : getHeartRateBpm(),
}
state.socket.send(JSON.stringify(payload))
state.heartRateSocket.send(JSON.stringify(payload))
state.lastHeartRateSentText = `${formatClockTime(payload.timestamp)} @ ${payload.bpm} bpm`
updateUiState()
}
@@ -1690,6 +1800,9 @@
stopPlayback()
log('已暂停回放')
})
if (elements.clearDebugLogBtn) {
elements.clearDebugLogBtn.addEventListener('click', clearDebugLog)
}
updateReadout()
setSocketBadge(false)
@@ -1715,4 +1828,6 @@
refreshGatewayBridgeStatus()
window.setInterval(refreshGatewayBridgeStatus, 3000)
connectSocket()
connectHeartRateSocket()
connectDebugSocket()
})()