UNPKG

webserial-core

Version:

A strongly-typed, event-driven, abstract TypeScript library for the Web Serial API with custom parsers, command queue, handshake validation, and auto-reconnect.

1 lines 12.1 kB
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();