tassello 0.4.0 → 0.5.0
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/CHANGELOG.md +2 -0
- package/dist/types/client/widgets/types.d.ts +8 -0
- package/dist/www/assets/index.js +1 -1
- package/docs/plugins.md +14 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -81,6 +81,8 @@ change the plugin API (`tassello/sdk`, `tassello/server`, `tassello/vite`), as n
|
|
|
81
81
|
- Plugins: `connection.subscribeNetwork`, `networkAction`, the `Network*`, `WifiNetwork`,
|
|
82
82
|
`WifiSecurity` and `ConnectionKind` types, and the `NetworkIcon`, `WifiIcon`, `WifiOffIcon`,
|
|
83
83
|
`EthernetIcon`, `HotspotIcon` and `ShieldIcon` icons.
|
|
84
|
+
- Plugins: a `select` property's options can be known at run time, e.g. the robot's: `useOptions`,
|
|
85
|
+
a hook the properties panel calls. Its values are not checked against the options.
|
|
84
86
|
|
|
85
87
|
## 0.1.0
|
|
86
88
|
|
|
@@ -36,6 +36,10 @@ type PropertyKind<V> = [V] extends [boolean] ? {
|
|
|
36
36
|
value: V;
|
|
37
37
|
label: string;
|
|
38
38
|
}[];
|
|
39
|
+
useOptions?: () => {
|
|
40
|
+
value: V;
|
|
41
|
+
label: string;
|
|
42
|
+
}[];
|
|
39
43
|
} | {
|
|
40
44
|
kind: 'image';
|
|
41
45
|
default: V;
|
|
@@ -86,6 +90,10 @@ export type Property = PropertyBase<Config, string> & (PropertyKind<boolean> | P
|
|
|
86
90
|
value: string;
|
|
87
91
|
label: string;
|
|
88
92
|
}[];
|
|
93
|
+
useOptions?: () => {
|
|
94
|
+
value: string;
|
|
95
|
+
label: string;
|
|
96
|
+
}[];
|
|
89
97
|
} | {
|
|
90
98
|
kind: 'image' | 'behavior-tree';
|
|
91
99
|
default: string;
|
package/dist/www/assets/index.js
CHANGED
|
@@ -66,4 +66,4 @@ package://my_robot_bt/nodes.xml`,help:`XML files of TreeNodesModel (as Groot exp
|
|
|
66
66
|
`),l=c<0?0:e.msg.split(`
|
|
67
67
|
`).length-1;return(0,J.jsxs)(`div`,{role:`option`,"aria-selected":n,className:`log-row ${o}${n?` selected`:``}`,style:{top:t},onClick:()=>a(n?null:e.seq),children:[r!==`none`&&(0,J.jsx)(`time`,{className:`log-time`,children:r===`clock`?oa(e.stamp):aa(e.stamp)}),(0,J.jsx)(`span`,{className:`log-level`,children:s}),i&&(0,J.jsx)(`span`,{className:`log-node`,children:e.name}),(0,J.jsx)(`span`,{className:`log-msg`,children:c<0?e.msg:e.msg.slice(0,c)}),l>0&&(0,J.jsxs)(`span`,{className:`log-more`,title:`${l} more line${l===1?``:`s`}`,children:[`+`,l]})]})});function vl({entries:e,version:t,selected:n,follow:r,time:i,showNode:a,nodeWidth:o,compact:s,empty:c,onSelect:l,onFollow:u}){let d=s?hl:ml,f=(0,q.useRef)(null),[p,m]=(0,q.useState)({top:0,height:0}),h=(0,q.useRef)(null),g=(0,q.useRef)({entries:e,follow:r,rowPx:d});g.current={entries:e,follow:r,rowPx:d};let _=()=>{let e=f.current;if(!e)return;let{entries:t,follow:n,rowPx:r}=g.current;n?e.scrollTop=e.scrollHeight:h.current&&(e.scrollTop=ea(t,h.current.seq)*r+Math.min(h.current.offset,r-1)),m({top:e.scrollTop,height:e.clientHeight})};(0,q.useEffect)(()=>{h.current=null},[t]),(0,q.useLayoutEffect)(_,[e,r,d]),(0,q.useLayoutEffect)(()=>{let e=f.current;if(!e)return;let t=new ResizeObserver(_);return t.observe(e),()=>t.disconnect()},[]);let v=()=>{let t=f.current;if(!t)return;let n=t.scrollTop,i=Math.min(e.length-1,Math.floor(n/d));h.current=i>=0?{seq:e[i].seq,offset:n-i*d}:null;let a=t.scrollHeight-n-t.clientHeight<d/2;a!==r&&u(a),m({top:n,height:t.clientHeight})},y=e=>{let t=f.current;if(!t)return;let n=e*d;n<t.scrollTop?t.scrollTop=n:n+d>t.scrollTop+t.clientHeight&&(t.scrollTop=n+d-t.clientHeight)},b=t=>{if(e.length===0)return;let r=Math.max(1,Math.floor(p.height/d)-1),i=n===null?-1:ea(e,n),a;switch(t.key){case`ArrowDown`:a=i<0?e.length-1:i+1;break;case`ArrowUp`:a=i<0?e.length-1:i-1;break;case`PageDown`:a=i+r;break;case`PageUp`:a=i-r;break;case`Home`:a=0;break;case`End`:a=e.length-1;break;default:return}t.preventDefault(),a=Math.min(e.length-1,Math.max(0,a)),l(e[a].seq),y(a)},x=Math.max(0,Math.floor(p.top/d)-gl),S=Math.min(e.length,Math.ceil((p.top+p.height)/d)+gl),C=[];for(let t=x;t<S;t++){let r=e[t];C.push((0,J.jsx)(_l,{entry:r,top:t*d,selected:r.seq===n,time:i,showNode:a,onSelect:l},r.seq))}return(0,J.jsx)(`div`,{ref:f,className:`logs-list${s?` compact`:``}`,role:`listbox`,"aria-label":`Messages`,tabIndex:0,style:{"--log-row":`${d}px`,"--log-node":`${o}ch`},onScroll:v,onKeyDown:b,children:e.length>0?(0,J.jsx)(`div`,{className:`logs-rows`,style:{height:e.length*d},children:C}):(0,J.jsx)(`p`,{className:`logs-empty`,children:c})})}var yl=560,bl=420,xl=300,Sl={min:6,max:22};async function Cl(e){if(navigator.clipboard&&window.isSecureContext)return navigator.clipboard.writeText(e);let t=document.createElement(`textarea`);t.value=e,t.setAttribute(`readonly`,``),t.style.position=`fixed`,t.style.opacity=`0`,document.body.append(t),t.select();let n=document.execCommand(`copy`);if(t.remove(),!n)throw Error(`the browser does not allow it`)}function wl(e,t){let n=URL.createObjectURL(new Blob([t],{type:`text/plain`})),r=document.createElement(`a`);r.href=n,r.download=e,r.click(),setTimeout(()=>URL.revokeObjectURL(n),1e3)}function Tl(){let e=new Date,t=e=>String(e).padStart(2,`0`),n=`${e.getFullYear()}${t(e.getMonth()+1)}${t(e.getDate())}`,r=`${t(e.getHours())}${t(e.getMinutes())}${t(e.getSeconds())}`;return`rosout_${b.hostname||`robot`}_${n}_${r}.log`}function El(e){return`${e.toLocaleString()} message${e===1?``:`s`}`}function Dl({nodes:e,chosen:t,overlay:n,onToggle:r,onAll:i,onClose:a}){return(0,J.jsxs)(`aside`,{className:`logs-nodes${n?` overlay`:``}`,"aria-label":`Nodes`,children:[(0,J.jsxs)(`header`,{className:`logs-nodes-header`,children:[(0,J.jsx)(`span`,{children:`Nodes`}),(0,J.jsx)(`button`,{type:`button`,className:`link-button`,disabled:t.size===0,onClick:i,children:`Show all`}),n&&(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,"aria-label":`Close`,onClick:a,children:(0,J.jsx)(H,{width:15,height:15})})]}),e.length===0?(0,J.jsx)(`p`,{className:`logs-nodes-empty`,children:`No node yet`}):(0,J.jsx)(`ul`,{children:e.map(e=>{let n=t.has(e.name),i=e.counts[3]+e.counts[4],a=e.counts[2];return(0,J.jsx)(`li`,{children:(0,J.jsxs)(`button`,{type:`button`,className:`logs-node${e.node?``:` gone`}`,"aria-pressed":n,title:`${e.node??`${e.name}: not running`} · ${El(e.total)}`,onClick:()=>r(e.name),children:[(0,J.jsx)(`span`,{className:`logs-node-dot`,"aria-hidden":`true`}),(0,J.jsx)(`span`,{className:`logs-node-name`,children:e.name}),i>0&&(0,J.jsx)(`span`,{className:`logs-node-badge error`,children:ca(i)}),a>0&&(0,J.jsx)(`span`,{className:`logs-node-badge warn`,children:ca(a)}),(0,J.jsx)(`span`,{className:`logs-node-count`,children:ca(e.total)})]})},e.name)})})]})}function Ol({entry:e,running:t,onOnly:n,onClose:r}){let{level:a,tag:o}=Gi[Ki(e.level)],s=Xi(e.name,t),c=()=>Cl(sa(e)).then(()=>G(`Copied the message`),e=>G(`Could not copy the message: ${e instanceof Error?e.message:e}`,`error`));return(0,J.jsxs)(`section`,{className:`logs-details ${a}`,"aria-label":`The message`,children:[(0,J.jsxs)(`header`,{className:`logs-details-header`,children:[(0,J.jsx)(`span`,{className:`logs-pill`,children:o}),(0,J.jsx)(`strong`,{className:`logs-details-node`,title:s??`${e.name}: not running`,children:e.name}),(0,J.jsx)(`span`,{className:`logs-toolbar-end`}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:`Only this node’s messages`,"aria-label":`Only this node’s messages`,onClick:n,children:(0,J.jsx)(i,{width:16,height:16})}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:`Copy`,"aria-label":`Copy`,onClick:()=>void c(),children:(0,J.jsx)(z,{width:16,height:16})}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:`Close (Esc)`,"aria-label":`Close`,onClick:r,children:(0,J.jsx)(H,{width:16,height:16})})]}),(0,J.jsx)(`pre`,{className:`logs-details-msg`,children:e.msg}),(0,J.jsxs)(`dl`,{className:`logs-details-facts`,children:[(0,J.jsxs)(`div`,{children:[(0,J.jsx)(`dt`,{children:`Time`}),(0,J.jsxs)(`dd`,{children:[oa(e.stamp),` · `,aa(e.stamp)]})]}),e.file&&(0,J.jsxs)(`div`,{children:[(0,J.jsx)(`dt`,{children:`Where`}),(0,J.jsxs)(`dd`,{children:[e.file,`:`,e.line,e.function&&` · ${e.function}`]})]}),(0,J.jsxs)(`div`,{children:[(0,J.jsx)(`dt`,{children:`Node`}),(0,J.jsx)(`dd`,{children:s??`Not running`})]})]})]})}function kl({config:e}){let[t,n]=(0,q.useReducer)($i,Ji),[r,i]=(0,q.useState)({state:`waiting`,message:`Connecting…`}),[a,o]=(0,q.useState)(()=>qi(e.level)),[s,c]=(0,q.useState)(e.include),[l,u]=(0,q.useState)(e.exclude),[d,f]=(0,q.useState)(e.regex),[p,m]=(0,q.useState)(new Set),[h,g]=(0,q.useState)(!0),[_,v]=(0,q.useState)(null),[y,x]=(0,q.useState)(null),[S,C]=(0,q.useState)({width:0,height:0}),w=(0,q.useRef)(null);(0,q.useEffect)(()=>o(qi(e.level)),[e.level]),(0,q.useEffect)(()=>c(e.include),[e.include]),(0,q.useEffect)(()=>u(e.exclude),[e.exclude]),(0,q.useEffect)(()=>f(e.regex),[e.regex]);let T=e.topic.trim();(0,q.useEffect)(()=>{if(n({type:`reset`}),!T)i({state:`error`,message:`No log topic: set one in edit mode`});else return i({state:`waiting`,message:`Connecting…`}),b.subscribeRosout({topic:T},{status:(e,t)=>i({state:e,message:t}),update:e=>n({type:`update`,update:e})})},[T]),(0,q.useEffect)(()=>{v(null),g(!0)},[t.version]),(0,q.useEffect)(()=>{let e=w.current;if(!e)return;let t=new ResizeObserver(([e])=>C({width:e.contentRect.width,height:e.contentRect.height}));return t.observe(e),()=>t.disconnect()},[]);let D=(0,q.useDeferredValue)(s),O=(0,q.useDeferredValue)(l),k=(0,q.useMemo)(()=>na(e.nodes),[e.nodes]),A=(0,q.useMemo)(()=>ta(D,d),[D,d]),j=(0,q.useMemo)(()=>ta(O,d),[O,d]),N=(0,q.useMemo)(()=>ra(t.entries,t.running,{scope:k,nodes:p,levels:a,include:A.test,exclude:j.test}),[t.entries,t.running,k,p,a,A,j]),P=(e,t)=>o(n=>{if(t)return new Set([e]);let r=new Set(n);return r.delete(e)||r.add(e),r}),F=(0,q.useCallback)(e=>m(t=>{let n=new Set(t);return n.delete(e)||n.add(e),n}),[]),I=_===null?void 0:t.entries[ea(t.entries,_)],L=I?.seq===_?I:void 0,{width:R,height:ee}=S,te=R>=yl,z=y??te,ie=N.nodes.filter(e=>e.total>0),ae=Math.min(Sl.max,Math.max(Sl.min,...ie.map(e=>e.name.length))),oe=`No message matches the filters`;t.received?t.entries.length===0?oe=t.cleared?`Cleared: the new messages will show here`:`No message yet`:N.total===0&&(oe=`No message of ${e.nodes.trim()} yet`):oe=r.state===`error`?r.message:`Reading the log…`;let se;if(r.state===`error`)se={text:r.message,tone:`error`};else if(!t.received)se={text:r.message,tone:`idle`};else{let e=[N.shown.length===N.total?El(N.total):`${N.shown.length.toLocaleString()} of ${El(N.total)}`];p.size>0&&e.push(p.size===1?`of ${[...p][0]}`:`of ${p.size} nodes`),t.skipped>0&&e.push(`${t.skipped.toLocaleString()} skipped: too many at once`),t.paused&&e.push(t.pending.length>0?`paused, ${El(t.pending.length)} waiting`:`paused`),se={text:e.join(` · `),tone:t.paused?`paused`:r.state}}return(0,J.jsxs)(`div`,{ref:w,className:`logs${L&&ee>0&&ee<xl?` cramped`:``}`,onKeyDown:e=>{e.key===`Escape`&&_!==null&&v(null)},children:[(0,J.jsxs)(`div`,{className:`logs-toolbar`,role:`toolbar`,"aria-label":`Logs`,children:[(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,"aria-pressed":z,title:`The nodes`,"aria-label":`The nodes`,onClick:()=>x(!z),children:(0,J.jsx)(Ne,{width:17,height:17})}),(0,J.jsx)(`div`,{className:`logs-levels`,role:`group`,"aria-label":`Levels`,children:Gi.map(({level:e,label:t,letter:n},r)=>(0,J.jsxs)(`button`,{type:`button`,className:`logs-level ${e}`,"aria-pressed":a.has(e),"aria-label":`${t}: ${N.levels[r]}`,title:`${t}: ${El(N.levels[r])}. Shift+click: this level only`,onClick:t=>P(e,t.shiftKey),children:[(0,J.jsx)(`span`,{className:`logs-level-dot`,"aria-hidden":`true`}),(0,J.jsx)(`span`,{className:`logs-level-name`,children:t}),(0,J.jsx)(`span`,{className:`logs-level-letter`,"aria-hidden":`true`,children:n}),(0,J.jsx)(`span`,{className:`logs-level-count`,children:ca(N.levels[r])})]},e))}),(0,J.jsxs)(`div`,{className:`logs-tools`,children:[(0,J.jsxs)(`label`,{className:`logs-search${A.error?` invalid`:``}`,title:A.error??`Only the messages containing any of these, separated by ;`,children:[(0,J.jsx)(ne,{width:14,height:14}),(0,J.jsx)(`input`,{type:`search`,value:s,placeholder:`Include…`,"aria-label":`Only the messages containing`,spellCheck:!1,onChange:e=>c(e.target.value)})]}),(0,J.jsxs)(`label`,{className:`logs-search${j.error?` invalid`:``}`,title:j.error??`Not the messages containing any of these, separated by ;`,children:[(0,J.jsx)(be,{width:14,height:14}),(0,J.jsx)(`input`,{type:`search`,value:l,placeholder:`Exclude…`,"aria-label":`Not the messages containing`,spellCheck:!1,onChange:e=>u(e.target.value)})]}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button logs-regex`,"aria-pressed":d,title:`As regular expressions`,"aria-label":`As regular expressions`,onClick:()=>f(e=>!e),children:`.*`}),(0,J.jsx)(`span`,{className:`overlay-separator`}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,"aria-pressed":t.paused,title:t.paused?`Resume`:`Pause: the new messages wait`,"aria-label":t.paused?`Resume`:`Pause`,onClick:()=>n({type:t.paused?`resume`:`pause`}),children:(0,J.jsx)(Ae,{width:17,height:17})}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,"aria-pressed":h,title:`Follow the newest`,"aria-label":`Follow the newest`,onClick:()=>g(e=>!e),children:(0,J.jsx)(re,{width:17,height:17})}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:`Download the messages shown`,"aria-label":`Download the messages shown`,disabled:N.shown.length===0,onClick:()=>wl(Tl(),N.shown.map(sa).join(`
|
|
68
68
|
`)+`
|
|
69
|
-
`),children:(0,J.jsx)(M,{width:17,height:17})}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:`Clear the messages`,"aria-label":`Clear the messages`,disabled:t.entries.length===0&&t.pending.length===0,onClick:()=>{v(null),n({type:`clear`})},children:(0,J.jsx)(E,{width:17,height:17})})]})]}),(0,J.jsxs)(`div`,{className:`logs-main`,children:[z&&(0,J.jsx)(Dl,{nodes:N.nodes,chosen:p,overlay:!te,onToggle:F,onAll:()=>m(new Set),onClose:()=>x(!1)}),(0,J.jsx)(vl,{entries:N.shown,version:t.version,selected:_,follow:h,time:e.time,showNode:p.size!==1&&ie.length!==1,nodeWidth:ae,compact:R>0&&R<bl,empty:oe,onSelect:v,onFollow:g})]}),L&&(0,J.jsx)(Ol,{entry:L,running:t.running,onOnly:()=>m(new Set([L.name])),onClose:()=>v(null)}),(0,J.jsxs)(`div`,{className:`logs-status ${se.tone}`,title:se.text,children:[(0,J.jsx)(`span`,{className:`logs-dot`,"aria-hidden":`true`}),(0,J.jsx)(`span`,{className:`logs-status-text`,children:se.text})]})]})}var Al=Ye({type:`logs`,name:`Logs`,description:`The ROS log of every node (/rosout), coloured by level, filtered by node, level and text.`,icon:_,defaultSize:{w:10,h:6},minSize:{w:4,h:3},component:kl,properties:[{key:`topic`,label:`Log topic`,kind:`topic`,default:`/rosout`,topicTypes:[`rcl_interfaces/msg/Log`],help:`rcl_interfaces/Log: every node’s logger publishes on /rosout`},{key:`nodes`,label:`Nodes`,kind:`string`,default:``,placeholder:`amcl, *_server`,help:`Only these nodes, with their child loggers: names or globs (*), separated by commas or spaces. All if empty`},{key:`level`,label:`Levels shown`,kind:`select`,default:`debug`,options:[{value:`debug`,label:`All`},{value:`info`,label:`Info and above`},{value:`warn`,label:`Warnings and above`},{value:`error`,label:`Errors and above`}],help:`When the panel opens: its toolbar shows and hides each level`},{key:`include`,label:`Include`,kind:`string`,default:``,placeholder:`goal; path`,help:`Only the messages containing any of these, separated by ;. The toolbar changes it`},{key:`exclude`,label:`Exclude`,kind:`string`,default:``,placeholder:`Publishing`,help:`Not the messages containing any of these, separated by ;. The toolbar changes it`},{key:`regex`,label:`Filters as regular expressions`,kind:`boolean`,default:!1},{key:`time`,label:`Time`,kind:`select`,default:`clock`,options:[{value:`clock`,label:`Clock time, 14:03:27.512`},{value:`stamp`,label:`ROS stamp, in seconds`},{value:`none`,label:`Hidden`}],help:`Of the messages’ stamps, on their nodes’ clocks: seconds on sim time`}]}),jl=[`map`,`global`,`local`],Ml=2,Nl=2e3,Pl=.25,Fl=8,Il=34,Ll=6,Rl=2e3,zl=.25,Bl={robot:`#4f8cff`,stale:`#8b95a7`,goal:`#2fbf71`,plan:`#2fbf71`,pose:`#f0a020`},Vl=e=>e*180/Math.PI;function Hl(e){return`x ${e.x.toFixed(2)} y ${e.y.toFixed(2)} θ ${Math.round(Vl(e.yaw))}°`}function Ul(e,t,n){return new DOMMatrix().translate(t/2,n/2).scale(e.scale,-e.scale).translate(-e.x,-e.y)}function Wl(e){return new DOMMatrix().translate(e.x,e.y).rotate(Vl(e.yaw))}function Gl(e,t){return Wl(t).translate(e.originX,e.originY).rotate(Vl(e.originYaw)).scale(e.resolution,e.resolution)}function Kl(e,t,n,r){let i=t.transformPoint({x:n.x,y:n.y}),a=-n.yaw;e.save(),e.translate(i.x,i.y),e.rotate(a),e.strokeStyle=r,e.fillStyle=r,e.lineWidth=3,e.lineCap=`round`,e.beginPath(),e.moveTo(0,0),e.lineTo(26,0),e.stroke(),e.beginPath(),e.moveTo(Il,0),e.lineTo(23,-7),e.lineTo(23,7),e.closePath(),e.fill(),e.beginPath(),e.arc(0,0,5,0,2*Math.PI),e.fill(),e.lineWidth=2,e.strokeStyle=`#fff`,e.stroke(),e.restore()}function ql(e,t,n,r,i){let a=t.transformPoint({x:n.x,y:n.y}),o=Math.max(Fl,Pl*r),s=i?Bl.stale:Bl.robot;e.save(),e.translate(a.x,a.y),e.rotate(-n.yaw),e.beginPath(),e.arc(0,0,o,0,2*Math.PI),e.fillStyle=`${s}55`,e.fill(),e.lineWidth=2,e.strokeStyle=s,e.stroke(),e.beginPath(),e.moveTo(o+6,0),e.lineTo(-o*.35,-o*.55),e.lineTo(-o*.1,0),e.lineTo(-o*.35,o*.55),e.closePath(),e.fillStyle=s,e.fill(),e.lineWidth=1.5,e.strokeStyle=`#fff`,e.stroke(),e.restore()}function Jl(e,t,n){if(!(n.length<4)){e.save(),e.beginPath();for(let r=0;r<n.length;r+=2){let i=t.transformPoint({x:n[r],y:n[r+1]});r===0?e.moveTo(i.x,i.y):e.lineTo(i.x,i.y)}e.lineCap=`round`,e.lineJoin=`round`,e.strokeStyle=`rgb(0 0 0 / 35%)`,e.lineWidth=6,e.stroke(),e.strokeStyle=Bl.plan,e.lineWidth=3,e.stroke(),e.restore()}}function Yl(e,t,n){let r=window.devicePixelRatio||1,i=e.clientWidth,a=e.clientHeight;(e.width!==Math.round(i*r)||e.height!==Math.round(a*r))&&(e.width=Math.round(i*r),e.height=Math.round(a*r));let o=e.getContext(`2d`);if(!o||(o.setTransform(1,0,0,1,0,0),o.clearRect(0,0,e.width,e.height),!t))return;let s=Ul(t,i,a),c=new DOMMatrix().scale(r,r).multiply(s);for(let e of jl){let i=n.layers[e],a=n.placements[e];i&&a&&(o.imageSmoothingEnabled=i.info.resolution*t.scale*r<1,o.setTransform(c.multiply(Gl(i.info,a))),o.drawImage(i.image,0,0))}o.setTransform(r,0,0,r,0,0),n.plan&&Jl(o,s.multiply(Wl(n.plan.placement)),n.plan.points),n.goal&&Kl(o,s,n.goal,Bl.goal),n.robot?.transform&&ql(o,s,n.robot.transform,t.scale,n.robot.ageMs>Rl),n.preview&&Kl(o,s,n.preview.pose,Bl[n.preview.tool])}function Xl(e,t,n,r){let i=Gl(e.info,t),a=e.info.known??{x:0,y:0,width:e.info.width,height:e.info.height},o=[[a.x,a.y],[a.x+a.width,a.y],[a.x,a.y+a.height],[a.x+a.width,a.y+a.height]].map(([e,t])=>i.transformPoint({x:e,y:t})),s=o.map(e=>e.x),c=o.map(e=>e.y),[l,u,d,f]=[Math.min(...s),Math.max(...s),Math.min(...c),Math.max(...c)],p=.94*Math.min(n/(u-l||1),r/(f-d||1));return{x:(l+u)/2,y:(d+f)/2,scale:Math.min(Nl,Math.max(Ml,p))}}function Zl({config:e,editing:t}){let n=(0,q.useRef)(null),i=(0,q.useRef)(null),a=(0,q.useRef)(null),o=(0,q.useRef)({}),[c,l]=(0,q.useState)({map:``,global:``,local:``}),[u,d]=(0,q.useState)({state:`waiting`,message:`Connecting…`}),[f,p]=(0,q.useState)(null),[m,h]=(0,q.useState)(!1),g=(0,q.useRef)(null),_=(0,q.useRef)(null),[y,S]=(0,q.useState)(``),C=(0,q.useRef)(null),w=(0,q.useRef)(new Map),T=(0,q.useRef)(null),E=e.mapFrame.trim(),D=s(E,e.robotFrame),O=e=>c[e]&&c[e]!==E?c[e]:``,k={map:s(E,O(`map`)),global:s(E,O(`global`)),local:s(E,O(`local`))},A={};for(let e of jl)A[e]=O(e)?k[e]?.transform??null:{x:0,y:0,yaw:0};let j=s(E,y&&y!==E?y:``),M=y&&y!==E?j?.transform??null:{x:0,y:0,yaw:0},N=(0,q.useRef)({layers:o.current,placements:A,planPlacement:M,robot:D});N.current={layers:o.current,placements:A,planPlacement:M,robot:D};let P=(0,q.useRef)(0),F=(0,q.useCallback)(()=>{P.current||=requestAnimationFrame(()=>{P.current=0;let e=n.current,{planPlacement:t,...r}=N.current;e&&Yl(e,i.current,{...r,plan:_.current&&t?{points:_.current.points,placement:t}:null,goal:g.current,preview:C.current})})},[]);(0,q.useEffect)(F),(0,q.useEffect)(()=>()=>cancelAnimationFrame(P.current),[]);let I=()=>({width:n.current?.clientWidth??1,height:n.current?.clientHeight??1}),L=(0,q.useCallback)(()=>{let{width:e,height:t}=I();for(let n of jl){let r=o.current[n],s=N.current.placements[n];if(r&&s)return i.current=Xl(r,s,e,t),a.current=n===`map`?`map`:`costmap`,F(),!0}let n=N.current.robot?.transform;return n?(i.current={x:n.x,y:n.y,scale:50},a.current=`robot`,F(),!0):!1},[F]),R=(0,q.useCallback)((e,t,n,r)=>{let i=!0,s=b.subscribeGrid({topic:t,style:n,maxFps:r},{status:(t,n)=>e===`map`&&d({state:t,message:n}),frame:async(t,{meta:n})=>{let r=await createImageBitmap(t);if(!i){r.close();return}let s=n;o.current[e]?.image.close(),o.current[e]={image:r,info:s},l(t=>t[e]===s.frameId?t:{...t,[e]:s.frameId});let c=a.current;(e===`map`?c!==`map`&&c!==`user`:c===null||c===`robot`)&&L(),F()}});return()=>{i=!1,s(),o.current[e]?.image.close(),delete o.current[e],F()}},[L,F]),ee=e.mapTopic.trim();(0,q.useEffect)(()=>{if(!ee)d({state:`error`,message:`No map topic: set one in edit mode`});else return d({state:`waiting`,message:`Connecting…`}),R(`map`,ee,`map`,1)},[ee,R]);let z=e.showGlobalCostmap?e.globalCostmapTopic.trim():``;(0,q.useEffect)(()=>z?R(`global`,z,`costmap`,1):void 0,[z,R]);let ne=e.showLocalCostmap?e.localCostmapTopic.trim():``;(0,q.useEffect)(()=>ne?R(`local`,ne,`costmap`,4):void 0,[ne,R]);let re=e.showPlan?e.planTopic.trim():``;(0,q.useEffect)(()=>{if(!re)return;let e=b.subscribePath({topic:re},e=>{_.current=e,S(e.frameId),F()});return()=>{e(),_.current=null,S(``),F()}},[re,F]),(0,q.useEffect)(()=>{let e=D?.transform;e&&g.current&&Math.hypot(e.x-g.current.x,e.y-g.current.y)<zl&&(g.current=null),m&&e&&i.current?i.current={...i.current,x:e.x,y:e.y}:!i.current&&e&&L()},[D,m,L]),(0,q.useEffect)(()=>{let e=n.current;if(!e)return;let t=new ResizeObserver(F);return t.observe(e),()=>t.disconnect()},[F]);let ie=(0,q.useCallback)((e,t)=>{let n=i.current;if(!n)return;let{width:r,height:o}=I(),s=Math.min(Nl,Math.max(Ml,n.scale*t));a.current=`user`;let c=Ul(n,r,o).inverse().transformPoint(e);i.current={scale:s,x:c.x-(e.x-r/2)/s,y:c.y+(e.y-o/2)/s},F()},[F]);(0,q.useEffect)(()=>{let e=n.current;if(!e)return;let t=t=>{t.preventDefault();let n=e.getBoundingClientRect();ie({x:t.clientX-n.left,y:t.clientY-n.top},Math.exp(-t.deltaY*.0015))};return e.addEventListener(`wheel`,t,{passive:!1}),()=>e.removeEventListener(`wheel`,t)},[ie]),(0,q.useEffect)(()=>{if(!f)return;let e=e=>e.key===`Escape`&&p(null);return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[f]);let ae=e=>{let t=n.current.getBoundingClientRect();return{x:e.clientX-t.left,y:e.clientY-t.top}},oe=e=>{let{width:t,height:n}=I(),{x:r,y:a}=Ul(i.current,t,n).inverse().transformPoint(e);return{x:r,y:a}},se=()=>{let[e,t]=[...w.current.values()];return{distance:Math.hypot(e.x-t.x,e.y-t.y),center:{x:(e.x+t.x)/2,y:(e.y+t.y)/2}}},ce=(t,n)=>{b.send({type:`pose`,topic:t===`goal`?e.goalTopic.trim():e.poseTopic.trim(),covariance:t===`pose`,frameId:E,...n})?(t===`goal`&&(g.current=n),G(`${t===`goal`?`Goal`:`Pose estimate`} sent: ${Hl(n)}`)):G(`Not connected to the robot`,`error`)},B=e=>{if(!i.current||e.pointerType===`mouse`&&e.button!==0)return;e.currentTarget.setPointerCapture(e.pointerId);let t=ae(e);if(w.current.set(e.pointerId,t),w.current.size===2)C.current=null,T.current={kind:`pinch`,...se()};else if(w.current.size===1&&f){let n=oe(t);T.current={kind:`place`,tool:f,pointerId:e.pointerId,anchor:n,start:t},C.current={tool:f,pose:{x:n.x,y:n.y,yaw:0}}}else w.current.size===1&&(T.current={kind:`pan`});F()},le=e=>{let t=w.current.get(e.pointerId),n=i.current;if(!t||!n)return;let r=ae(e);w.current.set(e.pointerId,r);let o=T.current;if(o?.kind===`place`){let e=oe(r),t=Math.hypot(r.x-o.start.x,r.y-o.start.y)>Ll;C.current={tool:o.tool,pose:{...o.anchor,yaw:t?Math.atan2(e.y-o.anchor.y,e.x-o.anchor.x):0}}}else if(o?.kind===`pan`)a.current=`user`,i.current={...n,x:n.x-(r.x-t.x)/n.scale,y:n.y+(r.y-t.y)/n.scale},h(!1);else if(o?.kind===`pinch`&&w.current.size===2){let e=se();i.current={...n,x:n.x-(e.center.x-o.center.x)/n.scale,y:n.y+(e.center.y-o.center.y)/n.scale},ie(e.center,e.distance/(o.distance||1)),T.current={kind:`pinch`,...e}}F()},ue=e=>{if(!w.current.delete(e.pointerId))return;let t=T.current;if(t?.kind===`place`&&t.pointerId===e.pointerId){let n=C.current;C.current=null,T.current=null,e.type===`pointerup`&&n&&(ce(t.tool,n.pose),p(null))}else w.current.size===1?T.current={kind:`pan`}:w.current.size===0&&(T.current=null);F()},de=D?.transform,V;if(!E||!e.robotFrame.trim())V={text:`Set the TF frames in edit mode`,className:`error`};else if(de){let e=D.ageMs>Rl;V={text:`${Hl(de)}${e?` (stale)`:``}`,className:e?`stale`:``}}else V={text:D?.error??`Locating the robot…`,className:`error`};let fe=(e,t,n)=>(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,"aria-pressed":f===e,title:t,"aria-label":t,onClick:()=>p(t=>t===e?null:e),children:(0,J.jsx)(n,{width:17,height:17})});return(0,J.jsxs)(`div`,{className:`navigation`,children:[(0,J.jsx)(`canvas`,{ref:n,className:f?`placing`:void 0,onPointerDown:B,onPointerMove:le,onPointerUp:ue,onPointerCancel:ue}),u.state!==`streaming`&&(0,J.jsxs)(`div`,{className:`nav-overlay ${u.state}`,children:[(0,J.jsx)(te,{width:28,height:28}),(0,J.jsx)(`span`,{children:u.message})]}),(0,J.jsxs)(`div`,{className:`overlay-toolbar`,children:[fe(`pose`,`Set the pose estimate`,v),fe(`goal`,`Send a navigation goal`,x),(0,J.jsx)(`span`,{className:`overlay-separator`}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:`Fit the map`,"aria-label":`Fit the map`,onClick:()=>{h(!1),L()},children:(0,J.jsx)(ge,{width:17,height:17})}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,"aria-pressed":m,title:`Follow the robot`,"aria-label":`Follow the robot`,disabled:!de,onClick:()=>h(e=>!e),children:(0,J.jsx)(r,{width:17,height:17})})]}),f&&!t&&(0,J.jsx)(`div`,{className:`nav-hint`,children:f===`goal`?`Press where the robot should go, drag towards where it should face`:`Press where the robot is, drag towards where it faces`}),(0,J.jsx)(`div`,{className:`nav-readout ${V.className}`,children:V.text})]})}var Ql=Ye({type:`navigation`,name:`Navigation`,description:`The map, the costmaps and the robot on them; set its pose and send it goals.`,icon:te,defaultSize:{w:8,h:6},minSize:{w:3,h:3},component:Zl,properties:[{key:`mapTopic`,label:`Map topic`,kind:`topic`,default:`/map`,topicTypes:[`nav_msgs/msg/OccupancyGrid`]},{key:`showGlobalCostmap`,label:`Show the global costmap`,kind:`boolean`,default:!1},{key:`globalCostmapTopic`,label:`Global costmap topic`,kind:`topic`,default:`/global_costmap/costmap`,topicTypes:[`nav_msgs/msg/OccupancyGrid`],visible:e=>e.showGlobalCostmap},{key:`showLocalCostmap`,label:`Show the local costmap`,kind:`boolean`,default:!0},{key:`localCostmapTopic`,label:`Local costmap topic`,kind:`topic`,default:`/local_costmap/costmap`,topicTypes:[`nav_msgs/msg/OccupancyGrid`],visible:e=>e.showLocalCostmap},{key:`showPlan`,label:`Show the planned path`,kind:`boolean`,default:!0},{key:`planTopic`,label:`Plan topic`,kind:`topic`,default:`/plan`,topicTypes:[`nav_msgs/msg/Path`],help:`nav_msgs/Path: Nav2’s planner publishes its plan on /plan`,visible:e=>e.showPlan},{key:`goalTopic`,label:`Goal topic`,kind:`topic`,default:`/goal_pose`,topicTypes:[`geometry_msgs/msg/PoseStamped`],help:`geometry_msgs/PoseStamped, which Nav2’s bt_navigator follows`},{key:`poseTopic`,label:`Pose estimate topic`,kind:`topic`,default:`/initialpose`,topicTypes:[`geometry_msgs/msg/PoseWithCovarianceStamped`],help:`geometry_msgs/PoseWithCovarianceStamped, which AMCL starts from`},{key:`mapFrame`,label:`Map frame`,kind:`string`,default:`map`,help:`The frame the map is drawn in, and the goals and poses are set in`},{key:`robotFrame`,label:`Robot frame`,kind:`string`,default:`base_footprint`,help:`Where the robot is, from TF`}]}),$l={open:`None (open)`,owe:`Enhanced Open (OWE)`,wep:`WEP`,"wpa-psk":`WPA/WPA2 Personal`,sae:`WPA3 Personal`,"wpa-eap":`WPA/WPA2 Enterprise`};function eu(e){return e!==`open`&&e!==`owe`}var tu=/^[\w.@-]{1,15}$/,nu=Array.from({length:14},(e,t)=>t+1),ru=[36,40,44,48,52,56,60,64,100,104,108,112,116,120,124,128,132,136,140,144,149,153,157,161,165,169,173,177];function iu(e){return tu.test(e)}function au(e){let t=e.split(`.`);return t.length===4&&t.every(e=>/^(0|[1-9]\d{0,2})$/.test(e)&&Number(e)<=255)}function ou(e){if(!/^[0-9a-f:.]+$/i.test(e)||!e.includes(`:`))return!1;let t=e.split(`::`);if(t.length>2)return!1;let n=t.flatMap(e=>e?e.split(`:`):[]),r=n.at(-1)??``,i=r.includes(`.`);if(i&&!au(r))return!1;let a=i?n.slice(0,-1):n;if(!a.every(e=>/^[0-9a-f]{1,4}$/i.test(e)))return!1;let o=a.length+(i?2:0);return t.length===2?o<8:o===8}function su(e){let[t,n,...r]=e.trim().split(`/`);return r.length>0||!au(t)?`“${e.trim()}” is not an IPv4 address`:n===void 0?`Give its network's size: ${t}/24`:!/^\d{1,2}$/.test(n)||Number(n)<1||Number(n)>32?`/${n} is not a network's size: /1 to /32 (/24: 255.255.255.0)`:null}function cu(e){return e.split(/[\s,;]+/).map(e=>e.trim()).filter(Boolean)}function lu(e){return new TextEncoder().encode(e).length}function uu(e,t,n=!1){switch(e){case`open`:case`owe`:return null;case`wpa-psk`:return/^[0-9a-f]{64}$/i.test(t)?null:/^[\x20-\x7e]*$/.test(t)?t.length<8||t.length>63?n?`8 to 63 characters`:`WPA passwords have 8 to 63 characters`:null:`Letters, digits and symbols of the keyboard only: no accents`;case`sae`:return t.length===0?`The network’s password`:t.length>128?`At most 128 characters`:null;case`wep`:return/^([\x20-\x7e]{5}|[\x20-\x7e]{13}|[0-9a-f]{10}|[0-9a-f]{26})$/i.test(t)?null:`WEP keys have 5 or 13 characters, or 10 or 26 hexadecimal digits`;case`wpa-eap`:return t.length===0?`Your password on the network`:null}}function du(e,t){let n={},{kind:r}=e,i=e.name.trim();if(i?(i.length>255||/[\x00-\x1f]/.test(i))&&(n.name=`At most 255 characters, on one line`):n.name=`A name for the connection`,e.interface&&!iu(e.interface)?n.interface=`“${e.interface}” is not an interface's name`:r===`hotspot`&&!e.interface&&(n.interface=`The Wi-Fi interface of the access point`),(!Number.isInteger(e.priority)||e.priority<-999||e.priority>999)&&(n.priority=`A whole number, from -999 to 999`),r!==`ethernet`){let i=lu(e.ssid);if(i===0?n.ssid=r===`hotspot`?`The name the access point shows`:`The network’s name`:i>32?n.ssid=`At most 32 bytes (fewer with accents and symbols)`:/[\x00-\x1f]/.test(e.ssid)&&(n.ssid=`No control characters`),r===`hotspot`&&e.security!==`open`&&e.security!==`wpa-psk`&&(n.security=`An access point is open, or secured by a WPA2 password`),e.security===`wpa-eap`&&(e.identity.trim()?/[\x00-\x1f]/.test(e.identity)&&(n.identity=`No control characters`):n.identity=`Your user name on the network`),e.password!==null){let t=uu(e.security,e.password,r===`hotspot`);t&&(n.password=t)}else t&&eu(e.security)&&(n.password=`The network’s password`);if(r===`hotspot`){let t=e.band===`a`?ru:e.band===`bg`?nu:[];e.channel!==0&&!t.includes(e.channel)&&(n.channel=e.band?`Not a channel of ${e.band===`a`?`5`:`2.4`} GHz`:`Choose the band of the channel`)}}(r===`hotspot`?[`shared`]:[`auto`,`manual`,`shared`,`disabled`]).includes(e.ipv4)||(n.ipv4=r===`hotspot`?`An access point gives the addresses (shared)`:`Not a way to get an address`);let a=e.addresses.map(su).find(e=>e!==null);a?n.addresses=a:e.ipv4===`manual`&&e.addresses.length===0?n.addresses=`The robot’s address on this network: 192.168.1.20/24`:e.ipv4===`shared`&&e.addresses.length>1&&(n.addresses=`One address: the robot’s, on the network it gives`),e.gateway&&!au(e.gateway)&&(n.gateway=`“${e.gateway}” is not an IPv4 address`);let o=e.dns.find(e=>!au(e)&&!ou(e));return o!==void 0&&(n.dns=`“${o}” is not an IP address`),n}function fu(e){let t=e.kind;return t!==`wifi`&&t!==`hotspot`&&t!==`ethernet`?null:{kind:t,name:e.name,interface:e.interface,autoconnect:e.autoconnect,priority:e.priority,ssid:e.ssid,hidden:e.hidden,security:e.security??`open`,password:null,identity:e.identity,band:e.band,channel:e.channel,ipv4:[`auto`,`manual`,`shared`,`disabled`].includes(e.ipv4Method)?e.ipv4Method:`auto`,addresses:e.addresses,gateway:e.gateway,dns:e.dns}}function pu(e){return{kind:e,name:``,interface:``,autoconnect:!0,priority:0,ssid:``,hidden:!1,security:e===`ethernet`?`open`:`wpa-psk`,password:e===`ethernet`?null:``,identity:``,band:``,channel:0,ipv4:e===`hotspot`?`shared`:`auto`,addresses:[],gateway:``,dns:[]}}function mu(e){return e>=70?4:e>=50?3:e>=30?2:1}function hu(e){return e<=0?``:e<3e3?`2.4 GHz`:e<5925?`5 GHz`:`6 GHz`}function gu(e){let t=e.split(/[\s,]+/).filter(Boolean).map(e=>RegExp(`^${e.replace(/[.+^${}()|[\]\\]/g,`\\$&`).replace(/\*/g,`.*`).replace(/\?/g,`.`)}$`));return t.length===0?()=>!0:e=>t.some(t=>t.test(e))}function _u(e,t,n){return e.filter(e=>t(e.name)&&(n||e.type!==`loopback`&&e.type!==`wifi-p2p`&&!e.external&&!(e.software&&e.state===10)))}var vu=[[/need auth/,`Waiting for the password…`],[/getting IP/i,`Getting an address…`],[/checking IP/i,`Checking the connection…`]];function yu(e,t){let{state:n}=e;if(n===100)return e.external?{text:`Configured by another program`,tone:`on`}:{text:e.connectionName?`Connected to ${e.connectionName}`:`Connected`,tone:`on`};if(n>30&&n<100){let t=vu.find(([t])=>t.test(e.stateText));return{text:t?t[1]:`Connecting…`,tone:`busy`}}return n===110?{text:`Disconnecting…`,tone:`busy`}:n===120?{text:e.reason?`Failed: ${e.reason}`:`Failed`,tone:`failed`}:n===20?e.carrier===!1?{text:`Cable unplugged`,tone:`off`}:e.type===`wifi`&&!t?{text:`Wi-Fi is off`,tone:`off`}:{text:`Unavailable`,tone:`off`}:n===10?{text:`Not managed by NetworkManager`,tone:`off`}:{text:`Disconnected`,tone:`off`}}function bu(e){switch(e.type){case`wifi`:return`Wi-Fi`;case`ethernet`:return`Ethernet`;case`gsm`:case`cdma`:return`Mobile broadband`;case`bt`:return`Bluetooth`;case`loopback`:return`Loopback`;default:return e.type.charAt(0).toUpperCase()+e.type.slice(1)}}var xu={openvpn:`OpenVPN`,vpnc:`Cisco VPN`,openconnect:`OpenConnect`,l2tp:`L2TP`,pptp:`PPTP`,strongswan:`IPsec`,libreswan:`IPsec`,fortisslvpn:`Fortinet`};function Su(e){switch(e.kind){case`wifi`:return`Wi-Fi network`;case`hotspot`:return`Access point`;case`ethernet`:return`Wired`;case`vpn`:return e.type===`wireguard`?`WireGuard`:`VPN${e.vpnService?` (${xu[e.vpnService]??e.vpnService})`:``}`;default:return e.type}}var Cu=[{kind:`wifi`,title:`Wi-Fi networks`},{kind:`hotspot`,title:`Access points`},{kind:`ethernet`,title:`Wired`},{kind:`vpn`,title:`VPN`},{kind:`other`,title:`Other`}];function wu(e,t){return Number(t.activeState!==null)-Number(e.activeState!==null)||(t.lastUsed??0)-(e.lastUsed??0)||e.name.localeCompare(t.name)}function Tu(e,t){let n=t.trim().toLowerCase();return!n||[e.name,e.ssid,e.interface,e.device??``].some(e=>e.toLowerCase().includes(n))}function Eu(e,t){if(e.device)return t.some(t=>t.name===e.device);if(e.interface)return t.some(t=>t.name===e.interface);let n=e.kind===`wifi`||e.kind===`hotspot`?`wifi`:e.kind===`ethernet`?`ethernet`:null;return n===null||t.some(e=>e.type===n)}function Du(e,t,n,r){let i=e.filter(e=>(r||!e.external)&&Eu(e,t)&&Tu(e,n));return Cu.map(({kind:e,title:t})=>({kind:e,title:t,connections:i.filter(t=>t.kind===e).sort(wu)})).filter(e=>e.connections.length>0)}function Ou(e,t,n){let r=e.filter(e=>e.kind===`wifi`&&e.ssid===t&&(!e.interface||e.interface===n)).sort(wu);return r.find(e=>e.device===n)??r.find(e=>e.device===null)??null}function ku(e,t,n){let r=n.trim().toLowerCase();return e.networks.filter(e=>e.device===t&&(!r||e.ssid.toLowerCase().includes(r))).map(n=>({network:n,saved:Ou(e.connections,n.ssid,t)})).sort((e,t)=>Number(t.network.active)-Number(e.network.active)||Number(t.saved!==null)-Number(e.saved!==null)||t.network.signal-e.network.signal)}function Au(e,t){return e.filter(e=>e.kind===`hotspot`&&(!e.interface||e.interface===t)).sort(wu)}function ju(e,t){return t.uuid&&e.uuid===t.uuid?!0:!!t.ssid&&e.ssid===t.ssid&&(!t.device||!e.device||e.device===t.device)}function Mu(e,t,n){let r=e.filter(e=>e.action!==`rescan`&&e.action!==`wifi`&&ju(e,t)),i=r.find(e=>e.finished===null)??null,a=r.find(e=>e.finished!==null);return{running:i,failed:!i&&a?.error&&n-(a.finished??0)<12e4?a:null}}function Nu(e,t,n,r){if(n||r)return n||r;let i=e.devices.find(t=>t.name===e.via),a=t===`wifi`||t===`hotspot`?`wifi`:t===`ethernet`?`ethernet`:null;return i&&i.type===a?i.name:null}function Pu(e,t){let{via:n}=e;if(!n)return null;let r=`This page reaches the robot through ${n}`,i=`uuid`in t?e.connections.find(e=>e.uuid===t.uuid):void 0,a=i?.device===n;switch(t.action){case`connect`:return!i||a||Nu(e,i.kind,t.device,i.interface)!==n?null:i.kind===`hotspot`?`${r}: starting the access point disconnects it from its network, and this page loses the robot. Join ${i.ssid||i.name} to reach it again.`:`${r}: connecting it to ${i.ssid||i.name} moves the robot to that network, and this page loses it unless it is on that network too.`;case`create`:{if(!t.connect)return null;let{settings:i}=t;return Nu(e,i.kind,t.device,i.interface)===n?i.kind===`hotspot`?`${r}: starting the access point disconnects it from its network, and this page loses the robot. Join ${i.ssid} to reach it again.`:`${r}: connecting it to ${i.ssid||i.name} moves the robot to that network, and this page loses it unless it is on that network too.`:null}case`disconnect`:return a?`${r}: disconnecting ${i.name} cuts this page off from the robot.`:null;case`delete`:return a?`${r}: forgetting ${i.name} disconnects it, and cuts this page off.`:null;case`modify`:return a?`${r}: saving reconnects ${i.name}, and if its address changes this page loses the robot.`:null;case`wifi`:{let i=e.devices.find(e=>e.name===n);return!t.enabled&&i?.type===`wifi`?`${r}, a Wi-Fi interface: turning Wi-Fi off cuts this page off from the robot.`:null}default:return null}}function Fu(e,t){if(e===null)return`Never used`;let n=Math.max(0,Math.round((t-e)/1e3));if(n<60)return`Used just now`;if(n<3600)return`Used ${Math.floor(n/60)} min ago`;if(n<86400)return`Used ${Math.floor(n/3600)} h ago`;let r=Math.floor(n/86400);return r<30?`Used ${r} day${r===1?``:`s`} ago`:`Used on ${new Date(e).toLocaleDateString()}`}function Iu(e,t){let n=new Set(t.map(e=>e.name));if(!n.has(e))return e;let r=2;for(;n.has(`${e} (${r})`);)r++;return`${e} (${r})`}function Lu(e,t,n,r,i,a){return{...pu(`wifi`),name:Iu(e.ssid,a),ssid:e.ssid,security:e.security,password:t,identity:n,autoconnect:r,hidden:i}}function Ru(e){let t=e.filter(e=>e.state===100&&!e.external);return t.find(e=>e.gateway)??t[0]??null}function zu(e,t){if(e.error)return{text:e.error,tone:`failed`};let n=e.activity.find(e=>e.finished===null);if(n)return{text:`${n.label}…`,tone:`busy`};let r=e.activity[0];if(r?.error&&t-(r.finished??0)<6e4)return{text:`${r.label}: ${r.error}`,tone:`failed`};let i=[],a;switch(e.connectivity){case`full`:i.push(`Online`),a=`on`;break;case`limited`:i.push(`No internet`),a=`warn`;break;case`portal`:i.push(`Behind a login page`),a=`warn`;break;default:i.push(e.state.startsWith(`connected`)?`Local network only`:`Disconnected`),a=e.state.startsWith(`connected`)?`warn`:`off`}let o=Ru(e.devices);if(o){let e=o.ipv4[0]?.split(`/`)[0];i.push(`${o.name}: ${o.connectionName}${e?` (${e})`:``}`)}return e.via&&i.push(`this page through ${e.via}`),{text:i.join(` · `),tone:a}}function Bu(e,t){return t?`This widget is read-only`:e.canChange?!e.permissions.control&&!e.permissions.modify?`The server’s user may not change the robot’s networks (see the README: a polkit rule)`:null:`The networks are changed from the dashboard’s own pages only`}var Vu=[[10,[16]],[16,[28]],[26,[44]],[18,[32,32]],[24,[43,43]],[16,[27,27,27,27]],[18,[31,31,31,31]],[22,[38,38,39,39]],[22,[36,36,36,37,37]],[26,[43,43,43,43,44]],[30,[50,51,51,51,51]],[22,[36,36,36,36,36,36,37,37]],[22,[37,37,37,37,37,37,37,37,38]]],Hu=[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62]];function Uu(e,t){let n=0;for(let r=7;r>=0;r--)n=n<<1^(n>>>7)*285,n^=(t>>>r&1)*e;return n}function Wu(e){let t=Array(e).fill(0);t[e-1]=1;let n=1;for(let r=0;r<e;r++){for(let r=0;r<e;r++)t[r]=Uu(t[r],n),r+1<e&&(t[r]^=t[r+1]);n=Uu(n,2)}return t}function Gu(e,t){let n=t.map(()=>0);for(let r of e){let e=r^(n.shift()??0);n.push(0),t.forEach((t,r)=>{n[r]^=Uu(t,e)})}return n}var Ku=[(e,t)=>(e+t)%2==0,(e,t)=>t%2==0,e=>e%3==0,(e,t)=>(e+t)%3==0,(e,t)=>(Math.floor(e/3)+Math.floor(t/2))%2==0,(e,t)=>e*t%2+e*t%3==0,(e,t)=>(e*t%2+e*t%3)%2==0,(e,t)=>((e+t)%2+e*t%3)%2==0];function qu(e,t){let[n,r]=Vu[t-1],i=r.reduce((e,t)=>e+t,0)*8,a=[],o=(e,t)=>{for(let n=t-1;n>=0;n--)a.push(e>>>n&1)};o(4,4),o(e.length,t<10?8:16),e.forEach(e=>o(e,8)),o(0,Math.min(4,i-a.length)),o(0,(8-a.length%8)%8);for(let e=236;a.length<i;e^=253)o(e,8);let s=[];for(let e=0;e<a.length;e+=8)s.push(a.slice(e,e+8).reduce((e,t)=>e<<1|t,0));let c=Wu(n),l=[],u=0;for(let e of r){let t=s.slice(u,u+e);l.push({data:t,ec:Gu(t,c)}),u+=e}let d=[];for(let e=0;e<Math.max(...r);e++)l.forEach(t=>e<t.data.length&&d.push(t.data[e]));for(let e=0;e<n;e++)l.forEach(t=>d.push(t.ec[e]));return d}var Ju=class{size;dark;fixed;constructor(e){this.size=e,this.dark=Array.from({length:e},()=>Array(e).fill(!1)),this.fixed=Array.from({length:e},()=>Array(e).fill(!1))}set(e,t,n){this.dark[t][e]=n,this.fixed[t][e]=!0}};function Yu(e,t){let{size:n}=e;for(let t=0;t<n;t++)e.set(6,t,t%2==0),e.set(t,6,t%2==0);for(let[t,r]of[[3,3],[n-4,3],[3,n-4]])for(let i=-4;i<=4;i++)for(let a=-4;a<=4;a++){let o=t+a,s=r+i;if(o>=0&&o<n&&s>=0&&s<n){let t=Math.max(Math.abs(a),Math.abs(i));e.set(o,s,t!==2&&t!==4)}}let r=Hu[t-1],i=r.length-1;if(r.forEach((t,n)=>r.forEach((r,a)=>{if(!(n===0&&a===0||n===0&&a===i||n===i&&a===0))for(let n=-2;n<=2;n++)for(let i=-2;i<=2;i++)e.set(t+i,r+n,Math.max(Math.abs(i),Math.abs(n))!==1)})),Xu(e,0),t>=7){let r=t;for(let e=0;e<12;e++)r=r<<1^(r>>>11)*7973;let i=t<<12|r;for(let t=0;t<18;t++){let r=(i>>>t&1)==1,a=n-11+t%3,o=Math.floor(t/3);e.set(a,o,r),e.set(o,a,r)}}}function Xu(e,t){let{size:n}=e,r=0|t,i=r;for(let e=0;e<10;e++)i=i<<1^(i>>>9)*1335;let a=(r<<10|i)^21522,o=e=>(a>>>e&1)==1;for(let t=0;t<=5;t++)e.set(8,t,o(t));e.set(8,7,o(6)),e.set(8,8,o(7)),e.set(7,8,o(8));for(let t=9;t<15;t++)e.set(14-t,8,o(t));for(let t=0;t<8;t++)e.set(n-1-t,8,o(t));for(let t=8;t<15;t++)e.set(8,n-15+t,o(t));e.set(8,n-8,!0)}function Zu(e,t){let{size:n}=e,r=0;for(let i=n-1;i>=1;i-=2){i===6&&(i=5);for(let a=0;a<n;a++)for(let o=0;o<2;o++){let s=i-o,c=i+1&2?a:n-1-a;!e.fixed[c][s]&&r<t.length*8&&(e.dark[c][s]=(t[r>>>3]>>>7-(r&7)&1)==1,r++)}}}function Qu(e,t){let n=Ku[t];for(let t=0;t<e.size;t++)for(let r=0;r<e.size;r++)!e.fixed[t][r]&&n(r,t)&&(e.dark[t][r]=!e.dark[t][r])}function $u(e){let t=e.length,n=0,r=[...e,...Array.from({length:t},(t,n)=>e.map(e=>e[n]))];for(let e of r){let r=1;for(let i=1;i<=t;i++)i<t&&e[i]===e[i-1]?r++:(r>=5&&(n+=3+(r-5)),r=1);let i=n=>n>=0&&n<t&&e[n];for(let e=0;e+7<=t;e++){if(!(i(e)&&!i(e+1)&&i(e+2)&&i(e+3)&&i(e+4)&&!i(e+5)&&i(e+6)))continue;let t=[1,2,3,4].every(t=>!i(e-t)),r=[7,8,9,10].every(t=>!i(e+t));(t||r)&&(n+=40)}}for(let r=0;r+1<t;r++)for(let i=0;i+1<t;i++){let t=e[r][i];t===e[r][i+1]&&t===e[r+1][i]&&t===e[r+1][i+1]&&(n+=3)}let i=e.reduce((e,t)=>e+t.filter(Boolean).length,0),a=t*t;return n+=(Math.ceil(Math.abs(i*20-a*10)/a)-1)*10,n}function ed(e,t){let n=new TextEncoder().encode(e),r=0;for(let e=1;e<=Vu.length&&r===0;e++){let t=Vu[e-1][1].reduce((e,t)=>e+t,0)*8;4+(e<10?8:16)+n.length*8<=t&&(r=e)}if(r===0)return null;let i=qu(n,r),a=null;for(let e of t===void 0?[0,1,2,3,4,5,6,7]:[t]){let t=new Ju(17+4*r);Yu(t,r),Zu(t,i),Qu(t,e),Xu(t,e);let n=$u(t.dark);(!a||n<a.score)&&(a={score:n,dark:t.dark})}return a.dark}function td(e,t,n,r){let i=e=>e.replace(/([\\;,:"])/g,`\\$1`),a=n===`nopass`?``:`P:${i(t)};`;return`WIFI:T:${n};S:${i(e)};${a}${r?`H:true;`:``};`}function nd({signal:e,size:t=18}){let n=mu(e),r=e=>n>=e?void 0:.22;return(0,J.jsxs)(`svg`,{viewBox:`0 0 24 24`,width:t,height:t,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,"aria-hidden":`true`,className:`net-signal`,children:[(0,J.jsx)(`path`,{d:`M2.5 9a14 14 0 0 1 19 0`,opacity:r(4)}),(0,J.jsx)(`path`,{d:`M5.5 12.5a9.5 9.5 0 0 1 13 0`,opacity:r(3)}),(0,J.jsx)(`path`,{d:`M8.6 15.9a5 5 0 0 1 6.8 0`,opacity:r(2)}),(0,J.jsx)(`circle`,{cx:`12`,cy:`19.3`,r:`1.3`,fill:`currentColor`,stroke:`none`})]})}function rd({kind:e,size:t=18}){let n={width:t,height:t};switch(e){case`wifi`:return(0,J.jsx)(O,{...n});case`hotspot`:return(0,J.jsx)(o,{...n});case`ethernet`:return(0,J.jsx)(c,{...n});case`vpn`:return(0,J.jsx)(He,{...n});default:return(0,J.jsx)(ue,{...n})}}function id({device:e,size:t=18}){return(0,J.jsx)(rd,{kind:e.type===`wifi`?`wifi`:e.type===`ethernet`?`ethernet`:e.type===`tun`||e.type===`wireguard`?`vpn`:`other`,size:t})}function ad({tone:e}){return(0,J.jsx)(`span`,{className:`net-dot ${e}`,"aria-hidden":`true`})}function od({label:e,help:t,error:n,children:r}){let i=(0,q.useId)();return(0,J.jsxs)(`div`,{className:`field`,children:[(0,J.jsx)(`label`,{className:`field-label`,htmlFor:i,children:e}),r(i),n?(0,J.jsx)(`p`,{className:`field-help error`,children:n}):t&&(0,J.jsx)(`p`,{className:`field-help`,children:t})]})}function sd({label:e,help:t,checked:n,onChange:r}){return(0,J.jsxs)(`div`,{className:`field`,children:[(0,J.jsxs)(`label`,{className:`toggle`,children:[(0,J.jsx)(`input`,{type:`checkbox`,checked:n,onChange:e=>r(e.target.checked)}),(0,J.jsx)(`span`,{className:`toggle-switch`,"aria-hidden":`true`}),(0,J.jsx)(`span`,{className:`field-label`,children:e})]}),t&&(0,J.jsx)(`p`,{className:`field-help`,children:t})]})}function cd({id:e,value:t,placeholder:n,autoFocus:r=!1,initiallyShown:i=!1,onChange:a}){let[o,s]=(0,q.useState)(i);return(0,J.jsxs)(`div`,{className:`net-password`,children:[(0,J.jsx)(`input`,{id:e,type:o?`text`:`password`,value:t,placeholder:n,autoComplete:`new-password`,autoCapitalize:`off`,spellCheck:!1,autoFocus:r,onChange:e=>a(e.target.value)}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:o?`Hide the password`:`Show the password`,"aria-label":o?`Hide the password`:`Show the password`,"aria-pressed":o,onClick:()=>s(e=>!e),children:o?(0,J.jsx)(P,{width:16,height:16}):(0,J.jsx)(Le,{width:16,height:16})})]})}function ld({text:e,label:t}){let n=(0,q.useMemo)(()=>{let t=ed(e);if(!t)return null;let n=``;return t.forEach((e,t)=>{for(let r=0;r<e.length;r++){if(!e[r])continue;let i=1;for(;r+i<e.length&&e[r+i];)i++;n+=`M${r+4} ${t+4}h${i}v1h-${i}z`,r+=i-1}}),{d:n,size:t.length+8}},[e]);return n?(0,J.jsxs)(`svg`,{className:`net-qr`,viewBox:`0 0 ${n.size} ${n.size}`,shapeRendering:`crispEdges`,role:`img`,"aria-label":t,children:[(0,J.jsx)(`rect`,{width:n.size,height:n.size,fill:`#fff`}),(0,J.jsx)(`path`,{d:n.d,fill:`#000`})]}):null}function ud(){let e=crypto.getRandomValues(new Uint32Array(12));return Array.from(e,e=>`abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789`[e%55]).join(``)}function dd(e){return/Disconnected from the robot|No answer from the robot/.test(rs(e))}async function fd(e,t,n){try{let n=await b.networkAction(e);n.message?G(n.message,`error`):t&&G(t)}catch(e){if(n&&dd(e)){G(`This page lost the robot: it moved to another network, where it is to be reached now`);return}throw e}}function pd({risk:e,error:t}){return(0,J.jsxs)(J.Fragment,{children:[e&&(0,J.jsx)(`p`,{className:`net-warning`,children:e}),t&&(0,J.jsx)(`p`,{className:`net-error`,role:`alert`,children:t})]})}function md({state:e,request:t,onClose:n}){let r=t.kind===`password`?t.connection:null,i=t.kind===`join`?t.network:e.networks.find(e=>e.ssid===r.ssid&&(!t.device||e.device===t.device)),a=r?.ssid||r?.name||i?.ssid||``,o=r?.security??i?.security??`wpa-psk`,[s,c]=(0,q.useState)(``),[l,u]=(0,q.useState)(r?.identity??``),[d,f]=(0,q.useState)(!0),[p,m]=(0,q.useState)(!1),[h,g]=(0,q.useState)(!1),[_,v]=(0,q.useState)(t.kind===`password`?t.error:null),y=o===`wpa-eap`,b=uu(o,s)??(y&&!l.trim()?`Your user name on the network`:null),x=r?{action:`connect`,uuid:r.uuid,device:t.kind===`password`?t.device:null,password:s}:{action:`create`,settings:Lu(i,s,l.trim(),d,!1,e.connections),connect:!0,device:i.device},S=Pu(e,x),C=async e=>{if(e.preventDefault(),m(!0),!b){g(!0),v(null);try{await fd(x,`Connected to ${a}`,S!==null),n()}catch(e){v(rs(e)),g(!1)}}};return(0,J.jsx)($e,{title:r?`The password of ${a}`:`Join ${a}`,onClose:n,children:(0,J.jsxs)(`form`,{className:`modal-body`,onSubmit:e=>void C(e),children:[i&&(0,J.jsxs)(`p`,{className:`net-dialog-meta`,children:[(0,J.jsx)(nd,{signal:i.signal,size:16}),[$l[o],hu(i.frequency),`${i.signal}%`].filter(Boolean).join(` · `)]}),y&&(0,J.jsx)(od,{label:`User name`,error:p&&!l.trim()?`Your user name on the network`:null,children:e=>(0,J.jsx)(`input`,{id:e,value:l,autoComplete:`off`,autoCapitalize:`off`,spellCheck:!1,autoFocus:!0,onChange:e=>u(e.target.value)})}),(0,J.jsx)(od,{label:`Password`,error:p?uu(o,s):null,help:y?`PEAP with MSCHAPv2, the server’s certificate not checked`:void 0,children:e=>(0,J.jsx)(cd,{id:e,value:s,autoFocus:!y,onChange:c})}),!r&&(0,J.jsx)(sd,{label:`Connect automatically`,help:`Whenever the robot is in its range`,checked:d,onChange:f}),(0,J.jsx)(pd,{risk:S,error:_}),(0,J.jsxs)(`div`,{className:`modal-actions`,children:[(0,J.jsx)(`button`,{type:`button`,className:`button`,onClick:n,children:`Cancel`}),(0,J.jsx)(`button`,{type:`submit`,className:`button ${S?`danger`:`primary`}`,disabled:h,children:h?`Connecting…`:r?`Connect`:`Join`})]})]})})}var hd={wifi:[{value:`auto`,label:`Automatic (DHCP)`},{value:`manual`,label:`Manual`},{value:`disabled`,label:`Disabled`}],hotspot:[{value:`shared`,label:`The robot gives them`}],ethernet:[{value:`auto`,label:`Automatic (DHCP)`},{value:`manual`,label:`Manual`},{value:`shared`,label:`Shared: the robot gives them, and its internet`},{value:`disabled`,label:`Disabled`}]},gd={bg:[1,2,3,4,5,6,7,8,9,10,11,12,13],a:[36,40,44,48,149,153,157,161,165]},_d=[`wpa-psk`,`sae`,`wpa-eap`,`open`,`owe`,`wep`];function vd(e,t){if(t.kind===`edit`)return fu(t.connection);let n=t.settings,r=pu(n);if(n===`hotspot`){let n=e.devices.filter(e=>e.type===`wifi`&&e.accessPoint),i=n.find(e=>e.name===t.device)??n.find(t=>t.name!==e.via)??n[0],a=(b.hostname||`robot`).slice(0,32);return{...r,interface:i?.name??``,ssid:a,name:Iu(a,e.connections),password:ud(),autoconnect:!1}}return n===`ethernet`?{...r,interface:t.device??``,name:Iu(`Wired connection`,e.connections)}:{...r,hidden:!0,interface:t.device??``}}function yd({state:e,request:t,onClose:n}){let r=t.kind===`edit`?t.connection:null,[i,a]=(0,q.useState)(()=>vd(e,t)),[o,s]=(0,q.useState)(i.addresses.join(`, `)),[c,l]=(0,q.useState)(i.dns.join(`, `)),[u,d]=(0,q.useState)(r!==null),[f,p]=(0,q.useState)(!0),[m,h]=(0,q.useState)(!1),[g,_]=(0,q.useState)(!1),[v,y]=(0,q.useState)(null),{kind:b}=i,x=b!==`ethernet`,S=t=>a(n=>{let i={...n,...t};return!u&&t.ssid!==void 0&&i.ssid&&(i.name=Iu(i.ssid,e.connections.filter(e=>e.uuid!==r?.uuid))),i}),C=du(i,!r);r&&x&&i.security!==(r.security??`open`)&&eu(i.security)&&i.password===null&&(C.password=`The network’s password, for its new security`);let w=e=>m?C[e]??null:null,T=r?{action:`modify`,uuid:r.uuid,settings:i}:{action:`create`,settings:i,connect:f,device:null},E=Pu(e,T),D=async e=>{if(e.preventDefault(),h(!0),Object.keys(C).length>0)return;_(!0),y(null);let t=`Saved ${i.name}`;!r&&f&&(t=b===`hotspot`?`The access point ${i.ssid} is on`:`Connected to ${i.ssid||i.name}`);try{await fd(T,t,E!==null),n()}catch(e){y(rs(e)),_(!1)}},O=e.devices.filter(e=>e.type===`wifi`&&(b!==`hotspot`||e.accessPoint)),k=e.devices.filter(e=>e.type===`ethernet`),A=e.devices.find(e=>e.name===i.interface),j;j=r?`Edit ${r.name}`:b===`hotspot`?`New access point`:b===`ethernet`?`New wired connection`:`Join a hidden network`;let M=`Save`;!r&&f&&(M=b===`hotspot`?`Start`:b===`wifi`?`Join`:`Connect`);let N=b===`ethernet`||O.length>1||i.interface||b===`hotspot`?(0,J.jsx)(od,{label:`Interface`,error:w(`interface`),help:b===`hotspot`?void 0:`Bound to it, the connection is not used by other interfaces: for a sensor’s cable, say`,children:t=>(0,J.jsxs)(`select`,{id:t,value:i.interface,onChange:e=>S({interface:e.target.value}),children:[b!==`hotspot`&&(0,J.jsx)(`option`,{value:``,children:x?`Any Wi-Fi interface`:`Any wired interface`}),b===`hotspot`&&!i.interface&&(0,J.jsx)(`option`,{value:``,children:`Choose one`}),(x?O:k).map(t=>(0,J.jsxs)(`option`,{value:t.name,children:[t.name,t.product||t.vendor?` (${t.product||t.vendor})`:``,t.name===e.via?` · this page`:``]},t.name)),i.interface&&!e.devices.some(e=>e.name===i.interface)&&(0,J.jsxs)(`option`,{value:i.interface,children:[i.interface,` (not here)`]})]})}):null,P=r&&i.password===null?`Unchanged: type a new one to change it`:void 0;return(0,J.jsx)($e,{title:j,onClose:n,wide:!0,children:(0,J.jsxs)(`form`,{className:`modal-body net-form`,onSubmit:e=>void D(e),children:[b===`hotspot`&&N,x&&(0,J.jsx)(od,{label:`Network name (SSID)`,error:w(`ssid`),help:b===`hotspot`?`What phones and laptops list`:void 0,children:e=>(0,J.jsx)(`input`,{id:e,value:i.ssid,autoComplete:`off`,autoCapitalize:`off`,spellCheck:!1,autoFocus:!r,onChange:e=>S({ssid:e.target.value})})}),x&&(0,J.jsxs)(`div`,{className:`net-form-row`,children:[(0,J.jsx)(od,{label:`Security`,error:w(`security`),children:e=>(0,J.jsx)(`select`,{id:e,value:i.security,onChange:e=>{let t=e.target.value;S({security:t,password:eu(t)?i.password??(r?null:``):null})},children:(b===`hotspot`?[`wpa-psk`,`open`]:_d).map(e=>(0,J.jsx)(`option`,{value:e,children:b===`hotspot`&&e===`wpa-psk`?`WPA2 Personal`:$l[e]},e))})}),i.security===`wpa-eap`&&(0,J.jsx)(od,{label:`User name`,error:w(`identity`),children:e=>(0,J.jsx)(`input`,{id:e,value:i.identity,autoComplete:`off`,autoCapitalize:`off`,spellCheck:!1,onChange:e=>S({identity:e.target.value})})})]}),x&&eu(i.security)&&(0,J.jsx)(od,{label:`Password`,error:w(`password`),help:P??(b===`hotspot`?`8 to 63 characters: give it to who joins, or share the access point’s QR code`:i.security===`wpa-eap`?`PEAP with MSCHAPv2, the server’s certificate not checked`:void 0),children:e=>(0,J.jsxs)(`div`,{className:`net-password-row`,children:[(0,J.jsx)(cd,{id:e,value:i.password??``,placeholder:r&&i.password===null?`Unchanged`:void 0,initiallyShown:b===`hotspot`&&!r,onChange:e=>S({password:e===``&&r?null:e})}),b===`hotspot`&&(0,J.jsx)(`button`,{type:`button`,className:`button`,onClick:()=>S({password:ud()}),children:`New one`})]})}),b===`hotspot`&&i.security===`open`&&(0,J.jsx)(`p`,{className:`net-warning`,children:`Open: anyone in range may join the robot’s network, and reach the dashboard.`}),b===`hotspot`&&(0,J.jsxs)(`div`,{className:`net-form-row`,children:[(0,J.jsx)(od,{label:`Band`,children:e=>(0,J.jsxs)(`select`,{id:e,value:i.band,onChange:e=>S({band:e.target.value,channel:0}),children:[(0,J.jsx)(`option`,{value:``,children:`Automatic`}),(0,J.jsx)(`option`,{value:`bg`,disabled:A&&!A.band24,children:`2.4 GHz: farther, every device`}),(0,J.jsx)(`option`,{value:`a`,disabled:A&&!A.band5,children:`5 GHz: faster`})]})}),(0,J.jsx)(od,{label:`Channel`,error:w(`channel`),children:e=>(0,J.jsxs)(`select`,{id:e,value:i.channel,disabled:!i.band,onChange:e=>S({channel:Number(e.target.value)}),children:[(0,J.jsx)(`option`,{value:0,children:`Automatic`}),(i.band?gd[i.band]:[]).map(e=>(0,J.jsx)(`option`,{value:e,children:e},e)),i.band&&i.channel!==0&&!gd[i.band].includes(i.channel)&&(0,J.jsx)(`option`,{value:i.channel,children:i.channel})]})})]}),b===`wifi`&&(0,J.jsx)(sd,{label:`Hidden network`,help:`It does not show its name: the robot looks for it by name`,checked:i.hidden,onChange:e=>S({hidden:e})}),b!==`hotspot`&&N,(0,J.jsx)(od,{label:`Connection name`,error:w(`name`),help:`How the robot’s lists name it`,children:e=>(0,J.jsx)(`input`,{id:e,value:i.name,autoComplete:`off`,onChange:e=>{d(!0),S({name:e.target.value})}})}),(0,J.jsx)(`h3`,{className:`net-form-heading`,children:`Addresses`}),b!==`hotspot`&&(0,J.jsx)(od,{label:`IPv4`,error:w(`ipv4`),children:e=>(0,J.jsx)(`select`,{id:e,value:i.ipv4,onChange:e=>S({ipv4:e.target.value}),children:hd[b].map(e=>(0,J.jsx)(`option`,{value:e.value,children:e.label},e.value))})}),(i.ipv4===`manual`||i.ipv4===`shared`)&&(0,J.jsx)(od,{label:i.ipv4===`manual`?`The robot’s address`:`The robot’s address on the network it gives`,error:w(`addresses`),help:i.ipv4===`manual`?`With its network’s size: 192.168.1.20/24. Several: separated by commas`:`Optional: 10.42.0.1/24 if empty. The others get addresses of its network`,children:e=>(0,J.jsx)(`input`,{id:e,value:o,placeholder:i.ipv4===`manual`?`192.168.1.20/24`:`10.42.0.1/24`,inputMode:`decimal`,autoComplete:`off`,spellCheck:!1,onChange:e=>{s(e.target.value),S({addresses:cu(e.target.value)})}})}),i.ipv4!==`disabled`&&b!==`hotspot`&&(0,J.jsxs)(`div`,{className:`net-form-row`,children:[i.ipv4===`manual`&&(0,J.jsx)(od,{label:`Gateway`,error:w(`gateway`),help:`Optional: the way to other networks`,children:e=>(0,J.jsx)(`input`,{id:e,value:i.gateway,placeholder:`192.168.1.1`,inputMode:`decimal`,autoComplete:`off`,spellCheck:!1,onChange:e=>S({gateway:e.target.value.trim()})})}),(0,J.jsx)(od,{label:`DNS servers`,error:w(`dns`),help:i.ipv4===`manual`?`Optional, separated by commas`:`Optional: besides the network’s`,children:e=>(0,J.jsx)(`input`,{id:e,value:c,placeholder:`1.1.1.1, 8.8.8.8`,inputMode:`decimal`,autoComplete:`off`,spellCheck:!1,onChange:e=>{l(e.target.value),S({dns:cu(e.target.value)})}})})]}),(0,J.jsx)(`h3`,{className:`net-form-heading`,children:`When`}),(0,J.jsx)(sd,{label:b===`hotspot`?`Start automatically`:`Connect automatically`,help:b===`hotspot`?`When the robot starts: it is then the robot’s network wherever it goes`:b===`wifi`?`Whenever the robot is in its range`:`Whenever a cable is plugged in`,checked:i.autoconnect,onChange:e=>S({autoconnect:e})}),i.autoconnect&&(0,J.jsx)(od,{label:`Priority`,error:w(`priority`),help:`Of the connections that connect automatically, the highest first`,children:e=>(0,J.jsx)(`input`,{id:e,type:`number`,min:-999,max:999,step:1,value:Number.isFinite(i.priority)?i.priority:``,onChange:e=>S({priority:e.target.value===``?0:Number(e.target.value)})})}),!r&&(0,J.jsx)(sd,{label:b===`hotspot`?`Start it now`:`Connect now`,checked:f,onChange:p}),(0,J.jsx)(pd,{risk:E,error:v}),(0,J.jsxs)(`div`,{className:`modal-actions`,children:[(0,J.jsx)(`button`,{type:`button`,className:`button`,onClick:n,children:`Cancel`}),(0,J.jsx)(`button`,{type:`submit`,className:`button ${E?`danger`:`primary`}`,disabled:g,children:g?f&&!r?`Connecting…`:`Saving…`:M})]})]})})}function bd({text:e,what:t}){return(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:`Copy the ${t}`,"aria-label":`Copy the ${t}`,onClick:()=>ts(e).then(()=>G(`Copied the ${t}`),e=>G(`Could not copy the ${t}: ${rs(e)}`,`error`)),children:(0,J.jsx)(z,{width:15,height:15})})}function xd({connection:e,onClose:t}){let[n,r]=(0,q.useState)(null),[i,a]=(0,q.useState)(null),[o,s]=(0,q.useState)(!1);(0,q.useEffect)(()=>{let t=!0;return b.networkAction({action:`secrets`,uuid:e.uuid}).then(e=>t&&r(e),e=>t&&a(rs(e))),()=>{t=!1}},[e.uuid]);let c=n?.ssid??e.ssid,l=n?.security??e.security??`open`,u=!eu(l),d=u||!!n?.password,f=n&&d&&l!==`wpa-eap`?td(c,n.password,u?`nopass`:l===`wep`?`WEP`:`WPA`,n.hidden):null,p;return p=i?(0,J.jsx)(`p`,{className:`net-error`,children:i}):n?(0,J.jsxs)(J.Fragment,{children:[f&&(0,J.jsxs)(`div`,{className:`net-share-code`,children:[(0,J.jsx)(ld,{text:f,label:`QR code to join ${c}`}),(0,J.jsxs)(`p`,{className:`net-muted`,children:[`Point a phone’s camera at it to join `,c]})]}),(0,J.jsxs)(`dl`,{className:`net-facts net-share-facts`,children:[(0,J.jsxs)(`div`,{children:[(0,J.jsx)(`dt`,{children:`Network`}),(0,J.jsxs)(`dd`,{children:[(0,J.jsx)(`span`,{children:c}),(0,J.jsx)(bd,{text:c,what:`network’s name`})]})]}),(0,J.jsxs)(`div`,{children:[(0,J.jsx)(`dt`,{children:`Security`}),(0,J.jsx)(`dd`,{children:$l[l]})]}),!u&&(0,J.jsxs)(`div`,{children:[(0,J.jsx)(`dt`,{children:`Password`}),(0,J.jsx)(`dd`,{children:d?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`span`,{className:`net-secret`,children:o?n.password:`•`.repeat(Math.min(16,n.password.length))}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:o?`Hide the password`:`Show the password`,"aria-label":o?`Hide the password`:`Show the password`,"aria-pressed":o,onClick:()=>s(e=>!e),children:o?`Hide`:`Show`}),(0,J.jsx)(bd,{text:n.password,what:`password`})]}):(0,J.jsx)(`span`,{className:`net-muted`,children:`Not readable: the server’s user may not read NetworkManager’s secrets, or they are kept by a desktop’s keyring`})})]})]})]}):(0,J.jsx)(`p`,{className:`net-muted`,children:`Reading the password…`}),(0,J.jsx)($e,{title:`Share ${c||e.name}`,onClose:t,children:(0,J.jsxs)(`div`,{className:`modal-body`,children:[p,(0,J.jsx)(`div`,{className:`modal-actions`,children:(0,J.jsx)(`button`,{type:`button`,className:`button primary`,onClick:t,autoFocus:!0,children:`Done`})})]})})}function Sd({request:e,state:t,onClose:n}){switch(e.kind){case`join`:case`password`:return(0,J.jsx)(md,{state:t,request:e,onClose:n});case`create`:case`edit`:return(0,J.jsx)(yd,{state:t,request:e,onClose:n});case`share`:return(0,J.jsx)(xd,{connection:e.connection,onClose:n})}}var Cd={auto:`Automatic (DHCP)`,manual:`Manual`,shared:`Shared: the robot gives the addresses`,disabled:`Disabled`,"link-local":`Link-local only`};function $({label:e,children:t}){return(0,J.jsxs)(`div`,{children:[(0,J.jsx)(`dt`,{children:e}),(0,J.jsx)(`dd`,{children:t})]})}function wd({icon:e,name:t,title:n,children:r,footer:i,onClose:a}){return(0,J.jsxs)(`section`,{className:`net-details`,"aria-label":`${t}: details`,children:[(0,J.jsxs)(`header`,{className:`net-details-header`,children:[(0,J.jsx)(`span`,{className:`net-details-icon`,children:e}),(0,J.jsx)(`strong`,{className:`net-details-name`,title:n??t,children:t}),(0,J.jsx)(`span`,{className:`net-spacer`}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:`Close (Esc)`,"aria-label":`Close`,onClick:a,children:(0,J.jsx)(H,{width:16,height:16})})]}),(0,J.jsx)(`div`,{className:`net-details-body`,children:r}),i&&(0,J.jsx)(`footer`,{className:`net-details-actions`,children:i})]})}function Td({label:e,actions:t,danger:n=!1,primary:r=!1,disabled:i,onClick:a}){let o=t.blocked??i??null;return(0,J.jsx)(`button`,{type:`button`,className:`button${n?` danger`:r?` primary`:``}`,disabled:o!==null,title:o??void 0,onClick:a,children:e})}function Ed({list:e}){return e.length>0?(0,J.jsx)(J.Fragment,{children:e.map(e=>(0,J.jsx)(`span`,{className:`net-fact-line`,children:e},e))}):(0,J.jsx)(`span`,{className:`net-muted`,children:`None`})}function Dd(e){return/\/netplan-[^/]*$/.test(e)?`From netplan (/etc/netplan)`:e.startsWith(`/run/`)?`In memory: lost at reboot`:null}function Od({state:e,connection:t,now:n,signal:r=!0}){let i=e.devices.find(e=>e.name===t.device),a=t.kind===`wifi`||t.kind===`hotspot`,o=r&&t.kind===`wifi`?e.networks.find(e=>e.ssid===t.ssid):void 0,s=Dd(t.file),c=i?.state===100,l;return l=t.activeState===`activated`?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(ad,{tone:`on`}),` `,t.kind===`hotspot`?`On`:`Connected`,`, on `,t.device]}):t.activeState?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(ad,{tone:`busy`}),` `,i?yu(i,e.wifi).text:t.activeState]}):`Not connected`,(0,J.jsxs)(`dl`,{className:`net-facts`,children:[(0,J.jsx)($,{label:`Status`,children:l}),(0,J.jsx)($,{label:`Kind`,children:Su(t)}),a&&t.ssid&&t.ssid!==t.name&&(0,J.jsx)($,{label:`Network`,children:t.ssid}),a&&t.security&&(0,J.jsx)($,{label:`Security`,children:$l[t.security]}),t.security===`wpa-eap`&&t.identity&&(0,J.jsx)($,{label:`User name`,children:t.identity}),t.kind===`hotspot`&&(0,J.jsxs)($,{label:`Band`,children:[t.band?t.band===`a`?`5 GHz`:`2.4 GHz`:`Automatic`,t.channel>0&&`, channel ${t.channel}`]}),o&&(0,J.jsxs)($,{label:`Signal`,children:[o.signal,`% · `,hu(o.frequency),`, channel `,o.channel]}),t.hidden&&(0,J.jsx)($,{label:`Hidden`,children:`Its name is not shown: looked for by name`}),(t.kind===`wifi`||t.kind===`hotspot`||t.kind===`ethernet`)&&(0,J.jsx)($,{label:`Interface`,children:t.interface||`Any`}),t.ipv4Method&&(0,J.jsx)($,{label:`IPv4`,children:Cd[t.ipv4Method]??t.ipv4Method}),(c||t.addresses.length>0)&&(0,J.jsx)($,{label:`Address`,children:(0,J.jsx)(Ed,{list:c?i.ipv4:t.addresses})}),(c?i.gateway:t.gateway)&&(0,J.jsx)($,{label:`Gateway`,children:c?i.gateway:t.gateway}),(c?i.dns:t.dns).length>0&&(0,J.jsx)($,{label:`DNS`,children:(0,J.jsx)(Ed,{list:c?i.dns:t.dns})}),(0,J.jsx)($,{label:`Automatic`,children:t.autoconnect?`Yes${t.priority===0?``:`, priority ${t.priority}`}`:`No: only when asked`}),(0,J.jsx)($,{label:`Last used`,children:(0,J.jsx)(`span`,{title:t.lastUsed?new Date(t.lastUsed).toLocaleString():void 0,children:Fu(t.lastUsed,n).replace(/^Used (on )?/,``).replace(/^Never used$/,`Never`)})}),t.file&&(0,J.jsxs)($,{label:`File`,children:[(0,J.jsx)(`span`,{className:`net-fact-line`,children:t.file}),s&&(0,J.jsx)(`span`,{className:`net-fact-line net-muted`,children:s})]}),(0,J.jsx)($,{label:`UUID`,children:(0,J.jsx)(`span`,{className:`net-mono`,children:t.uuid})})]})}function kd({connection:e,actions:t}){if(t.readOnly||e.external)return null;let n=e.kind===`hotspot`,r=e.kind===`wifi`||n||e.kind===`ethernet`,i=e.readOnly?`NetworkManager has it read-only`:null;return(0,J.jsxs)(J.Fragment,{children:[e.activeState?(0,J.jsx)(Td,{label:n?`Stop`:`Disconnect`,actions:t,onClick:()=>t.disconnect(e)}):(0,J.jsx)(Td,{label:n?`Start`:`Connect`,primary:!0,actions:t,onClick:()=>t.connect(e,null)}),r&&(0,J.jsx)(Td,{label:`Edit…`,actions:t,disabled:i,onClick:()=>t.edit(e)}),(e.kind===`wifi`||n)&&(0,J.jsx)(Td,{label:`Share…`,actions:t,onClick:()=>t.share(e)}),(0,J.jsx)(Td,{label:`Forget…`,danger:!0,actions:t,disabled:i,onClick:()=>t.forget(e)})]})}function Ad({state:e,connection:t,actions:n,now:r,onClose:i}){let{failed:a}=Mu(e.activity,{uuid:t.uuid},r);return(0,J.jsxs)(wd,{icon:(0,J.jsx)(rd,{kind:t.kind,size:16}),name:t.name,onClose:i,footer:(0,J.jsx)(kd,{connection:t,actions:n}),children:[a&&(0,J.jsx)(`p`,{className:`net-error`,children:`${a.label}: ${a.error}`}),t.external&&(0,J.jsx)(`p`,{className:`net-muted`,children:`NetworkManager made it for an interface another program configures (docker, a VPN client…): it is not changed here.`}),(0,J.jsx)(Od,{state:e,connection:t,now:r})]})}function jd({state:e,network:t,actions:n,now:r,onClose:i}){let a=Ou(e.connections,t.ssid,t.device),{failed:o}=Mu(e.activity,{uuid:a?.uuid??null,ssid:t.ssid,device:t.device},r),s=a?(0,J.jsx)(kd,{connection:a,actions:n}):n.readOnly?null:(0,J.jsx)(Td,{label:`Join…`,primary:!0,actions:n,onClick:()=>n.join(t)});return(0,J.jsxs)(wd,{icon:(0,J.jsx)(nd,{signal:t.signal,size:16}),name:t.ssid,onClose:i,footer:s,children:[o&&(0,J.jsx)(`p`,{className:`net-error`,children:`${o.label}: ${o.error}`}),(0,J.jsxs)(`dl`,{className:`net-facts`,children:[(0,J.jsxs)($,{label:`Signal`,children:[t.signal,`%`]}),(0,J.jsxs)($,{label:`Security`,children:[$l[t.security],t.securityText&&` (${t.securityText})`]}),(0,J.jsxs)($,{label:`Band`,children:[hu(t.frequency),`, channel `,t.channel,` (`,t.frequency,` MHz)`]}),t.rate&&(0,J.jsx)($,{label:`Rate`,children:t.rate}),(0,J.jsxs)($,{label:`Access point`,children:[(0,J.jsx)(`span`,{className:`net-mono`,children:t.bssid}),t.points>1&&(0,J.jsxs)(`span`,{className:`net-fact-line net-muted`,children:[`The strongest of `,t.points]})]}),t.mode!==`Infra`&&(0,J.jsx)($,{label:`Mode`,children:t.mode}),(0,J.jsx)($,{label:`Seen by`,children:t.device})]}),a&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsxs)(`h3`,{children:[`Saved as `,a.name]}),(0,J.jsx)(Od,{state:e,connection:a,now:r,signal:!1})]})]})}function Md({state:e,device:t,actions:n,onClose:r}){let i=yu(t,e.wifi),a=t.connection?e.connections.find(e=>e.uuid===t.connection):void 0,o=t.type===`wifi`?`wifi`:t.type===`ethernet`?`ethernet`:null,s=o?e.connections.filter(n=>!n.activeState&&(n.kind===o||o===`wifi`&&n.kind===`hotspot`)&&Eu(n,[t])&&(n.kind!==`wifi`||e.networks.some(e=>e.device===t.name&&e.ssid===n.ssid))):[],c=[t.band24&&`2.4 GHz`,t.band5&&`5 GHz`].filter(Boolean).join(` and `);return(0,J.jsxs)(wd,{icon:(0,J.jsx)(id,{device:t,size:16}),name:t.name,onClose:r,footer:a&&!t.external&&!n.readOnly?(0,J.jsx)(Td,{label:a.kind===`hotspot`?`Stop the access point`:`Disconnect`,actions:n,onClick:()=>n.disconnect(a)}):null,children:[(0,J.jsxs)(`dl`,{className:`net-facts`,children:[(0,J.jsxs)($,{label:`Status`,children:[(0,J.jsx)(ad,{tone:i.tone}),` `,i.text]}),t.reason&&t.state!==100&&(0,J.jsx)($,{label:`Why`,children:t.reason}),(0,J.jsx)($,{label:`Kind`,children:bu(t)}),(t.vendor||t.product)&&(0,J.jsx)($,{label:`Hardware`,children:[t.vendor,t.product].filter(Boolean).join(` `)}),t.driver&&(0,J.jsx)($,{label:`Driver`,children:t.driver}),t.hwaddr&&(0,J.jsx)($,{label:`MAC address`,children:(0,J.jsx)(`span`,{className:`net-mono`,children:t.hwaddr})}),t.speed&&(0,J.jsx)($,{label:`Speed`,children:t.speed}),t.mtu&&(0,J.jsx)($,{label:`MTU`,children:t.mtu}),t.type===`wifi`&&(0,J.jsx)($,{label:`Access point`,children:t.accessPoint?`It can be one${c?`, on ${c}`:``}`:`Not supported`}),(0,J.jsx)($,{label:`IPv4`,children:(0,J.jsx)(Ed,{list:t.ipv4})}),t.ipv6.length>0&&(0,J.jsx)($,{label:`IPv6`,children:(0,J.jsx)(Ed,{list:t.ipv6})}),t.gateway&&(0,J.jsx)($,{label:`Gateway`,children:t.gateway}),t.dns.length>0&&(0,J.jsx)($,{label:`DNS`,children:(0,J.jsx)(Ed,{list:t.dns})})]}),s.length>0&&!n.readOnly&&!t.external&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`h3`,{children:`Connect with`}),(0,J.jsx)(`ul`,{className:`net-choices`,children:s.map(e=>(0,J.jsxs)(`li`,{children:[(0,J.jsx)(rd,{kind:e.kind,size:15}),(0,J.jsx)(`span`,{className:`net-choice-name`,children:e.name}),(0,J.jsx)(Td,{label:e.kind===`hotspot`?`Start`:`Connect`,actions:n,onClick:()=>n.connect(e,t.name)})]},e.uuid))})]})]})}function Nd({selection:e,state:t,actions:n,now:r,onClose:i}){switch(e.type){case`network`:{let a=t.networks.find(t=>t.device===e.device&&t.ssid===e.ssid);return a?(0,J.jsx)(jd,{state:t,network:a,actions:n,now:r,onClose:i}):null}case`connection`:{let a=t.connections.find(t=>t.uuid===e.uuid);return a?(0,J.jsx)(Ad,{state:t,connection:a,actions:n,now:r,onClose:i}):null}case`device`:{let r=t.devices.find(t=>t.name===e.name);return r?(0,J.jsx)(Md,{state:t,device:r,actions:n,onClose:i}):null}}}function Pd(e,t){return JSON.stringify(e)===JSON.stringify(t)}function Fd({label:e,busy:t,primary:n=!1,actions:r,onClick:i}){return r.readOnly?null:(0,J.jsx)(`button`,{type:`button`,className:`net-action${n?` primary`:``}`,disabled:!!t||r.blocked!==null,title:r.blocked??void 0,onClick:i,children:t??e})}function Id({selected:e,icon:t,name:n,tags:r,sub:i,tone:a,action:o,onSelect:s}){return(0,J.jsxs)(`li`,{className:`net-row${e?` selected`:``}`,children:[(0,J.jsxs)(`button`,{type:`button`,className:`net-row-main`,"aria-pressed":e,onClick:s,children:[(0,J.jsx)(`span`,{className:`net-row-icon ${a}`,children:t}),(0,J.jsxs)(`span`,{className:`net-row-text`,children:[(0,J.jsxs)(`span`,{className:`net-row-name`,children:[(0,J.jsx)(`span`,{className:`net-row-label`,title:n,children:n}),r]}),(0,J.jsxs)(`span`,{className:`net-row-sub ${a}`,title:i,children:[(a===`busy`||a===`failed`)&&(0,J.jsx)(ad,{tone:a}),(0,J.jsx)(`span`,{className:`net-row-sub-text`,children:i})]})]})]}),o&&(0,J.jsx)(`div`,{className:`net-row-actions`,children:o})]})}function Ld({state:e,device:t}){return t&&t===e.via?(0,J.jsx)(`span`,{className:`net-tag accent`,title:`This page reaches the robot through it`,children:`this page`}):null}function Rd(e){return e?.ipv4[0]?.split(`/`)[0]??``}function zd({connection:e,...t}){let{state:n,actions:r,selection:i,onSelect:a,now:o}=t,s=n.devices.find(t=>t.name===e.device),{running:c,failed:l}=Mu(n.activity,{uuid:e.uuid,ssid:e.ssid||null,device:e.device},o),u=e.kind===`hotspot`,d,f=``;c?(d=`${c.label}…`,f=`busy`):e.activeState===`activated`?(d=[u?`On, on ${e.device}`:`Connected on ${e.device}`,Rd(s)].filter(Boolean).join(` · `),f=`on`):e.activeState===`activating`?(d=s?yu(s,n.wifi).text:`Connecting…`,f=`busy`):e.activeState===`deactivating`?(d=`Disconnecting…`,f=`busy`):l?(d=l.error??``,f=`failed`):d=[e.kind===`ethernet`&&e.ipv4Method===`manual`?e.addresses.join(`, `):u&&e.ssid!==e.name?e.ssid:``,Fu(e.lastUsed,o),e.autoconnect?``:`not automatic`].filter(Boolean).join(` · `);let p=e.activeState!==null,m={type:`connection`,uuid:e.uuid},h=n.devices.find(t=>t.name===e.interface),g=h&&n.devices.filter(e=>e.type===h.type).length>1;return(0,J.jsx)(Id,{selected:Pd(i,m),icon:(0,J.jsx)(rd,{kind:e.kind}),name:e.name,tags:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(Ld,{state:n,device:e.device}),g&&(0,J.jsx)(`span`,{className:`net-tag`,children:e.interface})]}),sub:d,tone:f,onSelect:()=>a(m),action:e.external?null:p?(0,J.jsx)(Fd,{label:u?`Stop`:`Disconnect`,busy:c?`…`:null,actions:r,onClick:()=>r.disconnect(e)}):(0,J.jsx)(Fd,{label:u?`Start`:`Connect`,busy:c?`…`:null,primary:!0,actions:r,onClick:()=>r.connect(e,null)})})}function Bd({network:e,saved:t,device:n,...r}){let{state:i,actions:a,selection:o,onSelect:s,now:c}=r,{running:l,failed:u}=Mu(i.activity,{uuid:t?.uuid??null,ssid:e.ssid,device:n.name},c),d=e.active&&n.connection!==null,f=d?i.connections.find(e=>e.uuid===n.connection):void 0,p=hu(e.frequency),m,h=``;if(l)m=`${l.label}…`,h=`busy`;else if(d){let e=yu(n,i.wifi);m=n.state===100?[`Connected`,Rd(n),p].filter(Boolean).join(` · `):e.text,h=e.tone}else u?(m=u.error??``,h=`failed`):m=[t?`Saved`:``,e.securityText||`Open`,p].filter(Boolean).join(` · `);let g={type:`network`,device:n.name,ssid:e.ssid},_;return d&&f?_=(0,J.jsx)(Fd,{label:`Disconnect`,actions:a,onClick:()=>a.disconnect(f)}):d||(_=(0,J.jsx)(Fd,{label:t?`Connect`:`Join`,busy:l?`…`:null,primary:!0,actions:a,onClick:()=>t?a.connect(t,n.name):a.join(e)})),(0,J.jsx)(Id,{selected:Pd(o,g),icon:(0,J.jsx)(nd,{signal:e.signal}),name:e.ssid,tags:(0,J.jsxs)(J.Fragment,{children:[e.security!==`open`&&e.security!==`owe`&&(0,J.jsx)(Oe,{className:`net-lock`,width:12,height:12}),d&&(0,J.jsx)(Ld,{state:i,device:n.name})]}),sub:m,tone:d&&h===``?`on`:h,action:_,onSelect:()=>s(g)})}function Vd({icon:e,label:t,disabled:n,onClick:r}){return(0,J.jsx)(`li`,{className:`net-row`,children:(0,J.jsxs)(`button`,{type:`button`,className:`net-row-main net-add`,disabled:n!==null,title:n??void 0,onClick:r,children:[(0,J.jsx)(`span`,{className:`net-row-icon`,children:e}),(0,J.jsx)(`span`,{className:`net-row-label`,children:t})]})})}function Hd({device:e,many:t,...n}){let{state:r,text:i,actions:a}=n,s=r.connections.find(t=>t.device===e.name&&(t.kind===`wifi`||t.kind===`hotspot`)),c=ku(r,e.name,i),l=s?.kind===`wifi`&&!c.some(e=>e.network.active)?s:null,u=e.accessPoint?Au(r.connections,e.name).filter(e=>e.uuid!==s?.uuid):[],d=null;c.length===0&&(d=i.trim()?`No network matches “${i.trim()}”`:s?.kind===`hotspot`?`An access point does not look for networks: stop it to join one`:r.scanning?`Looking for networks…`:`No network in range`);let f=a.blocked;return(0,J.jsxs)(`section`,{className:`net-section`,"aria-label":`Wi-Fi of ${e.name}`,children:[t&&(0,J.jsxs)(`h3`,{className:`net-heading`,children:[e.name,(e.product||e.vendor)&&(0,J.jsx)(`span`,{className:`net-heading-detail`,children:e.product||e.vendor})]}),(0,J.jsxs)(`ul`,{className:`net-list`,children:[s?.kind===`hotspot`&&(0,J.jsx)(zd,{...n,connection:s}),l&&(0,J.jsx)(zd,{...n,connection:l}),c.map(t=>(0,J.jsx)(Bd,{...n,network:t.network,saved:t.saved,device:e},t.network.ssid))]}),d&&(0,J.jsx)(`p`,{className:`net-note`,children:d}),!i.trim()&&(0,J.jsxs)(`ul`,{className:`net-list net-extra`,children:[u.map(e=>(0,J.jsx)(zd,{...n,connection:e},e.uuid)),!a.readOnly&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(Vd,{icon:(0,J.jsx)(ce,{width:16,height:16}),label:`Join a hidden network…`,disabled:f,onClick:()=>a.create(`wifi`,e.name)}),e.accessPoint&&(0,J.jsx)(Vd,{icon:(0,J.jsx)(o,{width:16,height:16}),label:`New access point…`,disabled:f,onClick:()=>a.create(`hotspot`,e.name)})]})]})]})}function Ud(e){let{state:t,devices:n,actions:r}=e,i=n.filter(e=>e.type===`wifi`);return t.wifi?i.length===0?(0,J.jsxs)(`div`,{className:`net-empty`,children:[(0,J.jsx)(f,{width:28,height:28}),(0,J.jsx)(`p`,{children:`No Wi-Fi interface`})]}):(0,J.jsx)(`div`,{className:`net-scroll`,children:i.map(t=>(0,J.jsx)(Hd,{...e,device:t,many:i.length>1},t.name))}):(0,J.jsxs)(`div`,{className:`net-empty`,children:[(0,J.jsx)(f,{width:28,height:28}),(0,J.jsx)(`p`,{children:t.wifiHardware?`Wi-Fi is off`:`Wi-Fi is off by its hardware switch`}),t.wifiHardware&&!r.readOnly&&(0,J.jsx)(`button`,{type:`button`,className:`button primary`,disabled:r.blocked!==null,title:r.blocked??void 0,onClick:()=>r.setWifi(!0),children:`Turn Wi-Fi on`})]})}function Wd(e){let{state:t,devices:n,text:r,all:i,actions:a}=e,o=Du(t.connections,n,r,i);return(0,J.jsxs)(`div`,{className:`net-scroll`,children:[o.length===0&&(0,J.jsx)(`p`,{className:`net-note`,children:r.trim()?`No connection matches “${r.trim()}”`:`No connection saved`}),o.map(t=>(0,J.jsxs)(`section`,{className:`net-section`,"aria-label":t.title,children:[(0,J.jsxs)(`h3`,{className:`net-heading`,children:[t.title,(0,J.jsx)(`span`,{className:`net-heading-detail`,children:t.connections.length})]}),(0,J.jsx)(`ul`,{className:`net-list`,children:t.connections.map(t=>(0,J.jsx)(zd,{...e,connection:t},t.uuid))})]},t.kind)),!r.trim()&&!a.readOnly&&(0,J.jsx)(`ul`,{className:`net-list net-extra`,children:(0,J.jsx)(Vd,{icon:(0,J.jsx)(ce,{width:16,height:16}),label:`New wired connection…`,disabled:a.blocked,onClick:()=>a.create(`ethernet`,null)})})]})}function Gd(e){let{state:t,devices:n,selection:r,onSelect:i,actions:a}=e;return n.length===0?(0,J.jsx)(`p`,{className:`net-note`,children:`No interface`}):(0,J.jsx)(`div`,{className:`net-scroll`,children:(0,J.jsx)(`ul`,{className:`net-list`,children:n.map(e=>{let n=yu(e,t.wifi),o=e.connection?t.connections.find(t=>t.uuid===e.connection):void 0,s={type:`device`,name:e.name};return(0,J.jsx)(Id,{selected:Pd(r,s),icon:(0,J.jsx)(id,{device:e}),name:e.name,tags:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(Ld,{state:t,device:e.name}),(0,J.jsx)(`span`,{className:`net-row-detail`,children:e.product||e.vendor||bu(e)})]}),sub:[n.text,Rd(e)].filter(Boolean).join(` · `),tone:n.tone,onSelect:()=>i(s),action:o&&!e.external&&e.state===100?(0,J.jsx)(Fd,{label:o.kind===`hotspot`?`Stop`:`Disconnect`,actions:a,onClick:()=>a.disconnect(o)}):null},e.name)})})})}var Kd=620,qd=300,Jd=[{view:`wifi`,label:`Wi-Fi`,title:`The Wi-Fi networks in range`,Icon:O},{view:`saved`,label:`Saved`,title:`The connections saved`,Icon:B},{view:`devices`,label:`Interfaces`,title:`The interfaces, their addresses`,Icon:ue}];function Yd(e){let[t,n]=(0,q.useState)(()=>b.serverNow());return(0,q.useEffect)(()=>{let t=setInterval(()=>n(b.serverNow()),e);return()=>clearInterval(t)},[e]),t}function Xd({config:e}){let t=et(),[n,r]=(0,q.useState)(null),[i,a]=(0,q.useState)(e.view),[o,s]=(0,q.useState)(``),[c,l]=(0,q.useState)(null),[u,d]=(0,q.useState)(null),[p,m]=(0,q.useState)(null),[h,g]=(0,q.useState)({width:0,height:0}),_=(0,q.useRef)(null),v=Yd(5e3);(0,q.useEffect)(()=>a(e.view),[e.view]),(0,q.useEffect)(()=>b.subscribeNetwork(r),[]),(0,q.useEffect)(()=>{let e=_.current;if(!e)return;let t=new ResizeObserver(([e])=>g({width:e.contentRect.width,height:e.contentRect.height}));return t.observe(e),()=>t.disconnect()},[n!==null]);let y=(0,q.useMemo)(()=>gu(e.interfaces),[e.interfaces]),x=(0,q.useMemo)(()=>n?_u(n.devices,y,e.allInterfaces):[],[n,y,e.allInterfaces]),S=x.some(e=>e.type===`wifi`),C=i===`wifi`&&!S&&n&&x.length>0?`saved`:i,w=n?Bu(n,e.readOnly):null,T=(e,t)=>fd(e,t,n?Pu(n,e)!==null:!1).then(()=>null,e=>rs(e)),E=async(e,r)=>{let i=n?Pu(n,e):null;return!i&&!r.always||t.confirm({title:r.title,message:(0,J.jsxs)(J.Fragment,{children:[r.message,i&&(0,J.jsx)(`p`,{className:`net-warning`,children:i})]}),confirmLabel:r.confirmLabel,danger:i!==null||r.danger})},D=e=>e.kind===`wifi`&&e.ssid?e.ssid:e.name,k={blocked:w,readOnly:e.readOnly,connect:async(e,t)=>{let n={action:`connect`,uuid:e.uuid,device:t,password:null},r=e.kind===`hotspot`,i={title:r?`Start ${e.name}?`:`Switch to ${D(e)}?`,confirmLabel:r?`Start`:`Switch`,always:!1};if(!await E(n,i))return;let a=await T(n,r?`The access point ${D(e)} is on`:`Connected to ${D(e)}`);a&&(a===`Wrong password, or none given`&&e.security&&eu(e.security)?d({kind:`password`,connection:e,device:t,error:a}):G(`Could not ${r?`start`:`connect to`} ${D(e)}: ${a}`,`error`))},disconnect:async t=>{let n={action:`disconnect`,uuid:t.uuid},r=t.kind===`hotspot`,i={title:r?`Stop ${t.name}?`:`Disconnect from ${D(t)}?`,message:r?`Whoever joined the robot’s network loses it.`:t.autoconnect?`It does not connect by itself again until asked, or until the robot restarts.`:`It stays saved.`,confirmLabel:r?`Stop`:`Disconnect`,danger:!0,always:e.confirm};if(!await E(n,i))return;let a=await T(n,null);a&&G(`Could not ${r?`stop`:`disconnect from`} ${D(t)}: ${a}`,`error`)},forget:async e=>{let t={action:`delete`,uuid:e.uuid},n={title:`Forget ${e.name}?`,message:e.kind===`wifi`?`Its password is deleted with it: joining the network again asks for it.`:`Its settings are deleted with it.`,confirmLabel:`Forget`,danger:!0,always:!0};if(!await E(t,n))return;c?.type===`connection`&&c.uuid===e.uuid&&l(null);let r=await T(t,`Forgot ${e.name}`);r&&G(`Could not forget ${e.name}: ${r}`,`error`)},join:async e=>{if(eu(e.security)){d({kind:`join`,network:e});return}let t={action:`create`,settings:Lu(e,null,``,!0,!1,n?.connections??[]),connect:!0,device:e.device},r={title:`Join ${e.ssid}?`,message:e.security===`open`?`It is open: others on it may see what the robot sends.`:`It is open, its traffic encrypted (OWE).`,confirmLabel:`Join`,always:e.security===`open`};if(!await E(t,r))return;let i=await T(t,`Connected to ${e.ssid}`);i&&G(`Could not join ${e.ssid}: ${i}`,`error`)},create:(e,t)=>d({kind:`create`,settings:e,device:t}),edit:e=>d({kind:`edit`,connection:e}),share:e=>d({kind:`share`,connection:e}),setWifi:async t=>{let n={action:`wifi`,enabled:t},r={title:t?`Turn Wi-Fi on?`:`Turn Wi-Fi off?`,message:t?``:`Every Wi-Fi interface disconnects, access points stop.`,confirmLabel:t?`Turn on`:`Turn off`,danger:!t,always:!t&&e.confirm};if(!await E(n,r))return;let i=await T(n,null);i&&G(`Could not turn Wi-Fi ${t?`on`:`off`}: ${i}`,`error`)}},A=()=>void b.networkAction({action:`rescan`,device:null}).catch(e=>G(`Could not scan: ${rs(e)}`,`error`)),j=e=>{let t=e.currentTarget.getBoundingClientRect(),r=x.filter(e=>e.type===`wifi`),i=w;m({x:t.right-200,y:t.bottom+4,items:[{label:`Join a hidden network…`,disabled:i!==null||r.length===0||!n?.wifi,run:()=>k.create(`wifi`,null)},{label:`New access point…`,disabled:i!==null||!r.some(e=>e.accessPoint)||!n?.wifi,run:()=>k.create(`hotspot`,null)},{label:`New wired connection…`,disabled:i!==null,run:()=>k.create(`ethernet`,null)}]})};if((0,q.useEffect)(()=>{n&&c&&((c.type===`network`?n.networks.some(e=>e.device===c.device&&e.ssid===c.ssid):c.type===`connection`?n.connections.some(e=>e.uuid===c.uuid):n.devices.some(e=>e.name===c.name))||l(null))},[n,c]),!n)return(0,J.jsx)(`div`,{className:`widget-message`,children:`Reading the networks…`});let M={wifi:n.networks.filter(e=>x.some(t=>t.name===e.device)).length,saved:Du(n.connections,x,``,e.allInterfaces).reduce((e,t)=>e+t.connections.length,0),devices:x.length},N={state:n,devices:x,text:o,all:e.allInterfaces,selection:c,onSelect:e=>l(t=>JSON.stringify(t)===JSON.stringify(e)?null:e),actions:k,now:v},P=zu(n,v),{width:F,height:I}=h,L=F>=Kd,R=c&&(0,J.jsx)(Nd,{selection:c,state:n,actions:k,now:v,onClose:()=>l(null)}),ee;ee=n.error?(0,J.jsx)(`p`,{className:`net-note error`,children:n.error}):C===`wifi`?(0,J.jsx)(Ud,{...N}):C===`saved`?(0,J.jsx)(Wd,{...N}):(0,J.jsx)(Gd,{...N});let te=!e.readOnly&&!n.error&&w?(0,J.jsx)(`p`,{className:`net-banner`,role:`note`,children:w}):null;return(0,J.jsxs)(`div`,{ref:_,className:`net${R&&!L&&I>0&&I<qd?` cramped`:``}`,onKeyDown:e=>{e.key===`Escape`&&c&&l(null)},children:[(0,J.jsxs)(`div`,{className:`net-toolbar`,role:`toolbar`,"aria-label":`Networking`,children:[(0,J.jsx)(`div`,{className:`net-views`,role:`group`,"aria-label":`View`,children:Jd.filter(({view:e})=>e!==`wifi`||S).map(({view:e,label:t,title:n,Icon:r})=>(0,J.jsxs)(`button`,{type:`button`,className:`net-view`,"aria-pressed":C===e,title:n,onClick:()=>{a(e),l(null)},children:[(0,J.jsx)(r,{width:14,height:14}),(0,J.jsx)(`span`,{className:`net-view-name`,children:t}),(0,J.jsx)(`span`,{className:`net-view-count`,children:M[e]})]},e))}),C!==`devices`&&(0,J.jsxs)(`label`,{className:`net-search`,title:`Only those whose name contains`,children:[(0,J.jsx)(ne,{width:14,height:14}),(0,J.jsx)(`input`,{type:`search`,value:o,placeholder:`Filter…`,"aria-label":`Only those whose name contains`,spellCheck:!1,onChange:e=>s(e.target.value)})]}),(0,J.jsxs)(`div`,{className:`net-tools`,children:[C===`wifi`&&(0,J.jsx)(`button`,{type:`button`,className:`overlay-button${n.scanning?` net-spinning`:``}`,title:n.scanning?`Looking for networks…`:`Look for networks again`,"aria-label":`Look for networks again`,disabled:!n.wifi||n.scanning||n.error!==null,onClick:A,children:(0,J.jsx)(xe,{width:17,height:17})}),S&&!e.readOnly&&(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,"aria-pressed":n.wifi,title:n.wifiHardware?w??(n.wifi?`Wi-Fi is on: turn it off`:`Wi-Fi is off: turn it on`):`Wi-Fi is off by its hardware switch`,"aria-label":n.wifi?`Turn Wi-Fi off`:`Turn Wi-Fi on`,disabled:w!==null||!n.wifiHardware||n.error!==null,onClick:()=>k.setWifi(!n.wifi),children:n.wifi?(0,J.jsx)(O,{width:17,height:17}):(0,J.jsx)(f,{width:17,height:17})}),!e.readOnly&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`span`,{className:`overlay-separator`}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:w??`A new connection: a hidden network, an access point, a cable’s`,"aria-label":`New connection`,"aria-haspopup":`menu`,disabled:w!==null||n.error!==null,onClick:j,children:(0,J.jsx)(ce,{width:17,height:17})})]})]})]}),te,(0,J.jsxs)(`div`,{className:`net-body${L?` beside`:``}`,children:[(0,J.jsx)(`div`,{className:`net-main`,children:ee}),R]}),(0,J.jsxs)(`div`,{className:`net-status ${P.tone}`,title:P.text,children:[(0,J.jsx)(ad,{tone:P.tone}),(0,J.jsx)(`span`,{className:`net-status-text`,children:P.text})]}),u&&(0,J.jsx)(Sd,{request:u,state:n,onClose:()=>d(null)}),p&&(0,J.jsx)(os,{x:Math.max(4,p.x),y:p.y,items:p.items,onClose:()=>m(null)})]})}var Zd=Ye({type:`networking`,name:`Networking`,description:`The robot’s networks: join Wi-Fi networks, switch between those saved, set addresses, make it an access point.`,icon:ue,defaultSize:{w:6,h:7},minSize:{w:3,h:3},component:Xd,properties:[{key:`view`,label:`View`,kind:`select`,default:`wifi`,options:[{value:`wifi`,label:`Wi-Fi networks in range`},{value:`saved`,label:`Saved connections`},{value:`devices`,label:`Interfaces`}],help:`When the panel opens: its toolbar changes it`},{key:`interfaces`,label:`Interfaces`,kind:`string`,default:``,placeholder:`wlan0, eth*`,help:`Only these, and their connections: names or globs (*), separated by commas. All if empty`},{key:`allInterfaces`,label:`Show every interface`,kind:`boolean`,default:!1,help:`Also those other programs configure (docker’s bridges, VPN clients’ tunnels), the loopback, Wi-Fi P2P`},{key:`confirm`,label:`Ask before disconnecting`,kind:`boolean`,default:!0,help:`And before turning Wi-Fi off. A change that would cut this page off from the robot is always asked about`},{key:`readOnly`,label:`Read-only`,kind:`boolean`,default:!1,help:`The networks are shown, not changed: e.g. on a panel for operators`}]}),Qd=`modulepreload`,$d=function(e){return`/`+e},ef={},tf=function(e){return e.pathname.endsWith(`.css`)},nf=function(e,t,n){if(t in e)return e[t];let r=n();if(!r){e[t]=void 0;return}let i=r.then(()=>{e[t]=void 0},n=>{throw e[t]=void 0,n});return e[t]=i,i},rf=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e,i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?new URL(import.meta.resolve(e)):new URL(e,import.meta.url)}r=o(t.map(t=>{t=$d(t,n);let r=s(t),i=tf(r);return nf(ef,r.href,()=>{if(e===void 0){e={all:new Set,styles:new Set};let t=document.getElementsByTagName(`link`);for(let n=t.length-1;n>=0;n--){let r=t[n];e.all.add(r.href),r.rel===`stylesheet`&&e.styles.add(r.href)}}if((i?e.styles:e.all).has(r.href))return;let t=document.createElement(`link`);if(t.rel=i?`stylesheet`:Qd,i||(t.as=`script`),t.crossOrigin=``,t.href=r.href,a&&t.setAttribute(`nonce`,a),document.head.appendChild(t),i)return new Promise((e,n)=>{t.addEventListener(`load`,e),t.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${r}`)))})})}).filter(e=>e!==void 0))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},af=(0,q.lazy)(()=>rf(()=>import(`./RobotModelView.js`),__vite__mapDeps([0,1,2,3,4])));function of({config:e}){return(0,J.jsx)(q.Suspense,{fallback:(0,J.jsx)(`div`,{className:`widget-message`,children:`Loading the 3D view…`}),children:(0,J.jsx)(af,{config:e})})}var sf=Ye({type:`robot-model`,name:`Robot Model`,description:`The robot in 3D, its moving joints highlighted, and its TF frames.`,icon:Fe,defaultSize:{w:6,h:5},minSize:{w:3,h:3},component:of,properties:[{key:`descriptionTopic`,label:`Robot description topic`,kind:`topic`,default:`/robot_description`,topicTypes:[`std_msgs/msg/String`],help:`The URDF, as robot_state_publisher publishes it. Its package:// meshes come from the robot`},{key:`jointStatesTopic`,label:`Joint states topic`,kind:`topic`,default:`/joint_states`,topicTypes:[`sensor_msgs/msg/JointState`],help:`Where the joints are, and how they move`},{key:`showFrames`,label:`Show the TF frames`,kind:`boolean`,default:!1},{key:`highlightMotion`,label:`Highlight the moving joints`,kind:`boolean`,default:!0,help:`Their links take another colour, with an arrow in the direction of the motion`}]}),cf={x:0,y:0,z:0};function lf(e){return e.x===0&&e.y===0&&e.z===0}function uf(e,t){b.send({type:`twist`,topic:e.topic,stamped:e.stamped,frameId:e.frameId,linear:{x:t.x*e.maxLinear,y:t.y*e.maxLinear},angular:{z:t.z*e.maxAngular}})}function df({config:e,editing:t}){let n=We()===`open`,[r,i]=(0,q.useState)(cf),a=(0,q.useRef)(cf),o=(0,q.useRef)(e);o.current=e;let s=(0,q.useRef)(!1),c=(0,q.useCallback)(e=>{let t={...a.current,...e};a.current=t,i(t),!lf(t)&&!s.current?(s.current=!0,uf(o.current,t)):lf(t)&&s.current&&(s.current=!1,uf(o.current,cf))},[]);(0,q.useEffect)(()=>{let t=setInterval(()=>s.current&&uf(o.current,a.current),1e3/Math.min(50,Math.max(1,e.rate)));return()=>clearInterval(t)},[e.rate]);let{topic:l,stamped:u}=e;(0,q.useEffect)(()=>()=>{s.current&&(s.current=!1,uf({...o.current,topic:l,stamped:u},cf))},[l,u]);let d=t||!n,{maxLinear:f,maxAngular:p}=e;return(0,J.jsxs)(`div`,{className:`teleop`,children:[(0,J.jsx)(`div`,{className:`teleop-pads${e.omnidirectional?` two`:``}`,children:e.omnidirectional?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(Ze,{axes:`xy`,label:`x · y`,disabled:d,onMove:(e,t)=>c({x:t,y:-e})}),(0,J.jsx)(Ze,{axes:`x`,label:`z`,disabled:d,onMove:e=>c({z:-e})})]}):(0,J.jsx)(Ze,{axes:`xy`,label:`x · z`,disabled:d,onMove:(e,t)=>c({x:t,y:0,z:-e})})}),(0,J.jsx)(`div`,{className:`teleop-readout`,children:n||t?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsxs)(`span`,{children:[`x `,(r.x*f).toFixed(3)]}),e.omnidirectional&&(0,J.jsxs)(`span`,{children:[`y `,(r.y*f).toFixed(3)]}),(0,J.jsxs)(`span`,{children:[`z `,(r.z*p).toFixed(3)]}),(0,J.jsx)(`span`,{className:`teleop-topic`,children:e.topic})]}):(0,J.jsx)(`span`,{className:`offline`,children:`Disconnected`})})]})}var ff=Ye({type:`teleop`,name:`Teleop`,description:`Drive the robot with on-screen pads, publishing velocity commands.`,icon:h,defaultSize:{w:6,h:4},minSize:{w:3,h:3},component:df,properties:[{key:`topic`,label:`Topic`,kind:`topic`,default:`cmd_vel`,topicTypes:[`geometry_msgs/msg/Twist`,`geometry_msgs/msg/TwistStamped`],help:`With twist_mux, one of its inputs, above autonomous navigation and below the joysticks`},{key:`omnidirectional`,label:`Omnidirectional`,kind:`boolean`,default:!1,help:`Two pads: x and y, and z. Otherwise one: x and z`},{key:`stamped`,label:`TwistStamped`,kind:`boolean`,default:!1,help:`Publish geometry_msgs/TwistStamped instead of Twist`},{key:`frameId`,label:`Frame id`,kind:`string`,default:`base_link`,visible:e=>e.stamped},{key:`maxLinear`,label:`Max linear speed (m/s)`,kind:`number`,default:.2,min:0,step:.01},{key:`maxAngular`,label:`Max angular speed (rad/s)`,kind:`number`,default:.5,min:0,step:.01},{key:`rate`,label:`Rate (Hz)`,kind:`number`,default:10,min:1,max:50,step:1,help:`How often the command is published while a pad is held`}]}),pf=(0,q.lazy)(()=>rf(()=>import(`./TerminalView.js`),__vite__mapDeps([5,1,2,3,4,6,7,8])));function mf({config:e,instance:t,panelId:n,editing:r}){return(0,J.jsx)(q.Suspense,{fallback:(0,J.jsx)(`div`,{className:`widget-message`,children:`Loading the terminal…`}),children:(0,J.jsx)(pf,{config:e,panelId:n,widgetId:t.id,editing:r})})}var hf=Ye({type:`terminal`,name:`Terminal`,description:`A shell on the robot, or a command. On touch screens, the keys phones lack: Esc, Tab, Ctrl, Alt, arrows.`,icon:Ie,defaultSize:{w:8,h:6},minSize:{w:3,h:3},component:mf,properties:[{key:`command`,label:`Command`,kind:`text`,default:``,placeholder:`htop`,help:`Run instead of a shell, by the login shell: e.g. htop, or tmux new -A -s robot for one session that every page shares. Empty: the shell`},{key:`folder`,label:`Folder`,kind:`string`,default:`~`,placeholder:`~/ros2_ws`,help:`Where it starts: /…, ~/… or package://<package>/…`},{key:`fontSize`,label:`Text size`,kind:`number`,default:13,min:7,max:32,step:1,help:`In pixels. Each device changes it with the toolbar, or by pinching`},{key:`theme`,label:`Colours`,kind:`select`,default:`auto`,options:[{value:`auto`,label:`The page’s, dark or light`},{value:`dark`,label:`Dark`},{value:`light`,label:`Light`}]},{key:`extraKeys`,label:`Extra keys`,kind:`select`,default:`touch`,options:[{value:`touch`,label:`On touch screens`},{value:`always`,label:`Always`},{value:`never`,label:`Never`}],help:`Keys the on-screen keyboard lacks, under the terminal. Ctrl, Alt and Shift apply to the next key, of the row or of the keyboard: tap twice, or hold, to keep them. Swiping up on a key types what its corner shows`},{key:`keys`,label:`Extra keys’ layout`,kind:`text`,default:nt,help:`A line per row, keys separated by spaces: ESC TAB CTRL ALT SHIFT HOME END PGUP PGDN UP DOWN LEFT RIGHT INS DEL BKSP ENTER SPACE F1…F12 KEYBOARD PASTE, or what to type (~, |, ros2). What swiping up types follows a colon: -:|`,visible:e=>e.extraKeys!==`never`},{key:`fullScreenTyping`,label:`Full screen while typing`,kind:`boolean`,default:!0,help:`On touch screens: while the on-screen keyboard is open, the terminal fills the screen above it, the extra keys right above it`}]});function gf(e,t){return typeof t==typeof e.default&&(e.kind!==`select`||e.options.some(e=>e.value===t))&&(typeof t!=`number`||Number.isFinite(t))}var _f=class{registered;defaults=new Map;current;constructor(e){this.registered=e.map(e=>({definition:e,plugin:null})),this.current=this.registered}registerPlugin(e,t){let n=[];for(let r of t.widgets??[]){let t=this.registered.find(e=>e.definition.type===r.type);t?n.push(`its widget type "${r.type}" is already ${t.plugin?`${t.plugin}'s`:`Tassello's`}`):this.registered=[...this.registered,{definition:r,plugin:e}]}for(let[e,r]of Object.entries(t.defaults??{})){let t=this.defaults.get(e)??new Map;for(let[i,a]of Object.entries(r)){t.set(i,a);let r=this.registered.find(t=>t.definition.type===e)?.definition,o=r?.properties.find(e=>e.key===i);r&&(!o||!gf(o,a))&&n.push(`its default of ${e}.${i} is not one of the property's values`)}this.defaults.set(e,t)}return this.current=this.registered.map(e=>({...e,definition:this.withDefaults(e.definition)})),n}entries(){return this.current}definition(e){return this.current.find(t=>t.definition.type===e)?.definition}withDefaults(e){let t=this.defaults.get(e.type);return t?{...e,properties:e.properties.map(e=>t.has(e.key)&&gf(e,t.get(e.key))?{...e,default:t.get(e.key)}:e)}:e}};function vf(e,t){let n={};for(let r of e.properties){let e=t[r.key];n[r.key]=gf(r,e)?e:r.default}return n}var yf=new _f([ff,Ql,sf,vt,Wi,pl,zi,lt,Ni,Si,Al,eo,dl,hf,Zd]);function bf(e,t){return yf.registerPlugin(e,t)}function xf(){return yf.entries()}function Sf(e){return yf.definition(e)}function Cf(e,t){return e.x<t.x+t.w&&t.x<e.x+e.w&&e.y<t.y+t.h&&t.y<e.y+e.h}function wf(e,t,n){return e.x>=0&&e.y>=0&&!t.some(t=>t.id!==n&&Cf(e,t))}function Tf(e){return{cols:Math.max(0,...e.map(e=>e.x+e.w)),rows:Math.max(0,...e.map(e=>e.y+e.h))}}function Ef(e,t,n){let{rows:r}=Tf(e);for(let i=0;i<=r;i++)for(let r=0;r+t.w<=Math.max(n,t.w);r++){let n={x:r,y:i,...t};if(wf(n,e))return n}return{x:0,y:r,...t}}function Df(e,t=64){return{left:e.x*t+4,top:e.y*t+4,width:e.w*t-8,height:e.h*t-8}}var Of={desktop:{label:`Desktop`,columns:null,previewWidth:null},tablet:{label:`Tablet`,columns:12,previewWidth:820},phone:{label:`Phone`,columns:6,previewWidth:390}};function kf(e){return e<640?`phone`:e<1100?`tablet`:`desktop`}function Af(e,t){return e.x<t.x+t.w&&t.x<e.x+e.w&&e.y<t.y+t.h&&t.y<e.y+e.h}function jf(e,t,n,r){if(e.x<0||e.y<0||n!==null&&e.x+e.w>n)return!1;for(let[n,i]of t.rects)if(n!==r&&Af(e,i))return!1;return!0}function Mf(e,t,n,r,i){for(let a=i;;a++)for(let i=0;i+t<=r;i++){let r={x:i,y:a,w:t,h:n};if(![...e.values()].some(e=>Af(r,e)))return r}}function Nf(e,t,n){let r=Of[t].columns;if(r===null)return{rects:new Map(e.widgets.map(e=>[e.id,{x:e.x,y:e.y,w:e.w,h:e.h}])),hidden:[],custom:!0};let i=e.layouts?.[t],a=new Set(i?.hidden??[]),o=new Map;for(let t of e.widgets){let e=i?.rects[t.id];if(e&&!a.has(t.id)){let n=Math.min(e.w,r);o.set(t.id,{x:Math.min(e.x,r-n),y:e.y,w:n,h:e.h})}}let s=Math.max(0,...[...o.values()].map(e=>e.y+e.h)),c=e.widgets.filter(e=>!a.has(e.id)&&!o.has(e.id)).sort((e,t)=>e.y-t.y||e.x-t.x);for(let e of c){let t=Mf(o,Math.min(e.w,r),e.w>r?Math.max(n(e.type).h,Math.round(e.h*r/e.w)):e.h,r,s);o.set(e.id,t),s=t.y}return{rects:o,hidden:[...a],custom:Object.keys(i?.rects??{}).length>0}}function Pf(e,t,n){return{...e,layouts:{...e.layouts,[t]:n}}}function Ff(e,t,n,r,i){if(t===`desktop`)return{...e,widgets:e.widgets.map(e=>e.id===r?{...e,...i}:e)};let a=Object.fromEntries(n.rects);return a[r]=i,Pf(e,t,{rects:a,hidden:n.hidden})}function If(e,t,n,r,i){return Pf(e,t,{rects:n.custom?Object.fromEntries([...n.rects].filter(([e])=>e!==r)):{},hidden:i?[...new Set([...n.hidden,r])]:n.hidden.filter(e=>e!==r)})}function Lf(e,t){let{[t]:n,...r}=e.layouts??{},{layouts:i,...a}=e;return Object.keys(r).length>0?{...a,layouts:r}:a}var Rf=4,zf=4,Bf=100,Vf=32;function Hf(e){let[t,n]=(0,q.useState)({width:0,height:0});return(0,q.useEffect)(()=>{let t=e.current;if(!t)return;let r=new ResizeObserver(()=>n({width:t.clientWidth,height:t.clientHeight}));return r.observe(t),()=>r.disconnect()},[e]),t}function Uf({panel:e,device:t,layout:n,previewWidth:r,editing:i,selectedId:a,onSelect:o,onPlace:s,onRemove:c,onHide:l,onVisibleColumns:u}){let d=(0,q.useRef)(null),f=Hf(d),[p,m]=(0,q.useState)(null),h=Of[t].columns,g=(r??f.width)-32,_=h===null?64:Math.max(Vf,Math.floor(g/h)),v=h??Math.max(1,Math.floor(g/_)),y=Math.max(1,Math.floor((f.height-32)/_));(0,q.useEffect)(()=>u?.(v),[v,u]);let b=[...n.rects.values()],x=Tf(p?[...b,p.rect]:b),S=h??(i?Math.max(x.cols+Rf,v):x.cols),C=i?Math.max(x.rows+Rf,y):x.rows,w=(e,t,r)=>{let a=n.rects.get(t.id);if(!i||e.button!==0||p||!a)return;e.stopPropagation(),e.currentTarget.setPointerCapture(e.pointerId);let s=d.current;m({id:t.id,kind:r,pointerId:e.pointerId,startX:e.clientX,startY:e.clientY,startScrollLeft:s.scrollLeft,startScrollTop:s.scrollTop,origin:a,rect:a,dx:0,dy:0,moved:!1}),o(t.id)},T=t=>{if(!p||t.pointerId!==p.pointerId)return;let n=d.current,r=t.clientX-p.startX+n.scrollLeft-p.startScrollLeft,i=t.clientY-p.startY+n.scrollTop-p.startScrollTop,{origin:a}=p,o;if(p.kind===`move`){let e=Math.max(0,a.x+Math.round(r/_));o={...a,x:h===null?e:Math.min(h-a.w,e),y:Math.max(0,a.y+Math.round(i/_))}}else{let t=Sf(e.widgets.find(e=>e.id===p.id)?.type??``)?.minSize,n=h===null?Bf:h-a.x;o={...a,w:Math.min(n,Math.max(Math.min(t?.w??1,n),a.w+Math.round(r/_))),h:Math.min(Bf,Math.max(t?.h??1,a.h+Math.round(i/_)))}}m({...p,dx:r,dy:i,rect:o,moved:p.moved||Math.hypot(r,i)>zf})},D=e=>{if(!p||e.pointerId!==p.pointerId)return;let{id:t,rect:r,origin:i,moved:a}=p;m(null);let o=r.x!==i.x||r.y!==i.y||r.w!==i.w||r.h!==i.h;a&&o&&jf(r,n,h,t)&&s(t,r)},O=!p||jf(p.rect,n,h,p.id);return(0,J.jsx)(`div`,{ref:d,className:`board-scroll${h===null?``:` columns`}`,onPointerDown:e=>{i&&!e.target.closest(`.widget`)&&o(null)},children:(0,J.jsxs)(`div`,{className:`board${i?` editing`:``}${r?` preview`:``}`,style:{width:S*_,height:C*_,"--cell":`${_}px`},children:[p?.moved&&(0,J.jsx)(`div`,{className:`drop-target${O?``:` invalid`}`,style:Df(p.rect,_)}),e.widgets.map(r=>{let o=n.rects.get(r.id);if(!o)return null;let s=p?.id===r.id&&p.moved?p:null,u=Df(o,_);s?.kind===`move`?u.transform=`translate(${s.dx}px, ${s.dy}px)`:s?.kind===`resize`&&(u.width=Math.max(_-8,o.w*_-8+s.dx),u.height=Math.max(_-8,o.h*_-8+s.dy));let d=Sf(r.type),f=d?.icon??Pe;return(0,J.jsxs)(`div`,{className:[`widget`,i&&`editing`,a===r.id&&`selected`,s&&`dragging`].filter(Boolean).join(` `),style:u,onPointerDown:e=>w(e,r,`move`),onPointerMove:T,onPointerUp:D,onPointerCancel:D,children:[(0,J.jsx)(Wf,{instance:r,panelId:e.id,editing:i}),i&&(0,J.jsxs)(`div`,{className:`widget-chrome`,children:[(0,J.jsxs)(`span`,{className:`widget-tag`,children:[(0,J.jsx)(f,{width:14,height:14}),d?.name??r.type]}),(0,J.jsxs)(`div`,{className:`widget-buttons`,children:[l&&(0,J.jsx)(`button`,{type:`button`,className:`widget-button`,title:`Hide on ${Of[t].label.toLowerCase()}s`,"aria-label":`Hide on ${Of[t].label.toLowerCase()}s`,onPointerDown:e=>e.stopPropagation(),onClick:()=>l(r.id),children:(0,J.jsx)(P,{width:14,height:14})}),(0,J.jsx)(`button`,{type:`button`,className:`widget-button remove`,title:`Remove widget`,"aria-label":`Remove widget`,onPointerDown:e=>e.stopPropagation(),onClick:()=>c(r.id),children:(0,J.jsx)(E,{width:14,height:14})})]}),(0,J.jsx)(`div`,{className:`resize-handle`,title:`Resize`,onPointerDown:e=>w(e,r,`resize`)})]})]},r.id)})]})})}var Wf=(0,q.memo)(function({instance:e,panelId:t,editing:n}){let r=Sf(e.type),i=(0,q.useMemo)(()=>r?vf(r,e.config):{},[r,e.config]);return(0,J.jsxs)(`div`,{className:`widget-card`,children:[e.title&&(0,J.jsx)(`div`,{className:`widget-title`,children:e.title}),(0,J.jsx)(`div`,{className:`widget-body`,children:r?(0,J.jsx)(Gf,{resetKey:i,children:(0,J.jsx)(r.component,{config:i,instance:e,panelId:t,editing:n})}):(0,J.jsxs)(`div`,{className:`widget-message`,children:[`Unknown widget type “`,e.type,`”: is the plugin that adds it installed?`]})})]})}),Gf=class extends q.Component{state={error:null};static getDerivedStateFromError(e){return{error:e}}componentDidUpdate(e){this.state.error&&e.resetKey!==this.props.resetKey&&this.setState({error:null})}render(){return this.state.error?(0,J.jsxs)(`div`,{className:`widget-message error`,children:[`This widget failed: `,this.state.error.message]}):this.props.children}};function Kf({widget:e,onChange:t,onDuplicate:n,onRemove:r,onClose:i}){let a=Sf(e.type),o=a?vf(a,e.config):e.config,s=a?.icon??Pe,c=(n,r)=>t({...e,config:{...e.config,[n]:r}});return(0,J.jsxs)(`aside`,{className:`properties`,"aria-label":`Widget properties`,children:[(0,J.jsxs)(`header`,{className:`properties-header`,children:[(0,J.jsx)(s,{}),(0,J.jsx)(`h2`,{children:a?.name??e.type}),(0,J.jsx)(`button`,{type:`button`,className:`icon-button`,"aria-label":`Close`,onClick:i,children:(0,J.jsx)(H,{})})]}),(0,J.jsxs)(`div`,{className:`properties-body`,children:[(0,J.jsx)(qf,{label:`Title`,help:`Shown above the widget, if set`,children:n=>(0,J.jsx)(Yf,{id:n,value:e.title,onCommit:n=>t({...e,title:n})})}),a?.properties.filter(e=>!e.visible||e.visible(o)).map(e=>(0,J.jsx)(Jf,{property:e,value:o[e.key],onChange:t=>c(e.key,t)},e.key)),(0,J.jsxs)(`p`,{className:`properties-meta`,children:[e.w,`×`,e.h,` cells at column `,e.x+1,`, row `,e.y+1]})]}),(0,J.jsxs)(`footer`,{className:`properties-footer`,children:[(0,J.jsxs)(`button`,{type:`button`,className:`button`,onClick:n,children:[(0,J.jsx)(z,{}),` Duplicate`]}),(0,J.jsxs)(`button`,{type:`button`,className:`button danger`,onClick:r,children:[(0,J.jsx)(E,{}),` Remove`]})]})]})}function qf({label:e,help:t,children:n}){let r=(0,q.useId)();return(0,J.jsxs)(`div`,{className:`field`,children:[(0,J.jsx)(`label`,{className:`field-label`,htmlFor:r,children:e}),n(r),t&&(0,J.jsx)(`p`,{className:`field-help`,children:t})]})}function Jf({property:e,value:t,onChange:n}){return e.kind===`boolean`?(0,J.jsxs)(`div`,{className:`field`,children:[(0,J.jsxs)(`label`,{className:`toggle`,children:[(0,J.jsx)(`input`,{type:`checkbox`,checked:t===!0,onChange:e=>n(e.target.checked)}),(0,J.jsx)(`span`,{className:`toggle-switch`,"aria-hidden":`true`}),(0,J.jsx)(`span`,{className:`field-label`,children:e.label})]}),e.help&&(0,J.jsx)(`p`,{className:`field-help`,children:e.help})]}):(0,J.jsx)(qf,{label:e.label,help:e.help,children:r=>{switch(e.kind){case`number`:return(0,J.jsx)(Yf,{id:r,type:`number`,min:e.min,max:e.max,step:e.step??`any`,value:String(t),onCommit:t=>{let r=Number(t);t.trim()&&Number.isFinite(r)&&n(Math.min(e.max??1/0,Math.max(e.min??-1/0,r)))}});case`select`:return(0,J.jsx)(`select`,{id:r,value:String(t),onChange:e=>n(e.target.value),children:e.options.map(e=>(0,J.jsx)(`option`,{value:e.value,children:e.label},e.value))});case`color`:return(0,J.jsxs)(`div`,{className:`color-field`,children:[(0,J.jsx)(`input`,{id:r,type:`color`,value:String(t),onChange:e=>n(e.target.value)}),(0,J.jsx)(`code`,{children:String(t)})]});case`text`:return(0,J.jsx)(Yf,{id:r,multiline:!0,value:String(t),placeholder:e.placeholder,onCommit:n});case`image`:case`behavior-tree`:return(0,J.jsx)(Mt,{id:r,kind:e.kind,value:String(t),placeholder:e.placeholder,onCommit:n,children:e=>(0,J.jsx)(Yf,{...e})});case`topic`:return(0,J.jsx)(Xf,{id:r,value:String(t),types:e.topicTypes,placeholder:e.placeholder,onCommit:n});default:return(0,J.jsx)(Yf,{id:r,value:String(t),placeholder:e.placeholder,onCommit:n})}}})}function Yf({value:e,onCommit:t,multiline:n=!1,...r}){let[i,a]=(0,q.useState)(e);(0,q.useEffect)(()=>a(e),[e]);let o=()=>i!==e&&t(i),s={...r,value:i,spellCheck:!1,onBlur:o,onChange:e=>a(e.target.value)};return n?(0,J.jsx)(`textarea`,{...s,rows:4,onKeyDown:t=>{t.key===`Enter`&&(t.ctrlKey||t.metaKey)?o():t.key===`Escape`&&a(e)}}):(0,J.jsx)(`input`,{...s,onKeyDown:t=>{t.key===`Enter`?o():t.key===`Escape`&&a(e)}})}function Xf({id:e,value:t,types:n,placeholder:r,onCommit:i}){let[a,o]=(0,q.useState)([]),s=`${e}-topics`,c=()=>Xe.listTopics().then(o).catch(()=>o([])),l=a.filter(e=>e.types.some(e=>n.includes(e)));return(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(Yf,{id:e,value:t,placeholder:r,list:s,onFocus:c,onCommit:i}),(0,J.jsx)(`datalist`,{id:s,children:l.map(e=>(0,J.jsx)(`option`,{value:e.name,children:e.types.join(`, `)},e.name))})]})}function Zf(){let e=new Map;for(let t of xf())e.set(t.plugin,[...e.get(t.plugin)??[],t]);return[...e].map(([e,t])=>({plugin:e,entries:t}))}function Qf({onAdd:e,onClose:t}){let n=Zf();return(0,J.jsx)($e,{title:`Add a widget`,onClose:t,wide:!0,children:(0,J.jsx)(`div`,{className:`palette-scroll`,children:n.map(({plugin:t,entries:r})=>(0,J.jsxs)(`section`,{children:[n.length>1&&(0,J.jsx)(`h3`,{className:`palette-heading`,children:t??`Tassello`}),(0,J.jsx)(`div`,{className:`palette`,children:r.map(({definition:t})=>(0,J.jsxs)(`button`,{type:`button`,className:`palette-item`,onClick:()=>e(t),children:[(0,J.jsx)(`span`,{className:`palette-icon`,children:(0,J.jsx)(t.icon,{width:24,height:24})}),(0,J.jsxs)(`span`,{className:`palette-text`,children:[(0,J.jsx)(`strong`,{children:t.name}),(0,J.jsx)(`span`,{children:t.description})]})]},t.type))})]},t??``))})})}var $f=500,ep=18,tp={desktop:Ve,tablet:l,phone:ie},np=e=>Sf(e)?.minSize??{w:1,h:1};function rp(){let[e,t]=(0,q.useState)(()=>kf(window.innerWidth));return(0,q.useEffect)(()=>{let e=()=>t(kf(window.innerWidth));return window.addEventListener(`resize`,e),()=>window.removeEventListener(`resize`,e)},[]),e}function ip(){return decodeURIComponent(location.hash.replace(/^#\/?/,``))}function ap(e,t=!1){let n=`#/${encodeURIComponent(e)}`;t?(history.replaceState(null,``,n),window.dispatchEvent(new HashChangeEvent(`hashchange`))):location.hash=n}function op(e){return`${e.replace(/[^A-Za-z0-9_-]+/g,`-`).slice(-48)}-${Math.random().toString(36).slice(2,8)}`}function sp(e){return e instanceof Error?e.message:String(e)}function cp(){let[e,t]=(0,q.useState)(`saved`),n=(0,q.useRef)(null),r=(0,q.useRef)(null),i=(0,q.useRef)(Promise.resolve()),a=(0,q.useCallback)(()=>{r.current&&=(clearTimeout(r.current),null);let e=n.current;return n.current=null,e&&(i.current=i.current.then(()=>Xe.savePanel(e).then(()=>{n.current||t(`saved`)},n=>{t(`failed`),G(`Could not save “${e.name}”: ${sp(n)}`,`error`)}))),i.current},[]),o=(0,q.useCallback)(e=>{n.current=e,t(`saving`),r.current&&clearTimeout(r.current),r.current=setTimeout(a,$f)},[a]);return(0,q.useEffect)(()=>{let e=()=>{let e=n.current;e&&(n.current=null,fetch(`/api/panels/${encodeURIComponent(e.id)}`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e),keepalive:!0}))};return window.addEventListener(`pagehide`,e),()=>window.removeEventListener(`pagehide`,e)},[]),[e,(0,q.useMemo)(()=>({schedule:o,flush:a,isPending:()=>n.current!==null}),[o,a])]}function lp(){let e=We(),t=et(),[n,r]=cp(),[i,a]=(0,q.useState)(null),[o,s]=(0,q.useState)(ip),c=(0,q.useRef)(o);c.current=o;let[l,u]=(0,q.useState)(null),d=(0,q.useRef)(null),[f,p]=(0,q.useState)(null),[m,h]=(0,q.useState)(`view`),[g,_]=(0,q.useState)(null),[v,y]=(0,q.useState)(!1),[x,S]=(0,q.useState)(!1),C=(0,q.useRef)([]),[w,T]=(0,q.useState)(!1),E=(0,q.useRef)(12),D=(0,q.useCallback)(e=>{E.current=e},[]),O=m===`edit`,A=rp(),[j,M]=(0,q.useState)(null),N=O?j??A:A,P=(0,q.useRef)(N);P.current=N;let F=(0,q.useMemo)(()=>l?Nf(l,N,np):null,[l,N]),I=(0,q.useCallback)(e=>{d.current=e,u(e)},[]),L=(0,q.useCallback)(async()=>{try{a(await Xe.listPanels())}catch(e){G(`Could not list the panels: ${sp(e)}`,`error`)}},[]),R=(0,q.useCallback)(e=>{let t=d.current;if(!t)return;let n=e(t);C.current=[...C.current.slice(-49),t],T(!0),I(n),r.schedule(n)},[r,I]),ee=(0,q.useCallback)(()=>{let e=C.current.pop();T(C.current.length>0),e&&(I(e),r.schedule(e),_(t=>t&&e.widgets.some(e=>e.id===t)?t:null))},[r,I]);(0,q.useEffect)(()=>{let e=()=>s(ip());return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),(0,q.useEffect)(()=>{e!==`closed`&&L()},[L,e]),(0,q.useEffect)(()=>{i&&i.length>0&&!i.some(e=>e.id===o)&&ap(i[0].id,!0)},[i,o]);let te=(0,q.useCallback)(async e=>{try{let t=await Xe.getPanel(e);e===c.current&&(I(t),p(null))}catch(t){e===c.current&&p(sp(t))}},[I]),z=i?.some(e=>e.id===o)??!1;(0,q.useEffect)(()=>{if(!z)return;let e=!0;return r.flush().then(()=>{e&&(C.current=[],T(!1),_(null),te(o))}),()=>{e=!1}},[o,z,te,r]),(0,q.useEffect)(()=>b.onServerMessage(e=>{e.type===`error`&&G(e.message,`error`),e.type===`panels.changed`&&e.origin!==b.clientId&&(L(),e.id===d.current?.id&&!e.deleted&&!r.isPending()&&te(e.id))}),[L,te,r]);let ne=async e=>{e===`view`&&(_(null),M(null),await r.flush()),h(e),S(!1)},re=e=>{y(!1);let t=d.current;if(!t)return;let n=P.current===`desktop`?E.current:ep,r=Ef(t.widgets,e.defaultSize,n),i={id:op(e.type),type:e.type,title:``,...r,config:{}};R(e=>({...e,widgets:[...e.widgets,i]})),_(i.id)},ie=(0,q.useCallback)((e,t)=>R(n=>Ff(n,P.current,Nf(n,P.current,np),e,t)),[R]),ae=(0,q.useCallback)((e,t=!0)=>{let n=P.current;n!==`desktop`&&(R(r=>If(r,n,Nf(r,n,np),e,t)),_(t=>t===e?null:t))},[R]),oe=(0,q.useCallback)(e=>{let t=d.current?.widgets.find(t=>t.id===e);if(R(t=>({...t,widgets:t.widgets.filter(t=>t.id!==e)})),_(t=>t===e?null:t),t){let e=t.title||Sf(t.type)?.name||t.type;G(`Removed “${e}”`,`info`,{label:`Undo`,run:ee})}},[R,ee]),se=e=>{let t=d.current,n=t?.widgets.find(t=>t.id===e);if(!t||!n)return;let r=P.current===`desktop`?E.current:ep,i=Ef(t.widgets,{w:n.w,h:n.h},r),a={...n,...i,id:op(n.type),config:structuredClone(n.config)};R(e=>({...e,widgets:[...e.widgets,a]})),_(a.id)},B=async()=>{S(!1);let e=await t.prompt({title:`New panel`,label:`Name`,confirmLabel:`Create`});if(e)try{let t=await Xe.createPanel({name:e,widgets:[]});await L(),ap(t.id),h(`edit`)}catch(e){G(`Could not create the panel: ${sp(e)}`,`error`)}},le=async()=>{S(!1);let e=d.current;if(!e)return;let n=await t.prompt({title:`Rename panel`,label:`Name`,initial:e.name,confirmLabel:`Rename`});n&&n!==e.name&&(R(e=>({...e,name:n})),await r.flush(),await L())},ue=async()=>{S(!1);let e=d.current;if(e)try{await r.flush();let t=await Xe.createPanel({name:`${e.name} (copy)`,widgets:e.widgets});await L(),ap(t.id)}catch(e){G(`Could not duplicate the panel: ${sp(e)}`,`error`)}},de=async()=>{S(!1);let e=d.current;if(e&&await t.confirm({title:`Delete “${e.name}”?`,message:`The panel and its widgets are deleted for good.`,confirmLabel:`Delete`,danger:!0}))try{await r.flush(),await Xe.deletePanel(e.id),I(null),await L()}catch(e){G(`Could not delete the panel: ${sp(e)}`,`error`)}};(0,q.useEffect)(()=>{if(!O)return;let e=e=>{if(e.target.closest(`input, textarea, select, [role="dialog"]`))return;if(e.key===`z`&&(e.ctrlKey||e.metaKey)&&!e.shiftKey){e.preventDefault(),ee();return}let t=d.current?.widgets.find(e=>e.id===g);if(!t)return;if(e.key===`Delete`||e.key===`Backspace`){e.preventDefault(),oe(t.id);return}let n={ArrowLeft:[-1,0],ArrowRight:[1,0],ArrowUp:[0,-1],ArrowDown:[0,1]}[e.key],r=Nf(d.current,P.current,np),i=r.rects.get(t.id);if(n&&i){e.preventDefault();let a={...i,x:i.x+n[0],y:i.y+n[1]};jf(a,r,Of[P.current].columns,t.id)&&ie(t.id,a)}};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[O,g,ee,oe,ie]);let V=O?l?.widgets.find(e=>e.id===g):void 0,fe={connecting:`Connecting…`,open:`Connected to ${b.hostname}`,closed:`Disconnected, reconnecting…`}[e];return(0,J.jsxs)(`div`,{className:`app${O?` editing`:``}`,children:[(0,J.jsxs)(`header`,{className:`topbar`,children:[(0,J.jsxs)(`div`,{className:`brand`,title:fe,children:[(0,J.jsx)(Re,{width:22,height:22}),(0,J.jsx)(`span`,{className:`brand-name`,children:b.hostname||`Tassello`}),(0,J.jsx)(`span`,{className:`connection-dot ${e}`,"aria-label":fe})]}),i&&i.length>0&&(0,J.jsx)(`select`,{className:`panel-select`,"aria-label":`Panel`,value:o,onChange:e=>ap(e.target.value),children:i.map(e=>(0,J.jsx)(`option`,{value:e.id,children:e.name},e.id))}),(0,J.jsx)(`div`,{className:`topbar-spacer`}),O&&l&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`span`,{className:`save-state ${n}`,children:{saved:`Saved`,saving:`Saving…`,failed:`Not saved`}[n]}),(0,J.jsx)(`button`,{type:`button`,className:`icon-button`,title:`Undo (Ctrl+Z)`,"aria-label":`Undo`,disabled:!w,onClick:ee,children:(0,J.jsx)(_e,{})}),(0,J.jsxs)(`button`,{type:`button`,className:`button primary`,"aria-label":`Add widget`,onClick:()=>y(!0),children:[(0,J.jsx)(ce,{}),` `,(0,J.jsx)(`span`,{className:`hide-narrow`,children:`Add widget`})]})]}),O&&(0,J.jsx)(`div`,{className:`mode-switch device-switch`,role:`group`,"aria-label":`Layout of`,children:Object.keys(Of).map(e=>{let t=tp[e];return(0,J.jsx)(`button`,{type:`button`,"aria-pressed":N===e,title:`The ${Of[e].label.toLowerCase()} layout`,"aria-label":`${Of[e].label} layout`,onClick:()=>{M(e),_(null)},children:(0,J.jsx)(t,{})},e)})}),O&&(0,J.jsxs)(`div`,{className:`menu`,children:[(0,J.jsx)(`button`,{type:`button`,className:`icon-button`,"aria-label":`Panel actions`,"aria-expanded":x,onClick:()=>S(e=>!e),children:(0,J.jsx)(k,{})}),x&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`div`,{className:`menu-backdrop`,onPointerDown:()=>S(!1)}),(0,J.jsxs)(`div`,{className:`menu-list`,role:`menu`,children:[(0,J.jsx)(`button`,{type:`button`,role:`menuitem`,onClick:B,children:`New panel…`}),l&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`button`,{type:`button`,role:`menuitem`,onClick:le,children:`Rename panel…`}),(0,J.jsx)(`button`,{type:`button`,role:`menuitem`,onClick:ue,children:`Duplicate panel`}),(0,J.jsx)(`button`,{type:`button`,role:`menuitem`,className:`danger`,onClick:de,children:`Delete panel…`})]})]})]})]}),(0,J.jsxs)(`div`,{className:`mode-switch`,role:`group`,"aria-label":`Mode`,children:[(0,J.jsxs)(`button`,{type:`button`,"aria-label":`View`,title:`View`,"aria-pressed":!O,onClick:()=>ne(`view`),children:[(0,J.jsx)(Le,{}),` `,(0,J.jsx)(`span`,{className:`hide-narrow`,children:`View`})]}),(0,J.jsxs)(`button`,{type:`button`,"aria-label":`Edit`,title:`Edit`,"aria-pressed":O,onClick:()=>ne(`edit`),children:[(0,J.jsx)(he,{}),` `,(0,J.jsx)(`span`,{className:`hide-narrow`,children:`Edit`})]})]})]}),(0,J.jsx)(`main`,{className:`workspace`,children:i===null?(0,J.jsx)(`div`,{className:`empty-state`,children:`Loading…`}):i.length===0?(0,J.jsxs)(`div`,{className:`empty-state`,children:[(0,J.jsx)(`h1`,{children:`No panels yet`}),(0,J.jsx)(`p`,{children:`A panel is a board of widgets: create one for each task, e.g. teleoperation or navigation.`}),(0,J.jsxs)(`button`,{type:`button`,className:`button primary`,onClick:B,children:[(0,J.jsx)(ce,{}),` Create a panel`]})]}):f?(0,J.jsxs)(`div`,{className:`empty-state`,children:[(0,J.jsx)(`h1`,{children:`Cannot open this panel`}),(0,J.jsx)(`p`,{children:f})]}):l?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsxs)(`div`,{className:`board-column`,children:[O&&N!==`desktop`&&F&&(0,J.jsxs)(`div`,{className:`layout-bar`,children:[(0,J.jsxs)(`span`,{children:[(0,J.jsxs)(`strong`,{children:[Of[N].label,` layout`]}),F.custom?` · arranged for this device`:` · automatic, from the desktop’s: moving a widget arranges it for this device`]}),F.hidden.length>0&&(0,J.jsxs)(`span`,{className:`layout-hidden`,children:[`Hidden:`,F.hidden.map(e=>{let t=l.widgets.find(t=>t.id===e);return(0,J.jsxs)(`button`,{type:`button`,className:`layout-chip`,title:`Show it again`,onClick:()=>ae(e,!1),children:[t?.title||Sf(t?.type??``)?.name||e,` +`]},e)})]}),(F.custom||F.hidden.length>0)&&(0,J.jsx)(`button`,{type:`button`,className:`link-button`,onClick:()=>R(e=>Lf(e,N)),children:`Back to automatic`})]}),F&&(0,J.jsx)(Uf,{panel:l,device:N,layout:F,previewWidth:N===A?null:Of[N].previewWidth,editing:O,selectedId:g,onSelect:_,onPlace:ie,onRemove:oe,onHide:N===`desktop`?void 0:ae,onVisibleColumns:D},N)]}),l.widgets.length===0&&(0,J.jsx)(`div`,{className:`board-hint`,children:O?(0,J.jsxs)(J.Fragment,{children:[`This panel is empty: add widgets with `,(0,J.jsx)(`strong`,{children:`Add widget`}),`.`]}):(0,J.jsxs)(J.Fragment,{children:[`This panel is empty: switch to `,(0,J.jsx)(`strong`,{children:`Edit`}),` to add widgets.`]})}),V&&(0,J.jsx)(Kf,{widget:V,onChange:e=>R(t=>({...t,widgets:t.widgets.map(t=>t.id===e.id?e:t)})),onDuplicate:()=>se(V.id),onRemove:()=>oe(V.id),onClose:()=>_(null)},V.id)]}):null}),v&&(0,J.jsx)(Qf,{onAdd:re,onClose:()=>y(!1)}),(0,J.jsx)(Ke,{})]})}var up=[];function dp(e){return e instanceof Error?e.message:String(e)}async function fp(){try{up=await Xe.listPlugins()}catch(e){return[`The plugins could not be listed: ${dp(e)}`]}let e=up.flatMap(e=>e.error?[`Plugin ${e.name}: its server side did not start: ${e.error}`]:[]);for(let e of up)for(let t of e.styles){let e=document.createElement(`link`);e.rel=`stylesheet`,e.href=t,document.head.append(e)}return(await Promise.allSettled(up.map(e=>e.client?rf(()=>import(e.client),[]):Promise.resolve(null)))).forEach((t,n)=>{let{name:r}=up[n];if(t.status===`rejected`){e.push(`Plugin ${r} did not load: ${dp(t.reason)}`);return}if(t.value===null)return;let i=t.value.default;if(typeof i!=`object`||!i)e.push(`Plugin ${r}: its client module has no default export (definePlugin({ widgets }))`);else for(let t of bf(r,i))e.push(`Plugin ${r}: ${t}`)}),e}b.connect();var pp=await fp();(0,K.createRoot)(document.getElementById(`root`)).render((0,J.jsx)(q.StrictMode,{children:(0,J.jsx)(tt,{children:(0,J.jsx)(lp,{})})}));for(let e of pp)console.error(e),G(e,`error`);
|
|
69
|
+
`),children:(0,J.jsx)(M,{width:17,height:17})}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:`Clear the messages`,"aria-label":`Clear the messages`,disabled:t.entries.length===0&&t.pending.length===0,onClick:()=>{v(null),n({type:`clear`})},children:(0,J.jsx)(E,{width:17,height:17})})]})]}),(0,J.jsxs)(`div`,{className:`logs-main`,children:[z&&(0,J.jsx)(Dl,{nodes:N.nodes,chosen:p,overlay:!te,onToggle:F,onAll:()=>m(new Set),onClose:()=>x(!1)}),(0,J.jsx)(vl,{entries:N.shown,version:t.version,selected:_,follow:h,time:e.time,showNode:p.size!==1&&ie.length!==1,nodeWidth:ae,compact:R>0&&R<bl,empty:oe,onSelect:v,onFollow:g})]}),L&&(0,J.jsx)(Ol,{entry:L,running:t.running,onOnly:()=>m(new Set([L.name])),onClose:()=>v(null)}),(0,J.jsxs)(`div`,{className:`logs-status ${se.tone}`,title:se.text,children:[(0,J.jsx)(`span`,{className:`logs-dot`,"aria-hidden":`true`}),(0,J.jsx)(`span`,{className:`logs-status-text`,children:se.text})]})]})}var Al=Ye({type:`logs`,name:`Logs`,description:`The ROS log of every node (/rosout), coloured by level, filtered by node, level and text.`,icon:_,defaultSize:{w:10,h:6},minSize:{w:4,h:3},component:kl,properties:[{key:`topic`,label:`Log topic`,kind:`topic`,default:`/rosout`,topicTypes:[`rcl_interfaces/msg/Log`],help:`rcl_interfaces/Log: every node’s logger publishes on /rosout`},{key:`nodes`,label:`Nodes`,kind:`string`,default:``,placeholder:`amcl, *_server`,help:`Only these nodes, with their child loggers: names or globs (*), separated by commas or spaces. All if empty`},{key:`level`,label:`Levels shown`,kind:`select`,default:`debug`,options:[{value:`debug`,label:`All`},{value:`info`,label:`Info and above`},{value:`warn`,label:`Warnings and above`},{value:`error`,label:`Errors and above`}],help:`When the panel opens: its toolbar shows and hides each level`},{key:`include`,label:`Include`,kind:`string`,default:``,placeholder:`goal; path`,help:`Only the messages containing any of these, separated by ;. The toolbar changes it`},{key:`exclude`,label:`Exclude`,kind:`string`,default:``,placeholder:`Publishing`,help:`Not the messages containing any of these, separated by ;. The toolbar changes it`},{key:`regex`,label:`Filters as regular expressions`,kind:`boolean`,default:!1},{key:`time`,label:`Time`,kind:`select`,default:`clock`,options:[{value:`clock`,label:`Clock time, 14:03:27.512`},{value:`stamp`,label:`ROS stamp, in seconds`},{value:`none`,label:`Hidden`}],help:`Of the messages’ stamps, on their nodes’ clocks: seconds on sim time`}]}),jl=[`map`,`global`,`local`],Ml=2,Nl=2e3,Pl=.25,Fl=8,Il=34,Ll=6,Rl=2e3,zl=.25,Bl={robot:`#4f8cff`,stale:`#8b95a7`,goal:`#2fbf71`,plan:`#2fbf71`,pose:`#f0a020`},Vl=e=>e*180/Math.PI;function Hl(e){return`x ${e.x.toFixed(2)} y ${e.y.toFixed(2)} θ ${Math.round(Vl(e.yaw))}°`}function Ul(e,t,n){return new DOMMatrix().translate(t/2,n/2).scale(e.scale,-e.scale).translate(-e.x,-e.y)}function Wl(e){return new DOMMatrix().translate(e.x,e.y).rotate(Vl(e.yaw))}function Gl(e,t){return Wl(t).translate(e.originX,e.originY).rotate(Vl(e.originYaw)).scale(e.resolution,e.resolution)}function Kl(e,t,n,r){let i=t.transformPoint({x:n.x,y:n.y}),a=-n.yaw;e.save(),e.translate(i.x,i.y),e.rotate(a),e.strokeStyle=r,e.fillStyle=r,e.lineWidth=3,e.lineCap=`round`,e.beginPath(),e.moveTo(0,0),e.lineTo(26,0),e.stroke(),e.beginPath(),e.moveTo(Il,0),e.lineTo(23,-7),e.lineTo(23,7),e.closePath(),e.fill(),e.beginPath(),e.arc(0,0,5,0,2*Math.PI),e.fill(),e.lineWidth=2,e.strokeStyle=`#fff`,e.stroke(),e.restore()}function ql(e,t,n,r,i){let a=t.transformPoint({x:n.x,y:n.y}),o=Math.max(Fl,Pl*r),s=i?Bl.stale:Bl.robot;e.save(),e.translate(a.x,a.y),e.rotate(-n.yaw),e.beginPath(),e.arc(0,0,o,0,2*Math.PI),e.fillStyle=`${s}55`,e.fill(),e.lineWidth=2,e.strokeStyle=s,e.stroke(),e.beginPath(),e.moveTo(o+6,0),e.lineTo(-o*.35,-o*.55),e.lineTo(-o*.1,0),e.lineTo(-o*.35,o*.55),e.closePath(),e.fillStyle=s,e.fill(),e.lineWidth=1.5,e.strokeStyle=`#fff`,e.stroke(),e.restore()}function Jl(e,t,n){if(!(n.length<4)){e.save(),e.beginPath();for(let r=0;r<n.length;r+=2){let i=t.transformPoint({x:n[r],y:n[r+1]});r===0?e.moveTo(i.x,i.y):e.lineTo(i.x,i.y)}e.lineCap=`round`,e.lineJoin=`round`,e.strokeStyle=`rgb(0 0 0 / 35%)`,e.lineWidth=6,e.stroke(),e.strokeStyle=Bl.plan,e.lineWidth=3,e.stroke(),e.restore()}}function Yl(e,t,n){let r=window.devicePixelRatio||1,i=e.clientWidth,a=e.clientHeight;(e.width!==Math.round(i*r)||e.height!==Math.round(a*r))&&(e.width=Math.round(i*r),e.height=Math.round(a*r));let o=e.getContext(`2d`);if(!o||(o.setTransform(1,0,0,1,0,0),o.clearRect(0,0,e.width,e.height),!t))return;let s=Ul(t,i,a),c=new DOMMatrix().scale(r,r).multiply(s);for(let e of jl){let i=n.layers[e],a=n.placements[e];i&&a&&(o.imageSmoothingEnabled=i.info.resolution*t.scale*r<1,o.setTransform(c.multiply(Gl(i.info,a))),o.drawImage(i.image,0,0))}o.setTransform(r,0,0,r,0,0),n.plan&&Jl(o,s.multiply(Wl(n.plan.placement)),n.plan.points),n.goal&&Kl(o,s,n.goal,Bl.goal),n.robot?.transform&&ql(o,s,n.robot.transform,t.scale,n.robot.ageMs>Rl),n.preview&&Kl(o,s,n.preview.pose,Bl[n.preview.tool])}function Xl(e,t,n,r){let i=Gl(e.info,t),a=e.info.known??{x:0,y:0,width:e.info.width,height:e.info.height},o=[[a.x,a.y],[a.x+a.width,a.y],[a.x,a.y+a.height],[a.x+a.width,a.y+a.height]].map(([e,t])=>i.transformPoint({x:e,y:t})),s=o.map(e=>e.x),c=o.map(e=>e.y),[l,u,d,f]=[Math.min(...s),Math.max(...s),Math.min(...c),Math.max(...c)],p=.94*Math.min(n/(u-l||1),r/(f-d||1));return{x:(l+u)/2,y:(d+f)/2,scale:Math.min(Nl,Math.max(Ml,p))}}function Zl({config:e,editing:t}){let n=(0,q.useRef)(null),i=(0,q.useRef)(null),a=(0,q.useRef)(null),o=(0,q.useRef)({}),[c,l]=(0,q.useState)({map:``,global:``,local:``}),[u,d]=(0,q.useState)({state:`waiting`,message:`Connecting…`}),[f,p]=(0,q.useState)(null),[m,h]=(0,q.useState)(!1),g=(0,q.useRef)(null),_=(0,q.useRef)(null),[y,S]=(0,q.useState)(``),C=(0,q.useRef)(null),w=(0,q.useRef)(new Map),T=(0,q.useRef)(null),E=e.mapFrame.trim(),D=s(E,e.robotFrame),O=e=>c[e]&&c[e]!==E?c[e]:``,k={map:s(E,O(`map`)),global:s(E,O(`global`)),local:s(E,O(`local`))},A={};for(let e of jl)A[e]=O(e)?k[e]?.transform??null:{x:0,y:0,yaw:0};let j=s(E,y&&y!==E?y:``),M=y&&y!==E?j?.transform??null:{x:0,y:0,yaw:0},N=(0,q.useRef)({layers:o.current,placements:A,planPlacement:M,robot:D});N.current={layers:o.current,placements:A,planPlacement:M,robot:D};let P=(0,q.useRef)(0),F=(0,q.useCallback)(()=>{P.current||=requestAnimationFrame(()=>{P.current=0;let e=n.current,{planPlacement:t,...r}=N.current;e&&Yl(e,i.current,{...r,plan:_.current&&t?{points:_.current.points,placement:t}:null,goal:g.current,preview:C.current})})},[]);(0,q.useEffect)(F),(0,q.useEffect)(()=>()=>cancelAnimationFrame(P.current),[]);let I=()=>({width:n.current?.clientWidth??1,height:n.current?.clientHeight??1}),L=(0,q.useCallback)(()=>{let{width:e,height:t}=I();for(let n of jl){let r=o.current[n],s=N.current.placements[n];if(r&&s)return i.current=Xl(r,s,e,t),a.current=n===`map`?`map`:`costmap`,F(),!0}let n=N.current.robot?.transform;return n?(i.current={x:n.x,y:n.y,scale:50},a.current=`robot`,F(),!0):!1},[F]),R=(0,q.useCallback)((e,t,n,r)=>{let i=!0,s=b.subscribeGrid({topic:t,style:n,maxFps:r},{status:(t,n)=>e===`map`&&d({state:t,message:n}),frame:async(t,{meta:n})=>{let r=await createImageBitmap(t);if(!i){r.close();return}let s=n;o.current[e]?.image.close(),o.current[e]={image:r,info:s},l(t=>t[e]===s.frameId?t:{...t,[e]:s.frameId});let c=a.current;(e===`map`?c!==`map`&&c!==`user`:c===null||c===`robot`)&&L(),F()}});return()=>{i=!1,s(),o.current[e]?.image.close(),delete o.current[e],F()}},[L,F]),ee=e.mapTopic.trim();(0,q.useEffect)(()=>{if(!ee)d({state:`error`,message:`No map topic: set one in edit mode`});else return d({state:`waiting`,message:`Connecting…`}),R(`map`,ee,`map`,1)},[ee,R]);let z=e.showGlobalCostmap?e.globalCostmapTopic.trim():``;(0,q.useEffect)(()=>z?R(`global`,z,`costmap`,1):void 0,[z,R]);let ne=e.showLocalCostmap?e.localCostmapTopic.trim():``;(0,q.useEffect)(()=>ne?R(`local`,ne,`costmap`,4):void 0,[ne,R]);let re=e.showPlan?e.planTopic.trim():``;(0,q.useEffect)(()=>{if(!re)return;let e=b.subscribePath({topic:re},e=>{_.current=e,S(e.frameId),F()});return()=>{e(),_.current=null,S(``),F()}},[re,F]),(0,q.useEffect)(()=>{let e=D?.transform;e&&g.current&&Math.hypot(e.x-g.current.x,e.y-g.current.y)<zl&&(g.current=null),m&&e&&i.current?i.current={...i.current,x:e.x,y:e.y}:!i.current&&e&&L()},[D,m,L]),(0,q.useEffect)(()=>{let e=n.current;if(!e)return;let t=new ResizeObserver(F);return t.observe(e),()=>t.disconnect()},[F]);let ie=(0,q.useCallback)((e,t)=>{let n=i.current;if(!n)return;let{width:r,height:o}=I(),s=Math.min(Nl,Math.max(Ml,n.scale*t));a.current=`user`;let c=Ul(n,r,o).inverse().transformPoint(e);i.current={scale:s,x:c.x-(e.x-r/2)/s,y:c.y+(e.y-o/2)/s},F()},[F]);(0,q.useEffect)(()=>{let e=n.current;if(!e)return;let t=t=>{t.preventDefault();let n=e.getBoundingClientRect();ie({x:t.clientX-n.left,y:t.clientY-n.top},Math.exp(-t.deltaY*.0015))};return e.addEventListener(`wheel`,t,{passive:!1}),()=>e.removeEventListener(`wheel`,t)},[ie]),(0,q.useEffect)(()=>{if(!f)return;let e=e=>e.key===`Escape`&&p(null);return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[f]);let ae=e=>{let t=n.current.getBoundingClientRect();return{x:e.clientX-t.left,y:e.clientY-t.top}},oe=e=>{let{width:t,height:n}=I(),{x:r,y:a}=Ul(i.current,t,n).inverse().transformPoint(e);return{x:r,y:a}},se=()=>{let[e,t]=[...w.current.values()];return{distance:Math.hypot(e.x-t.x,e.y-t.y),center:{x:(e.x+t.x)/2,y:(e.y+t.y)/2}}},ce=(t,n)=>{b.send({type:`pose`,topic:t===`goal`?e.goalTopic.trim():e.poseTopic.trim(),covariance:t===`pose`,frameId:E,...n})?(t===`goal`&&(g.current=n),G(`${t===`goal`?`Goal`:`Pose estimate`} sent: ${Hl(n)}`)):G(`Not connected to the robot`,`error`)},B=e=>{if(!i.current||e.pointerType===`mouse`&&e.button!==0)return;e.currentTarget.setPointerCapture(e.pointerId);let t=ae(e);if(w.current.set(e.pointerId,t),w.current.size===2)C.current=null,T.current={kind:`pinch`,...se()};else if(w.current.size===1&&f){let n=oe(t);T.current={kind:`place`,tool:f,pointerId:e.pointerId,anchor:n,start:t},C.current={tool:f,pose:{x:n.x,y:n.y,yaw:0}}}else w.current.size===1&&(T.current={kind:`pan`});F()},le=e=>{let t=w.current.get(e.pointerId),n=i.current;if(!t||!n)return;let r=ae(e);w.current.set(e.pointerId,r);let o=T.current;if(o?.kind===`place`){let e=oe(r),t=Math.hypot(r.x-o.start.x,r.y-o.start.y)>Ll;C.current={tool:o.tool,pose:{...o.anchor,yaw:t?Math.atan2(e.y-o.anchor.y,e.x-o.anchor.x):0}}}else if(o?.kind===`pan`)a.current=`user`,i.current={...n,x:n.x-(r.x-t.x)/n.scale,y:n.y+(r.y-t.y)/n.scale},h(!1);else if(o?.kind===`pinch`&&w.current.size===2){let e=se();i.current={...n,x:n.x-(e.center.x-o.center.x)/n.scale,y:n.y+(e.center.y-o.center.y)/n.scale},ie(e.center,e.distance/(o.distance||1)),T.current={kind:`pinch`,...e}}F()},ue=e=>{if(!w.current.delete(e.pointerId))return;let t=T.current;if(t?.kind===`place`&&t.pointerId===e.pointerId){let n=C.current;C.current=null,T.current=null,e.type===`pointerup`&&n&&(ce(t.tool,n.pose),p(null))}else w.current.size===1?T.current={kind:`pan`}:w.current.size===0&&(T.current=null);F()},de=D?.transform,V;if(!E||!e.robotFrame.trim())V={text:`Set the TF frames in edit mode`,className:`error`};else if(de){let e=D.ageMs>Rl;V={text:`${Hl(de)}${e?` (stale)`:``}`,className:e?`stale`:``}}else V={text:D?.error??`Locating the robot…`,className:`error`};let fe=(e,t,n)=>(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,"aria-pressed":f===e,title:t,"aria-label":t,onClick:()=>p(t=>t===e?null:e),children:(0,J.jsx)(n,{width:17,height:17})});return(0,J.jsxs)(`div`,{className:`navigation`,children:[(0,J.jsx)(`canvas`,{ref:n,className:f?`placing`:void 0,onPointerDown:B,onPointerMove:le,onPointerUp:ue,onPointerCancel:ue}),u.state!==`streaming`&&(0,J.jsxs)(`div`,{className:`nav-overlay ${u.state}`,children:[(0,J.jsx)(te,{width:28,height:28}),(0,J.jsx)(`span`,{children:u.message})]}),(0,J.jsxs)(`div`,{className:`overlay-toolbar`,children:[fe(`pose`,`Set the pose estimate`,v),fe(`goal`,`Send a navigation goal`,x),(0,J.jsx)(`span`,{className:`overlay-separator`}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:`Fit the map`,"aria-label":`Fit the map`,onClick:()=>{h(!1),L()},children:(0,J.jsx)(ge,{width:17,height:17})}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,"aria-pressed":m,title:`Follow the robot`,"aria-label":`Follow the robot`,disabled:!de,onClick:()=>h(e=>!e),children:(0,J.jsx)(r,{width:17,height:17})})]}),f&&!t&&(0,J.jsx)(`div`,{className:`nav-hint`,children:f===`goal`?`Press where the robot should go, drag towards where it should face`:`Press where the robot is, drag towards where it faces`}),(0,J.jsx)(`div`,{className:`nav-readout ${V.className}`,children:V.text})]})}var Ql=Ye({type:`navigation`,name:`Navigation`,description:`The map, the costmaps and the robot on them; set its pose and send it goals.`,icon:te,defaultSize:{w:8,h:6},minSize:{w:3,h:3},component:Zl,properties:[{key:`mapTopic`,label:`Map topic`,kind:`topic`,default:`/map`,topicTypes:[`nav_msgs/msg/OccupancyGrid`]},{key:`showGlobalCostmap`,label:`Show the global costmap`,kind:`boolean`,default:!1},{key:`globalCostmapTopic`,label:`Global costmap topic`,kind:`topic`,default:`/global_costmap/costmap`,topicTypes:[`nav_msgs/msg/OccupancyGrid`],visible:e=>e.showGlobalCostmap},{key:`showLocalCostmap`,label:`Show the local costmap`,kind:`boolean`,default:!0},{key:`localCostmapTopic`,label:`Local costmap topic`,kind:`topic`,default:`/local_costmap/costmap`,topicTypes:[`nav_msgs/msg/OccupancyGrid`],visible:e=>e.showLocalCostmap},{key:`showPlan`,label:`Show the planned path`,kind:`boolean`,default:!0},{key:`planTopic`,label:`Plan topic`,kind:`topic`,default:`/plan`,topicTypes:[`nav_msgs/msg/Path`],help:`nav_msgs/Path: Nav2’s planner publishes its plan on /plan`,visible:e=>e.showPlan},{key:`goalTopic`,label:`Goal topic`,kind:`topic`,default:`/goal_pose`,topicTypes:[`geometry_msgs/msg/PoseStamped`],help:`geometry_msgs/PoseStamped, which Nav2’s bt_navigator follows`},{key:`poseTopic`,label:`Pose estimate topic`,kind:`topic`,default:`/initialpose`,topicTypes:[`geometry_msgs/msg/PoseWithCovarianceStamped`],help:`geometry_msgs/PoseWithCovarianceStamped, which AMCL starts from`},{key:`mapFrame`,label:`Map frame`,kind:`string`,default:`map`,help:`The frame the map is drawn in, and the goals and poses are set in`},{key:`robotFrame`,label:`Robot frame`,kind:`string`,default:`base_footprint`,help:`Where the robot is, from TF`}]}),$l={open:`None (open)`,owe:`Enhanced Open (OWE)`,wep:`WEP`,"wpa-psk":`WPA/WPA2 Personal`,sae:`WPA3 Personal`,"wpa-eap":`WPA/WPA2 Enterprise`};function eu(e){return e!==`open`&&e!==`owe`}var tu=/^[\w.@-]{1,15}$/,nu=Array.from({length:14},(e,t)=>t+1),ru=[36,40,44,48,52,56,60,64,100,104,108,112,116,120,124,128,132,136,140,144,149,153,157,161,165,169,173,177];function iu(e){return tu.test(e)}function au(e){let t=e.split(`.`);return t.length===4&&t.every(e=>/^(0|[1-9]\d{0,2})$/.test(e)&&Number(e)<=255)}function ou(e){if(!/^[0-9a-f:.]+$/i.test(e)||!e.includes(`:`))return!1;let t=e.split(`::`);if(t.length>2)return!1;let n=t.flatMap(e=>e?e.split(`:`):[]),r=n.at(-1)??``,i=r.includes(`.`);if(i&&!au(r))return!1;let a=i?n.slice(0,-1):n;if(!a.every(e=>/^[0-9a-f]{1,4}$/i.test(e)))return!1;let o=a.length+(i?2:0);return t.length===2?o<8:o===8}function su(e){let[t,n,...r]=e.trim().split(`/`);return r.length>0||!au(t)?`“${e.trim()}” is not an IPv4 address`:n===void 0?`Give its network's size: ${t}/24`:!/^\d{1,2}$/.test(n)||Number(n)<1||Number(n)>32?`/${n} is not a network's size: /1 to /32 (/24: 255.255.255.0)`:null}function cu(e){return e.split(/[\s,;]+/).map(e=>e.trim()).filter(Boolean)}function lu(e){return new TextEncoder().encode(e).length}function uu(e,t,n=!1){switch(e){case`open`:case`owe`:return null;case`wpa-psk`:return/^[0-9a-f]{64}$/i.test(t)?null:/^[\x20-\x7e]*$/.test(t)?t.length<8||t.length>63?n?`8 to 63 characters`:`WPA passwords have 8 to 63 characters`:null:`Letters, digits and symbols of the keyboard only: no accents`;case`sae`:return t.length===0?`The network’s password`:t.length>128?`At most 128 characters`:null;case`wep`:return/^([\x20-\x7e]{5}|[\x20-\x7e]{13}|[0-9a-f]{10}|[0-9a-f]{26})$/i.test(t)?null:`WEP keys have 5 or 13 characters, or 10 or 26 hexadecimal digits`;case`wpa-eap`:return t.length===0?`Your password on the network`:null}}function du(e,t){let n={},{kind:r}=e,i=e.name.trim();if(i?(i.length>255||/[\x00-\x1f]/.test(i))&&(n.name=`At most 255 characters, on one line`):n.name=`A name for the connection`,e.interface&&!iu(e.interface)?n.interface=`“${e.interface}” is not an interface's name`:r===`hotspot`&&!e.interface&&(n.interface=`The Wi-Fi interface of the access point`),(!Number.isInteger(e.priority)||e.priority<-999||e.priority>999)&&(n.priority=`A whole number, from -999 to 999`),r!==`ethernet`){let i=lu(e.ssid);if(i===0?n.ssid=r===`hotspot`?`The name the access point shows`:`The network’s name`:i>32?n.ssid=`At most 32 bytes (fewer with accents and symbols)`:/[\x00-\x1f]/.test(e.ssid)&&(n.ssid=`No control characters`),r===`hotspot`&&e.security!==`open`&&e.security!==`wpa-psk`&&(n.security=`An access point is open, or secured by a WPA2 password`),e.security===`wpa-eap`&&(e.identity.trim()?/[\x00-\x1f]/.test(e.identity)&&(n.identity=`No control characters`):n.identity=`Your user name on the network`),e.password!==null){let t=uu(e.security,e.password,r===`hotspot`);t&&(n.password=t)}else t&&eu(e.security)&&(n.password=`The network’s password`);if(r===`hotspot`){let t=e.band===`a`?ru:e.band===`bg`?nu:[];e.channel!==0&&!t.includes(e.channel)&&(n.channel=e.band?`Not a channel of ${e.band===`a`?`5`:`2.4`} GHz`:`Choose the band of the channel`)}}(r===`hotspot`?[`shared`]:[`auto`,`manual`,`shared`,`disabled`]).includes(e.ipv4)||(n.ipv4=r===`hotspot`?`An access point gives the addresses (shared)`:`Not a way to get an address`);let a=e.addresses.map(su).find(e=>e!==null);a?n.addresses=a:e.ipv4===`manual`&&e.addresses.length===0?n.addresses=`The robot’s address on this network: 192.168.1.20/24`:e.ipv4===`shared`&&e.addresses.length>1&&(n.addresses=`One address: the robot’s, on the network it gives`),e.gateway&&!au(e.gateway)&&(n.gateway=`“${e.gateway}” is not an IPv4 address`);let o=e.dns.find(e=>!au(e)&&!ou(e));return o!==void 0&&(n.dns=`“${o}” is not an IP address`),n}function fu(e){let t=e.kind;return t!==`wifi`&&t!==`hotspot`&&t!==`ethernet`?null:{kind:t,name:e.name,interface:e.interface,autoconnect:e.autoconnect,priority:e.priority,ssid:e.ssid,hidden:e.hidden,security:e.security??`open`,password:null,identity:e.identity,band:e.band,channel:e.channel,ipv4:[`auto`,`manual`,`shared`,`disabled`].includes(e.ipv4Method)?e.ipv4Method:`auto`,addresses:e.addresses,gateway:e.gateway,dns:e.dns}}function pu(e){return{kind:e,name:``,interface:``,autoconnect:!0,priority:0,ssid:``,hidden:!1,security:e===`ethernet`?`open`:`wpa-psk`,password:e===`ethernet`?null:``,identity:``,band:``,channel:0,ipv4:e===`hotspot`?`shared`:`auto`,addresses:[],gateway:``,dns:[]}}function mu(e){return e>=70?4:e>=50?3:e>=30?2:1}function hu(e){return e<=0?``:e<3e3?`2.4 GHz`:e<5925?`5 GHz`:`6 GHz`}function gu(e){let t=e.split(/[\s,]+/).filter(Boolean).map(e=>RegExp(`^${e.replace(/[.+^${}()|[\]\\]/g,`\\$&`).replace(/\*/g,`.*`).replace(/\?/g,`.`)}$`));return t.length===0?()=>!0:e=>t.some(t=>t.test(e))}function _u(e,t,n){return e.filter(e=>t(e.name)&&(n||e.type!==`loopback`&&e.type!==`wifi-p2p`&&!e.external&&!(e.software&&e.state===10)))}var vu=[[/need auth/,`Waiting for the password…`],[/getting IP/i,`Getting an address…`],[/checking IP/i,`Checking the connection…`]];function yu(e,t){let{state:n}=e;if(n===100)return e.external?{text:`Configured by another program`,tone:`on`}:{text:e.connectionName?`Connected to ${e.connectionName}`:`Connected`,tone:`on`};if(n>30&&n<100){let t=vu.find(([t])=>t.test(e.stateText));return{text:t?t[1]:`Connecting…`,tone:`busy`}}return n===110?{text:`Disconnecting…`,tone:`busy`}:n===120?{text:e.reason?`Failed: ${e.reason}`:`Failed`,tone:`failed`}:n===20?e.carrier===!1?{text:`Cable unplugged`,tone:`off`}:e.type===`wifi`&&!t?{text:`Wi-Fi is off`,tone:`off`}:{text:`Unavailable`,tone:`off`}:n===10?{text:`Not managed by NetworkManager`,tone:`off`}:{text:`Disconnected`,tone:`off`}}function bu(e){switch(e.type){case`wifi`:return`Wi-Fi`;case`ethernet`:return`Ethernet`;case`gsm`:case`cdma`:return`Mobile broadband`;case`bt`:return`Bluetooth`;case`loopback`:return`Loopback`;default:return e.type.charAt(0).toUpperCase()+e.type.slice(1)}}var xu={openvpn:`OpenVPN`,vpnc:`Cisco VPN`,openconnect:`OpenConnect`,l2tp:`L2TP`,pptp:`PPTP`,strongswan:`IPsec`,libreswan:`IPsec`,fortisslvpn:`Fortinet`};function Su(e){switch(e.kind){case`wifi`:return`Wi-Fi network`;case`hotspot`:return`Access point`;case`ethernet`:return`Wired`;case`vpn`:return e.type===`wireguard`?`WireGuard`:`VPN${e.vpnService?` (${xu[e.vpnService]??e.vpnService})`:``}`;default:return e.type}}var Cu=[{kind:`wifi`,title:`Wi-Fi networks`},{kind:`hotspot`,title:`Access points`},{kind:`ethernet`,title:`Wired`},{kind:`vpn`,title:`VPN`},{kind:`other`,title:`Other`}];function wu(e,t){return Number(t.activeState!==null)-Number(e.activeState!==null)||(t.lastUsed??0)-(e.lastUsed??0)||e.name.localeCompare(t.name)}function Tu(e,t){let n=t.trim().toLowerCase();return!n||[e.name,e.ssid,e.interface,e.device??``].some(e=>e.toLowerCase().includes(n))}function Eu(e,t){if(e.device)return t.some(t=>t.name===e.device);if(e.interface)return t.some(t=>t.name===e.interface);let n=e.kind===`wifi`||e.kind===`hotspot`?`wifi`:e.kind===`ethernet`?`ethernet`:null;return n===null||t.some(e=>e.type===n)}function Du(e,t,n,r){let i=e.filter(e=>(r||!e.external)&&Eu(e,t)&&Tu(e,n));return Cu.map(({kind:e,title:t})=>({kind:e,title:t,connections:i.filter(t=>t.kind===e).sort(wu)})).filter(e=>e.connections.length>0)}function Ou(e,t,n){let r=e.filter(e=>e.kind===`wifi`&&e.ssid===t&&(!e.interface||e.interface===n)).sort(wu);return r.find(e=>e.device===n)??r.find(e=>e.device===null)??null}function ku(e,t,n){let r=n.trim().toLowerCase();return e.networks.filter(e=>e.device===t&&(!r||e.ssid.toLowerCase().includes(r))).map(n=>({network:n,saved:Ou(e.connections,n.ssid,t)})).sort((e,t)=>Number(t.network.active)-Number(e.network.active)||Number(t.saved!==null)-Number(e.saved!==null)||t.network.signal-e.network.signal)}function Au(e,t){return e.filter(e=>e.kind===`hotspot`&&(!e.interface||e.interface===t)).sort(wu)}function ju(e,t){return t.uuid&&e.uuid===t.uuid?!0:!!t.ssid&&e.ssid===t.ssid&&(!t.device||!e.device||e.device===t.device)}function Mu(e,t,n){let r=e.filter(e=>e.action!==`rescan`&&e.action!==`wifi`&&ju(e,t)),i=r.find(e=>e.finished===null)??null,a=r.find(e=>e.finished!==null);return{running:i,failed:!i&&a?.error&&n-(a.finished??0)<12e4?a:null}}function Nu(e,t,n,r){if(n||r)return n||r;let i=e.devices.find(t=>t.name===e.via),a=t===`wifi`||t===`hotspot`?`wifi`:t===`ethernet`?`ethernet`:null;return i&&i.type===a?i.name:null}function Pu(e,t){let{via:n}=e;if(!n)return null;let r=`This page reaches the robot through ${n}`,i=`uuid`in t?e.connections.find(e=>e.uuid===t.uuid):void 0,a=i?.device===n;switch(t.action){case`connect`:return!i||a||Nu(e,i.kind,t.device,i.interface)!==n?null:i.kind===`hotspot`?`${r}: starting the access point disconnects it from its network, and this page loses the robot. Join ${i.ssid||i.name} to reach it again.`:`${r}: connecting it to ${i.ssid||i.name} moves the robot to that network, and this page loses it unless it is on that network too.`;case`create`:{if(!t.connect)return null;let{settings:i}=t;return Nu(e,i.kind,t.device,i.interface)===n?i.kind===`hotspot`?`${r}: starting the access point disconnects it from its network, and this page loses the robot. Join ${i.ssid} to reach it again.`:`${r}: connecting it to ${i.ssid||i.name} moves the robot to that network, and this page loses it unless it is on that network too.`:null}case`disconnect`:return a?`${r}: disconnecting ${i.name} cuts this page off from the robot.`:null;case`delete`:return a?`${r}: forgetting ${i.name} disconnects it, and cuts this page off.`:null;case`modify`:return a?`${r}: saving reconnects ${i.name}, and if its address changes this page loses the robot.`:null;case`wifi`:{let i=e.devices.find(e=>e.name===n);return!t.enabled&&i?.type===`wifi`?`${r}, a Wi-Fi interface: turning Wi-Fi off cuts this page off from the robot.`:null}default:return null}}function Fu(e,t){if(e===null)return`Never used`;let n=Math.max(0,Math.round((t-e)/1e3));if(n<60)return`Used just now`;if(n<3600)return`Used ${Math.floor(n/60)} min ago`;if(n<86400)return`Used ${Math.floor(n/3600)} h ago`;let r=Math.floor(n/86400);return r<30?`Used ${r} day${r===1?``:`s`} ago`:`Used on ${new Date(e).toLocaleDateString()}`}function Iu(e,t){let n=new Set(t.map(e=>e.name));if(!n.has(e))return e;let r=2;for(;n.has(`${e} (${r})`);)r++;return`${e} (${r})`}function Lu(e,t,n,r,i,a){return{...pu(`wifi`),name:Iu(e.ssid,a),ssid:e.ssid,security:e.security,password:t,identity:n,autoconnect:r,hidden:i}}function Ru(e){let t=e.filter(e=>e.state===100&&!e.external);return t.find(e=>e.gateway)??t[0]??null}function zu(e,t){if(e.error)return{text:e.error,tone:`failed`};let n=e.activity.find(e=>e.finished===null);if(n)return{text:`${n.label}…`,tone:`busy`};let r=e.activity[0];if(r?.error&&t-(r.finished??0)<6e4)return{text:`${r.label}: ${r.error}`,tone:`failed`};let i=[],a;switch(e.connectivity){case`full`:i.push(`Online`),a=`on`;break;case`limited`:i.push(`No internet`),a=`warn`;break;case`portal`:i.push(`Behind a login page`),a=`warn`;break;default:i.push(e.state.startsWith(`connected`)?`Local network only`:`Disconnected`),a=e.state.startsWith(`connected`)?`warn`:`off`}let o=Ru(e.devices);if(o){let e=o.ipv4[0]?.split(`/`)[0];i.push(`${o.name}: ${o.connectionName}${e?` (${e})`:``}`)}return e.via&&i.push(`this page through ${e.via}`),{text:i.join(` · `),tone:a}}function Bu(e,t){return t?`This widget is read-only`:e.canChange?!e.permissions.control&&!e.permissions.modify?`The server’s user may not change the robot’s networks (see the README: a polkit rule)`:null:`The networks are changed from the dashboard’s own pages only`}var Vu=[[10,[16]],[16,[28]],[26,[44]],[18,[32,32]],[24,[43,43]],[16,[27,27,27,27]],[18,[31,31,31,31]],[22,[38,38,39,39]],[22,[36,36,36,37,37]],[26,[43,43,43,43,44]],[30,[50,51,51,51,51]],[22,[36,36,36,36,36,36,37,37]],[22,[37,37,37,37,37,37,37,37,38]]],Hu=[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62]];function Uu(e,t){let n=0;for(let r=7;r>=0;r--)n=n<<1^(n>>>7)*285,n^=(t>>>r&1)*e;return n}function Wu(e){let t=Array(e).fill(0);t[e-1]=1;let n=1;for(let r=0;r<e;r++){for(let r=0;r<e;r++)t[r]=Uu(t[r],n),r+1<e&&(t[r]^=t[r+1]);n=Uu(n,2)}return t}function Gu(e,t){let n=t.map(()=>0);for(let r of e){let e=r^(n.shift()??0);n.push(0),t.forEach((t,r)=>{n[r]^=Uu(t,e)})}return n}var Ku=[(e,t)=>(e+t)%2==0,(e,t)=>t%2==0,e=>e%3==0,(e,t)=>(e+t)%3==0,(e,t)=>(Math.floor(e/3)+Math.floor(t/2))%2==0,(e,t)=>e*t%2+e*t%3==0,(e,t)=>(e*t%2+e*t%3)%2==0,(e,t)=>((e+t)%2+e*t%3)%2==0];function qu(e,t){let[n,r]=Vu[t-1],i=r.reduce((e,t)=>e+t,0)*8,a=[],o=(e,t)=>{for(let n=t-1;n>=0;n--)a.push(e>>>n&1)};o(4,4),o(e.length,t<10?8:16),e.forEach(e=>o(e,8)),o(0,Math.min(4,i-a.length)),o(0,(8-a.length%8)%8);for(let e=236;a.length<i;e^=253)o(e,8);let s=[];for(let e=0;e<a.length;e+=8)s.push(a.slice(e,e+8).reduce((e,t)=>e<<1|t,0));let c=Wu(n),l=[],u=0;for(let e of r){let t=s.slice(u,u+e);l.push({data:t,ec:Gu(t,c)}),u+=e}let d=[];for(let e=0;e<Math.max(...r);e++)l.forEach(t=>e<t.data.length&&d.push(t.data[e]));for(let e=0;e<n;e++)l.forEach(t=>d.push(t.ec[e]));return d}var Ju=class{size;dark;fixed;constructor(e){this.size=e,this.dark=Array.from({length:e},()=>Array(e).fill(!1)),this.fixed=Array.from({length:e},()=>Array(e).fill(!1))}set(e,t,n){this.dark[t][e]=n,this.fixed[t][e]=!0}};function Yu(e,t){let{size:n}=e;for(let t=0;t<n;t++)e.set(6,t,t%2==0),e.set(t,6,t%2==0);for(let[t,r]of[[3,3],[n-4,3],[3,n-4]])for(let i=-4;i<=4;i++)for(let a=-4;a<=4;a++){let o=t+a,s=r+i;if(o>=0&&o<n&&s>=0&&s<n){let t=Math.max(Math.abs(a),Math.abs(i));e.set(o,s,t!==2&&t!==4)}}let r=Hu[t-1],i=r.length-1;if(r.forEach((t,n)=>r.forEach((r,a)=>{if(!(n===0&&a===0||n===0&&a===i||n===i&&a===0))for(let n=-2;n<=2;n++)for(let i=-2;i<=2;i++)e.set(t+i,r+n,Math.max(Math.abs(i),Math.abs(n))!==1)})),Xu(e,0),t>=7){let r=t;for(let e=0;e<12;e++)r=r<<1^(r>>>11)*7973;let i=t<<12|r;for(let t=0;t<18;t++){let r=(i>>>t&1)==1,a=n-11+t%3,o=Math.floor(t/3);e.set(a,o,r),e.set(o,a,r)}}}function Xu(e,t){let{size:n}=e,r=0|t,i=r;for(let e=0;e<10;e++)i=i<<1^(i>>>9)*1335;let a=(r<<10|i)^21522,o=e=>(a>>>e&1)==1;for(let t=0;t<=5;t++)e.set(8,t,o(t));e.set(8,7,o(6)),e.set(8,8,o(7)),e.set(7,8,o(8));for(let t=9;t<15;t++)e.set(14-t,8,o(t));for(let t=0;t<8;t++)e.set(n-1-t,8,o(t));for(let t=8;t<15;t++)e.set(8,n-15+t,o(t));e.set(8,n-8,!0)}function Zu(e,t){let{size:n}=e,r=0;for(let i=n-1;i>=1;i-=2){i===6&&(i=5);for(let a=0;a<n;a++)for(let o=0;o<2;o++){let s=i-o,c=i+1&2?a:n-1-a;!e.fixed[c][s]&&r<t.length*8&&(e.dark[c][s]=(t[r>>>3]>>>7-(r&7)&1)==1,r++)}}}function Qu(e,t){let n=Ku[t];for(let t=0;t<e.size;t++)for(let r=0;r<e.size;r++)!e.fixed[t][r]&&n(r,t)&&(e.dark[t][r]=!e.dark[t][r])}function $u(e){let t=e.length,n=0,r=[...e,...Array.from({length:t},(t,n)=>e.map(e=>e[n]))];for(let e of r){let r=1;for(let i=1;i<=t;i++)i<t&&e[i]===e[i-1]?r++:(r>=5&&(n+=3+(r-5)),r=1);let i=n=>n>=0&&n<t&&e[n];for(let e=0;e+7<=t;e++){if(!(i(e)&&!i(e+1)&&i(e+2)&&i(e+3)&&i(e+4)&&!i(e+5)&&i(e+6)))continue;let t=[1,2,3,4].every(t=>!i(e-t)),r=[7,8,9,10].every(t=>!i(e+t));(t||r)&&(n+=40)}}for(let r=0;r+1<t;r++)for(let i=0;i+1<t;i++){let t=e[r][i];t===e[r][i+1]&&t===e[r+1][i]&&t===e[r+1][i+1]&&(n+=3)}let i=e.reduce((e,t)=>e+t.filter(Boolean).length,0),a=t*t;return n+=(Math.ceil(Math.abs(i*20-a*10)/a)-1)*10,n}function ed(e,t){let n=new TextEncoder().encode(e),r=0;for(let e=1;e<=Vu.length&&r===0;e++){let t=Vu[e-1][1].reduce((e,t)=>e+t,0)*8;4+(e<10?8:16)+n.length*8<=t&&(r=e)}if(r===0)return null;let i=qu(n,r),a=null;for(let e of t===void 0?[0,1,2,3,4,5,6,7]:[t]){let t=new Ju(17+4*r);Yu(t,r),Zu(t,i),Qu(t,e),Xu(t,e);let n=$u(t.dark);(!a||n<a.score)&&(a={score:n,dark:t.dark})}return a.dark}function td(e,t,n,r){let i=e=>e.replace(/([\\;,:"])/g,`\\$1`),a=n===`nopass`?``:`P:${i(t)};`;return`WIFI:T:${n};S:${i(e)};${a}${r?`H:true;`:``};`}function nd({signal:e,size:t=18}){let n=mu(e),r=e=>n>=e?void 0:.22;return(0,J.jsxs)(`svg`,{viewBox:`0 0 24 24`,width:t,height:t,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,"aria-hidden":`true`,className:`net-signal`,children:[(0,J.jsx)(`path`,{d:`M2.5 9a14 14 0 0 1 19 0`,opacity:r(4)}),(0,J.jsx)(`path`,{d:`M5.5 12.5a9.5 9.5 0 0 1 13 0`,opacity:r(3)}),(0,J.jsx)(`path`,{d:`M8.6 15.9a5 5 0 0 1 6.8 0`,opacity:r(2)}),(0,J.jsx)(`circle`,{cx:`12`,cy:`19.3`,r:`1.3`,fill:`currentColor`,stroke:`none`})]})}function rd({kind:e,size:t=18}){let n={width:t,height:t};switch(e){case`wifi`:return(0,J.jsx)(O,{...n});case`hotspot`:return(0,J.jsx)(o,{...n});case`ethernet`:return(0,J.jsx)(c,{...n});case`vpn`:return(0,J.jsx)(He,{...n});default:return(0,J.jsx)(ue,{...n})}}function id({device:e,size:t=18}){return(0,J.jsx)(rd,{kind:e.type===`wifi`?`wifi`:e.type===`ethernet`?`ethernet`:e.type===`tun`||e.type===`wireguard`?`vpn`:`other`,size:t})}function ad({tone:e}){return(0,J.jsx)(`span`,{className:`net-dot ${e}`,"aria-hidden":`true`})}function od({label:e,help:t,error:n,children:r}){let i=(0,q.useId)();return(0,J.jsxs)(`div`,{className:`field`,children:[(0,J.jsx)(`label`,{className:`field-label`,htmlFor:i,children:e}),r(i),n?(0,J.jsx)(`p`,{className:`field-help error`,children:n}):t&&(0,J.jsx)(`p`,{className:`field-help`,children:t})]})}function sd({label:e,help:t,checked:n,onChange:r}){return(0,J.jsxs)(`div`,{className:`field`,children:[(0,J.jsxs)(`label`,{className:`toggle`,children:[(0,J.jsx)(`input`,{type:`checkbox`,checked:n,onChange:e=>r(e.target.checked)}),(0,J.jsx)(`span`,{className:`toggle-switch`,"aria-hidden":`true`}),(0,J.jsx)(`span`,{className:`field-label`,children:e})]}),t&&(0,J.jsx)(`p`,{className:`field-help`,children:t})]})}function cd({id:e,value:t,placeholder:n,autoFocus:r=!1,initiallyShown:i=!1,onChange:a}){let[o,s]=(0,q.useState)(i);return(0,J.jsxs)(`div`,{className:`net-password`,children:[(0,J.jsx)(`input`,{id:e,type:o?`text`:`password`,value:t,placeholder:n,autoComplete:`new-password`,autoCapitalize:`off`,spellCheck:!1,autoFocus:r,onChange:e=>a(e.target.value)}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:o?`Hide the password`:`Show the password`,"aria-label":o?`Hide the password`:`Show the password`,"aria-pressed":o,onClick:()=>s(e=>!e),children:o?(0,J.jsx)(P,{width:16,height:16}):(0,J.jsx)(Le,{width:16,height:16})})]})}function ld({text:e,label:t}){let n=(0,q.useMemo)(()=>{let t=ed(e);if(!t)return null;let n=``;return t.forEach((e,t)=>{for(let r=0;r<e.length;r++){if(!e[r])continue;let i=1;for(;r+i<e.length&&e[r+i];)i++;n+=`M${r+4} ${t+4}h${i}v1h-${i}z`,r+=i-1}}),{d:n,size:t.length+8}},[e]);return n?(0,J.jsxs)(`svg`,{className:`net-qr`,viewBox:`0 0 ${n.size} ${n.size}`,shapeRendering:`crispEdges`,role:`img`,"aria-label":t,children:[(0,J.jsx)(`rect`,{width:n.size,height:n.size,fill:`#fff`}),(0,J.jsx)(`path`,{d:n.d,fill:`#000`})]}):null}function ud(){let e=crypto.getRandomValues(new Uint32Array(12));return Array.from(e,e=>`abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789`[e%55]).join(``)}function dd(e){return/Disconnected from the robot|No answer from the robot/.test(rs(e))}async function fd(e,t,n){try{let n=await b.networkAction(e);n.message?G(n.message,`error`):t&&G(t)}catch(e){if(n&&dd(e)){G(`This page lost the robot: it moved to another network, where it is to be reached now`);return}throw e}}function pd({risk:e,error:t}){return(0,J.jsxs)(J.Fragment,{children:[e&&(0,J.jsx)(`p`,{className:`net-warning`,children:e}),t&&(0,J.jsx)(`p`,{className:`net-error`,role:`alert`,children:t})]})}function md({state:e,request:t,onClose:n}){let r=t.kind===`password`?t.connection:null,i=t.kind===`join`?t.network:e.networks.find(e=>e.ssid===r.ssid&&(!t.device||e.device===t.device)),a=r?.ssid||r?.name||i?.ssid||``,o=r?.security??i?.security??`wpa-psk`,[s,c]=(0,q.useState)(``),[l,u]=(0,q.useState)(r?.identity??``),[d,f]=(0,q.useState)(!0),[p,m]=(0,q.useState)(!1),[h,g]=(0,q.useState)(!1),[_,v]=(0,q.useState)(t.kind===`password`?t.error:null),y=o===`wpa-eap`,b=uu(o,s)??(y&&!l.trim()?`Your user name on the network`:null),x=r?{action:`connect`,uuid:r.uuid,device:t.kind===`password`?t.device:null,password:s}:{action:`create`,settings:Lu(i,s,l.trim(),d,!1,e.connections),connect:!0,device:i.device},S=Pu(e,x),C=async e=>{if(e.preventDefault(),m(!0),!b){g(!0),v(null);try{await fd(x,`Connected to ${a}`,S!==null),n()}catch(e){v(rs(e)),g(!1)}}};return(0,J.jsx)($e,{title:r?`The password of ${a}`:`Join ${a}`,onClose:n,children:(0,J.jsxs)(`form`,{className:`modal-body`,onSubmit:e=>void C(e),children:[i&&(0,J.jsxs)(`p`,{className:`net-dialog-meta`,children:[(0,J.jsx)(nd,{signal:i.signal,size:16}),[$l[o],hu(i.frequency),`${i.signal}%`].filter(Boolean).join(` · `)]}),y&&(0,J.jsx)(od,{label:`User name`,error:p&&!l.trim()?`Your user name on the network`:null,children:e=>(0,J.jsx)(`input`,{id:e,value:l,autoComplete:`off`,autoCapitalize:`off`,spellCheck:!1,autoFocus:!0,onChange:e=>u(e.target.value)})}),(0,J.jsx)(od,{label:`Password`,error:p?uu(o,s):null,help:y?`PEAP with MSCHAPv2, the server’s certificate not checked`:void 0,children:e=>(0,J.jsx)(cd,{id:e,value:s,autoFocus:!y,onChange:c})}),!r&&(0,J.jsx)(sd,{label:`Connect automatically`,help:`Whenever the robot is in its range`,checked:d,onChange:f}),(0,J.jsx)(pd,{risk:S,error:_}),(0,J.jsxs)(`div`,{className:`modal-actions`,children:[(0,J.jsx)(`button`,{type:`button`,className:`button`,onClick:n,children:`Cancel`}),(0,J.jsx)(`button`,{type:`submit`,className:`button ${S?`danger`:`primary`}`,disabled:h,children:h?`Connecting…`:r?`Connect`:`Join`})]})]})})}var hd={wifi:[{value:`auto`,label:`Automatic (DHCP)`},{value:`manual`,label:`Manual`},{value:`disabled`,label:`Disabled`}],hotspot:[{value:`shared`,label:`The robot gives them`}],ethernet:[{value:`auto`,label:`Automatic (DHCP)`},{value:`manual`,label:`Manual`},{value:`shared`,label:`Shared: the robot gives them, and its internet`},{value:`disabled`,label:`Disabled`}]},gd={bg:[1,2,3,4,5,6,7,8,9,10,11,12,13],a:[36,40,44,48,149,153,157,161,165]},_d=[`wpa-psk`,`sae`,`wpa-eap`,`open`,`owe`,`wep`];function vd(e,t){if(t.kind===`edit`)return fu(t.connection);let n=t.settings,r=pu(n);if(n===`hotspot`){let n=e.devices.filter(e=>e.type===`wifi`&&e.accessPoint),i=n.find(e=>e.name===t.device)??n.find(t=>t.name!==e.via)??n[0],a=(b.hostname||`robot`).slice(0,32);return{...r,interface:i?.name??``,ssid:a,name:Iu(a,e.connections),password:ud(),autoconnect:!1}}return n===`ethernet`?{...r,interface:t.device??``,name:Iu(`Wired connection`,e.connections)}:{...r,hidden:!0,interface:t.device??``}}function yd({state:e,request:t,onClose:n}){let r=t.kind===`edit`?t.connection:null,[i,a]=(0,q.useState)(()=>vd(e,t)),[o,s]=(0,q.useState)(i.addresses.join(`, `)),[c,l]=(0,q.useState)(i.dns.join(`, `)),[u,d]=(0,q.useState)(r!==null),[f,p]=(0,q.useState)(!0),[m,h]=(0,q.useState)(!1),[g,_]=(0,q.useState)(!1),[v,y]=(0,q.useState)(null),{kind:b}=i,x=b!==`ethernet`,S=t=>a(n=>{let i={...n,...t};return!u&&t.ssid!==void 0&&i.ssid&&(i.name=Iu(i.ssid,e.connections.filter(e=>e.uuid!==r?.uuid))),i}),C=du(i,!r);r&&x&&i.security!==(r.security??`open`)&&eu(i.security)&&i.password===null&&(C.password=`The network’s password, for its new security`);let w=e=>m?C[e]??null:null,T=r?{action:`modify`,uuid:r.uuid,settings:i}:{action:`create`,settings:i,connect:f,device:null},E=Pu(e,T),D=async e=>{if(e.preventDefault(),h(!0),Object.keys(C).length>0)return;_(!0),y(null);let t=`Saved ${i.name}`;!r&&f&&(t=b===`hotspot`?`The access point ${i.ssid} is on`:`Connected to ${i.ssid||i.name}`);try{await fd(T,t,E!==null),n()}catch(e){y(rs(e)),_(!1)}},O=e.devices.filter(e=>e.type===`wifi`&&(b!==`hotspot`||e.accessPoint)),k=e.devices.filter(e=>e.type===`ethernet`),A=e.devices.find(e=>e.name===i.interface),j;j=r?`Edit ${r.name}`:b===`hotspot`?`New access point`:b===`ethernet`?`New wired connection`:`Join a hidden network`;let M=`Save`;!r&&f&&(M=b===`hotspot`?`Start`:b===`wifi`?`Join`:`Connect`);let N=b===`ethernet`||O.length>1||i.interface||b===`hotspot`?(0,J.jsx)(od,{label:`Interface`,error:w(`interface`),help:b===`hotspot`?void 0:`Bound to it, the connection is not used by other interfaces: for a sensor’s cable, say`,children:t=>(0,J.jsxs)(`select`,{id:t,value:i.interface,onChange:e=>S({interface:e.target.value}),children:[b!==`hotspot`&&(0,J.jsx)(`option`,{value:``,children:x?`Any Wi-Fi interface`:`Any wired interface`}),b===`hotspot`&&!i.interface&&(0,J.jsx)(`option`,{value:``,children:`Choose one`}),(x?O:k).map(t=>(0,J.jsxs)(`option`,{value:t.name,children:[t.name,t.product||t.vendor?` (${t.product||t.vendor})`:``,t.name===e.via?` · this page`:``]},t.name)),i.interface&&!e.devices.some(e=>e.name===i.interface)&&(0,J.jsxs)(`option`,{value:i.interface,children:[i.interface,` (not here)`]})]})}):null,P=r&&i.password===null?`Unchanged: type a new one to change it`:void 0;return(0,J.jsx)($e,{title:j,onClose:n,wide:!0,children:(0,J.jsxs)(`form`,{className:`modal-body net-form`,onSubmit:e=>void D(e),children:[b===`hotspot`&&N,x&&(0,J.jsx)(od,{label:`Network name (SSID)`,error:w(`ssid`),help:b===`hotspot`?`What phones and laptops list`:void 0,children:e=>(0,J.jsx)(`input`,{id:e,value:i.ssid,autoComplete:`off`,autoCapitalize:`off`,spellCheck:!1,autoFocus:!r,onChange:e=>S({ssid:e.target.value})})}),x&&(0,J.jsxs)(`div`,{className:`net-form-row`,children:[(0,J.jsx)(od,{label:`Security`,error:w(`security`),children:e=>(0,J.jsx)(`select`,{id:e,value:i.security,onChange:e=>{let t=e.target.value;S({security:t,password:eu(t)?i.password??(r?null:``):null})},children:(b===`hotspot`?[`wpa-psk`,`open`]:_d).map(e=>(0,J.jsx)(`option`,{value:e,children:b===`hotspot`&&e===`wpa-psk`?`WPA2 Personal`:$l[e]},e))})}),i.security===`wpa-eap`&&(0,J.jsx)(od,{label:`User name`,error:w(`identity`),children:e=>(0,J.jsx)(`input`,{id:e,value:i.identity,autoComplete:`off`,autoCapitalize:`off`,spellCheck:!1,onChange:e=>S({identity:e.target.value})})})]}),x&&eu(i.security)&&(0,J.jsx)(od,{label:`Password`,error:w(`password`),help:P??(b===`hotspot`?`8 to 63 characters: give it to who joins, or share the access point’s QR code`:i.security===`wpa-eap`?`PEAP with MSCHAPv2, the server’s certificate not checked`:void 0),children:e=>(0,J.jsxs)(`div`,{className:`net-password-row`,children:[(0,J.jsx)(cd,{id:e,value:i.password??``,placeholder:r&&i.password===null?`Unchanged`:void 0,initiallyShown:b===`hotspot`&&!r,onChange:e=>S({password:e===``&&r?null:e})}),b===`hotspot`&&(0,J.jsx)(`button`,{type:`button`,className:`button`,onClick:()=>S({password:ud()}),children:`New one`})]})}),b===`hotspot`&&i.security===`open`&&(0,J.jsx)(`p`,{className:`net-warning`,children:`Open: anyone in range may join the robot’s network, and reach the dashboard.`}),b===`hotspot`&&(0,J.jsxs)(`div`,{className:`net-form-row`,children:[(0,J.jsx)(od,{label:`Band`,children:e=>(0,J.jsxs)(`select`,{id:e,value:i.band,onChange:e=>S({band:e.target.value,channel:0}),children:[(0,J.jsx)(`option`,{value:``,children:`Automatic`}),(0,J.jsx)(`option`,{value:`bg`,disabled:A&&!A.band24,children:`2.4 GHz: farther, every device`}),(0,J.jsx)(`option`,{value:`a`,disabled:A&&!A.band5,children:`5 GHz: faster`})]})}),(0,J.jsx)(od,{label:`Channel`,error:w(`channel`),children:e=>(0,J.jsxs)(`select`,{id:e,value:i.channel,disabled:!i.band,onChange:e=>S({channel:Number(e.target.value)}),children:[(0,J.jsx)(`option`,{value:0,children:`Automatic`}),(i.band?gd[i.band]:[]).map(e=>(0,J.jsx)(`option`,{value:e,children:e},e)),i.band&&i.channel!==0&&!gd[i.band].includes(i.channel)&&(0,J.jsx)(`option`,{value:i.channel,children:i.channel})]})})]}),b===`wifi`&&(0,J.jsx)(sd,{label:`Hidden network`,help:`It does not show its name: the robot looks for it by name`,checked:i.hidden,onChange:e=>S({hidden:e})}),b!==`hotspot`&&N,(0,J.jsx)(od,{label:`Connection name`,error:w(`name`),help:`How the robot’s lists name it`,children:e=>(0,J.jsx)(`input`,{id:e,value:i.name,autoComplete:`off`,onChange:e=>{d(!0),S({name:e.target.value})}})}),(0,J.jsx)(`h3`,{className:`net-form-heading`,children:`Addresses`}),b!==`hotspot`&&(0,J.jsx)(od,{label:`IPv4`,error:w(`ipv4`),children:e=>(0,J.jsx)(`select`,{id:e,value:i.ipv4,onChange:e=>S({ipv4:e.target.value}),children:hd[b].map(e=>(0,J.jsx)(`option`,{value:e.value,children:e.label},e.value))})}),(i.ipv4===`manual`||i.ipv4===`shared`)&&(0,J.jsx)(od,{label:i.ipv4===`manual`?`The robot’s address`:`The robot’s address on the network it gives`,error:w(`addresses`),help:i.ipv4===`manual`?`With its network’s size: 192.168.1.20/24. Several: separated by commas`:`Optional: 10.42.0.1/24 if empty. The others get addresses of its network`,children:e=>(0,J.jsx)(`input`,{id:e,value:o,placeholder:i.ipv4===`manual`?`192.168.1.20/24`:`10.42.0.1/24`,inputMode:`decimal`,autoComplete:`off`,spellCheck:!1,onChange:e=>{s(e.target.value),S({addresses:cu(e.target.value)})}})}),i.ipv4!==`disabled`&&b!==`hotspot`&&(0,J.jsxs)(`div`,{className:`net-form-row`,children:[i.ipv4===`manual`&&(0,J.jsx)(od,{label:`Gateway`,error:w(`gateway`),help:`Optional: the way to other networks`,children:e=>(0,J.jsx)(`input`,{id:e,value:i.gateway,placeholder:`192.168.1.1`,inputMode:`decimal`,autoComplete:`off`,spellCheck:!1,onChange:e=>S({gateway:e.target.value.trim()})})}),(0,J.jsx)(od,{label:`DNS servers`,error:w(`dns`),help:i.ipv4===`manual`?`Optional, separated by commas`:`Optional: besides the network’s`,children:e=>(0,J.jsx)(`input`,{id:e,value:c,placeholder:`1.1.1.1, 8.8.8.8`,inputMode:`decimal`,autoComplete:`off`,spellCheck:!1,onChange:e=>{l(e.target.value),S({dns:cu(e.target.value)})}})})]}),(0,J.jsx)(`h3`,{className:`net-form-heading`,children:`When`}),(0,J.jsx)(sd,{label:b===`hotspot`?`Start automatically`:`Connect automatically`,help:b===`hotspot`?`When the robot starts: it is then the robot’s network wherever it goes`:b===`wifi`?`Whenever the robot is in its range`:`Whenever a cable is plugged in`,checked:i.autoconnect,onChange:e=>S({autoconnect:e})}),i.autoconnect&&(0,J.jsx)(od,{label:`Priority`,error:w(`priority`),help:`Of the connections that connect automatically, the highest first`,children:e=>(0,J.jsx)(`input`,{id:e,type:`number`,min:-999,max:999,step:1,value:Number.isFinite(i.priority)?i.priority:``,onChange:e=>S({priority:e.target.value===``?0:Number(e.target.value)})})}),!r&&(0,J.jsx)(sd,{label:b===`hotspot`?`Start it now`:`Connect now`,checked:f,onChange:p}),(0,J.jsx)(pd,{risk:E,error:v}),(0,J.jsxs)(`div`,{className:`modal-actions`,children:[(0,J.jsx)(`button`,{type:`button`,className:`button`,onClick:n,children:`Cancel`}),(0,J.jsx)(`button`,{type:`submit`,className:`button ${E?`danger`:`primary`}`,disabled:g,children:g?f&&!r?`Connecting…`:`Saving…`:M})]})]})})}function bd({text:e,what:t}){return(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:`Copy the ${t}`,"aria-label":`Copy the ${t}`,onClick:()=>ts(e).then(()=>G(`Copied the ${t}`),e=>G(`Could not copy the ${t}: ${rs(e)}`,`error`)),children:(0,J.jsx)(z,{width:15,height:15})})}function xd({connection:e,onClose:t}){let[n,r]=(0,q.useState)(null),[i,a]=(0,q.useState)(null),[o,s]=(0,q.useState)(!1);(0,q.useEffect)(()=>{let t=!0;return b.networkAction({action:`secrets`,uuid:e.uuid}).then(e=>t&&r(e),e=>t&&a(rs(e))),()=>{t=!1}},[e.uuid]);let c=n?.ssid??e.ssid,l=n?.security??e.security??`open`,u=!eu(l),d=u||!!n?.password,f=n&&d&&l!==`wpa-eap`?td(c,n.password,u?`nopass`:l===`wep`?`WEP`:`WPA`,n.hidden):null,p;return p=i?(0,J.jsx)(`p`,{className:`net-error`,children:i}):n?(0,J.jsxs)(J.Fragment,{children:[f&&(0,J.jsxs)(`div`,{className:`net-share-code`,children:[(0,J.jsx)(ld,{text:f,label:`QR code to join ${c}`}),(0,J.jsxs)(`p`,{className:`net-muted`,children:[`Point a phone’s camera at it to join `,c]})]}),(0,J.jsxs)(`dl`,{className:`net-facts net-share-facts`,children:[(0,J.jsxs)(`div`,{children:[(0,J.jsx)(`dt`,{children:`Network`}),(0,J.jsxs)(`dd`,{children:[(0,J.jsx)(`span`,{children:c}),(0,J.jsx)(bd,{text:c,what:`network’s name`})]})]}),(0,J.jsxs)(`div`,{children:[(0,J.jsx)(`dt`,{children:`Security`}),(0,J.jsx)(`dd`,{children:$l[l]})]}),!u&&(0,J.jsxs)(`div`,{children:[(0,J.jsx)(`dt`,{children:`Password`}),(0,J.jsx)(`dd`,{children:d?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`span`,{className:`net-secret`,children:o?n.password:`•`.repeat(Math.min(16,n.password.length))}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:o?`Hide the password`:`Show the password`,"aria-label":o?`Hide the password`:`Show the password`,"aria-pressed":o,onClick:()=>s(e=>!e),children:o?`Hide`:`Show`}),(0,J.jsx)(bd,{text:n.password,what:`password`})]}):(0,J.jsx)(`span`,{className:`net-muted`,children:`Not readable: the server’s user may not read NetworkManager’s secrets, or they are kept by a desktop’s keyring`})})]})]})]}):(0,J.jsx)(`p`,{className:`net-muted`,children:`Reading the password…`}),(0,J.jsx)($e,{title:`Share ${c||e.name}`,onClose:t,children:(0,J.jsxs)(`div`,{className:`modal-body`,children:[p,(0,J.jsx)(`div`,{className:`modal-actions`,children:(0,J.jsx)(`button`,{type:`button`,className:`button primary`,onClick:t,autoFocus:!0,children:`Done`})})]})})}function Sd({request:e,state:t,onClose:n}){switch(e.kind){case`join`:case`password`:return(0,J.jsx)(md,{state:t,request:e,onClose:n});case`create`:case`edit`:return(0,J.jsx)(yd,{state:t,request:e,onClose:n});case`share`:return(0,J.jsx)(xd,{connection:e.connection,onClose:n})}}var Cd={auto:`Automatic (DHCP)`,manual:`Manual`,shared:`Shared: the robot gives the addresses`,disabled:`Disabled`,"link-local":`Link-local only`};function $({label:e,children:t}){return(0,J.jsxs)(`div`,{children:[(0,J.jsx)(`dt`,{children:e}),(0,J.jsx)(`dd`,{children:t})]})}function wd({icon:e,name:t,title:n,children:r,footer:i,onClose:a}){return(0,J.jsxs)(`section`,{className:`net-details`,"aria-label":`${t}: details`,children:[(0,J.jsxs)(`header`,{className:`net-details-header`,children:[(0,J.jsx)(`span`,{className:`net-details-icon`,children:e}),(0,J.jsx)(`strong`,{className:`net-details-name`,title:n??t,children:t}),(0,J.jsx)(`span`,{className:`net-spacer`}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:`Close (Esc)`,"aria-label":`Close`,onClick:a,children:(0,J.jsx)(H,{width:16,height:16})})]}),(0,J.jsx)(`div`,{className:`net-details-body`,children:r}),i&&(0,J.jsx)(`footer`,{className:`net-details-actions`,children:i})]})}function Td({label:e,actions:t,danger:n=!1,primary:r=!1,disabled:i,onClick:a}){let o=t.blocked??i??null;return(0,J.jsx)(`button`,{type:`button`,className:`button${n?` danger`:r?` primary`:``}`,disabled:o!==null,title:o??void 0,onClick:a,children:e})}function Ed({list:e}){return e.length>0?(0,J.jsx)(J.Fragment,{children:e.map(e=>(0,J.jsx)(`span`,{className:`net-fact-line`,children:e},e))}):(0,J.jsx)(`span`,{className:`net-muted`,children:`None`})}function Dd(e){return/\/netplan-[^/]*$/.test(e)?`From netplan (/etc/netplan)`:e.startsWith(`/run/`)?`In memory: lost at reboot`:null}function Od({state:e,connection:t,now:n,signal:r=!0}){let i=e.devices.find(e=>e.name===t.device),a=t.kind===`wifi`||t.kind===`hotspot`,o=r&&t.kind===`wifi`?e.networks.find(e=>e.ssid===t.ssid):void 0,s=Dd(t.file),c=i?.state===100,l;return l=t.activeState===`activated`?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(ad,{tone:`on`}),` `,t.kind===`hotspot`?`On`:`Connected`,`, on `,t.device]}):t.activeState?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(ad,{tone:`busy`}),` `,i?yu(i,e.wifi).text:t.activeState]}):`Not connected`,(0,J.jsxs)(`dl`,{className:`net-facts`,children:[(0,J.jsx)($,{label:`Status`,children:l}),(0,J.jsx)($,{label:`Kind`,children:Su(t)}),a&&t.ssid&&t.ssid!==t.name&&(0,J.jsx)($,{label:`Network`,children:t.ssid}),a&&t.security&&(0,J.jsx)($,{label:`Security`,children:$l[t.security]}),t.security===`wpa-eap`&&t.identity&&(0,J.jsx)($,{label:`User name`,children:t.identity}),t.kind===`hotspot`&&(0,J.jsxs)($,{label:`Band`,children:[t.band?t.band===`a`?`5 GHz`:`2.4 GHz`:`Automatic`,t.channel>0&&`, channel ${t.channel}`]}),o&&(0,J.jsxs)($,{label:`Signal`,children:[o.signal,`% · `,hu(o.frequency),`, channel `,o.channel]}),t.hidden&&(0,J.jsx)($,{label:`Hidden`,children:`Its name is not shown: looked for by name`}),(t.kind===`wifi`||t.kind===`hotspot`||t.kind===`ethernet`)&&(0,J.jsx)($,{label:`Interface`,children:t.interface||`Any`}),t.ipv4Method&&(0,J.jsx)($,{label:`IPv4`,children:Cd[t.ipv4Method]??t.ipv4Method}),(c||t.addresses.length>0)&&(0,J.jsx)($,{label:`Address`,children:(0,J.jsx)(Ed,{list:c?i.ipv4:t.addresses})}),(c?i.gateway:t.gateway)&&(0,J.jsx)($,{label:`Gateway`,children:c?i.gateway:t.gateway}),(c?i.dns:t.dns).length>0&&(0,J.jsx)($,{label:`DNS`,children:(0,J.jsx)(Ed,{list:c?i.dns:t.dns})}),(0,J.jsx)($,{label:`Automatic`,children:t.autoconnect?`Yes${t.priority===0?``:`, priority ${t.priority}`}`:`No: only when asked`}),(0,J.jsx)($,{label:`Last used`,children:(0,J.jsx)(`span`,{title:t.lastUsed?new Date(t.lastUsed).toLocaleString():void 0,children:Fu(t.lastUsed,n).replace(/^Used (on )?/,``).replace(/^Never used$/,`Never`)})}),t.file&&(0,J.jsxs)($,{label:`File`,children:[(0,J.jsx)(`span`,{className:`net-fact-line`,children:t.file}),s&&(0,J.jsx)(`span`,{className:`net-fact-line net-muted`,children:s})]}),(0,J.jsx)($,{label:`UUID`,children:(0,J.jsx)(`span`,{className:`net-mono`,children:t.uuid})})]})}function kd({connection:e,actions:t}){if(t.readOnly||e.external)return null;let n=e.kind===`hotspot`,r=e.kind===`wifi`||n||e.kind===`ethernet`,i=e.readOnly?`NetworkManager has it read-only`:null;return(0,J.jsxs)(J.Fragment,{children:[e.activeState?(0,J.jsx)(Td,{label:n?`Stop`:`Disconnect`,actions:t,onClick:()=>t.disconnect(e)}):(0,J.jsx)(Td,{label:n?`Start`:`Connect`,primary:!0,actions:t,onClick:()=>t.connect(e,null)}),r&&(0,J.jsx)(Td,{label:`Edit…`,actions:t,disabled:i,onClick:()=>t.edit(e)}),(e.kind===`wifi`||n)&&(0,J.jsx)(Td,{label:`Share…`,actions:t,onClick:()=>t.share(e)}),(0,J.jsx)(Td,{label:`Forget…`,danger:!0,actions:t,disabled:i,onClick:()=>t.forget(e)})]})}function Ad({state:e,connection:t,actions:n,now:r,onClose:i}){let{failed:a}=Mu(e.activity,{uuid:t.uuid},r);return(0,J.jsxs)(wd,{icon:(0,J.jsx)(rd,{kind:t.kind,size:16}),name:t.name,onClose:i,footer:(0,J.jsx)(kd,{connection:t,actions:n}),children:[a&&(0,J.jsx)(`p`,{className:`net-error`,children:`${a.label}: ${a.error}`}),t.external&&(0,J.jsx)(`p`,{className:`net-muted`,children:`NetworkManager made it for an interface another program configures (docker, a VPN client…): it is not changed here.`}),(0,J.jsx)(Od,{state:e,connection:t,now:r})]})}function jd({state:e,network:t,actions:n,now:r,onClose:i}){let a=Ou(e.connections,t.ssid,t.device),{failed:o}=Mu(e.activity,{uuid:a?.uuid??null,ssid:t.ssid,device:t.device},r),s=a?(0,J.jsx)(kd,{connection:a,actions:n}):n.readOnly?null:(0,J.jsx)(Td,{label:`Join…`,primary:!0,actions:n,onClick:()=>n.join(t)});return(0,J.jsxs)(wd,{icon:(0,J.jsx)(nd,{signal:t.signal,size:16}),name:t.ssid,onClose:i,footer:s,children:[o&&(0,J.jsx)(`p`,{className:`net-error`,children:`${o.label}: ${o.error}`}),(0,J.jsxs)(`dl`,{className:`net-facts`,children:[(0,J.jsxs)($,{label:`Signal`,children:[t.signal,`%`]}),(0,J.jsxs)($,{label:`Security`,children:[$l[t.security],t.securityText&&` (${t.securityText})`]}),(0,J.jsxs)($,{label:`Band`,children:[hu(t.frequency),`, channel `,t.channel,` (`,t.frequency,` MHz)`]}),t.rate&&(0,J.jsx)($,{label:`Rate`,children:t.rate}),(0,J.jsxs)($,{label:`Access point`,children:[(0,J.jsx)(`span`,{className:`net-mono`,children:t.bssid}),t.points>1&&(0,J.jsxs)(`span`,{className:`net-fact-line net-muted`,children:[`The strongest of `,t.points]})]}),t.mode!==`Infra`&&(0,J.jsx)($,{label:`Mode`,children:t.mode}),(0,J.jsx)($,{label:`Seen by`,children:t.device})]}),a&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsxs)(`h3`,{children:[`Saved as `,a.name]}),(0,J.jsx)(Od,{state:e,connection:a,now:r,signal:!1})]})]})}function Md({state:e,device:t,actions:n,onClose:r}){let i=yu(t,e.wifi),a=t.connection?e.connections.find(e=>e.uuid===t.connection):void 0,o=t.type===`wifi`?`wifi`:t.type===`ethernet`?`ethernet`:null,s=o?e.connections.filter(n=>!n.activeState&&(n.kind===o||o===`wifi`&&n.kind===`hotspot`)&&Eu(n,[t])&&(n.kind!==`wifi`||e.networks.some(e=>e.device===t.name&&e.ssid===n.ssid))):[],c=[t.band24&&`2.4 GHz`,t.band5&&`5 GHz`].filter(Boolean).join(` and `);return(0,J.jsxs)(wd,{icon:(0,J.jsx)(id,{device:t,size:16}),name:t.name,onClose:r,footer:a&&!t.external&&!n.readOnly?(0,J.jsx)(Td,{label:a.kind===`hotspot`?`Stop the access point`:`Disconnect`,actions:n,onClick:()=>n.disconnect(a)}):null,children:[(0,J.jsxs)(`dl`,{className:`net-facts`,children:[(0,J.jsxs)($,{label:`Status`,children:[(0,J.jsx)(ad,{tone:i.tone}),` `,i.text]}),t.reason&&t.state!==100&&(0,J.jsx)($,{label:`Why`,children:t.reason}),(0,J.jsx)($,{label:`Kind`,children:bu(t)}),(t.vendor||t.product)&&(0,J.jsx)($,{label:`Hardware`,children:[t.vendor,t.product].filter(Boolean).join(` `)}),t.driver&&(0,J.jsx)($,{label:`Driver`,children:t.driver}),t.hwaddr&&(0,J.jsx)($,{label:`MAC address`,children:(0,J.jsx)(`span`,{className:`net-mono`,children:t.hwaddr})}),t.speed&&(0,J.jsx)($,{label:`Speed`,children:t.speed}),t.mtu&&(0,J.jsx)($,{label:`MTU`,children:t.mtu}),t.type===`wifi`&&(0,J.jsx)($,{label:`Access point`,children:t.accessPoint?`It can be one${c?`, on ${c}`:``}`:`Not supported`}),(0,J.jsx)($,{label:`IPv4`,children:(0,J.jsx)(Ed,{list:t.ipv4})}),t.ipv6.length>0&&(0,J.jsx)($,{label:`IPv6`,children:(0,J.jsx)(Ed,{list:t.ipv6})}),t.gateway&&(0,J.jsx)($,{label:`Gateway`,children:t.gateway}),t.dns.length>0&&(0,J.jsx)($,{label:`DNS`,children:(0,J.jsx)(Ed,{list:t.dns})})]}),s.length>0&&!n.readOnly&&!t.external&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`h3`,{children:`Connect with`}),(0,J.jsx)(`ul`,{className:`net-choices`,children:s.map(e=>(0,J.jsxs)(`li`,{children:[(0,J.jsx)(rd,{kind:e.kind,size:15}),(0,J.jsx)(`span`,{className:`net-choice-name`,children:e.name}),(0,J.jsx)(Td,{label:e.kind===`hotspot`?`Start`:`Connect`,actions:n,onClick:()=>n.connect(e,t.name)})]},e.uuid))})]})]})}function Nd({selection:e,state:t,actions:n,now:r,onClose:i}){switch(e.type){case`network`:{let a=t.networks.find(t=>t.device===e.device&&t.ssid===e.ssid);return a?(0,J.jsx)(jd,{state:t,network:a,actions:n,now:r,onClose:i}):null}case`connection`:{let a=t.connections.find(t=>t.uuid===e.uuid);return a?(0,J.jsx)(Ad,{state:t,connection:a,actions:n,now:r,onClose:i}):null}case`device`:{let r=t.devices.find(t=>t.name===e.name);return r?(0,J.jsx)(Md,{state:t,device:r,actions:n,onClose:i}):null}}}function Pd(e,t){return JSON.stringify(e)===JSON.stringify(t)}function Fd({label:e,busy:t,primary:n=!1,actions:r,onClick:i}){return r.readOnly?null:(0,J.jsx)(`button`,{type:`button`,className:`net-action${n?` primary`:``}`,disabled:!!t||r.blocked!==null,title:r.blocked??void 0,onClick:i,children:t??e})}function Id({selected:e,icon:t,name:n,tags:r,sub:i,tone:a,action:o,onSelect:s}){return(0,J.jsxs)(`li`,{className:`net-row${e?` selected`:``}`,children:[(0,J.jsxs)(`button`,{type:`button`,className:`net-row-main`,"aria-pressed":e,onClick:s,children:[(0,J.jsx)(`span`,{className:`net-row-icon ${a}`,children:t}),(0,J.jsxs)(`span`,{className:`net-row-text`,children:[(0,J.jsxs)(`span`,{className:`net-row-name`,children:[(0,J.jsx)(`span`,{className:`net-row-label`,title:n,children:n}),r]}),(0,J.jsxs)(`span`,{className:`net-row-sub ${a}`,title:i,children:[(a===`busy`||a===`failed`)&&(0,J.jsx)(ad,{tone:a}),(0,J.jsx)(`span`,{className:`net-row-sub-text`,children:i})]})]})]}),o&&(0,J.jsx)(`div`,{className:`net-row-actions`,children:o})]})}function Ld({state:e,device:t}){return t&&t===e.via?(0,J.jsx)(`span`,{className:`net-tag accent`,title:`This page reaches the robot through it`,children:`this page`}):null}function Rd(e){return e?.ipv4[0]?.split(`/`)[0]??``}function zd({connection:e,...t}){let{state:n,actions:r,selection:i,onSelect:a,now:o}=t,s=n.devices.find(t=>t.name===e.device),{running:c,failed:l}=Mu(n.activity,{uuid:e.uuid,ssid:e.ssid||null,device:e.device},o),u=e.kind===`hotspot`,d,f=``;c?(d=`${c.label}…`,f=`busy`):e.activeState===`activated`?(d=[u?`On, on ${e.device}`:`Connected on ${e.device}`,Rd(s)].filter(Boolean).join(` · `),f=`on`):e.activeState===`activating`?(d=s?yu(s,n.wifi).text:`Connecting…`,f=`busy`):e.activeState===`deactivating`?(d=`Disconnecting…`,f=`busy`):l?(d=l.error??``,f=`failed`):d=[e.kind===`ethernet`&&e.ipv4Method===`manual`?e.addresses.join(`, `):u&&e.ssid!==e.name?e.ssid:``,Fu(e.lastUsed,o),e.autoconnect?``:`not automatic`].filter(Boolean).join(` · `);let p=e.activeState!==null,m={type:`connection`,uuid:e.uuid},h=n.devices.find(t=>t.name===e.interface),g=h&&n.devices.filter(e=>e.type===h.type).length>1;return(0,J.jsx)(Id,{selected:Pd(i,m),icon:(0,J.jsx)(rd,{kind:e.kind}),name:e.name,tags:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(Ld,{state:n,device:e.device}),g&&(0,J.jsx)(`span`,{className:`net-tag`,children:e.interface})]}),sub:d,tone:f,onSelect:()=>a(m),action:e.external?null:p?(0,J.jsx)(Fd,{label:u?`Stop`:`Disconnect`,busy:c?`…`:null,actions:r,onClick:()=>r.disconnect(e)}):(0,J.jsx)(Fd,{label:u?`Start`:`Connect`,busy:c?`…`:null,primary:!0,actions:r,onClick:()=>r.connect(e,null)})})}function Bd({network:e,saved:t,device:n,...r}){let{state:i,actions:a,selection:o,onSelect:s,now:c}=r,{running:l,failed:u}=Mu(i.activity,{uuid:t?.uuid??null,ssid:e.ssid,device:n.name},c),d=e.active&&n.connection!==null,f=d?i.connections.find(e=>e.uuid===n.connection):void 0,p=hu(e.frequency),m,h=``;if(l)m=`${l.label}…`,h=`busy`;else if(d){let e=yu(n,i.wifi);m=n.state===100?[`Connected`,Rd(n),p].filter(Boolean).join(` · `):e.text,h=e.tone}else u?(m=u.error??``,h=`failed`):m=[t?`Saved`:``,e.securityText||`Open`,p].filter(Boolean).join(` · `);let g={type:`network`,device:n.name,ssid:e.ssid},_;return d&&f?_=(0,J.jsx)(Fd,{label:`Disconnect`,actions:a,onClick:()=>a.disconnect(f)}):d||(_=(0,J.jsx)(Fd,{label:t?`Connect`:`Join`,busy:l?`…`:null,primary:!0,actions:a,onClick:()=>t?a.connect(t,n.name):a.join(e)})),(0,J.jsx)(Id,{selected:Pd(o,g),icon:(0,J.jsx)(nd,{signal:e.signal}),name:e.ssid,tags:(0,J.jsxs)(J.Fragment,{children:[e.security!==`open`&&e.security!==`owe`&&(0,J.jsx)(Oe,{className:`net-lock`,width:12,height:12}),d&&(0,J.jsx)(Ld,{state:i,device:n.name})]}),sub:m,tone:d&&h===``?`on`:h,action:_,onSelect:()=>s(g)})}function Vd({icon:e,label:t,disabled:n,onClick:r}){return(0,J.jsx)(`li`,{className:`net-row`,children:(0,J.jsxs)(`button`,{type:`button`,className:`net-row-main net-add`,disabled:n!==null,title:n??void 0,onClick:r,children:[(0,J.jsx)(`span`,{className:`net-row-icon`,children:e}),(0,J.jsx)(`span`,{className:`net-row-label`,children:t})]})})}function Hd({device:e,many:t,...n}){let{state:r,text:i,actions:a}=n,s=r.connections.find(t=>t.device===e.name&&(t.kind===`wifi`||t.kind===`hotspot`)),c=ku(r,e.name,i),l=s?.kind===`wifi`&&!c.some(e=>e.network.active)?s:null,u=e.accessPoint?Au(r.connections,e.name).filter(e=>e.uuid!==s?.uuid):[],d=null;c.length===0&&(d=i.trim()?`No network matches “${i.trim()}”`:s?.kind===`hotspot`?`An access point does not look for networks: stop it to join one`:r.scanning?`Looking for networks…`:`No network in range`);let f=a.blocked;return(0,J.jsxs)(`section`,{className:`net-section`,"aria-label":`Wi-Fi of ${e.name}`,children:[t&&(0,J.jsxs)(`h3`,{className:`net-heading`,children:[e.name,(e.product||e.vendor)&&(0,J.jsx)(`span`,{className:`net-heading-detail`,children:e.product||e.vendor})]}),(0,J.jsxs)(`ul`,{className:`net-list`,children:[s?.kind===`hotspot`&&(0,J.jsx)(zd,{...n,connection:s}),l&&(0,J.jsx)(zd,{...n,connection:l}),c.map(t=>(0,J.jsx)(Bd,{...n,network:t.network,saved:t.saved,device:e},t.network.ssid))]}),d&&(0,J.jsx)(`p`,{className:`net-note`,children:d}),!i.trim()&&(0,J.jsxs)(`ul`,{className:`net-list net-extra`,children:[u.map(e=>(0,J.jsx)(zd,{...n,connection:e},e.uuid)),!a.readOnly&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(Vd,{icon:(0,J.jsx)(ce,{width:16,height:16}),label:`Join a hidden network…`,disabled:f,onClick:()=>a.create(`wifi`,e.name)}),e.accessPoint&&(0,J.jsx)(Vd,{icon:(0,J.jsx)(o,{width:16,height:16}),label:`New access point…`,disabled:f,onClick:()=>a.create(`hotspot`,e.name)})]})]})]})}function Ud(e){let{state:t,devices:n,actions:r}=e,i=n.filter(e=>e.type===`wifi`);return t.wifi?i.length===0?(0,J.jsxs)(`div`,{className:`net-empty`,children:[(0,J.jsx)(f,{width:28,height:28}),(0,J.jsx)(`p`,{children:`No Wi-Fi interface`})]}):(0,J.jsx)(`div`,{className:`net-scroll`,children:i.map(t=>(0,J.jsx)(Hd,{...e,device:t,many:i.length>1},t.name))}):(0,J.jsxs)(`div`,{className:`net-empty`,children:[(0,J.jsx)(f,{width:28,height:28}),(0,J.jsx)(`p`,{children:t.wifiHardware?`Wi-Fi is off`:`Wi-Fi is off by its hardware switch`}),t.wifiHardware&&!r.readOnly&&(0,J.jsx)(`button`,{type:`button`,className:`button primary`,disabled:r.blocked!==null,title:r.blocked??void 0,onClick:()=>r.setWifi(!0),children:`Turn Wi-Fi on`})]})}function Wd(e){let{state:t,devices:n,text:r,all:i,actions:a}=e,o=Du(t.connections,n,r,i);return(0,J.jsxs)(`div`,{className:`net-scroll`,children:[o.length===0&&(0,J.jsx)(`p`,{className:`net-note`,children:r.trim()?`No connection matches “${r.trim()}”`:`No connection saved`}),o.map(t=>(0,J.jsxs)(`section`,{className:`net-section`,"aria-label":t.title,children:[(0,J.jsxs)(`h3`,{className:`net-heading`,children:[t.title,(0,J.jsx)(`span`,{className:`net-heading-detail`,children:t.connections.length})]}),(0,J.jsx)(`ul`,{className:`net-list`,children:t.connections.map(t=>(0,J.jsx)(zd,{...e,connection:t},t.uuid))})]},t.kind)),!r.trim()&&!a.readOnly&&(0,J.jsx)(`ul`,{className:`net-list net-extra`,children:(0,J.jsx)(Vd,{icon:(0,J.jsx)(ce,{width:16,height:16}),label:`New wired connection…`,disabled:a.blocked,onClick:()=>a.create(`ethernet`,null)})})]})}function Gd(e){let{state:t,devices:n,selection:r,onSelect:i,actions:a}=e;return n.length===0?(0,J.jsx)(`p`,{className:`net-note`,children:`No interface`}):(0,J.jsx)(`div`,{className:`net-scroll`,children:(0,J.jsx)(`ul`,{className:`net-list`,children:n.map(e=>{let n=yu(e,t.wifi),o=e.connection?t.connections.find(t=>t.uuid===e.connection):void 0,s={type:`device`,name:e.name};return(0,J.jsx)(Id,{selected:Pd(r,s),icon:(0,J.jsx)(id,{device:e}),name:e.name,tags:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(Ld,{state:t,device:e.name}),(0,J.jsx)(`span`,{className:`net-row-detail`,children:e.product||e.vendor||bu(e)})]}),sub:[n.text,Rd(e)].filter(Boolean).join(` · `),tone:n.tone,onSelect:()=>i(s),action:o&&!e.external&&e.state===100?(0,J.jsx)(Fd,{label:o.kind===`hotspot`?`Stop`:`Disconnect`,actions:a,onClick:()=>a.disconnect(o)}):null},e.name)})})})}var Kd=620,qd=300,Jd=[{view:`wifi`,label:`Wi-Fi`,title:`The Wi-Fi networks in range`,Icon:O},{view:`saved`,label:`Saved`,title:`The connections saved`,Icon:B},{view:`devices`,label:`Interfaces`,title:`The interfaces, their addresses`,Icon:ue}];function Yd(e){let[t,n]=(0,q.useState)(()=>b.serverNow());return(0,q.useEffect)(()=>{let t=setInterval(()=>n(b.serverNow()),e);return()=>clearInterval(t)},[e]),t}function Xd({config:e}){let t=et(),[n,r]=(0,q.useState)(null),[i,a]=(0,q.useState)(e.view),[o,s]=(0,q.useState)(``),[c,l]=(0,q.useState)(null),[u,d]=(0,q.useState)(null),[p,m]=(0,q.useState)(null),[h,g]=(0,q.useState)({width:0,height:0}),_=(0,q.useRef)(null),v=Yd(5e3);(0,q.useEffect)(()=>a(e.view),[e.view]),(0,q.useEffect)(()=>b.subscribeNetwork(r),[]),(0,q.useEffect)(()=>{let e=_.current;if(!e)return;let t=new ResizeObserver(([e])=>g({width:e.contentRect.width,height:e.contentRect.height}));return t.observe(e),()=>t.disconnect()},[n!==null]);let y=(0,q.useMemo)(()=>gu(e.interfaces),[e.interfaces]),x=(0,q.useMemo)(()=>n?_u(n.devices,y,e.allInterfaces):[],[n,y,e.allInterfaces]),S=x.some(e=>e.type===`wifi`),C=i===`wifi`&&!S&&n&&x.length>0?`saved`:i,w=n?Bu(n,e.readOnly):null,T=(e,t)=>fd(e,t,n?Pu(n,e)!==null:!1).then(()=>null,e=>rs(e)),E=async(e,r)=>{let i=n?Pu(n,e):null;return!i&&!r.always||t.confirm({title:r.title,message:(0,J.jsxs)(J.Fragment,{children:[r.message,i&&(0,J.jsx)(`p`,{className:`net-warning`,children:i})]}),confirmLabel:r.confirmLabel,danger:i!==null||r.danger})},D=e=>e.kind===`wifi`&&e.ssid?e.ssid:e.name,k={blocked:w,readOnly:e.readOnly,connect:async(e,t)=>{let n={action:`connect`,uuid:e.uuid,device:t,password:null},r=e.kind===`hotspot`,i={title:r?`Start ${e.name}?`:`Switch to ${D(e)}?`,confirmLabel:r?`Start`:`Switch`,always:!1};if(!await E(n,i))return;let a=await T(n,r?`The access point ${D(e)} is on`:`Connected to ${D(e)}`);a&&(a===`Wrong password, or none given`&&e.security&&eu(e.security)?d({kind:`password`,connection:e,device:t,error:a}):G(`Could not ${r?`start`:`connect to`} ${D(e)}: ${a}`,`error`))},disconnect:async t=>{let n={action:`disconnect`,uuid:t.uuid},r=t.kind===`hotspot`,i={title:r?`Stop ${t.name}?`:`Disconnect from ${D(t)}?`,message:r?`Whoever joined the robot’s network loses it.`:t.autoconnect?`It does not connect by itself again until asked, or until the robot restarts.`:`It stays saved.`,confirmLabel:r?`Stop`:`Disconnect`,danger:!0,always:e.confirm};if(!await E(n,i))return;let a=await T(n,null);a&&G(`Could not ${r?`stop`:`disconnect from`} ${D(t)}: ${a}`,`error`)},forget:async e=>{let t={action:`delete`,uuid:e.uuid},n={title:`Forget ${e.name}?`,message:e.kind===`wifi`?`Its password is deleted with it: joining the network again asks for it.`:`Its settings are deleted with it.`,confirmLabel:`Forget`,danger:!0,always:!0};if(!await E(t,n))return;c?.type===`connection`&&c.uuid===e.uuid&&l(null);let r=await T(t,`Forgot ${e.name}`);r&&G(`Could not forget ${e.name}: ${r}`,`error`)},join:async e=>{if(eu(e.security)){d({kind:`join`,network:e});return}let t={action:`create`,settings:Lu(e,null,``,!0,!1,n?.connections??[]),connect:!0,device:e.device},r={title:`Join ${e.ssid}?`,message:e.security===`open`?`It is open: others on it may see what the robot sends.`:`It is open, its traffic encrypted (OWE).`,confirmLabel:`Join`,always:e.security===`open`};if(!await E(t,r))return;let i=await T(t,`Connected to ${e.ssid}`);i&&G(`Could not join ${e.ssid}: ${i}`,`error`)},create:(e,t)=>d({kind:`create`,settings:e,device:t}),edit:e=>d({kind:`edit`,connection:e}),share:e=>d({kind:`share`,connection:e}),setWifi:async t=>{let n={action:`wifi`,enabled:t},r={title:t?`Turn Wi-Fi on?`:`Turn Wi-Fi off?`,message:t?``:`Every Wi-Fi interface disconnects, access points stop.`,confirmLabel:t?`Turn on`:`Turn off`,danger:!t,always:!t&&e.confirm};if(!await E(n,r))return;let i=await T(n,null);i&&G(`Could not turn Wi-Fi ${t?`on`:`off`}: ${i}`,`error`)}},A=()=>void b.networkAction({action:`rescan`,device:null}).catch(e=>G(`Could not scan: ${rs(e)}`,`error`)),j=e=>{let t=e.currentTarget.getBoundingClientRect(),r=x.filter(e=>e.type===`wifi`),i=w;m({x:t.right-200,y:t.bottom+4,items:[{label:`Join a hidden network…`,disabled:i!==null||r.length===0||!n?.wifi,run:()=>k.create(`wifi`,null)},{label:`New access point…`,disabled:i!==null||!r.some(e=>e.accessPoint)||!n?.wifi,run:()=>k.create(`hotspot`,null)},{label:`New wired connection…`,disabled:i!==null,run:()=>k.create(`ethernet`,null)}]})};if((0,q.useEffect)(()=>{n&&c&&((c.type===`network`?n.networks.some(e=>e.device===c.device&&e.ssid===c.ssid):c.type===`connection`?n.connections.some(e=>e.uuid===c.uuid):n.devices.some(e=>e.name===c.name))||l(null))},[n,c]),!n)return(0,J.jsx)(`div`,{className:`widget-message`,children:`Reading the networks…`});let M={wifi:n.networks.filter(e=>x.some(t=>t.name===e.device)).length,saved:Du(n.connections,x,``,e.allInterfaces).reduce((e,t)=>e+t.connections.length,0),devices:x.length},N={state:n,devices:x,text:o,all:e.allInterfaces,selection:c,onSelect:e=>l(t=>JSON.stringify(t)===JSON.stringify(e)?null:e),actions:k,now:v},P=zu(n,v),{width:F,height:I}=h,L=F>=Kd,R=c&&(0,J.jsx)(Nd,{selection:c,state:n,actions:k,now:v,onClose:()=>l(null)}),ee;ee=n.error?(0,J.jsx)(`p`,{className:`net-note error`,children:n.error}):C===`wifi`?(0,J.jsx)(Ud,{...N}):C===`saved`?(0,J.jsx)(Wd,{...N}):(0,J.jsx)(Gd,{...N});let te=!e.readOnly&&!n.error&&w?(0,J.jsx)(`p`,{className:`net-banner`,role:`note`,children:w}):null;return(0,J.jsxs)(`div`,{ref:_,className:`net${R&&!L&&I>0&&I<qd?` cramped`:``}`,onKeyDown:e=>{e.key===`Escape`&&c&&l(null)},children:[(0,J.jsxs)(`div`,{className:`net-toolbar`,role:`toolbar`,"aria-label":`Networking`,children:[(0,J.jsx)(`div`,{className:`net-views`,role:`group`,"aria-label":`View`,children:Jd.filter(({view:e})=>e!==`wifi`||S).map(({view:e,label:t,title:n,Icon:r})=>(0,J.jsxs)(`button`,{type:`button`,className:`net-view`,"aria-pressed":C===e,title:n,onClick:()=>{a(e),l(null)},children:[(0,J.jsx)(r,{width:14,height:14}),(0,J.jsx)(`span`,{className:`net-view-name`,children:t}),(0,J.jsx)(`span`,{className:`net-view-count`,children:M[e]})]},e))}),C!==`devices`&&(0,J.jsxs)(`label`,{className:`net-search`,title:`Only those whose name contains`,children:[(0,J.jsx)(ne,{width:14,height:14}),(0,J.jsx)(`input`,{type:`search`,value:o,placeholder:`Filter…`,"aria-label":`Only those whose name contains`,spellCheck:!1,onChange:e=>s(e.target.value)})]}),(0,J.jsxs)(`div`,{className:`net-tools`,children:[C===`wifi`&&(0,J.jsx)(`button`,{type:`button`,className:`overlay-button${n.scanning?` net-spinning`:``}`,title:n.scanning?`Looking for networks…`:`Look for networks again`,"aria-label":`Look for networks again`,disabled:!n.wifi||n.scanning||n.error!==null,onClick:A,children:(0,J.jsx)(xe,{width:17,height:17})}),S&&!e.readOnly&&(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,"aria-pressed":n.wifi,title:n.wifiHardware?w??(n.wifi?`Wi-Fi is on: turn it off`:`Wi-Fi is off: turn it on`):`Wi-Fi is off by its hardware switch`,"aria-label":n.wifi?`Turn Wi-Fi off`:`Turn Wi-Fi on`,disabled:w!==null||!n.wifiHardware||n.error!==null,onClick:()=>k.setWifi(!n.wifi),children:n.wifi?(0,J.jsx)(O,{width:17,height:17}):(0,J.jsx)(f,{width:17,height:17})}),!e.readOnly&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`span`,{className:`overlay-separator`}),(0,J.jsx)(`button`,{type:`button`,className:`overlay-button`,title:w??`A new connection: a hidden network, an access point, a cable’s`,"aria-label":`New connection`,"aria-haspopup":`menu`,disabled:w!==null||n.error!==null,onClick:j,children:(0,J.jsx)(ce,{width:17,height:17})})]})]})]}),te,(0,J.jsxs)(`div`,{className:`net-body${L?` beside`:``}`,children:[(0,J.jsx)(`div`,{className:`net-main`,children:ee}),R]}),(0,J.jsxs)(`div`,{className:`net-status ${P.tone}`,title:P.text,children:[(0,J.jsx)(ad,{tone:P.tone}),(0,J.jsx)(`span`,{className:`net-status-text`,children:P.text})]}),u&&(0,J.jsx)(Sd,{request:u,state:n,onClose:()=>d(null)}),p&&(0,J.jsx)(os,{x:Math.max(4,p.x),y:p.y,items:p.items,onClose:()=>m(null)})]})}var Zd=Ye({type:`networking`,name:`Networking`,description:`The robot’s networks: join Wi-Fi networks, switch between those saved, set addresses, make it an access point.`,icon:ue,defaultSize:{w:6,h:7},minSize:{w:3,h:3},component:Xd,properties:[{key:`view`,label:`View`,kind:`select`,default:`wifi`,options:[{value:`wifi`,label:`Wi-Fi networks in range`},{value:`saved`,label:`Saved connections`},{value:`devices`,label:`Interfaces`}],help:`When the panel opens: its toolbar changes it`},{key:`interfaces`,label:`Interfaces`,kind:`string`,default:``,placeholder:`wlan0, eth*`,help:`Only these, and their connections: names or globs (*), separated by commas. All if empty`},{key:`allInterfaces`,label:`Show every interface`,kind:`boolean`,default:!1,help:`Also those other programs configure (docker’s bridges, VPN clients’ tunnels), the loopback, Wi-Fi P2P`},{key:`confirm`,label:`Ask before disconnecting`,kind:`boolean`,default:!0,help:`And before turning Wi-Fi off. A change that would cut this page off from the robot is always asked about`},{key:`readOnly`,label:`Read-only`,kind:`boolean`,default:!1,help:`The networks are shown, not changed: e.g. on a panel for operators`}]}),Qd=`modulepreload`,$d=function(e){return`/`+e},ef={},tf=function(e){return e.pathname.endsWith(`.css`)},nf=function(e,t,n){if(t in e)return e[t];let r=n();if(!r){e[t]=void 0;return}let i=r.then(()=>{e[t]=void 0},n=>{throw e[t]=void 0,n});return e[t]=i,i},rf=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e,i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?new URL(import.meta.resolve(e)):new URL(e,import.meta.url)}r=o(t.map(t=>{t=$d(t,n);let r=s(t),i=tf(r);return nf(ef,r.href,()=>{if(e===void 0){e={all:new Set,styles:new Set};let t=document.getElementsByTagName(`link`);for(let n=t.length-1;n>=0;n--){let r=t[n];e.all.add(r.href),r.rel===`stylesheet`&&e.styles.add(r.href)}}if((i?e.styles:e.all).has(r.href))return;let t=document.createElement(`link`);if(t.rel=i?`stylesheet`:Qd,i||(t.as=`script`),t.crossOrigin=``,t.href=r.href,a&&t.setAttribute(`nonce`,a),document.head.appendChild(t),i)return new Promise((e,n)=>{t.addEventListener(`load`,e),t.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${r}`)))})})}).filter(e=>e!==void 0))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},af=(0,q.lazy)(()=>rf(()=>import(`./RobotModelView.js`),__vite__mapDeps([0,1,2,3,4])));function of({config:e}){return(0,J.jsx)(q.Suspense,{fallback:(0,J.jsx)(`div`,{className:`widget-message`,children:`Loading the 3D view…`}),children:(0,J.jsx)(af,{config:e})})}var sf=Ye({type:`robot-model`,name:`Robot Model`,description:`The robot in 3D, its moving joints highlighted, and its TF frames.`,icon:Fe,defaultSize:{w:6,h:5},minSize:{w:3,h:3},component:of,properties:[{key:`descriptionTopic`,label:`Robot description topic`,kind:`topic`,default:`/robot_description`,topicTypes:[`std_msgs/msg/String`],help:`The URDF, as robot_state_publisher publishes it. Its package:// meshes come from the robot`},{key:`jointStatesTopic`,label:`Joint states topic`,kind:`topic`,default:`/joint_states`,topicTypes:[`sensor_msgs/msg/JointState`],help:`Where the joints are, and how they move`},{key:`showFrames`,label:`Show the TF frames`,kind:`boolean`,default:!1},{key:`highlightMotion`,label:`Highlight the moving joints`,kind:`boolean`,default:!0,help:`Their links take another colour, with an arrow in the direction of the motion`}]}),cf={x:0,y:0,z:0};function lf(e){return e.x===0&&e.y===0&&e.z===0}function uf(e,t){b.send({type:`twist`,topic:e.topic,stamped:e.stamped,frameId:e.frameId,linear:{x:t.x*e.maxLinear,y:t.y*e.maxLinear},angular:{z:t.z*e.maxAngular}})}function df({config:e,editing:t}){let n=We()===`open`,[r,i]=(0,q.useState)(cf),a=(0,q.useRef)(cf),o=(0,q.useRef)(e);o.current=e;let s=(0,q.useRef)(!1),c=(0,q.useCallback)(e=>{let t={...a.current,...e};a.current=t,i(t),!lf(t)&&!s.current?(s.current=!0,uf(o.current,t)):lf(t)&&s.current&&(s.current=!1,uf(o.current,cf))},[]);(0,q.useEffect)(()=>{let t=setInterval(()=>s.current&&uf(o.current,a.current),1e3/Math.min(50,Math.max(1,e.rate)));return()=>clearInterval(t)},[e.rate]);let{topic:l,stamped:u}=e;(0,q.useEffect)(()=>()=>{s.current&&(s.current=!1,uf({...o.current,topic:l,stamped:u},cf))},[l,u]);let d=t||!n,{maxLinear:f,maxAngular:p}=e;return(0,J.jsxs)(`div`,{className:`teleop`,children:[(0,J.jsx)(`div`,{className:`teleop-pads${e.omnidirectional?` two`:``}`,children:e.omnidirectional?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(Ze,{axes:`xy`,label:`x · y`,disabled:d,onMove:(e,t)=>c({x:t,y:-e})}),(0,J.jsx)(Ze,{axes:`x`,label:`z`,disabled:d,onMove:e=>c({z:-e})})]}):(0,J.jsx)(Ze,{axes:`xy`,label:`x · z`,disabled:d,onMove:(e,t)=>c({x:t,y:0,z:-e})})}),(0,J.jsx)(`div`,{className:`teleop-readout`,children:n||t?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsxs)(`span`,{children:[`x `,(r.x*f).toFixed(3)]}),e.omnidirectional&&(0,J.jsxs)(`span`,{children:[`y `,(r.y*f).toFixed(3)]}),(0,J.jsxs)(`span`,{children:[`z `,(r.z*p).toFixed(3)]}),(0,J.jsx)(`span`,{className:`teleop-topic`,children:e.topic})]}):(0,J.jsx)(`span`,{className:`offline`,children:`Disconnected`})})]})}var ff=Ye({type:`teleop`,name:`Teleop`,description:`Drive the robot with on-screen pads, publishing velocity commands.`,icon:h,defaultSize:{w:6,h:4},minSize:{w:3,h:3},component:df,properties:[{key:`topic`,label:`Topic`,kind:`topic`,default:`cmd_vel`,topicTypes:[`geometry_msgs/msg/Twist`,`geometry_msgs/msg/TwistStamped`],help:`With twist_mux, one of its inputs, above autonomous navigation and below the joysticks`},{key:`omnidirectional`,label:`Omnidirectional`,kind:`boolean`,default:!1,help:`Two pads: x and y, and z. Otherwise one: x and z`},{key:`stamped`,label:`TwistStamped`,kind:`boolean`,default:!1,help:`Publish geometry_msgs/TwistStamped instead of Twist`},{key:`frameId`,label:`Frame id`,kind:`string`,default:`base_link`,visible:e=>e.stamped},{key:`maxLinear`,label:`Max linear speed (m/s)`,kind:`number`,default:.2,min:0,step:.01},{key:`maxAngular`,label:`Max angular speed (rad/s)`,kind:`number`,default:.5,min:0,step:.01},{key:`rate`,label:`Rate (Hz)`,kind:`number`,default:10,min:1,max:50,step:1,help:`How often the command is published while a pad is held`}]}),pf=(0,q.lazy)(()=>rf(()=>import(`./TerminalView.js`),__vite__mapDeps([5,1,2,3,4,6,7,8])));function mf({config:e,instance:t,panelId:n,editing:r}){return(0,J.jsx)(q.Suspense,{fallback:(0,J.jsx)(`div`,{className:`widget-message`,children:`Loading the terminal…`}),children:(0,J.jsx)(pf,{config:e,panelId:n,widgetId:t.id,editing:r})})}var hf=Ye({type:`terminal`,name:`Terminal`,description:`A shell on the robot, or a command. On touch screens, the keys phones lack: Esc, Tab, Ctrl, Alt, arrows.`,icon:Ie,defaultSize:{w:8,h:6},minSize:{w:3,h:3},component:mf,properties:[{key:`command`,label:`Command`,kind:`text`,default:``,placeholder:`htop`,help:`Run instead of a shell, by the login shell: e.g. htop, or tmux new -A -s robot for one session that every page shares. Empty: the shell`},{key:`folder`,label:`Folder`,kind:`string`,default:`~`,placeholder:`~/ros2_ws`,help:`Where it starts: /…, ~/… or package://<package>/…`},{key:`fontSize`,label:`Text size`,kind:`number`,default:13,min:7,max:32,step:1,help:`In pixels. Each device changes it with the toolbar, or by pinching`},{key:`theme`,label:`Colours`,kind:`select`,default:`auto`,options:[{value:`auto`,label:`The page’s, dark or light`},{value:`dark`,label:`Dark`},{value:`light`,label:`Light`}]},{key:`extraKeys`,label:`Extra keys`,kind:`select`,default:`touch`,options:[{value:`touch`,label:`On touch screens`},{value:`always`,label:`Always`},{value:`never`,label:`Never`}],help:`Keys the on-screen keyboard lacks, under the terminal. Ctrl, Alt and Shift apply to the next key, of the row or of the keyboard: tap twice, or hold, to keep them. Swiping up on a key types what its corner shows`},{key:`keys`,label:`Extra keys’ layout`,kind:`text`,default:nt,help:`A line per row, keys separated by spaces: ESC TAB CTRL ALT SHIFT HOME END PGUP PGDN UP DOWN LEFT RIGHT INS DEL BKSP ENTER SPACE F1…F12 KEYBOARD PASTE, or what to type (~, |, ros2). What swiping up types follows a colon: -:|`,visible:e=>e.extraKeys!==`never`},{key:`fullScreenTyping`,label:`Full screen while typing`,kind:`boolean`,default:!0,help:`On touch screens: while the on-screen keyboard is open, the terminal fills the screen above it, the extra keys right above it`}]});function gf(e,t){return typeof t==typeof e.default&&(e.kind!==`select`||!!e.useOptions||e.options.some(e=>e.value===t))&&(typeof t!=`number`||Number.isFinite(t))}var _f=class{registered;defaults=new Map;current;constructor(e){this.registered=e.map(e=>({definition:e,plugin:null})),this.current=this.registered}registerPlugin(e,t){let n=[];for(let r of t.widgets??[]){let t=this.registered.find(e=>e.definition.type===r.type);t?n.push(`its widget type "${r.type}" is already ${t.plugin?`${t.plugin}'s`:`Tassello's`}`):this.registered=[...this.registered,{definition:r,plugin:e}]}for(let[e,r]of Object.entries(t.defaults??{})){let t=this.defaults.get(e)??new Map;for(let[i,a]of Object.entries(r)){t.set(i,a);let r=this.registered.find(t=>t.definition.type===e)?.definition,o=r?.properties.find(e=>e.key===i);r&&(!o||!gf(o,a))&&n.push(`its default of ${e}.${i} is not one of the property's values`)}this.defaults.set(e,t)}return this.current=this.registered.map(e=>({...e,definition:this.withDefaults(e.definition)})),n}entries(){return this.current}definition(e){return this.current.find(t=>t.definition.type===e)?.definition}withDefaults(e){let t=this.defaults.get(e.type);return t?{...e,properties:e.properties.map(e=>t.has(e.key)&&gf(e,t.get(e.key))?{...e,default:t.get(e.key)}:e)}:e}};function vf(e,t){let n={};for(let r of e.properties){let e=t[r.key];n[r.key]=gf(r,e)?e:r.default}return n}var yf=new _f([ff,Ql,sf,vt,Wi,pl,zi,lt,Ni,Si,Al,eo,dl,hf,Zd]);function bf(e,t){return yf.registerPlugin(e,t)}function xf(){return yf.entries()}function Sf(e){return yf.definition(e)}function Cf(e,t){return e.x<t.x+t.w&&t.x<e.x+e.w&&e.y<t.y+t.h&&t.y<e.y+e.h}function wf(e,t,n){return e.x>=0&&e.y>=0&&!t.some(t=>t.id!==n&&Cf(e,t))}function Tf(e){return{cols:Math.max(0,...e.map(e=>e.x+e.w)),rows:Math.max(0,...e.map(e=>e.y+e.h))}}function Ef(e,t,n){let{rows:r}=Tf(e);for(let i=0;i<=r;i++)for(let r=0;r+t.w<=Math.max(n,t.w);r++){let n={x:r,y:i,...t};if(wf(n,e))return n}return{x:0,y:r,...t}}function Df(e,t=64){return{left:e.x*t+4,top:e.y*t+4,width:e.w*t-8,height:e.h*t-8}}var Of={desktop:{label:`Desktop`,columns:null,previewWidth:null},tablet:{label:`Tablet`,columns:12,previewWidth:820},phone:{label:`Phone`,columns:6,previewWidth:390}};function kf(e){return e<640?`phone`:e<1100?`tablet`:`desktop`}function Af(e,t){return e.x<t.x+t.w&&t.x<e.x+e.w&&e.y<t.y+t.h&&t.y<e.y+e.h}function jf(e,t,n,r){if(e.x<0||e.y<0||n!==null&&e.x+e.w>n)return!1;for(let[n,i]of t.rects)if(n!==r&&Af(e,i))return!1;return!0}function Mf(e,t,n,r,i){for(let a=i;;a++)for(let i=0;i+t<=r;i++){let r={x:i,y:a,w:t,h:n};if(![...e.values()].some(e=>Af(r,e)))return r}}function Nf(e,t,n){let r=Of[t].columns;if(r===null)return{rects:new Map(e.widgets.map(e=>[e.id,{x:e.x,y:e.y,w:e.w,h:e.h}])),hidden:[],custom:!0};let i=e.layouts?.[t],a=new Set(i?.hidden??[]),o=new Map;for(let t of e.widgets){let e=i?.rects[t.id];if(e&&!a.has(t.id)){let n=Math.min(e.w,r);o.set(t.id,{x:Math.min(e.x,r-n),y:e.y,w:n,h:e.h})}}let s=Math.max(0,...[...o.values()].map(e=>e.y+e.h)),c=e.widgets.filter(e=>!a.has(e.id)&&!o.has(e.id)).sort((e,t)=>e.y-t.y||e.x-t.x);for(let e of c){let t=Mf(o,Math.min(e.w,r),e.w>r?Math.max(n(e.type).h,Math.round(e.h*r/e.w)):e.h,r,s);o.set(e.id,t),s=t.y}return{rects:o,hidden:[...a],custom:Object.keys(i?.rects??{}).length>0}}function Pf(e,t,n){return{...e,layouts:{...e.layouts,[t]:n}}}function Ff(e,t,n,r,i){if(t===`desktop`)return{...e,widgets:e.widgets.map(e=>e.id===r?{...e,...i}:e)};let a=Object.fromEntries(n.rects);return a[r]=i,Pf(e,t,{rects:a,hidden:n.hidden})}function If(e,t,n,r,i){return Pf(e,t,{rects:n.custom?Object.fromEntries([...n.rects].filter(([e])=>e!==r)):{},hidden:i?[...new Set([...n.hidden,r])]:n.hidden.filter(e=>e!==r)})}function Lf(e,t){let{[t]:n,...r}=e.layouts??{},{layouts:i,...a}=e;return Object.keys(r).length>0?{...a,layouts:r}:a}var Rf=4,zf=4,Bf=100,Vf=32;function Hf(e){let[t,n]=(0,q.useState)({width:0,height:0});return(0,q.useEffect)(()=>{let t=e.current;if(!t)return;let r=new ResizeObserver(()=>n({width:t.clientWidth,height:t.clientHeight}));return r.observe(t),()=>r.disconnect()},[e]),t}function Uf({panel:e,device:t,layout:n,previewWidth:r,editing:i,selectedId:a,onSelect:o,onPlace:s,onRemove:c,onHide:l,onVisibleColumns:u}){let d=(0,q.useRef)(null),f=Hf(d),[p,m]=(0,q.useState)(null),h=Of[t].columns,g=(r??f.width)-32,_=h===null?64:Math.max(Vf,Math.floor(g/h)),v=h??Math.max(1,Math.floor(g/_)),y=Math.max(1,Math.floor((f.height-32)/_));(0,q.useEffect)(()=>u?.(v),[v,u]);let b=[...n.rects.values()],x=Tf(p?[...b,p.rect]:b),S=h??(i?Math.max(x.cols+Rf,v):x.cols),C=i?Math.max(x.rows+Rf,y):x.rows,w=(e,t,r)=>{let a=n.rects.get(t.id);if(!i||e.button!==0||p||!a)return;e.stopPropagation(),e.currentTarget.setPointerCapture(e.pointerId);let s=d.current;m({id:t.id,kind:r,pointerId:e.pointerId,startX:e.clientX,startY:e.clientY,startScrollLeft:s.scrollLeft,startScrollTop:s.scrollTop,origin:a,rect:a,dx:0,dy:0,moved:!1}),o(t.id)},T=t=>{if(!p||t.pointerId!==p.pointerId)return;let n=d.current,r=t.clientX-p.startX+n.scrollLeft-p.startScrollLeft,i=t.clientY-p.startY+n.scrollTop-p.startScrollTop,{origin:a}=p,o;if(p.kind===`move`){let e=Math.max(0,a.x+Math.round(r/_));o={...a,x:h===null?e:Math.min(h-a.w,e),y:Math.max(0,a.y+Math.round(i/_))}}else{let t=Sf(e.widgets.find(e=>e.id===p.id)?.type??``)?.minSize,n=h===null?Bf:h-a.x;o={...a,w:Math.min(n,Math.max(Math.min(t?.w??1,n),a.w+Math.round(r/_))),h:Math.min(Bf,Math.max(t?.h??1,a.h+Math.round(i/_)))}}m({...p,dx:r,dy:i,rect:o,moved:p.moved||Math.hypot(r,i)>zf})},D=e=>{if(!p||e.pointerId!==p.pointerId)return;let{id:t,rect:r,origin:i,moved:a}=p;m(null);let o=r.x!==i.x||r.y!==i.y||r.w!==i.w||r.h!==i.h;a&&o&&jf(r,n,h,t)&&s(t,r)},O=!p||jf(p.rect,n,h,p.id);return(0,J.jsx)(`div`,{ref:d,className:`board-scroll${h===null?``:` columns`}`,onPointerDown:e=>{i&&!e.target.closest(`.widget`)&&o(null)},children:(0,J.jsxs)(`div`,{className:`board${i?` editing`:``}${r?` preview`:``}`,style:{width:S*_,height:C*_,"--cell":`${_}px`},children:[p?.moved&&(0,J.jsx)(`div`,{className:`drop-target${O?``:` invalid`}`,style:Df(p.rect,_)}),e.widgets.map(r=>{let o=n.rects.get(r.id);if(!o)return null;let s=p?.id===r.id&&p.moved?p:null,u=Df(o,_);s?.kind===`move`?u.transform=`translate(${s.dx}px, ${s.dy}px)`:s?.kind===`resize`&&(u.width=Math.max(_-8,o.w*_-8+s.dx),u.height=Math.max(_-8,o.h*_-8+s.dy));let d=Sf(r.type),f=d?.icon??Pe;return(0,J.jsxs)(`div`,{className:[`widget`,i&&`editing`,a===r.id&&`selected`,s&&`dragging`].filter(Boolean).join(` `),style:u,onPointerDown:e=>w(e,r,`move`),onPointerMove:T,onPointerUp:D,onPointerCancel:D,children:[(0,J.jsx)(Wf,{instance:r,panelId:e.id,editing:i}),i&&(0,J.jsxs)(`div`,{className:`widget-chrome`,children:[(0,J.jsxs)(`span`,{className:`widget-tag`,children:[(0,J.jsx)(f,{width:14,height:14}),d?.name??r.type]}),(0,J.jsxs)(`div`,{className:`widget-buttons`,children:[l&&(0,J.jsx)(`button`,{type:`button`,className:`widget-button`,title:`Hide on ${Of[t].label.toLowerCase()}s`,"aria-label":`Hide on ${Of[t].label.toLowerCase()}s`,onPointerDown:e=>e.stopPropagation(),onClick:()=>l(r.id),children:(0,J.jsx)(P,{width:14,height:14})}),(0,J.jsx)(`button`,{type:`button`,className:`widget-button remove`,title:`Remove widget`,"aria-label":`Remove widget`,onPointerDown:e=>e.stopPropagation(),onClick:()=>c(r.id),children:(0,J.jsx)(E,{width:14,height:14})})]}),(0,J.jsx)(`div`,{className:`resize-handle`,title:`Resize`,onPointerDown:e=>w(e,r,`resize`)})]})]},r.id)})]})})}var Wf=(0,q.memo)(function({instance:e,panelId:t,editing:n}){let r=Sf(e.type),i=(0,q.useMemo)(()=>r?vf(r,e.config):{},[r,e.config]);return(0,J.jsxs)(`div`,{className:`widget-card`,children:[e.title&&(0,J.jsx)(`div`,{className:`widget-title`,children:e.title}),(0,J.jsx)(`div`,{className:`widget-body`,children:r?(0,J.jsx)(Gf,{resetKey:i,children:(0,J.jsx)(r.component,{config:i,instance:e,panelId:t,editing:n})}):(0,J.jsxs)(`div`,{className:`widget-message`,children:[`Unknown widget type “`,e.type,`”: is the plugin that adds it installed?`]})})]})}),Gf=class extends q.Component{state={error:null};static getDerivedStateFromError(e){return{error:e}}componentDidUpdate(e){this.state.error&&e.resetKey!==this.props.resetKey&&this.setState({error:null})}render(){return this.state.error?(0,J.jsxs)(`div`,{className:`widget-message error`,children:[`This widget failed: `,this.state.error.message]}):this.props.children}};function Kf({widget:e,onChange:t,onDuplicate:n,onRemove:r,onClose:i}){let a=Sf(e.type),o=a?vf(a,e.config):e.config,s=a?.icon??Pe,c=(n,r)=>t({...e,config:{...e.config,[n]:r}});return(0,J.jsxs)(`aside`,{className:`properties`,"aria-label":`Widget properties`,children:[(0,J.jsxs)(`header`,{className:`properties-header`,children:[(0,J.jsx)(s,{}),(0,J.jsx)(`h2`,{children:a?.name??e.type}),(0,J.jsx)(`button`,{type:`button`,className:`icon-button`,"aria-label":`Close`,onClick:i,children:(0,J.jsx)(H,{})})]}),(0,J.jsxs)(`div`,{className:`properties-body`,children:[(0,J.jsx)(qf,{label:`Title`,help:`Shown above the widget, if set`,children:n=>(0,J.jsx)(Yf,{id:n,value:e.title,onCommit:n=>t({...e,title:n})})}),a?.properties.filter(e=>!e.visible||e.visible(o)).map(e=>(0,J.jsx)(Jf,{property:e,value:o[e.key],onChange:t=>c(e.key,t)},e.key)),(0,J.jsxs)(`p`,{className:`properties-meta`,children:[e.w,`×`,e.h,` cells at column `,e.x+1,`, row `,e.y+1]})]}),(0,J.jsxs)(`footer`,{className:`properties-footer`,children:[(0,J.jsxs)(`button`,{type:`button`,className:`button`,onClick:n,children:[(0,J.jsx)(z,{}),` Duplicate`]}),(0,J.jsxs)(`button`,{type:`button`,className:`button danger`,onClick:r,children:[(0,J.jsx)(E,{}),` Remove`]})]})]})}function qf({label:e,help:t,children:n}){let r=(0,q.useId)();return(0,J.jsxs)(`div`,{className:`field`,children:[(0,J.jsx)(`label`,{className:`field-label`,htmlFor:r,children:e}),n(r),t&&(0,J.jsx)(`p`,{className:`field-help`,children:t})]})}function Jf({property:e,value:t,onChange:n}){return e.kind===`boolean`?(0,J.jsxs)(`div`,{className:`field`,children:[(0,J.jsxs)(`label`,{className:`toggle`,children:[(0,J.jsx)(`input`,{type:`checkbox`,checked:t===!0,onChange:e=>n(e.target.checked)}),(0,J.jsx)(`span`,{className:`toggle-switch`,"aria-hidden":`true`}),(0,J.jsx)(`span`,{className:`field-label`,children:e.label})]}),e.help&&(0,J.jsx)(`p`,{className:`field-help`,children:e.help})]}):(0,J.jsx)(qf,{label:e.label,help:e.help,children:r=>{switch(e.kind){case`number`:return(0,J.jsx)(Yf,{id:r,type:`number`,min:e.min,max:e.max,step:e.step??`any`,value:String(t),onCommit:t=>{let r=Number(t);t.trim()&&Number.isFinite(r)&&n(Math.min(e.max??1/0,Math.max(e.min??-1/0,r)))}});case`select`:return e.useOptions?(0,J.jsx)(Xf,{id:r,value:String(t),options:e.options,useOptions:e.useOptions,onChange:n}):(0,J.jsx)(`select`,{id:r,value:String(t),onChange:e=>n(e.target.value),children:e.options.map(e=>(0,J.jsx)(`option`,{value:e.value,children:e.label},e.value))});case`color`:return(0,J.jsxs)(`div`,{className:`color-field`,children:[(0,J.jsx)(`input`,{id:r,type:`color`,value:String(t),onChange:e=>n(e.target.value)}),(0,J.jsx)(`code`,{children:String(t)})]});case`text`:return(0,J.jsx)(Yf,{id:r,multiline:!0,value:String(t),placeholder:e.placeholder,onCommit:n});case`image`:case`behavior-tree`:return(0,J.jsx)(Mt,{id:r,kind:e.kind,value:String(t),placeholder:e.placeholder,onCommit:n,children:e=>(0,J.jsx)(Yf,{...e})});case`topic`:return(0,J.jsx)(Zf,{id:r,value:String(t),types:e.topicTypes,placeholder:e.placeholder,onCommit:n});default:return(0,J.jsx)(Yf,{id:r,value:String(t),placeholder:e.placeholder,onCommit:n})}}})}function Yf({value:e,onCommit:t,multiline:n=!1,...r}){let[i,a]=(0,q.useState)(e);(0,q.useEffect)(()=>a(e),[e]);let o=()=>i!==e&&t(i),s={...r,value:i,spellCheck:!1,onBlur:o,onChange:e=>a(e.target.value)};return n?(0,J.jsx)(`textarea`,{...s,rows:4,onKeyDown:t=>{t.key===`Enter`&&(t.ctrlKey||t.metaKey)?o():t.key===`Escape`&&a(e)}}):(0,J.jsx)(`input`,{...s,onKeyDown:t=>{t.key===`Enter`?o():t.key===`Escape`&&a(e)}})}function Xf({id:e,value:t,options:n,useOptions:r,onChange:i}){let a=r(),o=[...n,...a.filter(e=>!n.some(t=>t.value===e.value))];return(0,J.jsxs)(`select`,{id:e,value:t,onChange:e=>i(e.target.value),children:[!o.some(e=>e.value===t)&&(0,J.jsx)(`option`,{value:t,children:t}),o.map(e=>(0,J.jsx)(`option`,{value:e.value,children:e.label},e.value))]})}function Zf({id:e,value:t,types:n,placeholder:r,onCommit:i}){let[a,o]=(0,q.useState)([]),s=`${e}-topics`,c=()=>Xe.listTopics().then(o).catch(()=>o([])),l=a.filter(e=>e.types.some(e=>n.includes(e)));return(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(Yf,{id:e,value:t,placeholder:r,list:s,onFocus:c,onCommit:i}),(0,J.jsx)(`datalist`,{id:s,children:l.map(e=>(0,J.jsx)(`option`,{value:e.name,children:e.types.join(`, `)},e.name))})]})}function Qf(){let e=new Map;for(let t of xf())e.set(t.plugin,[...e.get(t.plugin)??[],t]);return[...e].map(([e,t])=>({plugin:e,entries:t}))}function $f({onAdd:e,onClose:t}){let n=Qf();return(0,J.jsx)($e,{title:`Add a widget`,onClose:t,wide:!0,children:(0,J.jsx)(`div`,{className:`palette-scroll`,children:n.map(({plugin:t,entries:r})=>(0,J.jsxs)(`section`,{children:[n.length>1&&(0,J.jsx)(`h3`,{className:`palette-heading`,children:t??`Tassello`}),(0,J.jsx)(`div`,{className:`palette`,children:r.map(({definition:t})=>(0,J.jsxs)(`button`,{type:`button`,className:`palette-item`,onClick:()=>e(t),children:[(0,J.jsx)(`span`,{className:`palette-icon`,children:(0,J.jsx)(t.icon,{width:24,height:24})}),(0,J.jsxs)(`span`,{className:`palette-text`,children:[(0,J.jsx)(`strong`,{children:t.name}),(0,J.jsx)(`span`,{children:t.description})]})]},t.type))})]},t??``))})})}var ep=500,tp=18,np={desktop:Ve,tablet:l,phone:ie},rp=e=>Sf(e)?.minSize??{w:1,h:1};function ip(){let[e,t]=(0,q.useState)(()=>kf(window.innerWidth));return(0,q.useEffect)(()=>{let e=()=>t(kf(window.innerWidth));return window.addEventListener(`resize`,e),()=>window.removeEventListener(`resize`,e)},[]),e}function ap(){return decodeURIComponent(location.hash.replace(/^#\/?/,``))}function op(e,t=!1){let n=`#/${encodeURIComponent(e)}`;t?(history.replaceState(null,``,n),window.dispatchEvent(new HashChangeEvent(`hashchange`))):location.hash=n}function sp(e){return`${e.replace(/[^A-Za-z0-9_-]+/g,`-`).slice(-48)}-${Math.random().toString(36).slice(2,8)}`}function cp(e){return e instanceof Error?e.message:String(e)}function lp(){let[e,t]=(0,q.useState)(`saved`),n=(0,q.useRef)(null),r=(0,q.useRef)(null),i=(0,q.useRef)(Promise.resolve()),a=(0,q.useCallback)(()=>{r.current&&=(clearTimeout(r.current),null);let e=n.current;return n.current=null,e&&(i.current=i.current.then(()=>Xe.savePanel(e).then(()=>{n.current||t(`saved`)},n=>{t(`failed`),G(`Could not save “${e.name}”: ${cp(n)}`,`error`)}))),i.current},[]),o=(0,q.useCallback)(e=>{n.current=e,t(`saving`),r.current&&clearTimeout(r.current),r.current=setTimeout(a,ep)},[a]);return(0,q.useEffect)(()=>{let e=()=>{let e=n.current;e&&(n.current=null,fetch(`/api/panels/${encodeURIComponent(e.id)}`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e),keepalive:!0}))};return window.addEventListener(`pagehide`,e),()=>window.removeEventListener(`pagehide`,e)},[]),[e,(0,q.useMemo)(()=>({schedule:o,flush:a,isPending:()=>n.current!==null}),[o,a])]}function up(){let e=We(),t=et(),[n,r]=lp(),[i,a]=(0,q.useState)(null),[o,s]=(0,q.useState)(ap),c=(0,q.useRef)(o);c.current=o;let[l,u]=(0,q.useState)(null),d=(0,q.useRef)(null),[f,p]=(0,q.useState)(null),[m,h]=(0,q.useState)(`view`),[g,_]=(0,q.useState)(null),[v,y]=(0,q.useState)(!1),[x,S]=(0,q.useState)(!1),C=(0,q.useRef)([]),[w,T]=(0,q.useState)(!1),E=(0,q.useRef)(12),D=(0,q.useCallback)(e=>{E.current=e},[]),O=m===`edit`,A=ip(),[j,M]=(0,q.useState)(null),N=O?j??A:A,P=(0,q.useRef)(N);P.current=N;let F=(0,q.useMemo)(()=>l?Nf(l,N,rp):null,[l,N]),I=(0,q.useCallback)(e=>{d.current=e,u(e)},[]),L=(0,q.useCallback)(async()=>{try{a(await Xe.listPanels())}catch(e){G(`Could not list the panels: ${cp(e)}`,`error`)}},[]),R=(0,q.useCallback)(e=>{let t=d.current;if(!t)return;let n=e(t);C.current=[...C.current.slice(-49),t],T(!0),I(n),r.schedule(n)},[r,I]),ee=(0,q.useCallback)(()=>{let e=C.current.pop();T(C.current.length>0),e&&(I(e),r.schedule(e),_(t=>t&&e.widgets.some(e=>e.id===t)?t:null))},[r,I]);(0,q.useEffect)(()=>{let e=()=>s(ap());return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),(0,q.useEffect)(()=>{e!==`closed`&&L()},[L,e]),(0,q.useEffect)(()=>{i&&i.length>0&&!i.some(e=>e.id===o)&&op(i[0].id,!0)},[i,o]);let te=(0,q.useCallback)(async e=>{try{let t=await Xe.getPanel(e);e===c.current&&(I(t),p(null))}catch(t){e===c.current&&p(cp(t))}},[I]),z=i?.some(e=>e.id===o)??!1;(0,q.useEffect)(()=>{if(!z)return;let e=!0;return r.flush().then(()=>{e&&(C.current=[],T(!1),_(null),te(o))}),()=>{e=!1}},[o,z,te,r]),(0,q.useEffect)(()=>b.onServerMessage(e=>{e.type===`error`&&G(e.message,`error`),e.type===`panels.changed`&&e.origin!==b.clientId&&(L(),e.id===d.current?.id&&!e.deleted&&!r.isPending()&&te(e.id))}),[L,te,r]);let ne=async e=>{e===`view`&&(_(null),M(null),await r.flush()),h(e),S(!1)},re=e=>{y(!1);let t=d.current;if(!t)return;let n=P.current===`desktop`?E.current:tp,r=Ef(t.widgets,e.defaultSize,n),i={id:sp(e.type),type:e.type,title:``,...r,config:{}};R(e=>({...e,widgets:[...e.widgets,i]})),_(i.id)},ie=(0,q.useCallback)((e,t)=>R(n=>Ff(n,P.current,Nf(n,P.current,rp),e,t)),[R]),ae=(0,q.useCallback)((e,t=!0)=>{let n=P.current;n!==`desktop`&&(R(r=>If(r,n,Nf(r,n,rp),e,t)),_(t=>t===e?null:t))},[R]),oe=(0,q.useCallback)(e=>{let t=d.current?.widgets.find(t=>t.id===e);if(R(t=>({...t,widgets:t.widgets.filter(t=>t.id!==e)})),_(t=>t===e?null:t),t){let e=t.title||Sf(t.type)?.name||t.type;G(`Removed “${e}”`,`info`,{label:`Undo`,run:ee})}},[R,ee]),se=e=>{let t=d.current,n=t?.widgets.find(t=>t.id===e);if(!t||!n)return;let r=P.current===`desktop`?E.current:tp,i=Ef(t.widgets,{w:n.w,h:n.h},r),a={...n,...i,id:sp(n.type),config:structuredClone(n.config)};R(e=>({...e,widgets:[...e.widgets,a]})),_(a.id)},B=async()=>{S(!1);let e=await t.prompt({title:`New panel`,label:`Name`,confirmLabel:`Create`});if(e)try{let t=await Xe.createPanel({name:e,widgets:[]});await L(),op(t.id),h(`edit`)}catch(e){G(`Could not create the panel: ${cp(e)}`,`error`)}},le=async()=>{S(!1);let e=d.current;if(!e)return;let n=await t.prompt({title:`Rename panel`,label:`Name`,initial:e.name,confirmLabel:`Rename`});n&&n!==e.name&&(R(e=>({...e,name:n})),await r.flush(),await L())},ue=async()=>{S(!1);let e=d.current;if(e)try{await r.flush();let t=await Xe.createPanel({name:`${e.name} (copy)`,widgets:e.widgets});await L(),op(t.id)}catch(e){G(`Could not duplicate the panel: ${cp(e)}`,`error`)}},de=async()=>{S(!1);let e=d.current;if(e&&await t.confirm({title:`Delete “${e.name}”?`,message:`The panel and its widgets are deleted for good.`,confirmLabel:`Delete`,danger:!0}))try{await r.flush(),await Xe.deletePanel(e.id),I(null),await L()}catch(e){G(`Could not delete the panel: ${cp(e)}`,`error`)}};(0,q.useEffect)(()=>{if(!O)return;let e=e=>{if(e.target.closest(`input, textarea, select, [role="dialog"]`))return;if(e.key===`z`&&(e.ctrlKey||e.metaKey)&&!e.shiftKey){e.preventDefault(),ee();return}let t=d.current?.widgets.find(e=>e.id===g);if(!t)return;if(e.key===`Delete`||e.key===`Backspace`){e.preventDefault(),oe(t.id);return}let n={ArrowLeft:[-1,0],ArrowRight:[1,0],ArrowUp:[0,-1],ArrowDown:[0,1]}[e.key],r=Nf(d.current,P.current,rp),i=r.rects.get(t.id);if(n&&i){e.preventDefault();let a={...i,x:i.x+n[0],y:i.y+n[1]};jf(a,r,Of[P.current].columns,t.id)&&ie(t.id,a)}};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[O,g,ee,oe,ie]);let V=O?l?.widgets.find(e=>e.id===g):void 0,fe={connecting:`Connecting…`,open:`Connected to ${b.hostname}`,closed:`Disconnected, reconnecting…`}[e];return(0,J.jsxs)(`div`,{className:`app${O?` editing`:``}`,children:[(0,J.jsxs)(`header`,{className:`topbar`,children:[(0,J.jsxs)(`div`,{className:`brand`,title:fe,children:[(0,J.jsx)(Re,{width:22,height:22}),(0,J.jsx)(`span`,{className:`brand-name`,children:b.hostname||`Tassello`}),(0,J.jsx)(`span`,{className:`connection-dot ${e}`,"aria-label":fe})]}),i&&i.length>0&&(0,J.jsx)(`select`,{className:`panel-select`,"aria-label":`Panel`,value:o,onChange:e=>op(e.target.value),children:i.map(e=>(0,J.jsx)(`option`,{value:e.id,children:e.name},e.id))}),(0,J.jsx)(`div`,{className:`topbar-spacer`}),O&&l&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`span`,{className:`save-state ${n}`,children:{saved:`Saved`,saving:`Saving…`,failed:`Not saved`}[n]}),(0,J.jsx)(`button`,{type:`button`,className:`icon-button`,title:`Undo (Ctrl+Z)`,"aria-label":`Undo`,disabled:!w,onClick:ee,children:(0,J.jsx)(_e,{})}),(0,J.jsxs)(`button`,{type:`button`,className:`button primary`,"aria-label":`Add widget`,onClick:()=>y(!0),children:[(0,J.jsx)(ce,{}),` `,(0,J.jsx)(`span`,{className:`hide-narrow`,children:`Add widget`})]})]}),O&&(0,J.jsx)(`div`,{className:`mode-switch device-switch`,role:`group`,"aria-label":`Layout of`,children:Object.keys(Of).map(e=>{let t=np[e];return(0,J.jsx)(`button`,{type:`button`,"aria-pressed":N===e,title:`The ${Of[e].label.toLowerCase()} layout`,"aria-label":`${Of[e].label} layout`,onClick:()=>{M(e),_(null)},children:(0,J.jsx)(t,{})},e)})}),O&&(0,J.jsxs)(`div`,{className:`menu`,children:[(0,J.jsx)(`button`,{type:`button`,className:`icon-button`,"aria-label":`Panel actions`,"aria-expanded":x,onClick:()=>S(e=>!e),children:(0,J.jsx)(k,{})}),x&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`div`,{className:`menu-backdrop`,onPointerDown:()=>S(!1)}),(0,J.jsxs)(`div`,{className:`menu-list`,role:`menu`,children:[(0,J.jsx)(`button`,{type:`button`,role:`menuitem`,onClick:B,children:`New panel…`}),l&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`button`,{type:`button`,role:`menuitem`,onClick:le,children:`Rename panel…`}),(0,J.jsx)(`button`,{type:`button`,role:`menuitem`,onClick:ue,children:`Duplicate panel`}),(0,J.jsx)(`button`,{type:`button`,role:`menuitem`,className:`danger`,onClick:de,children:`Delete panel…`})]})]})]})]}),(0,J.jsxs)(`div`,{className:`mode-switch`,role:`group`,"aria-label":`Mode`,children:[(0,J.jsxs)(`button`,{type:`button`,"aria-label":`View`,title:`View`,"aria-pressed":!O,onClick:()=>ne(`view`),children:[(0,J.jsx)(Le,{}),` `,(0,J.jsx)(`span`,{className:`hide-narrow`,children:`View`})]}),(0,J.jsxs)(`button`,{type:`button`,"aria-label":`Edit`,title:`Edit`,"aria-pressed":O,onClick:()=>ne(`edit`),children:[(0,J.jsx)(he,{}),` `,(0,J.jsx)(`span`,{className:`hide-narrow`,children:`Edit`})]})]})]}),(0,J.jsx)(`main`,{className:`workspace`,children:i===null?(0,J.jsx)(`div`,{className:`empty-state`,children:`Loading…`}):i.length===0?(0,J.jsxs)(`div`,{className:`empty-state`,children:[(0,J.jsx)(`h1`,{children:`No panels yet`}),(0,J.jsx)(`p`,{children:`A panel is a board of widgets: create one for each task, e.g. teleoperation or navigation.`}),(0,J.jsxs)(`button`,{type:`button`,className:`button primary`,onClick:B,children:[(0,J.jsx)(ce,{}),` Create a panel`]})]}):f?(0,J.jsxs)(`div`,{className:`empty-state`,children:[(0,J.jsx)(`h1`,{children:`Cannot open this panel`}),(0,J.jsx)(`p`,{children:f})]}):l?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsxs)(`div`,{className:`board-column`,children:[O&&N!==`desktop`&&F&&(0,J.jsxs)(`div`,{className:`layout-bar`,children:[(0,J.jsxs)(`span`,{children:[(0,J.jsxs)(`strong`,{children:[Of[N].label,` layout`]}),F.custom?` · arranged for this device`:` · automatic, from the desktop’s: moving a widget arranges it for this device`]}),F.hidden.length>0&&(0,J.jsxs)(`span`,{className:`layout-hidden`,children:[`Hidden:`,F.hidden.map(e=>{let t=l.widgets.find(t=>t.id===e);return(0,J.jsxs)(`button`,{type:`button`,className:`layout-chip`,title:`Show it again`,onClick:()=>ae(e,!1),children:[t?.title||Sf(t?.type??``)?.name||e,` +`]},e)})]}),(F.custom||F.hidden.length>0)&&(0,J.jsx)(`button`,{type:`button`,className:`link-button`,onClick:()=>R(e=>Lf(e,N)),children:`Back to automatic`})]}),F&&(0,J.jsx)(Uf,{panel:l,device:N,layout:F,previewWidth:N===A?null:Of[N].previewWidth,editing:O,selectedId:g,onSelect:_,onPlace:ie,onRemove:oe,onHide:N===`desktop`?void 0:ae,onVisibleColumns:D},N)]}),l.widgets.length===0&&(0,J.jsx)(`div`,{className:`board-hint`,children:O?(0,J.jsxs)(J.Fragment,{children:[`This panel is empty: add widgets with `,(0,J.jsx)(`strong`,{children:`Add widget`}),`.`]}):(0,J.jsxs)(J.Fragment,{children:[`This panel is empty: switch to `,(0,J.jsx)(`strong`,{children:`Edit`}),` to add widgets.`]})}),V&&(0,J.jsx)(Kf,{widget:V,onChange:e=>R(t=>({...t,widgets:t.widgets.map(t=>t.id===e.id?e:t)})),onDuplicate:()=>se(V.id),onRemove:()=>oe(V.id),onClose:()=>_(null)},V.id)]}):null}),v&&(0,J.jsx)($f,{onAdd:re,onClose:()=>y(!1)}),(0,J.jsx)(Ke,{})]})}var dp=[];function fp(e){return e instanceof Error?e.message:String(e)}async function pp(){try{dp=await Xe.listPlugins()}catch(e){return[`The plugins could not be listed: ${fp(e)}`]}let e=dp.flatMap(e=>e.error?[`Plugin ${e.name}: its server side did not start: ${e.error}`]:[]);for(let e of dp)for(let t of e.styles){let e=document.createElement(`link`);e.rel=`stylesheet`,e.href=t,document.head.append(e)}return(await Promise.allSettled(dp.map(e=>e.client?rf(()=>import(e.client),[]):Promise.resolve(null)))).forEach((t,n)=>{let{name:r}=dp[n];if(t.status===`rejected`){e.push(`Plugin ${r} did not load: ${fp(t.reason)}`);return}if(t.value===null)return;let i=t.value.default;if(typeof i!=`object`||!i)e.push(`Plugin ${r}: its client module has no default export (definePlugin({ widgets }))`);else for(let t of bf(r,i))e.push(`Plugin ${r}: ${t}`)}),e}b.connect();var mp=await pp();(0,K.createRoot)(document.getElementById(`root`)).render((0,J.jsx)(q.StrictMode,{children:(0,J.jsx)(tt,{children:(0,J.jsx)(up,{})})}));for(let e of mp)console.error(e),G(e,`error`);
|
package/docs/plugins.md
CHANGED
|
@@ -140,6 +140,20 @@ mistyped values, so properties can be added without touching the saved panels),
|
|
|
140
140
|
| `image` | `string`, an image of the robot or uploaded (see `imageUrl`) | `placeholder` |
|
|
141
141
|
| `behavior-tree` | `string`, a behavior tree's XML (BT.CPP's) of the robot, or uploaded | `placeholder` |
|
|
142
142
|
|
|
143
|
+
A `select`'s options can also be known only at run time, e.g. the robot's: `useOptions`, a hook
|
|
144
|
+
the properties panel calls, gives those, shown after `options`. Its values are then not checked
|
|
145
|
+
against the options, and a value among neither is kept (it may be one later, e.g. once fetched):
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
function useArms() {
|
|
149
|
+
const [arms, setArms] = useState<{ value: string; label: string }[]>([]);
|
|
150
|
+
useEffect(() => connection.subscribePlugin('my-robot', 'arms', {}, { data: (data) => setArms(data as typeof arms) }), []);
|
|
151
|
+
return arms;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
{ key: 'arm', label: 'Arm', kind: 'select', default: '', options: [{ value: '', label: 'None' }], useOptions: useArms }
|
|
155
|
+
```
|
|
156
|
+
|
|
143
157
|
### The robot
|
|
144
158
|
|
|
145
159
|
`connection`, the page's connection to the server:
|