mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-03-28 17:43:05 +01:00
51 lines
76 KiB
JavaScript
51 lines
76 KiB
JavaScript
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/BotCodeEditor-D8HqJbrh.js","assets/index-CyRLXsJ_.js","assets/index-Kkb-Bio9.css"])))=>i.map(i=>d[i]);
|
||
import{c as Xe,r as o,s as ze,j as e,L as c,B as F,I as x,t as N,g as hs,T as gs,a as fs,b as js,d as $e,e as Ke,f as bs,C as We,h as vs,i as ys,k as V,l as xe,_ as Ns,m as ws,S as _s,n as ks}from"./index-CyRLXsJ_.js";import{S as b}from"./separator-CrdYjM_C.js";/**
|
||
* @license lucide-react v0.562.0 - ISC
|
||
*
|
||
* This source code is licensed under the ISC license.
|
||
* See the LICENSE file in the root directory of this source tree.
|
||
*/const Ss=[["path",{d:"M18 8c0 3.613-3.869 7.429-5.393 8.795a1 1 0 0 1-1.214 0C9.87 15.429 6 11.613 6 8a6 6 0 0 1 12 0",key:"11u0oz"}],["circle",{cx:"12",cy:"8",r:"2",key:"1822b1"}],["path",{d:"M8.714 14h-3.71a1 1 0 0 0-.948.683l-2.004 6A1 1 0 0 0 3 22h18a1 1 0 0 0 .948-1.316l-2-6a1 1 0 0 0-.949-.684h-3.712",key:"q8zwxj"}]],Cs=Xe("map-pinned",Ss);/**
|
||
* @license lucide-react v0.562.0 - ISC
|
||
*
|
||
* This source code is licensed under the ISC license.
|
||
* See the LICENSE file in the root directory of this source tree.
|
||
*/const Ts=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z",key:"18887p"}]],Es=Xe("message-square",Ts),pe=[{name:"USA/Canada",freq:910.525,bw:62.5,sf:7,cr:5},{name:"Australia",freq:915.8,bw:250,sf:10,cr:5},{name:"Australia (narrow)",freq:916.575,bw:62.5,sf:7,cr:8},{name:"Australia SA, WA",freq:923.125,bw:62.5,sf:8,cr:8},{name:"Australia QLD",freq:923.125,bw:62.5,sf:8,cr:5},{name:"New Zealand",freq:917.375,bw:250,sf:11,cr:5},{name:"New Zealand (narrow)",freq:917.375,bw:62.5,sf:7,cr:5},{name:"EU/UK/Switzerland Long Range",freq:869.525,bw:250,sf:11,cr:5},{name:"EU/UK/Switzerland Medium Range",freq:869.525,bw:250,sf:10,cr:5},{name:"EU/UK/Switzerland Narrow",freq:869.618,bw:62.5,sf:8,cr:8},{name:"Czech Republic (Narrow)",freq:869.432,bw:62.5,sf:7,cr:5},{name:"EU 433MHz Long Range",freq:433.65,bw:250,sf:11,cr:5},{name:"Portugal 433MHz",freq:433.375,bw:62.5,sf:9,cr:6},{name:"Portugal 868MHz",freq:869.618,bw:62.5,sf:7,cr:6},{name:"Vietnam",freq:920.25,bw:250,sf:11,cr:5}];function Fs({config:s,health:t,appSettings:n,pageMode:a,onSave:d,onSaveAppSettings:w,onSetPrivateKey:S,onReboot:j,onDisconnect:_,onReconnect:h,onAdvertise:p,meshDiscovery:v,meshDiscoveryLoadingTarget:C,onDiscoverMesh:M,onClose:I,className:K}){const[U,L]=o.useState(""),[R,q]=o.useState(""),[B,O]=o.useState(""),[W,G]=o.useState(""),[m,A]=o.useState(""),[X,u]=o.useState(""),[y,T]=o.useState(""),[H,se]=o.useState(""),[re,Y]=o.useState("0"),[Q,E]=o.useState("current"),[J,z]=o.useState(!1),[i,l]=o.useState(!1),[f,g]=o.useState(!1),[D,Z]=o.useState(null),[P,je]=o.useState(""),[be,ve]=o.useState(!1),[ye,Ne]=o.useState(!1),[we,le]=o.useState(null),[_e,ke]=o.useState("0"),[ue,Se]=o.useState(""),[Ce,Te]=o.useState(""),[Ee,Fe]=o.useState(!1),[Ae,Me]=o.useState(null),[Le,qe]=o.useState(!1),[Pe,Re]=o.useState(null),[De,Ie]=o.useState(!1);o.useEffect(()=>{L(s.name),q(String(s.lat)),O(String(s.lon)),G(String(s.tx_power)),A(String(s.radio.freq)),u(String(s.radio.bw)),T(String(s.radio.sf)),se(String(s.radio.cr)),Y(String(s.path_hash_mode)),E(s.advert_location_source??"current")},[s]),o.useEffect(()=>{ke(String(Math.round(n.advert_interval/3600))),Se(ze(n.flood_scope)),Te(String(n.max_radio_contacts))},[n]);const as=o.useMemo(()=>{const r=parseFloat(m),k=parseFloat(X),ee=parseInt(y,10),te=parseInt(H,10);for(const ae of pe)if(ae.freq===r&&ae.bw===k&&ae.sf===ee&&ae.cr===te)return ae.name;return"custom"},[m,X,y,H]),rs=r=>{if(r==="custom")return;const k=pe.find(ee=>ee.name===r);k&&(A(String(k.freq)),u(String(k.bw)),T(String(k.sf)),se(String(k.cr)))},ns=()=>{if(!navigator.geolocation){N.error("Geolocation not supported",{description:"Your browser does not support geolocation"});return}z(!0),navigator.geolocation.getCurrentPosition(r=>{q(r.coords.latitude.toFixed(6)),O(r.coords.longitude.toFixed(6)),z(!1),N.success("Location updated")},r=>{z(!1),N.error("Failed to get location",{description:r.message})},{enableHighAccuracy:!0,timeout:1e4})},Be=()=>{const r=parseFloat(R),k=parseFloat(B),ee=parseInt(W,10),te=parseFloat(m),ae=parseFloat(X),Ue=parseInt(y,10),He=parseInt(H,10);if([r,k,ee,te,ae,Ue,He].some(ps=>isNaN(ps)))return Z("All numeric fields must have valid values"),null;const me=parseInt(re,10);return{name:U,lat:r,lon:k,tx_power:ee,...Q!==(s.advert_location_source??"current")?{advert_location_source:Q}:{},radio:{freq:te,bw:ae,sf:Ue,cr:He},...s.path_hash_mode_supported&&!isNaN(me)&&me!==s.path_hash_mode?{path_hash_mode:me}:{}}},os=async()=>{Z(null);const r=Be();if(r){l(!0);try{await d(r),N.success("Radio config saved")}catch(k){Z(k instanceof Error?k.message:"Failed to save")}finally{l(!1)}}},is=async()=>{Z(null);const r=Be();if(r){l(!0);try{await d(r),N.success("Radio config saved, rebooting..."),g(!0),await j(),a||I()}catch(k){Z(k instanceof Error?k.message:"Failed to save")}finally{g(!1),l(!1)}}},cs=async()=>{if(!P.trim()){le("Private key is required");return}le(null),ve(!0);try{await S(P.trim()),je(""),N.success("Private key set, rebooting..."),Ne(!0),await j(),a||I()}catch(r){le(r instanceof Error?r.message:"Failed to set private key")}finally{Ne(!1),ve(!1)}},ds=async()=>{Me(null),Fe(!0);try{const r={},k=parseInt(_e,10),ee=isNaN(k)?0:k*3600;ee!==n.advert_interval&&(r.advert_interval=ee),ue!==ze(n.flood_scope)&&(r.flood_scope=ue);const te=parseInt(Ce,10);!isNaN(te)&&te!==n.max_radio_contacts&&(r.max_radio_contacts=te),Object.keys(r).length>0&&await w(r),N.success("Settings saved")}catch(r){Me(r instanceof Error?r.message:"Failed to save")}finally{Fe(!1)}},ls=async()=>{qe(!0);try{await p()}finally{qe(!1)}},us=async r=>{Re(null);try{await M(r)}catch(k){Re(k instanceof Error?k.message:"Failed to run mesh discovery")}},$=(t==null?void 0:t.radio_state)??(t!=null&&t.radio_initializing?"initializing":"disconnected"),Oe=$==="paused"?"Reconnect":$==="connected"||$==="initializing"?"Disconnect":"Stop Trying",ms=$==="connected"?(t==null?void 0:t.connection_info)||"Connected":$==="initializing"?`Initializing ${(t==null?void 0:t.connection_info)||"radio"}`:$==="connecting"?`Attempting to connect${t!=null&&t.connection_info?` to ${t.connection_info}`:""}`:$==="paused"?`Connection paused${t!=null&&t.connection_info?` (${t.connection_info})`:""}`:"Not connected",xs=async()=>{Ie(!0);try{$==="paused"?(await h(),N.success("Reconnect requested")):(await _(),N.success("Radio connection paused"))}catch(r){N.error("Failed to change radio connection state",{description:r instanceof Error?r.message:"Check radio connection and try again"})}finally{Ie(!1)}};return e.jsxs("div",{className:K,children:[e.jsxs("div",{className:"space-y-3",children:[e.jsx(c,{children:"Connection"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:`w-2 h-2 rounded-full ${$==="connected"?"bg-status-connected":$==="initializing"||$==="connecting"?"bg-warning":"bg-status-disconnected"}`}),e.jsx("span",{className:$==="paused"||$==="disconnected"?"text-muted-foreground":"",children:ms})]}),e.jsx(F,{type:"button",variant:"outline",onClick:xs,disabled:De,className:"w-full",children:De?`${Oe}...`:Oe}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Disconnect pauses automatic reconnect attempts so another device can use the radio."})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"name",children:"Radio Name"}),e.jsx(x,{id:"name",value:U,onChange:r=>L(r.target.value)})]}),e.jsx(b,{}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"preset",children:"Preset"}),e.jsxs("select",{id:"preset",value:as,onChange:r=>rs(r.target.value),className:"w-full h-10 px-3 rounded-md border border-input bg-background text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",children:[e.jsx("option",{value:"custom",children:"Custom"}),pe.map(r=>e.jsx("option",{value:r.name,children:r.name},r.name))]})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"freq",children:"Frequency (MHz)"}),e.jsx(x,{id:"freq",type:"number",step:"any",value:m,onChange:r=>A(r.target.value)})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"bw",children:"Bandwidth (kHz)"}),e.jsx(x,{id:"bw",type:"number",step:"any",value:X,onChange:r=>u(r.target.value)})]})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"sf",children:"Spreading Factor"}),e.jsx(x,{id:"sf",type:"number",min:"7",max:"12",value:y,onChange:r=>T(r.target.value)})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"cr",children:"Coding Rate"}),e.jsx(x,{id:"cr",type:"number",min:"5",max:"8",value:H,onChange:r=>se(r.target.value)})]})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"tx-power",children:"TX Power (dBm)"}),e.jsx(x,{id:"tx-power",type:"number",value:W,onChange:r=>G(r.target.value)})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"max-tx",children:"Max TX Power"}),e.jsx(x,{id:"max-tx",type:"number",value:s.max_tx_power,disabled:!0})]})]}),e.jsx(b,{}),e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(c,{children:"Location"}),e.jsx(F,{type:"button",variant:"outline",size:"sm",onClick:ns,disabled:J,children:J?"Getting...":e.jsxs(e.Fragment,{children:[e.jsx(Cs,{className:"mr-1.5 h-4 w-4","aria-hidden":"true"}),"Use My Location"]})})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"lat",className:"text-xs text-muted-foreground",children:"Latitude"}),e.jsx(x,{id:"lat",type:"number",step:"any",value:R,onChange:r=>q(r.target.value)})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"lon",className:"text-xs text-muted-foreground",children:"Longitude"}),e.jsx(x,{id:"lon",type:"number",step:"any",value:B,onChange:r=>O(r.target.value)})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"advert-location-source",children:"Advert Location Source"}),e.jsxs("select",{id:"advert-location-source",value:Q,onChange:r=>E(r.target.value),className:"w-full h-10 px-3 rounded-md border border-input bg-background text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",children:[e.jsx("option",{value:"off",children:"Off"}),e.jsx("option",{value:"current",children:"Include Node Location"})]}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Companion-radio firmware does not distinguish between saved coordinates and live GPS here. When enabled, adverts include the node's current location state. That may be the last coordinates you set from RemoteTerm or live GPS coordinates if the node itself is already updating them. RemoteTerm cannot enable GPS on the node through the interface library."})]})]}),s.path_hash_mode_supported&&e.jsxs(e.Fragment,{children:[e.jsx(b,{}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"path-hash-mode",children:"Path Hash Mode"}),e.jsxs("select",{id:"path-hash-mode",value:re,onChange:r=>Y(r.target.value),className:"w-full h-10 px-3 rounded-md border border-input bg-background text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",children:[e.jsx("option",{value:"0",children:"1 byte (default)"}),e.jsx("option",{value:"1",children:"2 bytes"}),e.jsx("option",{value:"2",children:"3 bytes"})]}),e.jsxs("div",{className:"rounded-md border border-warning/50 bg-warning/10 p-3 text-xs text-warning",children:[e.jsx("p",{className:"font-semibold mb-1",children:"Compatibility Warning"}),e.jsx("p",{children:"ALL nodes along a message's route — your radio, every repeater, and the recipient — must be running firmware that supports the selected mode. Messages sent with 2-byte or 3-byte hops will be dropped by any node on older firmware."})]})]})]}),D&&e.jsx("div",{className:"text-sm text-destructive",role:"alert",children:D}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(F,{onClick:os,disabled:i||f,variant:"outline",className:"flex-1",children:i&&!f?"Saving...":"Save"}),e.jsx(F,{onClick:is,disabled:i||f,className:"flex-1",children:f?"Rebooting...":"Save & Reboot"})]}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Some settings may require a reboot to take effect on some radios."}),e.jsx(b,{}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"public-key",children:"Public Key"}),e.jsx(x,{id:"public-key",value:s.public_key,disabled:!0,className:"font-mono text-xs"})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"private-key",children:"Set Private Key (write-only)"}),e.jsx(x,{id:"private-key",type:"password",autoComplete:"off",value:P,onChange:r=>je(r.target.value),placeholder:"64-character hex private key"}),e.jsx(F,{onClick:cs,disabled:be||ye||!P.trim(),className:"w-full border-destructive/50 text-destructive hover:bg-destructive/10",variant:"outline",children:be||ye?"Setting & Rebooting...":"Set Private Key & Reboot"})]}),we&&e.jsx("div",{className:"text-sm text-destructive",role:"alert",children:we}),e.jsx(b,{}),e.jsx("div",{className:"space-y-2",children:e.jsx(c,{className:"text-base",children:"Flood & Advert Control"})}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"advert-interval",children:"Periodic Advertising Interval"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(x,{id:"advert-interval",type:"number",min:"0",value:_e,onChange:r=>ke(r.target.value),className:"w-28"}),e.jsx("span",{className:"text-sm text-muted-foreground",children:"hours (0 = off)"})]}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"How often to automatically advertise presence. Set to 0 to disable. Minimum: 1 hour. Recommended: 24 hours or higher."})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"flood-scope",children:"Flood Scope / Region"}),e.jsx(x,{id:"flood-scope",value:ue,onChange:r=>Se(r.target.value),placeholder:"MyRegion"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Tag outgoing flood messages with a region name (e.g. MyRegion). Repeaters configured for that region can forward the traffic, while repeaters configured to deny other regions may drop it. Leave empty to disable."})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"max-contacts",children:"Max Contacts on Radio"}),e.jsx(x,{id:"max-contacts",type:"number",min:"1",max:"1000",value:Ce,onChange:r=>Te(r.target.value)}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Configured radio contact capacity. Favorites reload first, then background maintenance refills to about 80% of this value and offloads once occupancy reaches about 95%."})]}),Ae&&e.jsx("div",{className:"text-sm text-destructive",role:"alert",children:Ae}),e.jsx(F,{onClick:ds,disabled:Ee,className:"w-full",children:Ee?"Saving...":"Save Settings"}),e.jsx(b,{}),e.jsx("div",{className:"space-y-2",children:e.jsx(c,{className:"text-base",children:"Hear & Be Heard"})}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{children:"Send Advertisement"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Send a flood advertisement to announce your presence on the mesh network."}),e.jsx(F,{onClick:ls,disabled:Le||!(t!=null&&t.radio_connected),className:"w-full bg-warning hover:bg-warning/90 text-warning-foreground",children:Le?"Sending...":"Send Advertisement"}),!(t!=null&&t.radio_connected)&&e.jsx("p",{className:"text-sm text-destructive",children:"Radio not connected"})]}),e.jsxs("div",{className:"space-y-3",children:[e.jsx(c,{children:"Mesh Discovery"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Discover nearby node types that currently respond to mesh discovery requests: repeaters and sensors."}),e.jsx("div",{className:"grid grid-cols-1 gap-2 sm:grid-cols-3",children:[{target:"repeaters",label:"Discover Repeaters"},{target:"sensors",label:"Discover Sensors"},{target:"all",label:"Discover Both"}].map(({target:r,label:k})=>e.jsx(F,{type:"button",variant:"outline",onClick:()=>us(r),disabled:C!==null||!(t!=null&&t.radio_connected),className:"w-full",children:C===r?"Listening...":k},r))}),!(t!=null&&t.radio_connected)&&e.jsx("p",{className:"text-sm text-destructive",children:"Radio not connected"}),Pe&&e.jsx("p",{className:"text-sm text-destructive",role:"alert",children:Pe}),v&&e.jsxs("div",{className:"space-y-2 rounded-md border border-input bg-muted/20 p-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-4",children:[e.jsxs("p",{className:"text-sm font-medium",children:["Last sweep: ",v.results.length," node",v.results.length===1?"":"s"]}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:[v.duration_seconds.toFixed(0),"s listen window"]})]}),v.results.length===0?e.jsx("p",{className:"text-sm text-muted-foreground",children:"No supported nodes responded during the last discovery sweep."}):e.jsx("div",{className:"space-y-2",children:v.results.map(r=>e.jsxs("div",{className:"rounded-md border border-input bg-background px-3 py-2",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsx("span",{className:"text-sm font-medium capitalize",children:r.node_type}),e.jsxs("span",{className:"text-xs text-muted-foreground",children:["heard ",r.heard_count," time",r.heard_count===1?"":"s"]})]}),e.jsx("p",{className:"mt-1 break-all font-mono text-xs text-muted-foreground",children:r.public_key}),e.jsxs("p",{className:"mt-1 text-xs text-muted-foreground",children:["Heard here: ",r.local_snr??"n/a"," dB SNR / ",r.local_rssi??"n/a"," ","dBm RSSI. Remote heard us: ",r.remote_snr??"n/a"," dB SNR."]})]},r.public_key))})]})]})]})}function As({colors:s}){return e.jsx("div",{className:"grid grid-cols-3 gap-[3px]","aria-hidden":"true",children:s.map((t,n)=>e.jsx("div",{className:"w-3 h-3 rounded-full ring-1 ring-border/40",style:{backgroundColor:t}},n))})}function Ms(){const[s,t]=o.useState(hs),n=a=>{t(a),fs(a)};return e.jsxs("fieldset",{className:"flex flex-wrap gap-2 md:grid md:grid-cols-4",children:[e.jsx("legend",{className:"sr-only",children:"Color theme"}),gs.map(a=>e.jsxs("label",{className:"flex items-center gap-2 px-2 py-1.5 rounded-md cursor-pointer border transition-colors focus-within:ring-2 focus-within:ring-ring md:w-full "+(s===a.id?"border-primary bg-primary/5":"border-transparent hover:bg-accent/50"),children:[e.jsx("input",{type:"radio",name:"theme",value:a.id,checked:s===a.id,onChange:()=>n(a.id),className:"sr-only"}),e.jsx(As,{colors:a.swatches}),e.jsx("span",{className:"text-xs whitespace-nowrap",children:a.name})]},a.id))]})}function Ls({onLocalLabelChange:s,className:t}){const[n,a]=o.useState(js),[d,w]=o.useState(()=>$e().text),[S,j]=o.useState(()=>$e().color),_=h=>{a(h),vs(h),h&&ys()};return e.jsxs("div",{className:t,children:[e.jsx("p",{className:"text-sm text-muted-foreground",children:"These settings apply only to this device/browser."}),e.jsxs("div",{className:"space-y-1",children:[e.jsx(c,{children:"Color Scheme"}),e.jsx(Ms,{}),e.jsx(qs,{className:"mt-6"})]}),e.jsx(b,{}),e.jsxs("div",{className:"space-y-3",children:[e.jsx(c,{children:"Local Label"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(x,{value:d,onChange:h=>{const p=h.target.value;w(p),Ke(p,S),s==null||s({text:p,color:S})},placeholder:"e.g. Home Base, Field Radio 2","aria-label":"Local label text",className:"flex-1"}),e.jsx("input",{type:"color",value:S,onChange:h=>{const p=h.target.value;j(p),Ke(d,p),s==null||s({text:d,color:p})},"aria-label":"Local label color",className:"w-10 h-9 rounded border border-input cursor-pointer bg-transparent p-0.5"})]}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Display a colored banner at the top of the page to identify this instance."})]}),e.jsx(b,{}),e.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:n,onChange:h=>_(h.target.checked),className:"w-4 h-4 rounded border-input accent-primary"}),e.jsx("span",{className:"text-sm",children:"Reopen to last viewed channel/conversation"})]})]})}function qs({className:s}){return e.jsxs("div",{className:`rounded-lg border border-border bg-card p-3 ${s??""}`,children:[e.jsx("p",{className:"text-xs text-muted-foreground mb-3",children:"Preview alert, message, sidebar, and badge contrast for the selected theme."}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(he,{className:"border border-status-connected/30 bg-status-connected/15 text-status-connected",children:"Connected preview: radio link healthy and syncing."}),e.jsx(he,{className:"border border-warning/50 bg-warning/10 text-warning",children:"Warning preview: packet audit suggests missing history."}),e.jsx(he,{className:"border border-destructive/30 bg-destructive/10 text-destructive",children:"Error preview: radio reconnect failed."})]}),e.jsxs("div",{className:"mt-4 space-y-2",children:[e.jsx(Ge,{sender:"Alice",bubbleClassName:"bg-msg-incoming text-foreground",text:"Hello, mesh!"}),e.jsx(Ge,{sender:"You",alignRight:!0,bubbleClassName:"bg-msg-outgoing text-foreground",text:"Hi there! I'm using RemoteTerm."})]}),e.jsxs("div",{className:"mt-4 rounded-md border border-border bg-background p-2",children:[e.jsx("p",{className:"mb-2 text-[11px] font-medium text-muted-foreground",children:"Sidebar preview"}),e.jsxs("div",{className:"space-y-1",children:[e.jsx(ge,{active:!0,leading:e.jsx("span",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-primary/10 text-primary","aria-hidden":"true",children:e.jsx(bs,{className:"h-3.5 w-3.5"})}),label:"Packet Feed"}),e.jsx(ge,{leading:e.jsx(We,{name:"Alice",publicKey:"ab".repeat(32),size:24}),label:"Alice",badge:e.jsx("span",{className:"rounded-full bg-badge-unread/90 px-1.5 py-0.5 text-[10px] font-semibold text-badge-unread-foreground",children:"3"})}),e.jsx(ge,{leading:e.jsx(We,{name:"Mesh Ops",publicKey:"cd".repeat(32),size:24}),label:"Mesh Ops",badge:e.jsx("span",{className:"rounded-full bg-badge-mention px-1.5 py-0.5 text-[10px] font-semibold text-badge-mention-foreground",children:"@2"})})]})]})]})}function he({children:s,className:t}){return e.jsx("div",{className:`rounded-md px-3 py-2 text-xs ${t}`,children:s})}function Ge({sender:s,text:t,bubbleClassName:n,alignRight:a=!1}){return e.jsx("div",{className:`flex ${a?"justify-end":"justify-start"}`,children:e.jsxs("div",{className:`max-w-[85%] ${a?"items-end":"items-start"} flex flex-col`,children:[e.jsx("span",{className:"mb-1 text-[11px] text-muted-foreground",children:s}),e.jsx("div",{className:`rounded-2xl px-3 py-2 text-sm break-words ${n}`,children:t})]})})}function ge({leading:s,label:t,badge:n,active:a=!1}){return e.jsxs("div",{className:`flex items-center gap-2 rounded-md border-l-2 px-3 py-2 text-[13px] ${a?"border-l-primary bg-accent text-foreground":"border-l-transparent"}`,children:[s,e.jsx("span",{className:`min-w-0 flex-1 truncate ${a?"font-medium":"text-foreground"}`,children:t}),n,!n&&e.jsx("span",{className:"text-muted-foreground","aria-hidden":"true",children:e.jsx(Es,{className:"h-3.5 w-3.5"})})]})}const Ps=o.lazy(()=>Ns(()=>import("./BotCodeEditor-D8HqJbrh.js"),__vite__mapDeps([0,1,2])).then(s=>({default:s.BotCodeEditor}))),Ze={mqtt_private:"Private MQTT",mqtt_community:"meshcoretomqtt/LetsMesh/MeshRank",bot:"Bot",webhook:"Webhook",apprise:"Apprise",sqs:"Amazon SQS"},Ye=[{value:"mqtt_private",label:"Private MQTT"},{value:"mqtt_community",label:"meshcoretomqtt/LetsMesh/MeshRank"},{value:"bot",label:"Bot"},{value:"webhook",label:"Webhook"},{value:"apprise",label:"Apprise"},{value:"sqs",label:"Amazon SQS"}],ie="meshcore/{IATA}/{PUBLIC_KEY}/packets",oe="mqtt-us-v1.letsmesh.net",ce=443,es="websockets",ss="token";function Qe(s,t){const n=s.broker_host||t.host,a=typeof s.broker_port=="number"?s.broker_port:t.port;return`${n}:${a}`}function Rs(s){const t=s.topic_prefix||"meshcore";return`${t}/dm:<pubkey>, ${t}/gm:<channel>, ${t}/raw/...`}function Ds(s,t=80){const n=(s||"").split(`
|
||
`).map(d=>d.trim()).filter(Boolean);if(n.length===0)return"No targets configured";const a=n.join(", ");return a.length<=t?a:`${a.slice(0,t-3)}...`}function Is(s){const t=(s.queue_url||"").trim();return t||"No queue configured"}function Bs(s,t){const n=Ze[s]||s,a=t.filter(d=>d.type===s).length+1;return`${n} #${a}`}const ts=`def bot(**kwargs) -> str | list[str] | None:
|
||
"""
|
||
Process messages and optionally return a reply.
|
||
|
||
Args:
|
||
kwargs keys currently provided:
|
||
sender_name: Display name of sender (may be None)
|
||
sender_key: 64-char hex public key (None for channel msgs)
|
||
message_text: The message content
|
||
is_dm: True for direct messages, False for channel
|
||
channel_key: 32-char hex key for channels, None for DMs
|
||
channel_name: Channel name with hash (e.g. "#bot"), None for DMs
|
||
sender_timestamp: Sender's timestamp (unix seconds, may be None)
|
||
path: Hex-encoded routing path (may be None)
|
||
is_outgoing: True if this is our own outgoing message
|
||
path_bytes_per_hop: Bytes per hop in path (1, 2, or 3) when known
|
||
|
||
Returns:
|
||
None for no reply, a string for a single reply,
|
||
or a list of strings to send multiple messages in order
|
||
"""
|
||
sender_name = kwargs.get("sender_name")
|
||
message_text = kwargs.get("message_text", "")
|
||
channel_name = kwargs.get("channel_name")
|
||
is_outgoing = kwargs.get("is_outgoing", False)
|
||
path_bytes_per_hop = kwargs.get("path_bytes_per_hop")
|
||
|
||
# Don't reply to our own outgoing messages
|
||
if is_outgoing:
|
||
return None
|
||
|
||
# Example: Only respond in #bot channel to "!pling" command
|
||
if channel_name == "#bot" and "!pling" in message_text.lower():
|
||
return "[BOT] Plong!"
|
||
return None`;function Je(s,t){return s==="connected"?t==="bot"||t==="webhook"||t==="apprise"?"Active":"Connected":s==="error"?"Error":s==="disconnected"?"Disconnected":"Inactive"}function Os(s,t){return t===!1?"bg-muted-foreground":s==="connected"?"bg-status-connected shadow-[0_0_6px_hsl(var(--status-connected)/0.5)]":s==="error"||s==="disconnected"?"bg-destructive shadow-[0_0_6px_hsl(var(--destructive)/0.5)]":"bg-muted-foreground"}function Us({config:s,scope:t,onChange:n,onScopeChange:a}){return e.jsxs("div",{className:"space-y-3",children:[e.jsx("p",{className:"text-xs text-muted-foreground",children:"Forward mesh data to your own MQTT broker for home automation, logging, or alerting."}),e.jsx("div",{className:"rounded-md border border-warning/50 bg-warning/10 px-3 py-2 text-xs text-warning",children:"Outgoing messages (DMs and group messages) will be reported to private MQTT brokers in decrypted/plaintext form."}),e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-mqtt-host",children:"Broker Host"}),e.jsx(x,{id:"fanout-mqtt-host",type:"text",placeholder:"e.g. 192.168.1.100",value:s.broker_host||"",onChange:d=>n({...s,broker_host:d.target.value})})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-mqtt-port",children:"Broker Port"}),e.jsx(x,{id:"fanout-mqtt-port",type:"number",min:"1",max:"65535",value:s.broker_port||1883,onChange:d=>n({...s,broker_port:parseInt(d.target.value,10)||1883})})]})]}),e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-mqtt-user",children:"Username"}),e.jsx(x,{id:"fanout-mqtt-user",type:"text",placeholder:"Optional",value:s.username||"",onChange:d=>n({...s,username:d.target.value})})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-mqtt-pass",children:"Password"}),e.jsx(x,{id:"fanout-mqtt-pass",type:"password",placeholder:"Optional",value:s.password||"",onChange:d=>n({...s,password:d.target.value})})]})]}),e.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:!!s.use_tls,onChange:d=>n({...s,use_tls:d.target.checked}),className:"h-4 w-4 rounded border-border"}),e.jsx("span",{className:"text-sm",children:"Use TLS"})]}),!!s.use_tls&&e.jsxs("label",{className:"flex items-center gap-3 cursor-pointer ml-7",children:[e.jsx("input",{type:"checkbox",checked:!!s.tls_insecure,onChange:d=>n({...s,tls_insecure:d.target.checked}),className:"h-4 w-4 rounded border-border"}),e.jsx("span",{className:"text-sm",children:"Skip certificate verification"})]}),e.jsx(b,{}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-mqtt-prefix",children:"Topic Prefix"}),e.jsx(x,{id:"fanout-mqtt-prefix",type:"text",value:s.topic_prefix||"meshcore",onChange:d=>n({...s,topic_prefix:d.target.value})})]}),e.jsx(b,{}),e.jsx(de,{scope:t,onChange:a,showRawPackets:!0})]})}function Hs({config:s,onChange:t}){const n=s.auth_mode||ss;return e.jsxs("div",{className:"space-y-3",children:[e.jsx("p",{className:"text-xs text-muted-foreground",children:"Share raw packet data with the MeshCore community for coverage mapping and network analysis. Only raw RF packets are shared — never decrypted messages."}),e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-comm-host",children:"Broker Host"}),e.jsx(x,{id:"fanout-comm-host",type:"text",placeholder:oe,value:s.broker_host||oe,onChange:a=>t({...s,broker_host:a.target.value})})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-comm-port",children:"Broker Port"}),e.jsx(x,{id:"fanout-comm-port",type:"number",min:"1",max:"65535",value:s.broker_port||ce,onChange:a=>t({...s,broker_port:parseInt(a.target.value,10)||ce})})]})]}),e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-comm-transport",children:"Transport"}),e.jsxs("select",{id:"fanout-comm-transport",value:s.transport||es,onChange:a=>t({...s,transport:a.target.value}),className:"w-full rounded-md border border-input bg-background px-3 py-2 text-sm",children:[e.jsx("option",{value:"websockets",children:"WebSockets"}),e.jsx("option",{value:"tcp",children:"TCP"})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-comm-auth-mode",children:"Authentication"}),e.jsxs("select",{id:"fanout-comm-auth-mode",value:n,onChange:a=>t({...s,auth_mode:a.target.value}),className:"w-full rounded-md border border-input bg-background px-3 py-2 text-sm",children:[e.jsx("option",{value:"token",children:"Token"}),e.jsx("option",{value:"none",children:"None"}),e.jsx("option",{value:"password",children:"Username / Password"})]})]})]}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:["LetsMesh uses ",e.jsx("code",{children:"token"})," auth. MeshRank uses ",e.jsx("code",{children:"none"}),"."]}),n==="token"&&e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-comm-token-audience",children:"Token Audience"}),e.jsx(x,{id:"fanout-comm-token-audience",type:"text",placeholder:s.broker_host||oe,value:s.token_audience??"",onChange:a=>t({...s,token_audience:a.target.value})}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Defaults to the broker host when blank"})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-comm-email",children:"Owner Email (optional)"}),e.jsx(x,{id:"fanout-comm-email",type:"email",placeholder:"you@example.com",value:s.email||"",onChange:a=>t({...s,email:a.target.value})}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Used to claim your node on the community aggregator"})]})]}),n==="password"&&e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-comm-username",children:"Username"}),e.jsx(x,{id:"fanout-comm-username",type:"text",value:s.username||"",onChange:a=>t({...s,username:a.target.value})})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-comm-password",children:"Password"}),e.jsx(x,{id:"fanout-comm-password",type:"password",value:s.password||"",onChange:a=>t({...s,password:a.target.value})})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:s.use_tls===void 0?!0:!!s.use_tls,onChange:a=>t({...s,use_tls:a.target.checked}),className:"h-4 w-4 rounded border-border"}),e.jsx("span",{className:"text-sm",children:"Use TLS"})]}),e.jsxs("label",{className:"flex items-center gap-3 cursor-pointer ml-7",children:[e.jsx("input",{type:"checkbox",checked:s.tls_verify===void 0?!0:!!s.tls_verify,onChange:a=>t({...s,tls_verify:a.target.checked}),className:"h-4 w-4 rounded border-border",disabled:s.use_tls===void 0?!1:!s.use_tls}),e.jsx("span",{className:"text-sm",children:"Verify TLS certificates"})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-comm-iata",children:"Region Code (IATA)"}),e.jsx(x,{id:"fanout-comm-iata",type:"text",maxLength:3,placeholder:"e.g. DEN, LAX, NYC",value:s.iata||"",onChange:a=>t({...s,iata:a.target.value.toUpperCase()}),className:"w-32"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Your nearest airport's IATA code (required)"})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-comm-topic-template",children:"Packet Topic Template"}),e.jsx(x,{id:"fanout-comm-topic-template",type:"text",value:s.topic_template||ie,onChange:a=>t({...s,topic_template:a.target.value})}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:["Use ",e.jsx("code",{children:"{IATA}"})," and ",e.jsx("code",{children:"{PUBLIC_KEY}"}),". Default:"," ",e.jsx("code",{children:ie})]})]})]})}function zs({config:s,onChange:t}){const n=s.code||"";return e.jsxs("div",{className:"space-y-3",children:[e.jsx("div",{className:"p-3 bg-destructive/10 border border-destructive/30 rounded-md",children:e.jsxs("p",{className:"text-sm text-destructive",children:[e.jsx("strong",{children:"Experimental:"})," This is an alpha feature and introduces automated message sending to your radio; unexpected behavior may occur. Use with caution, and please report any bugs!"]})}),e.jsx("div",{className:"p-3 bg-warning/10 border border-warning/30 rounded-md",children:e.jsxs("p",{className:"text-sm text-warning",children:[e.jsx("strong",{children:"Security Warning:"})," This feature executes arbitrary Python code on the server. Only run trusted code, and be cautious of arbitrary usage of message parameters."]})}),e.jsx("div",{className:"p-3 bg-warning/10 border border-warning/30 rounded-md",children:e.jsxs("p",{className:"text-sm text-warning",children:[e.jsx("strong",{children:"Don't wreck the mesh!"})," Bots process ALL messages, including their own. Be careful of creating infinite loops!"]})}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("p",{className:"text-xs text-muted-foreground",children:["Define a ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"bot()"})," function that receives message data and optionally returns a reply."]}),e.jsx(F,{type:"button",variant:"outline",size:"sm",onClick:()=>t({...s,code:ts}),children:"Reset to Example"})]}),e.jsx(o.Suspense,{fallback:e.jsx("div",{className:"h-64 md:h-96 rounded-md border border-input bg-code-editor-bg flex items-center justify-center text-muted-foreground",children:"Loading editor..."}),children:e.jsx(Ps,{value:n,onChange:a=>t({...s,code:a})})}),e.jsxs("div",{className:"text-xs text-muted-foreground space-y-1",children:[e.jsxs("p",{children:[e.jsx("strong",{children:"Available:"})," Standard Python libraries and any modules installed in the server environment."]}),e.jsxs("p",{children:[e.jsx("strong",{children:"Limits:"})," 10 second timeout per bot."]}),e.jsxs("p",{children:[e.jsx("strong",{children:"Note:"})," Bots respond to all messages, including your own. For channel messages, ",e.jsx("code",{children:"sender_key"})," is ",e.jsx("code",{children:"None"}),". Multiple enabled bots run concurrently. Outgoing messages are serialized with a two-second delay between sends to prevent repeater collision."]})]})]})}function $s(s){if(s==="all")return"all";if(s==="none")return"none";if(typeof s=="object"&&s!==null){const t=s,n=t.channels,a=t.contacts;return typeof n=="object"&&n!==null&&!Array.isArray(n)||typeof a=="object"&&a!==null&&!Array.isArray(a)?"except":"only"}return"all"}function Ve(s){return Array.isArray(s)?s:typeof s=="object"&&s!==null&&"except"in s?s.except??[]:[]}function de({scope:s,onChange:t,showRawPackets:n=!1}){const[a,d]=o.useState([]),[w,S]=o.useState([]);o.useEffect(()=>{V.getChannels().then(d).catch(console.error),(async()=>{const u=[];let T=0;for(;;){const H=await V.getContacts(1e3,T);if(u.push(...H),H.length<1e3)break;T+=1e3}S(u)})().catch(console.error)},[]);const j=s.messages??"all",_=$s(j),h=!n&&_==="none"?"all":_,p=h==="only"||h==="except",v=p&&typeof j=="object"&&j!==null?Ve(j.channels):[],C=p&&typeof j=="object"&&j!==null?Ve(j.contacts):[],M=(u,y)=>h==="except"?{channels:{except:u},contacts:{except:y}}:{channels:u,contacts:y},I=u=>{t(u==="all"||u==="none"?{...s,messages:u}:u==="only"?{...s,messages:{channels:[],contacts:[]}}:{...s,messages:{channels:{except:[]},contacts:{except:[]}}})},K=u=>{const y=[...v],T=y.indexOf(u);T>=0?y.splice(T,1):y.push(u),t({...s,messages:M(y,C)})},U=u=>{const y=[...C],T=y.indexOf(u);T>=0?y.splice(T,1):y.push(u),t({...s,messages:M(v,y)})},L=w.filter(u=>u.type===0||u.type===1),R={all:"All messages",none:"No messages",only:"Only listed channels/contacts",except:"All except listed channels/contacts"},q=n&&s.raw_packets==="all",O=(h==="none"||h==="only"&&v.length===0&&C.length===0||h==="except"&&a.length>0&&L.length>0&&v.length>=a.length&&C.length>=L.length)&&!q,W=u=>v.includes(u),G=u=>C.includes(u),m=h==="only"?"Newly added channels or contacts will not be automatically included.":"Newly added channels or contacts will be automatically included unless excluded here.",A=h==="except"?"exclude":"include",X=n?["all","none","only","except"]:["all","only","except"];return e.jsxs("div",{className:"space-y-3",children:[e.jsx(c,{children:"Message Scope"}),n&&e.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:q,onChange:u=>t({...s,raw_packets:u.target.checked?"all":"none"}),className:"h-4 w-4 rounded border-border"}),e.jsx("span",{className:"text-sm",children:"Forward raw packets"})]}),e.jsx("div",{className:"space-y-1",children:X.map(u=>e.jsxs("label",{className:"flex items-center gap-2 cursor-pointer",children:[e.jsx("input",{type:"radio",name:"scope-mode",checked:h===u,onChange:()=>I(u),className:"h-4 w-4 accent-primary"}),e.jsx("span",{className:"text-sm",children:R[u]})]},u))}),O&&e.jsx("div",{className:"rounded-md border border-warning/50 bg-warning/10 px-3 py-2 text-xs text-warning",children:"Nothing is selected — this integration will not forward any data."}),p&&e.jsxs(e.Fragment,{children:[e.jsx("p",{className:"text-xs text-muted-foreground",children:m}),a.length>0&&e.jsxs("div",{className:"space-y-1",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs(c,{className:"text-xs",children:["Channels"," ",e.jsxs("span",{className:"text-muted-foreground font-normal",children:["(",A,")"]})]}),e.jsxs("span",{className:"flex gap-1",children:[e.jsx("button",{type:"button",className:"text-xs text-muted-foreground hover:text-foreground transition-colors",onClick:()=>t({...s,messages:M(a.map(u=>u.key),C)}),children:"All"}),e.jsx("span",{className:"text-xs text-muted-foreground",children:"/"}),e.jsx("button",{type:"button",className:"text-xs text-muted-foreground hover:text-foreground transition-colors",onClick:()=>t({...s,messages:M([],C)}),children:"None"})]})]}),e.jsx("div",{className:"max-h-32 overflow-y-auto border border-input rounded-md p-2 space-y-1",children:a.map(u=>e.jsxs("label",{className:"flex items-center gap-2 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:W(u.key),onChange:()=>K(u.key),className:"h-3.5 w-3.5 rounded border-input accent-primary"}),e.jsx("span",{className:"text-sm truncate",children:u.name})]},u.key))})]}),L.length>0&&e.jsxs("div",{className:"space-y-1",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs(c,{className:"text-xs",children:["Contacts"," ",e.jsxs("span",{className:"text-muted-foreground font-normal",children:["(",A,")"]})]}),e.jsxs("span",{className:"flex gap-1",children:[e.jsx("button",{type:"button",className:"text-xs text-muted-foreground hover:text-foreground transition-colors",onClick:()=>t({...s,messages:M(v,L.map(u=>u.public_key))}),children:"All"}),e.jsx("span",{className:"text-xs text-muted-foreground",children:"/"}),e.jsx("button",{type:"button",className:"text-xs text-muted-foreground hover:text-foreground transition-colors",onClick:()=>t({...s,messages:M(v,[])}),children:"None"})]})]}),e.jsx("div",{className:"max-h-32 overflow-y-auto border border-input rounded-md p-2 space-y-1",children:L.map(u=>e.jsxs("label",{className:"flex items-center gap-2 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:G(u.public_key),onChange:()=>U(u.public_key),className:"h-3.5 w-3.5 rounded border-input accent-primary"}),e.jsx("span",{className:"text-sm truncate",children:u.name||u.public_key.substring(0,12)+"..."})]},u.public_key))})]})]})]})}function Ks({config:s,scope:t,onChange:n,onScopeChange:a}){return e.jsxs("div",{className:"space-y-3",children:[e.jsxs("p",{className:"text-xs text-muted-foreground",children:["Send push notifications via"," ",e.jsx("a",{href:"https://github.com/caronc/apprise",target:"_blank",rel:"noopener noreferrer",className:"underline hover:text-foreground",children:"Apprise"})," ","when messages are received. Supports Discord, Slack, Telegram, email, and"," ",e.jsx("a",{href:"https://github.com/caronc/apprise/wiki#supported-notifications",target:"_blank",rel:"noopener noreferrer",className:"underline hover:text-foreground",children:"100+ other services"}),"."]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-apprise-urls",children:"Notification URLs"}),e.jsx("textarea",{id:"fanout-apprise-urls",className:"w-full rounded-md border border-input bg-background px-3 py-2 text-sm font-mono min-h-[80px]",placeholder:`discord://webhook_id/token
|
||
slack://token_a/token_b/token_c
|
||
tgram://bot_token/chat_id`,value:s.urls||"",onChange:d=>n({...s,urls:d.target.value}),rows:4}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"One URL per line. All URLs receive every matched notification."})]}),e.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:s.preserve_identity!==!1,onChange:d=>n({...s,preserve_identity:d.target.checked}),className:"h-4 w-4 rounded border-border"}),e.jsxs("div",{children:[e.jsx("span",{className:"text-sm",children:"Preserve identity on Discord"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"When enabled, Discord webhooks will use their configured name/avatar instead of overriding with MeshCore sender info."})]})]}),e.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:s.include_path!==!1,onChange:d=>n({...s,include_path:d.target.checked}),className:"h-4 w-4 rounded border-border"}),e.jsx("span",{className:"text-sm",children:"Include routing path in notifications"})]}),e.jsx(b,{}),e.jsx(de,{scope:t,onChange:a})]})}function Ws({config:s,scope:t,onChange:n,onScopeChange:a}){const d=JSON.stringify(s.headers??{},null,2),[w,S]=o.useState(d),[j,_]=o.useState(null),h=p=>{S(p);try{const v=JSON.parse(p);if(typeof v!="object"||Array.isArray(v)){_("Must be a JSON object");return}_(null),n({...s,headers:v})}catch{_("Invalid JSON")}};return e.jsxs("div",{className:"space-y-3",children:[e.jsx("p",{className:"text-xs text-muted-foreground",children:"Send message data as JSON to an HTTP endpoint when messages are received."}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-webhook-url",children:"URL"}),e.jsx(x,{id:"fanout-webhook-url",type:"url",placeholder:"https://example.com/webhook",value:s.url||"",onChange:p=>n({...s,url:p.target.value})})]}),e.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4",children:e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-webhook-method",children:"HTTP Method"}),e.jsxs("select",{id:"fanout-webhook-method",value:s.method||"POST",onChange:p=>n({...s,method:p.target.value}),className:"w-full rounded-md border border-input bg-background px-3 py-2 text-sm",children:[e.jsx("option",{value:"POST",children:"POST"}),e.jsx("option",{value:"PUT",children:"PUT"}),e.jsx("option",{value:"PATCH",children:"PATCH"})]})]})}),e.jsx(b,{}),e.jsxs("div",{className:"space-y-3",children:[e.jsx(c,{children:"HMAC Signing"}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:["When a secret is set, each request includes an HMAC-SHA256 signature of the JSON body in the specified header (e.g. ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"sha256=ab12cd..."}),")."]}),e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-webhook-hmac-secret",children:"HMAC Secret"}),e.jsx(x,{id:"fanout-webhook-hmac-secret",type:"password",placeholder:"Leave empty to disable signing",value:s.hmac_secret||"",onChange:p=>n({...s,hmac_secret:p.target.value})})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-webhook-hmac-header",children:"Signature Header Name"}),e.jsx(x,{id:"fanout-webhook-hmac-header",type:"text",placeholder:"X-Webhook-Signature",value:s.hmac_header||"",onChange:p=>n({...s,hmac_header:p.target.value})})]})]})]}),e.jsx(b,{}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-webhook-headers",children:"Extra Headers (JSON)"}),e.jsx("textarea",{id:"fanout-webhook-headers",className:"w-full rounded-md border border-input bg-background px-3 py-2 text-sm font-mono min-h-[60px]",value:w,onChange:p=>h(p.target.value),placeholder:'{"Authorization": "Bearer ..."}'}),j&&e.jsx("p",{className:"text-xs text-destructive",children:j})]}),e.jsx(b,{}),e.jsx(de,{scope:t,onChange:a})]})}function Gs({config:s,scope:t,onChange:n,onScopeChange:a}){return e.jsxs("div",{className:"space-y-3",children:[e.jsx("p",{className:"text-xs text-muted-foreground",children:"Send matched mesh events to an Amazon SQS queue for durable processing by workers, Lambdas, or downstream automation."}),e.jsx("div",{className:"rounded-md border border-warning/50 bg-warning/10 px-3 py-2 text-xs text-warning",children:"Outgoing messages and any selected raw packets will be delivered exactly as forwarded by the fanout scope, including decrypted/plaintext message content."}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-sqs-queue-url",children:"Queue URL"}),e.jsx(x,{id:"fanout-sqs-queue-url",type:"url",placeholder:"https://sqs.us-east-1.amazonaws.com/123456789012/mesh-events",value:s.queue_url||"",onChange:d=>n({...s,queue_url:d.target.value})})]}),e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-sqs-region",children:"Region (optional)"}),e.jsx(x,{id:"fanout-sqs-region",type:"text",placeholder:"us-east-1",value:s.region_name||"",onChange:d=>n({...s,region_name:d.target.value})})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-sqs-endpoint",children:"Endpoint URL (optional)"}),e.jsx(x,{id:"fanout-sqs-endpoint",type:"url",placeholder:"http://localhost:4566",value:s.endpoint_url||"",onChange:d=>n({...s,endpoint_url:d.target.value})}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Useful for LocalStack or custom endpoints"})]})]}),e.jsx(b,{}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{children:"Static Credentials (optional)"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Leave blank to use the server's normal AWS credential chain."})]}),e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-sqs-access-key",children:"Access Key ID"}),e.jsx(x,{id:"fanout-sqs-access-key",type:"text",value:s.access_key_id||"",onChange:d=>n({...s,access_key_id:d.target.value})})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-sqs-secret-key",children:"Secret Access Key"}),e.jsx(x,{id:"fanout-sqs-secret-key",type:"password",value:s.secret_access_key||"",onChange:d=>n({...s,secret_access_key:d.target.value})})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-sqs-session-token",children:"Session Token (optional)"}),e.jsx(x,{id:"fanout-sqs-session-token",type:"password",value:s.session_token||"",onChange:d=>n({...s,session_token:d.target.value})})]}),e.jsx(b,{}),e.jsx(de,{scope:t,onChange:a,showRawPackets:!0})]})}function Ys({health:s,onHealthRefresh:t,className:n}){const[a,d]=o.useState([]),[w,S]=o.useState(null),[j,_]=o.useState(null),[h,p]=o.useState({}),[v,C]=o.useState({}),[M,I]=o.useState(""),[K,U]=o.useState(null),[L,R]=o.useState(""),[q,B]=o.useState(!1),[O,W]=o.useState(!1),G=o.useRef(null),m=o.useCallback(async()=>{try{const i=await V.getFanoutConfigs();d(i)}catch(i){console.error("Failed to load fanout configs:",i)}},[]);o.useEffect(()=>{m()},[m]),o.useEffect(()=>{if(!q)return;const i=l=>{var f;(f=G.current)!=null&&f.contains(l.target)||B(!1)};return document.addEventListener("mousedown",i),()=>document.removeEventListener("mousedown",i)},[q]);const A=async i=>{try{await V.updateFanoutConfig(i.id,{enabled:!i.enabled}),await m(),t&&await t(),N.success(i.enabled?"Integration disabled":"Integration enabled")}catch(l){N.error(l instanceof Error?l.message:"Failed to update")}},X=i=>{B(!1),U(null),R(""),_(null),S(i.id),p(i.config),C(i.scope),I(i.name)},u=i=>{B(!1),U(i.id),R(i.name)},y=()=>{U(null),R("")},T=()=>{confirm("Leave without saving?")&&(S(null),_(null))},H=async i=>{const l=L.trim();if(K===i.id){if(!l){N.error("Name cannot be empty"),y();return}if(l===i.name){y();return}try{await V.updateFanoutConfig(i.id,{name:l}),w===i.id&&I(l),await m(),N.success("Name updated")}catch(f){N.error(f instanceof Error?f.message:"Failed to update name")}finally{y()}}},se=async i=>{const l=j,f=w;if(!(!f&&!l)){W(!0);try{if(l)await V.createFanoutConfig({type:l,name:M,config:h,scope:v,enabled:i??!0});else{if(!f)throw new Error("Missing fanout config id for update");const g={name:M,config:h,scope:v};i!==void 0&&(g.enabled=i),await V.updateFanoutConfig(f,g)}if(_(null),S(null),await m(),t)try{await t()}catch(g){console.error("Failed to refresh health after saving fanout config:",g)}N.success(i?"Integration saved and enabled":"Integration saved")}catch(g){N.error(g instanceof Error?g.message:"Failed to save")}finally{W(!1)}}},re=async i=>{const l=a.find(f=>f.id===i);if(confirm(`Delete "${l==null?void 0:l.name}"? This cannot be undone.`))try{await V.deleteFanoutConfig(i),w===i&&S(null),await m(),t&&await t(),N.success("Integration deleted")}catch(f){N.error(f instanceof Error?f.message:"Failed to delete")}},Y=async i=>{const l={mqtt_private:{broker_host:"",broker_port:1883,username:"",password:"",use_tls:!1,tls_insecure:!1,topic_prefix:"meshcore"},mqtt_community:{broker_host:"mqtt-us-v1.letsmesh.net",broker_port:ce,transport:es,use_tls:!0,tls_verify:!0,auth_mode:ss,username:"",password:"",iata:"",email:"",token_audience:"",topic_template:ie},bot:{code:ts},webhook:{url:"",method:"POST",headers:{},hmac_secret:"",hmac_header:""},apprise:{urls:"",preserve_identity:!0,include_path:!0},sqs:{queue_url:"",region_name:"",endpoint_url:"",access_key_id:"",secret_access_key:"",session_token:""}},f={mqtt_private:{messages:"all",raw_packets:"all"},mqtt_community:{messages:"none",raw_packets:"all"},bot:{messages:"all",raw_packets:"none"},webhook:{messages:"all",raw_packets:"none"},apprise:{messages:"all",raw_packets:"none"},sqs:{messages:"all",raw_packets:"none"}};B(!1),S(null),_(i),I(Bs(i,a)),p(l[i]||{}),C(f[i]||{})},Q=w?a.find(i=>i.id===w):null,E=j??(Q==null?void 0:Q.type)??null,J=j!==null,z=Ye.map(i=>({type:i.value,label:i.label,configs:a.filter(l=>l.type===i.value).sort((l,f)=>l.name.localeCompare(f.name,void 0,{sensitivity:"base"}))})).filter(i=>i.configs.length>0);return E?e.jsxs("div",{className:xe("mx-auto w-full max-w-[800px] space-y-4",n),children:[e.jsx("button",{type:"button",className:"inline-flex items-center rounded-md border border-warning/50 bg-warning/10 px-3 py-2 text-sm text-warning transition-colors hover:bg-warning/20",onClick:T,children:"← Back to list"}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(c,{htmlFor:"fanout-edit-name",children:"Name"}),e.jsx(x,{id:"fanout-edit-name",type:"text",value:M,onChange:i=>I(i.target.value)})]}),e.jsxs("div",{className:"text-xs text-muted-foreground",children:["Type: ",Ze[E]||E]}),e.jsx(b,{}),E==="mqtt_private"&&e.jsx(Us,{config:h,scope:v,onChange:p,onScopeChange:C}),E==="mqtt_community"&&e.jsx(Hs,{config:h,onChange:p}),E==="bot"&&e.jsx(zs,{config:h,onChange:p}),E==="apprise"&&e.jsx(Ks,{config:h,scope:v,onChange:p,onScopeChange:C}),E==="webhook"&&e.jsx(Ws,{config:h,scope:v,onChange:p,onScopeChange:C}),E==="sqs"&&e.jsx(Gs,{config:h,scope:v,onChange:p,onScopeChange:C}),e.jsx(b,{}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(F,{onClick:()=>se(!0),disabled:O,className:"flex-1 bg-status-connected hover:bg-status-connected/90 text-primary-foreground",children:O?"Saving...":"Save as Enabled"}),e.jsx(F,{variant:"secondary",onClick:()=>se(!1),disabled:O,className:"flex-1",children:O?"Saving...":"Save as Disabled"}),!J&&Q&&e.jsx(F,{variant:"destructive",onClick:()=>re(Q.id),children:"Delete"})]})]}):e.jsxs("div",{className:xe("mx-auto w-full max-w-[800px] space-y-4",n),children:[e.jsx("div",{className:"rounded-md border border-warning/50 bg-warning/10 px-4 py-3 text-sm text-warning",children:"Integrations are an experimental feature in open beta, and allow you to fanout raw and decrypted messages across multiple services for automation, analysis, or archiving."}),(s==null?void 0:s.bots_disabled)&&e.jsx("div",{className:"rounded-md border border-destructive/50 bg-destructive/10 px-4 py-3 text-sm text-destructive",children:"Bot system is disabled by server configuration (MESHCORE_DISABLE_BOTS). Bot integrations cannot be created or modified."}),e.jsxs("div",{className:"relative inline-block",ref:G,children:[e.jsx(F,{type:"button",size:"sm","aria-haspopup":"menu","aria-expanded":q,onClick:()=>B(i=>!i),children:"Add Integration"}),q&&e.jsx("div",{role:"menu",className:"absolute left-0 top-full z-10 mt-2 min-w-56 rounded-md border border-input bg-background p-1 shadow-md",children:Ye.filter(i=>i.value!=="bot"||!(s!=null&&s.bots_disabled)).map(i=>e.jsx("button",{type:"button",role:"menuitem",className:"flex w-full rounded-sm px-3 py-2 text-left text-sm hover:bg-muted",onClick:()=>Y(i.value),children:i.label},i.value))})]}),z.length>0&&e.jsx("div",{className:"columns-1 gap-4 md:columns-2",children:z.map(i=>e.jsxs("section",{className:"mb-4 inline-block w-full break-inside-avoid space-y-2","aria-label":`${i.label} integrations`,children:[e.jsx("div",{className:"px-1 text-sm font-medium text-muted-foreground",children:i.label}),e.jsx("div",{className:"space-y-2",children:i.configs.map(l=>{var Z;const f=(Z=s==null?void 0:s.fanout_statuses)==null?void 0:Z[l.id],g=l.enabled?f==null?void 0:f.status:void 0,D=l.config;return e.jsxs("div",{role:"group","aria-label":`Integration ${l.name}`,className:"border border-input rounded-md overflow-hidden",children:[e.jsxs("div",{className:"flex items-center gap-2 px-3 py-2 bg-muted/50",children:[e.jsx("label",{className:"flex items-center cursor-pointer",onClick:P=>P.stopPropagation(),children:e.jsx("input",{type:"checkbox",checked:l.enabled,onChange:()=>A(l),className:"w-4 h-4 rounded border-input accent-primary","aria-label":`Enable ${l.name}`})}),e.jsx("div",{className:"flex-1 min-w-0",children:K===l.id?e.jsx(x,{value:L,autoFocus:!0,onChange:P=>R(P.target.value),onFocus:P=>P.currentTarget.select(),onBlur:()=>void H(l),onKeyDown:P=>{P.key==="Enter"&&(P.preventDefault(),P.currentTarget.blur()),P.key==="Escape"&&(P.preventDefault(),y())},"aria-label":`Edit name for ${l.name}`,className:"h-8"}):e.jsx("button",{type:"button",className:"block max-w-full cursor-text truncate text-left text-sm font-medium hover:text-foreground/80",onClick:()=>u(l),children:l.name})}),e.jsx("div",{className:xe("w-2 h-2 rounded-full transition-colors",Os(g,l.enabled)),title:l.enabled?Je(g,l.type):"Disabled","aria-hidden":"true"}),e.jsx("span",{className:"text-xs text-muted-foreground hidden sm:inline",children:l.enabled?Je(g,l.type):"Disabled"}),e.jsx(F,{type:"button",variant:"ghost",size:"sm",className:"h-6 px-2 text-xs",onClick:()=>X(l),children:"Edit"})]}),l.type==="mqtt_community"&&e.jsxs("div",{className:"space-y-1 border-t border-input px-3 py-2 text-xs text-muted-foreground",children:[e.jsxs("div",{children:["Broker:"," ",Qe(D,{host:oe,port:ce})]}),e.jsxs("div",{className:"break-all",children:["Topic:"," ",e.jsx("code",{children:D.topic_template||ie})]})]}),l.type==="mqtt_private"&&e.jsxs("div",{className:"space-y-1 border-t border-input px-3 py-2 text-xs text-muted-foreground",children:[e.jsxs("div",{children:["Broker:"," ",Qe(l.config,{host:"",port:1883})]}),e.jsxs("div",{className:"break-all",children:["Topics:"," ",e.jsx("code",{children:Rs(l.config)})]})]}),l.type==="webhook"&&e.jsx("div",{className:"space-y-1 border-t border-input px-3 py-2 text-xs text-muted-foreground",children:e.jsxs("div",{className:"break-all",children:["URL:"," ",e.jsx("code",{children:l.config.url||"Not set"})]})}),l.type==="apprise"&&e.jsx("div",{className:"space-y-1 border-t border-input px-3 py-2 text-xs text-muted-foreground",children:e.jsxs("div",{className:"break-all",children:["Targets:"," ",e.jsx("code",{children:Ds(l.config.urls)})]})}),l.type==="sqs"&&e.jsx("div",{className:"space-y-1 border-t border-input px-3 py-2 text-xs text-muted-foreground",children:e.jsxs("div",{className:"break-all",children:["Queue:"," ",e.jsx("code",{children:Is(l.config)})]})})]},l.id)})})]},i.type))})]})}function Qs({appSettings:s,health:t,onSaveAppSettings:n,onHealthRefresh:a,blockedKeys:d=[],blockedNames:w=[],onToggleBlockedKey:S,onToggleBlockedName:j,className:_}){const[h,p]=o.useState("14"),[v,C]=o.useState(!1),[M,I]=o.useState(!1),[K,U]=o.useState(!1),[L,R]=o.useState(!1),[q,B]=o.useState(null);o.useEffect(()=>{U(s.auto_decrypt_dm_on_advert)},[s]);const O=async()=>{const m=parseInt(h,10);if(isNaN(m)||m<1){N.error("Invalid retention days",{description:"Retention days must be at least 1"});return}C(!0);try{const A=await V.runMaintenance({pruneUndecryptedDays:m});N.success("Database cleanup complete",{description:`Deleted ${A.packets_deleted} old packet${A.packets_deleted===1?"":"s"}`}),await a()}catch(A){console.error("Failed to run maintenance:",A),N.error("Database cleanup failed",{description:A instanceof Error?A.message:"Unknown error"})}finally{C(!1)}},W=async()=>{I(!0);try{const m=await V.runMaintenance({purgeLinkedRawPackets:!0});N.success("Decrypted raw packets purged",{description:`Deleted ${m.packets_deleted} raw packet${m.packets_deleted===1?"":"s"}`}),await a()}catch(m){console.error("Failed to purge decrypted raw packets:",m),N.error("Failed to purge decrypted raw packets",{description:m instanceof Error?m.message:"Unknown error"})}finally{I(!1)}},G=async()=>{R(!0),B(null);try{await n({auto_decrypt_dm_on_advert:K}),N.success("Database settings saved")}catch(m){console.error("Failed to save database settings:",m),B(m instanceof Error?m.message:"Failed to save"),N.error("Failed to save settings")}finally{R(!1)}};return e.jsxs("div",{className:_,children:[e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"flex justify-between items-center",children:[e.jsx("span",{className:"text-sm text-muted-foreground",children:"Database size"}),e.jsxs("span",{className:"font-medium",children:[(t==null?void 0:t.database_size_mb)??"?"," MB"]})]}),t!=null&&t.oldest_undecrypted_timestamp?e.jsxs("div",{className:"flex justify-between items-center",children:[e.jsx("span",{className:"text-sm text-muted-foreground",children:"Oldest undecrypted packet"}),e.jsxs("span",{className:"font-medium",children:[ws(t.oldest_undecrypted_timestamp),e.jsxs("span",{className:"text-muted-foreground ml-1",children:["(",Math.floor((Date.now()/1e3-t.oldest_undecrypted_timestamp)/86400)," ","days old)"]})]})]}):e.jsxs("div",{className:"flex justify-between items-center",children:[e.jsx("span",{className:"text-sm text-muted-foreground",children:"Oldest undecrypted packet"}),e.jsx("span",{className:"text-muted-foreground",children:"None"})]})]}),e.jsx(b,{}),e.jsxs("div",{className:"space-y-3",children:[e.jsx(c,{children:"Delete Undecrypted Packets"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Permanently deletes stored raw packets containing DMs and channel messages that have not yet been decrypted. These packets are retained in case you later obtain the correct key — once deleted, these messages can never be recovered or decrypted."}),e.jsxs("div",{className:"flex gap-2 items-end",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx(c,{htmlFor:"retention-days",className:"text-xs",children:"Older than (days)"}),e.jsx(x,{id:"retention-days",type:"number",min:"1",max:"365",value:h,onChange:m=>p(m.target.value),className:"w-24"})]}),e.jsx(F,{variant:"outline",onClick:O,disabled:v,className:"border-destructive/50 text-destructive hover:bg-destructive/10",children:v?"Deleting...":"Permanently Delete"})]})]}),e.jsx(b,{}),e.jsxs("div",{className:"space-y-3",children:[e.jsx(c,{children:"Purge Archival Raw Packets"}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:["Deletes archival copies of raw packet bytes for messages that are already decrypted and visible in your chat history."," ",e.jsx("em",{className:"text-muted-foreground/80",children:"This will not affect any displayed messages or app functionality."})," ","The raw bytes are only useful for manual packet analysis."]}),e.jsx(F,{variant:"outline",onClick:W,disabled:M,className:"w-full border-warning/50 text-warning hover:bg-warning/10",children:M?"Purging Archival Raw Packets...":"Purge Archival Raw Packets"})]}),e.jsx(b,{}),e.jsxs("div",{className:"space-y-3",children:[e.jsx(c,{children:"DM Decryption"}),e.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:K,onChange:m=>U(m.target.checked),className:"w-4 h-4 rounded border-input accent-primary"}),e.jsx("span",{className:"text-sm",children:"Auto-decrypt historical DMs when new contact advertises"})]}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"When enabled, the server will automatically try to decrypt stored DM packets when a new contact sends an advertisement. This may cause brief delays on large packet backlogs."})]}),e.jsx(b,{}),e.jsxs("div",{className:"space-y-3",children:[e.jsx(c,{children:"Blocked Contacts"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Blocking only hides messages from the UI. MQTT forwarding and bot responses are not affected. Messages are still stored and will reappear if unblocked."}),d.length===0&&w.length===0?e.jsx("p",{className:"text-sm text-muted-foreground italic",children:"No blocked contacts"}):e.jsxs("div",{className:"space-y-2",children:[d.length>0&&e.jsxs("div",{children:[e.jsx("span",{className:"text-xs text-muted-foreground font-medium",children:"Blocked Keys"}),e.jsx("div",{className:"mt-1 space-y-1",children:d.map(m=>e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"text-xs font-mono truncate flex-1",children:m}),S&&e.jsx(F,{variant:"ghost",size:"sm",onClick:()=>S(m),className:"h-7 text-xs flex-shrink-0",children:"Unblock"})]},m))})]}),w.length>0&&e.jsxs("div",{children:[e.jsx("span",{className:"text-xs text-muted-foreground font-medium",children:"Blocked Names"}),e.jsx("div",{className:"mt-1 space-y-1",children:w.map(m=>e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"text-sm truncate flex-1",children:m}),j&&e.jsx(F,{variant:"ghost",size:"sm",onClick:()=>j(m),className:"h-7 text-xs flex-shrink-0",children:"Unblock"})]},m))})]})]})]}),q&&e.jsx("div",{className:"text-sm text-destructive",role:"alert",children:q}),e.jsx(F,{onClick:G,disabled:L,className:"w-full",children:L?"Saving...":"Save Settings"})]})}function fe(s){return`${s.toFixed(1)}%`}function Js({className:s}){const[t,n]=o.useState(null),[a,d]=o.useState(!1),[w,S]=o.useState(!1);return o.useEffect(()=>{let j=!1;return d(!0),S(!1),V.getStatistics().then(_=>{j||(n(_),d(!1))},()=>{j||(S(!0),d(!1))}),()=>{j=!0}},[]),e.jsx("div",{className:s,children:a&&!t?e.jsx("div",{className:"py-8 text-center text-muted-foreground",children:"Loading statistics... this can take a while if you have a lot of stored packets."}):t?e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm font-medium mb-2",children:"Network"}),e.jsxs("div",{className:"grid grid-cols-3 gap-3",children:[e.jsxs("div",{className:"text-center p-3 bg-muted/50 rounded-md",children:[e.jsx("div",{className:"text-2xl font-bold",children:t.contact_count}),e.jsx("div",{className:"text-xs text-muted-foreground",children:"Contacts"})]}),e.jsxs("div",{className:"text-center p-3 bg-muted/50 rounded-md",children:[e.jsx("div",{className:"text-2xl font-bold",children:t.repeater_count}),e.jsx("div",{className:"text-xs text-muted-foreground",children:"Repeaters"})]}),e.jsxs("div",{className:"text-center p-3 bg-muted/50 rounded-md",children:[e.jsx("div",{className:"text-2xl font-bold",children:t.channel_count}),e.jsx("div",{className:"text-xs text-muted-foreground",children:"Channels"})]})]})]}),e.jsx(b,{}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm font-medium mb-2",children:"Messages"}),e.jsxs("div",{className:"grid grid-cols-3 gap-3",children:[e.jsxs("div",{className:"text-center p-3 bg-muted/50 rounded-md",children:[e.jsx("div",{className:"text-2xl font-bold",children:t.total_dms}),e.jsx("div",{className:"text-xs text-muted-foreground",children:"Direct Messages"})]}),e.jsxs("div",{className:"text-center p-3 bg-muted/50 rounded-md",children:[e.jsx("div",{className:"text-2xl font-bold",children:t.total_channel_messages}),e.jsx("div",{className:"text-xs text-muted-foreground",children:"Channel Messages"})]}),e.jsxs("div",{className:"text-center p-3 bg-muted/50 rounded-md",children:[e.jsx("div",{className:"text-2xl font-bold",children:t.total_outgoing}),e.jsx("div",{className:"text-xs text-muted-foreground",children:"Sent (Outgoing)"})]})]})]}),e.jsx(b,{}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm font-medium mb-2",children:"Packets"}),e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex justify-between items-center",children:[e.jsx("span",{className:"text-sm text-muted-foreground",children:"Total stored"}),e.jsx("span",{className:"font-medium",children:t.total_packets})]}),e.jsxs("div",{className:"flex justify-between items-center",children:[e.jsx("span",{className:"text-sm text-success",children:"Decrypted"}),e.jsx("span",{className:"font-medium text-success",children:t.decrypted_packets})]}),e.jsxs("div",{className:"flex justify-between items-center",children:[e.jsx("span",{className:"text-sm text-warning",children:"Undecrypted"}),e.jsx("span",{className:"font-medium text-warning",children:t.undecrypted_packets})]})]})]}),e.jsx(b,{}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm font-medium mb-2",children:"Path Hash Width (24h)"}),e.jsxs("div",{className:"mb-2 text-xs text-muted-foreground",children:["Parsed stored raw packets from the last 24 hours:"," ",t.path_hash_width_24h.total_packets]}),e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex justify-between items-center text-sm",children:[e.jsx("span",{children:"1-byte hops"}),e.jsxs("span",{className:"text-muted-foreground",children:[t.path_hash_width_24h.single_byte," (",fe(t.path_hash_width_24h.single_byte_pct),")"]})]}),e.jsxs("div",{className:"flex justify-between items-center text-sm",children:[e.jsx("span",{children:"2-byte hops"}),e.jsxs("span",{className:"text-muted-foreground",children:[t.path_hash_width_24h.double_byte," (",fe(t.path_hash_width_24h.double_byte_pct),")"]})]}),e.jsxs("div",{className:"flex justify-between items-center text-sm",children:[e.jsx("span",{children:"3-byte hops"}),e.jsxs("span",{className:"text-muted-foreground",children:[t.path_hash_width_24h.triple_byte," (",fe(t.path_hash_width_24h.triple_byte_pct),")"]})]})]})]}),e.jsx(b,{}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm font-medium mb-2",children:"Activity"}),e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"text-muted-foreground",children:[e.jsx("th",{className:"text-left font-normal pb-1"}),e.jsx("th",{className:"text-right font-normal pb-1",children:"1h"}),e.jsx("th",{className:"text-right font-normal pb-1",children:"24h"}),e.jsx("th",{className:"text-right font-normal pb-1",children:"7d"})]})}),e.jsxs("tbody",{children:[e.jsxs("tr",{children:[e.jsx("td",{className:"py-1",children:"Contacts heard"}),e.jsx("td",{className:"text-right py-1",children:t.contacts_heard.last_hour}),e.jsx("td",{className:"text-right py-1",children:t.contacts_heard.last_24_hours}),e.jsx("td",{className:"text-right py-1",children:t.contacts_heard.last_week})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"py-1",children:"Repeaters heard"}),e.jsx("td",{className:"text-right py-1",children:t.repeaters_heard.last_hour}),e.jsx("td",{className:"text-right py-1",children:t.repeaters_heard.last_24_hours}),e.jsx("td",{className:"text-right py-1",children:t.repeaters_heard.last_week})]})]})]})]}),t.busiest_channels_24h.length>0&&e.jsxs(e.Fragment,{children:[e.jsx(b,{}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm font-medium mb-2",children:"Busiest Channels (24h)"}),e.jsx("div",{className:"space-y-1",children:t.busiest_channels_24h.map((j,_)=>e.jsxs("div",{className:"flex justify-between items-center text-sm",children:[e.jsxs("span",{children:[e.jsxs("span",{className:"text-muted-foreground mr-2",children:[_+1,"."]}),j.channel_name]}),e.jsxs("span",{className:"text-muted-foreground",children:[j.message_count," msgs"]})]},j.channel_key))})]})]})]}):w?e.jsx("div",{className:"py-8 text-center text-muted-foreground",children:"Failed to load statistics."}):null})}const ne="https://github.com/jkingsman/Remote-Terminal-for-MeshCore";function Vs({className:s}){return e.jsx("div",{className:s,children:e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{className:"text-center space-y-1",children:[e.jsx("h3",{className:"text-lg font-semibold",children:"RemoteTerm for MeshCore"}),e.jsxs("div",{className:"text-sm text-muted-foreground",children:["v","3.3.0",e.jsx("span",{className:"mx-1.5",children:"·"}),e.jsx("span",{className:"font-mono text-xs",children:"2936896"})]})]}),e.jsx(b,{}),e.jsxs("div",{className:"text-sm text-center space-y-2",children:[e.jsxs("p",{children:["Made with love and open source by"," ",e.jsx("a",{href:"https://jacksbrain.com",target:"_blank",rel:"noopener noreferrer",className:"text-primary hover:underline",children:"Jack Kingsman"})]}),e.jsxs("p",{children:["Licensed under the"," ",e.jsx("a",{href:`${ne}/blob/main/LICENSE.md`,target:"_blank",rel:"noopener noreferrer",className:"text-primary hover:underline",children:"MIT License"})]}),e.jsxs("p",{children:["This code is free, and ad-free, forever. If you love my work,"," ",e.jsx("a",{href:"https://ko-fi.com/jackkingsman",target:"_blank",rel:"noopener noreferrer",className:"text-primary hover:underline",children:"buy me a coffee!"})]})]}),e.jsx(b,{}),e.jsxs("div",{className:"flex justify-center gap-4 text-sm",children:[e.jsx("a",{href:ne,target:"_blank",rel:"noopener noreferrer",className:"text-primary hover:underline",children:"GitHub"}),e.jsx("a",{href:`${ne}/issues`,target:"_blank",rel:"noopener noreferrer",className:"text-primary hover:underline",children:"Report a Bug"}),e.jsx("a",{href:`${ne}/blob/main/CHANGELOG.md`,target:"_blank",rel:"noopener noreferrer",className:"text-primary hover:underline",children:"Changelog"})]}),e.jsx(b,{}),e.jsxs("div",{className:"text-sm text-center text-muted-foreground space-y-2",children:[e.jsx("p",{children:"With great appreciation to those who have made the tools upon which this is built:"}),e.jsxs("p",{children:[e.jsx("a",{href:"https://github.com/meshcore-dev/MeshCore",target:"_blank",rel:"noopener noreferrer",className:"text-primary hover:underline",children:"MeshCore"}),e.jsx("span",{className:"mx-1.5",children:"·"}),e.jsx("a",{href:"https://github.com/meshcore-dev/meshcore_py",target:"_blank",rel:"noopener noreferrer",className:"text-primary hover:underline",children:"meshcore_py"})]})]}),e.jsx(b,{}),e.jsx("div",{className:"text-center",children:e.jsx("a",{href:"/api/debug",target:"_blank",rel:"noopener noreferrer",className:"text-xs text-muted-foreground hover:text-primary hover:underline",children:"Open debug support snapshot"})})]})})}function et(s){const{open:t,pageMode:n=!1,config:a,health:d,appSettings:w,onClose:S,onSave:j,onSaveAppSettings:_,onSetPrivateKey:h,onReboot:p,onDisconnect:v,onReconnect:C,onAdvertise:M,meshDiscovery:I,meshDiscoveryLoadingTarget:K,onDiscoverMesh:U,onHealthRefresh:L,onRefreshAppSettings:R,onLocalLabelChange:q,blockedKeys:B,blockedNames:O,onToggleBlockedKey:W,onToggleBlockedName:G}=s,m=s.externalSidebarNav===!0,A=s.externalSidebarNav?s.desktopSection:void 0,X=()=>typeof window>"u"||typeof window.matchMedia!="function"?!1:window.matchMedia("(max-width: 767px)").matches,[u,y]=o.useState(X),T=m&&!u,[H,se]=o.useState({radio:!1,local:!1,fanout:!1,database:!1,statistics:!1,about:!1});o.useEffect(()=>{(t||n)&&R()},[t,n,R]),o.useEffect(()=>{if(typeof window>"u"||typeof window.matchMedia!="function")return;const g=window.matchMedia("(max-width: 767px)"),D=Z=>{y(Z.matches)};return y(g.matches),typeof g.addEventListener=="function"?(g.addEventListener("change",D),()=>g.removeEventListener("change",D)):(g.addListener(D),()=>g.removeListener(D))},[]);const re=g=>{se(D=>({...D,[g]:!D[g]}))},Y=g=>T?A===g:H[g],Q=!T,E=g=>!T||A===g,J="",z=T?"mx-auto w-full max-w-[800px] space-y-4 p-4":"mx-auto w-full max-w-[800px] space-y-4 border-t border-input p-4",i=T?"w-full h-full overflow-y-auto":"w-full h-full overflow-y-auto space-y-3",l="w-full flex items-center justify-between px-4 py-3 text-left hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset",f=g=>{if(!Q)return null;const D=_s[g];return e.jsxs("button",{type:"button",className:l,"aria-expanded":H[g],onClick:()=>re(g),children:[e.jsxs("span",{className:"inline-flex items-center gap-2 font-medium",role:"heading","aria-level":3,children:[e.jsx(D,{className:"h-4 w-4 text-muted-foreground","aria-hidden":"true"}),e.jsx("span",{children:ks[g]})]}),e.jsx("span",{className:"text-muted-foreground md:hidden","aria-hidden":"true",children:H[g]?"−":"+"})]})};return!n&&!t?null:a?e.jsxs("div",{className:i,children:[E("radio")&&e.jsxs("section",{className:J,children:[f("radio"),Y("radio")&&w&&e.jsx(Fs,{config:a,health:d,appSettings:w,pageMode:n,onSave:j,onSaveAppSettings:_,onSetPrivateKey:h,onReboot:p,onDisconnect:v,onReconnect:C,onAdvertise:M,meshDiscovery:I,meshDiscoveryLoadingTarget:K,onDiscoverMesh:U,onClose:S,className:z})]}),E("local")&&e.jsxs("section",{className:J,children:[f("local"),Y("local")&&e.jsx(Ls,{onLocalLabelChange:q,className:z})]}),E("database")&&e.jsxs("section",{className:J,children:[f("database"),Y("database")&&w&&e.jsx(Qs,{appSettings:w,health:d,onSaveAppSettings:_,onHealthRefresh:L,blockedKeys:B,blockedNames:O,onToggleBlockedKey:W,onToggleBlockedName:G,className:z})]}),E("fanout")&&e.jsxs("section",{className:J,children:[f("fanout"),Y("fanout")&&e.jsx(Ys,{health:d,onHealthRefresh:L,className:z})]}),E("statistics")&&e.jsxs("section",{className:J,children:[f("statistics"),Y("statistics")&&e.jsx(Js,{className:z})]}),E("about")&&e.jsxs("section",{className:J,children:[f("about"),Y("about")&&e.jsx(Vs,{className:z})]})]}):e.jsx("div",{className:"py-8 text-center text-muted-foreground",children:"Loading configuration..."})}export{et as SettingsModal};
|
||
//# sourceMappingURL=SettingsModal-kexfpcq5.js.map
|