webserial-core 2.0.1 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/AbstractSerialDevice.d.ts +17 -0
- package/dist/demos/assets/{demo-shared-DnvFynUr.js → demo-shared-BVQKG6NC.js} +46 -46
- package/dist/demos/assets/web-bluetooth-yFc46qqb.js +1 -0
- package/dist/demos/assets/web-serial-DLoCrJS2.js +1 -0
- package/dist/demos/assets/web-usb-De-CD5AK.js +1 -0
- package/dist/demos/assets/websocket-pLqbbDwD.js +1 -0
- package/dist/demos/web-bluetooth.html +6 -6
- package/dist/demos/web-serial.html +6 -6
- package/dist/demos/web-usb.html +6 -6
- package/dist/demos/websocket.html +6 -6
- package/dist/robots.txt +1 -1
- package/dist/webserial-core.cjs +1 -1
- package/dist/webserial-core.mjs +7 -1
- package/dist/webserial-core.umd.js +1 -1
- package/package.json +8 -8
- package/dist/demos/assets/web-bluetooth-VVwTClLx.js +0 -1
- package/dist/demos/assets/web-serial-CsBUUDvz.js +0 -1
- package/dist/demos/assets/web-usb-D7sSHjku.js +0 -1
- package/dist/demos/assets/websocket-DREvCVt-.js +0 -1
|
@@ -43,6 +43,23 @@ export declare abstract class AbstractSerialDevice<T> extends SerialEventEmitter
|
|
|
43
43
|
protected handshake(): Promise<boolean>;
|
|
44
44
|
connect(): Promise<void>;
|
|
45
45
|
disconnect(): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Returns `true` if the device is currently connected and the port
|
|
48
|
+
* is open, readable, and writable. Note that a port can become
|
|
49
|
+
* disconnected at any time (e.g. unplugged), so this is not a
|
|
50
|
+
* guarantee that a subsequent read/write will succeed, but it is
|
|
51
|
+
* a useful check before attempting communication.
|
|
52
|
+
* @returns `true` if the device is connected and ready for communication, `false` otherwise.
|
|
53
|
+
*/
|
|
54
|
+
isConnected(): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Returns `true` if the device is not connected or in the process of connecting.
|
|
57
|
+
* This is a convenience method equivalent to `!isConnected()`, but may be more
|
|
58
|
+
* semantically clear in certain contexts (e.g. when checking for disconnection
|
|
59
|
+
* in a read loop catch block).
|
|
60
|
+
* @returns `true` if the device is disconnected or not ready, `false` if it is currently connected.
|
|
61
|
+
*/
|
|
62
|
+
isDisconnected(): boolean;
|
|
46
63
|
/**
|
|
47
64
|
* Internal cleanup: tears down the port, reader, writer without
|
|
48
65
|
* marking it as user-initiated. This allows auto-reconnect to trigger.
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=class{listeners={};on(e,t){return this.listeners[e]||(this.listeners[e]=new Set),this.listeners[e].add(t),this}off(e,t){return this.listeners[e]&&this.listeners[e].delete(t),this}emit(e,...t){let n=this.listeners[e];if(!n||n.size===0)return!1;for(let e of n)e(...t);return!0}},t=class{static instances=new Set;static portInstanceMap=new WeakMap;static getInstances(){return Array.from(this.instances)}static register(e){this.instances.add(e)}static unregister(e){this.instances.delete(e)}static isPortInUse(e,t){let n=this.portInstanceMap.get(e);return n!==void 0&&n!==t}static lockPort(e,t){this.portInstanceMap.set(e,t)}static unlockPort(e){this.portInstanceMap.delete(e)}},n=class{queue=[];isProcessing=!1;isPaused=!0;timeoutId=null;commandTimeout;onSend;onTimeout;constructor(e){this.commandTimeout=e.commandTimeout,this.onSend=e.onSend,this.onTimeout=e.onTimeout}get queueSize(){return this.queue.length}enqueue(e){this.queue.push(e),this.tryProcessNext()}advance(){this.clearCommandTimeout(),this.isProcessing=!1,this.tryProcessNext()}pause(){this.isPaused=!0,this.clearCommandTimeout(),this.isProcessing=!1}resume(){this.isPaused=!1,this.tryProcessNext()}clear(){this.queue=[],this.clearCommandTimeout(),this.isProcessing=!1}snapshot(){return[...this.queue]}restore(e){this.queue=[...e,...this.queue]}tryProcessNext(){if(this.isPaused||this.isProcessing||this.queue.length===0)return;this.isProcessing=!0;let e=this.queue.shift();this.commandTimeout>0&&(this.timeoutId=setTimeout(()=>{this.timeoutId=null,this.onTimeout(e),this.advance()},this.commandTimeout)),this.onSend(e).catch(()=>{this.advance()})}clearCommandTimeout(){this.timeoutId!==null&&(clearTimeout(this.timeoutId),this.timeoutId=null)}},r=class e extends Error{constructor(t){super(t),this.name=`SerialPermissionError`,Object.setPrototypeOf(this,e.prototype)}},i=class e extends Error{constructor(t){super(t),this.name=`SerialReadError`,Object.setPrototypeOf(this,e.prototype)}},a=class e extends Error{constructor(t){super(t),this.name=`SerialWriteError`,Object.setPrototypeOf(this,e.prototype)}},o=class o extends e{port=null;reader=null;writer=null;queue;options;isConnecting=!1;abortController=null;userInitiatedDisconnect=!1;reconnectTimerId=null;isHandshaking=!1;static customProvider=null;static polyfillOptions;constructor(e){super(),this.options={baudRate:e.baudRate,dataBits:e.dataBits??8,stopBits:e.stopBits??1,parity:e.parity??`none`,bufferSize:e.bufferSize??255,flowControl:e.flowControl??`none`,filters:e.filters??[],commandTimeout:e.commandTimeout??0,parser:e.parser,autoReconnect:e.autoReconnect??!1,autoReconnectInterval:e.autoReconnectInterval??1500,handshakeTimeout:e.handshakeTimeout??2e3,provider:e.provider,polyfillOptions:e.polyfillOptions},this.queue=new n({commandTimeout:this.options.commandTimeout,onSend:async e=>{await this.writeToPort(e),this.emit(`serial:sent`,e,this)},onTimeout:e=>{this.emit(`serial:timeout`,e,this)}}),this.on(`serial:data`,()=>{this.queue.advance()}),t.register(this)}async handshake(){return!0}async connect(){if(!this.isConnecting&&!this.port){this.isConnecting=!0,this.emit(`serial:connecting`,this);try{let e=this.getSerial();if(!e)throw Error(`Web Serial API is not supported in this browser. Use AbstractSerialDevice.setProvider() to set a WebUSB polyfill.`);if(this.port=await this.findAndValidatePort(),!this.port){let t;try{t=await e.requestPort({filters:this.options.filters},this.options.polyfillOptions??o.polyfillOptions)}catch(e){throw e instanceof DOMException&&(e.name===`NotFoundError`||e.name===`SecurityError`||e.name===`AbortError`)?new r(e instanceof Error?e.message:String(e)):e instanceof Error?e:Error(String(e))}if(!await this.openAndHandshake(t))throw Error(`Handshake failed: the selected device did not respond correctly.`);this.port=t}this.abortController=new AbortController,this.queue.resume(),this.emit(`serial:connected`,this)}catch(e){if(e instanceof r?this.emit(`serial:need-permission`,this):this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this),this.port){t.unlockPort(this.port);try{await this.port.close()}catch{}this.port=null}throw e}finally{this.isConnecting=!1}}}async disconnect(){this.port&&(this.userInitiatedDisconnect=!0,this.stopReconnecting(),await this.cleanupPort())}async cleanupPort(){if(this.port){this.queue.pause(),this.abortController?.abort(),this.abortController=null;try{let e=this.reader,t=this.writer;if(this.reader=null,this.writer=null,e){try{await e.cancel()}catch{}try{e.releaseLock()}catch{}}if(t){try{await t.close()}catch{}try{t.releaseLock()}catch{}}try{await this.port.close()}catch{}}catch(e){this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this)}finally{this.port&&t.unlockPort(this.port),this.port=null,this.options.parser?.reset?.(),this.emit(`serial:disconnected`,this),!this.userInitiatedDisconnect&&this.options.autoReconnect&&this.startReconnecting(),this.userInitiatedDisconnect=!1}}}async forget(){await this.disconnect(),this.port&&typeof this.port.forget==`function`&&await this.port.forget(),t.unregister(this)}async send(e){let t;t=typeof e==`string`?new TextEncoder().encode(e):e,t.length>0&&this.queue.enqueue(t)}clearQueue(){this.queue.clear(),this.emit(`serial:queue-empty`,this)}async writeToPort(e){if(!this.port||!this.port.writable)throw new a(`Port not writable.`);this.writer=this.port.writable.getWriter();try{await this.writer.write(e)}catch(e){throw new a(e instanceof Error?e.message:String(e))}finally{this.writer.releaseLock(),this.writer=null}}async readLoop(){if(!(!this.port||!this.port.readable)&&!this.reader){this.reader=this.port.readable.getReader();try{for(;;){let{value:e,done:t}=await this.reader.read();if(t)break;e&&(this.options.parser?this.options.parser.parse(e,e=>{this.emit(`serial:data`,e,this)}):this.emit(`serial:data`,e,this))}}catch(e){if(this.port)throw new i(e instanceof Error?e.message:String(e))}finally{if(this.reader){try{this.reader.releaseLock()}catch{}this.reader=null}}}}async openAndHandshake(e){let n=this;if(t.isPortInUse(e,n))return!1;t.lockPort(e,n);try{await e.open({baudRate:this.options.baudRate,dataBits:this.options.dataBits,stopBits:this.options.stopBits,parity:this.options.parity,bufferSize:this.options.bufferSize,flowControl:this.options.flowControl})}catch(n){throw t.unlockPort(e),n instanceof Error?n:Error(String(n))}this.port=e,this.abortController=new AbortController;let r=this.queue.snapshot();this.isHandshaking=!0,this.readLoop().catch(e=>{!this.isHandshaking&&this.port&&(this.emit(`serial:error`,e,this),this.cleanupPort())}),this.queue.resume();try{let t=await this.runHandshakeWithTimeout();return this.isHandshaking=!1,t?(this.queue.pause(),this.queue.clear(),this.queue.restore(r),this.options.parser?.reset?.(),!0):(await this.teardownHandshake(e,r),!1)}catch{return this.isHandshaking=!1,await this.teardownHandshake(e,r),!1}}async teardownHandshake(e,n){this.queue.pause(),this.queue.clear(),this.queue.restore(n),await this.stopReader(),this.port=null,this.abortController=null,this.options.parser?.reset?.();try{await e.close()}catch{}t.unlockPort(e)}async stopReader(){let e=this.reader;if(this.reader=null,e){try{await e.cancel()}catch{}try{e.releaseLock()}catch{}}}async runHandshakeWithTimeout(){let e=this.options.handshakeTimeout??2e3;return Promise.race([this.handshake(),new Promise(t=>setTimeout(()=>t(!1),e))])}async findAndValidatePort(){let e=this.getSerial();if(!e)return null;let n=await e.getPorts(this.options.polyfillOptions??o.polyfillOptions);if(n.length===0)return null;let r=this.options.filters??[],i=this;for(let e of n)if(!t.isPortInUse(e,i)){if(r.length>0){let t=e.getInfo();if(!r.some(e=>{let n=e.usbVendorId===void 0||e.usbVendorId===t.usbVendorId,r=e.usbProductId===void 0||e.usbProductId===t.usbProductId;return n&&r}))continue}try{if(await this.openAndHandshake(e))return e}catch{}}return null}startReconnecting(){this.reconnectTimerId||=(this.emit(`serial:reconnecting`,this),setInterval(async()=>{if(this.port||this.isConnecting){this.stopReconnecting();return}try{let e=await this.findAndValidatePort();e&&(this.stopReconnecting(),await this.reconnect(e))}catch{}},this.options.autoReconnectInterval))}stopReconnecting(){this.reconnectTimerId&&=(clearInterval(this.reconnectTimerId),null)}async reconnect(e){if(!(this.isConnecting||this.port)){this.isConnecting=!0,this.emit(`serial:connecting`,this);try{this.port=e,this.abortController=new AbortController,this.queue.resume(),this.emit(`serial:connected`,this)}catch(e){this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this),this.port&&=(t.unlockPort(this.port),null),this.options.autoReconnect&&this.startReconnecting()}finally{this.isConnecting=!1}}}static getInstances(){return t.getInstances()}static async connectAll(){let e=t.getInstances();for(let t of e)try{await t.connect()}catch{}}static setProvider(e,t){o.customProvider=e,o.polyfillOptions=t}getSerial(){return this.options.provider?this.options.provider:o.customProvider?o.customProvider:typeof navigator<`u`&&navigator.serial?navigator.serial:null}};function s(e){let t=``,n=new TextDecoder;return{parse(r,i){t+=n.decode(r,{stream:!0});let a;for(;(a=t.indexOf(e))!==-1;)i(t.slice(0,a)),t=t.slice(a+e.length)},reset(){t=``,n=new TextDecoder}}}var c=`wsc-demo-theme`;function l(){return window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`}function u(){let e=localStorage.getItem(c)??l();return document.documentElement.setAttribute(`data-theme`,e),window.matchMedia(`(prefers-color-scheme: dark)`).addEventListener(`change`,e=>{localStorage.getItem(c)||document.documentElement.setAttribute(`data-theme`,e.matches?`dark`:`light`)}),e}function d(){let e=(document.documentElement.getAttribute(`data-theme`)??l())===`dark`?`light`:`dark`;return localStorage.setItem(c,e),document.documentElement.setAttribute(`data-theme`,e),e}function f(e,t,n){let r=e.querySelector(`.empty-state`);r&&r.remove();let{kind:i,label:a,time:o=new Date}=n,s=document.createElement(`div`);if(s.className=`msg ${i}`,a&&(i===`sent`||i===`received`)){let e=document.createElement(`div`);e.className=`msg-label`,e.textContent=a,s.appendChild(e)}let c=document.createElement(`div`);c.className=`msg-bubble`,c.textContent=t,s.appendChild(c);let l=document.createElement(`div`);l.className=`msg-time`,l.textContent=o.toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`,second:`2-digit`}),s.appendChild(l),e.appendChild(s),e.scrollTop=e.scrollHeight}function p(e){e.innerHTML=`
|
|
1
|
+
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=class{listeners={};on(e,t){return this.listeners[e]||(this.listeners[e]=new Set),this.listeners[e].add(t),this}off(e,t){return this.listeners[e]&&this.listeners[e].delete(t),this}emit(e,...t){let n=this.listeners[e];if(!n||n.size===0)return!1;for(let e of n)e(...t);return!0}},t=class{static instances=new Set;static portInstanceMap=new WeakMap;static getInstances(){return Array.from(this.instances)}static register(e){this.instances.add(e)}static unregister(e){this.instances.delete(e)}static isPortInUse(e,t){let n=this.portInstanceMap.get(e);return n!==void 0&&n!==t}static lockPort(e,t){this.portInstanceMap.set(e,t)}static unlockPort(e){this.portInstanceMap.delete(e)}},n=class{queue=[];isProcessing=!1;isPaused=!0;timeoutId=null;commandTimeout;onSend;onTimeout;constructor(e){this.commandTimeout=e.commandTimeout,this.onSend=e.onSend,this.onTimeout=e.onTimeout}get queueSize(){return this.queue.length}enqueue(e){this.queue.push(e),this.tryProcessNext()}advance(){this.clearCommandTimeout(),this.isProcessing=!1,this.tryProcessNext()}pause(){this.isPaused=!0,this.clearCommandTimeout(),this.isProcessing=!1}resume(){this.isPaused=!1,this.tryProcessNext()}clear(){this.queue=[],this.clearCommandTimeout(),this.isProcessing=!1}snapshot(){return[...this.queue]}restore(e){this.queue=[...e,...this.queue]}tryProcessNext(){if(this.isPaused||this.isProcessing||this.queue.length===0)return;this.isProcessing=!0;let e=this.queue.shift();this.commandTimeout>0&&(this.timeoutId=setTimeout(()=>{this.timeoutId=null,this.onTimeout(e),this.advance()},this.commandTimeout)),this.onSend(e).catch(()=>{this.advance()})}clearCommandTimeout(){this.timeoutId!==null&&(clearTimeout(this.timeoutId),this.timeoutId=null)}},r=class e extends Error{constructor(t){super(t),this.name=`SerialPermissionError`,Object.setPrototypeOf(this,e.prototype)}},i=class e extends Error{constructor(t){super(t),this.name=`SerialReadError`,Object.setPrototypeOf(this,e.prototype)}},a=class e extends Error{constructor(t){super(t),this.name=`SerialWriteError`,Object.setPrototypeOf(this,e.prototype)}},o=class o extends e{port=null;reader=null;writer=null;queue;options;isConnecting=!1;abortController=null;userInitiatedDisconnect=!1;reconnectTimerId=null;isHandshaking=!1;static customProvider=null;static polyfillOptions;constructor(e){super(),this.options={baudRate:e.baudRate,dataBits:e.dataBits??8,stopBits:e.stopBits??1,parity:e.parity??`none`,bufferSize:e.bufferSize??255,flowControl:e.flowControl??`none`,filters:e.filters??[],commandTimeout:e.commandTimeout??0,parser:e.parser,autoReconnect:e.autoReconnect??!1,autoReconnectInterval:e.autoReconnectInterval??1500,handshakeTimeout:e.handshakeTimeout??2e3,provider:e.provider,polyfillOptions:e.polyfillOptions},this.queue=new n({commandTimeout:this.options.commandTimeout,onSend:async e=>{await this.writeToPort(e),this.emit(`serial:sent`,e,this)},onTimeout:e=>{this.emit(`serial:timeout`,e,this)}}),this.on(`serial:data`,()=>{this.queue.advance()}),t.register(this)}async handshake(){return!0}async connect(){if(!this.isConnecting&&!this.port){this.isConnecting=!0,this.emit(`serial:connecting`,this);try{let e=this.getSerial();if(!e)throw Error(`Web Serial API is not supported in this browser. Use AbstractSerialDevice.setProvider() to set a WebUSB polyfill.`);if(this.port=await this.findAndValidatePort(),!this.port){let t;try{t=await e.requestPort({filters:this.options.filters},this.options.polyfillOptions??o.polyfillOptions)}catch(e){throw e instanceof DOMException&&(e.name===`NotFoundError`||e.name===`SecurityError`||e.name===`AbortError`)?new r(e instanceof Error?e.message:String(e)):e instanceof Error?e:Error(String(e))}if(!await this.openAndHandshake(t))throw Error(`Handshake failed: the selected device did not respond correctly.`);this.port=t}this.abortController=new AbortController,this.queue.resume(),this.emit(`serial:connected`,this)}catch(e){if(e instanceof r?this.emit(`serial:need-permission`,this):this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this),this.port){t.unlockPort(this.port);try{await this.port.close()}catch{}this.port=null}throw e}finally{this.isConnecting=!1}}}async disconnect(){this.port&&(this.userInitiatedDisconnect=!0,this.stopReconnecting(),await this.cleanupPort())}async cleanupPort(){if(this.port){this.queue.pause(),this.abortController?.abort(),this.abortController=null;try{let e=this.reader,t=this.writer;if(this.reader=null,this.writer=null,e){try{await e.cancel()}catch{}try{e.releaseLock()}catch{}}if(t){try{await t.close()}catch{}try{t.releaseLock()}catch{}}try{await this.port.close()}catch{}}catch(e){this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this)}finally{this.port&&t.unlockPort(this.port),this.port=null,this.options.parser?.reset?.(),this.emit(`serial:disconnected`,this),!this.userInitiatedDisconnect&&this.options.autoReconnect&&this.startReconnecting(),this.userInitiatedDisconnect=!1}}}async forget(){await this.disconnect(),this.port&&typeof this.port.forget==`function`&&await this.port.forget(),t.unregister(this)}async send(e){let t;t=typeof e==`string`?new TextEncoder().encode(e):e,t.length>0&&this.queue.enqueue(t)}clearQueue(){this.queue.clear(),this.emit(`serial:queue-empty`,this)}async writeToPort(e){if(!this.port||!this.port.writable)throw new a(`Port not writable.`);this.writer=this.port.writable.getWriter();try{await this.writer.write(e)}catch(e){throw new a(e instanceof Error?e.message:String(e))}finally{this.writer.releaseLock(),this.writer=null}}async readLoop(){if(!(!this.port||!this.port.readable)&&!this.reader){this.reader=this.port.readable.getReader();try{for(;;){let{value:e,done:t}=await this.reader.read();if(t)break;e&&(this.options.parser?this.options.parser.parse(e,e=>{this.emit(`serial:data`,e,this)}):this.emit(`serial:data`,e,this))}}catch(e){if(this.port)throw new i(e instanceof Error?e.message:String(e))}finally{if(this.reader){try{this.reader.releaseLock()}catch{}this.reader=null}}}}async openAndHandshake(e){let n=this;if(t.isPortInUse(e,n))return!1;t.lockPort(e,n);try{await e.open({baudRate:this.options.baudRate,dataBits:this.options.dataBits,stopBits:this.options.stopBits,parity:this.options.parity,bufferSize:this.options.bufferSize,flowControl:this.options.flowControl})}catch(n){throw t.unlockPort(e),n instanceof Error?n:Error(String(n))}this.port=e,this.abortController=new AbortController;let r=this.queue.snapshot();this.isHandshaking=!0,this.readLoop().catch(e=>{!this.isHandshaking&&this.port&&(this.emit(`serial:error`,e,this),this.cleanupPort())}),this.queue.resume();try{let t=await this.runHandshakeWithTimeout();return this.isHandshaking=!1,t?(this.queue.pause(),this.queue.clear(),this.queue.restore(r),this.options.parser?.reset?.(),!0):(await this.teardownHandshake(e,r),!1)}catch{return this.isHandshaking=!1,await this.teardownHandshake(e,r),!1}}async teardownHandshake(e,n){this.queue.pause(),this.queue.clear(),this.queue.restore(n),await this.stopReader(),this.port=null,this.abortController=null,this.options.parser?.reset?.();try{await e.close()}catch{}t.unlockPort(e)}async stopReader(){let e=this.reader;if(this.reader=null,e){try{await e.cancel()}catch{}try{e.releaseLock()}catch{}}}async runHandshakeWithTimeout(){let e=this.options.handshakeTimeout??2e3;return Promise.race([this.handshake(),new Promise(t=>setTimeout(()=>t(!1),e))])}async findAndValidatePort(){let e=this.getSerial();if(!e)return null;let n=await e.getPorts(this.options.polyfillOptions??o.polyfillOptions);if(n.length===0)return null;let r=this.options.filters??[],i=this;for(let e of n)if(!t.isPortInUse(e,i)){if(r.length>0){let t=e.getInfo();if(!r.some(e=>{let n=e.usbVendorId===void 0||e.usbVendorId===t.usbVendorId,r=e.usbProductId===void 0||e.usbProductId===t.usbProductId;return n&&r}))continue}try{if(await this.openAndHandshake(e))return e}catch{}}return null}startReconnecting(){this.reconnectTimerId||=(this.emit(`serial:reconnecting`,this),setInterval(async()=>{if(this.port||this.isConnecting){this.stopReconnecting();return}try{let e=await this.findAndValidatePort();e&&(this.stopReconnecting(),await this.reconnect(e))}catch{}},this.options.autoReconnectInterval))}stopReconnecting(){this.reconnectTimerId&&=(clearInterval(this.reconnectTimerId),null)}async reconnect(e){if(!(this.isConnecting||this.port)){this.isConnecting=!0,this.emit(`serial:connecting`,this);try{this.port=e,this.abortController=new AbortController,this.queue.resume(),this.emit(`serial:connected`,this)}catch(e){this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this),this.port&&=(t.unlockPort(this.port),null),this.options.autoReconnect&&this.startReconnecting()}finally{this.isConnecting=!1}}}static getInstances(){return t.getInstances()}static async connectAll(){let e=t.getInstances();for(let t of e)try{await t.connect()}catch{}}static setProvider(e,t){o.customProvider=e,o.polyfillOptions=t}getSerial(){return this.options.provider?this.options.provider:o.customProvider?o.customProvider:typeof navigator<`u`&&navigator.serial?navigator.serial:null}};function s(e){let t=``,n=new TextDecoder;return{parse(r,i){t+=n.decode(r,{stream:!0});let a;for(;(a=t.indexOf(e))!==-1;)i(t.slice(0,a)),t=t.slice(a+e.length)},reset(){t=``,n=new TextDecoder}}}function c(){return{parse(e,t){t(e)},reset(){}}}var l=`wsc-demo-theme`;function u(){return window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`}function d(){let e=localStorage.getItem(l)??u();return document.documentElement.setAttribute(`data-theme`,e),window.matchMedia(`(prefers-color-scheme: dark)`).addEventListener(`change`,e=>{localStorage.getItem(l)||document.documentElement.setAttribute(`data-theme`,e.matches?`dark`:`light`)}),e}function f(){let e=(document.documentElement.getAttribute(`data-theme`)??u())===`dark`?`light`:`dark`;return localStorage.setItem(l,e),document.documentElement.setAttribute(`data-theme`,e),e}function p(e,t,n){let r=e.querySelector(`.empty-state`);r&&r.remove();let{kind:i,label:a,time:o=new Date}=n,s=document.createElement(`div`);if(s.className=`msg ${i}`,a&&(i===`sent`||i===`received`)){let e=document.createElement(`div`);e.className=`msg-label`,e.textContent=a,s.appendChild(e)}let c=document.createElement(`div`);c.className=`msg-bubble`,c.textContent=t,s.appendChild(c);let l=document.createElement(`div`);l.className=`msg-time`,l.textContent=o.toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`,second:`2-digit`}),s.appendChild(l),e.appendChild(s),e.scrollTop=e.scrollHeight}function m(e){e.innerHTML=`
|
|
2
2
|
<div class="empty-state">
|
|
3
3
|
<div class="empty-icon">💬</div>
|
|
4
4
|
<span>Messages cleared</span>
|
|
5
|
-
</div>`}var
|
|
6
|
-
`);e.innerHTML=``,n.forEach((t,n)=>{let r=document.createElement(`div`);r.className=`code-line`;let i=document.createElement(`span`);i.className=`cl-num`,i.textContent=String(n+1);let a=document.createElement(`span`);a.className=`cl-txt`,a.innerHTML=
|
|
7
|
-
`).replace(/\\r/g,`\r`).replace(/\\t/g,` `).replace(/\\0/g,`\0`)}function
|
|
5
|
+
</div>`}var h=new Set(`import.export.from.default.as.class.extends.implements.constructor.super.new.this.return.const.let.var.async.await.function.protected.public.private.static.abstract.interface.type.enum.namespace.declare.readonly.true.false.null.undefined.void.never.any.unknown.if.else.for.while.do.switch.case.break.continue.try.catch.finally.throw.typeof.instanceof.in.of.keyof.infer.string.number.boolean.object.symbol.bigint.Promise.Array.Set.Map`.split(`.`));function g(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`)}function _(e){let t=``,n=0,r=e.length;for(;n<r;){if(e[n]===`/`&&e[n+1]===`/`){t+=`<span class="t-cmt">${g(e.slice(n))}</span>`;break}let i=e[n];if(i===`'`||i===`"`||i==="`"){let a=n+1;for(;a<r;){if(e[a]===`\\`&&a+1<r){a+=2;continue}if(e[a]===i){a++;break}a++}t+=`<span class="t-str">${g(e.slice(n,a))}</span>`,n=a;continue}if(/\d/.test(e[n])&&(n===0||!/\w/.test(e[n-1]))){let i=n;for(;i<r&&/[\d.xXa-fA-F_n]/.test(e[i]);)i++;t+=`<span class="t-num">${g(e.slice(n,i))}</span>`,n=i;continue}if(/[a-zA-Z_$]/.test(e[n])){let i=n;for(;i<r&&/[\w$]/.test(e[i]);)i++;let a=e.slice(n,i);h.has(a)?t+=`<span class="t-kw">${g(a)}</span>`:i<r&&e[i]===`(`?t+=`<span class="t-fn">${g(a)}</span>`:/^[A-Z]/.test(a)?t+=`<span class="t-cls">${g(a)}</span>`:t+=`<span class="t-var">${g(a)}</span>`,n=i;continue}t+=g(e[n]),n++}return t}function v(e,t){let n=t.split(`
|
|
6
|
+
`);e.innerHTML=``,n.forEach((t,n)=>{let r=document.createElement(`div`);r.className=`code-line`;let i=document.createElement(`span`);i.className=`cl-num`,i.textContent=String(n+1);let a=document.createElement(`span`);a.className=`cl-txt`,a.innerHTML=_(t)||` `,r.appendChild(i),r.appendChild(a),e.appendChild(r)})}function y(e){return`'${e.replace(/\\(?![nrt0\\'])/g,`\\\\`).replace(/'/g,`\\'`)}'`}function b(e){return e.replace(/\\n/g,`
|
|
7
|
+
`).replace(/\\r/g,`\r`).replace(/\\t/g,` `).replace(/\\0/g,`\0`)}function x(e){return!e||e.length===0?`[]`:`[${e.map(e=>{let t=[];return e.usbVendorId!==void 0&&t.push(`usbVendorId: 0x${e.usbVendorId.toString(16).padStart(4,`0`)}`),e.usbProductId!==void 0&&t.push(`usbProductId: 0x${e.usbProductId.toString(16).padStart(4,`0`)}`),`{ ${t.join(`, `)} }`}).join(`, `)}]`}function S(e){return e.trim().split(/\s+/).filter(Boolean).map(e=>parseInt(e,16)).filter(e=>!isNaN(e))}function C(e){let t=S(e);return t.length===0?`new Uint8Array([])`:`new Uint8Array([${t.map(e=>`0x${e.toString(16).padStart(2,`0`)}`).join(`, `)}])`}function w(e){return e.replace(/[^a-zA-Z0-9 _-]/g,``).split(/[\s_-]+/).filter(Boolean).map((e,t)=>t===0?e.charAt(0).toLowerCase()+e.slice(1).toLowerCase():e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(``)||`cmd`}function T(e,t){if(e.length===0)return``;let n=t.charAt(0).toLowerCase()+t.slice(1);return`
|
|
8
8
|
declare module 'webserial-core' {
|
|
9
9
|
interface SerialEventMap<T> {
|
|
10
|
-
`+e.map(e=>` '${n}:${
|
|
10
|
+
`+e.map(e=>` '${n}:${w(e.name||`listener`)}': (data: string) => void;`).join(`
|
|
11
11
|
`)+`
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
-
`}function
|
|
15
|
-
`+e.map(e=>{let t=
|
|
14
|
+
`}function E(e,t){if(e.length===0)return``;let n=t?`: Promise<void>`:``,r=t?`public `:``;return`
|
|
15
|
+
`+e.map(e=>{let t=w(e.name),i=`send`+t.charAt(0).toUpperCase()+t.slice(1);return e.mode===`hex`?` ${r}${i}()${n} { return this.send(${C(e.value)}); } // ${e.name}`:` ${r}${i}()${n} { return this.send('${e.value.replace(/'/g,`\\'`)}'); } // ${e.name}`}).join(`
|
|
16
16
|
`)+`
|
|
17
|
-
`}function
|
|
18
|
-
`);return`\n ${n?`private `:``}startListening()${a} {\n this.on('serial:data', ${i} => {\n${o}\n });\n }\n`}function
|
|
19
|
-
return true;`;let a;if(a=t===`hex`?` await this.send(${
|
|
20
|
-
import type { SerialPortFilter } from 'webserial-core';`:``,
|
|
21
|
-
import { AbstractSerialDevice,
|
|
22
|
-
${n?
|
|
17
|
+
`}function D(e,t,n){if(e.length===0)return``;let r=t.charAt(0).toLowerCase()+t.slice(1),i=n?`(data: string)`:`(data)`,a=n?`: void`:``,o=e.map(e=>{let t=e.name||`listener`,n=`${r}:${w(t)}`,i;switch(e.match){case`contains`:i=`String(data).includes('${e.pattern.replace(/'/g,`\\'`)}')`;break;case`startsWith`:i=`String(data).startsWith('${e.pattern.replace(/'/g,`\\'`)}')`;break;case`hex`:i=`(() => { const enc = new TextEncoder().encode(String(data)); const ex = ${`[${S(e.pattern).map(e=>`0x${e.toString(16).padStart(2,`0`)}`).join(`, `)}]`}; return enc.length === ex.length && enc.every((b, i) => b === ex[i]); })()`;break;default:i=`String(data).trim() === '${e.pattern.replace(/'/g,`\\'`)}' `}return` // ${t}\n if (${i}) {\n this.emit('${n}', data);\n }`}).join(`
|
|
18
|
+
`);return`\n ${n?`private `:``}startListening()${a} {\n this.on('serial:data', ${i} => {\n${o}\n });\n }\n`}function O(e,t,n,r,i){if(!e)return` // No handshake configured — accept any device.
|
|
19
|
+
return true;`;let a;if(a=t===`hex`?` await this.send(${C(e)});`:` await this.send('${e.replace(/'/g,`\\'`)}');`,!n)return`${a}\n return true;`;let o=i?`(data: string)`:`(data)`,s;return s=r===`hex`?`(() => { const enc = new TextEncoder().encode(String(data)); const ex = ${`[${S(n).map(e=>`0x${e.toString(16).padStart(2,`0`)}`).join(`, `)}]`}; return enc.length === ex.length && enc.every((b, i) => b === ex[i]); })()`:`String(data).trim() === '${n.replace(/'/g,`\\'`)}'`,`${a}\n return new Promise((resolve) => {\n const _h = ${o} => {\n this.off('serial:data', _h);\n resolve(${s});\n };\n this.on('serial:data', _h);\n });`}function k(e,t,n,r=[],i=[]){let a=n?`ts`:`js`,o=e.delimiter?y(e.delimiter):null,s=o?`delimiter`:`raw`,c=o?`delimiter(${o})`:`raw()`,l=n?`: Promise<boolean>`:``,u=n?`: SerialPortFilter[]`:``,d=n?`
|
|
20
|
+
import type { SerialPortFilter } from 'webserial-core';`:``,f=x(e.filters);return`// device.${a} — Generated by webserial-core demo
|
|
21
|
+
import { AbstractSerialDevice, ${s} } from 'webserial-core';${d}
|
|
22
|
+
${n?T(i,t):``}
|
|
23
23
|
export class ${t} extends AbstractSerialDevice${n?`<string>`:``} {
|
|
24
|
-
constructor(filters${n?`${
|
|
24
|
+
constructor(filters${n?`${u}`:` = []`}) {
|
|
25
25
|
super({
|
|
26
26
|
baudRate: ${e.baudRate},
|
|
27
27
|
dataBits: ${e.dataBits},
|
|
@@ -30,7 +30,7 @@ export class ${t} extends AbstractSerialDevice${n?`<string>`:``} {
|
|
|
30
30
|
flowControl: '${e.flowControl}',
|
|
31
31
|
bufferSize: ${e.bufferSize},
|
|
32
32
|
commandTimeout: ${e.commandTimeout},
|
|
33
|
-
parser:
|
|
33
|
+
parser: ${c},
|
|
34
34
|
autoReconnect: ${e.autoReconnect},
|
|
35
35
|
autoReconnectInterval: ${e.autoReconnectInterval},
|
|
36
36
|
handshakeTimeout: ${e.handshakeTimeout},
|
|
@@ -39,13 +39,13 @@ export class ${t} extends AbstractSerialDevice${n?`<string>`:``} {
|
|
|
39
39
|
this.startListening();`:``}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
${n?`protected `:``}async handshake()${
|
|
43
|
-
${
|
|
42
|
+
${n?`protected `:``}async handshake()${l} {
|
|
43
|
+
${O(e.hsCmd,e.hsCmdMode,e.hsExpect,e.hsExpectMode,n)}
|
|
44
44
|
}
|
|
45
|
-
${
|
|
45
|
+
${E(r,n)}${D(i,t,n)}}
|
|
46
46
|
|
|
47
47
|
// ── Usage ────────────────────────────────────────────────────────
|
|
48
|
-
const device = new ${t}(${
|
|
48
|
+
const device = new ${t}(${f});
|
|
49
49
|
|
|
50
50
|
device.on('serial:connected', () => console.log('Connected!'));
|
|
51
51
|
device.on('serial:disconnected', () => console.log('Disconnected.'));
|
|
@@ -60,29 +60,29 @@ device.on('serial:error', (err) => console.error(err.message));
|
|
|
60
60
|
|
|
61
61
|
// Disconnect:
|
|
62
62
|
// await device.disconnect();
|
|
63
|
-
`}function
|
|
64
|
-
import { AbstractSerialDevice,
|
|
63
|
+
`}function A(e,t,n,r=[],i=[]){let a=n?`ts`:`js`,o=e.delimiter?y(e.delimiter):null,s=o?`delimiter`:`raw`,c=o?`delimiter(${o})`:`raw()`,l=n?`: Promise<boolean>`:``;return`// device.${a} — Generated by webserial-core demo
|
|
64
|
+
import { AbstractSerialDevice, ${s}, createBluetoothProvider } from 'webserial-core';
|
|
65
65
|
|
|
66
66
|
// Inject the BLE provider before creating any device instance.
|
|
67
67
|
AbstractSerialDevice.setProvider(createBluetoothProvider());
|
|
68
|
-
${n?
|
|
68
|
+
${n?T(i,t):``}
|
|
69
69
|
export class ${t} extends AbstractSerialDevice${n?`<string>`:``} {
|
|
70
70
|
constructor() {
|
|
71
71
|
super({
|
|
72
72
|
baudRate: 9600, // Nominal — not used over BLE GATT
|
|
73
73
|
bufferSize: ${e.bufferSize},
|
|
74
74
|
commandTimeout: ${e.commandTimeout},
|
|
75
|
-
parser:
|
|
75
|
+
parser: ${c},
|
|
76
76
|
autoReconnect: false, // BLE requires user gesture to reconnect
|
|
77
77
|
handshakeTimeout: ${e.handshakeTimeout},
|
|
78
78
|
});${i.length>0?`
|
|
79
79
|
this.startListening();`:``}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
${n?`protected `:``}async handshake()${
|
|
83
|
-
${
|
|
82
|
+
${n?`protected `:``}async handshake()${l} {
|
|
83
|
+
${O(e.hsCmd,e.hsCmdMode,e.hsExpect,e.hsExpectMode,n)}
|
|
84
84
|
}
|
|
85
|
-
${
|
|
85
|
+
${E(r,n)}${D(i,t,n)}}
|
|
86
86
|
|
|
87
87
|
// ── Usage ────────────────────────────────────────────────────────
|
|
88
88
|
const device = new ${t}();
|
|
@@ -96,9 +96,9 @@ device.on('serial:error', (err) => console.error(err.message));
|
|
|
96
96
|
// await device.connect();
|
|
97
97
|
// await device.send('${e.prepend}COMMAND${e.append}');
|
|
98
98
|
// await device.disconnect();
|
|
99
|
-
`}function
|
|
100
|
-
import type { SerialPortFilter } from 'webserial-core';`:``,
|
|
101
|
-
import { AbstractSerialDevice,
|
|
99
|
+
`}function j(e,t,n,r=[],i=[]){let a=n?`ts`:`js`,o=e.delimiter?y(e.delimiter):null,s=o?`delimiter`:`raw`,c=o?`delimiter(${o})`:`raw()`,l=n?`: Promise<boolean>`:``,u=n?`: SerialPortFilter[]`:``,d=n?`
|
|
100
|
+
import type { SerialPortFilter } from 'webserial-core';`:``,f=x(e.filters);return`// device.${a} — Generated by webserial-core demo
|
|
101
|
+
import { AbstractSerialDevice, ${s}, WebUsbProvider } from 'webserial-core';${d}
|
|
102
102
|
|
|
103
103
|
// Inject the WebUSB polyfill provider.
|
|
104
104
|
AbstractSerialDevice.setProvider(
|
|
@@ -107,9 +107,9 @@ AbstractSerialDevice.setProvider(
|
|
|
107
107
|
usbTransferInterfaceClass: ${e.usbTransferInterfaceClass},
|
|
108
108
|
})
|
|
109
109
|
);
|
|
110
|
-
${n?
|
|
110
|
+
${n?T(i,t):``}
|
|
111
111
|
export class ${t} extends AbstractSerialDevice${n?`<string>`:``} {
|
|
112
|
-
constructor(filters${n?`${
|
|
112
|
+
constructor(filters${n?`${u}`:` = []`}) {
|
|
113
113
|
super({
|
|
114
114
|
baudRate: ${e.baudRate},
|
|
115
115
|
dataBits: ${e.dataBits},
|
|
@@ -118,7 +118,7 @@ export class ${t} extends AbstractSerialDevice${n?`<string>`:``} {
|
|
|
118
118
|
flowControl: '${e.flowControl}',
|
|
119
119
|
bufferSize: ${e.bufferSize},
|
|
120
120
|
commandTimeout: ${e.commandTimeout},
|
|
121
|
-
parser:
|
|
121
|
+
parser: ${c},
|
|
122
122
|
autoReconnect: ${e.autoReconnect},
|
|
123
123
|
autoReconnectInterval: ${e.autoReconnectInterval},
|
|
124
124
|
handshakeTimeout: ${e.handshakeTimeout},
|
|
@@ -127,15 +127,15 @@ export class ${t} extends AbstractSerialDevice${n?`<string>`:``} {
|
|
|
127
127
|
this.startListening();`:``}
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
${n?`protected `:``}async handshake()${
|
|
131
|
-
${
|
|
130
|
+
${n?`protected `:``}async handshake()${l} {
|
|
131
|
+
${O(e.hsCmd,e.hsCmdMode,e.hsExpect,e.hsExpectMode,n)}
|
|
132
132
|
}
|
|
133
|
-
${
|
|
133
|
+
${E(r,n)}${D(i,t,n)}}
|
|
134
134
|
|
|
135
135
|
// ── Usage ────────────────────────────────────────────────────────
|
|
136
136
|
// CP2102/ESP32: { usbVendorId: 0x10c4, usbProductId: 0xea60 }
|
|
137
137
|
// CH340/Arduino: { usbVendorId: 0x1a86, usbProductId: 0x7523 }
|
|
138
|
-
const device = new ${t}(${
|
|
138
|
+
const device = new ${t}(${f});
|
|
139
139
|
|
|
140
140
|
device.on('serial:connected', () => console.log('USB connected!'));
|
|
141
141
|
device.on('serial:disconnected', () => console.log('Disconnected.'));
|
|
@@ -145,14 +145,14 @@ device.on('serial:error', (err) => console.error(err.message));
|
|
|
145
145
|
// await device.connect();
|
|
146
146
|
// await device.send('${e.prepend}COMMAND${e.append}');
|
|
147
147
|
// await device.disconnect();
|
|
148
|
-
`}function
|
|
149
|
-
import { AbstractSerialDevice,
|
|
148
|
+
`}function M(e,t,n,r=[],i=[]){let a=n?`ts`:`js`,o=e.delimiter?y(e.delimiter):null,s=o?`delimiter`:`raw`,c=o?`delimiter(${o})`:`raw()`,l=n?`: Promise<boolean>`:``;return`// device.${a} — Generated by webserial-core demo
|
|
149
|
+
import { AbstractSerialDevice, ${s}, createWebSocketProvider } from 'webserial-core';
|
|
150
150
|
|
|
151
151
|
// Inject the WebSocket bridge provider.
|
|
152
152
|
// Start the Node.js bridge first: cd demos/websocket && node server.js
|
|
153
153
|
const wsProvider = createWebSocketProvider('${e.wsUrl}');
|
|
154
154
|
AbstractSerialDevice.setProvider(wsProvider);
|
|
155
|
-
${n?
|
|
155
|
+
${n?T(i,t):``}
|
|
156
156
|
export class ${t} extends AbstractSerialDevice${n?`<string>`:``} {
|
|
157
157
|
constructor() {
|
|
158
158
|
super({
|
|
@@ -163,7 +163,7 @@ export class ${t} extends AbstractSerialDevice${n?`<string>`:``} {
|
|
|
163
163
|
flowControl: '${e.flowControl}',
|
|
164
164
|
bufferSize: ${e.bufferSize},
|
|
165
165
|
commandTimeout: ${e.commandTimeout},
|
|
166
|
-
parser:
|
|
166
|
+
parser: ${c},
|
|
167
167
|
autoReconnect: ${e.autoReconnect},
|
|
168
168
|
autoReconnectInterval: ${e.autoReconnectInterval},
|
|
169
169
|
handshakeTimeout: ${e.handshakeTimeout},
|
|
@@ -171,10 +171,10 @@ export class ${t} extends AbstractSerialDevice${n?`<string>`:``} {
|
|
|
171
171
|
this.startListening();`:``}
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
${n?`protected `:``}async handshake()${
|
|
175
|
-
${
|
|
174
|
+
${n?`protected `:``}async handshake()${l} {
|
|
175
|
+
${O(e.hsCmd,e.hsCmdMode,e.hsExpect,e.hsExpectMode,n)}
|
|
176
176
|
}
|
|
177
|
-
${
|
|
177
|
+
${E(r,n)}${D(i,t,n)}}
|
|
178
178
|
|
|
179
179
|
// ── Usage ────────────────────────────────────────────────────────
|
|
180
180
|
const device = new ${t}();
|
|
@@ -187,7 +187,7 @@ device.on('serial:error', (err) => console.error(err.message));
|
|
|
187
187
|
// await device.connect();
|
|
188
188
|
// await device.send('${e.prepend}COMMAND${e.append}');
|
|
189
189
|
// await device.disconnect();
|
|
190
|
-
`}function
|
|
190
|
+
`}function N(e,t,n){let r=n===`ws`?{ws:`^8.0.0`}:{},i={vite:`^8.0.0`,...t?{typescript:`~5.9.3`}:{}};return JSON.stringify({name:e.toLowerCase().replace(/\s+/g,`-`).replace(/[^a-z0-9-]/g,``),version:`1.0.0`,type:`module`,scripts:{dev:`vite`,build:`vite build`,preview:`vite preview`},dependencies:{"webserial-core":`^2.0.0`,...r},devDependencies:i},null,2)}function P(){return JSON.stringify({compilerOptions:{target:`ES2022`,useDefineForClassFields:!0,lib:[`ES2022`,`DOM`,`DOM.Iterable`],module:`ESNext`,skipLibCheck:!0,moduleResolution:`bundler`,allowImportingTsExtensions:!0,strict:!0,noEmit:!0},include:[`**/*.ts`]},null,2)}function F(e,t,n){return`<!doctype html>
|
|
191
191
|
<html lang="en">
|
|
192
192
|
<head>
|
|
193
193
|
<meta charset="UTF-8" />
|
|
@@ -236,7 +236,7 @@ device.on('serial:error', (err) => console.error(err.message));
|
|
|
236
236
|
<\/script>
|
|
237
237
|
</body>
|
|
238
238
|
</html>
|
|
239
|
-
`}function
|
|
239
|
+
`}function I(e,t,n,r){return`# ${e}
|
|
240
240
|
|
|
241
241
|
A ${n} device using [webserial-core](https://github.com/danidoble/webserial-core).
|
|
242
242
|
|
|
@@ -269,4 +269,4 @@ await device.send('LED_ON\\n');
|
|
|
269
269
|
// Disconnect:
|
|
270
270
|
await device.disconnect();
|
|
271
271
|
\`\`\`
|
|
272
|
-
`}function
|
|
272
|
+
`}function L(e,t){let n=new Blob([t],{type:`text/plain;charset=utf-8`}),r=URL.createObjectURL(n),i=document.createElement(`a`);i.href=r,i.download=e,document.body.appendChild(i),i.click(),document.body.removeChild(i),URL.revokeObjectURL(r)}function R(e,t){let n=JSON.stringify(e,null,2),r=new Blob([n],{type:`application/json;charset=utf-8`}),i=URL.createObjectURL(r),a=document.createElement(`a`);a.href=i,a.download=t.endsWith(`.json`)?t:t+`.json`,document.body.appendChild(a),a.click(),document.body.removeChild(a),URL.revokeObjectURL(i)}function z(e,t){let n=new TextEncoder,r=G(e.map(e=>({name:e.name,data:n.encode(e.content)}))),i=new Blob([r.buffer],{type:`application/zip`}),a=URL.createObjectURL(i),o=document.createElement(`a`);o.href=a,o.download=t.endsWith(`.zip`)?t:t+`.zip`,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(a)}var B=null;function V(){if(B)return B;B=new Uint32Array(256);for(let e=0;e<256;e++){let t=e;for(let e=0;e<8;e++)t=t&1?3988292384^t>>>1:t>>>1;B[e]=t>>>0}return B}function H(e){let t=V(),n=4294967295;for(let r=0;r<e.length;r++)n=n>>>8^t[(n^e[r])&255];return(n^4294967295)>>>0}function U(e,t,n){new DataView(e.buffer,e.byteOffset+t).setUint16(0,n,!0)}function W(e,t,n){new DataView(e.buffer,e.byteOffset+t).setUint32(0,n,!0)}function G(e){let t=new TextEncoder,n=[],r=0;for(let i of e){let e=t.encode(i.name),a=H(i.data),o=new Uint8Array(30+e.length);W(o,0,67324752),U(o,4,20),U(o,6,0),U(o,8,0),U(o,10,0),U(o,12,0),W(o,14,a),W(o,18,i.data.length),W(o,22,i.data.length),U(o,26,e.length),U(o,28,0),o.set(e,30);let s=new Uint8Array(46+e.length);W(s,0,33639248),U(s,4,20),U(s,6,20),U(s,8,0),U(s,10,0),U(s,12,0),U(s,14,0),W(s,16,a),W(s,20,i.data.length),W(s,24,i.data.length),U(s,28,e.length),U(s,30,0),U(s,32,0),U(s,34,0),U(s,36,0),W(s,38,0),W(s,42,r),s.set(e,46),n.push({localHdr:o,data:i.data,cdHdr:s,offset:r}),r+=o.length+i.data.length}let i=n.reduce((e,t)=>e+t.cdHdr.length,0),a=new Uint8Array(22);W(a,0,101010256),U(a,4,0),U(a,6,0),U(a,8,n.length),U(a,10,n.length),W(a,12,i),W(a,16,r),U(a,20,0);let o=new Uint8Array(r+i+a.length),s=0;for(let e of n)o.set(e.localHdr,s),s+=e.localHdr.length,o.set(e.data,s),s+=e.data.length;for(let e of n)o.set(e.cdHdr,s),s+=e.cdHdr.length;return o.set(a,s),o}function K(){let e=document.createElement(`canvas`);e.style.cssText=`position:fixed;inset:0;width:100%;height:100%;pointer-events:none;z-index:0;opacity:0.55;`,document.body.insertBefore(e,document.body.firstChild);let t=e.getContext(`2d`),n=window.devicePixelRatio||1,r=[],i=[],a=[],o=0,s=0;function c(){o=Math.max(window.innerWidth,1),s=Math.max(window.innerHeight,1),e.width=Math.round(o*n),e.height=Math.round(s*n),e.style.width=`${o}px`,e.style.height=`${s}px`,t.setTransform(n,0,0,n,0,0);let c=Math.ceil(o/52),l=Math.ceil(s/52);r=[];let u=new Map,d=(e,t)=>{u.set(`${e},${t}`,(u.get(`${e},${t}`)??0)+1)};for(let e=0;e<=l;e++)for(let t=0;t<=c;t++){let n=t*52,i=e*52;t<c&&Math.random()>.22&&(r.push({x1:n,y1:i,x2:n+52,y2:i}),d(n,i),d(n+52,i)),e<l&&Math.random()>.22&&(r.push({x1:n,y1:i,x2:n,y2:i+52}),d(n,i),d(n,i+52))}i=Array.from(u.keys()).map(e=>{let[t,n]=e.split(`,`).map(Number);return[t,n]});let f=Math.max(10,Math.min(35,Math.floor(o*s/11e3)));a=Array.from({length:f},()=>({seg:Math.floor(Math.random()*Math.max(1,r.length)),t:Math.random(),speed:.004+Math.random()*.009,sz:1.5+Math.random()*2}))}function l(){let e=getComputedStyle(document.documentElement).getPropertyValue(`--accent`).trim();if(e.startsWith(`#`)){let t=e.length===4?e.slice(1).split(``).map(e=>e+e).join(``):e.slice(1);return[0,2,4].map(e=>parseInt(t.slice(e,e+2),16)).join(`,`)}return`34,197,94`}let u=0;function d(){t.clearRect(0,0,o,s);let e=l();t.strokeStyle=`rgba(${e},0.07)`,t.lineWidth=.8;for(let e of r)t.beginPath(),t.moveTo(e.x1,e.y1),t.lineTo(e.x2,e.y2),t.stroke();for(let[n,r]of i)t.beginPath(),t.arc(n,r,3.8,0,Math.PI*2),t.fillStyle=`rgba(${e},0.05)`,t.fill(),t.strokeStyle=`rgba(${e},0.2)`,t.lineWidth=.7,t.stroke(),t.beginPath(),t.arc(n,r,1.4,0,Math.PI*2),t.fillStyle=`rgba(${e},0.25)`,t.fill();for(let n of a){let i=r[n.seg];if(!i)continue;let a=i.x1+(i.x2-i.x1)*n.t,o=i.y1+(i.y2-i.y1)*n.t,s=Math.max(0,n.t-.24),c=i.x1+(i.x2-i.x1)*s,l=i.y1+(i.y2-i.y1)*s,u=t.createLinearGradient(c,l,a,o);u.addColorStop(0,`rgba(${e},0)`),u.addColorStop(1,`rgba(${e},0.42)`),t.strokeStyle=u,t.lineWidth=2,t.beginPath(),t.moveTo(c,l),t.lineTo(a,o),t.stroke();let d=t.createRadialGradient(a,o,0,a,o,n.sz*7);d.addColorStop(0,`rgba(${e},0.5)`),d.addColorStop(.4,`rgba(${e},0.15)`),d.addColorStop(1,`rgba(${e},0)`),t.fillStyle=d,t.beginPath(),t.arc(a,o,n.sz*7,0,Math.PI*2),t.fill(),t.fillStyle=`rgba(${e},1)`,t.beginPath(),t.arc(a,o,n.sz,0,Math.PI*2),t.fill(),t.fillStyle=`rgba(255,255,255,0.75)`,t.beginPath(),t.arc(a,o,n.sz*.4,0,Math.PI*2),t.fill(),n.t+=n.speed,n.t>=1&&(n.t=0,n.seg=Math.floor(Math.random()*r.length))}u=requestAnimationFrame(d)}c(),d(),new ResizeObserver(()=>{cancelAnimationFrame(u),c(),d()}).observe(document.body)}function q(){let e=document.querySelector(`.topbar`);if(!e)return;let t=document.createElement(`button`);t.className=`icon-btn mob-toggle`,t.title=`Navigation menu`,t.innerHTML=`<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2.2" fill="none" stroke-linecap="round"><circle cx="12" cy="5" r="1.3"/><circle cx="12" cy="12" r="1.3"/><circle cx="12" cy="19" r="1.3"/></svg>`,e.appendChild(t);let n=document.createElement(`div`);n.className=`mob-nav-drawer`;let r=document.createElement(`div`);r.className=`mob-nav-inner`;let i=e.querySelector(`.topbar-nav`);if(i){let e=document.createElement(`div`);e.className=`mob-nav-links`,i.querySelectorAll(`.nav-item`).forEach(t=>{e.appendChild(t.cloneNode(!0))}),r.appendChild(e)}let a=document.createElement(`div`);a.className=`mob-nav-sep`,r.appendChild(a);let o=document.createElement(`div`);o.className=`mob-nav-actions`;let s=document.getElementById(`btn-connect`),c=document.getElementById(`btn-disconnect`),l=(e,t,r)=>{let i=document.createElement(`button`);return i.className=`btn ${t}`,i.textContent=r,e&&(i.disabled=e.disabled,new MutationObserver(()=>{i.disabled=e.disabled}).observe(e,{attributes:!0,attributeFilter:[`disabled`]}),i.addEventListener(`click`,()=>{e.click(),n.classList.remove(`open`)})),i};o.appendChild(l(s,`btn-connect`,`Connect`)),o.appendChild(l(c,`btn-disconnect`,`Disconnect`)),r.appendChild(o),n.appendChild(r),document.body.appendChild(n),t.addEventListener(`click`,e=>{e.stopPropagation(),n.classList.toggle(`open`)}),document.addEventListener(`click`,()=>n.classList.remove(`open`)),n.addEventListener(`click`,e=>e.stopPropagation())}export{o as S,P as _,L as a,c as b,k as c,K as d,q as f,I as g,N as h,R as i,j as l,F as m,m as n,z as o,d as p,b as r,A as s,p as t,M as u,v,s as x,f as y};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{S as e,_ as t,a as n,b as r,d as i,f as a,g as o,h as ee,i as s,m as te,n as c,o as ne,p as l,r as u,s as d,t as f,v as re,x as ie,y as ae}from"./demo-shared-BVQKG6NC.js";var p=`6e400001-b5a3-f393-e0a9-e50e24dcca9e`,oe=`6e400003-b5a3-f393-e0a9-e50e24dcca9e`,se=`6e400002-b5a3-f393-e0a9-e50e24dcca9e`,m=20,ce=10;function le(e){let t=null,n=null,r=null;return{get readable(){return t},get writable(){return n},getInfo(){return{}},async open(){if(!e.gatt)throw Error(`GATT not available on this Bluetooth device.`);r=await e.gatt.connect();let i=await r.getPrimaryService(p),a=await i.getCharacteristic(oe),o=await i.getCharacteristic(se);await a.startNotifications(),t=new ReadableStream({start(e){a.addEventListener(`characteristicvaluechanged`,t=>{let n=t.target.value.buffer;e.enqueue(new Uint8Array(n))})}}),n=new WritableStream({async write(e){for(let t=0;t<e.length;t+=m){let n=e.slice(t,t+m);await o.writeValueWithoutResponse(n),t+m<e.length&&await new Promise(e=>setTimeout(e,ce))}}})},async close(){r?.connected&&r.disconnect(),t=null,n=null}}}function ue(){return{async requestPort(){if(!navigator.bluetooth)throw Error(`Web Bluetooth API is not supported in this browser. Use Chrome on Android, macOS, or ChromeOS.`);return le(await navigator.bluetooth.requestDevice({filters:[{services:[p]}]}))},async getPorts(){return[]}}}e.setProvider(ue());var h=[],g=[],de=class extends e{_hsCmd;_hsCmdMode;_hsExpect;_hsExpectMode;constructor(e,t,n,r,i){super(e),this._hsCmd=t,this._hsCmdMode=n,this._hsExpect=r,this._hsExpectMode=i}async handshake(){if(!this._hsCmd||(this._hsCmdMode===`hex`?await this.send(I(this._hsCmd)):await this.send(u(this._hsCmd)),!this._hsExpect))return!0;let e=this._hsExpect.trim();return new Promise(t=>{let n=r=>{if(this.off(`serial:data`,n),this._hsExpectMode===`hex`){let e=new TextEncoder().encode(String(r)),n=I(this._hsExpect);t(e.length===n.length&&e.every((e,t)=>e===n[t]))}else t(String(r).trim()===e)};this.on(`serial:data`,n)})}},_=e=>document.getElementById(e),v=_(`messages`),y=_(`btn-connect`),b=_(`btn-disconnect`),x=_(`btn-send`),S=_(`input-send`),C=_(`mode-toggle`),fe=_(`status-dot`),w=_(`status-text`),pe=_(`console-dot`),T=_(`console-text`),E=_(`sidebar`),D=_(`code-panel`),O=_(`code-view`),k=_(`code-tab`),me=_(`menu-btn`),he=_(`code-toggle-btn`),A=_(`theme-btn`),ge=_(`clear-btn`),j=_(`copy-btn`),_e=_(`dl-btn`),ve=_(`cfg-export-btn`),M=_(`cfg-import-input`);function N(){let e=e=>(_(e)?.value??``).trim(),t=(t,n)=>parseInt(e(t))||n,n=e=>_(e)?.value??``;return{bufferSize:t(`cfg-bufsize`,255),commandTimeout:t(`cfg-timeout`,3e3),handshakeTimeout:t(`cfg-handshake`,2e3),delimiter:e(`cfg-delim`),prepend:e(`cfg-prepend`),append:e(`cfg-append`),hsCmd:e(`cfg-hs-cmd`),hsCmdMode:n(`cfg-hs-cmd-mode`)||`text`,hsExpect:e(`cfg-hs-expect`),hsExpectMode:n(`cfg-hs-expect-mode`)||`text`}}function ye(e){let t=(e,t)=>{let n=document.getElementById(e);n&&(n instanceof HTMLInputElement&&n.type===`checkbox`?n.checked=!!t:(n instanceof HTMLInputElement||n instanceof HTMLSelectElement)&&(n.value=String(t??``)))};t(`cfg-bufsize`,e.bufferSize??255),t(`cfg-timeout`,e.commandTimeout??3e3),t(`cfg-handshake`,e.handshakeTimeout??2e3),t(`cfg-delim`,e.delimiter??`\\n`),t(`cfg-prepend`,e.prepend??``),t(`cfg-append`,e.append??``),t(`cfg-hs-cmd`,e.hsCmd??``),t(`cfg-hs-cmd-mode`,e.hsCmdMode??`text`),t(`cfg-hs-expect`,e.hsExpect??``),t(`cfg-hs-expect-mode`,e.hsExpectMode??`text`)}function P(e){let t=e.replace(/[^a-zA-Z0-9_$]/g,``).replace(/^[^a-zA-Z_$]/,`C`);return t.charAt(0).toUpperCase()+t.slice(1)||`MyDevice`}function F(e){return Array.from(e).map(e=>e.toString(16).toUpperCase().padStart(2,`0`)).join(` `)}function I(e){let t=e.replace(/\s+/g,``);if(t.length%2!=0)throw Error(`Odd number of hex characters.`);let n=new Uint8Array(t.length/2);for(let e=0;e<t.length;e+=2)n[e/2]=parseInt(t.substring(e,e+2),16);return n}function L(e,t){[fe,pe].forEach(t=>{t&&(t.className=`status-dot`,e!==`disconnected`&&t.classList.add(e))}),w&&(w.textContent=t),T&&(T.textContent=t)}var R=null;function z(){R&&clearTimeout(R),R=setTimeout(()=>{let e=N(),t=P((_(`dl-name`)?.value??`MyBleDevice`).trim()),n=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,r=n?`ts`:`js`;re(O,d(e,t,n,h,g)),k&&(k.textContent=`${t.substring(0,10).toLowerCase()}.${r}`)},180)}var be=l();A&&(A.textContent=be===`dark`?`☀️`:`🌙`),me?.addEventListener(`click`,()=>E.classList.toggle(`collapsed`)),he?.addEventListener(`click`,()=>D.classList.toggle(`collapsed`));var B=_(`resize-handle`);if(B){let e=0,t=0,n=n=>{let r=Math.max(180,Math.min(700,t+(e-n.clientX)));document.documentElement.style.setProperty(`--code-w`,`${r}px`)},r=()=>{B.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};B.addEventListener(`pointerdown`,i=>{e=i.clientX,t=D.getBoundingClientRect().width,B.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}var V=_(`sidebar-resize-handle`);if(V){let e=0,t=0,n=n=>{let r=Math.max(200,Math.min(600,t+(n.clientX-e)));document.documentElement.style.setProperty(`--sidebar-w`,`${r}px`)},r=()=>{V.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};V.addEventListener(`pointerdown`,i=>{e=i.clientX,t=E.getBoundingClientRect().width,V.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}window.innerWidth<=960&&D.classList.add(`collapsed`),window.innerWidth<=640&&E.classList.add(`collapsed`),A?.addEventListener(`click`,()=>{let e=ae();A&&(A.textContent=e===`dark`?`☀️`:`🌙`)}),ge?.addEventListener(`click`,()=>c(v)),j?.addEventListener(`click`,async()=>{let e=O?.textContent??``;try{await navigator.clipboard.writeText(e),j&&(j.textContent=`Copied!`,j.classList.add(`copied`),setTimeout(()=>{j.textContent=`Copy`,j.classList.remove(`copied`)},1500))}catch{}}),_e?.addEventListener(`click`,()=>{let e=N(),r=(_(`dl-name`)?.value??`my-ble-device`).trim(),i=P(r),a=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,s=document.querySelector(`input[name='dl-type']:checked`)?.value??`project`,c=a?`ts`:`js`,l=d(e,i,a,h,g);s===`project`?ne([{name:`device.${c}`,content:l},{name:`package.json`,content:ee(r,a,`ble`)},{name:`index.html`,content:te(i,c,`Web Bluetooth`)},{name:`README.md`,content:o(i,c,`Web Bluetooth`,`Requires a Chromium browser. The device must expose a Nordic UART Service (NUS) via BLE GATT.`)},...a?[{name:`tsconfig.json`,content:t()}]:[]],`${r}-project-${c}`):n(`${r}.${c}`,l)}),ve?.addEventListener(`click`,()=>{let e=(_(`dl-name`)?.value??`my-ble-device`).trim(),t=P(e);s({$version:1,provider:`ble`,className:e,language:document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`,dlType:document.querySelector(`input[name='dl-type']:checked`)?.value??`file`,cfg:N(),commands:h,listeners:g},t+`-config`)}),M?.addEventListener(`change`,()=>{let e=M.files?.[0];if(!e)return;let t=new FileReader;t.onload=e=>{try{let t=JSON.parse(e.target?.result);if(t.$version!==1||t.provider!==`ble`)return;let n=_(`dl-name`);n&&(n.value=t.className),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>{e.checked=e.value===t.language}),document.querySelectorAll(`input[name='dl-type']`).forEach(e=>{e.checked=e.value===t.dlType}),ye(t.cfg),h=t.commands.map(e=>({...e,id:crypto.randomUUID()})),g=t.listeners.map(e=>({...e,id:crypto.randomUUID()})),Q(),$(),z()}catch{}M.value=``},t.readAsText(e)}),[`cfg-bufsize`,`cfg-timeout`,`cfg-handshake`,`cfg-delim`,`cfg-prepend`,`cfg-append`,`cfg-hs-cmd`,`cfg-hs-cmd-mode`,`cfg-hs-expect`,`cfg-hs-expect-mode`,`dl-name`].forEach(e=>{let t=document.getElementById(e);t?.addEventListener(`change`,z),t?.addEventListener(`input`,z)}),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>e.addEventListener(`change`,z)),z();var H=`text`;C?.addEventListener(`click`,()=>{H=H===`text`?`hex`:`text`,C.textContent=H===`text`?`TXT`:`HEX`,S.placeholder=H===`text`?`Type a command, e.g. LED_ON`:`Hex bytes, e.g. FF 01 A3`});var U=null;function W(e){x.disabled=!e,S.disabled=!e,b.disabled=!e,y.disabled=e}y?.addEventListener(`click`,async()=>{if(U){try{await U.disconnect()}catch{}U=null}let e=N(),t=e.delimiter?u(e.delimiter):``;U=new de({baudRate:9600,bufferSize:e.bufferSize,commandTimeout:e.commandTimeout,parser:t?ie(t):r(),autoReconnect:!1,handshakeTimeout:e.handshakeTimeout},e.hsCmd,e.hsCmdMode,e.hsExpect,e.hsExpectMode),U.on(`serial:connecting`,()=>{L(`connecting`,`Connecting…`),y.disabled=!0,f(v,`Initiating Web Bluetooth connection…`,{kind:`system`})}),U.on(`serial:connected`,()=>{L(`connected`,`Connected`),W(!0),f(v,`Connected via Web Bluetooth!`,{kind:`system`})}),U.on(`serial:disconnected`,()=>{L(`disconnected`,`Disconnected`),W(!1),f(v,`Disconnected.`,{kind:`system`}),U=null}),U.on(`serial:data`,e=>{f(v,String(e),{kind:`received`,label:`Device`})}),U.on(`serial:error`,e=>{L(`error`,`Error`),f(v,`Error: ${e.message}`,{kind:`error`}),y.disabled=!1}),U.on(`serial:need-permission`,()=>{L(`error`,`Permission denied`),f(v,`Permission denied — select a valid BLE device and allow access.`,{kind:`error`}),y.disabled=!1}),U.on(`serial:timeout`,e=>{f(v,`Timeout: ${F(e)}`,{kind:`error`})});try{await U.connect()}catch{}}),b?.addEventListener(`click`,async()=>{await U?.disconnect()});async function G(){let e=S.value.trim();if(!e||!U)return;let t=N(),n=t.append?u(t.append):t.delimiter?u(t.delimiter):``;try{if(H===`hex`){let t=I(e);f(v,`HEX: ${F(t)}`,{kind:`sent`,label:`You`}),await U.send(t)}else{let r=t.prepend+e+n;f(v,e,{kind:`sent`,label:`You`}),await U.send(r)}S.value=``,S.focus()}catch(e){f(v,`Send error: ${e instanceof Error?e.message:String(e)}`,{kind:`error`})}}x?.addEventListener(`click`,G),S?.addEventListener(`keydown`,e=>{e.key===`Enter`&&G()});var K=_(`cmd-name`),q=_(`cmd-value`),xe=_(`cmd-mode`),Se=_(`cmd-add`),J=_(`cmd-list`),Y=_(`lst-name`),X=_(`lst-pattern`),Ce=_(`lst-match`),we=_(`lst-add`),Z=_(`lst-list`);function Q(){if(J){J.innerHTML=``;for(let e of h){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.mode.toUpperCase();let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.value;let a=document.createElement(`button`);a.className=`chip-send`,a.title=`Send now`,a.textContent=`▶`,a.addEventListener(`click`,()=>{if(U)if(e.mode===`hex`){let t=I(e.value);U.send(t).catch(()=>{}),f(v,`HEX: ${F(t)}`,{kind:`sent`,label:`You`})}else{let t=N(),n=t.append?u(t.append):t.delimiter?u(t.delimiter):``;U.send(t.prepend+e.value+n).catch(()=>{}),f(v,e.name,{kind:`sent`,label:`You`})}});let o=document.createElement(`button`);o.className=`chip-del`,o.title=`Remove`,o.textContent=`×`,o.addEventListener(`click`,()=>{h=h.filter(t=>t.id!==e.id),Q(),z()}),t.append(n,r,i,a,o),J.appendChild(t)}}}function $(){if(Z){Z.innerHTML=``;for(let e of g){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.match;let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.pattern;let a=document.createElement(`button`);a.className=`chip-del`,a.title=`Remove`,a.textContent=`×`,a.addEventListener(`click`,()=>{g=g.filter(t=>t.id!==e.id),$(),z()}),t.append(n,r,i,a),Z.appendChild(t)}}}Se?.addEventListener(`click`,()=>{let e=K?.value.trim(),t=q?.value.trim();if(!e||!t)return;let n=xe?.value??`text`;h.push({id:crypto.randomUUID(),name:e,value:t,mode:n}),K&&(K.value=``),q&&(q.value=``),Q(),z()}),we?.addEventListener(`click`,()=>{let e=Y?.value.trim(),t=X?.value.trim();if(!e||!t)return;let n=Ce?.value??`exact`;g.push({id:crypto.randomUUID(),name:e,pattern:t,match:n}),Y&&(Y.value=``),X&&(X.value=``),$(),z()}),navigator.bluetooth?f(v,`Web Bluetooth demo ready — configure settings and click Connect.`,{kind:`system`}):(f(v,`Web Bluetooth is NOT supported in this browser or OS.`,{kind:`error`}),y.disabled=!0),i(),a();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{S as e,_ as t,a as n,b as r,c as i,d as a,f as o,g as ee,h as te,i as s,m as ne,n as c,o as re,p as l,r as u,t as d,v as ie,x as ae,y as f}from"./demo-shared-BVQKG6NC.js";var p=[],m=[],oe=class extends e{_hsCmd;_hsCmdMode;_hsExpect;_hsExpectMode;constructor(e,t,n,r,i){super(e),this._hsCmd=t,this._hsCmdMode=n,this._hsExpect=r,this._hsExpectMode=i}async handshake(){if(!this._hsCmd||(this._hsCmdMode===`hex`?await this.send(P(this._hsCmd)):await this.send(u(this._hsCmd)),!this._hsExpect))return!0;let e=this._hsExpect.trim();return new Promise(t=>{let n=r=>{if(this.off(`serial:data`,n),this._hsExpectMode===`hex`){let e=new TextEncoder().encode(String(r)),n=P(this._hsExpect);t(e.length===n.length&&e.every((e,t)=>e===n[t]))}else t(String(r).trim()===e)};this.on(`serial:data`,n)})}},h=e=>document.getElementById(e),g=h(`messages`),_=h(`btn-connect`),v=h(`btn-disconnect`),y=h(`btn-send`),b=h(`input-send`),x=h(`mode-toggle`),se=h(`status-dot`),S=h(`status-text`),ce=h(`console-dot`),C=h(`console-text`),w=h(`sidebar`),T=h(`code-panel`),E=h(`code-view`),D=h(`code-tab`),le=h(`menu-btn`),ue=h(`code-toggle-btn`),O=h(`theme-btn`),de=h(`clear-btn`),k=h(`copy-btn`),fe=h(`dl-btn`),pe=h(`cfg-export-btn`),A=h(`cfg-import-input`);function j(){let e=e=>(h(e)?.value??``).trim(),t=(t,n)=>parseInt(e(t))||n,n=e=>h(e)?.value??``,r=e=>h(e)?.checked??!1,i=e(`cfg-vendor`),a=e(`cfg-product`),o=[];if(i||a){let e={};i&&(e.usbVendorId=parseInt(i,16)),a&&(e.usbProductId=parseInt(a,16)),o.push(e)}return{baudRate:t(`cfg-baud`,9600),dataBits:t(`cfg-databits`,8),stopBits:t(`cfg-stopbits`,1),parity:n(`cfg-parity`)||`none`,flowControl:n(`cfg-flow`)||`none`,bufferSize:t(`cfg-bufsize`,255),commandTimeout:t(`cfg-timeout`,3e3),autoReconnect:r(`cfg-autoreconnect`),autoReconnectInterval:t(`cfg-reconnect-ms`,1500),handshakeTimeout:t(`cfg-handshake`,2e3),delimiter:e(`cfg-delim`),prepend:e(`cfg-prepend`),append:e(`cfg-append`),hsCmd:e(`cfg-hs-cmd`),hsCmdMode:n(`cfg-hs-cmd-mode`)||`text`,hsExpect:e(`cfg-hs-expect`),hsExpectMode:n(`cfg-hs-expect-mode`)||`text`,filters:o}}function me(e){let t=(e,t)=>{let n=document.getElementById(e);n&&(n instanceof HTMLInputElement&&n.type===`checkbox`?n.checked=!!t:(n instanceof HTMLInputElement||n instanceof HTMLSelectElement)&&(n.value=String(t??``)))};t(`cfg-baud`,e.baudRate??9600),t(`cfg-databits`,e.dataBits??8),t(`cfg-stopbits`,e.stopBits??1),t(`cfg-parity`,e.parity??`none`),t(`cfg-flow`,e.flowControl??`none`),t(`cfg-bufsize`,e.bufferSize??255),t(`cfg-timeout`,e.commandTimeout??3e3),t(`cfg-handshake`,e.handshakeTimeout??2e3),t(`cfg-reconnect-ms`,e.autoReconnectInterval??1500),t(`cfg-autoreconnect`,e.autoReconnect??!1),t(`cfg-vendor`,e.filters?.[0]?.usbVendorId==null?``:e.filters[0].usbVendorId.toString(16)),t(`cfg-product`,e.filters?.[0]?.usbProductId==null?``:e.filters[0].usbProductId.toString(16)),t(`cfg-delim`,e.delimiter??`\\n`),t(`cfg-prepend`,e.prepend??``),t(`cfg-append`,e.append??``),t(`cfg-hs-cmd`,e.hsCmd??``),t(`cfg-hs-cmd-mode`,e.hsCmdMode??`text`),t(`cfg-hs-expect`,e.hsExpect??``),t(`cfg-hs-expect-mode`,e.hsExpectMode??`text`)}function M(e){let t=e.replace(/[^a-zA-Z0-9_$]/g,``).replace(/^[^a-zA-Z_$]/,`C`);return t.charAt(0).toUpperCase()+t.slice(1)||`MyDevice`}function N(e){return Array.from(e).map(e=>e.toString(16).toUpperCase().padStart(2,`0`)).join(` `)}function P(e){let t=e.replace(/\s+/g,``);if(t.length%2!=0)throw Error(`Odd number of hex characters.`);let n=new Uint8Array(t.length/2);for(let e=0;e<t.length;e+=2)n[e/2]=parseInt(t.substring(e,e+2),16);return n}function F(e,t){[se,ce].forEach(t=>{t&&(t.className=`status-dot`,e!==`disconnected`&&t.classList.add(e))}),S&&(S.textContent=t),C&&(C.textContent=t)}var I=null;function L(){I&&clearTimeout(I),I=setTimeout(()=>{let e=j(),t=M((h(`dl-name`)?.value??`MySerialDevice`).trim()),n=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,r=n?`ts`:`js`;ie(E,i(e,t,n,p,m)),D&&(D.textContent=`${t.substring(0,10).toLowerCase()}.${r}`)},180)}var R=l();O&&(O.textContent=R===`dark`?`☀️`:`🌙`),le?.addEventListener(`click`,()=>w.classList.toggle(`collapsed`)),ue?.addEventListener(`click`,()=>T.classList.toggle(`collapsed`));var z=h(`resize-handle`);if(z){let e=0,t=0,n=n=>{let r=Math.max(180,Math.min(700,t+(e-n.clientX)));document.documentElement.style.setProperty(`--code-w`,`${r}px`)},r=()=>{z.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};z.addEventListener(`pointerdown`,i=>{e=i.clientX,t=T.getBoundingClientRect().width,z.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}var B=h(`sidebar-resize-handle`);if(B){let e=0,t=0,n=n=>{let r=Math.max(200,Math.min(600,t+(n.clientX-e)));document.documentElement.style.setProperty(`--sidebar-w`,`${r}px`)},r=()=>{B.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};B.addEventListener(`pointerdown`,i=>{e=i.clientX,t=w.getBoundingClientRect().width,B.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}window.innerWidth<=960&&T.classList.add(`collapsed`),window.innerWidth<=640&&w.classList.add(`collapsed`),O?.addEventListener(`click`,()=>{let e=f();O&&(O.textContent=e===`dark`?`☀️`:`🌙`)}),de?.addEventListener(`click`,()=>c(g)),k?.addEventListener(`click`,async()=>{let e=E?.textContent??``;try{await navigator.clipboard.writeText(e),k&&(k.textContent=`Copied!`,k.classList.add(`copied`),setTimeout(()=>{k.textContent=`Copy`,k.classList.remove(`copied`)},1500))}catch{}}),fe?.addEventListener(`click`,()=>{let e=j(),r=(h(`dl-name`)?.value??`my-device`).trim(),a=M(r),o=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,s=document.querySelector(`input[name='dl-type']:checked`)?.value??`project`,c=o?`ts`:`js`,l=i(e,a,o,p,m);s===`project`?re([{name:`device.${c}`,content:l},{name:`package.json`,content:te(r,o,`serial`)},{name:`index.html`,content:ne(a,c,`Web Serial`)},{name:`README.md`,content:ee(a,c,`Web Serial`,`Requires a Chromium browser with Web Serial API support.`)},...o?[{name:`tsconfig.json`,content:t()}]:[]],`${r}-project-${c}`):n(`${r}.${c}`,l)}),pe?.addEventListener(`click`,()=>{let e=(h(`dl-name`)?.value??`my-device`).trim(),t=M(e);s({$version:1,provider:`serial`,className:e,language:document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`,dlType:document.querySelector(`input[name='dl-type']:checked`)?.value??`file`,cfg:j(),commands:p,listeners:m},t+`-config`)}),A?.addEventListener(`change`,()=>{let e=A.files?.[0];if(!e)return;let t=new FileReader;t.onload=e=>{try{let t=JSON.parse(e.target?.result);if(t.$version!==1||t.provider!==`serial`)return;let n=h(`dl-name`);n&&(n.value=t.className),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>{e.checked=e.value===t.language}),document.querySelectorAll(`input[name='dl-type']`).forEach(e=>{e.checked=e.value===t.dlType}),me(t.cfg),p=t.commands.map(e=>({...e,id:crypto.randomUUID()})),m=t.listeners.map(e=>({...e,id:crypto.randomUUID()})),Q(),$(),L()}catch{}A.value=``},t.readAsText(e)}),[`cfg-baud`,`cfg-databits`,`cfg-stopbits`,`cfg-parity`,`cfg-flow`,`cfg-bufsize`,`cfg-timeout`,`cfg-handshake`,`cfg-reconnect-ms`,`cfg-autoreconnect`,`cfg-vendor`,`cfg-product`,`cfg-delim`,`cfg-prepend`,`cfg-append`,`cfg-hs-cmd`,`cfg-hs-cmd-mode`,`cfg-hs-expect`,`cfg-hs-expect-mode`,`dl-name`].forEach(e=>{let t=document.getElementById(e);t?.addEventListener(`change`,L),t?.addEventListener(`input`,L)}),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>e.addEventListener(`change`,L)),L();var V=`text`;x?.addEventListener(`click`,()=>{V=V===`text`?`hex`:`text`,x.textContent=V===`text`?`TXT`:`HEX`,b.placeholder=V===`text`?`Type a command, e.g. LED_ON`:`Hex bytes, e.g. FF 01 A3`});var H=null;function U(e){y.disabled=!e,b.disabled=!e,v.disabled=!e,_.disabled=e}_?.addEventListener(`click`,async()=>{if(H){try{await H.disconnect()}catch{}H=null}let e=j(),t=e.delimiter?u(e.delimiter):``;H=new oe({baudRate:e.baudRate,dataBits:e.dataBits,stopBits:e.stopBits,parity:e.parity,flowControl:e.flowControl,bufferSize:e.bufferSize,commandTimeout:e.commandTimeout,parser:t?ae(t):r(),autoReconnect:e.autoReconnect,autoReconnectInterval:e.autoReconnectInterval,handshakeTimeout:e.handshakeTimeout,filters:e.filters??[]},e.hsCmd,e.hsCmdMode,e.hsExpect,e.hsExpectMode),H.on(`serial:connecting`,()=>{F(`connecting`,`Connecting…`),_.disabled=!0,d(g,`Connecting to serial device…`,{kind:`system`})}),H.on(`serial:connected`,()=>{F(`connected`,`Connected`),U(!0),d(g,`Connected via Web Serial!`,{kind:`system`})}),H.on(`serial:disconnected`,()=>{F(`disconnected`,`Disconnected`),U(!1),d(g,`Disconnected.`,{kind:`system`}),H=null}),H.on(`serial:data`,e=>{d(g,String(e),{kind:`received`,label:`Device`})}),H.on(`serial:error`,e=>{F(`error`,`Error`),d(g,`Error: ${e.message}`,{kind:`error`}),_.disabled=!1}),H.on(`serial:need-permission`,()=>{F(`error`,`Permission denied`),d(g,`Permission denied — select a port and allow access.`,{kind:`error`}),_.disabled=!1}),H.on(`serial:timeout`,e=>{d(g,`Timeout: ${N(e)}`,{kind:`error`})}),H.on(`serial:reconnecting`,()=>{F(`connecting`,`Reconnecting…`),d(g,`Auto-reconnecting…`,{kind:`system`})});try{await H.connect()}catch{}}),v?.addEventListener(`click`,async()=>{await H?.disconnect()});async function W(){let e=b.value.trim();if(!e||!H)return;let t=j(),n=t.append?u(t.append):t.delimiter?u(t.delimiter):``;try{if(V===`hex`){let t=P(e);d(g,`HEX: ${N(t)}`,{kind:`sent`,label:`You`}),await H.send(t)}else{let r=t.prepend+e+n;d(g,e,{kind:`sent`,label:`You`}),await H.send(r)}b.value=``,b.focus()}catch(e){d(g,`Send error: ${e instanceof Error?e.message:String(e)}`,{kind:`error`})}}y?.addEventListener(`click`,W),b?.addEventListener(`keydown`,e=>{e.key===`Enter`&&W()});var G=h(`cmd-name`),K=h(`cmd-value`),he=h(`cmd-mode`),ge=h(`cmd-add`),q=h(`cmd-list`),J=h(`lst-name`),Y=h(`lst-pattern`),X=h(`lst-match`),_e=h(`lst-add`),Z=h(`lst-list`);function Q(){if(q){q.innerHTML=``;for(let e of p){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.mode.toUpperCase();let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.value;let a=document.createElement(`button`);a.className=`chip-send`,a.title=`Send now`,a.textContent=`▶`,a.addEventListener(`click`,()=>{if(H)if(e.mode===`hex`){let t=P(e.value);H.send(t).catch(()=>{}),d(g,`HEX: ${N(t)}`,{kind:`sent`,label:`You`})}else{let t=j(),n=t.append?u(t.append):u(t.delimiter);H.send(t.prepend+e.value+n).catch(()=>{}),d(g,e.name,{kind:`sent`,label:`You`})}});let o=document.createElement(`button`);o.className=`chip-del`,o.title=`Remove`,o.textContent=`×`,o.addEventListener(`click`,()=>{p=p.filter(t=>t.id!==e.id),Q(),L()}),t.append(n,r,i,a,o),q.appendChild(t)}}}function $(){if(Z){Z.innerHTML=``;for(let e of m){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.match;let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.pattern;let a=document.createElement(`button`);a.className=`chip-del`,a.title=`Remove`,a.textContent=`×`,a.addEventListener(`click`,()=>{m=m.filter(t=>t.id!==e.id),$(),L()}),t.append(n,r,i,a),Z.appendChild(t)}}}ge?.addEventListener(`click`,()=>{let e=G?.value.trim(),t=K?.value.trim();if(!e||!t)return;let n=he?.value??`text`;p.push({id:crypto.randomUUID(),name:e,value:t,mode:n}),G&&(G.value=``),K&&(K.value=``),Q(),L()}),_e?.addEventListener(`click`,()=>{let e=J?.value.trim(),t=Y?.value.trim();if(!e||!t)return;let n=X?.value??`exact`;m.push({id:crypto.randomUUID(),name:e,pattern:t,match:n}),J&&(J.value=``),Y&&(Y.value=``),$(),L()}),d(g,`Web Serial demo ready — configure settings and click Connect.`,{kind:`system`}),a(),o();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{S as e,_ as t,a as n,b as r,d as i,f as a,g as o,h as ee,i as s,l as c,m as te,n as l,o as ne,p as u,r as d,t as f,v as re,x as ie,y as ae}from"./demo-shared-BVQKG6NC.js";var oe=32,se=34,p=0,ce=30,le=3,ue=7,de=1,fe=0,pe=771,me=768,he=255,ge=8,_e=`none`,m=1,ve=[16,8,7,6,5],ye=[1,2],be=[`none`,`even`,`odd`],xe=[`none`,`odd`,`even`],Se=[1,1.5,2],h={usbControlInterfaceClass:2,usbTransferInterfaceClass:10,protocol:void 0};function g(e,t){let n=e.configurations[0];if(!n)return null;for(let e of n.interfaces)if(e.alternates[0]?.interfaceClass===t)return e;return null}function _(e,t){let n=e.configurations[0];if(!n)return null;for(let e of n.interfaces){let n=e.alternates[0];if(!n||n.interfaceClass!==t)continue;let r=n.endpoints.some(e=>e.direction===`in`),i=n.endpoints.some(e=>e.direction===`out`);if(r&&i)return e}return null}function v(e,t){let n=e.alternates[0];if(n){for(let e of n.endpoints)if(e.direction===t)return e}throw TypeError(`Interface ${e.interfaceNumber} does not have an ${t} endpoint.`)}function Ce(e,t){return t===2?`cdc_acm`:e.vendorId===4292?`cp210x`:`none`}var we=class{device_;endpoint_;onError_;constructor(e,t,n){this.device_=e,this.endpoint_=t,this.onError_=n}pull(e){(async()=>{let t=this.endpoint_.packetSize;try{let n=await this.device_.transferIn(this.endpoint_.endpointNumber,t);if(n.status!==`ok`){e.error(`USB error: ${n.status}`),this.onError_();return}if(n.data?.buffer&&n.data.byteLength>0){let t=new Uint8Array(n.data.buffer,n.data.byteOffset,n.data.byteLength);t.length>0&&e.enqueue(t)}}catch(t){e.error(String(t)),this.onError_()}})()}},Te=class{device_;endpoint_;onError_;constructor(e,t,n){this.device_=e,this.endpoint_=t,this.onError_=n}async write(e,t){try{let n=await this.device_.transferOut(this.endpoint_.endpointNumber,e.buffer);n.status!==`ok`&&(t.error(n.status),this.onError_())}catch(e){t.error(String(e)),this.onError_()}}},y=class{device_;protocol_;controlInterface_;transferInterface_;inEndpoint_;outEndpoint_;serialOptions_;readable_=null;writable_=null;cdcOutputSignals_={dataTerminalReady:!1,requestToSend:!1,break:!1};constructor(e,t){this.device_=e;let n={...h,...t};this.protocol_=n.protocol??Ce(e,n.usbControlInterfaceClass);let r=n.usbControlInterfaceClass,i=n.usbTransferInterfaceClass;if(r===i){let t=_(e,i);if(!t)throw TypeError(`Unable to find interface with class ${i} that has both IN and OUT endpoints.`);this.controlInterface_=t,this.transferInterface_=t}else{let t=g(e,r);if(!t)throw TypeError(`Unable to find control interface with class ${r}.`);let n=_(e,i)??g(e,i);if(!n)throw TypeError(`Unable to find transfer interface with class ${i}.`);this.controlInterface_=t,this.transferInterface_=n}this.inEndpoint_=v(this.transferInterface_,`in`),this.outEndpoint_=v(this.transferInterface_,`out`)}get readable(){return!this.readable_&&this.device_.opened&&(this.readable_=new ReadableStream(new we(this.device_,this.inEndpoint_,()=>{this.readable_=null}),{highWaterMark:this.serialOptions_?.bufferSize??he})),this.readable_}get writable(){return!this.writable_&&this.device_.opened&&(this.writable_=new WritableStream(new Te(this.device_,this.outEndpoint_,()=>{this.writable_=null}),new ByteLengthQueuingStrategy({highWaterMark:this.serialOptions_?.bufferSize??he}))),this.writable_}async open(e){this.serialOptions_=e,this.validateOptions();try{switch(await this.device_.open(),this.device_.configuration===null&&await this.device_.selectConfiguration(1),await this.device_.claimInterface(this.controlInterface_.interfaceNumber),this.controlInterface_!==this.transferInterface_&&await this.device_.claimInterface(this.transferInterface_.interfaceNumber),this.protocol_){case`cdc_acm`:await this.cdcInit();break;case`cp210x`:await this.cp210xInit();break;case`none`:break}}catch(e){throw this.device_.opened&&await this.device_.close(),Error(`Error setting up device: `+(e instanceof Error?e.message:String(e)),{cause:e})}}async close(){let e=[];if(this.readable_&&e.push(this.readable_.cancel()),this.writable_&&e.push(this.writable_.abort()),await Promise.all(e),this.readable_=null,this.writable_=null,this.device_.opened){switch(this.protocol_){case`cdc_acm`:await this.cdcSetSignals({dataTerminalReady:!1,requestToSend:!1});break;case`cp210x`:await this.cp210xDeinit();break}await this.device_.close()}}async forget(){return this.device_.forget()}getInfo(){return{usbVendorId:this.device_.vendorId,usbProductId:this.device_.productId}}async cdcInit(){await this.cdcSetLineCoding(),await this.cdcSetSignals({dataTerminalReady:!0})}async cdcSetSignals(e){if(this.cdcOutputSignals_={...this.cdcOutputSignals_,...e},e.dataTerminalReady!==void 0||e.requestToSend!==void 0){let e=(this.cdcOutputSignals_.dataTerminalReady?1:0)|(this.cdcOutputSignals_.requestToSend?2:0);await this.device_.controlTransferOut({requestType:`class`,recipient:`interface`,request:se,value:e,index:this.controlInterface_.interfaceNumber})}}async cdcSetLineCoding(){let e=new ArrayBuffer(7),t=new DataView(e);if(t.setUint32(0,this.serialOptions_.baudRate,!0),t.setUint8(4,Se.indexOf(this.serialOptions_.stopBits??m)),t.setUint8(5,xe.indexOf(this.serialOptions_.parity??_e)),t.setUint8(6,this.serialOptions_.dataBits??ge),(await this.device_.controlTransferOut({requestType:`class`,recipient:`interface`,request:oe,value:0,index:this.controlInterface_.interfaceNumber},e)).status!==`ok`)throw new DOMException(`Failed to set line coding.`,`NetworkError`)}async cp210xInit(){let e=this.controlInterface_.interfaceNumber;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:p,value:de,index:e});let t=new ArrayBuffer(4);new DataView(t).setUint32(0,this.serialOptions_.baudRate,!0),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:ce,value:0,index:e},t);let n=this.serialOptions_.dataBits??ge,r={none:0,odd:16,even:32}[this.serialOptions_.parity??_e]??0,i=({1:0,2:2}[this.serialOptions_.stopBits??m]??0)<<8|r|n;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:le,value:i,index:e}),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:ue,value:pe,index:e})}async cp210xDeinit(){let e=this.controlInterface_.interfaceNumber;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:ue,value:me,index:e}),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:p,value:fe,index:e})}validateOptions(){if(this.serialOptions_.baudRate%1!=0)throw RangeError(`Invalid baud rate: ${this.serialOptions_.baudRate}`);if(this.serialOptions_.dataBits!==void 0&&!ve.includes(this.serialOptions_.dataBits))throw RangeError(`Invalid dataBits: ${this.serialOptions_.dataBits}`);if(this.serialOptions_.stopBits!==void 0&&!ye.includes(this.serialOptions_.stopBits))throw RangeError(`Invalid stopBits: ${this.serialOptions_.stopBits}`);if(this.serialOptions_.parity!==void 0&&!be.includes(this.serialOptions_.parity))throw RangeError(`Invalid parity: ${this.serialOptions_.parity}`)}},Ee=class{options_;constructor(e){this.options_={...h,...e}}async requestPort(e,t){let n={...this.options_,...t},r=[];if(e?.filters&&e.filters.length>0)for(let t of e.filters){let e={};t.usbVendorId!==void 0&&(e.vendorId=t.usbVendorId),t.usbProductId!==void 0&&(e.productId=t.usbProductId),n.usbControlInterfaceClass!==void 0&&n.usbControlInterfaceClass!==255?e.classCode=n.usbControlInterfaceClass:e.vendorId===void 0&&e.productId===void 0&&(e.classCode=n.usbControlInterfaceClass??2),r.push(e)}else r.push({classCode:n.usbControlInterfaceClass??2});return new y(await navigator.usb.requestDevice({filters:r}),n)}async getPorts(e){let t={...this.options_,...e},n=await navigator.usb.getDevices(),r=[];for(let e of n)try{let n=new y(e,t);r.push(n)}catch{}return r}},b=[],x=[],De=class extends e{_hsCmd;_hsCmdMode;_hsExpect;_hsExpectMode;constructor(e,t,n,r,i){super(e),this._hsCmd=t,this._hsCmdMode=n,this._hsExpect=r,this._hsExpectMode=i}async handshake(){if(!this._hsCmd||(this._hsCmdMode===`hex`?await this.send(R(this._hsCmd)):await this.send(d(this._hsCmd)),!this._hsExpect))return!0;let e=this._hsExpect.trim();return new Promise(t=>{let n=r=>{if(this.off(`serial:data`,n),this._hsExpectMode===`hex`){let e=new TextEncoder().encode(String(r)),n=R(this._hsExpect);t(e.length===n.length&&e.every((e,t)=>e===n[t]))}else t(String(r).trim()===e)};this.on(`serial:data`,n)})}},S=e=>document.getElementById(e),C=S(`messages`),w=S(`btn-connect`),Oe=S(`btn-disconnect`),T=S(`btn-send`),E=S(`input-send`),D=S(`mode-toggle`),ke=S(`status-dot`),O=S(`status-text`),Ae=S(`console-dot`),k=S(`console-text`),A=S(`sidebar`),j=S(`code-panel`),je=S(`code-view`),Me=S(`code-tab`),Ne=S(`menu-btn`),Pe=S(`code-toggle-btn`),M=S(`theme-btn`),Fe=S(`clear-btn`),N=S(`copy-btn`),Ie=S(`dl-btn`),Le=S(`cfg-export-btn`),P=S(`cfg-import-input`);function F(){let e=e=>(S(e)?.value??``).trim(),t=(t,n)=>parseInt(e(t))||n,n=e=>S(e)?.value??``,r=e=>S(e)?.checked??!1,i=e(`cfg-vendor`),a=e(`cfg-product`),o=[];if(i||a){let e={};i&&(e.usbVendorId=parseInt(i,16)),a&&(e.usbProductId=parseInt(a,16)),o.push(e)}return{usbControlInterfaceClass:t(`cfg-ctrl-class`,255),usbTransferInterfaceClass:t(`cfg-xfer-class`,255),baudRate:t(`cfg-baud`,9600),dataBits:t(`cfg-databits`,8),stopBits:t(`cfg-stopbits`,1),parity:n(`cfg-parity`)||`none`,flowControl:n(`cfg-flow`)||`none`,bufferSize:t(`cfg-bufsize`,255),commandTimeout:t(`cfg-timeout`,3e3),autoReconnect:r(`cfg-autoreconnect`),autoReconnectInterval:t(`cfg-reconnect-ms`,1500),handshakeTimeout:t(`cfg-handshake`,2e3),delimiter:e(`cfg-delim`),prepend:e(`cfg-prepend`),append:e(`cfg-append`),hsCmd:e(`cfg-hs-cmd`),hsCmdMode:n(`cfg-hs-cmd-mode`)||`text`,hsExpect:e(`cfg-hs-expect`),hsExpectMode:n(`cfg-hs-expect-mode`)||`text`,filters:o}}function Re(e){let t=(e,t)=>{let n=document.getElementById(e);n&&(n instanceof HTMLInputElement&&n.type===`checkbox`?n.checked=!!t:(n instanceof HTMLInputElement||n instanceof HTMLSelectElement)&&(n.value=String(t??``)))};t(`cfg-ctrl-class`,e.usbControlInterfaceClass??255),t(`cfg-xfer-class`,e.usbTransferInterfaceClass??255),t(`cfg-baud`,e.baudRate??9600),t(`cfg-databits`,e.dataBits??8),t(`cfg-stopbits`,e.stopBits??1),t(`cfg-parity`,e.parity??`none`),t(`cfg-flow`,e.flowControl??`none`),t(`cfg-bufsize`,e.bufferSize??255),t(`cfg-timeout`,e.commandTimeout??3e3),t(`cfg-handshake`,e.handshakeTimeout??2e3),t(`cfg-reconnect-ms`,e.autoReconnectInterval??1500),t(`cfg-autoreconnect`,e.autoReconnect??!1),t(`cfg-vendor`,e.filters?.[0]?.usbVendorId==null?``:e.filters[0].usbVendorId.toString(16)),t(`cfg-product`,e.filters?.[0]?.usbProductId==null?``:e.filters[0].usbProductId.toString(16)),t(`cfg-delim`,e.delimiter??`\\n`),t(`cfg-prepend`,e.prepend??``),t(`cfg-append`,e.append??``),t(`cfg-hs-cmd`,e.hsCmd??``),t(`cfg-hs-cmd-mode`,e.hsCmdMode??`text`),t(`cfg-hs-expect`,e.hsExpect??``),t(`cfg-hs-expect-mode`,e.hsExpectMode??`text`)}function I(e){let t=e.replace(/[^a-zA-Z0-9_$]/g,``).replace(/^[^a-zA-Z_$]/,`C`);return t.charAt(0).toUpperCase()+t.slice(1)||`MyDevice`}function L(e){return Array.from(e).map(e=>e.toString(16).toUpperCase().padStart(2,`0`)).join(` `)}function R(e){let t=e.replace(/\s+/g,``);if(t.length%2!=0)throw Error(`Odd number of hex characters.`);let n=new Uint8Array(t.length/2);for(let e=0;e<t.length;e+=2)n[e/2]=parseInt(t.substring(e,e+2),16);return n}function z(e,t){[ke,Ae].forEach(t=>{t&&(t.className=`status-dot`,e!==`disconnected`&&t.classList.add(e))}),O&&(O.textContent=t),k&&(k.textContent=t)}var B=null;function V(){B&&clearTimeout(B),B=setTimeout(()=>{let e=F(),t=I((S(`dl-name`)?.value??`MyUsbDevice`).trim()),n=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,r=n?`ts`:`js`;re(je,c(e,t,n,b,x)),Me&&(Me.textContent=`${t.substring(0,10).toLowerCase()}.${r}`)},180)}var ze=u();M&&(M.textContent=ze===`dark`?`☀️`:`🌙`),Ne?.addEventListener(`click`,()=>A.classList.toggle(`collapsed`)),Pe?.addEventListener(`click`,()=>j.classList.toggle(`collapsed`));var H=S(`resize-handle`);if(H){let e=0,t=0,n=n=>{let r=Math.max(180,Math.min(700,t+(e-n.clientX)));document.documentElement.style.setProperty(`--code-w`,`${r}px`)},r=()=>{H.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};H.addEventListener(`pointerdown`,i=>{e=i.clientX,t=j.getBoundingClientRect().width,H.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}var U=S(`sidebar-resize-handle`);if(U){let e=0,t=0,n=n=>{let r=Math.max(200,Math.min(600,t+(n.clientX-e)));document.documentElement.style.setProperty(`--sidebar-w`,`${r}px`)},r=()=>{U.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};U.addEventListener(`pointerdown`,i=>{e=i.clientX,t=A.getBoundingClientRect().width,U.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}window.innerWidth<=960&&j.classList.add(`collapsed`),window.innerWidth<=640&&A.classList.add(`collapsed`),M?.addEventListener(`click`,()=>{let e=ae();M&&(M.textContent=e===`dark`?`☀️`:`🌙`)}),Fe?.addEventListener(`click`,()=>l(C)),N?.addEventListener(`click`,async()=>{let e=je?.textContent??``;try{await navigator.clipboard.writeText(e),N&&(N.textContent=`Copied!`,N.classList.add(`copied`),setTimeout(()=>{N.textContent=`Copy`,N.classList.remove(`copied`)},1500))}catch{}}),Ie?.addEventListener(`click`,()=>{let e=F(),r=(S(`dl-name`)?.value??`my-usb-device`).trim(),i=I(r),a=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,s=document.querySelector(`input[name='dl-type']:checked`)?.value??`project`,l=a?`ts`:`js`,u=c(e,i,a,b,x);s===`project`?ne([{name:`device.${l}`,content:u},{name:`package.json`,content:ee(r,a,`usb`)},{name:`index.html`,content:te(i,l,`WebUSB`)},{name:`README.md`,content:o(i,l,`WebUSB`,`Requires a Chromium browser. Common USB-to-serial chips: CP2102 (0x10c4/0xea60), CH340 (0x1a86/0x7523).`)},...a?[{name:`tsconfig.json`,content:t()}]:[]],`${r}-project-${l}`):n(`${r}.${l}`,u)}),Le?.addEventListener(`click`,()=>{let e=(S(`dl-name`)?.value??`my-usb-device`).trim(),t=I(e);s({$version:1,provider:`usb`,className:e,language:document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`,dlType:document.querySelector(`input[name='dl-type']:checked`)?.value??`file`,cfg:F(),commands:b,listeners:x},t+`-config`)}),P?.addEventListener(`change`,()=>{let e=P.files?.[0];if(!e)return;let t=new FileReader;t.onload=e=>{try{let t=JSON.parse(e.target?.result);if(t.$version!==1||t.provider!==`usb`)return;let n=S(`dl-name`);n&&(n.value=t.className),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>{e.checked=e.value===t.language}),document.querySelectorAll(`input[name='dl-type']`).forEach(e=>{e.checked=e.value===t.dlType}),Re(t.cfg),b=t.commands.map(e=>({...e,id:crypto.randomUUID()})),x=t.listeners.map(e=>({...e,id:crypto.randomUUID()})),Q(),$(),V()}catch{}P.value=``},t.readAsText(e)}),[`cfg-ctrl-class`,`cfg-xfer-class`,`cfg-baud`,`cfg-databits`,`cfg-stopbits`,`cfg-parity`,`cfg-flow`,`cfg-bufsize`,`cfg-timeout`,`cfg-handshake`,`cfg-reconnect-ms`,`cfg-autoreconnect`,`cfg-vendor`,`cfg-product`,`cfg-delim`,`cfg-prepend`,`cfg-append`,`cfg-hs-cmd`,`cfg-hs-cmd-mode`,`cfg-hs-expect`,`cfg-hs-expect-mode`,`dl-name`].forEach(e=>{let t=document.getElementById(e);t?.addEventListener(`change`,V),t?.addEventListener(`input`,V)}),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>e.addEventListener(`change`,V)),V();var W=`text`;D?.addEventListener(`click`,()=>{W=W===`text`?`hex`:`text`,D.textContent=W===`text`?`TXT`:`HEX`,E.placeholder=W===`text`?`Type a command, e.g. LED_ON`:`Hex bytes, e.g. FF 01 A3`});var G=null;function Be(e){T.disabled=!e,E.disabled=!e,Oe.disabled=!e,w.disabled=e}w?.addEventListener(`click`,async()=>{if(G){try{await G.disconnect()}catch{}G=null}let t=F();e.setProvider(new Ee({usbControlInterfaceClass:t.usbControlInterfaceClass,usbTransferInterfaceClass:t.usbTransferInterfaceClass}));let n=t.delimiter?d(t.delimiter):``;G=new De({baudRate:t.baudRate,dataBits:t.dataBits,stopBits:t.stopBits,parity:t.parity,flowControl:t.flowControl,bufferSize:t.bufferSize,commandTimeout:t.commandTimeout,parser:n?ie(n):r(),autoReconnect:t.autoReconnect,autoReconnectInterval:t.autoReconnectInterval,handshakeTimeout:t.handshakeTimeout,filters:t.filters??[]},t.hsCmd,t.hsCmdMode,t.hsExpect,t.hsExpectMode),G.on(`serial:connecting`,()=>{z(`connecting`,`Connecting…`),w.disabled=!0,f(C,`Connecting via WebUSB…`,{kind:`system`})}),G.on(`serial:connected`,()=>{z(`connected`,`Connected`),Be(!0),f(C,`Connected via WebUSB!`,{kind:`system`})}),G.on(`serial:disconnected`,()=>{z(`disconnected`,`Disconnected`),Be(!1),f(C,`Disconnected.`,{kind:`system`}),G=null}),G.on(`serial:data`,e=>{f(C,String(e),{kind:`received`,label:`Device`})}),G.on(`serial:error`,e=>{z(`error`,`Error`),f(C,`Error: ${e.message}`,{kind:`error`}),w.disabled=!1}),G.on(`serial:need-permission`,()=>{z(`error`,`Permission denied`),f(C,`Permission denied — allow access to the USB device.`,{kind:`error`}),w.disabled=!1}),G.on(`serial:timeout`,e=>{f(C,`Timeout: ${L(e)}`,{kind:`error`})}),G.on(`serial:reconnecting`,()=>{z(`connecting`,`Reconnecting…`),f(C,`Auto-reconnecting via WebUSB…`,{kind:`system`})});try{await G.connect()}catch{}}),Oe?.addEventListener(`click`,async()=>{await G?.disconnect()});async function Ve(){let e=E.value.trim();if(!e||!G)return;let t=F(),n=t.append?d(t.append):t.delimiter?d(t.delimiter):``;try{if(W===`hex`){let t=R(e);f(C,`HEX: ${L(t)}`,{kind:`sent`,label:`You`}),await G.send(t)}else{let r=t.prepend+e+n;f(C,e,{kind:`sent`,label:`You`}),await G.send(r)}E.value=``,E.focus()}catch(e){f(C,`Send error: ${e instanceof Error?e.message:String(e)}`,{kind:`error`})}}T?.addEventListener(`click`,Ve),E?.addEventListener(`keydown`,e=>{e.key===`Enter`&&Ve()});var K=S(`cmd-name`),q=S(`cmd-value`),He=S(`cmd-mode`),Ue=S(`cmd-add`),J=S(`cmd-list`),Y=S(`lst-name`),X=S(`lst-pattern`),We=S(`lst-match`),Ge=S(`lst-add`),Z=S(`lst-list`);function Q(){if(J){J.innerHTML=``;for(let e of b){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.mode.toUpperCase();let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.value;let a=document.createElement(`button`);a.className=`chip-send`,a.title=`Send now`,a.textContent=`▶`,a.addEventListener(`click`,()=>{if(G)if(e.mode===`hex`){let t=R(e.value);G.send(t).catch(()=>{}),f(C,`HEX: ${L(t)}`,{kind:`sent`,label:`You`})}else{let t=F(),n=t.append?d(t.append):t.delimiter?d(t.delimiter):``;G.send(t.prepend+e.value+n).catch(()=>{}),f(C,e.name,{kind:`sent`,label:`You`})}});let o=document.createElement(`button`);o.className=`chip-del`,o.title=`Remove`,o.textContent=`×`,o.addEventListener(`click`,()=>{b=b.filter(t=>t.id!==e.id),Q(),V()}),t.append(n,r,i,a,o),J.appendChild(t)}}}function $(){if(Z){Z.innerHTML=``;for(let e of x){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.match;let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.pattern;let a=document.createElement(`button`);a.className=`chip-del`,a.title=`Remove`,a.textContent=`×`,a.addEventListener(`click`,()=>{x=x.filter(t=>t.id!==e.id),$(),V()}),t.append(n,r,i,a),Z.appendChild(t)}}}Ue?.addEventListener(`click`,()=>{let e=K?.value.trim(),t=q?.value.trim();!e||!t||(b.push({id:crypto.randomUUID(),name:e,value:t,mode:He?.value??`text`}),K&&(K.value=``),q&&(q.value=``),Q(),V())}),Ge?.addEventListener(`click`,()=>{let e=Y?.value.trim(),t=X?.value.trim();!e||!t||(x.push({id:crypto.randomUUID(),name:e,pattern:t,match:We?.value??`exact`}),Y&&(Y.value=``),X&&(X.value=``),$(),V())}),f(C,`WebUSB demo ready — CP2102/ESP32 (0x10c4/0xea60), CH340/Arduino (0x1a86/0x7523). Configure and click Connect.`,{kind:`system`}),i(),a();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{S as e,_ as t,a as n,b as r,d as i,f as a,g as o,h as s,i as c,m as ee,n as l,o as te,p as u,r as d,t as f,u as p,v as ne,x as re,y as ie}from"./demo-shared-BVQKG6NC.js";function m(e){return new Promise((t,n)=>{e.addEventListener(`open`,()=>t(),{once:!0}),e.addEventListener(`error`,e=>n(e),{once:!0})})}function h(e,t){return new Promise(n=>{let r=i=>{let a=JSON.parse(i.data);a.type===t&&(e.removeEventListener(`message`,r),n(a.payload))};e.addEventListener(`message`,r)})}function g(e,t){let n=null,r=null;return{get readable(){return n},get writable(){return r},getInfo(){return{usbVendorId:t.vendorId,usbProductId:t.productId}},async open(i){e.send(JSON.stringify({type:`open`,path:t.path,baudRate:i.baudRate,dataBits:i.dataBits,stopBits:i.stopBits,parity:i.parity,parser:{type:`delimiter`,value:`\\n`}})),await h(e,`opened`);let a=[],o=null,s=!1;function c(e){let t=JSON.parse(e.data);if(t.type===`data`&&t.bytes){let e=new Uint8Array(t.bytes);o?o.enqueue(e):a.push(e)}t.type===`closed`&&(s=!0,o&&o.close())}e.addEventListener(`message`,c),n=new ReadableStream({start(e){o=e;for(let t of a)e.enqueue(t);a.length=0,s&&e.close()},cancel(){e.removeEventListener(`message`,c),o=null}}),r=new WritableStream({write(t){e.send(JSON.stringify({type:`write`,bytes:Array.from(t)}))}})},async close(){e.send(JSON.stringify({type:`close`})),n=null,r=null,e.close()}}}function ae(e){return{async requestPort(t){let n=new WebSocket(e);await m(n),n.send(JSON.stringify({type:`list-ports`,filters:t?.filters??[]}));let r=(await h(n,`port-list`))[0];if(!r)throw Error(`No ports available on the bridge server. Make sure the Node.js server is running and a device is connected.`);return g(n,r)},async getPorts(){let t=new WebSocket(e);return await m(t),t.send(JSON.stringify({type:`list-ports`,filters:[]})),(await h(t,`port-list`)).map(e=>g(t,e))}}}var _=[],v=[],oe=class extends e{_hsCmd;_hsCmdMode;_hsExpect;_hsExpectMode;constructor(e,t,n,r,i){super(e),this._hsCmd=t,this._hsCmdMode=n,this._hsExpect=r,this._hsExpectMode=i}async handshake(){if(!this._hsCmd||(this._hsCmdMode===`hex`?await this.send(I(this._hsCmd)):await this.send(d(this._hsCmd)),!this._hsExpect))return!0;let e=this._hsExpect.trim();return new Promise(t=>{let n=r=>{if(this.off(`serial:data`,n),this._hsExpectMode===`hex`){let e=new TextEncoder().encode(String(r)),n=I(this._hsExpect);t(e.length===n.length&&e.every((e,t)=>e===n[t]))}else t(String(r).trim()===e)};this.on(`serial:data`,n)})}},y=e=>document.getElementById(e),b=y(`messages`),x=y(`btn-connect`),S=y(`btn-disconnect`),C=y(`btn-send`),w=y(`input-send`),T=y(`mode-toggle`),se=y(`status-dot`),E=y(`status-text`),ce=y(`console-dot`),D=y(`console-text`),O=y(`sidebar`),k=y(`code-panel`),le=y(`code-view`),ue=y(`code-tab`),de=y(`menu-btn`),fe=y(`code-toggle-btn`),A=y(`theme-btn`),pe=y(`clear-btn`),j=y(`copy-btn`),me=y(`dl-btn`),he=y(`cfg-export-btn`),M=y(`cfg-import-input`);function N(){let e=e=>(y(e)?.value??``).trim(),t=(t,n)=>parseInt(e(t))||n,n=e=>y(e)?.value??``;return{wsUrl:e(`cfg-wsurl`)||`ws://localhost:8080`,baudRate:t(`cfg-baud`,9600),dataBits:t(`cfg-databits`,8),stopBits:t(`cfg-stopbits`,1),parity:n(`cfg-parity`)||`none`,flowControl:n(`cfg-flow`)||`none`,bufferSize:t(`cfg-bufsize`,255),commandTimeout:t(`cfg-timeout`,3e3),autoReconnect:(e=>y(e)?.checked??!1)(`cfg-autoreconnect`),autoReconnectInterval:t(`cfg-reconnect-ms`,1500),handshakeTimeout:t(`cfg-handshake`,2e3),delimiter:e(`cfg-delim`),prepend:e(`cfg-prepend`),append:e(`cfg-append`),hsCmd:e(`cfg-hs-cmd`),hsCmdMode:n(`cfg-hs-cmd-mode`)||`text`,hsExpect:e(`cfg-hs-expect`),hsExpectMode:n(`cfg-hs-expect-mode`)||`text`}}function ge(e){let t=(e,t)=>{let n=document.getElementById(e);n&&(n instanceof HTMLInputElement&&n.type===`checkbox`?n.checked=!!t:(n instanceof HTMLInputElement||n instanceof HTMLSelectElement)&&(n.value=String(t??``)))};t(`cfg-wsurl`,e.wsUrl??`ws://localhost:8080`),t(`cfg-baud`,e.baudRate??9600),t(`cfg-databits`,e.dataBits??8),t(`cfg-stopbits`,e.stopBits??1),t(`cfg-parity`,e.parity??`none`),t(`cfg-flow`,e.flowControl??`none`),t(`cfg-bufsize`,e.bufferSize??255),t(`cfg-timeout`,e.commandTimeout??3e3),t(`cfg-handshake`,e.handshakeTimeout??2e3),t(`cfg-reconnect-ms`,e.autoReconnectInterval??1500),t(`cfg-autoreconnect`,e.autoReconnect??!1),t(`cfg-delim`,e.delimiter??`\\n`),t(`cfg-prepend`,e.prepend??``),t(`cfg-append`,e.append??``),t(`cfg-hs-cmd`,e.hsCmd??``),t(`cfg-hs-cmd-mode`,e.hsCmdMode??`text`),t(`cfg-hs-expect`,e.hsExpect??``),t(`cfg-hs-expect-mode`,e.hsExpectMode??`text`)}function P(e){let t=e.replace(/[^a-zA-Z0-9_$]/g,``).replace(/^[^a-zA-Z_$]/,`C`);return t.charAt(0).toUpperCase()+t.slice(1)||`MyDevice`}function F(e){return Array.from(e).map(e=>e.toString(16).toUpperCase().padStart(2,`0`)).join(` `)}function I(e){let t=e.replace(/\s+/g,``);if(t.length%2!=0)throw Error(`Odd number of hex characters.`);let n=new Uint8Array(t.length/2);for(let e=0;e<t.length;e+=2)n[e/2]=parseInt(t.substring(e,e+2),16);return n}function L(e,t){[se,ce].forEach(t=>{t&&(t.className=`status-dot`,e!==`disconnected`&&t.classList.add(e))}),E&&(E.textContent=t),D&&(D.textContent=t)}var R=null;function z(){R&&clearTimeout(R),R=setTimeout(()=>{let e=N(),t=P((y(`dl-name`)?.value??`MyWsDevice`).trim()),n=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,r=n?`ts`:`js`;ne(le,p(e,t,n,_,v)),ue&&(ue.textContent=`${t.substring(0,10).toLowerCase()}.${r}`)},180)}var _e=u();A&&(A.textContent=_e===`dark`?`☀️`:`🌙`),de?.addEventListener(`click`,()=>O.classList.toggle(`collapsed`)),fe?.addEventListener(`click`,()=>k.classList.toggle(`collapsed`));var B=y(`resize-handle`);if(B){let e=0,t=0,n=n=>{let r=Math.max(180,Math.min(700,t+(e-n.clientX)));document.documentElement.style.setProperty(`--code-w`,`${r}px`)},r=()=>{B.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};B.addEventListener(`pointerdown`,i=>{e=i.clientX,t=k.getBoundingClientRect().width,B.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}var V=y(`sidebar-resize-handle`);if(V){let e=0,t=0,n=n=>{let r=Math.max(200,Math.min(600,t+(n.clientX-e)));document.documentElement.style.setProperty(`--sidebar-w`,`${r}px`)},r=()=>{V.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};V.addEventListener(`pointerdown`,i=>{e=i.clientX,t=O.getBoundingClientRect().width,V.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}window.innerWidth<=960&&k.classList.add(`collapsed`),window.innerWidth<=640&&O.classList.add(`collapsed`),A?.addEventListener(`click`,()=>{let e=ie();A&&(A.textContent=e===`dark`?`☀️`:`🌙`)}),pe?.addEventListener(`click`,()=>l(b)),j?.addEventListener(`click`,async()=>{let e=le?.textContent??``;try{await navigator.clipboard.writeText(e),j&&(j.textContent=`Copied!`,j.classList.add(`copied`),setTimeout(()=>{j.textContent=`Copy`,j.classList.remove(`copied`)},1500))}catch{}}),me?.addEventListener(`click`,()=>{let e=N(),r=(y(`dl-name`)?.value??`my-ws-device`).trim(),i=P(r),a=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,c=document.querySelector(`input[name='dl-type']:checked`)?.value??`project`,l=a?`ts`:`js`,u=p(e,i,a,_,v);c===`project`?te([{name:`device.${l}`,content:u},{name:`package.json`,content:s(r,a,`ws`)},{name:`index.html`,content:ee(i,l,`WebSocket`)},{name:`README.md`,content:o(i,l,`WebSocket`,"Requires a WebSocket-to-serial bridge. Start the bridge: `cd demos/websocket && node server.js`")},...a?[{name:`tsconfig.json`,content:t()}]:[]],`${r}-project-${l}`):n(`${r}.${l}`,u)}),he?.addEventListener(`click`,()=>{let e=(y(`dl-name`)?.value??`my-ws-device`).trim(),t=P(e);c({$version:1,provider:`ws`,className:e,language:document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`,dlType:document.querySelector(`input[name='dl-type']:checked`)?.value??`file`,cfg:N(),commands:_,listeners:v},t+`-config`)}),M?.addEventListener(`change`,()=>{let e=M.files?.[0];if(!e)return;let t=new FileReader;t.onload=e=>{try{let t=JSON.parse(e.target?.result);if(t.$version!==1||t.provider!==`ws`)return;let n=y(`dl-name`);n&&(n.value=t.className),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>{e.checked=e.value===t.language}),document.querySelectorAll(`input[name='dl-type']`).forEach(e=>{e.checked=e.value===t.dlType}),ge(t.cfg),_=t.commands.map(e=>({...e,id:crypto.randomUUID()})),v=t.listeners.map(e=>({...e,id:crypto.randomUUID()})),Q(),$(),z()}catch{}M.value=``},t.readAsText(e)}),[`cfg-wsurl`,`cfg-baud`,`cfg-databits`,`cfg-stopbits`,`cfg-parity`,`cfg-flow`,`cfg-bufsize`,`cfg-timeout`,`cfg-handshake`,`cfg-reconnect-ms`,`cfg-autoreconnect`,`cfg-delim`,`cfg-prepend`,`cfg-append`,`cfg-hs-cmd`,`cfg-hs-cmd-mode`,`cfg-hs-expect`,`cfg-hs-expect-mode`,`dl-name`].forEach(e=>{let t=document.getElementById(e);t?.addEventListener(`change`,z),t?.addEventListener(`input`,z)}),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>e.addEventListener(`change`,z)),z();var H=`text`;T?.addEventListener(`click`,()=>{H=H===`text`?`hex`:`text`,T.textContent=H===`text`?`TXT`:`HEX`,w.placeholder=H===`text`?`Type a command, e.g. LED_ON`:`Hex bytes, e.g. FF 01 A3`});var U=null;function W(e){C.disabled=!e,w.disabled=!e,S.disabled=!e,x.disabled=e}x?.addEventListener(`click`,async()=>{if(U){try{await U.disconnect()}catch{}U=null}let t=N();e.setProvider(ae(t.wsUrl));let n=t.delimiter?d(t.delimiter):``;U=new oe({baudRate:t.baudRate,dataBits:t.dataBits,stopBits:t.stopBits,parity:t.parity,flowControl:t.flowControl,bufferSize:t.bufferSize,commandTimeout:t.commandTimeout,parser:n?re(n):r(),autoReconnect:t.autoReconnect,autoReconnectInterval:t.autoReconnectInterval,handshakeTimeout:t.handshakeTimeout},t.hsCmd,t.hsCmdMode,t.hsExpect,t.hsExpectMode),U.on(`serial:connecting`,()=>{L(`connecting`,`Connecting…`),x.disabled=!0,f(b,`Connecting to ${t.wsUrl}…`,{kind:`system`})}),U.on(`serial:connected`,()=>{L(`connected`,`Connected`),W(!0),f(b,`Connected via WebSocket (${t.wsUrl})!`,{kind:`system`})}),U.on(`serial:disconnected`,()=>{L(`disconnected`,`Disconnected`),W(!1),f(b,`Disconnected.`,{kind:`system`}),U=null}),U.on(`serial:data`,e=>{f(b,String(e),{kind:`received`,label:`Device`})}),U.on(`serial:error`,e=>{L(`error`,`Error`),f(b,`Error: ${e.message}`,{kind:`error`}),x.disabled=!1}),U.on(`serial:need-permission`,()=>{L(`error`,`Connection refused`),f(b,`Could not connect — is the WebSocket bridge running?`,{kind:`error`}),x.disabled=!1}),U.on(`serial:timeout`,e=>{f(b,`Timeout: ${F(e)}`,{kind:`error`})}),U.on(`serial:reconnecting`,()=>{L(`connecting`,`Reconnecting…`),f(b,`Auto-reconnecting via WebSocket…`,{kind:`system`})});try{await U.connect()}catch{}}),S?.addEventListener(`click`,async()=>{await U?.disconnect()});async function G(){let e=w.value.trim();if(!e||!U)return;let t=N(),n=t.append?d(t.append):t.delimiter?d(t.delimiter):``;try{if(H===`hex`){let t=I(e);f(b,`HEX: ${F(t)}`,{kind:`sent`,label:`You`}),await U.send(t)}else{let r=t.prepend+e+n;f(b,e,{kind:`sent`,label:`You`}),await U.send(r)}w.value=``,w.focus()}catch(e){f(b,`Send error: ${e instanceof Error?e.message:String(e)}`,{kind:`error`})}}C?.addEventListener(`click`,G),w?.addEventListener(`keydown`,e=>{e.key===`Enter`&&G()});var K=y(`cmd-name`),q=y(`cmd-value`),ve=y(`cmd-mode`),ye=y(`cmd-add`),J=y(`cmd-list`),Y=y(`lst-name`),X=y(`lst-pattern`),be=y(`lst-match`),xe=y(`lst-add`),Z=y(`lst-list`);function Q(){if(J){J.innerHTML=``;for(let e of _){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.mode.toUpperCase();let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.value;let a=document.createElement(`button`);a.className=`chip-send`,a.title=`Send now`,a.textContent=`▶`,a.addEventListener(`click`,()=>{if(U)if(e.mode===`hex`){let t=I(e.value);U.send(t).catch(()=>{}),f(b,`HEX: ${F(t)}`,{kind:`sent`,label:`You`})}else{let t=N(),n=t.append?d(t.append):d(t.delimiter);U.send(t.prepend+e.value+n).catch(()=>{}),f(b,e.name,{kind:`sent`,label:`You`})}});let o=document.createElement(`button`);o.className=`chip-del`,o.title=`Remove`,o.textContent=`×`,o.addEventListener(`click`,()=>{_=_.filter(t=>t.id!==e.id),Q(),z()}),t.append(n,r,i,a,o),J.appendChild(t)}}}function $(){if(Z){Z.innerHTML=``;for(let e of v){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.match;let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.pattern;let a=document.createElement(`button`);a.className=`chip-del`,a.title=`Remove`,a.textContent=`×`,a.addEventListener(`click`,()=>{v=v.filter(t=>t.id!==e.id),$(),z()}),t.append(n,r,i,a),Z.appendChild(t)}}}ye?.addEventListener(`click`,()=>{let e=K?.value.trim(),t=q?.value.trim();if(!e||!t)return;let n=ve?.value??`text`;_.push({id:crypto.randomUUID(),name:e,value:t,mode:n}),K&&(K.value=``),q&&(q.value=``),Q(),z()}),xe?.addEventListener(`click`,()=>{let e=Y?.value.trim(),t=X?.value.trim();if(!e||!t)return;let n=be?.value??`exact`;v.push({id:crypto.randomUUID(),name:e,pattern:t,match:n}),Y&&(Y.value=``),X&&(X.value=``),$(),z()}),f(b,`WebSocket bridge demo — start the Node.js bridge first: cd demos/websocket && node server.js`,{kind:`system`}),i(),a();
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
<script
|
|
17
17
|
type="module"
|
|
18
18
|
crossorigin
|
|
19
|
-
src="/demos/assets/web-bluetooth-
|
|
19
|
+
src="/demos/assets/web-bluetooth-yFc46qqb.js"
|
|
20
20
|
></script>
|
|
21
21
|
<link
|
|
22
22
|
rel="modulepreload"
|
|
23
23
|
crossorigin
|
|
24
|
-
href="/demos/assets/demo-shared-
|
|
24
|
+
href="/demos/assets/demo-shared-BVQKG6NC.js"
|
|
25
25
|
/>
|
|
26
26
|
<link
|
|
27
27
|
rel="stylesheet"
|
|
@@ -300,12 +300,12 @@
|
|
|
300
300
|
</div>
|
|
301
301
|
<div class="dl-type-row">
|
|
302
302
|
<label class="dl-opt"
|
|
303
|
-
><input type="radio" name="dl-type" value="file"
|
|
304
|
-
|
|
303
|
+
><input type="radio" name="dl-type" value="file" /> Standalone
|
|
304
|
+
file</label
|
|
305
305
|
>
|
|
306
306
|
<label class="dl-opt"
|
|
307
|
-
><input type="radio" name="dl-type" value="project" />
|
|
308
|
-
project (ZIP)</label
|
|
307
|
+
><input type="radio" name="dl-type" value="project" checked />
|
|
308
|
+
Full project (ZIP)</label
|
|
309
309
|
>
|
|
310
310
|
</div>
|
|
311
311
|
<div class="dl-btns">
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
<script
|
|
17
17
|
type="module"
|
|
18
18
|
crossorigin
|
|
19
|
-
src="/demos/assets/web-serial-
|
|
19
|
+
src="/demos/assets/web-serial-DLoCrJS2.js"
|
|
20
20
|
></script>
|
|
21
21
|
<link
|
|
22
22
|
rel="modulepreload"
|
|
23
23
|
crossorigin
|
|
24
|
-
href="/demos/assets/demo-shared-
|
|
24
|
+
href="/demos/assets/demo-shared-BVQKG6NC.js"
|
|
25
25
|
/>
|
|
26
26
|
<link
|
|
27
27
|
rel="stylesheet"
|
|
@@ -373,12 +373,12 @@
|
|
|
373
373
|
</div>
|
|
374
374
|
<div class="dl-type-row">
|
|
375
375
|
<label class="dl-opt"
|
|
376
|
-
><input type="radio" name="dl-type" value="file"
|
|
377
|
-
|
|
376
|
+
><input type="radio" name="dl-type" value="file" /> Standalone
|
|
377
|
+
file</label
|
|
378
378
|
>
|
|
379
379
|
<label class="dl-opt"
|
|
380
|
-
><input type="radio" name="dl-type" value="project" />
|
|
381
|
-
project (ZIP)</label
|
|
380
|
+
><input type="radio" name="dl-type" value="project" checked />
|
|
381
|
+
Full project (ZIP)</label
|
|
382
382
|
>
|
|
383
383
|
</div>
|
|
384
384
|
<div class="dl-btns">
|
package/dist/demos/web-usb.html
CHANGED
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
<script
|
|
17
17
|
type="module"
|
|
18
18
|
crossorigin
|
|
19
|
-
src="/demos/assets/web-usb-
|
|
19
|
+
src="/demos/assets/web-usb-De-CD5AK.js"
|
|
20
20
|
></script>
|
|
21
21
|
<link
|
|
22
22
|
rel="modulepreload"
|
|
23
23
|
crossorigin
|
|
24
|
-
href="/demos/assets/demo-shared-
|
|
24
|
+
href="/demos/assets/demo-shared-BVQKG6NC.js"
|
|
25
25
|
/>
|
|
26
26
|
<link
|
|
27
27
|
rel="stylesheet"
|
|
@@ -418,12 +418,12 @@
|
|
|
418
418
|
</div>
|
|
419
419
|
<div class="dl-type-row">
|
|
420
420
|
<label class="dl-opt"
|
|
421
|
-
><input type="radio" name="dl-type" value="file"
|
|
422
|
-
|
|
421
|
+
><input type="radio" name="dl-type" value="file" /> Standalone
|
|
422
|
+
file</label
|
|
423
423
|
>
|
|
424
424
|
<label class="dl-opt"
|
|
425
|
-
><input type="radio" name="dl-type" value="project" />
|
|
426
|
-
project (ZIP)</label
|
|
425
|
+
><input type="radio" name="dl-type" value="project" checked />
|
|
426
|
+
Full project (ZIP)</label
|
|
427
427
|
>
|
|
428
428
|
</div>
|
|
429
429
|
<div class="dl-btns">
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
<script
|
|
17
17
|
type="module"
|
|
18
18
|
crossorigin
|
|
19
|
-
src="/demos/assets/websocket-
|
|
19
|
+
src="/demos/assets/websocket-pLqbbDwD.js"
|
|
20
20
|
></script>
|
|
21
21
|
<link
|
|
22
22
|
rel="modulepreload"
|
|
23
23
|
crossorigin
|
|
24
|
-
href="/demos/assets/demo-shared-
|
|
24
|
+
href="/demos/assets/demo-shared-BVQKG6NC.js"
|
|
25
25
|
/>
|
|
26
26
|
<link
|
|
27
27
|
rel="stylesheet"
|
|
@@ -385,12 +385,12 @@
|
|
|
385
385
|
</div>
|
|
386
386
|
<div class="dl-type-row">
|
|
387
387
|
<label class="dl-opt"
|
|
388
|
-
><input type="radio" name="dl-type" value="file"
|
|
389
|
-
|
|
388
|
+
><input type="radio" name="dl-type" value="file" /> Standalone
|
|
389
|
+
file</label
|
|
390
390
|
>
|
|
391
391
|
<label class="dl-opt"
|
|
392
|
-
><input type="radio" name="dl-type" value="project" />
|
|
393
|
-
project (ZIP)</label
|
|
392
|
+
><input type="radio" name="dl-type" value="project" checked />
|
|
393
|
+
Full project (ZIP)</label
|
|
394
394
|
>
|
|
395
395
|
</div>
|
|
396
396
|
<div class="dl-btns">
|
package/dist/robots.txt
CHANGED
package/dist/webserial-core.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class{listeners={};on(e,t){return this.listeners[e]||(this.listeners[e]=new Set),this.listeners[e].add(t),this}off(e,t){return this.listeners[e]&&this.listeners[e].delete(t),this}emit(e,...t){let n=this.listeners[e];if(!n||n.size===0)return!1;for(let e of n)e(...t);return!0}},t=class{static instances=new Set;static portInstanceMap=new WeakMap;static getInstances(){return Array.from(this.instances)}static register(e){this.instances.add(e)}static unregister(e){this.instances.delete(e)}static isPortInUse(e,t){let n=this.portInstanceMap.get(e);return n!==void 0&&n!==t}static lockPort(e,t){this.portInstanceMap.set(e,t)}static unlockPort(e){this.portInstanceMap.delete(e)}},n=class{queue=[];isProcessing=!1;isPaused=!0;timeoutId=null;commandTimeout;onSend;onTimeout;constructor(e){this.commandTimeout=e.commandTimeout,this.onSend=e.onSend,this.onTimeout=e.onTimeout}get queueSize(){return this.queue.length}enqueue(e){this.queue.push(e),this.tryProcessNext()}advance(){this.clearCommandTimeout(),this.isProcessing=!1,this.tryProcessNext()}pause(){this.isPaused=!0,this.clearCommandTimeout(),this.isProcessing=!1}resume(){this.isPaused=!1,this.tryProcessNext()}clear(){this.queue=[],this.clearCommandTimeout(),this.isProcessing=!1}snapshot(){return[...this.queue]}restore(e){this.queue=[...e,...this.queue]}tryProcessNext(){if(this.isPaused||this.isProcessing||this.queue.length===0)return;this.isProcessing=!0;let e=this.queue.shift();this.commandTimeout>0&&(this.timeoutId=setTimeout(()=>{this.timeoutId=null,this.onTimeout(e),this.advance()},this.commandTimeout)),this.onSend(e).catch(()=>{this.advance()})}clearCommandTimeout(){this.timeoutId!==null&&(clearTimeout(this.timeoutId),this.timeoutId=null)}},r=class e extends Error{constructor(t){super(t),this.name=`SerialPortConflictError`,Object.setPrototypeOf(this,e.prototype)}},i=class e extends Error{constructor(t){super(t),this.name=`SerialPermissionError`,Object.setPrototypeOf(this,e.prototype)}},a=class e extends Error{constructor(t){super(t),this.name=`SerialTimeoutError`,Object.setPrototypeOf(this,e.prototype)}},o=class e extends Error{constructor(t){super(t),this.name=`SerialReadError`,Object.setPrototypeOf(this,e.prototype)}},s=class e extends Error{constructor(t){super(t),this.name=`SerialWriteError`,Object.setPrototypeOf(this,e.prototype)}},c=class r extends e{port=null;reader=null;writer=null;queue;options;isConnecting=!1;abortController=null;userInitiatedDisconnect=!1;reconnectTimerId=null;isHandshaking=!1;static customProvider=null;static polyfillOptions;constructor(e){super(),this.options={baudRate:e.baudRate,dataBits:e.dataBits??8,stopBits:e.stopBits??1,parity:e.parity??`none`,bufferSize:e.bufferSize??255,flowControl:e.flowControl??`none`,filters:e.filters??[],commandTimeout:e.commandTimeout??0,parser:e.parser,autoReconnect:e.autoReconnect??!1,autoReconnectInterval:e.autoReconnectInterval??1500,handshakeTimeout:e.handshakeTimeout??2e3,provider:e.provider,polyfillOptions:e.polyfillOptions},this.queue=new n({commandTimeout:this.options.commandTimeout,onSend:async e=>{await this.writeToPort(e),this.emit(`serial:sent`,e,this)},onTimeout:e=>{this.emit(`serial:timeout`,e,this)}}),this.on(`serial:data`,()=>{this.queue.advance()}),t.register(this)}async handshake(){return!0}async connect(){if(!this.isConnecting&&!this.port){this.isConnecting=!0,this.emit(`serial:connecting`,this);try{let e=this.getSerial();if(!e)throw Error(`Web Serial API is not supported in this browser. Use AbstractSerialDevice.setProvider() to set a WebUSB polyfill.`);if(this.port=await this.findAndValidatePort(),!this.port){let t;try{t=await e.requestPort({filters:this.options.filters},this.options.polyfillOptions??r.polyfillOptions)}catch(e){throw e instanceof DOMException&&(e.name===`NotFoundError`||e.name===`SecurityError`||e.name===`AbortError`)?new i(e instanceof Error?e.message:String(e)):e instanceof Error?e:Error(String(e))}if(!await this.openAndHandshake(t))throw Error(`Handshake failed: the selected device did not respond correctly.`);this.port=t}this.abortController=new AbortController,this.queue.resume(),this.emit(`serial:connected`,this)}catch(e){if(e instanceof i?this.emit(`serial:need-permission`,this):this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this),this.port){t.unlockPort(this.port);try{await this.port.close()}catch{}this.port=null}throw e}finally{this.isConnecting=!1}}}async disconnect(){this.port&&(this.userInitiatedDisconnect=!0,this.stopReconnecting(),await this.cleanupPort())}async cleanupPort(){if(this.port){this.queue.pause(),this.abortController?.abort(),this.abortController=null;try{let e=this.reader,t=this.writer;if(this.reader=null,this.writer=null,e){try{await e.cancel()}catch{}try{e.releaseLock()}catch{}}if(t){try{await t.close()}catch{}try{t.releaseLock()}catch{}}try{await this.port.close()}catch{}}catch(e){this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this)}finally{this.port&&t.unlockPort(this.port),this.port=null,this.options.parser?.reset?.(),this.emit(`serial:disconnected`,this),!this.userInitiatedDisconnect&&this.options.autoReconnect&&this.startReconnecting(),this.userInitiatedDisconnect=!1}}}async forget(){await this.disconnect(),this.port&&typeof this.port.forget==`function`&&await this.port.forget(),t.unregister(this)}async send(e){let t;t=typeof e==`string`?new TextEncoder().encode(e):e,t.length>0&&this.queue.enqueue(t)}clearQueue(){this.queue.clear(),this.emit(`serial:queue-empty`,this)}async writeToPort(e){if(!this.port||!this.port.writable)throw new s(`Port not writable.`);this.writer=this.port.writable.getWriter();try{await this.writer.write(e)}catch(e){throw new s(e instanceof Error?e.message:String(e))}finally{this.writer.releaseLock(),this.writer=null}}async readLoop(){if(!(!this.port||!this.port.readable)&&!this.reader){this.reader=this.port.readable.getReader();try{for(;;){let{value:e,done:t}=await this.reader.read();if(t)break;e&&(this.options.parser?this.options.parser.parse(e,e=>{this.emit(`serial:data`,e,this)}):this.emit(`serial:data`,e,this))}}catch(e){if(this.port)throw new o(e instanceof Error?e.message:String(e))}finally{if(this.reader){try{this.reader.releaseLock()}catch{}this.reader=null}}}}async openAndHandshake(e){let n=this;if(t.isPortInUse(e,n))return!1;t.lockPort(e,n);try{await e.open({baudRate:this.options.baudRate,dataBits:this.options.dataBits,stopBits:this.options.stopBits,parity:this.options.parity,bufferSize:this.options.bufferSize,flowControl:this.options.flowControl})}catch(n){throw t.unlockPort(e),n instanceof Error?n:Error(String(n))}this.port=e,this.abortController=new AbortController;let r=this.queue.snapshot();this.isHandshaking=!0,this.readLoop().catch(e=>{!this.isHandshaking&&this.port&&(this.emit(`serial:error`,e,this),this.cleanupPort())}),this.queue.resume();try{let t=await this.runHandshakeWithTimeout();return this.isHandshaking=!1,t?(this.queue.pause(),this.queue.clear(),this.queue.restore(r),this.options.parser?.reset?.(),!0):(await this.teardownHandshake(e,r),!1)}catch{return this.isHandshaking=!1,await this.teardownHandshake(e,r),!1}}async teardownHandshake(e,n){this.queue.pause(),this.queue.clear(),this.queue.restore(n),await this.stopReader(),this.port=null,this.abortController=null,this.options.parser?.reset?.();try{await e.close()}catch{}t.unlockPort(e)}async stopReader(){let e=this.reader;if(this.reader=null,e){try{await e.cancel()}catch{}try{e.releaseLock()}catch{}}}async runHandshakeWithTimeout(){let e=this.options.handshakeTimeout??2e3;return Promise.race([this.handshake(),new Promise(t=>setTimeout(()=>t(!1),e))])}async findAndValidatePort(){let e=this.getSerial();if(!e)return null;let n=await e.getPorts(this.options.polyfillOptions??r.polyfillOptions);if(n.length===0)return null;let i=this.options.filters??[],a=this;for(let e of n)if(!t.isPortInUse(e,a)){if(i.length>0){let t=e.getInfo();if(!i.some(e=>{let n=e.usbVendorId===void 0||e.usbVendorId===t.usbVendorId,r=e.usbProductId===void 0||e.usbProductId===t.usbProductId;return n&&r}))continue}try{if(await this.openAndHandshake(e))return e}catch{}}return null}startReconnecting(){this.reconnectTimerId||=(this.emit(`serial:reconnecting`,this),setInterval(async()=>{if(this.port||this.isConnecting){this.stopReconnecting();return}try{let e=await this.findAndValidatePort();e&&(this.stopReconnecting(),await this.reconnect(e))}catch{}},this.options.autoReconnectInterval))}stopReconnecting(){this.reconnectTimerId&&=(clearInterval(this.reconnectTimerId),null)}async reconnect(e){if(!(this.isConnecting||this.port)){this.isConnecting=!0,this.emit(`serial:connecting`,this);try{this.port=e,this.abortController=new AbortController,this.queue.resume(),this.emit(`serial:connected`,this)}catch(e){this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this),this.port&&=(t.unlockPort(this.port),null),this.options.autoReconnect&&this.startReconnecting()}finally{this.isConnecting=!1}}}static getInstances(){return t.getInstances()}static async connectAll(){let e=t.getInstances();for(let t of e)try{await t.connect()}catch{}}static setProvider(e,t){r.customProvider=e,r.polyfillOptions=t}getSerial(){return this.options.provider?this.options.provider:r.customProvider?r.customProvider:typeof navigator<`u`&&navigator.serial?navigator.serial:null}};function l(e){if(e<=0)throw Error(`FixedLengthParser: length must be greater than 0`);let t=new Uint8Array;return{parse(n,r){let i=new Uint8Array(t.length+n.length);for(i.set(t),i.set(n,t.length),t=i;t.length>=e;)r(t.slice(0,e)),t=t.slice(e)},reset(){t=new Uint8Array}}}function u(e){let t=``,n=new TextDecoder;return{parse(r,i){t+=n.decode(r,{stream:!0});let a;for(;(a=t.indexOf(e))!==-1;)i(t.slice(0,a)),t=t.slice(a+e.length)},reset(){t=``,n=new TextDecoder}}}function d(){return{parse(e,t){t(e)},reset(){}}}var f=32,p=34,m=0,h=30,g=3,_=7,v=1,y=0,b=771,x=768,S=255,C=8,w=`none`,T=1,E=[16,8,7,6,5],D=[1,2],O=[`none`,`even`,`odd`],k=[`none`,`odd`,`even`],A=[1,1.5,2],j={usbControlInterfaceClass:2,usbTransferInterfaceClass:10,protocol:void 0};function M(e,t){let n=e.configurations[0];if(!n)return null;for(let e of n.interfaces)if(e.alternates[0]?.interfaceClass===t)return e;return null}function N(e,t){let n=e.configurations[0];if(!n)return null;for(let e of n.interfaces){let n=e.alternates[0];if(!n||n.interfaceClass!==t)continue;let r=n.endpoints.some(e=>e.direction===`in`),i=n.endpoints.some(e=>e.direction===`out`);if(r&&i)return e}return null}function P(e,t){let n=e.alternates[0];if(n){for(let e of n.endpoints)if(e.direction===t)return e}throw TypeError(`Interface ${e.interfaceNumber} does not have an ${t} endpoint.`)}function F(e,t){return t===2?`cdc_acm`:e.vendorId===4292?`cp210x`:`none`}var I=class{device_;endpoint_;onError_;constructor(e,t,n){this.device_=e,this.endpoint_=t,this.onError_=n}pull(e){(async()=>{let t=this.endpoint_.packetSize;try{let n=await this.device_.transferIn(this.endpoint_.endpointNumber,t);if(n.status!==`ok`){e.error(`USB error: ${n.status}`),this.onError_();return}if(n.data?.buffer&&n.data.byteLength>0){let t=new Uint8Array(n.data.buffer,n.data.byteOffset,n.data.byteLength);t.length>0&&e.enqueue(t)}}catch(t){e.error(String(t)),this.onError_()}})()}},L=class{device_;endpoint_;onError_;constructor(e,t,n){this.device_=e,this.endpoint_=t,this.onError_=n}async write(e,t){try{let n=await this.device_.transferOut(this.endpoint_.endpointNumber,e.buffer);n.status!==`ok`&&(t.error(n.status),this.onError_())}catch(e){t.error(String(e)),this.onError_()}}},R=class{device_;protocol_;controlInterface_;transferInterface_;inEndpoint_;outEndpoint_;serialOptions_;readable_=null;writable_=null;cdcOutputSignals_={dataTerminalReady:!1,requestToSend:!1,break:!1};constructor(e,t){this.device_=e;let n={...j,...t};this.protocol_=n.protocol??F(e,n.usbControlInterfaceClass);let r=n.usbControlInterfaceClass,i=n.usbTransferInterfaceClass;if(r===i){let t=N(e,i);if(!t)throw TypeError(`Unable to find interface with class ${i} that has both IN and OUT endpoints.`);this.controlInterface_=t,this.transferInterface_=t}else{let t=M(e,r);if(!t)throw TypeError(`Unable to find control interface with class ${r}.`);let n=N(e,i)??M(e,i);if(!n)throw TypeError(`Unable to find transfer interface with class ${i}.`);this.controlInterface_=t,this.transferInterface_=n}this.inEndpoint_=P(this.transferInterface_,`in`),this.outEndpoint_=P(this.transferInterface_,`out`)}get readable(){return!this.readable_&&this.device_.opened&&(this.readable_=new ReadableStream(new I(this.device_,this.inEndpoint_,()=>{this.readable_=null}),{highWaterMark:this.serialOptions_?.bufferSize??S})),this.readable_}get writable(){return!this.writable_&&this.device_.opened&&(this.writable_=new WritableStream(new L(this.device_,this.outEndpoint_,()=>{this.writable_=null}),new ByteLengthQueuingStrategy({highWaterMark:this.serialOptions_?.bufferSize??S}))),this.writable_}async open(e){this.serialOptions_=e,this.validateOptions();try{switch(await this.device_.open(),this.device_.configuration===null&&await this.device_.selectConfiguration(1),await this.device_.claimInterface(this.controlInterface_.interfaceNumber),this.controlInterface_!==this.transferInterface_&&await this.device_.claimInterface(this.transferInterface_.interfaceNumber),this.protocol_){case`cdc_acm`:await this.cdcInit();break;case`cp210x`:await this.cp210xInit();break;case`none`:break}}catch(e){throw this.device_.opened&&await this.device_.close(),Error(`Error setting up device: `+(e instanceof Error?e.message:String(e)),{cause:e})}}async close(){let e=[];if(this.readable_&&e.push(this.readable_.cancel()),this.writable_&&e.push(this.writable_.abort()),await Promise.all(e),this.readable_=null,this.writable_=null,this.device_.opened){switch(this.protocol_){case`cdc_acm`:await this.cdcSetSignals({dataTerminalReady:!1,requestToSend:!1});break;case`cp210x`:await this.cp210xDeinit();break}await this.device_.close()}}async forget(){return this.device_.forget()}getInfo(){return{usbVendorId:this.device_.vendorId,usbProductId:this.device_.productId}}async cdcInit(){await this.cdcSetLineCoding(),await this.cdcSetSignals({dataTerminalReady:!0})}async cdcSetSignals(e){if(this.cdcOutputSignals_={...this.cdcOutputSignals_,...e},e.dataTerminalReady!==void 0||e.requestToSend!==void 0){let e=(this.cdcOutputSignals_.dataTerminalReady?1:0)|(this.cdcOutputSignals_.requestToSend?2:0);await this.device_.controlTransferOut({requestType:`class`,recipient:`interface`,request:p,value:e,index:this.controlInterface_.interfaceNumber})}}async cdcSetLineCoding(){let e=new ArrayBuffer(7),t=new DataView(e);if(t.setUint32(0,this.serialOptions_.baudRate,!0),t.setUint8(4,A.indexOf(this.serialOptions_.stopBits??T)),t.setUint8(5,k.indexOf(this.serialOptions_.parity??w)),t.setUint8(6,this.serialOptions_.dataBits??C),(await this.device_.controlTransferOut({requestType:`class`,recipient:`interface`,request:f,value:0,index:this.controlInterface_.interfaceNumber},e)).status!==`ok`)throw new DOMException(`Failed to set line coding.`,`NetworkError`)}async cp210xInit(){let e=this.controlInterface_.interfaceNumber;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:m,value:v,index:e});let t=new ArrayBuffer(4);new DataView(t).setUint32(0,this.serialOptions_.baudRate,!0),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:h,value:0,index:e},t);let n=this.serialOptions_.dataBits??C,r={none:0,odd:16,even:32}[this.serialOptions_.parity??w]??0,i=({1:0,2:2}[this.serialOptions_.stopBits??T]??0)<<8|r|n;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:g,value:i,index:e}),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:_,value:b,index:e})}async cp210xDeinit(){let e=this.controlInterface_.interfaceNumber;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:_,value:x,index:e}),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:m,value:y,index:e})}validateOptions(){if(this.serialOptions_.baudRate%1!=0)throw RangeError(`Invalid baud rate: ${this.serialOptions_.baudRate}`);if(this.serialOptions_.dataBits!==void 0&&!E.includes(this.serialOptions_.dataBits))throw RangeError(`Invalid dataBits: ${this.serialOptions_.dataBits}`);if(this.serialOptions_.stopBits!==void 0&&!D.includes(this.serialOptions_.stopBits))throw RangeError(`Invalid stopBits: ${this.serialOptions_.stopBits}`);if(this.serialOptions_.parity!==void 0&&!O.includes(this.serialOptions_.parity))throw RangeError(`Invalid parity: ${this.serialOptions_.parity}`)}},z=class{options_;constructor(e){this.options_={...j,...e}}async requestPort(e,t){let n={...this.options_,...t},r=[];if(e?.filters&&e.filters.length>0)for(let t of e.filters){let e={};t.usbVendorId!==void 0&&(e.vendorId=t.usbVendorId),t.usbProductId!==void 0&&(e.productId=t.usbProductId),n.usbControlInterfaceClass!==void 0&&n.usbControlInterfaceClass!==255?e.classCode=n.usbControlInterfaceClass:e.vendorId===void 0&&e.productId===void 0&&(e.classCode=n.usbControlInterfaceClass??2),r.push(e)}else r.push({classCode:n.usbControlInterfaceClass??2});return new R(await navigator.usb.requestDevice({filters:r}),n)}async getPorts(e){let t={...this.options_,...e},n=await navigator.usb.getDevices(),r=[];for(let e of n)try{let n=new R(e,t);r.push(n)}catch{}return r}},B=`6e400001-b5a3-f393-e0a9-e50e24dcca9e`,V=`6e400003-b5a3-f393-e0a9-e50e24dcca9e`,H=`6e400002-b5a3-f393-e0a9-e50e24dcca9e`,U=20,W=10;function G(e){let t=null,n=null,r=null;return{get readable(){return t},get writable(){return n},getInfo(){return{}},async open(){if(!e.gatt)throw Error(`GATT not available on this Bluetooth device.`);r=await e.gatt.connect();let i=await r.getPrimaryService(B),a=await i.getCharacteristic(V),o=await i.getCharacteristic(H);await a.startNotifications(),t=new ReadableStream({start(e){a.addEventListener(`characteristicvaluechanged`,t=>{let n=t.target.value.buffer;e.enqueue(new Uint8Array(n))})}}),n=new WritableStream({async write(e){for(let t=0;t<e.length;t+=U){let n=e.slice(t,t+U);await o.writeValueWithoutResponse(n),t+U<e.length&&await new Promise(e=>setTimeout(e,W))}}})},async close(){r?.connected&&r.disconnect(),t=null,n=null}}}function K(){return{async requestPort(){if(!navigator.bluetooth)throw Error(`Web Bluetooth API is not supported in this browser. Use Chrome on Android, macOS, or ChromeOS.`);return G(await navigator.bluetooth.requestDevice({filters:[{services:[B]}]}))},async getPorts(){return[]}}}function q(e){return new Promise((t,n)=>{e.addEventListener(`open`,()=>t(),{once:!0}),e.addEventListener(`error`,e=>n(e),{once:!0})})}function J(e,t){return new Promise(n=>{let r=i=>{let a=JSON.parse(i.data);a.type===t&&(e.removeEventListener(`message`,r),n(a.payload))};e.addEventListener(`message`,r)})}function Y(e,t){let n=null,r=null;return{get readable(){return n},get writable(){return r},getInfo(){return{usbVendorId:t.vendorId,usbProductId:t.productId}},async open(i){e.send(JSON.stringify({type:`open`,path:t.path,baudRate:i.baudRate,dataBits:i.dataBits,stopBits:i.stopBits,parity:i.parity,parser:{type:`delimiter`,value:`\\n`}})),await J(e,`opened`);let a=[],o=null,s=!1;function c(e){let t=JSON.parse(e.data);if(t.type===`data`&&t.bytes){let e=new Uint8Array(t.bytes);o?o.enqueue(e):a.push(e)}t.type===`closed`&&(s=!0,o&&o.close())}e.addEventListener(`message`,c),n=new ReadableStream({start(e){o=e;for(let t of a)e.enqueue(t);a.length=0,s&&e.close()},cancel(){e.removeEventListener(`message`,c),o=null}}),r=new WritableStream({write(t){e.send(JSON.stringify({type:`write`,bytes:Array.from(t)}))}})},async close(){e.send(JSON.stringify({type:`close`})),n=null,r=null,e.close()}}}function X(e){return{async requestPort(t){let n=new WebSocket(e);await q(n),n.send(JSON.stringify({type:`list-ports`,filters:t?.filters??[]}));let r=(await J(n,`port-list`))[0];if(!r)throw Error(`No ports available on the bridge server. Make sure the Node.js server is running and a device is connected.`);return Y(n,r)},async getPorts(){let t=new WebSocket(e);return await q(t),t.send(JSON.stringify({type:`list-ports`,filters:[]})),(await J(t,`port-list`)).map(e=>Y(t,e))}}}exports.AbstractSerialDevice=c,exports.CommandQueue=n,exports.SerialEventEmitter=e,exports.SerialPermissionError=i,exports.SerialPortConflictError=r,exports.SerialReadError=o,exports.SerialRegistry=t,exports.SerialTimeoutError=a,exports.SerialWriteError=s,exports.WebUsbProvider=z,exports.createBluetoothProvider=K,exports.createWebSocketProvider=X,exports.delimiter=u,exports.fixedLength=l,exports.raw=d;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class{listeners={};on(e,t){return this.listeners[e]||(this.listeners[e]=new Set),this.listeners[e].add(t),this}off(e,t){return this.listeners[e]&&this.listeners[e].delete(t),this}emit(e,...t){let n=this.listeners[e];if(!n||n.size===0)return!1;for(let e of n)e(...t);return!0}},t=class{static instances=new Set;static portInstanceMap=new WeakMap;static getInstances(){return Array.from(this.instances)}static register(e){this.instances.add(e)}static unregister(e){this.instances.delete(e)}static isPortInUse(e,t){let n=this.portInstanceMap.get(e);return n!==void 0&&n!==t}static lockPort(e,t){this.portInstanceMap.set(e,t)}static unlockPort(e){this.portInstanceMap.delete(e)}},n=class{queue=[];isProcessing=!1;isPaused=!0;timeoutId=null;commandTimeout;onSend;onTimeout;constructor(e){this.commandTimeout=e.commandTimeout,this.onSend=e.onSend,this.onTimeout=e.onTimeout}get queueSize(){return this.queue.length}enqueue(e){this.queue.push(e),this.tryProcessNext()}advance(){this.clearCommandTimeout(),this.isProcessing=!1,this.tryProcessNext()}pause(){this.isPaused=!0,this.clearCommandTimeout(),this.isProcessing=!1}resume(){this.isPaused=!1,this.tryProcessNext()}clear(){this.queue=[],this.clearCommandTimeout(),this.isProcessing=!1}snapshot(){return[...this.queue]}restore(e){this.queue=[...e,...this.queue]}tryProcessNext(){if(this.isPaused||this.isProcessing||this.queue.length===0)return;this.isProcessing=!0;let e=this.queue.shift();this.commandTimeout>0&&(this.timeoutId=setTimeout(()=>{this.timeoutId=null,this.onTimeout(e),this.advance()},this.commandTimeout)),this.onSend(e).catch(()=>{this.advance()})}clearCommandTimeout(){this.timeoutId!==null&&(clearTimeout(this.timeoutId),this.timeoutId=null)}},r=class e extends Error{constructor(t){super(t),this.name=`SerialPortConflictError`,Object.setPrototypeOf(this,e.prototype)}},i=class e extends Error{constructor(t){super(t),this.name=`SerialPermissionError`,Object.setPrototypeOf(this,e.prototype)}},a=class e extends Error{constructor(t){super(t),this.name=`SerialTimeoutError`,Object.setPrototypeOf(this,e.prototype)}},o=class e extends Error{constructor(t){super(t),this.name=`SerialReadError`,Object.setPrototypeOf(this,e.prototype)}},s=class e extends Error{constructor(t){super(t),this.name=`SerialWriteError`,Object.setPrototypeOf(this,e.prototype)}},c=class r extends e{port=null;reader=null;writer=null;queue;options;isConnecting=!1;abortController=null;userInitiatedDisconnect=!1;reconnectTimerId=null;isHandshaking=!1;static customProvider=null;static polyfillOptions;constructor(e){super(),this.options={baudRate:e.baudRate,dataBits:e.dataBits??8,stopBits:e.stopBits??1,parity:e.parity??`none`,bufferSize:e.bufferSize??255,flowControl:e.flowControl??`none`,filters:e.filters??[],commandTimeout:e.commandTimeout??0,parser:e.parser,autoReconnect:e.autoReconnect??!1,autoReconnectInterval:e.autoReconnectInterval??1500,handshakeTimeout:e.handshakeTimeout??2e3,provider:e.provider,polyfillOptions:e.polyfillOptions},this.queue=new n({commandTimeout:this.options.commandTimeout,onSend:async e=>{await this.writeToPort(e),this.emit(`serial:sent`,e,this)},onTimeout:e=>{this.emit(`serial:timeout`,e,this)}}),this.on(`serial:data`,()=>{this.queue.advance()}),t.register(this)}async handshake(){return!0}async connect(){if(!this.isConnecting&&!this.port){this.isConnecting=!0,this.emit(`serial:connecting`,this);try{let e=this.getSerial();if(!e)throw Error(`Web Serial API is not supported in this browser. Use AbstractSerialDevice.setProvider() to set a WebUSB polyfill.`);if(this.port=await this.findAndValidatePort(),!this.port){let t;try{t=await e.requestPort({filters:this.options.filters},this.options.polyfillOptions??r.polyfillOptions)}catch(e){throw e instanceof DOMException&&(e.name===`NotFoundError`||e.name===`SecurityError`||e.name===`AbortError`)?new i(e instanceof Error?e.message:String(e)):e instanceof Error?e:Error(String(e))}if(!await this.openAndHandshake(t))throw Error(`Handshake failed: the selected device did not respond correctly.`);this.port=t}this.abortController=new AbortController,this.queue.resume(),this.emit(`serial:connected`,this)}catch(e){if(e instanceof i?this.emit(`serial:need-permission`,this):this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this),this.port){t.unlockPort(this.port);try{await this.port.close()}catch{}this.port=null}throw e}finally{this.isConnecting=!1}}}async disconnect(){this.port&&(this.userInitiatedDisconnect=!0,this.stopReconnecting(),await this.cleanupPort())}isConnected(){return!!(this.port&&this.port.connected&&this.port.readable&&this.port.writable)}isDisconnected(){return!this.isConnected()}async cleanupPort(){if(this.port){this.queue.pause(),this.abortController?.abort(),this.abortController=null;try{let e=this.reader,t=this.writer;if(this.reader=null,this.writer=null,e){try{await e.cancel()}catch{}try{e.releaseLock()}catch{}}if(t){try{await t.close()}catch{}try{t.releaseLock()}catch{}}try{await this.port.close()}catch{}}catch(e){this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this)}finally{this.port&&t.unlockPort(this.port),this.port=null,this.options.parser?.reset?.(),this.emit(`serial:disconnected`,this),!this.userInitiatedDisconnect&&this.options.autoReconnect&&this.startReconnecting(),this.userInitiatedDisconnect=!1}}}async forget(){await this.disconnect(),this.port&&typeof this.port.forget==`function`&&await this.port.forget(),t.unregister(this)}async send(e){let t;t=typeof e==`string`?new TextEncoder().encode(e):e,t.length>0&&this.queue.enqueue(t)}clearQueue(){this.queue.clear(),this.emit(`serial:queue-empty`,this)}async writeToPort(e){if(!this.port||!this.port.writable)throw new s(`Port not writable.`);this.writer=this.port.writable.getWriter();try{await this.writer.write(e)}catch(e){throw new s(e instanceof Error?e.message:String(e))}finally{this.writer.releaseLock(),this.writer=null}}async readLoop(){if(!(!this.port||!this.port.readable)&&!this.reader){this.reader=this.port.readable.getReader();try{for(;;){let{value:e,done:t}=await this.reader.read();if(t)break;e&&(this.options.parser?this.options.parser.parse(e,e=>{this.emit(`serial:data`,e,this)}):this.emit(`serial:data`,e,this))}}catch(e){if(this.port)throw new o(e instanceof Error?e.message:String(e))}finally{if(this.reader){try{this.reader.releaseLock()}catch{}this.reader=null}}}}async openAndHandshake(e){let n=this;if(t.isPortInUse(e,n))return!1;t.lockPort(e,n);try{await e.open({baudRate:this.options.baudRate,dataBits:this.options.dataBits,stopBits:this.options.stopBits,parity:this.options.parity,bufferSize:this.options.bufferSize,flowControl:this.options.flowControl})}catch(n){throw t.unlockPort(e),n instanceof Error?n:Error(String(n))}this.port=e,this.abortController=new AbortController;let r=this.queue.snapshot();this.isHandshaking=!0,this.readLoop().catch(e=>{!this.isHandshaking&&this.port&&(this.emit(`serial:error`,e,this),this.cleanupPort())}),this.queue.resume();try{let t=await this.runHandshakeWithTimeout();return this.isHandshaking=!1,t?(this.queue.pause(),this.queue.clear(),this.queue.restore(r),this.options.parser?.reset?.(),!0):(await this.teardownHandshake(e,r),!1)}catch{return this.isHandshaking=!1,await this.teardownHandshake(e,r),!1}}async teardownHandshake(e,n){this.queue.pause(),this.queue.clear(),this.queue.restore(n),await this.stopReader(),this.port=null,this.abortController=null,this.options.parser?.reset?.();try{await e.close()}catch{}t.unlockPort(e)}async stopReader(){let e=this.reader;if(this.reader=null,e){try{await e.cancel()}catch{}try{e.releaseLock()}catch{}}}async runHandshakeWithTimeout(){let e=this.options.handshakeTimeout??2e3;return Promise.race([this.handshake(),new Promise(t=>setTimeout(()=>t(!1),e))])}async findAndValidatePort(){let e=this.getSerial();if(!e)return null;let n=await e.getPorts(this.options.polyfillOptions??r.polyfillOptions);if(n.length===0)return null;let i=this.options.filters??[],a=this;for(let e of n)if(!t.isPortInUse(e,a)){if(i.length>0){let t=e.getInfo();if(!i.some(e=>{let n=e.usbVendorId===void 0||e.usbVendorId===t.usbVendorId,r=e.usbProductId===void 0||e.usbProductId===t.usbProductId;return n&&r}))continue}try{if(await this.openAndHandshake(e))return e}catch{}}return null}startReconnecting(){this.reconnectTimerId||=(this.emit(`serial:reconnecting`,this),setInterval(async()=>{if(this.port||this.isConnecting){this.stopReconnecting();return}try{let e=await this.findAndValidatePort();e&&(this.stopReconnecting(),await this.reconnect(e))}catch{}},this.options.autoReconnectInterval))}stopReconnecting(){this.reconnectTimerId&&=(clearInterval(this.reconnectTimerId),null)}async reconnect(e){if(!(this.isConnecting||this.port)){this.isConnecting=!0,this.emit(`serial:connecting`,this);try{this.port=e,this.abortController=new AbortController,this.queue.resume(),this.emit(`serial:connected`,this)}catch(e){this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this),this.port&&=(t.unlockPort(this.port),null),this.options.autoReconnect&&this.startReconnecting()}finally{this.isConnecting=!1}}}static getInstances(){return t.getInstances()}static async connectAll(){let e=t.getInstances();for(let t of e)try{await t.connect()}catch{}}static setProvider(e,t){r.customProvider=e,r.polyfillOptions=t}getSerial(){return this.options.provider?this.options.provider:r.customProvider?r.customProvider:typeof navigator<`u`&&navigator.serial?navigator.serial:null}};function l(e){if(e<=0)throw Error(`FixedLengthParser: length must be greater than 0`);let t=new Uint8Array;return{parse(n,r){let i=new Uint8Array(t.length+n.length);for(i.set(t),i.set(n,t.length),t=i;t.length>=e;)r(t.slice(0,e)),t=t.slice(e)},reset(){t=new Uint8Array}}}function u(e){let t=``,n=new TextDecoder;return{parse(r,i){t+=n.decode(r,{stream:!0});let a;for(;(a=t.indexOf(e))!==-1;)i(t.slice(0,a)),t=t.slice(a+e.length)},reset(){t=``,n=new TextDecoder}}}function d(){return{parse(e,t){t(e)},reset(){}}}var f=32,p=34,m=0,h=30,g=3,_=7,v=1,y=0,b=771,x=768,S=255,C=8,w=`none`,T=1,E=[16,8,7,6,5],D=[1,2],O=[`none`,`even`,`odd`],k=[`none`,`odd`,`even`],A=[1,1.5,2],j={usbControlInterfaceClass:2,usbTransferInterfaceClass:10,protocol:void 0};function M(e,t){let n=e.configurations[0];if(!n)return null;for(let e of n.interfaces)if(e.alternates[0]?.interfaceClass===t)return e;return null}function N(e,t){let n=e.configurations[0];if(!n)return null;for(let e of n.interfaces){let n=e.alternates[0];if(!n||n.interfaceClass!==t)continue;let r=n.endpoints.some(e=>e.direction===`in`),i=n.endpoints.some(e=>e.direction===`out`);if(r&&i)return e}return null}function P(e,t){let n=e.alternates[0];if(n){for(let e of n.endpoints)if(e.direction===t)return e}throw TypeError(`Interface ${e.interfaceNumber} does not have an ${t} endpoint.`)}function F(e,t){return t===2?`cdc_acm`:e.vendorId===4292?`cp210x`:`none`}var I=class{device_;endpoint_;onError_;constructor(e,t,n){this.device_=e,this.endpoint_=t,this.onError_=n}pull(e){(async()=>{let t=this.endpoint_.packetSize;try{let n=await this.device_.transferIn(this.endpoint_.endpointNumber,t);if(n.status!==`ok`){e.error(`USB error: ${n.status}`),this.onError_();return}if(n.data?.buffer&&n.data.byteLength>0){let t=new Uint8Array(n.data.buffer,n.data.byteOffset,n.data.byteLength);t.length>0&&e.enqueue(t)}}catch(t){e.error(String(t)),this.onError_()}})()}},L=class{device_;endpoint_;onError_;constructor(e,t,n){this.device_=e,this.endpoint_=t,this.onError_=n}async write(e,t){try{let n=await this.device_.transferOut(this.endpoint_.endpointNumber,e.buffer);n.status!==`ok`&&(t.error(n.status),this.onError_())}catch(e){t.error(String(e)),this.onError_()}}},R=class{device_;protocol_;controlInterface_;transferInterface_;inEndpoint_;outEndpoint_;serialOptions_;readable_=null;writable_=null;cdcOutputSignals_={dataTerminalReady:!1,requestToSend:!1,break:!1};constructor(e,t){this.device_=e;let n={...j,...t};this.protocol_=n.protocol??F(e,n.usbControlInterfaceClass);let r=n.usbControlInterfaceClass,i=n.usbTransferInterfaceClass;if(r===i){let t=N(e,i);if(!t)throw TypeError(`Unable to find interface with class ${i} that has both IN and OUT endpoints.`);this.controlInterface_=t,this.transferInterface_=t}else{let t=M(e,r);if(!t)throw TypeError(`Unable to find control interface with class ${r}.`);let n=N(e,i)??M(e,i);if(!n)throw TypeError(`Unable to find transfer interface with class ${i}.`);this.controlInterface_=t,this.transferInterface_=n}this.inEndpoint_=P(this.transferInterface_,`in`),this.outEndpoint_=P(this.transferInterface_,`out`)}get readable(){return!this.readable_&&this.device_.opened&&(this.readable_=new ReadableStream(new I(this.device_,this.inEndpoint_,()=>{this.readable_=null}),{highWaterMark:this.serialOptions_?.bufferSize??S})),this.readable_}get writable(){return!this.writable_&&this.device_.opened&&(this.writable_=new WritableStream(new L(this.device_,this.outEndpoint_,()=>{this.writable_=null}),new ByteLengthQueuingStrategy({highWaterMark:this.serialOptions_?.bufferSize??S}))),this.writable_}async open(e){this.serialOptions_=e,this.validateOptions();try{switch(await this.device_.open(),this.device_.configuration===null&&await this.device_.selectConfiguration(1),await this.device_.claimInterface(this.controlInterface_.interfaceNumber),this.controlInterface_!==this.transferInterface_&&await this.device_.claimInterface(this.transferInterface_.interfaceNumber),this.protocol_){case`cdc_acm`:await this.cdcInit();break;case`cp210x`:await this.cp210xInit();break;case`none`:break}}catch(e){throw this.device_.opened&&await this.device_.close(),Error(`Error setting up device: `+(e instanceof Error?e.message:String(e)),{cause:e})}}async close(){let e=[];if(this.readable_&&e.push(this.readable_.cancel()),this.writable_&&e.push(this.writable_.abort()),await Promise.all(e),this.readable_=null,this.writable_=null,this.device_.opened){switch(this.protocol_){case`cdc_acm`:await this.cdcSetSignals({dataTerminalReady:!1,requestToSend:!1});break;case`cp210x`:await this.cp210xDeinit();break}await this.device_.close()}}async forget(){return this.device_.forget()}getInfo(){return{usbVendorId:this.device_.vendorId,usbProductId:this.device_.productId}}async cdcInit(){await this.cdcSetLineCoding(),await this.cdcSetSignals({dataTerminalReady:!0})}async cdcSetSignals(e){if(this.cdcOutputSignals_={...this.cdcOutputSignals_,...e},e.dataTerminalReady!==void 0||e.requestToSend!==void 0){let e=!!this.cdcOutputSignals_.dataTerminalReady|(this.cdcOutputSignals_.requestToSend?2:0);await this.device_.controlTransferOut({requestType:`class`,recipient:`interface`,request:p,value:e,index:this.controlInterface_.interfaceNumber})}}async cdcSetLineCoding(){let e=new ArrayBuffer(7),t=new DataView(e);if(t.setUint32(0,this.serialOptions_.baudRate,!0),t.setUint8(4,A.indexOf(this.serialOptions_.stopBits??T)),t.setUint8(5,k.indexOf(this.serialOptions_.parity??w)),t.setUint8(6,this.serialOptions_.dataBits??C),(await this.device_.controlTransferOut({requestType:`class`,recipient:`interface`,request:f,value:0,index:this.controlInterface_.interfaceNumber},e)).status!==`ok`)throw new DOMException(`Failed to set line coding.`,`NetworkError`)}async cp210xInit(){let e=this.controlInterface_.interfaceNumber;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:m,value:v,index:e});let t=new ArrayBuffer(4);new DataView(t).setUint32(0,this.serialOptions_.baudRate,!0),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:h,value:0,index:e},t);let n=this.serialOptions_.dataBits??C,r={none:0,odd:16,even:32}[this.serialOptions_.parity??w]??0,i=({1:0,2:2}[this.serialOptions_.stopBits??T]??0)<<8|r|n;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:g,value:i,index:e}),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:_,value:b,index:e})}async cp210xDeinit(){let e=this.controlInterface_.interfaceNumber;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:_,value:x,index:e}),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:m,value:y,index:e})}validateOptions(){if(this.serialOptions_.baudRate%1!=0)throw RangeError(`Invalid baud rate: ${this.serialOptions_.baudRate}`);if(this.serialOptions_.dataBits!==void 0&&!E.includes(this.serialOptions_.dataBits))throw RangeError(`Invalid dataBits: ${this.serialOptions_.dataBits}`);if(this.serialOptions_.stopBits!==void 0&&!D.includes(this.serialOptions_.stopBits))throw RangeError(`Invalid stopBits: ${this.serialOptions_.stopBits}`);if(this.serialOptions_.parity!==void 0&&!O.includes(this.serialOptions_.parity))throw RangeError(`Invalid parity: ${this.serialOptions_.parity}`)}},z=class{options_;constructor(e){this.options_={...j,...e}}async requestPort(e,t){let n={...this.options_,...t},r=[];if(e?.filters&&e.filters.length>0)for(let t of e.filters){let e={};t.usbVendorId!==void 0&&(e.vendorId=t.usbVendorId),t.usbProductId!==void 0&&(e.productId=t.usbProductId),n.usbControlInterfaceClass!==void 0&&n.usbControlInterfaceClass!==255?e.classCode=n.usbControlInterfaceClass:e.vendorId===void 0&&e.productId===void 0&&(e.classCode=n.usbControlInterfaceClass??2),r.push(e)}else r.push({classCode:n.usbControlInterfaceClass??2});return new R(await navigator.usb.requestDevice({filters:r}),n)}async getPorts(e){let t={...this.options_,...e},n=await navigator.usb.getDevices(),r=[];for(let e of n)try{let n=new R(e,t);r.push(n)}catch{}return r}},B=`6e400001-b5a3-f393-e0a9-e50e24dcca9e`,V=`6e400003-b5a3-f393-e0a9-e50e24dcca9e`,H=`6e400002-b5a3-f393-e0a9-e50e24dcca9e`,U=20,W=10;function G(e){let t=null,n=null,r=null;return{get readable(){return t},get writable(){return n},getInfo(){return{}},async open(){if(!e.gatt)throw Error(`GATT not available on this Bluetooth device.`);r=await e.gatt.connect();let i=await r.getPrimaryService(B),a=await i.getCharacteristic(V),o=await i.getCharacteristic(H);await a.startNotifications(),t=new ReadableStream({start(e){a.addEventListener(`characteristicvaluechanged`,t=>{let n=t.target.value.buffer;e.enqueue(new Uint8Array(n))})}}),n=new WritableStream({async write(e){for(let t=0;t<e.length;t+=U){let n=e.slice(t,t+U);await o.writeValueWithoutResponse(n),t+U<e.length&&await new Promise(e=>setTimeout(e,W))}}})},async close(){r?.connected&&r.disconnect(),t=null,n=null}}}function K(){return{async requestPort(){if(!navigator.bluetooth)throw Error(`Web Bluetooth API is not supported in this browser. Use Chrome on Android, macOS, or ChromeOS.`);return G(await navigator.bluetooth.requestDevice({filters:[{services:[B]}]}))},async getPorts(){return[]}}}function q(e){return new Promise((t,n)=>{e.addEventListener(`open`,()=>t(),{once:!0}),e.addEventListener(`error`,e=>n(e),{once:!0})})}function J(e,t){return new Promise(n=>{let r=i=>{let a=JSON.parse(i.data);a.type===t&&(e.removeEventListener(`message`,r),n(a.payload))};e.addEventListener(`message`,r)})}function Y(e,t){let n=null,r=null;return{get readable(){return n},get writable(){return r},getInfo(){return{usbVendorId:t.vendorId,usbProductId:t.productId}},async open(i){e.send(JSON.stringify({type:`open`,path:t.path,baudRate:i.baudRate,dataBits:i.dataBits,stopBits:i.stopBits,parity:i.parity,parser:{type:`delimiter`,value:`\\n`}})),await J(e,`opened`);let a=[],o=null,s=!1;function c(e){let t=JSON.parse(e.data);if(t.type===`data`&&t.bytes){let e=new Uint8Array(t.bytes);o?o.enqueue(e):a.push(e)}t.type===`closed`&&(s=!0,o&&o.close())}e.addEventListener(`message`,c),n=new ReadableStream({start(e){o=e;for(let t of a)e.enqueue(t);a.length=0,s&&e.close()},cancel(){e.removeEventListener(`message`,c),o=null}}),r=new WritableStream({write(t){e.send(JSON.stringify({type:`write`,bytes:Array.from(t)}))}})},async close(){e.send(JSON.stringify({type:`close`})),n=null,r=null,e.close()}}}function X(e){return{async requestPort(t){let n=new WebSocket(e);await q(n),n.send(JSON.stringify({type:`list-ports`,filters:t?.filters??[]}));let r=(await J(n,`port-list`))[0];if(!r)throw Error(`No ports available on the bridge server. Make sure the Node.js server is running and a device is connected.`);return Y(n,r)},async getPorts(){let t=new WebSocket(e);return await q(t),t.send(JSON.stringify({type:`list-ports`,filters:[]})),(await J(t,`port-list`)).map(e=>Y(t,e))}}}exports.AbstractSerialDevice=c,exports.CommandQueue=n,exports.SerialEventEmitter=e,exports.SerialPermissionError=i,exports.SerialPortConflictError=r,exports.SerialReadError=o,exports.SerialRegistry=t,exports.SerialTimeoutError=a,exports.SerialWriteError=s,exports.WebUsbProvider=z,exports.createBluetoothProvider=K,exports.createWebSocketProvider=X,exports.delimiter=u,exports.fixedLength=l,exports.raw=d;
|
package/dist/webserial-core.mjs
CHANGED
|
@@ -181,6 +181,12 @@ var e = class {
|
|
|
181
181
|
async disconnect() {
|
|
182
182
|
this.port && (this.userInitiatedDisconnect = !0, this.stopReconnecting(), await this.cleanupPort());
|
|
183
183
|
}
|
|
184
|
+
isConnected() {
|
|
185
|
+
return !!(this.port && this.port.connected && this.port.readable && this.port.writable);
|
|
186
|
+
}
|
|
187
|
+
isDisconnected() {
|
|
188
|
+
return !this.isConnected();
|
|
189
|
+
}
|
|
184
190
|
async cleanupPort() {
|
|
185
191
|
if (this.port) {
|
|
186
192
|
this.queue.pause(), this.abortController?.abort(), this.abortController = null;
|
|
@@ -597,7 +603,7 @@ var I = class {
|
|
|
597
603
|
...this.cdcOutputSignals_,
|
|
598
604
|
...e
|
|
599
605
|
}, e.dataTerminalReady !== void 0 || e.requestToSend !== void 0) {
|
|
600
|
-
let e =
|
|
606
|
+
let e = !!this.cdcOutputSignals_.dataTerminalReady | (this.cdcOutputSignals_.requestToSend ? 2 : 0);
|
|
601
607
|
await this.device_.controlTransferOut({
|
|
602
608
|
requestType: "class",
|
|
603
609
|
recipient: "interface",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports):typeof define==`function`&&define.amd?define([`exports`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.WebSerialCore={}))})(this,function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=class{listeners={};on(e,t){return this.listeners[e]||(this.listeners[e]=new Set),this.listeners[e].add(t),this}off(e,t){return this.listeners[e]&&this.listeners[e].delete(t),this}emit(e,...t){let n=this.listeners[e];if(!n||n.size===0)return!1;for(let e of n)e(...t);return!0}},n=class{static instances=new Set;static portInstanceMap=new WeakMap;static getInstances(){return Array.from(this.instances)}static register(e){this.instances.add(e)}static unregister(e){this.instances.delete(e)}static isPortInUse(e,t){let n=this.portInstanceMap.get(e);return n!==void 0&&n!==t}static lockPort(e,t){this.portInstanceMap.set(e,t)}static unlockPort(e){this.portInstanceMap.delete(e)}},r=class{queue=[];isProcessing=!1;isPaused=!0;timeoutId=null;commandTimeout;onSend;onTimeout;constructor(e){this.commandTimeout=e.commandTimeout,this.onSend=e.onSend,this.onTimeout=e.onTimeout}get queueSize(){return this.queue.length}enqueue(e){this.queue.push(e),this.tryProcessNext()}advance(){this.clearCommandTimeout(),this.isProcessing=!1,this.tryProcessNext()}pause(){this.isPaused=!0,this.clearCommandTimeout(),this.isProcessing=!1}resume(){this.isPaused=!1,this.tryProcessNext()}clear(){this.queue=[],this.clearCommandTimeout(),this.isProcessing=!1}snapshot(){return[...this.queue]}restore(e){this.queue=[...e,...this.queue]}tryProcessNext(){if(this.isPaused||this.isProcessing||this.queue.length===0)return;this.isProcessing=!0;let e=this.queue.shift();this.commandTimeout>0&&(this.timeoutId=setTimeout(()=>{this.timeoutId=null,this.onTimeout(e),this.advance()},this.commandTimeout)),this.onSend(e).catch(()=>{this.advance()})}clearCommandTimeout(){this.timeoutId!==null&&(clearTimeout(this.timeoutId),this.timeoutId=null)}},i=class e extends Error{constructor(t){super(t),this.name=`SerialPortConflictError`,Object.setPrototypeOf(this,e.prototype)}},a=class e extends Error{constructor(t){super(t),this.name=`SerialPermissionError`,Object.setPrototypeOf(this,e.prototype)}},o=class e extends Error{constructor(t){super(t),this.name=`SerialTimeoutError`,Object.setPrototypeOf(this,e.prototype)}},s=class e extends Error{constructor(t){super(t),this.name=`SerialReadError`,Object.setPrototypeOf(this,e.prototype)}},c=class e extends Error{constructor(t){super(t),this.name=`SerialWriteError`,Object.setPrototypeOf(this,e.prototype)}},l=class e extends t{port=null;reader=null;writer=null;queue;options;isConnecting=!1;abortController=null;userInitiatedDisconnect=!1;reconnectTimerId=null;isHandshaking=!1;static customProvider=null;static polyfillOptions;constructor(e){super(),this.options={baudRate:e.baudRate,dataBits:e.dataBits??8,stopBits:e.stopBits??1,parity:e.parity??`none`,bufferSize:e.bufferSize??255,flowControl:e.flowControl??`none`,filters:e.filters??[],commandTimeout:e.commandTimeout??0,parser:e.parser,autoReconnect:e.autoReconnect??!1,autoReconnectInterval:e.autoReconnectInterval??1500,handshakeTimeout:e.handshakeTimeout??2e3,provider:e.provider,polyfillOptions:e.polyfillOptions},this.queue=new r({commandTimeout:this.options.commandTimeout,onSend:async e=>{await this.writeToPort(e),this.emit(`serial:sent`,e,this)},onTimeout:e=>{this.emit(`serial:timeout`,e,this)}}),this.on(`serial:data`,()=>{this.queue.advance()}),n.register(this)}async handshake(){return!0}async connect(){if(!this.isConnecting&&!this.port){this.isConnecting=!0,this.emit(`serial:connecting`,this);try{let t=this.getSerial();if(!t)throw Error(`Web Serial API is not supported in this browser. Use AbstractSerialDevice.setProvider() to set a WebUSB polyfill.`);if(this.port=await this.findAndValidatePort(),!this.port){let n;try{n=await t.requestPort({filters:this.options.filters},this.options.polyfillOptions??e.polyfillOptions)}catch(e){throw e instanceof DOMException&&(e.name===`NotFoundError`||e.name===`SecurityError`||e.name===`AbortError`)?new a(e instanceof Error?e.message:String(e)):e instanceof Error?e:Error(String(e))}if(!await this.openAndHandshake(n))throw Error(`Handshake failed: the selected device did not respond correctly.`);this.port=n}this.abortController=new AbortController,this.queue.resume(),this.emit(`serial:connected`,this)}catch(e){if(e instanceof a?this.emit(`serial:need-permission`,this):this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this),this.port){n.unlockPort(this.port);try{await this.port.close()}catch{}this.port=null}throw e}finally{this.isConnecting=!1}}}async disconnect(){this.port&&(this.userInitiatedDisconnect=!0,this.stopReconnecting(),await this.cleanupPort())}async cleanupPort(){if(this.port){this.queue.pause(),this.abortController?.abort(),this.abortController=null;try{let e=this.reader,t=this.writer;if(this.reader=null,this.writer=null,e){try{await e.cancel()}catch{}try{e.releaseLock()}catch{}}if(t){try{await t.close()}catch{}try{t.releaseLock()}catch{}}try{await this.port.close()}catch{}}catch(e){this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this)}finally{this.port&&n.unlockPort(this.port),this.port=null,this.options.parser?.reset?.(),this.emit(`serial:disconnected`,this),!this.userInitiatedDisconnect&&this.options.autoReconnect&&this.startReconnecting(),this.userInitiatedDisconnect=!1}}}async forget(){await this.disconnect(),this.port&&typeof this.port.forget==`function`&&await this.port.forget(),n.unregister(this)}async send(e){let t;t=typeof e==`string`?new TextEncoder().encode(e):e,t.length>0&&this.queue.enqueue(t)}clearQueue(){this.queue.clear(),this.emit(`serial:queue-empty`,this)}async writeToPort(e){if(!this.port||!this.port.writable)throw new c(`Port not writable.`);this.writer=this.port.writable.getWriter();try{await this.writer.write(e)}catch(e){throw new c(e instanceof Error?e.message:String(e))}finally{this.writer.releaseLock(),this.writer=null}}async readLoop(){if(!(!this.port||!this.port.readable)&&!this.reader){this.reader=this.port.readable.getReader();try{for(;;){let{value:e,done:t}=await this.reader.read();if(t)break;e&&(this.options.parser?this.options.parser.parse(e,e=>{this.emit(`serial:data`,e,this)}):this.emit(`serial:data`,e,this))}}catch(e){if(this.port)throw new s(e instanceof Error?e.message:String(e))}finally{if(this.reader){try{this.reader.releaseLock()}catch{}this.reader=null}}}}async openAndHandshake(e){let t=this;if(n.isPortInUse(e,t))return!1;n.lockPort(e,t);try{await e.open({baudRate:this.options.baudRate,dataBits:this.options.dataBits,stopBits:this.options.stopBits,parity:this.options.parity,bufferSize:this.options.bufferSize,flowControl:this.options.flowControl})}catch(t){throw n.unlockPort(e),t instanceof Error?t:Error(String(t))}this.port=e,this.abortController=new AbortController;let r=this.queue.snapshot();this.isHandshaking=!0,this.readLoop().catch(e=>{!this.isHandshaking&&this.port&&(this.emit(`serial:error`,e,this),this.cleanupPort())}),this.queue.resume();try{let t=await this.runHandshakeWithTimeout();return this.isHandshaking=!1,t?(this.queue.pause(),this.queue.clear(),this.queue.restore(r),this.options.parser?.reset?.(),!0):(await this.teardownHandshake(e,r),!1)}catch{return this.isHandshaking=!1,await this.teardownHandshake(e,r),!1}}async teardownHandshake(e,t){this.queue.pause(),this.queue.clear(),this.queue.restore(t),await this.stopReader(),this.port=null,this.abortController=null,this.options.parser?.reset?.();try{await e.close()}catch{}n.unlockPort(e)}async stopReader(){let e=this.reader;if(this.reader=null,e){try{await e.cancel()}catch{}try{e.releaseLock()}catch{}}}async runHandshakeWithTimeout(){let e=this.options.handshakeTimeout??2e3;return Promise.race([this.handshake(),new Promise(t=>setTimeout(()=>t(!1),e))])}async findAndValidatePort(){let t=this.getSerial();if(!t)return null;let r=await t.getPorts(this.options.polyfillOptions??e.polyfillOptions);if(r.length===0)return null;let i=this.options.filters??[],a=this;for(let e of r)if(!n.isPortInUse(e,a)){if(i.length>0){let t=e.getInfo();if(!i.some(e=>{let n=e.usbVendorId===void 0||e.usbVendorId===t.usbVendorId,r=e.usbProductId===void 0||e.usbProductId===t.usbProductId;return n&&r}))continue}try{if(await this.openAndHandshake(e))return e}catch{}}return null}startReconnecting(){this.reconnectTimerId||=(this.emit(`serial:reconnecting`,this),setInterval(async()=>{if(this.port||this.isConnecting){this.stopReconnecting();return}try{let e=await this.findAndValidatePort();e&&(this.stopReconnecting(),await this.reconnect(e))}catch{}},this.options.autoReconnectInterval))}stopReconnecting(){this.reconnectTimerId&&=(clearInterval(this.reconnectTimerId),null)}async reconnect(e){if(!(this.isConnecting||this.port)){this.isConnecting=!0,this.emit(`serial:connecting`,this);try{this.port=e,this.abortController=new AbortController,this.queue.resume(),this.emit(`serial:connected`,this)}catch(e){this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this),this.port&&=(n.unlockPort(this.port),null),this.options.autoReconnect&&this.startReconnecting()}finally{this.isConnecting=!1}}}static getInstances(){return n.getInstances()}static async connectAll(){let e=n.getInstances();for(let t of e)try{await t.connect()}catch{}}static setProvider(t,n){e.customProvider=t,e.polyfillOptions=n}getSerial(){return this.options.provider?this.options.provider:e.customProvider?e.customProvider:typeof navigator<`u`&&navigator.serial?navigator.serial:null}};function u(e){if(e<=0)throw Error(`FixedLengthParser: length must be greater than 0`);let t=new Uint8Array;return{parse(n,r){let i=new Uint8Array(t.length+n.length);for(i.set(t),i.set(n,t.length),t=i;t.length>=e;)r(t.slice(0,e)),t=t.slice(e)},reset(){t=new Uint8Array}}}function d(e){let t=``,n=new TextDecoder;return{parse(r,i){t+=n.decode(r,{stream:!0});let a;for(;(a=t.indexOf(e))!==-1;)i(t.slice(0,a)),t=t.slice(a+e.length)},reset(){t=``,n=new TextDecoder}}}function f(){return{parse(e,t){t(e)},reset(){}}}var p=32,m=34,h=0,g=30,_=3,v=7,y=1,b=0,x=771,S=768,C=255,w=8,T=`none`,E=1,D=[16,8,7,6,5],O=[1,2],k=[`none`,`even`,`odd`],A=[`none`,`odd`,`even`],j=[1,1.5,2],M={usbControlInterfaceClass:2,usbTransferInterfaceClass:10,protocol:void 0};function N(e,t){let n=e.configurations[0];if(!n)return null;for(let e of n.interfaces)if(e.alternates[0]?.interfaceClass===t)return e;return null}function P(e,t){let n=e.configurations[0];if(!n)return null;for(let e of n.interfaces){let n=e.alternates[0];if(!n||n.interfaceClass!==t)continue;let r=n.endpoints.some(e=>e.direction===`in`),i=n.endpoints.some(e=>e.direction===`out`);if(r&&i)return e}return null}function F(e,t){let n=e.alternates[0];if(n){for(let e of n.endpoints)if(e.direction===t)return e}throw TypeError(`Interface ${e.interfaceNumber} does not have an ${t} endpoint.`)}function I(e,t){return t===2?`cdc_acm`:e.vendorId===4292?`cp210x`:`none`}var L=class{device_;endpoint_;onError_;constructor(e,t,n){this.device_=e,this.endpoint_=t,this.onError_=n}pull(e){(async()=>{let t=this.endpoint_.packetSize;try{let n=await this.device_.transferIn(this.endpoint_.endpointNumber,t);if(n.status!==`ok`){e.error(`USB error: ${n.status}`),this.onError_();return}if(n.data?.buffer&&n.data.byteLength>0){let t=new Uint8Array(n.data.buffer,n.data.byteOffset,n.data.byteLength);t.length>0&&e.enqueue(t)}}catch(t){e.error(String(t)),this.onError_()}})()}},R=class{device_;endpoint_;onError_;constructor(e,t,n){this.device_=e,this.endpoint_=t,this.onError_=n}async write(e,t){try{let n=await this.device_.transferOut(this.endpoint_.endpointNumber,e.buffer);n.status!==`ok`&&(t.error(n.status),this.onError_())}catch(e){t.error(String(e)),this.onError_()}}},z=class{device_;protocol_;controlInterface_;transferInterface_;inEndpoint_;outEndpoint_;serialOptions_;readable_=null;writable_=null;cdcOutputSignals_={dataTerminalReady:!1,requestToSend:!1,break:!1};constructor(e,t){this.device_=e;let n={...M,...t};this.protocol_=n.protocol??I(e,n.usbControlInterfaceClass);let r=n.usbControlInterfaceClass,i=n.usbTransferInterfaceClass;if(r===i){let t=P(e,i);if(!t)throw TypeError(`Unable to find interface with class ${i} that has both IN and OUT endpoints.`);this.controlInterface_=t,this.transferInterface_=t}else{let t=N(e,r);if(!t)throw TypeError(`Unable to find control interface with class ${r}.`);let n=P(e,i)??N(e,i);if(!n)throw TypeError(`Unable to find transfer interface with class ${i}.`);this.controlInterface_=t,this.transferInterface_=n}this.inEndpoint_=F(this.transferInterface_,`in`),this.outEndpoint_=F(this.transferInterface_,`out`)}get readable(){return!this.readable_&&this.device_.opened&&(this.readable_=new ReadableStream(new L(this.device_,this.inEndpoint_,()=>{this.readable_=null}),{highWaterMark:this.serialOptions_?.bufferSize??C})),this.readable_}get writable(){return!this.writable_&&this.device_.opened&&(this.writable_=new WritableStream(new R(this.device_,this.outEndpoint_,()=>{this.writable_=null}),new ByteLengthQueuingStrategy({highWaterMark:this.serialOptions_?.bufferSize??C}))),this.writable_}async open(e){this.serialOptions_=e,this.validateOptions();try{switch(await this.device_.open(),this.device_.configuration===null&&await this.device_.selectConfiguration(1),await this.device_.claimInterface(this.controlInterface_.interfaceNumber),this.controlInterface_!==this.transferInterface_&&await this.device_.claimInterface(this.transferInterface_.interfaceNumber),this.protocol_){case`cdc_acm`:await this.cdcInit();break;case`cp210x`:await this.cp210xInit();break;case`none`:break}}catch(e){throw this.device_.opened&&await this.device_.close(),Error(`Error setting up device: `+(e instanceof Error?e.message:String(e)),{cause:e})}}async close(){let e=[];if(this.readable_&&e.push(this.readable_.cancel()),this.writable_&&e.push(this.writable_.abort()),await Promise.all(e),this.readable_=null,this.writable_=null,this.device_.opened){switch(this.protocol_){case`cdc_acm`:await this.cdcSetSignals({dataTerminalReady:!1,requestToSend:!1});break;case`cp210x`:await this.cp210xDeinit();break}await this.device_.close()}}async forget(){return this.device_.forget()}getInfo(){return{usbVendorId:this.device_.vendorId,usbProductId:this.device_.productId}}async cdcInit(){await this.cdcSetLineCoding(),await this.cdcSetSignals({dataTerminalReady:!0})}async cdcSetSignals(e){if(this.cdcOutputSignals_={...this.cdcOutputSignals_,...e},e.dataTerminalReady!==void 0||e.requestToSend!==void 0){let e=(this.cdcOutputSignals_.dataTerminalReady?1:0)|(this.cdcOutputSignals_.requestToSend?2:0);await this.device_.controlTransferOut({requestType:`class`,recipient:`interface`,request:m,value:e,index:this.controlInterface_.interfaceNumber})}}async cdcSetLineCoding(){let e=new ArrayBuffer(7),t=new DataView(e);if(t.setUint32(0,this.serialOptions_.baudRate,!0),t.setUint8(4,j.indexOf(this.serialOptions_.stopBits??E)),t.setUint8(5,A.indexOf(this.serialOptions_.parity??T)),t.setUint8(6,this.serialOptions_.dataBits??w),(await this.device_.controlTransferOut({requestType:`class`,recipient:`interface`,request:p,value:0,index:this.controlInterface_.interfaceNumber},e)).status!==`ok`)throw new DOMException(`Failed to set line coding.`,`NetworkError`)}async cp210xInit(){let e=this.controlInterface_.interfaceNumber;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:h,value:y,index:e});let t=new ArrayBuffer(4);new DataView(t).setUint32(0,this.serialOptions_.baudRate,!0),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:g,value:0,index:e},t);let n=this.serialOptions_.dataBits??w,r={none:0,odd:16,even:32}[this.serialOptions_.parity??T]??0,i=({1:0,2:2}[this.serialOptions_.stopBits??E]??0)<<8|r|n;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:_,value:i,index:e}),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:v,value:x,index:e})}async cp210xDeinit(){let e=this.controlInterface_.interfaceNumber;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:v,value:S,index:e}),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:h,value:b,index:e})}validateOptions(){if(this.serialOptions_.baudRate%1!=0)throw RangeError(`Invalid baud rate: ${this.serialOptions_.baudRate}`);if(this.serialOptions_.dataBits!==void 0&&!D.includes(this.serialOptions_.dataBits))throw RangeError(`Invalid dataBits: ${this.serialOptions_.dataBits}`);if(this.serialOptions_.stopBits!==void 0&&!O.includes(this.serialOptions_.stopBits))throw RangeError(`Invalid stopBits: ${this.serialOptions_.stopBits}`);if(this.serialOptions_.parity!==void 0&&!k.includes(this.serialOptions_.parity))throw RangeError(`Invalid parity: ${this.serialOptions_.parity}`)}},B=class{options_;constructor(e){this.options_={...M,...e}}async requestPort(e,t){let n={...this.options_,...t},r=[];if(e?.filters&&e.filters.length>0)for(let t of e.filters){let e={};t.usbVendorId!==void 0&&(e.vendorId=t.usbVendorId),t.usbProductId!==void 0&&(e.productId=t.usbProductId),n.usbControlInterfaceClass!==void 0&&n.usbControlInterfaceClass!==255?e.classCode=n.usbControlInterfaceClass:e.vendorId===void 0&&e.productId===void 0&&(e.classCode=n.usbControlInterfaceClass??2),r.push(e)}else r.push({classCode:n.usbControlInterfaceClass??2});return new z(await navigator.usb.requestDevice({filters:r}),n)}async getPorts(e){let t={...this.options_,...e},n=await navigator.usb.getDevices(),r=[];for(let e of n)try{let n=new z(e,t);r.push(n)}catch{}return r}},V=`6e400001-b5a3-f393-e0a9-e50e24dcca9e`,H=`6e400003-b5a3-f393-e0a9-e50e24dcca9e`,U=`6e400002-b5a3-f393-e0a9-e50e24dcca9e`,W=20,G=10;function K(e){let t=null,n=null,r=null;return{get readable(){return t},get writable(){return n},getInfo(){return{}},async open(){if(!e.gatt)throw Error(`GATT not available on this Bluetooth device.`);r=await e.gatt.connect();let i=await r.getPrimaryService(V),a=await i.getCharacteristic(H),o=await i.getCharacteristic(U);await a.startNotifications(),t=new ReadableStream({start(e){a.addEventListener(`characteristicvaluechanged`,t=>{let n=t.target.value.buffer;e.enqueue(new Uint8Array(n))})}}),n=new WritableStream({async write(e){for(let t=0;t<e.length;t+=W){let n=e.slice(t,t+W);await o.writeValueWithoutResponse(n),t+W<e.length&&await new Promise(e=>setTimeout(e,G))}}})},async close(){r?.connected&&r.disconnect(),t=null,n=null}}}function q(){return{async requestPort(){if(!navigator.bluetooth)throw Error(`Web Bluetooth API is not supported in this browser. Use Chrome on Android, macOS, or ChromeOS.`);return K(await navigator.bluetooth.requestDevice({filters:[{services:[V]}]}))},async getPorts(){return[]}}}function J(e){return new Promise((t,n)=>{e.addEventListener(`open`,()=>t(),{once:!0}),e.addEventListener(`error`,e=>n(e),{once:!0})})}function Y(e,t){return new Promise(n=>{let r=i=>{let a=JSON.parse(i.data);a.type===t&&(e.removeEventListener(`message`,r),n(a.payload))};e.addEventListener(`message`,r)})}function X(e,t){let n=null,r=null;return{get readable(){return n},get writable(){return r},getInfo(){return{usbVendorId:t.vendorId,usbProductId:t.productId}},async open(i){e.send(JSON.stringify({type:`open`,path:t.path,baudRate:i.baudRate,dataBits:i.dataBits,stopBits:i.stopBits,parity:i.parity,parser:{type:`delimiter`,value:`\\n`}})),await Y(e,`opened`);let a=[],o=null,s=!1;function c(e){let t=JSON.parse(e.data);if(t.type===`data`&&t.bytes){let e=new Uint8Array(t.bytes);o?o.enqueue(e):a.push(e)}t.type===`closed`&&(s=!0,o&&o.close())}e.addEventListener(`message`,c),n=new ReadableStream({start(e){o=e;for(let t of a)e.enqueue(t);a.length=0,s&&e.close()},cancel(){e.removeEventListener(`message`,c),o=null}}),r=new WritableStream({write(t){e.send(JSON.stringify({type:`write`,bytes:Array.from(t)}))}})},async close(){e.send(JSON.stringify({type:`close`})),n=null,r=null,e.close()}}}function Z(e){return{async requestPort(t){let n=new WebSocket(e);await J(n),n.send(JSON.stringify({type:`list-ports`,filters:t?.filters??[]}));let r=(await Y(n,`port-list`))[0];if(!r)throw Error(`No ports available on the bridge server. Make sure the Node.js server is running and a device is connected.`);return X(n,r)},async getPorts(){let t=new WebSocket(e);return await J(t),t.send(JSON.stringify({type:`list-ports`,filters:[]})),(await Y(t,`port-list`)).map(e=>X(t,e))}}}e.AbstractSerialDevice=l,e.CommandQueue=r,e.SerialEventEmitter=t,e.SerialPermissionError=a,e.SerialPortConflictError=i,e.SerialReadError=s,e.SerialRegistry=n,e.SerialTimeoutError=o,e.SerialWriteError=c,e.WebUsbProvider=B,e.createBluetoothProvider=q,e.createWebSocketProvider=Z,e.delimiter=d,e.fixedLength=u,e.raw=f});
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports):typeof define==`function`&&define.amd?define([`exports`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.WebSerialCore={}))})(this,function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=class{listeners={};on(e,t){return this.listeners[e]||(this.listeners[e]=new Set),this.listeners[e].add(t),this}off(e,t){return this.listeners[e]&&this.listeners[e].delete(t),this}emit(e,...t){let n=this.listeners[e];if(!n||n.size===0)return!1;for(let e of n)e(...t);return!0}},n=class{static instances=new Set;static portInstanceMap=new WeakMap;static getInstances(){return Array.from(this.instances)}static register(e){this.instances.add(e)}static unregister(e){this.instances.delete(e)}static isPortInUse(e,t){let n=this.portInstanceMap.get(e);return n!==void 0&&n!==t}static lockPort(e,t){this.portInstanceMap.set(e,t)}static unlockPort(e){this.portInstanceMap.delete(e)}},r=class{queue=[];isProcessing=!1;isPaused=!0;timeoutId=null;commandTimeout;onSend;onTimeout;constructor(e){this.commandTimeout=e.commandTimeout,this.onSend=e.onSend,this.onTimeout=e.onTimeout}get queueSize(){return this.queue.length}enqueue(e){this.queue.push(e),this.tryProcessNext()}advance(){this.clearCommandTimeout(),this.isProcessing=!1,this.tryProcessNext()}pause(){this.isPaused=!0,this.clearCommandTimeout(),this.isProcessing=!1}resume(){this.isPaused=!1,this.tryProcessNext()}clear(){this.queue=[],this.clearCommandTimeout(),this.isProcessing=!1}snapshot(){return[...this.queue]}restore(e){this.queue=[...e,...this.queue]}tryProcessNext(){if(this.isPaused||this.isProcessing||this.queue.length===0)return;this.isProcessing=!0;let e=this.queue.shift();this.commandTimeout>0&&(this.timeoutId=setTimeout(()=>{this.timeoutId=null,this.onTimeout(e),this.advance()},this.commandTimeout)),this.onSend(e).catch(()=>{this.advance()})}clearCommandTimeout(){this.timeoutId!==null&&(clearTimeout(this.timeoutId),this.timeoutId=null)}},i=class e extends Error{constructor(t){super(t),this.name=`SerialPortConflictError`,Object.setPrototypeOf(this,e.prototype)}},a=class e extends Error{constructor(t){super(t),this.name=`SerialPermissionError`,Object.setPrototypeOf(this,e.prototype)}},o=class e extends Error{constructor(t){super(t),this.name=`SerialTimeoutError`,Object.setPrototypeOf(this,e.prototype)}},s=class e extends Error{constructor(t){super(t),this.name=`SerialReadError`,Object.setPrototypeOf(this,e.prototype)}},c=class e extends Error{constructor(t){super(t),this.name=`SerialWriteError`,Object.setPrototypeOf(this,e.prototype)}},l=class e extends t{port=null;reader=null;writer=null;queue;options;isConnecting=!1;abortController=null;userInitiatedDisconnect=!1;reconnectTimerId=null;isHandshaking=!1;static customProvider=null;static polyfillOptions;constructor(e){super(),this.options={baudRate:e.baudRate,dataBits:e.dataBits??8,stopBits:e.stopBits??1,parity:e.parity??`none`,bufferSize:e.bufferSize??255,flowControl:e.flowControl??`none`,filters:e.filters??[],commandTimeout:e.commandTimeout??0,parser:e.parser,autoReconnect:e.autoReconnect??!1,autoReconnectInterval:e.autoReconnectInterval??1500,handshakeTimeout:e.handshakeTimeout??2e3,provider:e.provider,polyfillOptions:e.polyfillOptions},this.queue=new r({commandTimeout:this.options.commandTimeout,onSend:async e=>{await this.writeToPort(e),this.emit(`serial:sent`,e,this)},onTimeout:e=>{this.emit(`serial:timeout`,e,this)}}),this.on(`serial:data`,()=>{this.queue.advance()}),n.register(this)}async handshake(){return!0}async connect(){if(!this.isConnecting&&!this.port){this.isConnecting=!0,this.emit(`serial:connecting`,this);try{let t=this.getSerial();if(!t)throw Error(`Web Serial API is not supported in this browser. Use AbstractSerialDevice.setProvider() to set a WebUSB polyfill.`);if(this.port=await this.findAndValidatePort(),!this.port){let n;try{n=await t.requestPort({filters:this.options.filters},this.options.polyfillOptions??e.polyfillOptions)}catch(e){throw e instanceof DOMException&&(e.name===`NotFoundError`||e.name===`SecurityError`||e.name===`AbortError`)?new a(e instanceof Error?e.message:String(e)):e instanceof Error?e:Error(String(e))}if(!await this.openAndHandshake(n))throw Error(`Handshake failed: the selected device did not respond correctly.`);this.port=n}this.abortController=new AbortController,this.queue.resume(),this.emit(`serial:connected`,this)}catch(e){if(e instanceof a?this.emit(`serial:need-permission`,this):this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this),this.port){n.unlockPort(this.port);try{await this.port.close()}catch{}this.port=null}throw e}finally{this.isConnecting=!1}}}async disconnect(){this.port&&(this.userInitiatedDisconnect=!0,this.stopReconnecting(),await this.cleanupPort())}isConnected(){return!!(this.port&&this.port.connected&&this.port.readable&&this.port.writable)}isDisconnected(){return!this.isConnected()}async cleanupPort(){if(this.port){this.queue.pause(),this.abortController?.abort(),this.abortController=null;try{let e=this.reader,t=this.writer;if(this.reader=null,this.writer=null,e){try{await e.cancel()}catch{}try{e.releaseLock()}catch{}}if(t){try{await t.close()}catch{}try{t.releaseLock()}catch{}}try{await this.port.close()}catch{}}catch(e){this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this)}finally{this.port&&n.unlockPort(this.port),this.port=null,this.options.parser?.reset?.(),this.emit(`serial:disconnected`,this),!this.userInitiatedDisconnect&&this.options.autoReconnect&&this.startReconnecting(),this.userInitiatedDisconnect=!1}}}async forget(){await this.disconnect(),this.port&&typeof this.port.forget==`function`&&await this.port.forget(),n.unregister(this)}async send(e){let t;t=typeof e==`string`?new TextEncoder().encode(e):e,t.length>0&&this.queue.enqueue(t)}clearQueue(){this.queue.clear(),this.emit(`serial:queue-empty`,this)}async writeToPort(e){if(!this.port||!this.port.writable)throw new c(`Port not writable.`);this.writer=this.port.writable.getWriter();try{await this.writer.write(e)}catch(e){throw new c(e instanceof Error?e.message:String(e))}finally{this.writer.releaseLock(),this.writer=null}}async readLoop(){if(!(!this.port||!this.port.readable)&&!this.reader){this.reader=this.port.readable.getReader();try{for(;;){let{value:e,done:t}=await this.reader.read();if(t)break;e&&(this.options.parser?this.options.parser.parse(e,e=>{this.emit(`serial:data`,e,this)}):this.emit(`serial:data`,e,this))}}catch(e){if(this.port)throw new s(e instanceof Error?e.message:String(e))}finally{if(this.reader){try{this.reader.releaseLock()}catch{}this.reader=null}}}}async openAndHandshake(e){let t=this;if(n.isPortInUse(e,t))return!1;n.lockPort(e,t);try{await e.open({baudRate:this.options.baudRate,dataBits:this.options.dataBits,stopBits:this.options.stopBits,parity:this.options.parity,bufferSize:this.options.bufferSize,flowControl:this.options.flowControl})}catch(t){throw n.unlockPort(e),t instanceof Error?t:Error(String(t))}this.port=e,this.abortController=new AbortController;let r=this.queue.snapshot();this.isHandshaking=!0,this.readLoop().catch(e=>{!this.isHandshaking&&this.port&&(this.emit(`serial:error`,e,this),this.cleanupPort())}),this.queue.resume();try{let t=await this.runHandshakeWithTimeout();return this.isHandshaking=!1,t?(this.queue.pause(),this.queue.clear(),this.queue.restore(r),this.options.parser?.reset?.(),!0):(await this.teardownHandshake(e,r),!1)}catch{return this.isHandshaking=!1,await this.teardownHandshake(e,r),!1}}async teardownHandshake(e,t){this.queue.pause(),this.queue.clear(),this.queue.restore(t),await this.stopReader(),this.port=null,this.abortController=null,this.options.parser?.reset?.();try{await e.close()}catch{}n.unlockPort(e)}async stopReader(){let e=this.reader;if(this.reader=null,e){try{await e.cancel()}catch{}try{e.releaseLock()}catch{}}}async runHandshakeWithTimeout(){let e=this.options.handshakeTimeout??2e3;return Promise.race([this.handshake(),new Promise(t=>setTimeout(()=>t(!1),e))])}async findAndValidatePort(){let t=this.getSerial();if(!t)return null;let r=await t.getPorts(this.options.polyfillOptions??e.polyfillOptions);if(r.length===0)return null;let i=this.options.filters??[],a=this;for(let e of r)if(!n.isPortInUse(e,a)){if(i.length>0){let t=e.getInfo();if(!i.some(e=>{let n=e.usbVendorId===void 0||e.usbVendorId===t.usbVendorId,r=e.usbProductId===void 0||e.usbProductId===t.usbProductId;return n&&r}))continue}try{if(await this.openAndHandshake(e))return e}catch{}}return null}startReconnecting(){this.reconnectTimerId||=(this.emit(`serial:reconnecting`,this),setInterval(async()=>{if(this.port||this.isConnecting){this.stopReconnecting();return}try{let e=await this.findAndValidatePort();e&&(this.stopReconnecting(),await this.reconnect(e))}catch{}},this.options.autoReconnectInterval))}stopReconnecting(){this.reconnectTimerId&&=(clearInterval(this.reconnectTimerId),null)}async reconnect(e){if(!(this.isConnecting||this.port)){this.isConnecting=!0,this.emit(`serial:connecting`,this);try{this.port=e,this.abortController=new AbortController,this.queue.resume(),this.emit(`serial:connected`,this)}catch(e){this.emit(`serial:error`,e instanceof Error?e:Error(String(e)),this),this.port&&=(n.unlockPort(this.port),null),this.options.autoReconnect&&this.startReconnecting()}finally{this.isConnecting=!1}}}static getInstances(){return n.getInstances()}static async connectAll(){let e=n.getInstances();for(let t of e)try{await t.connect()}catch{}}static setProvider(t,n){e.customProvider=t,e.polyfillOptions=n}getSerial(){return this.options.provider?this.options.provider:e.customProvider?e.customProvider:typeof navigator<`u`&&navigator.serial?navigator.serial:null}};function u(e){if(e<=0)throw Error(`FixedLengthParser: length must be greater than 0`);let t=new Uint8Array;return{parse(n,r){let i=new Uint8Array(t.length+n.length);for(i.set(t),i.set(n,t.length),t=i;t.length>=e;)r(t.slice(0,e)),t=t.slice(e)},reset(){t=new Uint8Array}}}function d(e){let t=``,n=new TextDecoder;return{parse(r,i){t+=n.decode(r,{stream:!0});let a;for(;(a=t.indexOf(e))!==-1;)i(t.slice(0,a)),t=t.slice(a+e.length)},reset(){t=``,n=new TextDecoder}}}function f(){return{parse(e,t){t(e)},reset(){}}}var p=32,m=34,h=0,g=30,_=3,v=7,y=1,b=0,x=771,S=768,C=255,w=8,T=`none`,E=1,D=[16,8,7,6,5],O=[1,2],k=[`none`,`even`,`odd`],A=[`none`,`odd`,`even`],j=[1,1.5,2],M={usbControlInterfaceClass:2,usbTransferInterfaceClass:10,protocol:void 0};function N(e,t){let n=e.configurations[0];if(!n)return null;for(let e of n.interfaces)if(e.alternates[0]?.interfaceClass===t)return e;return null}function P(e,t){let n=e.configurations[0];if(!n)return null;for(let e of n.interfaces){let n=e.alternates[0];if(!n||n.interfaceClass!==t)continue;let r=n.endpoints.some(e=>e.direction===`in`),i=n.endpoints.some(e=>e.direction===`out`);if(r&&i)return e}return null}function F(e,t){let n=e.alternates[0];if(n){for(let e of n.endpoints)if(e.direction===t)return e}throw TypeError(`Interface ${e.interfaceNumber} does not have an ${t} endpoint.`)}function I(e,t){return t===2?`cdc_acm`:e.vendorId===4292?`cp210x`:`none`}var L=class{device_;endpoint_;onError_;constructor(e,t,n){this.device_=e,this.endpoint_=t,this.onError_=n}pull(e){(async()=>{let t=this.endpoint_.packetSize;try{let n=await this.device_.transferIn(this.endpoint_.endpointNumber,t);if(n.status!==`ok`){e.error(`USB error: ${n.status}`),this.onError_();return}if(n.data?.buffer&&n.data.byteLength>0){let t=new Uint8Array(n.data.buffer,n.data.byteOffset,n.data.byteLength);t.length>0&&e.enqueue(t)}}catch(t){e.error(String(t)),this.onError_()}})()}},R=class{device_;endpoint_;onError_;constructor(e,t,n){this.device_=e,this.endpoint_=t,this.onError_=n}async write(e,t){try{let n=await this.device_.transferOut(this.endpoint_.endpointNumber,e.buffer);n.status!==`ok`&&(t.error(n.status),this.onError_())}catch(e){t.error(String(e)),this.onError_()}}},z=class{device_;protocol_;controlInterface_;transferInterface_;inEndpoint_;outEndpoint_;serialOptions_;readable_=null;writable_=null;cdcOutputSignals_={dataTerminalReady:!1,requestToSend:!1,break:!1};constructor(e,t){this.device_=e;let n={...M,...t};this.protocol_=n.protocol??I(e,n.usbControlInterfaceClass);let r=n.usbControlInterfaceClass,i=n.usbTransferInterfaceClass;if(r===i){let t=P(e,i);if(!t)throw TypeError(`Unable to find interface with class ${i} that has both IN and OUT endpoints.`);this.controlInterface_=t,this.transferInterface_=t}else{let t=N(e,r);if(!t)throw TypeError(`Unable to find control interface with class ${r}.`);let n=P(e,i)??N(e,i);if(!n)throw TypeError(`Unable to find transfer interface with class ${i}.`);this.controlInterface_=t,this.transferInterface_=n}this.inEndpoint_=F(this.transferInterface_,`in`),this.outEndpoint_=F(this.transferInterface_,`out`)}get readable(){return!this.readable_&&this.device_.opened&&(this.readable_=new ReadableStream(new L(this.device_,this.inEndpoint_,()=>{this.readable_=null}),{highWaterMark:this.serialOptions_?.bufferSize??C})),this.readable_}get writable(){return!this.writable_&&this.device_.opened&&(this.writable_=new WritableStream(new R(this.device_,this.outEndpoint_,()=>{this.writable_=null}),new ByteLengthQueuingStrategy({highWaterMark:this.serialOptions_?.bufferSize??C}))),this.writable_}async open(e){this.serialOptions_=e,this.validateOptions();try{switch(await this.device_.open(),this.device_.configuration===null&&await this.device_.selectConfiguration(1),await this.device_.claimInterface(this.controlInterface_.interfaceNumber),this.controlInterface_!==this.transferInterface_&&await this.device_.claimInterface(this.transferInterface_.interfaceNumber),this.protocol_){case`cdc_acm`:await this.cdcInit();break;case`cp210x`:await this.cp210xInit();break;case`none`:break}}catch(e){throw this.device_.opened&&await this.device_.close(),Error(`Error setting up device: `+(e instanceof Error?e.message:String(e)),{cause:e})}}async close(){let e=[];if(this.readable_&&e.push(this.readable_.cancel()),this.writable_&&e.push(this.writable_.abort()),await Promise.all(e),this.readable_=null,this.writable_=null,this.device_.opened){switch(this.protocol_){case`cdc_acm`:await this.cdcSetSignals({dataTerminalReady:!1,requestToSend:!1});break;case`cp210x`:await this.cp210xDeinit();break}await this.device_.close()}}async forget(){return this.device_.forget()}getInfo(){return{usbVendorId:this.device_.vendorId,usbProductId:this.device_.productId}}async cdcInit(){await this.cdcSetLineCoding(),await this.cdcSetSignals({dataTerminalReady:!0})}async cdcSetSignals(e){if(this.cdcOutputSignals_={...this.cdcOutputSignals_,...e},e.dataTerminalReady!==void 0||e.requestToSend!==void 0){let e=!!this.cdcOutputSignals_.dataTerminalReady|(this.cdcOutputSignals_.requestToSend?2:0);await this.device_.controlTransferOut({requestType:`class`,recipient:`interface`,request:m,value:e,index:this.controlInterface_.interfaceNumber})}}async cdcSetLineCoding(){let e=new ArrayBuffer(7),t=new DataView(e);if(t.setUint32(0,this.serialOptions_.baudRate,!0),t.setUint8(4,j.indexOf(this.serialOptions_.stopBits??E)),t.setUint8(5,A.indexOf(this.serialOptions_.parity??T)),t.setUint8(6,this.serialOptions_.dataBits??w),(await this.device_.controlTransferOut({requestType:`class`,recipient:`interface`,request:p,value:0,index:this.controlInterface_.interfaceNumber},e)).status!==`ok`)throw new DOMException(`Failed to set line coding.`,`NetworkError`)}async cp210xInit(){let e=this.controlInterface_.interfaceNumber;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:h,value:y,index:e});let t=new ArrayBuffer(4);new DataView(t).setUint32(0,this.serialOptions_.baudRate,!0),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:g,value:0,index:e},t);let n=this.serialOptions_.dataBits??w,r={none:0,odd:16,even:32}[this.serialOptions_.parity??T]??0,i=({1:0,2:2}[this.serialOptions_.stopBits??E]??0)<<8|r|n;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:_,value:i,index:e}),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:v,value:x,index:e})}async cp210xDeinit(){let e=this.controlInterface_.interfaceNumber;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:v,value:S,index:e}),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:h,value:b,index:e})}validateOptions(){if(this.serialOptions_.baudRate%1!=0)throw RangeError(`Invalid baud rate: ${this.serialOptions_.baudRate}`);if(this.serialOptions_.dataBits!==void 0&&!D.includes(this.serialOptions_.dataBits))throw RangeError(`Invalid dataBits: ${this.serialOptions_.dataBits}`);if(this.serialOptions_.stopBits!==void 0&&!O.includes(this.serialOptions_.stopBits))throw RangeError(`Invalid stopBits: ${this.serialOptions_.stopBits}`);if(this.serialOptions_.parity!==void 0&&!k.includes(this.serialOptions_.parity))throw RangeError(`Invalid parity: ${this.serialOptions_.parity}`)}},B=class{options_;constructor(e){this.options_={...M,...e}}async requestPort(e,t){let n={...this.options_,...t},r=[];if(e?.filters&&e.filters.length>0)for(let t of e.filters){let e={};t.usbVendorId!==void 0&&(e.vendorId=t.usbVendorId),t.usbProductId!==void 0&&(e.productId=t.usbProductId),n.usbControlInterfaceClass!==void 0&&n.usbControlInterfaceClass!==255?e.classCode=n.usbControlInterfaceClass:e.vendorId===void 0&&e.productId===void 0&&(e.classCode=n.usbControlInterfaceClass??2),r.push(e)}else r.push({classCode:n.usbControlInterfaceClass??2});return new z(await navigator.usb.requestDevice({filters:r}),n)}async getPorts(e){let t={...this.options_,...e},n=await navigator.usb.getDevices(),r=[];for(let e of n)try{let n=new z(e,t);r.push(n)}catch{}return r}},V=`6e400001-b5a3-f393-e0a9-e50e24dcca9e`,H=`6e400003-b5a3-f393-e0a9-e50e24dcca9e`,U=`6e400002-b5a3-f393-e0a9-e50e24dcca9e`,W=20,G=10;function K(e){let t=null,n=null,r=null;return{get readable(){return t},get writable(){return n},getInfo(){return{}},async open(){if(!e.gatt)throw Error(`GATT not available on this Bluetooth device.`);r=await e.gatt.connect();let i=await r.getPrimaryService(V),a=await i.getCharacteristic(H),o=await i.getCharacteristic(U);await a.startNotifications(),t=new ReadableStream({start(e){a.addEventListener(`characteristicvaluechanged`,t=>{let n=t.target.value.buffer;e.enqueue(new Uint8Array(n))})}}),n=new WritableStream({async write(e){for(let t=0;t<e.length;t+=W){let n=e.slice(t,t+W);await o.writeValueWithoutResponse(n),t+W<e.length&&await new Promise(e=>setTimeout(e,G))}}})},async close(){r?.connected&&r.disconnect(),t=null,n=null}}}function q(){return{async requestPort(){if(!navigator.bluetooth)throw Error(`Web Bluetooth API is not supported in this browser. Use Chrome on Android, macOS, or ChromeOS.`);return K(await navigator.bluetooth.requestDevice({filters:[{services:[V]}]}))},async getPorts(){return[]}}}function J(e){return new Promise((t,n)=>{e.addEventListener(`open`,()=>t(),{once:!0}),e.addEventListener(`error`,e=>n(e),{once:!0})})}function Y(e,t){return new Promise(n=>{let r=i=>{let a=JSON.parse(i.data);a.type===t&&(e.removeEventListener(`message`,r),n(a.payload))};e.addEventListener(`message`,r)})}function X(e,t){let n=null,r=null;return{get readable(){return n},get writable(){return r},getInfo(){return{usbVendorId:t.vendorId,usbProductId:t.productId}},async open(i){e.send(JSON.stringify({type:`open`,path:t.path,baudRate:i.baudRate,dataBits:i.dataBits,stopBits:i.stopBits,parity:i.parity,parser:{type:`delimiter`,value:`\\n`}})),await Y(e,`opened`);let a=[],o=null,s=!1;function c(e){let t=JSON.parse(e.data);if(t.type===`data`&&t.bytes){let e=new Uint8Array(t.bytes);o?o.enqueue(e):a.push(e)}t.type===`closed`&&(s=!0,o&&o.close())}e.addEventListener(`message`,c),n=new ReadableStream({start(e){o=e;for(let t of a)e.enqueue(t);a.length=0,s&&e.close()},cancel(){e.removeEventListener(`message`,c),o=null}}),r=new WritableStream({write(t){e.send(JSON.stringify({type:`write`,bytes:Array.from(t)}))}})},async close(){e.send(JSON.stringify({type:`close`})),n=null,r=null,e.close()}}}function Z(e){return{async requestPort(t){let n=new WebSocket(e);await J(n),n.send(JSON.stringify({type:`list-ports`,filters:t?.filters??[]}));let r=(await Y(n,`port-list`))[0];if(!r)throw Error(`No ports available on the bridge server. Make sure the Node.js server is running and a device is connected.`);return X(n,r)},async getPorts(){let t=new WebSocket(e);return await J(t),t.send(JSON.stringify({type:`list-ports`,filters:[]})),(await Y(t,`port-list`)).map(e=>X(t,e))}}}e.AbstractSerialDevice=l,e.CommandQueue=r,e.SerialEventEmitter=t,e.SerialPermissionError=a,e.SerialPortConflictError=i,e.SerialReadError=s,e.SerialRegistry=n,e.SerialTimeoutError=o,e.SerialWriteError=c,e.WebUsbProvider=B,e.createBluetoothProvider=q,e.createWebSocketProvider=Z,e.delimiter=d,e.fixedLength=u,e.raw=f});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webserial-core",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"author": "danidoble",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -11,20 +11,20 @@
|
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"@eslint/js": "^10.0.1",
|
|
13
13
|
"@iconify/vue": "^5.0.0",
|
|
14
|
-
"@types/bun": "^1.3.
|
|
14
|
+
"@types/bun": "^1.3.12",
|
|
15
15
|
"@types/w3c-web-serial": "^1.0.8",
|
|
16
|
-
"@types/w3c-web-usb": "^1.0.
|
|
16
|
+
"@types/w3c-web-usb": "^1.0.14",
|
|
17
17
|
"@types/web-bluetooth": "^0.0.21",
|
|
18
|
-
"eslint": "^10.0
|
|
19
|
-
"globals": "^17.
|
|
18
|
+
"eslint": "^10.2.0",
|
|
19
|
+
"globals": "^17.5.0",
|
|
20
20
|
"jiti": "^2.6.1",
|
|
21
21
|
"prettier": "3.8.1",
|
|
22
22
|
"typescript": "~5.9.3",
|
|
23
|
-
"typescript-eslint": "^8.
|
|
24
|
-
"vite": "^8.0.
|
|
23
|
+
"typescript-eslint": "^8.58.2",
|
|
24
|
+
"vite": "^8.0.8",
|
|
25
25
|
"vite-plugin-dts": "^4.5.4",
|
|
26
26
|
"vitepress": "^1.6.4",
|
|
27
|
-
"vitepress-plugin-llms": "^1.
|
|
27
|
+
"vitepress-plugin-llms": "^1.12.1"
|
|
28
28
|
},
|
|
29
29
|
"exports": {
|
|
30
30
|
".": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{_ as e,a as t,b as n,d as r,f as i,g as a,h as o,i as s,m as ee,n as c,o as te,p as l,r as u,s as ne,t as d,v as re,x as f,y as ie}from"./demo-shared-DnvFynUr.js";var p=`6e400001-b5a3-f393-e0a9-e50e24dcca9e`,ae=`6e400003-b5a3-f393-e0a9-e50e24dcca9e`,oe=`6e400002-b5a3-f393-e0a9-e50e24dcca9e`,m=20,se=10;function ce(e){let t=null,n=null,r=null;return{get readable(){return t},get writable(){return n},getInfo(){return{}},async open(){if(!e.gatt)throw Error(`GATT not available on this Bluetooth device.`);r=await e.gatt.connect();let i=await r.getPrimaryService(p),a=await i.getCharacteristic(ae),o=await i.getCharacteristic(oe);await a.startNotifications(),t=new ReadableStream({start(e){a.addEventListener(`characteristicvaluechanged`,t=>{let n=t.target.value.buffer;e.enqueue(new Uint8Array(n))})}}),n=new WritableStream({async write(e){for(let t=0;t<e.length;t+=m){let n=e.slice(t,t+m);await o.writeValueWithoutResponse(n),t+m<e.length&&await new Promise(e=>setTimeout(e,se))}}})},async close(){r?.connected&&r.disconnect(),t=null,n=null}}}function le(){return{async requestPort(){if(!navigator.bluetooth)throw Error(`Web Bluetooth API is not supported in this browser. Use Chrome on Android, macOS, or ChromeOS.`);return ce(await navigator.bluetooth.requestDevice({filters:[{services:[p]}]}))},async getPorts(){return[]}}}f.setProvider(le());var h=[],g=[],ue=class extends f{_hsCmd;_hsCmdMode;_hsExpect;_hsExpectMode;constructor(e,t,n,r,i){super(e),this._hsCmd=t,this._hsCmdMode=n,this._hsExpect=r,this._hsExpectMode=i}async handshake(){if(!this._hsCmd||(this._hsCmdMode===`hex`?await this.send(I(this._hsCmd)):await this.send(u(this._hsCmd)),!this._hsExpect))return!0;let e=this._hsExpect.trim();return new Promise(t=>{let n=r=>{if(this.off(`serial:data`,n),this._hsExpectMode===`hex`){let e=new TextEncoder().encode(String(r)),n=I(this._hsExpect);t(e.length===n.length&&e.every((e,t)=>e===n[t]))}else t(String(r).trim()===e)};this.on(`serial:data`,n)})}},_=e=>document.getElementById(e),v=_(`messages`),y=_(`btn-connect`),b=_(`btn-disconnect`),x=_(`btn-send`),S=_(`input-send`),C=_(`mode-toggle`),de=_(`status-dot`),w=_(`status-text`),fe=_(`console-dot`),T=_(`console-text`),E=_(`sidebar`),D=_(`code-panel`),O=_(`code-view`),k=_(`code-tab`),pe=_(`menu-btn`),me=_(`code-toggle-btn`),A=_(`theme-btn`),he=_(`clear-btn`),j=_(`copy-btn`),ge=_(`dl-btn`),_e=_(`cfg-export-btn`),M=_(`cfg-import-input`);function N(){let e=e=>(_(e)?.value??``).trim(),t=(t,n)=>parseInt(e(t))||n,n=e=>_(e)?.value??``;return{bufferSize:t(`cfg-bufsize`,255),commandTimeout:t(`cfg-timeout`,3e3),handshakeTimeout:t(`cfg-handshake`,2e3),delimiter:e(`cfg-delim`)||`\\n`,prepend:e(`cfg-prepend`),append:e(`cfg-append`),hsCmd:e(`cfg-hs-cmd`),hsCmdMode:n(`cfg-hs-cmd-mode`)||`text`,hsExpect:e(`cfg-hs-expect`),hsExpectMode:n(`cfg-hs-expect-mode`)||`text`}}function ve(e){let t=(e,t)=>{let n=document.getElementById(e);n&&(n instanceof HTMLInputElement&&n.type===`checkbox`?n.checked=!!t:(n instanceof HTMLInputElement||n instanceof HTMLSelectElement)&&(n.value=String(t??``)))};t(`cfg-bufsize`,e.bufferSize??255),t(`cfg-timeout`,e.commandTimeout??3e3),t(`cfg-handshake`,e.handshakeTimeout??2e3),t(`cfg-delim`,e.delimiter??`\\n`),t(`cfg-prepend`,e.prepend??``),t(`cfg-append`,e.append??``),t(`cfg-hs-cmd`,e.hsCmd??``),t(`cfg-hs-cmd-mode`,e.hsCmdMode??`text`),t(`cfg-hs-expect`,e.hsExpect??``),t(`cfg-hs-expect-mode`,e.hsExpectMode??`text`)}function P(e){let t=e.replace(/[^a-zA-Z0-9_$]/g,``).replace(/^[^a-zA-Z_$]/,`C`);return t.charAt(0).toUpperCase()+t.slice(1)||`MyDevice`}function F(e){return Array.from(e).map(e=>e.toString(16).toUpperCase().padStart(2,`0`)).join(` `)}function I(e){let t=e.replace(/\s+/g,``);if(t.length%2!=0)throw Error(`Odd number of hex characters.`);let n=new Uint8Array(t.length/2);for(let e=0;e<t.length;e+=2)n[e/2]=parseInt(t.substring(e,e+2),16);return n}function L(e,t){[de,fe].forEach(t=>{t&&(t.className=`status-dot`,e!==`disconnected`&&t.classList.add(e))}),w&&(w.textContent=t),T&&(T.textContent=t)}var R=null;function z(){R&&clearTimeout(R),R=setTimeout(()=>{let e=N(),t=P((_(`dl-name`)?.value??`MyBleDevice`).trim()),n=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,r=n?`ts`:`js`;re(O,ne(e,t,n,h,g)),k&&(k.textContent=`${t.substring(0,10).toLowerCase()}.${r}`)},180)}var ye=l();A&&(A.textContent=ye===`dark`?`☀️`:`🌙`),pe?.addEventListener(`click`,()=>E.classList.toggle(`collapsed`)),me?.addEventListener(`click`,()=>D.classList.toggle(`collapsed`));var B=_(`resize-handle`);if(B){let e=0,t=0,n=n=>{let r=Math.max(180,Math.min(700,t+(e-n.clientX)));document.documentElement.style.setProperty(`--code-w`,`${r}px`)},r=()=>{B.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};B.addEventListener(`pointerdown`,i=>{e=i.clientX,t=D.getBoundingClientRect().width,B.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}var V=_(`sidebar-resize-handle`);if(V){let e=0,t=0,n=n=>{let r=Math.max(200,Math.min(600,t+(n.clientX-e)));document.documentElement.style.setProperty(`--sidebar-w`,`${r}px`)},r=()=>{V.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};V.addEventListener(`pointerdown`,i=>{e=i.clientX,t=E.getBoundingClientRect().width,V.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}window.innerWidth<=960&&D.classList.add(`collapsed`),window.innerWidth<=640&&E.classList.add(`collapsed`),A?.addEventListener(`click`,()=>{let e=ie();A&&(A.textContent=e===`dark`?`☀️`:`🌙`)}),he?.addEventListener(`click`,()=>c(v)),j?.addEventListener(`click`,async()=>{let e=O?.textContent??``;try{await navigator.clipboard.writeText(e),j&&(j.textContent=`Copied!`,j.classList.add(`copied`),setTimeout(()=>{j.textContent=`Copy`,j.classList.remove(`copied`)},1500))}catch{}}),ge?.addEventListener(`click`,()=>{let n=N(),r=(_(`dl-name`)?.value??`my-ble-device`).trim(),i=P(r),s=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,c=document.querySelector(`input[name='dl-type']:checked`)?.value??`file`,l=s?`ts`:`js`,u=ne(n,i,s,h,g);c===`project`?te([{name:`device.${l}`,content:u},{name:`package.json`,content:o(r,s,`ble`)},{name:`index.html`,content:ee(i,l,`Web Bluetooth`)},{name:`README.md`,content:a(i,l,`Web Bluetooth`,`Requires a Chromium browser. The device must expose a Nordic UART Service (NUS) via BLE GATT.`)},...s?[{name:`tsconfig.json`,content:e()}]:[]],`${r}-project`):t(`${r}.${l}`,u)}),_e?.addEventListener(`click`,()=>{let e=(_(`dl-name`)?.value??`my-ble-device`).trim(),t=P(e);s({$version:1,provider:`ble`,className:e,language:document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`,dlType:document.querySelector(`input[name='dl-type']:checked`)?.value??`file`,cfg:N(),commands:h,listeners:g},t+`-config`)}),M?.addEventListener(`change`,()=>{let e=M.files?.[0];if(!e)return;let t=new FileReader;t.onload=e=>{try{let t=JSON.parse(e.target?.result);if(t.$version!==1||t.provider!==`ble`)return;let n=_(`dl-name`);n&&(n.value=t.className),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>{e.checked=e.value===t.language}),document.querySelectorAll(`input[name='dl-type']`).forEach(e=>{e.checked=e.value===t.dlType}),ve(t.cfg),h=t.commands.map(e=>({...e,id:crypto.randomUUID()})),g=t.listeners.map(e=>({...e,id:crypto.randomUUID()})),Q(),$(),z()}catch{}M.value=``},t.readAsText(e)}),[`cfg-bufsize`,`cfg-timeout`,`cfg-handshake`,`cfg-delim`,`cfg-prepend`,`cfg-append`,`cfg-hs-cmd`,`cfg-hs-cmd-mode`,`cfg-hs-expect`,`cfg-hs-expect-mode`,`dl-name`].forEach(e=>{let t=document.getElementById(e);t?.addEventListener(`change`,z),t?.addEventListener(`input`,z)}),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>e.addEventListener(`change`,z)),z();var H=`text`;C?.addEventListener(`click`,()=>{H=H===`text`?`hex`:`text`,C.textContent=H===`text`?`TXT`:`HEX`,S.placeholder=H===`text`?`Type a command, e.g. LED_ON`:`Hex bytes, e.g. FF 01 A3`});var U=null;function W(e){x.disabled=!e,S.disabled=!e,b.disabled=!e,y.disabled=e}y?.addEventListener(`click`,async()=>{if(U){try{await U.disconnect()}catch{}U=null}let e=N(),t=u(e.delimiter);U=new ue({baudRate:9600,bufferSize:e.bufferSize,commandTimeout:e.commandTimeout,parser:n(t),autoReconnect:!1,handshakeTimeout:e.handshakeTimeout},e.hsCmd,e.hsCmdMode,e.hsExpect,e.hsExpectMode),U.on(`serial:connecting`,()=>{L(`connecting`,`Connecting…`),y.disabled=!0,d(v,`Initiating Web Bluetooth connection…`,{kind:`system`})}),U.on(`serial:connected`,()=>{L(`connected`,`Connected`),W(!0),d(v,`Connected via Web Bluetooth!`,{kind:`system`})}),U.on(`serial:disconnected`,()=>{L(`disconnected`,`Disconnected`),W(!1),d(v,`Disconnected.`,{kind:`system`}),U=null}),U.on(`serial:data`,e=>{d(v,String(e),{kind:`received`,label:`Device`})}),U.on(`serial:error`,e=>{L(`error`,`Error`),d(v,`Error: ${e.message}`,{kind:`error`}),y.disabled=!1}),U.on(`serial:need-permission`,()=>{L(`error`,`Permission denied`),d(v,`Permission denied — select a valid BLE device and allow access.`,{kind:`error`}),y.disabled=!1}),U.on(`serial:timeout`,e=>{d(v,`Timeout: ${F(e)}`,{kind:`error`})});try{await U.connect()}catch{}}),b?.addEventListener(`click`,async()=>{await U?.disconnect()});async function G(){let e=S.value.trim();if(!e||!U)return;let t=N(),n=t.append?u(t.append):u(t.delimiter);try{if(H===`hex`){let t=I(e);d(v,`HEX: ${F(t)}`,{kind:`sent`,label:`You`}),await U.send(t)}else{let r=t.prepend+e+n;d(v,e,{kind:`sent`,label:`You`}),await U.send(r)}S.value=``,S.focus()}catch(e){d(v,`Send error: ${e instanceof Error?e.message:String(e)}`,{kind:`error`})}}x?.addEventListener(`click`,G),S?.addEventListener(`keydown`,e=>{e.key===`Enter`&&G()});var K=_(`cmd-name`),q=_(`cmd-value`),be=_(`cmd-mode`),xe=_(`cmd-add`),J=_(`cmd-list`),Y=_(`lst-name`),X=_(`lst-pattern`),Se=_(`lst-match`),Ce=_(`lst-add`),Z=_(`lst-list`);function Q(){if(J){J.innerHTML=``;for(let e of h){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.mode.toUpperCase();let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.value;let a=document.createElement(`button`);a.className=`chip-send`,a.title=`Send now`,a.textContent=`▶`,a.addEventListener(`click`,()=>{if(U)if(e.mode===`hex`){let t=I(e.value);U.send(t).catch(()=>{}),d(v,`HEX: ${F(t)}`,{kind:`sent`,label:`You`})}else{let t=N(),n=t.append?u(t.append):u(t.delimiter);U.send(t.prepend+e.value+n).catch(()=>{}),d(v,e.name,{kind:`sent`,label:`You`})}});let o=document.createElement(`button`);o.className=`chip-del`,o.title=`Remove`,o.textContent=`×`,o.addEventListener(`click`,()=>{h=h.filter(t=>t.id!==e.id),Q(),z()}),t.append(n,r,i,a,o),J.appendChild(t)}}}function $(){if(Z){Z.innerHTML=``;for(let e of g){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.match;let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.pattern;let a=document.createElement(`button`);a.className=`chip-del`,a.title=`Remove`,a.textContent=`×`,a.addEventListener(`click`,()=>{g=g.filter(t=>t.id!==e.id),$(),z()}),t.append(n,r,i,a),Z.appendChild(t)}}}xe?.addEventListener(`click`,()=>{let e=K?.value.trim(),t=q?.value.trim();if(!e||!t)return;let n=be?.value??`text`;h.push({id:crypto.randomUUID(),name:e,value:t,mode:n}),K&&(K.value=``),q&&(q.value=``),Q(),z()}),Ce?.addEventListener(`click`,()=>{let e=Y?.value.trim(),t=X?.value.trim();if(!e||!t)return;let n=Se?.value??`exact`;g.push({id:crypto.randomUUID(),name:e,pattern:t,match:n}),Y&&(Y.value=``),X&&(X.value=``),$(),z()}),navigator.bluetooth?d(v,`Web Bluetooth demo ready — configure settings and click Connect.`,{kind:`system`}):(d(v,`Web Bluetooth is NOT supported in this browser or OS.`,{kind:`error`}),y.disabled=!0),r(v),i();
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{_ as e,a as t,b as n,c as r,d as i,f as a,g as o,h as ee,i as s,m as te,n as c,o as ne,p as l,r as u,t as d,v as f,x as re,y as ie}from"./demo-shared-DnvFynUr.js";var p=[],m=[],h=class extends re{_hsCmd;_hsCmdMode;_hsExpect;_hsExpectMode;constructor(e,t,n,r,i){super(e),this._hsCmd=t,this._hsCmdMode=n,this._hsExpect=r,this._hsExpectMode=i}async handshake(){if(!this._hsCmd||(this._hsCmdMode===`hex`?await this.send(F(this._hsCmd)):await this.send(u(this._hsCmd)),!this._hsExpect))return!0;let e=this._hsExpect.trim();return new Promise(t=>{let n=r=>{if(this.off(`serial:data`,n),this._hsExpectMode===`hex`){let e=new TextEncoder().encode(String(r)),n=F(this._hsExpect);t(e.length===n.length&&e.every((e,t)=>e===n[t]))}else t(String(r).trim()===e)};this.on(`serial:data`,n)})}},g=e=>document.getElementById(e),_=g(`messages`),v=g(`btn-connect`),y=g(`btn-disconnect`),b=g(`btn-send`),x=g(`input-send`),S=g(`mode-toggle`),ae=g(`status-dot`),C=g(`status-text`),oe=g(`console-dot`),w=g(`console-text`),T=g(`sidebar`),E=g(`code-panel`),D=g(`code-view`),O=g(`code-tab`),se=g(`menu-btn`),ce=g(`code-toggle-btn`),k=g(`theme-btn`),le=g(`clear-btn`),A=g(`copy-btn`),ue=g(`dl-btn`),de=g(`cfg-export-btn`),j=g(`cfg-import-input`);function M(){let e=e=>(g(e)?.value??``).trim(),t=(t,n)=>parseInt(e(t))||n,n=e=>g(e)?.value??``,r=e=>g(e)?.checked??!1,i=e(`cfg-vendor`),a=e(`cfg-product`),o=[];if(i||a){let e={};i&&(e.usbVendorId=parseInt(i,16)),a&&(e.usbProductId=parseInt(a,16)),o.push(e)}return{baudRate:t(`cfg-baud`,9600),dataBits:t(`cfg-databits`,8),stopBits:t(`cfg-stopbits`,1),parity:n(`cfg-parity`)||`none`,flowControl:n(`cfg-flow`)||`none`,bufferSize:t(`cfg-bufsize`,255),commandTimeout:t(`cfg-timeout`,3e3),autoReconnect:r(`cfg-autoreconnect`),autoReconnectInterval:t(`cfg-reconnect-ms`,1500),handshakeTimeout:t(`cfg-handshake`,2e3),delimiter:e(`cfg-delim`)||`\\n`,prepend:e(`cfg-prepend`),append:e(`cfg-append`),hsCmd:e(`cfg-hs-cmd`),hsCmdMode:n(`cfg-hs-cmd-mode`)||`text`,hsExpect:e(`cfg-hs-expect`),hsExpectMode:n(`cfg-hs-expect-mode`)||`text`,filters:o}}function fe(e){let t=(e,t)=>{let n=document.getElementById(e);n&&(n instanceof HTMLInputElement&&n.type===`checkbox`?n.checked=!!t:(n instanceof HTMLInputElement||n instanceof HTMLSelectElement)&&(n.value=String(t??``)))};t(`cfg-baud`,e.baudRate??9600),t(`cfg-databits`,e.dataBits??8),t(`cfg-stopbits`,e.stopBits??1),t(`cfg-parity`,e.parity??`none`),t(`cfg-flow`,e.flowControl??`none`),t(`cfg-bufsize`,e.bufferSize??255),t(`cfg-timeout`,e.commandTimeout??3e3),t(`cfg-handshake`,e.handshakeTimeout??2e3),t(`cfg-reconnect-ms`,e.autoReconnectInterval??1500),t(`cfg-autoreconnect`,e.autoReconnect??!1),t(`cfg-vendor`,e.filters?.[0]?.usbVendorId==null?``:e.filters[0].usbVendorId.toString(16)),t(`cfg-product`,e.filters?.[0]?.usbProductId==null?``:e.filters[0].usbProductId.toString(16)),t(`cfg-delim`,e.delimiter??`\\n`),t(`cfg-prepend`,e.prepend??``),t(`cfg-append`,e.append??``),t(`cfg-hs-cmd`,e.hsCmd??``),t(`cfg-hs-cmd-mode`,e.hsCmdMode??`text`),t(`cfg-hs-expect`,e.hsExpect??``),t(`cfg-hs-expect-mode`,e.hsExpectMode??`text`)}function N(e){let t=e.replace(/[^a-zA-Z0-9_$]/g,``).replace(/^[^a-zA-Z_$]/,`C`);return t.charAt(0).toUpperCase()+t.slice(1)||`MyDevice`}function P(e){return Array.from(e).map(e=>e.toString(16).toUpperCase().padStart(2,`0`)).join(` `)}function F(e){let t=e.replace(/\s+/g,``);if(t.length%2!=0)throw Error(`Odd number of hex characters.`);let n=new Uint8Array(t.length/2);for(let e=0;e<t.length;e+=2)n[e/2]=parseInt(t.substring(e,e+2),16);return n}function I(e,t){[ae,oe].forEach(t=>{t&&(t.className=`status-dot`,e!==`disconnected`&&t.classList.add(e))}),C&&(C.textContent=t),w&&(w.textContent=t)}var L=null;function R(){L&&clearTimeout(L),L=setTimeout(()=>{let e=M(),t=N((g(`dl-name`)?.value??`MySerialDevice`).trim()),n=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,i=n?`ts`:`js`;f(D,r(e,t,n,p,m)),O&&(O.textContent=`${t.substring(0,10).toLowerCase()}.${i}`)},180)}var z=l();k&&(k.textContent=z===`dark`?`☀️`:`🌙`),se?.addEventListener(`click`,()=>T.classList.toggle(`collapsed`)),ce?.addEventListener(`click`,()=>E.classList.toggle(`collapsed`));var B=g(`resize-handle`);if(B){let e=0,t=0,n=n=>{let r=Math.max(180,Math.min(700,t+(e-n.clientX)));document.documentElement.style.setProperty(`--code-w`,`${r}px`)},r=()=>{B.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};B.addEventListener(`pointerdown`,i=>{e=i.clientX,t=E.getBoundingClientRect().width,B.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}var V=g(`sidebar-resize-handle`);if(V){let e=0,t=0,n=n=>{let r=Math.max(200,Math.min(600,t+(n.clientX-e)));document.documentElement.style.setProperty(`--sidebar-w`,`${r}px`)},r=()=>{V.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};V.addEventListener(`pointerdown`,i=>{e=i.clientX,t=T.getBoundingClientRect().width,V.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}window.innerWidth<=960&&E.classList.add(`collapsed`),window.innerWidth<=640&&T.classList.add(`collapsed`),k?.addEventListener(`click`,()=>{let e=ie();k&&(k.textContent=e===`dark`?`☀️`:`🌙`)}),le?.addEventListener(`click`,()=>c(_)),A?.addEventListener(`click`,async()=>{let e=D?.textContent??``;try{await navigator.clipboard.writeText(e),A&&(A.textContent=`Copied!`,A.classList.add(`copied`),setTimeout(()=>{A.textContent=`Copy`,A.classList.remove(`copied`)},1500))}catch{}}),ue?.addEventListener(`click`,()=>{let n=M(),i=(g(`dl-name`)?.value??`my-device`).trim(),a=N(i),s=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,c=document.querySelector(`input[name='dl-type']:checked`)?.value??`file`,l=s?`ts`:`js`,u=r(n,a,s,p,m);c===`project`?ne([{name:`device.${l}`,content:u},{name:`package.json`,content:ee(i,s,`serial`)},{name:`index.html`,content:te(a,l,`Web Serial`)},{name:`README.md`,content:o(a,l,`Web Serial`,`Requires a Chromium browser with Web Serial API support.`)},...s?[{name:`tsconfig.json`,content:e()}]:[]],`${i}-project`):t(`${i}.${l}`,u)}),de?.addEventListener(`click`,()=>{let e=(g(`dl-name`)?.value??`my-device`).trim(),t=N(e);s({$version:1,provider:`serial`,className:e,language:document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`,dlType:document.querySelector(`input[name='dl-type']:checked`)?.value??`file`,cfg:M(),commands:p,listeners:m},t+`-config`)}),j?.addEventListener(`change`,()=>{let e=j.files?.[0];if(!e)return;let t=new FileReader;t.onload=e=>{try{let t=JSON.parse(e.target?.result);if(t.$version!==1||t.provider!==`serial`)return;let n=g(`dl-name`);n&&(n.value=t.className),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>{e.checked=e.value===t.language}),document.querySelectorAll(`input[name='dl-type']`).forEach(e=>{e.checked=e.value===t.dlType}),fe(t.cfg),p=t.commands.map(e=>({...e,id:crypto.randomUUID()})),m=t.listeners.map(e=>({...e,id:crypto.randomUUID()})),Q(),$(),R()}catch{}j.value=``},t.readAsText(e)}),[`cfg-baud`,`cfg-databits`,`cfg-stopbits`,`cfg-parity`,`cfg-flow`,`cfg-bufsize`,`cfg-timeout`,`cfg-handshake`,`cfg-reconnect-ms`,`cfg-autoreconnect`,`cfg-vendor`,`cfg-product`,`cfg-delim`,`cfg-prepend`,`cfg-append`,`cfg-hs-cmd`,`cfg-hs-cmd-mode`,`cfg-hs-expect`,`cfg-hs-expect-mode`,`dl-name`].forEach(e=>{let t=document.getElementById(e);t?.addEventListener(`change`,R),t?.addEventListener(`input`,R)}),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>e.addEventListener(`change`,R)),R();var H=`text`;S?.addEventListener(`click`,()=>{H=H===`text`?`hex`:`text`,S.textContent=H===`text`?`TXT`:`HEX`,x.placeholder=H===`text`?`Type a command, e.g. LED_ON`:`Hex bytes, e.g. FF 01 A3`});var U=null;function W(e){b.disabled=!e,x.disabled=!e,y.disabled=!e,v.disabled=e}v?.addEventListener(`click`,async()=>{if(U){try{await U.disconnect()}catch{}U=null}let e=M(),t=u(e.delimiter);U=new h({baudRate:e.baudRate,dataBits:e.dataBits,stopBits:e.stopBits,parity:e.parity,flowControl:e.flowControl,bufferSize:e.bufferSize,commandTimeout:e.commandTimeout,parser:n(t),autoReconnect:e.autoReconnect,autoReconnectInterval:e.autoReconnectInterval,handshakeTimeout:e.handshakeTimeout,filters:e.filters??[]},e.hsCmd,e.hsCmdMode,e.hsExpect,e.hsExpectMode),U.on(`serial:connecting`,()=>{I(`connecting`,`Connecting…`),v.disabled=!0,d(_,`Connecting to serial device…`,{kind:`system`})}),U.on(`serial:connected`,()=>{I(`connected`,`Connected`),W(!0),d(_,`Connected via Web Serial!`,{kind:`system`})}),U.on(`serial:disconnected`,()=>{I(`disconnected`,`Disconnected`),W(!1),d(_,`Disconnected.`,{kind:`system`}),U=null}),U.on(`serial:data`,e=>{d(_,String(e),{kind:`received`,label:`Device`})}),U.on(`serial:error`,e=>{I(`error`,`Error`),d(_,`Error: ${e.message}`,{kind:`error`}),v.disabled=!1}),U.on(`serial:need-permission`,()=>{I(`error`,`Permission denied`),d(_,`Permission denied — select a port and allow access.`,{kind:`error`}),v.disabled=!1}),U.on(`serial:timeout`,e=>{d(_,`Timeout: ${P(e)}`,{kind:`error`})}),U.on(`serial:reconnecting`,()=>{I(`connecting`,`Reconnecting…`),d(_,`Auto-reconnecting…`,{kind:`system`})});try{await U.connect()}catch{}}),y?.addEventListener(`click`,async()=>{await U?.disconnect()});async function G(){let e=x.value.trim();if(!e||!U)return;let t=M(),n=t.append?u(t.append):u(t.delimiter);try{if(H===`hex`){let t=F(e);d(_,`HEX: ${P(t)}`,{kind:`sent`,label:`You`}),await U.send(t)}else{let r=t.prepend+e+n;d(_,e,{kind:`sent`,label:`You`}),await U.send(r)}x.value=``,x.focus()}catch(e){d(_,`Send error: ${e instanceof Error?e.message:String(e)}`,{kind:`error`})}}b?.addEventListener(`click`,G),x?.addEventListener(`keydown`,e=>{e.key===`Enter`&&G()});var K=g(`cmd-name`),q=g(`cmd-value`),pe=g(`cmd-mode`),me=g(`cmd-add`),J=g(`cmd-list`),Y=g(`lst-name`),X=g(`lst-pattern`),he=g(`lst-match`),ge=g(`lst-add`),Z=g(`lst-list`);function Q(){if(J){J.innerHTML=``;for(let e of p){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.mode.toUpperCase();let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.value;let a=document.createElement(`button`);a.className=`chip-send`,a.title=`Send now`,a.textContent=`▶`,a.addEventListener(`click`,()=>{if(U)if(e.mode===`hex`){let t=F(e.value);U.send(t).catch(()=>{}),d(_,`HEX: ${P(t)}`,{kind:`sent`,label:`You`})}else{let t=M(),n=t.append?u(t.append):u(t.delimiter);U.send(t.prepend+e.value+n).catch(()=>{}),d(_,e.name,{kind:`sent`,label:`You`})}});let o=document.createElement(`button`);o.className=`chip-del`,o.title=`Remove`,o.textContent=`×`,o.addEventListener(`click`,()=>{p=p.filter(t=>t.id!==e.id),Q(),R()}),t.append(n,r,i,a,o),J.appendChild(t)}}}function $(){if(Z){Z.innerHTML=``;for(let e of m){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.match;let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.pattern;let a=document.createElement(`button`);a.className=`chip-del`,a.title=`Remove`,a.textContent=`×`,a.addEventListener(`click`,()=>{m=m.filter(t=>t.id!==e.id),$(),R()}),t.append(n,r,i,a),Z.appendChild(t)}}}me?.addEventListener(`click`,()=>{let e=K?.value.trim(),t=q?.value.trim();if(!e||!t)return;let n=pe?.value??`text`;p.push({id:crypto.randomUUID(),name:e,value:t,mode:n}),K&&(K.value=``),q&&(q.value=``),Q(),R()}),ge?.addEventListener(`click`,()=>{let e=Y?.value.trim(),t=X?.value.trim();if(!e||!t)return;let n=he?.value??`exact`;m.push({id:crypto.randomUUID(),name:e,pattern:t,match:n}),Y&&(Y.value=``),X&&(X.value=``),$(),R()}),d(_,`Web Serial demo ready — configure settings and click Connect.`,{kind:`system`}),i(_),a();
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{_ as e,a as t,b as n,d as r,f as i,g as a,h as o,i as s,l as ee,m as te,n as ne,o as re,p as c,r as l,t as u,v as ie,x as ae,y as oe}from"./demo-shared-DnvFynUr.js";var se=32,ce=34,d=0,le=30,ue=3,de=7,fe=1,pe=0,me=771,he=768,f=255,ge=8,p=`none`,m=1,_e=[16,8,7,6,5],ve=[1,2],ye=[`none`,`even`,`odd`],be=[`none`,`odd`,`even`],xe=[1,1.5,2],h={usbControlInterfaceClass:2,usbTransferInterfaceClass:10,protocol:void 0};function g(e,t){let n=e.configurations[0];if(!n)return null;for(let e of n.interfaces)if(e.alternates[0]?.interfaceClass===t)return e;return null}function _(e,t){let n=e.configurations[0];if(!n)return null;for(let e of n.interfaces){let n=e.alternates[0];if(!n||n.interfaceClass!==t)continue;let r=n.endpoints.some(e=>e.direction===`in`),i=n.endpoints.some(e=>e.direction===`out`);if(r&&i)return e}return null}function v(e,t){let n=e.alternates[0];if(n){for(let e of n.endpoints)if(e.direction===t)return e}throw TypeError(`Interface ${e.interfaceNumber} does not have an ${t} endpoint.`)}function Se(e,t){return t===2?`cdc_acm`:e.vendorId===4292?`cp210x`:`none`}var Ce=class{device_;endpoint_;onError_;constructor(e,t,n){this.device_=e,this.endpoint_=t,this.onError_=n}pull(e){(async()=>{let t=this.endpoint_.packetSize;try{let n=await this.device_.transferIn(this.endpoint_.endpointNumber,t);if(n.status!==`ok`){e.error(`USB error: ${n.status}`),this.onError_();return}if(n.data?.buffer&&n.data.byteLength>0){let t=new Uint8Array(n.data.buffer,n.data.byteOffset,n.data.byteLength);t.length>0&&e.enqueue(t)}}catch(t){e.error(String(t)),this.onError_()}})()}},we=class{device_;endpoint_;onError_;constructor(e,t,n){this.device_=e,this.endpoint_=t,this.onError_=n}async write(e,t){try{let n=await this.device_.transferOut(this.endpoint_.endpointNumber,e.buffer);n.status!==`ok`&&(t.error(n.status),this.onError_())}catch(e){t.error(String(e)),this.onError_()}}},y=class{device_;protocol_;controlInterface_;transferInterface_;inEndpoint_;outEndpoint_;serialOptions_;readable_=null;writable_=null;cdcOutputSignals_={dataTerminalReady:!1,requestToSend:!1,break:!1};constructor(e,t){this.device_=e;let n={...h,...t};this.protocol_=n.protocol??Se(e,n.usbControlInterfaceClass);let r=n.usbControlInterfaceClass,i=n.usbTransferInterfaceClass;if(r===i){let t=_(e,i);if(!t)throw TypeError(`Unable to find interface with class ${i} that has both IN and OUT endpoints.`);this.controlInterface_=t,this.transferInterface_=t}else{let t=g(e,r);if(!t)throw TypeError(`Unable to find control interface with class ${r}.`);let n=_(e,i)??g(e,i);if(!n)throw TypeError(`Unable to find transfer interface with class ${i}.`);this.controlInterface_=t,this.transferInterface_=n}this.inEndpoint_=v(this.transferInterface_,`in`),this.outEndpoint_=v(this.transferInterface_,`out`)}get readable(){return!this.readable_&&this.device_.opened&&(this.readable_=new ReadableStream(new Ce(this.device_,this.inEndpoint_,()=>{this.readable_=null}),{highWaterMark:this.serialOptions_?.bufferSize??f})),this.readable_}get writable(){return!this.writable_&&this.device_.opened&&(this.writable_=new WritableStream(new we(this.device_,this.outEndpoint_,()=>{this.writable_=null}),new ByteLengthQueuingStrategy({highWaterMark:this.serialOptions_?.bufferSize??f}))),this.writable_}async open(e){this.serialOptions_=e,this.validateOptions();try{switch(await this.device_.open(),this.device_.configuration===null&&await this.device_.selectConfiguration(1),await this.device_.claimInterface(this.controlInterface_.interfaceNumber),this.controlInterface_!==this.transferInterface_&&await this.device_.claimInterface(this.transferInterface_.interfaceNumber),this.protocol_){case`cdc_acm`:await this.cdcInit();break;case`cp210x`:await this.cp210xInit();break;case`none`:break}}catch(e){throw this.device_.opened&&await this.device_.close(),Error(`Error setting up device: `+(e instanceof Error?e.message:String(e)),{cause:e})}}async close(){let e=[];if(this.readable_&&e.push(this.readable_.cancel()),this.writable_&&e.push(this.writable_.abort()),await Promise.all(e),this.readable_=null,this.writable_=null,this.device_.opened){switch(this.protocol_){case`cdc_acm`:await this.cdcSetSignals({dataTerminalReady:!1,requestToSend:!1});break;case`cp210x`:await this.cp210xDeinit();break}await this.device_.close()}}async forget(){return this.device_.forget()}getInfo(){return{usbVendorId:this.device_.vendorId,usbProductId:this.device_.productId}}async cdcInit(){await this.cdcSetLineCoding(),await this.cdcSetSignals({dataTerminalReady:!0})}async cdcSetSignals(e){if(this.cdcOutputSignals_={...this.cdcOutputSignals_,...e},e.dataTerminalReady!==void 0||e.requestToSend!==void 0){let e=(this.cdcOutputSignals_.dataTerminalReady?1:0)|(this.cdcOutputSignals_.requestToSend?2:0);await this.device_.controlTransferOut({requestType:`class`,recipient:`interface`,request:ce,value:e,index:this.controlInterface_.interfaceNumber})}}async cdcSetLineCoding(){let e=new ArrayBuffer(7),t=new DataView(e);if(t.setUint32(0,this.serialOptions_.baudRate,!0),t.setUint8(4,xe.indexOf(this.serialOptions_.stopBits??m)),t.setUint8(5,be.indexOf(this.serialOptions_.parity??p)),t.setUint8(6,this.serialOptions_.dataBits??ge),(await this.device_.controlTransferOut({requestType:`class`,recipient:`interface`,request:se,value:0,index:this.controlInterface_.interfaceNumber},e)).status!==`ok`)throw new DOMException(`Failed to set line coding.`,`NetworkError`)}async cp210xInit(){let e=this.controlInterface_.interfaceNumber;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:d,value:fe,index:e});let t=new ArrayBuffer(4);new DataView(t).setUint32(0,this.serialOptions_.baudRate,!0),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:le,value:0,index:e},t);let n=this.serialOptions_.dataBits??ge,r={none:0,odd:16,even:32}[this.serialOptions_.parity??p]??0,i=({1:0,2:2}[this.serialOptions_.stopBits??m]??0)<<8|r|n;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:ue,value:i,index:e}),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:de,value:me,index:e})}async cp210xDeinit(){let e=this.controlInterface_.interfaceNumber;await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:de,value:he,index:e}),await this.device_.controlTransferOut({requestType:`vendor`,recipient:`interface`,request:d,value:pe,index:e})}validateOptions(){if(this.serialOptions_.baudRate%1!=0)throw RangeError(`Invalid baud rate: ${this.serialOptions_.baudRate}`);if(this.serialOptions_.dataBits!==void 0&&!_e.includes(this.serialOptions_.dataBits))throw RangeError(`Invalid dataBits: ${this.serialOptions_.dataBits}`);if(this.serialOptions_.stopBits!==void 0&&!ve.includes(this.serialOptions_.stopBits))throw RangeError(`Invalid stopBits: ${this.serialOptions_.stopBits}`);if(this.serialOptions_.parity!==void 0&&!ye.includes(this.serialOptions_.parity))throw RangeError(`Invalid parity: ${this.serialOptions_.parity}`)}},Te=class{options_;constructor(e){this.options_={...h,...e}}async requestPort(e,t){let n={...this.options_,...t},r=[];if(e?.filters&&e.filters.length>0)for(let t of e.filters){let e={};t.usbVendorId!==void 0&&(e.vendorId=t.usbVendorId),t.usbProductId!==void 0&&(e.productId=t.usbProductId),n.usbControlInterfaceClass!==void 0&&n.usbControlInterfaceClass!==255?e.classCode=n.usbControlInterfaceClass:e.vendorId===void 0&&e.productId===void 0&&(e.classCode=n.usbControlInterfaceClass??2),r.push(e)}else r.push({classCode:n.usbControlInterfaceClass??2});return new y(await navigator.usb.requestDevice({filters:r}),n)}async getPorts(e){let t={...this.options_,...e},n=await navigator.usb.getDevices(),r=[];for(let e of n)try{let n=new y(e,t);r.push(n)}catch{}return r}},b=[],x=[],Ee=class extends ae{_hsCmd;_hsCmdMode;_hsExpect;_hsExpectMode;constructor(e,t,n,r,i){super(e),this._hsCmd=t,this._hsCmdMode=n,this._hsExpect=r,this._hsExpectMode=i}async handshake(){if(!this._hsCmd||(this._hsCmdMode===`hex`?await this.send(L(this._hsCmd)):await this.send(l(this._hsCmd)),!this._hsExpect))return!0;let e=this._hsExpect.trim();return new Promise(t=>{let n=r=>{if(this.off(`serial:data`,n),this._hsExpectMode===`hex`){let e=new TextEncoder().encode(String(r)),n=L(this._hsExpect);t(e.length===n.length&&e.every((e,t)=>e===n[t]))}else t(String(r).trim()===e)};this.on(`serial:data`,n)})}},S=e=>document.getElementById(e),C=S(`messages`),w=S(`btn-connect`),De=S(`btn-disconnect`),Oe=S(`btn-send`),T=S(`input-send`),ke=S(`mode-toggle`),Ae=S(`status-dot`),je=S(`status-text`),Me=S(`console-dot`),E=S(`console-text`),D=S(`sidebar`),O=S(`code-panel`),k=S(`code-view`),A=S(`code-tab`),Ne=S(`menu-btn`),Pe=S(`code-toggle-btn`),j=S(`theme-btn`),Fe=S(`clear-btn`),M=S(`copy-btn`),Ie=S(`dl-btn`),Le=S(`cfg-export-btn`),N=S(`cfg-import-input`);function P(){let e=e=>(S(e)?.value??``).trim(),t=(t,n)=>parseInt(e(t))||n,n=e=>S(e)?.value??``,r=e=>S(e)?.checked??!1,i=e(`cfg-vendor`),a=e(`cfg-product`),o=[];if(i||a){let e={};i&&(e.usbVendorId=parseInt(i,16)),a&&(e.usbProductId=parseInt(a,16)),o.push(e)}return{usbControlInterfaceClass:t(`cfg-ctrl-class`,255),usbTransferInterfaceClass:t(`cfg-xfer-class`,255),baudRate:t(`cfg-baud`,9600),dataBits:t(`cfg-databits`,8),stopBits:t(`cfg-stopbits`,1),parity:n(`cfg-parity`)||`none`,flowControl:n(`cfg-flow`)||`none`,bufferSize:t(`cfg-bufsize`,255),commandTimeout:t(`cfg-timeout`,3e3),autoReconnect:r(`cfg-autoreconnect`),autoReconnectInterval:t(`cfg-reconnect-ms`,1500),handshakeTimeout:t(`cfg-handshake`,2e3),delimiter:e(`cfg-delim`)||`\\n`,prepend:e(`cfg-prepend`),append:e(`cfg-append`),hsCmd:e(`cfg-hs-cmd`),hsCmdMode:n(`cfg-hs-cmd-mode`)||`text`,hsExpect:e(`cfg-hs-expect`),hsExpectMode:n(`cfg-hs-expect-mode`)||`text`,filters:o}}function Re(e){let t=(e,t)=>{let n=document.getElementById(e);n&&(n instanceof HTMLInputElement&&n.type===`checkbox`?n.checked=!!t:(n instanceof HTMLInputElement||n instanceof HTMLSelectElement)&&(n.value=String(t??``)))};t(`cfg-ctrl-class`,e.usbControlInterfaceClass??255),t(`cfg-xfer-class`,e.usbTransferInterfaceClass??255),t(`cfg-baud`,e.baudRate??9600),t(`cfg-databits`,e.dataBits??8),t(`cfg-stopbits`,e.stopBits??1),t(`cfg-parity`,e.parity??`none`),t(`cfg-flow`,e.flowControl??`none`),t(`cfg-bufsize`,e.bufferSize??255),t(`cfg-timeout`,e.commandTimeout??3e3),t(`cfg-handshake`,e.handshakeTimeout??2e3),t(`cfg-reconnect-ms`,e.autoReconnectInterval??1500),t(`cfg-autoreconnect`,e.autoReconnect??!1),t(`cfg-vendor`,e.filters?.[0]?.usbVendorId==null?``:e.filters[0].usbVendorId.toString(16)),t(`cfg-product`,e.filters?.[0]?.usbProductId==null?``:e.filters[0].usbProductId.toString(16)),t(`cfg-delim`,e.delimiter??`\\n`),t(`cfg-prepend`,e.prepend??``),t(`cfg-append`,e.append??``),t(`cfg-hs-cmd`,e.hsCmd??``),t(`cfg-hs-cmd-mode`,e.hsCmdMode??`text`),t(`cfg-hs-expect`,e.hsExpect??``),t(`cfg-hs-expect-mode`,e.hsExpectMode??`text`)}function F(e){let t=e.replace(/[^a-zA-Z0-9_$]/g,``).replace(/^[^a-zA-Z_$]/,`C`);return t.charAt(0).toUpperCase()+t.slice(1)||`MyDevice`}function I(e){return Array.from(e).map(e=>e.toString(16).toUpperCase().padStart(2,`0`)).join(` `)}function L(e){let t=e.replace(/\s+/g,``);if(t.length%2!=0)throw Error(`Odd number of hex characters.`);let n=new Uint8Array(t.length/2);for(let e=0;e<t.length;e+=2)n[e/2]=parseInt(t.substring(e,e+2),16);return n}function R(e,t){[Ae,Me].forEach(t=>{t&&(t.className=`status-dot`,e!==`disconnected`&&t.classList.add(e))}),je&&(je.textContent=t),E&&(E.textContent=t)}var z=null;function B(){z&&clearTimeout(z),z=setTimeout(()=>{let e=P(),t=F((S(`dl-name`)?.value??`MyUsbDevice`).trim()),n=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,r=n?`ts`:`js`;ie(k,ee(e,t,n,b,x)),A&&(A.textContent=`${t.substring(0,10).toLowerCase()}.${r}`)},180)}var ze=c();j&&(j.textContent=ze===`dark`?`☀️`:`🌙`),Ne?.addEventListener(`click`,()=>D.classList.toggle(`collapsed`)),Pe?.addEventListener(`click`,()=>O.classList.toggle(`collapsed`));var V=S(`resize-handle`);if(V){let e=0,t=0,n=n=>{let r=Math.max(180,Math.min(700,t+(e-n.clientX)));document.documentElement.style.setProperty(`--code-w`,`${r}px`)},r=()=>{V.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};V.addEventListener(`pointerdown`,i=>{e=i.clientX,t=O.getBoundingClientRect().width,V.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}var H=S(`sidebar-resize-handle`);if(H){let e=0,t=0,n=n=>{let r=Math.max(200,Math.min(600,t+(n.clientX-e)));document.documentElement.style.setProperty(`--sidebar-w`,`${r}px`)},r=()=>{H.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};H.addEventListener(`pointerdown`,i=>{e=i.clientX,t=D.getBoundingClientRect().width,H.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}window.innerWidth<=960&&O.classList.add(`collapsed`),window.innerWidth<=640&&D.classList.add(`collapsed`),j?.addEventListener(`click`,()=>{let e=oe();j&&(j.textContent=e===`dark`?`☀️`:`🌙`)}),Fe?.addEventListener(`click`,()=>ne(C)),M?.addEventListener(`click`,async()=>{let e=k?.textContent??``;try{await navigator.clipboard.writeText(e),M&&(M.textContent=`Copied!`,M.classList.add(`copied`),setTimeout(()=>{M.textContent=`Copy`,M.classList.remove(`copied`)},1500))}catch{}}),Ie?.addEventListener(`click`,()=>{let n=P(),r=(S(`dl-name`)?.value??`my-usb-device`).trim(),i=F(r),s=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,ne=document.querySelector(`input[name='dl-type']:checked`)?.value??`file`,c=s?`ts`:`js`,l=ee(n,i,s,b,x);ne===`project`?re([{name:`device.${c}`,content:l},{name:`package.json`,content:o(r,s,`usb`)},{name:`index.html`,content:te(i,c,`WebUSB`)},{name:`README.md`,content:a(i,c,`WebUSB`,`Requires a Chromium browser. Common USB-to-serial chips: CP2102 (0x10c4/0xea60), CH340 (0x1a86/0x7523).`)},...s?[{name:`tsconfig.json`,content:e()}]:[]],`${r}-project`):t(`${r}.${c}`,l)}),Le?.addEventListener(`click`,()=>{let e=(S(`dl-name`)?.value??`my-usb-device`).trim(),t=F(e);s({$version:1,provider:`usb`,className:e,language:document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`,dlType:document.querySelector(`input[name='dl-type']:checked`)?.value??`file`,cfg:P(),commands:b,listeners:x},t+`-config`)}),N?.addEventListener(`change`,()=>{let e=N.files?.[0];if(!e)return;let t=new FileReader;t.onload=e=>{try{let t=JSON.parse(e.target?.result);if(t.$version!==1||t.provider!==`usb`)return;let n=S(`dl-name`);n&&(n.value=t.className),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>{e.checked=e.value===t.language}),document.querySelectorAll(`input[name='dl-type']`).forEach(e=>{e.checked=e.value===t.dlType}),Re(t.cfg),b=t.commands.map(e=>({...e,id:crypto.randomUUID()})),x=t.listeners.map(e=>({...e,id:crypto.randomUUID()})),Q(),$(),B()}catch{}N.value=``},t.readAsText(e)}),[`cfg-ctrl-class`,`cfg-xfer-class`,`cfg-baud`,`cfg-databits`,`cfg-stopbits`,`cfg-parity`,`cfg-flow`,`cfg-bufsize`,`cfg-timeout`,`cfg-handshake`,`cfg-reconnect-ms`,`cfg-autoreconnect`,`cfg-vendor`,`cfg-product`,`cfg-delim`,`cfg-prepend`,`cfg-append`,`cfg-hs-cmd`,`cfg-hs-cmd-mode`,`cfg-hs-expect`,`cfg-hs-expect-mode`,`dl-name`].forEach(e=>{let t=document.getElementById(e);t?.addEventListener(`change`,B),t?.addEventListener(`input`,B)}),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>e.addEventListener(`change`,B)),B();var U=`text`;ke?.addEventListener(`click`,()=>{U=U===`text`?`hex`:`text`,ke.textContent=U===`text`?`TXT`:`HEX`,T.placeholder=U===`text`?`Type a command, e.g. LED_ON`:`Hex bytes, e.g. FF 01 A3`});var W=null;function Be(e){Oe.disabled=!e,T.disabled=!e,De.disabled=!e,w.disabled=e}w?.addEventListener(`click`,async()=>{if(W){try{await W.disconnect()}catch{}W=null}let e=P();ae.setProvider(new Te({usbControlInterfaceClass:e.usbControlInterfaceClass,usbTransferInterfaceClass:e.usbTransferInterfaceClass}));let t=l(e.delimiter);W=new Ee({baudRate:e.baudRate,dataBits:e.dataBits,stopBits:e.stopBits,parity:e.parity,flowControl:e.flowControl,bufferSize:e.bufferSize,commandTimeout:e.commandTimeout,parser:n(t),autoReconnect:e.autoReconnect,autoReconnectInterval:e.autoReconnectInterval,handshakeTimeout:e.handshakeTimeout,filters:e.filters??[]},e.hsCmd,e.hsCmdMode,e.hsExpect,e.hsExpectMode),W.on(`serial:connecting`,()=>{R(`connecting`,`Connecting…`),w.disabled=!0,u(C,`Connecting via WebUSB…`,{kind:`system`})}),W.on(`serial:connected`,()=>{R(`connected`,`Connected`),Be(!0),u(C,`Connected via WebUSB!`,{kind:`system`})}),W.on(`serial:disconnected`,()=>{R(`disconnected`,`Disconnected`),Be(!1),u(C,`Disconnected.`,{kind:`system`}),W=null}),W.on(`serial:data`,e=>{u(C,String(e),{kind:`received`,label:`Device`})}),W.on(`serial:error`,e=>{R(`error`,`Error`),u(C,`Error: ${e.message}`,{kind:`error`}),w.disabled=!1}),W.on(`serial:need-permission`,()=>{R(`error`,`Permission denied`),u(C,`Permission denied — allow access to the USB device.`,{kind:`error`}),w.disabled=!1}),W.on(`serial:timeout`,e=>{u(C,`Timeout: ${I(e)}`,{kind:`error`})}),W.on(`serial:reconnecting`,()=>{R(`connecting`,`Reconnecting…`),u(C,`Auto-reconnecting via WebUSB…`,{kind:`system`})});try{await W.connect()}catch{}}),De?.addEventListener(`click`,async()=>{await W?.disconnect()});async function G(){let e=T.value.trim();if(!e||!W)return;let t=P(),n=t.append?l(t.append):l(t.delimiter);try{if(U===`hex`){let t=L(e);u(C,`HEX: ${I(t)}`,{kind:`sent`,label:`You`}),await W.send(t)}else{let r=t.prepend+e+n;u(C,e,{kind:`sent`,label:`You`}),await W.send(r)}T.value=``,T.focus()}catch(e){u(C,`Send error: ${e instanceof Error?e.message:String(e)}`,{kind:`error`})}}Oe?.addEventListener(`click`,G),T?.addEventListener(`keydown`,e=>{e.key===`Enter`&&G()});var K=S(`cmd-name`),q=S(`cmd-value`),Ve=S(`cmd-mode`),He=S(`cmd-add`),J=S(`cmd-list`),Y=S(`lst-name`),X=S(`lst-pattern`),Ue=S(`lst-match`),We=S(`lst-add`),Z=S(`lst-list`);function Q(){if(J){J.innerHTML=``;for(let e of b){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.mode.toUpperCase();let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.value;let a=document.createElement(`button`);a.className=`chip-send`,a.title=`Send now`,a.textContent=`▶`,a.addEventListener(`click`,()=>{if(W)if(e.mode===`hex`){let t=L(e.value);W.send(t).catch(()=>{}),u(C,`HEX: ${I(t)}`,{kind:`sent`,label:`You`})}else{let t=P(),n=t.append?l(t.append):l(t.delimiter);W.send(t.prepend+e.value+n).catch(()=>{}),u(C,e.name,{kind:`sent`,label:`You`})}});let o=document.createElement(`button`);o.className=`chip-del`,o.title=`Remove`,o.textContent=`×`,o.addEventListener(`click`,()=>{b=b.filter(t=>t.id!==e.id),Q(),B()}),t.append(n,r,i,a,o),J.appendChild(t)}}}function $(){if(Z){Z.innerHTML=``;for(let e of x){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.match;let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.pattern;let a=document.createElement(`button`);a.className=`chip-del`,a.title=`Remove`,a.textContent=`×`,a.addEventListener(`click`,()=>{x=x.filter(t=>t.id!==e.id),$(),B()}),t.append(n,r,i,a),Z.appendChild(t)}}}He?.addEventListener(`click`,()=>{let e=K?.value.trim(),t=q?.value.trim();!e||!t||(b.push({id:crypto.randomUUID(),name:e,value:t,mode:Ve?.value??`text`}),K&&(K.value=``),q&&(q.value=``),Q(),B())}),We?.addEventListener(`click`,()=>{let e=Y?.value.trim(),t=X?.value.trim();!e||!t||(x.push({id:crypto.randomUUID(),name:e,pattern:t,match:Ue?.value??`exact`}),Y&&(Y.value=``),X&&(X.value=``),$(),B())}),u(C,`WebUSB demo ready — CP2102/ESP32 (0x10c4/0xea60), CH340/Arduino (0x1a86/0x7523). Configure and click Connect.`,{kind:`system`}),r(C),i();
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{_ as e,a as t,b as n,d as r,f as i,g as a,h as o,i as s,m as c,n as l,o as ee,p as u,r as d,t as f,u as p,v as te,x as m,y as ne}from"./demo-shared-DnvFynUr.js";function re(e){return new Promise((t,n)=>{e.addEventListener(`open`,()=>t(),{once:!0}),e.addEventListener(`error`,e=>n(e),{once:!0})})}function h(e,t){return new Promise(n=>{let r=i=>{let a=JSON.parse(i.data);a.type===t&&(e.removeEventListener(`message`,r),n(a.payload))};e.addEventListener(`message`,r)})}function g(e,t){let n=null,r=null;return{get readable(){return n},get writable(){return r},getInfo(){return{usbVendorId:t.vendorId,usbProductId:t.productId}},async open(i){e.send(JSON.stringify({type:`open`,path:t.path,baudRate:i.baudRate,dataBits:i.dataBits,stopBits:i.stopBits,parity:i.parity,parser:{type:`delimiter`,value:`\\n`}})),await h(e,`opened`);let a=[],o=null,s=!1;function c(e){let t=JSON.parse(e.data);if(t.type===`data`&&t.bytes){let e=new Uint8Array(t.bytes);o?o.enqueue(e):a.push(e)}t.type===`closed`&&(s=!0,o&&o.close())}e.addEventListener(`message`,c),n=new ReadableStream({start(e){o=e;for(let t of a)e.enqueue(t);a.length=0,s&&e.close()},cancel(){e.removeEventListener(`message`,c),o=null}}),r=new WritableStream({write(t){e.send(JSON.stringify({type:`write`,bytes:Array.from(t)}))}})},async close(){e.send(JSON.stringify({type:`close`})),n=null,r=null,e.close()}}}function ie(e){return{async requestPort(t){let n=new WebSocket(e);await re(n),n.send(JSON.stringify({type:`list-ports`,filters:t?.filters??[]}));let r=(await h(n,`port-list`))[0];if(!r)throw Error(`No ports available on the bridge server. Make sure the Node.js server is running and a device is connected.`);return g(n,r)},async getPorts(){let t=new WebSocket(e);return await re(t),t.send(JSON.stringify({type:`list-ports`,filters:[]})),(await h(t,`port-list`)).map(e=>g(t,e))}}}var _=[],v=[],ae=class extends m{_hsCmd;_hsCmdMode;_hsExpect;_hsExpectMode;constructor(e,t,n,r,i){super(e),this._hsCmd=t,this._hsCmdMode=n,this._hsExpect=r,this._hsExpectMode=i}async handshake(){if(!this._hsCmd||(this._hsCmdMode===`hex`?await this.send(I(this._hsCmd)):await this.send(d(this._hsCmd)),!this._hsExpect))return!0;let e=this._hsExpect.trim();return new Promise(t=>{let n=r=>{if(this.off(`serial:data`,n),this._hsExpectMode===`hex`){let e=new TextEncoder().encode(String(r)),n=I(this._hsExpect);t(e.length===n.length&&e.every((e,t)=>e===n[t]))}else t(String(r).trim()===e)};this.on(`serial:data`,n)})}},y=e=>document.getElementById(e),b=y(`messages`),x=y(`btn-connect`),S=y(`btn-disconnect`),C=y(`btn-send`),w=y(`input-send`),T=y(`mode-toggle`),oe=y(`status-dot`),E=y(`status-text`),se=y(`console-dot`),D=y(`console-text`),O=y(`sidebar`),k=y(`code-panel`),ce=y(`code-view`),le=y(`code-tab`),ue=y(`menu-btn`),de=y(`code-toggle-btn`),A=y(`theme-btn`),fe=y(`clear-btn`),j=y(`copy-btn`),pe=y(`dl-btn`),me=y(`cfg-export-btn`),M=y(`cfg-import-input`);function N(){let e=e=>(y(e)?.value??``).trim(),t=(t,n)=>parseInt(e(t))||n,n=e=>y(e)?.value??``;return{wsUrl:e(`cfg-wsurl`)||`ws://localhost:8080`,baudRate:t(`cfg-baud`,9600),dataBits:t(`cfg-databits`,8),stopBits:t(`cfg-stopbits`,1),parity:n(`cfg-parity`)||`none`,flowControl:n(`cfg-flow`)||`none`,bufferSize:t(`cfg-bufsize`,255),commandTimeout:t(`cfg-timeout`,3e3),autoReconnect:(e=>y(e)?.checked??!1)(`cfg-autoreconnect`),autoReconnectInterval:t(`cfg-reconnect-ms`,1500),handshakeTimeout:t(`cfg-handshake`,2e3),delimiter:e(`cfg-delim`)||`\\n`,prepend:e(`cfg-prepend`),append:e(`cfg-append`),hsCmd:e(`cfg-hs-cmd`),hsCmdMode:n(`cfg-hs-cmd-mode`)||`text`,hsExpect:e(`cfg-hs-expect`),hsExpectMode:n(`cfg-hs-expect-mode`)||`text`}}function he(e){let t=(e,t)=>{let n=document.getElementById(e);n&&(n instanceof HTMLInputElement&&n.type===`checkbox`?n.checked=!!t:(n instanceof HTMLInputElement||n instanceof HTMLSelectElement)&&(n.value=String(t??``)))};t(`cfg-wsurl`,e.wsUrl??`ws://localhost:8080`),t(`cfg-baud`,e.baudRate??9600),t(`cfg-databits`,e.dataBits??8),t(`cfg-stopbits`,e.stopBits??1),t(`cfg-parity`,e.parity??`none`),t(`cfg-flow`,e.flowControl??`none`),t(`cfg-bufsize`,e.bufferSize??255),t(`cfg-timeout`,e.commandTimeout??3e3),t(`cfg-handshake`,e.handshakeTimeout??2e3),t(`cfg-reconnect-ms`,e.autoReconnectInterval??1500),t(`cfg-autoreconnect`,e.autoReconnect??!1),t(`cfg-delim`,e.delimiter??`\\n`),t(`cfg-prepend`,e.prepend??``),t(`cfg-append`,e.append??``),t(`cfg-hs-cmd`,e.hsCmd??``),t(`cfg-hs-cmd-mode`,e.hsCmdMode??`text`),t(`cfg-hs-expect`,e.hsExpect??``),t(`cfg-hs-expect-mode`,e.hsExpectMode??`text`)}function P(e){let t=e.replace(/[^a-zA-Z0-9_$]/g,``).replace(/^[^a-zA-Z_$]/,`C`);return t.charAt(0).toUpperCase()+t.slice(1)||`MyDevice`}function F(e){return Array.from(e).map(e=>e.toString(16).toUpperCase().padStart(2,`0`)).join(` `)}function I(e){let t=e.replace(/\s+/g,``);if(t.length%2!=0)throw Error(`Odd number of hex characters.`);let n=new Uint8Array(t.length/2);for(let e=0;e<t.length;e+=2)n[e/2]=parseInt(t.substring(e,e+2),16);return n}function L(e,t){[oe,se].forEach(t=>{t&&(t.className=`status-dot`,e!==`disconnected`&&t.classList.add(e))}),E&&(E.textContent=t),D&&(D.textContent=t)}var R=null;function z(){R&&clearTimeout(R),R=setTimeout(()=>{let e=N(),t=P((y(`dl-name`)?.value??`MyWsDevice`).trim()),n=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,r=n?`ts`:`js`;te(ce,p(e,t,n,_,v)),le&&(le.textContent=`${t.substring(0,10).toLowerCase()}.${r}`)},180)}var ge=u();A&&(A.textContent=ge===`dark`?`☀️`:`🌙`),ue?.addEventListener(`click`,()=>O.classList.toggle(`collapsed`)),de?.addEventListener(`click`,()=>k.classList.toggle(`collapsed`));var B=y(`resize-handle`);if(B){let e=0,t=0,n=n=>{let r=Math.max(180,Math.min(700,t+(e-n.clientX)));document.documentElement.style.setProperty(`--code-w`,`${r}px`)},r=()=>{B.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};B.addEventListener(`pointerdown`,i=>{e=i.clientX,t=k.getBoundingClientRect().width,B.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}var V=y(`sidebar-resize-handle`);if(V){let e=0,t=0,n=n=>{let r=Math.max(200,Math.min(600,t+(n.clientX-e)));document.documentElement.style.setProperty(`--sidebar-w`,`${r}px`)},r=()=>{V.classList.remove(`dragging`),document.removeEventListener(`pointermove`,n)};V.addEventListener(`pointerdown`,i=>{e=i.clientX,t=O.getBoundingClientRect().width,V.classList.add(`dragging`),document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r,{once:!0}),i.preventDefault()})}window.innerWidth<=960&&k.classList.add(`collapsed`),window.innerWidth<=640&&O.classList.add(`collapsed`),A?.addEventListener(`click`,()=>{let e=ne();A&&(A.textContent=e===`dark`?`☀️`:`🌙`)}),fe?.addEventListener(`click`,()=>l(b)),j?.addEventListener(`click`,async()=>{let e=ce?.textContent??``;try{await navigator.clipboard.writeText(e),j&&(j.textContent=`Copied!`,j.classList.add(`copied`),setTimeout(()=>{j.textContent=`Copy`,j.classList.remove(`copied`)},1500))}catch{}}),pe?.addEventListener(`click`,()=>{let n=N(),r=(y(`dl-name`)?.value??`my-ws-device`).trim(),i=P(r),s=(document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`)===`ts`,l=document.querySelector(`input[name='dl-type']:checked`)?.value??`file`,u=s?`ts`:`js`,d=p(n,i,s,_,v);l===`project`?ee([{name:`device.${u}`,content:d},{name:`package.json`,content:o(r,s,`ws`)},{name:`index.html`,content:c(i,u,`WebSocket`)},{name:`README.md`,content:a(i,u,`WebSocket`,"Requires a WebSocket-to-serial bridge. Start the bridge: `cd demos/websocket && node server.js`")},...s?[{name:`tsconfig.json`,content:e()}]:[]],`${r}-project`):t(`${r}.${u}`,d)}),me?.addEventListener(`click`,()=>{let e=(y(`dl-name`)?.value??`my-ws-device`).trim(),t=P(e);s({$version:1,provider:`ws`,className:e,language:document.querySelector(`input[name='dl-lang']:checked`)?.value??`ts`,dlType:document.querySelector(`input[name='dl-type']:checked`)?.value??`file`,cfg:N(),commands:_,listeners:v},t+`-config`)}),M?.addEventListener(`change`,()=>{let e=M.files?.[0];if(!e)return;let t=new FileReader;t.onload=e=>{try{let t=JSON.parse(e.target?.result);if(t.$version!==1||t.provider!==`ws`)return;let n=y(`dl-name`);n&&(n.value=t.className),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>{e.checked=e.value===t.language}),document.querySelectorAll(`input[name='dl-type']`).forEach(e=>{e.checked=e.value===t.dlType}),he(t.cfg),_=t.commands.map(e=>({...e,id:crypto.randomUUID()})),v=t.listeners.map(e=>({...e,id:crypto.randomUUID()})),Q(),$(),z()}catch{}M.value=``},t.readAsText(e)}),[`cfg-wsurl`,`cfg-baud`,`cfg-databits`,`cfg-stopbits`,`cfg-parity`,`cfg-flow`,`cfg-bufsize`,`cfg-timeout`,`cfg-handshake`,`cfg-reconnect-ms`,`cfg-autoreconnect`,`cfg-delim`,`cfg-prepend`,`cfg-append`,`cfg-hs-cmd`,`cfg-hs-cmd-mode`,`cfg-hs-expect`,`cfg-hs-expect-mode`,`dl-name`].forEach(e=>{let t=document.getElementById(e);t?.addEventListener(`change`,z),t?.addEventListener(`input`,z)}),document.querySelectorAll(`input[name='dl-lang']`).forEach(e=>e.addEventListener(`change`,z)),z();var H=`text`;T?.addEventListener(`click`,()=>{H=H===`text`?`hex`:`text`,T.textContent=H===`text`?`TXT`:`HEX`,w.placeholder=H===`text`?`Type a command, e.g. LED_ON`:`Hex bytes, e.g. FF 01 A3`});var U=null;function W(e){C.disabled=!e,w.disabled=!e,S.disabled=!e,x.disabled=e}x?.addEventListener(`click`,async()=>{if(U){try{await U.disconnect()}catch{}U=null}let e=N();m.setProvider(ie(e.wsUrl));let t=d(e.delimiter);U=new ae({baudRate:e.baudRate,dataBits:e.dataBits,stopBits:e.stopBits,parity:e.parity,flowControl:e.flowControl,bufferSize:e.bufferSize,commandTimeout:e.commandTimeout,parser:n(t),autoReconnect:e.autoReconnect,autoReconnectInterval:e.autoReconnectInterval,handshakeTimeout:e.handshakeTimeout},e.hsCmd,e.hsCmdMode,e.hsExpect,e.hsExpectMode),U.on(`serial:connecting`,()=>{L(`connecting`,`Connecting…`),x.disabled=!0,f(b,`Connecting to ${e.wsUrl}…`,{kind:`system`})}),U.on(`serial:connected`,()=>{L(`connected`,`Connected`),W(!0),f(b,`Connected via WebSocket (${e.wsUrl})!`,{kind:`system`})}),U.on(`serial:disconnected`,()=>{L(`disconnected`,`Disconnected`),W(!1),f(b,`Disconnected.`,{kind:`system`}),U=null}),U.on(`serial:data`,e=>{f(b,String(e),{kind:`received`,label:`Device`})}),U.on(`serial:error`,e=>{L(`error`,`Error`),f(b,`Error: ${e.message}`,{kind:`error`}),x.disabled=!1}),U.on(`serial:need-permission`,()=>{L(`error`,`Connection refused`),f(b,`Could not connect — is the WebSocket bridge running?`,{kind:`error`}),x.disabled=!1}),U.on(`serial:timeout`,e=>{f(b,`Timeout: ${F(e)}`,{kind:`error`})}),U.on(`serial:reconnecting`,()=>{L(`connecting`,`Reconnecting…`),f(b,`Auto-reconnecting via WebSocket…`,{kind:`system`})});try{await U.connect()}catch{}}),S?.addEventListener(`click`,async()=>{await U?.disconnect()});async function G(){let e=w.value.trim();if(!e||!U)return;let t=N(),n=t.append?d(t.append):d(t.delimiter);try{if(H===`hex`){let t=I(e);f(b,`HEX: ${F(t)}`,{kind:`sent`,label:`You`}),await U.send(t)}else{let r=t.prepend+e+n;f(b,e,{kind:`sent`,label:`You`}),await U.send(r)}w.value=``,w.focus()}catch(e){f(b,`Send error: ${e instanceof Error?e.message:String(e)}`,{kind:`error`})}}C?.addEventListener(`click`,G),w?.addEventListener(`keydown`,e=>{e.key===`Enter`&&G()});var K=y(`cmd-name`),q=y(`cmd-value`),_e=y(`cmd-mode`),ve=y(`cmd-add`),J=y(`cmd-list`),Y=y(`lst-name`),X=y(`lst-pattern`),ye=y(`lst-match`),be=y(`lst-add`),Z=y(`lst-list`);function Q(){if(J){J.innerHTML=``;for(let e of _){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.mode.toUpperCase();let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.value;let a=document.createElement(`button`);a.className=`chip-send`,a.title=`Send now`,a.textContent=`▶`,a.addEventListener(`click`,()=>{if(U)if(e.mode===`hex`){let t=I(e.value);U.send(t).catch(()=>{}),f(b,`HEX: ${F(t)}`,{kind:`sent`,label:`You`})}else{let t=N(),n=t.append?d(t.append):d(t.delimiter);U.send(t.prepend+e.value+n).catch(()=>{}),f(b,e.name,{kind:`sent`,label:`You`})}});let o=document.createElement(`button`);o.className=`chip-del`,o.title=`Remove`,o.textContent=`×`,o.addEventListener(`click`,()=>{_=_.filter(t=>t.id!==e.id),Q(),z()}),t.append(n,r,i,a,o),J.appendChild(t)}}}function $(){if(Z){Z.innerHTML=``;for(let e of v){let t=document.createElement(`div`);t.className=`chip`;let n=document.createElement(`span`);n.className=`chip-badge`,n.textContent=e.match;let r=document.createElement(`span`);r.className=`chip-name`,r.textContent=e.name;let i=document.createElement(`span`);i.className=`chip-val`,i.textContent=e.pattern;let a=document.createElement(`button`);a.className=`chip-del`,a.title=`Remove`,a.textContent=`×`,a.addEventListener(`click`,()=>{v=v.filter(t=>t.id!==e.id),$(),z()}),t.append(n,r,i,a),Z.appendChild(t)}}}ve?.addEventListener(`click`,()=>{let e=K?.value.trim(),t=q?.value.trim();if(!e||!t)return;let n=_e?.value??`text`;_.push({id:crypto.randomUUID(),name:e,value:t,mode:n}),K&&(K.value=``),q&&(q.value=``),Q(),z()}),be?.addEventListener(`click`,()=>{let e=Y?.value.trim(),t=X?.value.trim();if(!e||!t)return;let n=ye?.value??`exact`;v.push({id:crypto.randomUUID(),name:e,pattern:t,match:n}),Y&&(Y.value=``),X&&(X.value=``),$(),z()}),f(b,`WebSocket bridge demo — start the Node.js bridge first: cd demos/websocket && node server.js`,{kind:`system`}),r(b),i();
|