|
|
|
@ -46,26 +46,137 @@ const net = { |
|
|
|
const target = normalizePingHost(host) |
|
|
|
const target = normalizePingHost(host) |
|
|
|
if (!target) return false |
|
|
|
if (!target) return false |
|
|
|
const wait = Math.max(1000, Number(timeout) || 2000) |
|
|
|
const wait = Math.max(1000, Number(timeout) || 2000) |
|
|
|
const isIp = nodeNet.isIP(target) !== 0 |
|
|
|
const hosts = [...new Set([target, 'www.qq.com', 'www.microsoft.com'])] |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
for (const currentHost of hosts) { |
|
|
|
if (!isIp) await dns.promises.lookup(target) |
|
|
|
const isIp = nodeNet.isIP(currentHost) !== 0 |
|
|
|
} catch { |
|
|
|
try { |
|
|
|
if (!await trySystemPing(target, wait)) return false |
|
|
|
if (!isIp) await dns.promises.lookup(currentHost) |
|
|
|
|
|
|
|
} catch { |
|
|
|
|
|
|
|
if (!await trySystemPing(currentHost, wait)) continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
for (const port of [443, 80, 53]) { |
|
|
|
|
|
|
|
if (await tryConnect(currentHost, port, wait)) { |
|
|
|
|
|
|
|
ipcRenderer.send('app-network-status', true) |
|
|
|
|
|
|
|
return true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (await trySystemPing(currentHost, wait)) { |
|
|
|
|
|
|
|
ipcRenderer.send('app-network-status', true) |
|
|
|
|
|
|
|
return true |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const online = !!globalThis.navigator?.onLine |
|
|
|
|
|
|
|
ipcRenderer.send('app-network-status', online) |
|
|
|
|
|
|
|
return online |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for (const port of [443, 80, 53]) { |
|
|
|
function currentRouteText() { |
|
|
|
if (await tryConnect(target, port, wait)) return true |
|
|
|
return window.location.hash || window.location.pathname || '#/' |
|
|
|
} |
|
|
|
} |
|
|
|
return trySystemPing(target, wait) |
|
|
|
|
|
|
|
|
|
|
|
function reportRoute() { |
|
|
|
|
|
|
|
ipcRenderer.send('app-route-changed', currentRouteText()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function installRouteReporter() { |
|
|
|
|
|
|
|
reportRoute() |
|
|
|
|
|
|
|
window.addEventListener('hashchange', reportRoute) |
|
|
|
|
|
|
|
window.addEventListener('popstate', reportRoute) |
|
|
|
|
|
|
|
const timer = setInterval(reportRoute, 1000) |
|
|
|
|
|
|
|
window.addEventListener('beforeunload', () => clearInterval(timer)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
installRouteReporter() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function injectShellDrawer() { |
|
|
|
|
|
|
|
if (document.getElementById('__electron_shell_drawer__')) return |
|
|
|
|
|
|
|
const style = document.createElement('style') |
|
|
|
|
|
|
|
style.id = '__electron_shell_drawer_style__' |
|
|
|
|
|
|
|
style.textContent = ` |
|
|
|
|
|
|
|
#__electron_shell_drawer__{position:fixed;left:0;top:0;bottom:0;z-index:2147483647;pointer-events:none;font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif} |
|
|
|
|
|
|
|
#__electron_shell_drawer__ *{box-sizing:border-box} |
|
|
|
|
|
|
|
#__electron_shell_handle__{position:absolute;left:0;top:50%;transform:translateY(-50%);width:18px;height:120px;border-radius:0 12px 12px 0;background:#2f8cff;color:#fff;display:flex;align-items:center;justify-content:center;pointer-events:auto;cursor:pointer;box-shadow:0 2px 10px rgba(0,0,0,.16)} |
|
|
|
|
|
|
|
#__electron_shell_panel__{position:absolute;left:0;top:56px;bottom:56px;width:320px;max-width:88vw;background:rgba(255,255,255,.98);border-right:1px solid rgba(0,0,0,.08);box-shadow:6px 0 24px rgba(0,0,0,.12);transform:translateX(-100%);transition:transform .18s ease;pointer-events:auto;overflow:auto;padding:12px} |
|
|
|
|
|
|
|
#__electron_shell_drawer__.open #__electron_shell_panel__{transform:translateX(0)} |
|
|
|
|
|
|
|
#__electron_shell_header__{font-size:13px;font-weight:600;margin:0 0 10px} |
|
|
|
|
|
|
|
#__electron_shell_row__{display:flex;gap:8px;align-items:center;margin:8px 0} |
|
|
|
|
|
|
|
#__electron_shell_url__{width:100%;padding:8px 10px;border:1px solid #cfd7e3;border-radius:6px;font-size:13px;outline:none} |
|
|
|
|
|
|
|
.__electron_shell_btn__{border:1px solid #c7d7ef;background:#fff;color:#1f3f6d;border-radius:6px;padding:7px 10px;font-size:12px;cursor:pointer} |
|
|
|
|
|
|
|
.__electron_shell_btn_primary__{background:#2f8cff;border-color:#2f8cff;color:#fff} |
|
|
|
|
|
|
|
#__electron_shell_status__{font-size:12px;line-height:1.5;color:#445} |
|
|
|
|
|
|
|
#__electron_shell_routes__{margin-top:8px;font-size:12px;word-break:break-all;color:#234} |
|
|
|
|
|
|
|
` |
|
|
|
|
|
|
|
document.head.appendChild(style) |
|
|
|
|
|
|
|
const wrap = document.createElement('div') |
|
|
|
|
|
|
|
wrap.id = '__electron_shell_drawer__' |
|
|
|
|
|
|
|
wrap.innerHTML = ` |
|
|
|
|
|
|
|
<div id="__electron_shell_handle__">›</div> |
|
|
|
|
|
|
|
<div id="__electron_shell_panel__"> |
|
|
|
|
|
|
|
<div id="__electron_shell_header__">壳层入口</div> |
|
|
|
|
|
|
|
<div id="__electron_shell_status__"></div> |
|
|
|
|
|
|
|
<div id="__electron_shell_row__"> |
|
|
|
|
|
|
|
<input id="__electron_shell_url__" type="text" placeholder="输入 http(s):// 地址" /> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
<div id="__electron_shell_row__"> |
|
|
|
|
|
|
|
<button class="__electron_shell_btn__ __electron_shell_btn_primary__" id="__electron_shell_open__">打开网址</button> |
|
|
|
|
|
|
|
<button class="__electron_shell_btn__" id="__electron_shell_local__">本地包</button> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
<div id="__electron_shell_row__"> |
|
|
|
|
|
|
|
<button class="__electron_shell_btn__" id="__electron_shell_copy__">复制当前页</button> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
<div id="__electron_shell_routes__"></div> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
` |
|
|
|
|
|
|
|
document.body.appendChild(wrap) |
|
|
|
|
|
|
|
const handle = wrap.querySelector('#__electron_shell_handle__') |
|
|
|
|
|
|
|
const panel = wrap.querySelector('#__electron_shell_panel__') |
|
|
|
|
|
|
|
const input = wrap.querySelector('#__electron_shell_url__') |
|
|
|
|
|
|
|
const status = wrap.querySelector('#__electron_shell_status__') |
|
|
|
|
|
|
|
const routes = wrap.querySelector('#__electron_shell_routes__') |
|
|
|
|
|
|
|
const openBtn = wrap.querySelector('#__electron_shell_open__') |
|
|
|
|
|
|
|
const localBtn = wrap.querySelector('#__electron_shell_local__') |
|
|
|
|
|
|
|
const copyBtn = wrap.querySelector('#__electron_shell_copy__') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const syncState = async () => { |
|
|
|
|
|
|
|
const url = await app.shellGetTargetUrl().catch(() => '') |
|
|
|
|
|
|
|
input.value = url || '' |
|
|
|
|
|
|
|
status.textContent = [ |
|
|
|
|
|
|
|
`网络:${globalThis.navigator?.onLine ? '在线' : '离线'}`, |
|
|
|
|
|
|
|
`倾角:${JSON.stringify(sensor.sensorList ? JSON.parse(sensor.sensorList()).data?.some?.(el => String(el?.name || '').includes('HWT6053')) : false)}`, |
|
|
|
|
|
|
|
].join(' | ') |
|
|
|
|
|
|
|
routes.textContent = `当前:${window.location.hash || window.location.pathname || '#/'}` |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
handle.addEventListener('click', () => wrap.classList.toggle('open')) |
|
|
|
|
|
|
|
openBtn.addEventListener('click', async () => { |
|
|
|
|
|
|
|
const url = input.value.trim() |
|
|
|
|
|
|
|
await app.shellSetTargetUrl(url) |
|
|
|
|
|
|
|
location.reload() |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
localBtn.addEventListener('click', async () => { |
|
|
|
|
|
|
|
await app.shellSetTargetUrl('') |
|
|
|
|
|
|
|
location.reload() |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
copyBtn.addEventListener('click', async () => { |
|
|
|
|
|
|
|
await navigator.clipboard?.writeText(window.location.href).catch(() => {}) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
setInterval(syncState, 1500) |
|
|
|
|
|
|
|
syncState() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const shellOpenObserver = new MutationObserver(() => { |
|
|
|
|
|
|
|
if (document.body && !document.getElementById('__electron_shell_drawer__')) injectShellDrawer() |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
shellOpenObserver.observe(document.documentElement || document, { childList: true, subtree: true }) |
|
|
|
|
|
|
|
|
|
|
|
const appCallbacks = { |
|
|
|
const appCallbacks = { |
|
|
|
onFileWrite: null, |
|
|
|
onFileWrite: null, |
|
|
|
onFileRead: null, |
|
|
|
onFileRead: null, |
|
|
|
onFileDelete: null, |
|
|
|
onFileDelete: null, |
|
|
|
onPageModeChanged: null |
|
|
|
onPageModeChanged: null, |
|
|
|
|
|
|
|
onShellOpenUrlDialog: null |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
ipcRenderer.on('app-file-write-callback', (_event, token, filePath, success, err) => { |
|
|
|
ipcRenderer.on('app-file-write-callback', (_event, token, filePath, success, err) => { |
|
|
|
@ -80,12 +191,84 @@ ipcRenderer.on('app-file-delete-callback', (_event, token, filePath, success) => |
|
|
|
ipcRenderer.on('app-page-mode', (_event, mode) => { |
|
|
|
ipcRenderer.on('app-page-mode', (_event, mode) => { |
|
|
|
if (typeof appCallbacks.onPageModeChanged === 'function') appCallbacks.onPageModeChanged(mode) |
|
|
|
if (typeof appCallbacks.onPageModeChanged === 'function') appCallbacks.onPageModeChanged(mode) |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
ipcRenderer.on('app-shell-open-url-dialog', () => { |
|
|
|
|
|
|
|
if (typeof appCallbacks.onShellOpenUrlDialog === 'function') appCallbacks.onShellOpenUrlDialog() |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getRouteQueryValue(name) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
const hash = window.location.hash || '' |
|
|
|
|
|
|
|
const queryText = hash.includes('?') ? hash.slice(hash.indexOf('?') + 1) : window.location.search.slice(1) |
|
|
|
|
|
|
|
return new URLSearchParams(queryText).get(name) |
|
|
|
|
|
|
|
} catch { |
|
|
|
|
|
|
|
return null |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function tryParseJson(value) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
return value ? JSON.parse(value) : null |
|
|
|
|
|
|
|
} catch { |
|
|
|
|
|
|
|
return null |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function restoreZcDataFromList() { |
|
|
|
|
|
|
|
const id = getRouteQueryValue('id') |
|
|
|
|
|
|
|
if (!id) return null |
|
|
|
|
|
|
|
const list = tryParseJson(localStorage.getItem('List')) || [] |
|
|
|
|
|
|
|
const current = list.find(item => String(item?.idString) === String(id)) |
|
|
|
|
|
|
|
if (!current) return null |
|
|
|
|
|
|
|
const restored = current.zcName |
|
|
|
|
|
|
|
? list.filter(item => item?.zcName === current.zcName) |
|
|
|
|
|
|
|
: [current] |
|
|
|
|
|
|
|
if (!restored.length) return null |
|
|
|
|
|
|
|
const value = JSON.stringify(restored) |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
localStorage.setItem('zcData', value) |
|
|
|
|
|
|
|
ipcRenderer.sendSync('set-cache-sync', 'zcData', value) |
|
|
|
|
|
|
|
} catch {} |
|
|
|
|
|
|
|
return value |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function isZcDataUsable(value) { |
|
|
|
|
|
|
|
const id = getRouteQueryValue('id') |
|
|
|
|
|
|
|
if (!id) return true |
|
|
|
|
|
|
|
const data = tryParseJson(value) |
|
|
|
|
|
|
|
return Array.isArray(data) && data.some(item => String(item?.idString) === String(id)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const app = { |
|
|
|
const app = { |
|
|
|
clearCache() { return ipcRenderer.sendSync('app-clear-cache-sync') }, |
|
|
|
clearCache() { return ipcRenderer.sendSync('app-clear-cache-sync') }, |
|
|
|
versionCode() { return ipcRenderer.sendSync('app-version-code-sync') }, |
|
|
|
versionCode() { return ipcRenderer.sendSync('app-version-code-sync') }, |
|
|
|
setCache(key, value) { return ipcRenderer.sendSync('set-cache-sync', key, value) }, |
|
|
|
shellGetTargetUrl() { return ipcRenderer.invoke('app-shell-get-target-url') }, |
|
|
|
getCache(key) { return ipcRenderer.sendSync('get-cache-sync', key) }, |
|
|
|
shellSetTargetUrl(targetUrl) { return ipcRenderer.invoke('app-shell-set-target-url', targetUrl) }, |
|
|
|
|
|
|
|
setCache(key, value) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
if (key != null) localStorage.setItem(String(key), String(value ?? '')) |
|
|
|
|
|
|
|
} catch {} |
|
|
|
|
|
|
|
return ipcRenderer.sendSync('set-cache-sync', key, value) |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
getCache(key) { |
|
|
|
|
|
|
|
const value = ipcRenderer.sendSync('get-cache-sync', key) |
|
|
|
|
|
|
|
if (value != null) { |
|
|
|
|
|
|
|
if (key !== 'zcData' || isZcDataUsable(value)) return value |
|
|
|
|
|
|
|
const restored = restoreZcDataFromList() |
|
|
|
|
|
|
|
if (restored != null) return restored |
|
|
|
|
|
|
|
return value |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
const localValue = localStorage.getItem(String(key)) |
|
|
|
|
|
|
|
if (localValue != null) { |
|
|
|
|
|
|
|
if (key !== 'zcData' || isZcDataUsable(localValue)) return localValue |
|
|
|
|
|
|
|
const restored = restoreZcDataFromList() |
|
|
|
|
|
|
|
if (restored != null) return restored |
|
|
|
|
|
|
|
return localValue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} catch {} |
|
|
|
|
|
|
|
if (key === 'zcData') return restoreZcDataFromList() |
|
|
|
|
|
|
|
return null |
|
|
|
|
|
|
|
}, |
|
|
|
offlinePage() { ipcRenderer.send('app-offline-page') }, |
|
|
|
offlinePage() { ipcRenderer.send('app-offline-page') }, |
|
|
|
onlinePage() { ipcRenderer.send('app-online-page') }, |
|
|
|
onlinePage() { ipcRenderer.send('app-online-page') }, |
|
|
|
exportFile(filename, content, token) { ipcRenderer.send('app-export-file', filename, content, token) }, |
|
|
|
exportFile(filename, content, token) { ipcRenderer.send('app-export-file', filename, content, token) }, |
|
|
|
@ -100,7 +283,9 @@ const app = { |
|
|
|
get onFileDelete() { return appCallbacks.onFileDelete }, |
|
|
|
get onFileDelete() { return appCallbacks.onFileDelete }, |
|
|
|
set onFileDelete(fn) { appCallbacks.onFileDelete = fn }, |
|
|
|
set onFileDelete(fn) { appCallbacks.onFileDelete = fn }, |
|
|
|
get onPageModeChanged() { return appCallbacks.onPageModeChanged }, |
|
|
|
get onPageModeChanged() { return appCallbacks.onPageModeChanged }, |
|
|
|
set onPageModeChanged(fn) { appCallbacks.onPageModeChanged = fn } |
|
|
|
set onPageModeChanged(fn) { appCallbacks.onPageModeChanged = fn }, |
|
|
|
|
|
|
|
get onShellOpenUrlDialog() { return appCallbacks.onShellOpenUrlDialog }, |
|
|
|
|
|
|
|
set onShellOpenUrlDialog(fn) { appCallbacks.onShellOpenUrlDialog = fn } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const sensorCallbacks = { |
|
|
|
const sensorCallbacks = { |
|
|
|
@ -139,30 +324,87 @@ const sensor = { |
|
|
|
set onOperationCallback(fn) { sensorCallbacks.onOperationCallback = fn } |
|
|
|
set onOperationCallback(fn) { sensorCallbacks.onOperationCallback = fn } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const appSensorCallbacks = { onValueCallback: null } |
|
|
|
const appSensorDataArr = [] |
|
|
|
const appSensor = { |
|
|
|
const nativeAppSensor = { |
|
|
|
version: '1.0.0', |
|
|
|
version: '1.0.0', |
|
|
|
name: 'appSensor', |
|
|
|
name: 'appSensor', |
|
|
|
dataArr: [], |
|
|
|
|
|
|
|
open() { return ipcRenderer.sendSync('app-sensor-open-sync') }, |
|
|
|
open() { return ipcRenderer.sendSync('app-sensor-open-sync') }, |
|
|
|
close() { return ipcRenderer.sendSync('app-sensor-close-sync') }, |
|
|
|
close() { return ipcRenderer.sendSync('app-sensor-close-sync') }, |
|
|
|
isOpen() { return ipcRenderer.sendSync('app-sensor-is-open-sync') }, |
|
|
|
isOpen() { return ipcRenderer.sendSync('app-sensor-is-open-sync') } |
|
|
|
get onValueCallback() { return appSensorCallbacks.onValueCallback }, |
|
|
|
|
|
|
|
set onValueCallback(fn) { appSensorCallbacks.onValueCallback = fn } |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
ipcRenderer.on('sensor-data', (_event, data) => { |
|
|
|
ipcRenderer.on('sensor-data', (_event, data) => { |
|
|
|
if (data && data.sensor && data.method) { |
|
|
|
if (data && data.sensor && data.method) { |
|
|
|
const index = appSensor.dataArr.findIndex(item => item.sensor === data.sensor && item.method === data.method) |
|
|
|
const index = appSensorDataArr.findIndex(item => item.sensor === data.sensor && item.method === data.method) |
|
|
|
if (index >= 0) { |
|
|
|
if (index >= 0) { |
|
|
|
appSensor.dataArr.splice(index, 1, data) |
|
|
|
appSensorDataArr.splice(index, 1, data) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
appSensor.dataArr.push(data) |
|
|
|
appSensorDataArr.push(data) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (typeof appSensorCallbacks.onValueCallback === 'function') appSensorCallbacks.onValueCallback(data) |
|
|
|
window.postMessage({ __electronSensorData: data }, '*') |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function injectAppSensorBridge() { |
|
|
|
|
|
|
|
const source = ` |
|
|
|
|
|
|
|
;(() => { |
|
|
|
|
|
|
|
const dataArr = [] |
|
|
|
|
|
|
|
const callbacks = { onValueCallback: null } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function upsertSensorData(data) { |
|
|
|
|
|
|
|
const targetArr = window.appSensor && Array.isArray(window.appSensor.dataArr) |
|
|
|
|
|
|
|
? window.appSensor.dataArr |
|
|
|
|
|
|
|
: dataArr |
|
|
|
|
|
|
|
if (data && data.sensor && data.method) { |
|
|
|
|
|
|
|
const index = targetArr.findIndex(item => item.sensor === data.sensor && item.method === data.method) |
|
|
|
|
|
|
|
if (index >= 0) targetArr.splice(index, 1, data) |
|
|
|
|
|
|
|
else targetArr.push(data) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (typeof callbacks.onValueCallback === 'function') callbacks.onValueCallback(data) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Object.defineProperty(window, 'appSensor', { |
|
|
|
|
|
|
|
configurable: true, |
|
|
|
|
|
|
|
enumerable: true, |
|
|
|
|
|
|
|
value: { |
|
|
|
|
|
|
|
version: '1.0.0', |
|
|
|
|
|
|
|
name: 'appSensor', |
|
|
|
|
|
|
|
dataArr, |
|
|
|
|
|
|
|
open() { return window.__electronNativeAppSensor.open() }, |
|
|
|
|
|
|
|
close() { return window.__electronNativeAppSensor.close() }, |
|
|
|
|
|
|
|
isOpen() { return window.__electronNativeAppSensor.isOpen() }, |
|
|
|
|
|
|
|
get onValueCallback() { return callbacks.onValueCallback }, |
|
|
|
|
|
|
|
set onValueCallback(fn) { callbacks.onValueCallback = fn } |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
window.addEventListener('message', event => { |
|
|
|
|
|
|
|
const payload = event.data |
|
|
|
|
|
|
|
if (!payload || !payload.__electronSensorData) return |
|
|
|
|
|
|
|
upsertSensorData(payload.__electronSensorData) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
})() |
|
|
|
|
|
|
|
` |
|
|
|
|
|
|
|
const script = document.createElement('script') |
|
|
|
|
|
|
|
script.textContent = source |
|
|
|
|
|
|
|
;(document.documentElement || document.head).appendChild(script) |
|
|
|
|
|
|
|
script.remove() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function injectAppSensorBridgeWhenReady() { |
|
|
|
|
|
|
|
if (document.documentElement || document.head) { |
|
|
|
|
|
|
|
injectAppSensorBridge() |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
const timer = setInterval(() => { |
|
|
|
|
|
|
|
if (!(document.documentElement || document.head)) return |
|
|
|
|
|
|
|
clearInterval(timer) |
|
|
|
|
|
|
|
injectAppSensorBridge() |
|
|
|
|
|
|
|
}, 0) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
injectAppSensorBridgeWhenReady() |
|
|
|
|
|
|
|
|
|
|
|
const appLocationCallbacks = { |
|
|
|
const appLocationCallbacks = { |
|
|
|
onOpened: null, |
|
|
|
onOpened: null, |
|
|
|
onLocationChanged: null |
|
|
|
onLocationChanged: null |
|
|
|
@ -206,8 +448,18 @@ for (const [eventName, cbName] of [ |
|
|
|
['usb-callback-device-data', 'onDeviceData'] |
|
|
|
['usb-callback-device-data', 'onDeviceData'] |
|
|
|
]) { |
|
|
|
]) { |
|
|
|
ipcRenderer.on(eventName, (_event, jsonStr) => { |
|
|
|
ipcRenderer.on(eventName, (_event, jsonStr) => { |
|
|
|
|
|
|
|
let parsed = jsonStr |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
if (typeof jsonStr === 'string') parsed = JSON.parse(jsonStr) |
|
|
|
|
|
|
|
} catch {} |
|
|
|
|
|
|
|
console.log(`[Electron USB] ${cbName}`, parsed) |
|
|
|
const cb = usbSerialCallbacks[cbName] |
|
|
|
const cb = usbSerialCallbacks[cbName] |
|
|
|
if (typeof cb === 'function') cb(jsonStr) |
|
|
|
if (typeof cb === 'function') cb(jsonStr) |
|
|
|
|
|
|
|
window.postMessage({ |
|
|
|
|
|
|
|
__electronUsbSerialEvent: true, |
|
|
|
|
|
|
|
callbackName: cbName, |
|
|
|
|
|
|
|
value: jsonStr |
|
|
|
|
|
|
|
}, '*') |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -243,6 +495,84 @@ const usbSerial = { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function injectUsbSerialBridge() { |
|
|
|
|
|
|
|
const source = ` |
|
|
|
|
|
|
|
;(() => { |
|
|
|
|
|
|
|
if (window.usbSerial) return |
|
|
|
|
|
|
|
const callbacks = { |
|
|
|
|
|
|
|
onDeviceAttached: null, |
|
|
|
|
|
|
|
onDeviceDetached: null, |
|
|
|
|
|
|
|
onDeviceDiscovered: null, |
|
|
|
|
|
|
|
onDeviceProbeResult: null, |
|
|
|
|
|
|
|
onDeviceOpened: null, |
|
|
|
|
|
|
|
onDeviceState: null, |
|
|
|
|
|
|
|
onDeviceData: null |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Object.defineProperty(window, 'usbSerial', { |
|
|
|
|
|
|
|
configurable: true, |
|
|
|
|
|
|
|
enumerable: true, |
|
|
|
|
|
|
|
value: { |
|
|
|
|
|
|
|
get onDeviceAttached() { return callbacks.onDeviceAttached }, |
|
|
|
|
|
|
|
set onDeviceAttached(fn) { callbacks.onDeviceAttached = fn }, |
|
|
|
|
|
|
|
get onDeviceDetached() { return callbacks.onDeviceDetached }, |
|
|
|
|
|
|
|
set onDeviceDetached(fn) { callbacks.onDeviceDetached = fn }, |
|
|
|
|
|
|
|
get onDeviceDiscovered() { return callbacks.onDeviceDiscovered }, |
|
|
|
|
|
|
|
set onDeviceDiscovered(fn) { callbacks.onDeviceDiscovered = fn }, |
|
|
|
|
|
|
|
get onDeviceProbeResult() { return callbacks.onDeviceProbeResult }, |
|
|
|
|
|
|
|
set onDeviceProbeResult(fn) { callbacks.onDeviceProbeResult = fn }, |
|
|
|
|
|
|
|
get onDeviceOpened() { return callbacks.onDeviceOpened }, |
|
|
|
|
|
|
|
set onDeviceOpened(fn) { callbacks.onDeviceOpened = fn }, |
|
|
|
|
|
|
|
get onDeviceState() { return callbacks.onDeviceState }, |
|
|
|
|
|
|
|
set onDeviceState(fn) { callbacks.onDeviceState = fn }, |
|
|
|
|
|
|
|
get onDeviceData() { return callbacks.onDeviceData }, |
|
|
|
|
|
|
|
set onDeviceData(fn) { callbacks.onDeviceData = fn }, |
|
|
|
|
|
|
|
startDiscovery() { window.__electronNativeUsbSerial.startDiscovery() }, |
|
|
|
|
|
|
|
stopDiscovery() { window.__electronNativeUsbSerial.stopDiscovery() }, |
|
|
|
|
|
|
|
getDiscoveredDevices() { return window.__electronNativeUsbSerial.getDiscoveredDevices() }, |
|
|
|
|
|
|
|
deviceProbe(deviceName, portNumber, baudRate, hexCmd, timeoutMs) { |
|
|
|
|
|
|
|
return window.__electronNativeUsbSerial.deviceProbe(deviceName, portNumber, baudRate, hexCmd, timeoutMs) |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
deviceOpen(deviceName, portNumber, baudRate) { |
|
|
|
|
|
|
|
return window.__electronNativeUsbSerial.deviceOpen(deviceName, portNumber, baudRate) |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
deviceClose(deviceName, portNumber) { |
|
|
|
|
|
|
|
return window.__electronNativeUsbSerial.deviceClose(deviceName, portNumber) |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
deviceWrite(deviceName, portNumber, hexData) { |
|
|
|
|
|
|
|
return window.__electronNativeUsbSerial.deviceWrite(deviceName, portNumber, hexData) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
window.addEventListener('message', event => { |
|
|
|
|
|
|
|
const payload = event.data |
|
|
|
|
|
|
|
if (!payload || !payload.__electronUsbSerialEvent) return |
|
|
|
|
|
|
|
const cb = callbacks[payload.callbackName] |
|
|
|
|
|
|
|
if (typeof cb === 'function') cb(payload.value) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
})() |
|
|
|
|
|
|
|
` |
|
|
|
|
|
|
|
const script = document.createElement('script') |
|
|
|
|
|
|
|
script.textContent = source |
|
|
|
|
|
|
|
;(document.documentElement || document.head).appendChild(script) |
|
|
|
|
|
|
|
script.remove() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function injectUsbSerialBridgeWhenReady() { |
|
|
|
|
|
|
|
if (document.documentElement || document.head) { |
|
|
|
|
|
|
|
injectUsbSerialBridge() |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
const timer = setInterval(() => { |
|
|
|
|
|
|
|
if (!(document.documentElement || document.head)) return |
|
|
|
|
|
|
|
clearInterval(timer) |
|
|
|
|
|
|
|
injectUsbSerialBridge() |
|
|
|
|
|
|
|
}, 0) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
injectUsbSerialBridgeWhenReady() |
|
|
|
|
|
|
|
|
|
|
|
const bluetoothSppCallbacks = { |
|
|
|
const bluetoothSppCallbacks = { |
|
|
|
onDeviceSelected: null, |
|
|
|
onDeviceSelected: null, |
|
|
|
onDeviceOpened: null, |
|
|
|
onDeviceOpened: null, |
|
|
|
@ -289,7 +619,7 @@ const bluetoothSpp = { |
|
|
|
contextBridge.exposeInMainWorld('app', app) |
|
|
|
contextBridge.exposeInMainWorld('app', app) |
|
|
|
contextBridge.exposeInMainWorld('net', net) |
|
|
|
contextBridge.exposeInMainWorld('net', net) |
|
|
|
contextBridge.exposeInMainWorld('sensor', sensor) |
|
|
|
contextBridge.exposeInMainWorld('sensor', sensor) |
|
|
|
contextBridge.exposeInMainWorld('appSensor', appSensor) |
|
|
|
contextBridge.exposeInMainWorld('__electronNativeAppSensor', nativeAppSensor) |
|
|
|
contextBridge.exposeInMainWorld('appLocation', appLocation) |
|
|
|
contextBridge.exposeInMainWorld('appLocation', appLocation) |
|
|
|
contextBridge.exposeInMainWorld('usbSerial', usbSerial) |
|
|
|
contextBridge.exposeInMainWorld('__electronNativeUsbSerial', usbSerial) |
|
|
|
contextBridge.exposeInMainWorld('bluetoothSpp', bluetoothSpp) |
|
|
|
contextBridge.exposeInMainWorld('bluetoothSpp', bluetoothSpp) |
|
|
|
|