friend-assistant 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/BUILD.json
CHANGED
package/dist/friend.mjs
CHANGED
|
@@ -9594,7 +9594,7 @@ async function daemonCommand(args, io2, deps = realDeps) {
|
|
|
9594
9594
|
import crossSpawn5 from "cross-spawn";
|
|
9595
9595
|
var PACKAGE = "friend-assistant";
|
|
9596
9596
|
var real = {
|
|
9597
|
-
current: () => true ? "0.1.
|
|
9597
|
+
current: () => true ? "0.1.14" : "development",
|
|
9598
9598
|
latest: () => new Promise((resolve10) => {
|
|
9599
9599
|
const child = crossSpawn5("npm", ["view", PACKAGE, "version", "--prefer-online"], { stdio: ["ignore", "pipe", "ignore"] });
|
|
9600
9600
|
let out = "";
|
|
@@ -9847,7 +9847,7 @@ async function run(argv, io2) {
|
|
|
9847
9847
|
case "version":
|
|
9848
9848
|
case "--version":
|
|
9849
9849
|
case "-v":
|
|
9850
|
-
io2.out(true ? "0.1.
|
|
9850
|
+
io2.out(true ? "0.1.14" : "development");
|
|
9851
9851
|
return 0;
|
|
9852
9852
|
case "lan":
|
|
9853
9853
|
return await lanCommand(rest, io2);
|
|
@@ -4450,4 +4450,4 @@ ${this.parser.parse(e)}</blockquote>
|
|
|
4450
4450
|
${e}</tr>
|
|
4451
4451
|
`}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?`th`:`td`;return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+`</${n}>
|
|
4452
4452
|
`}strong({tokens:e}){return`<strong>${this.parser.parseInline(e)}</strong>`}em({tokens:e}){return`<em>${this.parser.parseInline(e)}</em>`}codespan({text:e}){return`<code>${IS(e,!0)}</code>`}br(e){return`<br>`}del({tokens:e}){return`<del>${this.parser.parseInline(e)}</del>`}link({href:e,title:t,text:n,tokens:r,autolink:i}){let a=i?IS(n,!0):this.parser.parseInline(r),o=RS(e);if(o===null)return a;e=IS(o,i);let s=`<a href="`+e+`"`;return t&&(s+=` title="`+IS(t)+`"`),s+=`>`+a+`</a>`,s}image({href:e,title:t,text:n,tokens:r}){r&&(n=this.parser.parseInline(r,this.parser.textRenderer));let i=RS(e);if(i===null)return IS(n);e=i;let a=`<img src="${IS(e)}" alt="${IS(n)}"`;return t&&(a+=` title="${IS(t)}"`),a+=`>`,a}text(e){return`tokens`in e&&e.tokens?this.parser.parseInline(e.tokens):`escaped`in e&&e.escaped?e.text:IS(e.text)}},ZS=class{strong({text:e}){return e}em({text:e}){return e}codespan({text:e}){return e}del({text:e}){return e}html({text:e}){return e}text({text:e}){return e}link({text:e}){return``+e}image({text:e}){return``+e}br(){return``}checkbox({raw:e}){return e}},QS=class e{options;renderer;textRenderer;constructor(e){this.options=e||fx,this.options.renderer=this.options.renderer||new XS,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new ZS}static parse(t,n){return new e(n).parse(t)}static parseInline(t,n){return new e(n).parseInline(t)}parse(e){this.renderer.parser=this;let t=``;for(let n=0;n<e.length;n++){let r=e[n];if(this.options.extensions?.renderers?.[r.type]){let e=r,n=this.options.extensions.renderers[e.type].call({parser:this},e);if(n!==!1||![`space`,`hr`,`heading`,`code`,`table`,`blockquote`,`list`,`checkbox`,`html`,`def`,`paragraph`,`text`].includes(e.type)){t+=n||``;continue}}let i=r;switch(i.type){case`space`:t+=this.renderer.space(i);break;case`hr`:t+=this.renderer.hr(i);break;case`heading`:t+=this.renderer.heading(i);break;case`code`:t+=this.renderer.code(i);break;case`table`:t+=this.renderer.table(i);break;case`blockquote`:t+=this.renderer.blockquote(i);break;case`list`:t+=this.renderer.list(i);break;case`checkbox`:t+=this.renderer.checkbox(i);break;case`html`:t+=this.renderer.html(i);break;case`def`:t+=this.renderer.def(i);break;case`paragraph`:t+=this.renderer.paragraph(i);break;case`text`:t+=this.renderer.text(i);break;default:{let e=`Token with "`+i.type+`" type was not found.`;if(this.options.silent)return console.error(e),``;throw Error(e)}}}return t}parseInline(e,t=this.renderer){this.renderer.parser=this;let n=``;for(let r=0;r<e.length;r++){let i=e[r];if(this.options.extensions?.renderers?.[i.type]){let e=this.options.extensions.renderers[i.type].call({parser:this},i);if(e!==!1||![`escape`,`html`,`link`,`image`,`checkbox`,`strong`,`em`,`codespan`,`br`,`del`,`text`].includes(i.type)){n+=e||``;continue}}let a=i;switch(a.type){case`escape`:n+=t.text(a);break;case`html`:n+=t.html(a);break;case`link`:n+=t.link(a);break;case`image`:n+=t.image(a);break;case`checkbox`:n+=t.checkbox(a);break;case`strong`:n+=t.strong(a);break;case`em`:n+=t.em(a);break;case`codespan`:n+=t.codespan(a);break;case`br`:n+=t.br(a);break;case`del`:n+=t.del(a);break;case`text`:n+=t.text(a);break;default:{let e=`Token with "`+a.type+`" type was not found.`;if(this.options.silent)return console.error(e),``;throw Error(e)}}}return n}},$S=class{options;block;constructor(e){this.options=e||fx}static passThroughHooks=new Set([`preprocess`,`postprocess`,`processAllTokens`,`emStrongMask`]);static passThroughHooksRespectAsync=new Set([`preprocess`,`postprocess`,`processAllTokens`]);preprocess(e){return e}postprocess(e){return e}processAllTokens(e){return e}emStrongMask(e){return e}provideLexer(e=this.block){return e?YS.lex:YS.lexInline}provideParser(e=this.block){return e?QS.parse:QS.parseInline}},eC=new class{defaults=dx();options=this.setOptions;parse=this.parseMarkdown(!0);parseInline=this.parseMarkdown(!1);Parser=QS;Renderer=XS;TextRenderer=ZS;Lexer=YS;Tokenizer=JS;Hooks=$S;constructor(...e){this.use(...e)}walkTokens(e,t){let n=[];for(let r of e)switch(n=n.concat(t.call(this,r)),r.type){case`table`:{let e=r;for(let r of e.header)n=n.concat(this.walkTokens(r.tokens,t));for(let r of e.rows)for(let e of r)n=n.concat(this.walkTokens(e.tokens,t));break}case`list`:{let e=r;n=n.concat(this.walkTokens(e.items,t));break}default:{let e=r;this.defaults.extensions?.childTokens?.[e.type]?this.defaults.extensions.childTokens[e.type].forEach(r=>{let i=e[r].flat(1/0);n=n.concat(this.walkTokens(i,t))}):e.tokens&&(n=n.concat(this.walkTokens(e.tokens,t)))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(e=>{let n={...e};if(n.async=this.defaults.async||n.async||!1,e.extensions&&(e.extensions.forEach(e=>{if(!e.name)throw Error(`extension name required`);if(`renderer`in e){let n=t.renderers[e.name];n?t.renderers[e.name]=function(...t){let r=e.renderer.apply(this,t);return r===!1&&(r=n.apply(this,t)),r}:t.renderers[e.name]=e.renderer}if(`tokenizer`in e){if(!e.level||e.level!==`block`&&e.level!==`inline`)throw Error(`extension level must be 'block' or 'inline'`);let n=t[e.level];n?n.unshift(e.tokenizer):t[e.level]=[e.tokenizer],e.start&&(e.level===`block`?t.startBlock?t.startBlock.push(e.start):t.startBlock=[e.start]:e.level===`inline`&&(t.startInline?t.startInline.push(e.start):t.startInline=[e.start]))}`childTokens`in e&&e.childTokens&&(t.childTokens[e.name]=e.childTokens)}),n.extensions=t),e.renderer){let t=this.defaults.renderer||new XS(this.defaults);for(let n in e.renderer){if(!(n in t))throw Error(`renderer '${n}' does not exist`);if([`options`,`parser`].includes(n))continue;let r=n,i=e.renderer[r],a=t[r];t[r]=(...e)=>{let n=i.apply(t,e);return n===!1&&(n=a.apply(t,e)),n||``}}n.renderer=t}if(e.tokenizer){let t=this.defaults.tokenizer||new JS(this.defaults);for(let n in e.tokenizer){if(!(n in t))throw Error(`tokenizer '${n}' does not exist`);if([`options`,`rules`,`lexer`].includes(n))continue;let r=n,i=e.tokenizer[r],a=t[r];t[r]=(...e)=>{let n=i.apply(t,e);return n===!1&&(n=a.apply(t,e)),n}}n.tokenizer=t}if(e.hooks){let t=this.defaults.hooks||new $S;for(let n in e.hooks){if(!(n in t))throw Error(`hook '${n}' does not exist`);if([`options`,`block`].includes(n))continue;let r=n,i=e.hooks[r],a=t[r];t[r]=$S.passThroughHooks.has(n)?e=>{if(this.defaults.async&&$S.passThroughHooksRespectAsync.has(n))return(async()=>{let n=await i.call(t,e);return a.call(t,n)})();let r=i.call(t,e);return a.call(t,r)}:(...e)=>{if(this.defaults.async)return(async()=>{let n=await i.apply(t,e);return n===!1&&(n=await a.apply(t,e)),n})();let n=i.apply(t,e);return n===!1&&(n=a.apply(t,e)),n}}n.hooks=t}if(e.walkTokens){let t=this.defaults.walkTokens,r=e.walkTokens;n.walkTokens=function(e){let n=[];return n.push(r.call(this,e)),t&&(n=n.concat(t.call(this,e))),n}}this.defaults={...this.defaults,...n}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return YS.lex(e,t??this.defaults)}parser(e,t){return QS.parse(e,t??this.defaults)}parseMarkdown(e){return(t,n)=>{let r={...n},i={...this.defaults,...r},a=this.onError(!!i.silent,!!i.async);if(this.defaults.async===!0&&r.async===!1)return a(Error(`marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise.`));if(typeof t>`u`||t===null)return a(Error(`marked(): input parameter is undefined or null`));if(typeof t!=`string`)return a(Error(`marked(): input parameter is of type `+Object.prototype.toString.call(t)+`, string expected`));if(i.hooks&&(i.hooks.options=i,i.hooks.block=e),i.async)return(async()=>{let n=i.hooks?await i.hooks.preprocess(t):t,r=await(i.hooks?await i.hooks.provideLexer(e):e?YS.lex:YS.lexInline)(n,i),a=i.hooks?await i.hooks.processAllTokens(r):r;i.walkTokens&&await Promise.all(this.walkTokens(a,i.walkTokens));let o=await(i.hooks?await i.hooks.provideParser(e):e?QS.parse:QS.parseInline)(a,i);return i.hooks?await i.hooks.postprocess(o):o})().catch(a);try{i.hooks&&(t=i.hooks.preprocess(t));let n=(i.hooks?i.hooks.provideLexer(e):e?YS.lex:YS.lexInline)(t,i);i.hooks&&(n=i.hooks.processAllTokens(n)),i.walkTokens&&this.walkTokens(n,i.walkTokens);let r=(i.hooks?i.hooks.provideParser(e):e?QS.parse:QS.parseInline)(n,i);return i.hooks&&(r=i.hooks.postprocess(r)),r}catch(e){return a(e)}}}onError(e,t){return n=>{if(n.message+=`
|
|
4453
|
-
Please report this to https://github.com/markedjs/marked.`,e){let e=`<p>An error occurred:</p><pre>`+IS(n.message+``,!0)+`</pre>`;return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}};function tC(e,t){return eC.parse(e,t)}tC.options=tC.setOptions=function(e){return eC.setOptions(e),tC.defaults=eC.defaults,px(tC.defaults),tC},tC.getDefaults=dx,tC.defaults=fx;function nC(...e){return eC.use(...e),tC.defaults=eC.defaults,px(tC.defaults),tC}tC.use=nC,tC.walkTokens=function(e,t){return eC.walkTokens(e,t)},tC.parseInline=eC.parseInline,tC.Parser=QS,tC.parser=QS.parse,tC.Renderer=XS,tC.TextRenderer=ZS,tC.Lexer=YS,tC.lexer=YS.lex,tC.Tokenizer=JS,tC.Hooks=$S,tC.parse=tC,tC.options,tC.setOptions,tC.walkTokens,tC.parseInline,QS.parse,YS.lex;var rC=!1;function iC(){rC||(rC=!0,ux.addHook(`afterSanitizeAttributes`,e=>{e instanceof Element&&e.tagName===`A`&&(e.setAttribute(`rel`,`noopener noreferrer`),e.setAttribute(`target`,`_blank`))}))}function aC(e){iC();let t=tC.parse(e,{async:!1,breaks:!0});return ux.sanitize(t,{ALLOWED_TAGS:[`p`,`br`,`strong`,`em`,`code`,`pre`,`ul`,`ol`,`li`,`a`,`h1`,`h2`,`h3`,`h4`,`blockquote`,`hr`,`del`],ALLOWED_ATTR:[`href`,`rel`,`target`],ALLOWED_URI_REGEXP:/^(?:https?:|mailto:)/i})}var oC=e=>Number.isFinite(e);function sC(e){let t=Math.floor(Math.log10(e)),n=e/10**t;return(n<=1?1:n<=2?2:n<=2.5?2.5:n<=5?5:10)*10**t}function cC(e,t,n=5){let r=oC(e)?e:0,i=oC(t)?t:r+1;r>i&&([r,i]=[i,r]),r===i&&(i=r+(Math.abs(r)||1));let a=sC((i-r)/Math.max(1,n-1)),o=Math.floor(r/a)*a,s=Math.ceil(i/a)*a,c=[];for(let e=o;e<=s+a/2;e+=a)c.push(Math.round(e*1e9)/1e9);return{min:o,max:s,ticks:c}}function lC(e,t){let[n,r]=e,[i,a]=t;return r===n?()=>i:e=>i+(e-n)/(r-n)*(a-i)}function uC(e){return e.map(([e,t],n)=>`${n===0?`M`:`L`}${e.toFixed(2)} ${t.toFixed(2)}`).join(` `)}function dC(e,t,n,r,i){let a=r=>{let i=r*Math.PI/180;return`${(e+n*Math.cos(i)).toFixed(2)} ${(t+n*Math.sin(i)).toFixed(2)}`},o=+(Math.abs(i-r)>180);return`M ${a(r)} A ${n} ${n} 0 ${o} 1 ${a(i)}`}function fC(e,t,n){return!oC(e)||!oC(t)||!oC(n)||n<=t?0:Math.min(1,Math.max(0,(e-t)/(n-t)))*240}function pC(e,t,n,r){let i=t.x-e.x,a=t.y-e.y,o=Math.hypot(i,a);if(o===0||o<=n+r)return{from:{...e},to:{...t}};let s=i/o,c=a/o;return{from:{x:e.x+s*n,y:e.y+c*n},to:{x:t.x-s*r,y:t.y-c*r}}}function mC(e,t,n){if(e<=0)return[];if(e===1)return[{x:t/2,y:n/2}];let r=Math.min(t,n)*.36;return Array.from({length:e},(i,a)=>{let o=-Math.PI/2+a*2*Math.PI/e;return{x:t/2+r*Math.cos(o),y:n/2+r*Math.sin(o)}})}var hC=e=>typeof e==`object`&&!!e&&!Array.isArray(e),gC=e=>typeof e==`string`?e:typeof e==`number`?String(e):``,_C=e=>Array.isArray(e)?e:[],vC=e=>typeof e==`number`?Number.isFinite(e)?e:null:typeof e==`string`&&e.trim()!==``&&Number.isFinite(Number(e))?Number(e):null,yC=(e,t)=>e.length>t?e.slice(0,t)+`…`:e,bC=[`var(--accent)`,`#ffb454`,`#7b8cff`,`#5ef0b0`,`#ff6b7a`,`#c792ea`],xC=({text:e})=>e?(0,p.jsx)(`h3`,{className:`obj-title`,children:e}):null,SC=({text:e=`No data`})=>(0,p.jsx)(`p`,{className:`obj-empty`,children:e}),CC=400,wC=220,TC={l:44,r:12,t:10,b:28};function EC({props:e}){let t=e.kind===`bar`||e.kind===`area`?e.kind:`line`,n=_C(e.labels).slice(0,120).map(gC),r=_C(e.series).slice(0,6).map((e,t)=>{let n=hC(e)?e:{},r=_C(n.values).slice(0,120).map((e,t)=>({idx:t,v:vC(e)})).filter(e=>e.v!==null);return{name:gC(n.name)||`series ${t+1}`,points:r}}).filter(e=>e.points.length>0);if(r.length===0)return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(xC,{text:gC(e.title)}),(0,p.jsx)(SC,{})]});let i=r.flatMap(e=>e.points.map(e=>e.v)),a=Math.min(...i),o=Math.max(...i),s=cC(t===`line`?a:Math.min(0,a),t===`line`?o:Math.max(0,o),5),c=Math.max(n.length,...r.map(e=>e.points[e.points.length-1].idx+1),2),l=lC([s.min,s.max],[wC-TC.b,TC.t]),u=lC([0,c-1],[TC.l,CC-TC.r]),d=(CC-TC.l-TC.r)/c,f=Math.max(1,d*.7/r.length),m=(e,t)=>TC.l+e*d+d*.15+t*f,h=Math.max(1,Math.ceil(c/6)),g=gC(e.unit);return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(xC,{text:gC(e.title)}),(0,p.jsxs)(`svg`,{className:`obj-chart`,viewBox:`0 0 ${CC} ${wC}`,role:`img`,"aria-label":gC(e.title)||`chart`,children:[s.ticks.map(e=>(0,p.jsxs)(`g`,{children:[(0,p.jsx)(`line`,{className:`grid`,x1:TC.l,x2:CC-TC.r,y1:l(e),y2:l(e)}),(0,p.jsxs)(`text`,{className:`tick`,x:TC.l-6,y:l(e)+3,textAnchor:`end`,children:[e,g&&e===s.max?g:``]})]},e)),Array.from({length:c},(e,t)=>t).filter(e=>e%h===0&&n[e]).map(e=>(0,p.jsx)(`text`,{className:`tick`,x:t===`bar`?m(e,0)+f*r.length/2:u(e),y:212,textAnchor:`middle`,children:yC(n[e],8)},e)),r.map((e,n)=>{let r=bC[n%bC.length];if(t===`bar`)return(0,p.jsx)(`g`,{children:e.points.map(e=>(0,p.jsx)(`rect`,{className:`bar`,x:m(e.idx,n),y:Math.min(l(e.v),l(0)),width:f,height:Math.abs(l(e.v)-l(0)),fill:r},e.idx))},n);let i=e.points.map(e=>[u(e.idx),l(e.v)]),a=uC(i),o=`${a} L${i[i.length-1][0].toFixed(2)} ${l(s.min).toFixed(2)} L${i[0][0].toFixed(2)} ${l(s.min).toFixed(2)} Z`;return(0,p.jsxs)(`g`,{children:[t===`area`&&(0,p.jsx)(`path`,{className:`series-area`,d:o,fill:r,opacity:.18}),(0,p.jsx)(`path`,{className:`series-line`,d:a,fill:`none`,stroke:r,strokeWidth:2,strokeLinejoin:`round`})]},n)})]}),r.length>1&&(0,p.jsx)(`ul`,{className:`legend`,children:r.map((e,t)=>(0,p.jsxs)(`li`,{children:[(0,p.jsx)(`i`,{style:{background:bC[t%bC.length]}}),(0,p.jsx)(`span`,{children:e.name})]},t))})]})}function DC({props:e}){let t=vC(e.min)??0,n=vC(e.max)??100,r=vC(e.value)??t,i=fC(r,t,n),a=i/240;return(0,p.jsxs)(`div`,{className:`obj-gauge ${a>.95?`gauge-crit`:a>.8?`gauge-warn`:``}`,children:[(0,p.jsxs)(`svg`,{viewBox:`0 0 200 150`,role:`img`,"aria-label":`${gC(e.label)} ${r}`,children:[(0,p.jsx)(`path`,{className:`track`,d:dC(100,100,70,150,390),fill:`none`,strokeWidth:12,strokeLinecap:`round`}),i>0&&(0,p.jsx)(`path`,{className:`value`,d:dC(100,100,70,150,150+i),fill:`none`,strokeWidth:12,strokeLinecap:`round`})]}),(0,p.jsxs)(`div`,{className:`gauge-read`,children:[(0,p.jsx)(`span`,{className:`gauge-value`,children:String(Math.round(r*100)/100)}),gC(e.unit)&&(0,p.jsx)(`small`,{className:`gauge-unit`,children:gC(e.unit)})]}),(0,p.jsx)(`span`,{className:`obj-label`,children:gC(e.label)})]})}function OC({props:e}){let t=_C(e.items).slice(0,30).map(e=>hC(e)?e:{}).map(e=>({time:gC(e.time),title:gC(e.title),detail:gC(e.detail)})).filter(e=>e.title||e.time);return t.length===0?(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(xC,{text:gC(e.title)}),(0,p.jsx)(SC,{text:`Nothing to show`})]}):(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(xC,{text:gC(e.title)}),(0,p.jsx)(`ol`,{className:`obj-timeline`,children:t.map((e,t)=>(0,p.jsxs)(`li`,{children:[(0,p.jsx)(`time`,{children:e.time}),(0,p.jsx)(`strong`,{children:e.title}),e.detail&&(0,p.jsx)(`span`,{children:e.detail})]},t))})]})}var kC=400,AC=300,jC=12;function MC({props:e}){let t=(0,l.useId)(),n=new Set,r=[];for(let t of _C(e.nodes)){let e=hC(t)?t:{},i=gC(e.id);if(i&&!n.has(i)&&(n.add(i),r.push({id:i,label:gC(e.label)||i}),r.length>=40))break}if(r.length===0)return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(xC,{text:gC(e.title)}),(0,p.jsx)(SC,{text:`Nothing to show`})]});let i=new Map(r.map((e,t)=>[e.id,mC(r.length,kC,AC)[t]])),a=[];for(let t of _C(e.edges)){let e=hC(t)?t:{},n=gC(e.from),r=gC(e.to);if(i.has(n)&&i.has(r)&&n!==r&&(a.push({from:n,to:r,label:gC(e.label)}),a.length>=80))break}return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(xC,{text:gC(e.title)}),(0,p.jsxs)(`svg`,{className:`obj-graph`,viewBox:`0 0 ${kC} ${AC}`,role:`img`,"aria-label":gC(e.title)||`graph`,children:[(0,p.jsx)(`defs`,{children:(0,p.jsx)(`marker`,{id:t,viewBox:`0 0 10 10`,refX:`9`,refY:`5`,markerWidth:`6`,markerHeight:`6`,orient:`auto-start-reverse`,children:(0,p.jsx)(`path`,{d:`M0 0 L10 5 L0 10 z`,className:`arrow`})})}),a.map((e,n)=>{let{from:r,to:a}=pC(i.get(e.from),i.get(e.to),14,17),o=(r.x+a.x)/2+(kC/2-(r.x+a.x)/2)*.25,s=(r.y+a.y)/2+(AC/2-(r.y+a.y)/2)*.25;return(0,p.jsxs)(`g`,{children:[(0,p.jsx)(`path`,{className:`edge`,d:`M ${r.x.toFixed(1)} ${r.y.toFixed(1)} Q ${o.toFixed(1)} ${s.toFixed(1)} ${a.x.toFixed(1)} ${a.y.toFixed(1)}`,fill:`none`,markerEnd:`url(#${t})`}),e.label&&(0,p.jsx)(`text`,{className:`edge-label`,x:o,y:s,textAnchor:`middle`,children:yC(e.label,12)})]},n)}),r.map(e=>{let t=i.get(e.id);return(0,p.jsxs)(`g`,{children:[(0,p.jsx)(`circle`,{className:`node`,cx:t.x,cy:t.y,r:jC}),(0,p.jsx)(`text`,{className:`node-label`,x:t.x,y:t.y+26,textAnchor:`middle`,children:yC(e.label,18)})]},e.id)})]})]})}var NC=[`default-src 'none'`,`script-src 'unsafe-inline'`,`style-src 'unsafe-inline'`,`img-src data: blob:`,`form-action 'none'`,`base-uri 'none'`].join(`; `),PC=`html,body{margin:0;min-height:100%;background:#050b10;color:#d8f6ff;font:15px/1.5 system-ui,sans-serif}`;function FC(e){return`<!doctype html><html><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="${NC}"><meta name="viewport" content="width=device-width,initial-scale=1"><style>${PC}</style></head><body>${e}</body></html>`}function IC({props:e}){let t=typeof e.html==`string`?e.html:``,n=typeof e.title==`string`?e.title:``,r=(0,l.useMemo)(()=>FC(t),[t]);return(0,p.jsxs)(`div`,{className:`obj-sandbox`,children:[n&&(0,p.jsx)(`h3`,{className:`obj-title`,children:n}),(0,p.jsx)(`iframe`,{title:n||`Page made by Friend`,sandbox:`allow-scripts`,referrerPolicy:`no-referrer`,allow:``,srcDoc:r})]})}var LC=e=>typeof e==`string`?e:typeof e==`number`?String(e):``,RC=e=>Array.isArray(e)?e:[];function zC({props:e}){let t=(0,l.useMemo)(()=>aC(LC(e.text)),[e.text]);return(0,p.jsxs)(p.Fragment,{children:[LC(e.title)&&(0,p.jsx)(`h3`,{className:`obj-title`,children:LC(e.title)}),(0,p.jsx)(`div`,{className:`obj-md`,dangerouslySetInnerHTML:{__html:t}})]})}function BC({props:e}){return(0,p.jsxs)(`div`,{className:`obj-stat`,children:[(0,p.jsx)(`span`,{className:`obj-label`,children:LC(e.label)}),(0,p.jsxs)(`span`,{className:`obj-value`,children:[(0,p.jsx)(`span`,{children:LC(e.value)}),LC(e.unit)&&(0,p.jsx)(`small`,{children:LC(e.unit)})]}),LC(e.delta)&&(0,p.jsx)(`span`,{className:`obj-delta`,children:LC(e.delta)})]})}function VC({props:e}){return(0,p.jsxs)(p.Fragment,{children:[LC(e.title)&&(0,p.jsx)(`h3`,{className:`obj-title`,children:LC(e.title)}),(0,p.jsx)(`ul`,{className:`obj-list`,children:RC(e.items).map((e,t)=>{let n=typeof e==`object`&&e?e:{text:e};return(0,p.jsx)(`li`,{className:n.done?`done`:void 0,children:LC(n.text)},t)})})]})}function HC({props:e}){let t=RC(e.columns),n=RC(e.rows);return(0,p.jsxs)(p.Fragment,{children:[LC(e.title)&&(0,p.jsx)(`h3`,{className:`obj-title`,children:LC(e.title)}),(0,p.jsx)(`div`,{className:`obj-table-wrap`,children:(0,p.jsxs)(`table`,{className:`obj-table`,children:[(0,p.jsx)(`thead`,{children:(0,p.jsx)(`tr`,{children:t.map((e,t)=>(0,p.jsx)(`th`,{children:LC(e)},t))})}),(0,p.jsx)(`tbody`,{children:n.map((e,t)=>(0,p.jsx)(`tr`,{children:RC(e).map((e,t)=>(0,p.jsx)(`td`,{children:LC(e)},t))},t))})]})})]})}function UC({props:e}){return(0,p.jsxs)(p.Fragment,{children:[LC(e.title)&&(0,p.jsx)(`h3`,{className:`obj-title`,children:LC(e.title)}),(0,p.jsx)(`pre`,{className:`obj-code`,"data-lang":LC(e.language),children:(0,p.jsx)(`code`,{children:LC(e.code)})})]})}var WC={card:zC,stat:BC,list:VC,table:HC,code:UC,chart:EC,gauge:DC,timeline:OC,graph:MC,sandbox:IC};function GC(e,t){let n=new Map(t.map(e=>[e.id,e])),r=[],i=new Set;for(let t of e){if(i.has(t.object.id))continue;i.add(t.object.id);let e=n.get(t.object.id);r.push(e?{object:e,leaving:!1}:{object:t.object,leaving:!0})}for(let e of t)i.has(e.id)||(i.add(e.id),r.push({object:e,leaving:!1}));return r}function KC(e,t){let[n,r]=(0,l.useState)(()=>e.map(e=>({object:e,leaving:!1}))),i=(0,l.useRef)(new Map);return(0,l.useEffect)(()=>{r(n=>{let a=GC(n,e),o=new Set(n.filter(e=>e.leaving).map(e=>e.object.id));for(let e of a){let n=i.current.get(e.object.id);if(!e.leaving)n&&clearTimeout(n),i.current.delete(e.object.id);else if(!o.has(e.object.id)&&!n){let n=e.object.id;i.current.set(n,setTimeout(()=>{i.current.delete(n),r(e=>e.filter(e=>!(e.object.id===n&&e.leaving)))},Math.max(t,0)))}}return t<=0?a.filter(e=>!e.leaving):a})},[e,t]),(0,l.useEffect)(()=>()=>{for(let e of i.current.values())clearTimeout(e);i.current.clear()},[]),n}var qC=.12,JC=.1,YC=.02,XC=(e,t,n)=>Math.min(Math.max(e,t),n);function ZC(e){let t=XC(e.w,qC,1),n=XC(e.h,JC,1);return{x:XC(e.x,0,1-t),y:XC(e.y,0,1-n),w:t,h:n}}function QC(e,t){let n=t.getBoundingClientRect(),r=e.getBoundingClientRect();return n.width<=0||n.height<=0?null:ZC({x:(r.left-n.left)/n.width,y:(r.top-n.top)/n.height,w:r.width/n.width,h:r.height/n.height})}function $C(e,t,n){let r=(0,l.useRef)(null),[i,a]=(0,l.useState)(null),o=(0,l.useCallback)((n,r)=>{let i=ZC(r);t({type:`object.place`,id:n,placement:{x:i.x,y:i.y,w:i.w,h:i.h}}),e({type:`stage.move`,objectId:n,rect:i})},[e,t]),s=(0,l.useCallback)((e,t,i)=>{if(e.pointerType===`mouse`&&e.button!==0)return;let o=n(),s=e.currentTarget.closest(`[data-object]`);if(!o||!s||r.current)return;let c=QC(s,o);if(!c)return;let l=o.getBoundingClientRect();r.current={id:t.id,mode:i,pointer:e.pointerId,startX:e.clientX,startY:e.clientY,from:c,stageW:l.width,stageH:l.height};try{e.currentTarget.setPointerCapture(e.pointerId)}catch{}e.stopPropagation(),e.preventDefault(),a({id:t.id,box:c})},[n]),c=(e,t)=>{let n=(t.clientX-e.startX)/e.stageW,r=(t.clientY-e.startY)/e.stageH;return e.mode===`move`?ZC({...e.from,x:e.from.x+n,y:e.from.y+r}):ZC({...e.from,w:e.from.w+n,h:e.from.h+r})};return{preview:i,begin:s,move:(0,l.useCallback)(e=>{let t=r.current;t&&t.pointer===e.pointerId&&a({id:t.id,box:c(t,e)})},[]),end:(0,l.useCallback)((e,t)=>{let n=r.current;n&&n.pointer===e.pointerId&&(r.current=null,a(null),t||o(n.id,c(n,e)))},[o]),key:(0,l.useCallback)((e,t)=>{let r={ArrowLeft:[-.02,0],ArrowRight:[YC,0],ArrowUp:[0,-.02],ArrowDown:[0,YC]}[e.key],i=n(),a=e.currentTarget.closest(`[data-object]`);if(!r||!i||!a)return;let s=QC(a,i);s&&(e.preventDefault(),o(t.id,e.shiftKey?{...s,w:s.w+r[0],h:s.h+r[1]}:{...s,x:s.x+r[0],y:s.y+r[1]}))},[o,n])}}function ew(e,t){if(e===`auto`)return{position:`relative`};if(t.w<600)return{position:`relative`,width:`100%`};let[n,r,i,a]=typeof e==`string`?uy[e]:[e.x*100,e.y*100,e.w*100,e.h*100],o={position:`absolute`,left:`${n}%`,top:`${r}%`,width:`${i}%`};return e===`top`||e===`bottom`?{...o,maxHeight:`${a}%`}:{...o,height:`${a}%`}}var tw=350,nw=()=>typeof matchMedia==`function`&&matchMedia(`(prefers-reduced-motion: reduce)`).matches,rw=class extends l.Component{state={failed:!1};static getDerivedStateFromError(){return{failed:!0}}render(){return this.state.failed?null:this.props.children}};function iw(){let e=()=>({w:window.innerWidth,h:window.innerHeight}),[t,n]=(0,l.useState)(e);return(0,l.useEffect)(()=>{let t=()=>n(e());return window.addEventListener(`resize`,t),()=>window.removeEventListener(`resize`,t)},[]),t}var aw=350;function ow({state:e,send:t,dispatch:n,voice:r,slash:i,onMenu:a}){let o=iw(),[s,c]=(0,l.useState)(0),[u,d]=(0,l.useState)(!1),f=ne(),m=o.w<600,h=e.scene===`focus`||e.scene===`multi`,g={"--accent":e.mood.accent,"--intensity":e.mood.intensity},_=e.voice.mode,v=_===`listening`?`listening`:_===`speaking`?`speaking`:_===`transcribing`?`thinking`:e.presence.state,y=gu(e.look.reactivity),b=Math.min(1,(_===`listening`||_===`speaking`?e.voice.level:e.presence.amplitude)*y),x=(0,l.useRef)({timer:null,down:!1,talking:!1,id:null}),S=e=>{if(!(e.target===e.currentTarget||e.target.closest(`[data-presence]`))||e.pointerType===`mouse`&&e.button!==0)return;let t=x.current;if(!(t.down&&t.id!==null&&e.pointerId!==t.id)){t.timer&&clearTimeout(t.timer),t.down=!0,t.talking=!1,t.id=typeof e.pointerId==`number`?e.pointerId:null;try{t.id!==null&&e.currentTarget.setPointerCapture?.(t.id)}catch{}t.timer=r?setTimeout(()=>{t.down&&(t.talking=!0,r.pressStart())},aw):null}},C=(e,t)=>{let n=x.current;n.down&&(n.id===null||typeof e.pointerId!=`number`||e.pointerId===n.id)&&(n.down=!1,n.id=null,n.timer&&clearTimeout(n.timer),n.timer=null,n.talking?(n.talking=!1,t?r?.blur?.():r?.pressEnd()):t||c(e=>e+1))},w=(0,l.useRef)(null),T=$C(t,n,()=>w.current),E=KC(e.objects,nw()?0:tw),D=E.filter(e=>e.object.placement===`auto`),O=E.filter(e=>e.object.placement!==`auto`),k=({object:e,leaving:r})=>{let i=WC[e.type];if(!i)return null;let a=!m&&!r,s=T.preview?.id===e.id?T.preview.box:null,c=s?{x:s.x,y:s.y,w:s.w,h:s.h}:e.placement;return(0,p.jsxs)(`section`,{"data-object":e.id,className:`obj glass${e.style===`alert`?` alert`:``}${e.pinned?` pinned`:``}${s?` dragging`:``}${r?` leaving`:``}`,style:ew(c,o),children:[a&&(0,p.jsx)(`div`,{className:`obj-grip`,role:`button`,tabIndex:0,"aria-label":`Move ${e.id}. Drag, or use the arrow keys; hold Shift to resize.`,onPointerDown:t=>T.begin(t,e,`move`),onPointerMove:T.move,onPointerUp:e=>T.end(e,!1),onPointerCancel:e=>T.end(e,!0),onKeyDown:t=>T.key(t,e)}),(0,p.jsx)(`button`,{className:`obj-pin${e.pinned?` on`:``}`,"aria-label":e.pinned?`Unpin ${e.id}`:`Pin ${e.id} to the screen`,"aria-pressed":!!e.pinned,title:e.pinned?`Pinned: stays on the screen. Click to unpin.`:`Pin: keep this on the screen.`,disabled:r,onClick:()=>t({type:`stage.pin`,objectId:e.id,pinned:!e.pinned}),children:(0,p.jsx)(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:e.pinned?`currentColor`:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":`true`,children:(0,p.jsx)(`path`,{d:`M12 17v5M9 3h6l-1 7 3 3H7l3-3-1-7z`})})}),(0,p.jsx)(`button`,{className:`obj-close`,"aria-label":`Dismiss ${e.id}`,title:e.pinned?`Remove this widget`:`Dismiss`,disabled:r,onClick:()=>{t({type:`stage.event`,objectId:e.id,event:`dismiss`}),n({type:`object.dismiss`,id:e.id})},children:`×`}),a&&(0,p.jsx)(`div`,{className:`obj-resize`,"aria-hidden":`true`,onPointerDown:t=>T.begin(t,e,`resize`),onPointerMove:T.move,onPointerUp:e=>T.end(e,!1),onPointerCancel:e=>T.end(e,!0)}),(0,p.jsx)(rw,{children:(0,p.jsx)(i,{props:e.props})})]},e.id)};return(0,p.jsxs)(`div`,{ref:w,"data-testid":`stage`,className:`stage`,style:g,onPointerDown:S,onPointerUp:e=>C(e,!1),onPointerCancel:e=>C(e,!0),onContextMenu:e=>r&&e.preventDefault(),children:[e.look.ambient&&(0,p.jsx)(`div`,{className:`ambient`,"aria-hidden":`true`,style:{opacity:.35+.65*e.mood.intensity}}),(0,p.jsx)(hu,{state:v,amplitude:b,pulse:e.presence.pulse,mode:h?`corner`:`full`,accent:e.mood.accent,style:e.presence.style,intensity:e.mood.intensity,scale:e.look.size}),(0,p.jsxs)(`div`,{className:m?`objects objects-stack`:`objects`,style:{zIndex:10},children:[D.length>0&&(0,p.jsx)(`div`,{className:`auto-grid`,children:D.map(k)}),O.map(k)]}),e.look.captions&&e.captions&&(0,p.jsx)(`p`,{className:`captions captions-${e.look.captionSize}${e.captionDone?` done`:``}`,"aria-live":`polite`,children:e.captions}),f&&r&&!u&&(0,p.jsx)(Fy,{voice:r,mode:_,onType:()=>c(e=>e+1),onMenu:()=>a?.()}),(0,p.jsx)(My,{requestOpen:s,slash:i,onOpenChange:d,onSend:e=>t({type:`chat.send`,text:e})})]})}function sw(){let e=()=>Math.random().toString(36).slice(2,12)+Date.now().toString(36);try{let t=sessionStorage.getItem(`friend.tab`);return t||(t=e(),sessionStorage.setItem(`friend.tab`,t)),t}catch{return e()}}function cw(){return`${location.protocol===`https:`?`wss`:`ws`}://${location.host}/ws`}function lw(e=cw()){let t=null,n=`connecting`,r=!1,i=0,a=!1,o=sw(),s=null,c=new Set,l=new Set,u=e=>{n=e,l.forEach(t=>t(e))};function d(){if(r)return;u(`connecting`);let n=new WebSocket(`${e}${e.includes(`?`)?`&`:`?`}tab=${o}${a?`&takeover=1`:``}`);t=n,n.onopen=()=>{i=0,a=!1,u(`open`)},n.onmessage=e=>{try{let t=JSON.parse(String(e.data));c.forEach(e=>e(t))}catch{}},n.onclose=e=>{if(!r){if(e.code===4401)return u(`unauthorized`);if(e.code===4409||e.code===4410)return u(`elsewhere`);u(`closed`),s=setTimeout(d,Math.min(500*2**i++,8e3))}},n.onerror=()=>{}}return d(),{send(e){t?.readyState===WebSocket.OPEN&&t.send(JSON.stringify(e))},onMessage(e){return c.add(e),()=>c.delete(e)},onState(e){return l.add(e),()=>l.delete(e)},state:()=>n,takeOver(){r||n!==`elsewhere`||(a=!0,i=0,d())},close(){r=!0,s&&clearTimeout(s),t?.close()}}}var uw={scene:`conversation`,objects:[],captions:``,captionDone:!0,presence:{state:`idle`,style:`sphere`,amplitude:0,pulse:0},mood:{accent:`#3fd8ff`,intensity:.6},approval:null,alerts:[],connection:`connecting`,chat:[],chatLive:!1,view:`companion`,look:{size:1,reactivity:.7,ambient:!0,captions:!0,captionSize:`medium`},viewKnown:!1,busy:!1,activity:null,deviceOnline:!1,voice:{mode:`idle`,level:0,available:!1,enrolled:!1},challenge:null,camera:{enabled:!1,stream:null},speaker:null,ownerName:``},dw=200,fw=e=>e.length>dw?e.slice(-200):e,pw=0,mw=e=>Math.min(1,Math.max(0,e));function hw(e,t){switch(t.cmd){case`scene`:return{...e,scene:t.mode};case`show`:{let n=e.objects.findIndex(e=>e.id===t.object.id),r=n===-1?[...e.objects,t.object]:e.objects.map((e,r)=>r===n?t.object:e);return{...e,objects:r}}case`update`:return{...e,objects:e.objects.map(e=>e.id===t.id?{...e,props:{...e.props,...t.props}}:e)};case`hide`:return{...e,objects:e.objects.filter(e=>e.id!==t.id)};case`clear`:return{...e,objects:e.objects.filter(e=>e.pinned),scene:`conversation`};case`pin`:return{...e,objects:e.objects.map(e=>e.id===t.id?{...e,pinned:!0}:e)};case`unpin`:return{...e,objects:e.objects.map(e=>e.id===t.id?(({pinned:e,...t})=>t)(e):e)};case`place`:return{...e,objects:e.objects.map(e=>e.id===t.id?{...e,placement:t.placement}:e)};case`presence`:return{...e,presence:{...e.presence,state:t.state??e.presence.state,style:t.style??e.presence.style}};case`mood`:return{...e,mood:{accent:t.accent??e.mood.accent,intensity:t.intensity??e.mood.intensity}}}}function gw(e,t){let n=t;if(n&&typeof n==`object`){if(n.type===`conn`)return{...e,connection:n.value};if(n.type===`chat.seed`){let t=Array.isArray(n.lines)?n.lines:[];return e.chat.length===0?{...e,chat:fw(t)}:{...e,chat:fw([...t,...e.chat])}}if(n.type===`alert.add`)return{...e,alerts:[...e.alerts.slice(-3),{id:++pw,level:n.level,text:n.text}]};if(n.type===`camera.enable`)return{...e,camera:{enabled:n.value,stream:n.value?e.camera.stream:null}};if(n.type===`camera.open`)return{...e,camera:{...e.camera,stream:n.stream}};if(n.type===`camera.closed`)return{...e,camera:{...e.camera,stream:null}};if(n.type===`challenge.dismiss`)return e.challenge?.id===n.id?{...e,challenge:null}:e;if(n.type===`voice.mode`)return{...e,voice:{...e.voice,mode:n.mode,level:n.mode===`idle`?0:e.voice.level}};if(n.type===`voice.level`){let t=Number.isFinite(n.level)?Math.min(1,Math.max(0,n.level)):0;return{...e,voice:{...e.voice,level:t}}}if(n.type===`object.dismiss`)return{...e,objects:e.objects.filter(e=>e.id!==n.id)};if(n.type===`object.place`)return{...e,objects:e.objects.map(e=>e.id===n.id?{...e,placement:n.placement}:e)};if(n.type===`alert.dismiss`)return{...e,alerts:e.alerts.filter(e=>e.id!==n.id)}}let r=xy.safeParse(t);if(!r.success)return e;let i=r.data;switch(i.type){case`snapshot`:return{...e,scene:i.stage.scene,objects:i.stage.objects,presence:{...e.presence,state:i.stage.presence.state,style:i.stage.presence.style},mood:i.stage.mood,approval:i.pendingApproval??null};case`stage`:return hw(e,i.command);case`chat.user`:return{...e,chat:fw([...e.chat,{role:`user`,text:i.text}]),chatLive:!1};case`ui`:return{...e,view:i.view,look:i.look??e.look,ownerName:i.ownerName??e.ownerName,viewKnown:!0};case`caption.delta`:{let t=e.captionDone,n=e.chat.at(-1),r=e.chatLive&&n?.role===`assistant`?[...e.chat.slice(0,-1),{role:`assistant`,text:n.text+i.text}]:fw([...e.chat,{role:`assistant`,text:i.text}]);return{...e,chat:r,chatLive:!0,captions:(t?``:e.captions)+i.text,captionDone:!1,presence:{...e.presence,amplitude:mw(.3+i.text.length*.04),pulse:e.presence.pulse+1}}}case`caption.end`:return{...e,chatLive:!1,captionDone:!0,presence:{...e.presence,amplitude:0}};case`approval.request`:{let{type:t,...n}=i;return{...e,approval:n}}case`approval.resolved`:return e.approval?.id===i.id?{...e,approval:null}:e;case`alert`:return{...e,alerts:[...e.alerts.slice(-3),{id:++pw,level:i.level,text:i.text}]};case`activity`:return{...e,activity:i.text};case`status`:return{...e,busy:i.busy,activity:i.busy?e.activity:null,deviceOnline:i.deviceOnline,voice:{...e.voice,available:i.voiceAvailable??e.voice.available,enrolled:i.voiceEnrolled??e.voice.enrolled}};case`error`:return{...e,alerts:[...e.alerts.slice(-3),{id:++pw,level:`error`,text:i.message}]};case`voice.heard`:return{...e,captions:i.trust===`guest`?`Guest: ${i.text}`:i.trust===`member`&&i.speaker?`${i.speaker}: ${i.text}`:i.text,captionDone:!0,speaker:{name:i.trust===`member`?i.speaker??null:null,trust:i.trust??`unverified`,seq:(e.speaker?.seq??0)+1}};case`voice.challenge`:return{...e,challenge:{id:i.id,text:i.text}};case`voice.challenge.end`:return e.challenge?.id===i.id?{...e,challenge:null}:e;case`audio.chunk`:case`audio.stop`:return e;case`camera.request`:case`camera.cancel`:return e}}var _w=1024,vw=.8,yw=300;function bw(e,t){let n=Math.min(1,_w/Math.max(e,t));return{width:Math.round(e*n),height:Math.round(t*n)}}function xw(e){let t=e?.name;return t===`NotAllowedError`||t===`SecurityError`?`permission`:t===`NotFoundError`||t===`OverconstrainedError`?`no-camera`:`busy`}async function Sw(){if(!navigator.mediaDevices?.getUserMedia)throw Object.assign(Error(`This browser cannot use the camera here (use localhost or HTTPS).`),{name:`NotFoundError`});return navigator.mediaDevices.getUserMedia({video:{width:{ideal:1280}},audio:!1})}async function Cw(e,t=yw){let n=document.createElement(`video`);n.muted=!0,n.playsInline=!0,n.srcObject=e,await n.play(),await new Promise(e=>setTimeout(e,t));let{width:r,height:i}=bw(n.videoWidth||640,n.videoHeight||480),a=document.createElement(`canvas`);return a.width=r,a.height=i,a.getContext(`2d`).drawImage(n,0,0,r,i),n.srcObject=null,new Promise((e,t)=>a.toBlob(n=>n?e(n):t(Error(`Could not encode the picture.`)),`image/jpeg`,vw))}var ww=class{opts;active=null;constructor(e){this.opts=e}async handle(e){if(e.type===`camera.cancel`){let t=this.active;t&&t.id===e.id&&(t.cancelled=!0,this.release(t));return}if(!this.opts.enabled())return this.opts.send({type:`camera.denied`,id:e.id,reason:`off`});if(this.active)return this.opts.send({type:`camera.denied`,id:e.id,reason:`busy`});let t={id:e.id,cancelled:!1,stream:null,opened:!1,released:!1};this.active=t;try{let n=await(this.opts.open??Sw)();if(t.stream=n,t.cancelled){n.getTracks().forEach(e=>e.stop());return}t.opened=!0,this.opts.dispatch({type:`camera.open`,stream:n});let r=await(this.opts.frame??(e=>Cw(e)))(n);if(t.cancelled)return;this.release(t);try{await this.opts.upload(e.id,r)}catch{this.opts.dispatch({type:`alert.add`,level:`warn`,text:`Could not send the camera picture to Friend.`})}}catch(n){t.cancelled||this.opts.send({type:`camera.denied`,id:e.id,reason:xw(n)})}finally{this.release(t),this.active===t&&(this.active=null)}}dispose(){let e=this.active;e&&(e.cancelled=!0,this.release(e))}release(e){e.released||(e.released=!0,e.stream?.getTracks().forEach(e=>e.stop()),e.opened&&this.opts.dispatch({type:`camera.closed`}))}},Tw={401:`Please sign in again.`,413:`That recording was too long.`,409:`Friend is busy with the last request. Try again in a moment.`,429:`You're talking too fast. Wait a moment.`,503:`Voice is not available. Run: friend voice setup`};async function Ew(e,t){let n;try{n=await fetch(`/api/voice/utterance`,{method:`POST`,headers:{"content-type":e.type||`audio/webm`},body:e,signal:t})}catch(e){throw e.name===`AbortError`?e:Error(`Could not reach Friend. Check the connection.`)}if(!n.ok){let e=``;try{e=(await n.json()).error??``}catch{}throw Error(e||Tw[n.status]||`The voice request failed (${n.status}).`)}let r=await n.json();return{text:typeof r.text==`string`?r.text:``}}var Dw=0;function Ow(e){try{let t=navigator.audioSession;t&&t.type!==e&&(t.type=e)}catch{}}function kw(){Dw===0&&Ow(`playback`)}function Aw(){Dw++,Ow(`play-and-record`);let e=!1;return()=>{e||(e=!0,Dw=Math.max(0,Dw-1),Dw===0&&Ow(`playback`))}}var jw=4096,Mw=8,Nw=20,Pw=6e6;function Fw(e){try{let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n}catch{return null}}var Iw=class{ctx=null;analyser=null;pending=new Map;queue=[];current=null;busy=!1;gen=0;isPlaying=!1;listeners=new Set;unlock(){kw();let e=this.ensureContext();e.state!==`running`&&e.resume().catch(()=>{})}locked(){return!this.ctx||this.ctx.state!==`running`}enqueue(e){if(!Number.isInteger(e.seq)||e.seq<0||e.seq>jw)return;let t=Fw(e.data);if(!t)return;let n=this.pending.get(e.id);if(n||(this.pending.size>=Mw&&this.pending.delete(this.pending.keys().next().value),n={parts:new Map,lastSeq:null,bytes:0},this.pending.set(e.id,n)),n.bytes+=t.length,n.bytes>Pw){this.pending.delete(e.id);return}if(n.parts.set(e.seq,t),e.last&&(n.lastSeq=e.seq),n.lastSeq===null||n.parts.size!==n.lastSeq+1)return;this.pending.delete(e.id);let r=new Uint8Array([...n.parts.values()].reduce((e,t)=>e+t.length,0)),i=0;for(let e=0;e<=n.lastSeq;e++){let t=n.parts.get(e);if(!t)return;r.set(t,i),i+=t.length}for(this.queue.push(r.buffer);this.queue.length>Nw;)this.queue.shift();this.pump()}stop(){this.gen++,this.queue=[],this.pending.clear(),this.busy=!1;let e=this.current;if(this.current=null,e){e.onended=null;try{e.stop()}catch{}}this.setPlaying(!1)}dispose(){this.stop(),this.ctx?.close().catch(()=>{}),this.ctx=null,this.analyser=null}playing(){return this.isPlaying}onChange(e){return this.listeners.add(e),()=>this.listeners.delete(e)}level(){if(!this.analyser||!this.isPlaying)return 0;let e=new Uint8Array(this.analyser.fftSize);this.analyser.getByteTimeDomainData(e);let t=0;for(let n of e)t+=((n-128)/128)**2;return Math.min(1,Math.sqrt(t/e.length)*4)}ensureContext(){if(!this.ctx){let e=window.AudioContext??window.webkitAudioContext;this.ctx=new e,this.analyser=this.ctx.createAnalyser(),this.analyser.fftSize=1024,this.analyser.connect(this.ctx.destination)}return this.ctx}setPlaying(e){this.isPlaying!==e&&(this.isPlaying=e,this.listeners.forEach(t=>t(e)))}async pump(){if(this.busy)return;let e=this.queue.shift();if(!e){this.setPlaying(!1);return}this.busy=!0;let t=this.gen;try{let n=this.ensureContext();if(n.state!==`running`){try{await n.resume()}catch{}if(t!==this.gen)return;if(n.state!==`running`){this.queue.unshift(e),this.busy=!1;return}}let r=await n.decodeAudioData(e);if(t!==this.gen)return;let i=n.createBufferSource();i.buffer=r,i.connect(this.analyser),i.onended=()=>{this.current===i&&(this.current=null,this.busy=!1,this.pump())},this.current=i,this.setPlaying(!0),i.start()}catch{if(t!==this.gen)return;this.busy=!1,this.pump()}}},Lw=[`audio/webm;codecs=opus`,`audio/webm`,`audio/mp4`,`audio/ogg;codecs=opus`],Rw=3e3;function zw(e){let t=e;return t?.name===`NotAllowedError`||t?.name===`SecurityError`?Error(`Microphone permission was denied. Allow it in the browser to talk.`):t?.name===`NotFoundError`||t?.name===`OverconstrainedError`?Error(`No microphone was found.`):Error(t?.message||`Could not open the microphone.`)}var Bw=class e{opts;stream=null;rec=null;chunks=[];ctx=null;analyser=null;timer=null;starting=!1;leaveSession=null;constructor(e={}){this.opts=e}static supported(){return typeof navigator<`u`&&!!navigator.mediaDevices?.getUserMedia&&typeof MediaRecorder<`u`}get active(){return this.rec!==null}async start(){if(!(this.rec||this.starting)){if(!e.supported())throw Error(`This browser cannot record audio here (use localhost or HTTPS).`);this.starting=!0;try{this.leaveSession=Aw();try{this.stream=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0}})}catch(e){throw this.leaveSession?.(),this.leaveSession=null,zw(e)}try{let e=Lw.find(e=>MediaRecorder.isTypeSupported(e));this.chunks=[];let t=new MediaRecorder(this.stream,e?{mimeType:e}:void 0);t.ondataavailable=e=>{e.data&&e.data.size>0&&this.chunks.push(e.data)},t.onerror=()=>this.failed();for(let e of this.stream.getTracks())e.onended=()=>this.failed();this.openMeter(this.stream),t.start(250),this.rec=t,this.opts.maxMs&&(this.timer=setTimeout(()=>{this.timer=null,this.opts.onLimit?.()},this.opts.maxMs))}catch(e){throw this.release(),zw(e)}}finally{this.starting=!1}}}stop(){let e=this.rec;return e?new Promise(t=>{let n=!1,r=null,i=()=>{if(n)return;n=!0,r&&clearTimeout(r);let i=new Blob(this.chunks,{type:e.mimeType||`audio/webm`});this.release(),t(i)};if(r=setTimeout(i,Rw),e.onstop=i,e.state===`inactive`)return i();try{e.stop()}catch{i()}}):Promise.reject(Error(`Not recording.`))}cancel(){let e=this.rec;if(e){e.ondataavailable=null,e.onstop=null,e.onerror=null;try{e.state!==`inactive`&&e.stop()}catch{}}this.release()}level(){if(!this.analyser||!this.rec)return 0;let e=new Uint8Array(this.analyser.fftSize);this.analyser.getByteTimeDomainData(e);let t=0;for(let n of e)t+=((n-128)/128)**2;return Math.min(1,Math.sqrt(t/e.length)*4)}failed(){this.rec&&(this.cancel(),this.opts.onError?.())}openMeter(e){try{this.ctx=new AudioContext,this.analyser=this.ctx.createAnalyser(),this.analyser.fftSize=1024,this.ctx.createMediaStreamSource(e).connect(this.analyser)}catch{this.analyser=null}}release(){this.timer&&clearTimeout(this.timer),this.timer=null,this.stream?.getTracks().forEach(e=>{e.onended=null,e.stop()}),this.ctx?.close().catch(()=>{}),this.stream=null,this.rec=null,this.ctx=null,this.analyser=null,this.chunks=[],this.leaveSession?.(),this.leaveSession=null}},Vw=2e3,Hw=()=>typeof window<`u`&&window.isSecureContext===!1,Uw=50,Ww=class{deps;recorder;player;mode=`idle`;pressed=!1;disposed=!1;uploads=0;toldAboutSound=!1;abort=null;poll=null;unsubscribe;constructor(e){this.deps=e;let t=()=>this.recorderFailed();this.recorder=e.recorder??e.makeRecorder?.(t)??new Bw({maxMs:6e4,onLimit:()=>void this.pressEnd(),onError:t}),this.player=e.player??new Iw,this.unsubscribe=this.player.onChange(e=>{this.mode!==`listening`&&this.mode!==`transcribing`&&this.setMode(e?`speaking`:`idle`)})}async pressStart(){if(!(this.pressed||this.disposed)){if(!this.deps.available()){this.alert(`warn`,`Voice is not available. Run: friend voice setup`);return}if(!(this.deps.supported?.()??Bw.supported())){this.alert(`warn`,Hw()?`This page is not secure, so the browser blocks the microphone. On the PC run: friend lan https on, restart Friend, and open the https:// address.`:`This browser cannot record audio here (use localhost or HTTPS).`);return}this.pressed=!0,this.player.unlock(),this.player.playing()&&(this.deps.send({type:`voice.interrupt`}),this.player.stop());try{await this.recorder.start()}catch(e){this.pressed=!1,this.alert(`error`,e.message),this.setMode(`idle`);return}if(!this.pressed){this.recorder.cancel(),this.setMode(`idle`);return}this.setMode(`listening`)}}async pressEnd(){if(!this.pressed||(this.pressed=!1,!this.recorder.active))return;let e=await this.recorder.stop();if(e.size<Vw){this.alert(`info`,`Hold the key a little longer while you speak.`),this.setMode(`idle`);return}this.setMode(`transcribing`);let t=new AbortController;this.abort=t,this.uploads++;try{let{text:n}=await this.deps.post(e,t.signal);n.trim()||this.alert(`info`,`I didn't catch that.`)}catch(e){e.name!==`AbortError`&&this.alert(`error`,e.message)}finally{this.uploads--,this.abort===t&&(this.abort=null),this.settle()}}blur(){this.pressed&&(this.pressed=!1,this.recorder.cancel(),this.setMode(`idle`))}handleServer(e){if(e.type!==`audio.chunk`)return this.player.stop();this.player.enqueue(e),!this.toldAboutSound&&this.player.locked?.()&&(this.toldAboutSound=!0,this.deps.dispatch({type:`alert.add`,level:`info`,text:`Sound is blocked by the browser. Click or press a key once anywhere on the page to hear me.`}))}unlock(){this.player.unlock()}dispose(){this.disposed=!0,this.pressed=!1,this.stopPolling(),this.abort?.abort(),this.recorder.cancel(),this.player.stop(),this.player.dispose?.(),this.unsubscribe()}recorderFailed(){this.pressed=!1,this.recorder.cancel(),this.alert(`error`,`The microphone stopped working.`),this.setMode(`idle`)}settle(){this.uploads>0||this.mode!==`transcribing`||this.setMode(this.player.playing()?`speaking`:`idle`)}setMode(e){this.disposed||(this.mode=e,this.deps.dispatch({type:`voice.mode`,mode:e}),e===`listening`||e===`speaking`?this.startPolling():this.stopPolling())}startPolling(){this.stopPolling(),this.poll=setInterval(()=>{let e=this.mode===`listening`?this.recorder.level():this.player.level();this.deps.dispatch({type:`voice.level`,level:e})},Uw)}stopPolling(){this.poll&&clearInterval(this.poll),this.poll=null}alert(e,t){this.disposed||this.deps.dispatch({type:`alert.add`,level:e,text:t})}};function Gw(e){let[t,n]=(0,l.useReducer)(gw,uw),r=(0,l.useRef)(null),i=(0,l.useRef)(null),a=(0,l.useRef)(null),o=(0,l.useRef)(e);o.current=e;let s=(0,l.useRef)(t);return s.current=t,(0,l.useEffect)(()=>{let e=lw();r.current=e;let t=new Ww({send:t=>e.send(t),dispatch:e=>n(e),post:Ew,available:()=>s.current.voice.available});i.current=t;let c=new ww({send:t=>e.send(t),dispatch:e=>n(e),enabled:()=>s.current.camera.enabled,upload:async(e,t)=>{let n=await fetch(`/api/camera/frame/${encodeURIComponent(e)}`,{method:`POST`,headers:{"content-type":`image/jpeg`},body:t});if(!n.ok)throw Error(`upload failed (${n.status})`)}});a.current=c;let l=e.onMessage(e=>{n(e);let r=e?.type,i=e?.id;(r===`camera.request`||r===`camera.cancel`)&&typeof i==`string`&&c.handle({type:r,id:i}),(r===`audio.chunk`||r===`audio.stop`)&&t.handleServer(e)}),u=e.onState(e=>{n({type:`conn`,value:e}),e===`unauthorized`&&o.current(),e===`elsewhere`&&(t.blur(),c.dispose())});return()=>{l(),u(),t.dispose(),c.dispose(),e.close(),i.current=null,a.current=null,r.current=null}},[]),(0,l.useEffect)(()=>{t.camera.enabled||a.current?.dispose()},[t.camera.enabled]),{state:t,send:(0,l.useCallback)(e=>r.current?.send(e),[]),dispatch:(0,l.useCallback)(e=>n(e),[]),voice:(0,l.useMemo)(()=>({pressStart:()=>void i.current?.pressStart(),pressEnd:()=>void i.current?.pressEnd(),blur:()=>i.current?.blur(),unlock:()=>i.current?.unlock()}),[]),useHere:(0,l.useCallback)(()=>r.current?.takeOver(),[])}}var Kw={401:`Please sign in again.`,403:`Incorrect PIN.`,413:`That recording was too long.`,429:`Too many attempts. Wait a moment.`,503:`Voice is not available. Run: friend voice setup`};async function qw(e,t){let n;try{n=await fetch(e,t)}catch{throw Error(`Could not reach Friend. Check the connection.`)}if(!n.ok){let e=``;try{e=(await n.json()).error??``}catch{}throw Error(e||Kw[n.status]||`The request failed (${n.status}).`)}return await n.json()}var Jw=(e,t)=>({method:e,headers:{"content-type":`application/json`},body:JSON.stringify(t)}),Yw={state:()=>qw(`/api/voice/enroll`,{}),sample:e=>qw(`/api/voice/enroll/sample`,{method:`POST`,headers:{"content-type":e.type||`audio/webm`},body:e}),finish:e=>qw(`/api/voice/enroll/finish`,Jw(`POST`,{pin:e})),cancel:async()=>{await qw(`/api/voice/enroll/cancel`,Jw(`POST`,{}))},remove:async e=>{await qw(`/api/voice/enroll`,Jw(`DELETE`,{pin:e}))}},Xw=[`Friend, good evening. It is a pleasant day to get things done.`,`Open my projects and show me what changed today.`,`My voice is my key, so please remember it well.`,`Check the weather and read me the latest headlines.`,`Always protect the boss, and never trust a stranger.`],Zw=15e3;function Qw({api:e=Yw,createRecorder:t=e=>new Bw(e),onClose:n,who:r}){let[i,a]=(0,l.useState)(null),[o,s]=(0,l.useState)(0),[c,u]=(0,l.useState)(null),[f,m]=(0,l.useState)(`record`),[h,g]=(0,l.useState)(!1),[_,v]=(0,l.useState)(!1),[y,b]=(0,l.useState)(null),[S,C]=(0,l.useState)(``),w=(0,l.useRef)(!0),T=(0,l.useRef)(null);(0,l.useEffect)(()=>(w.current=!0,e.state().then(e=>{w.current&&(a(e),s(e.samples))}).catch(e=>w.current&&b(e.message)),()=>{w.current=!1,T.current?.cancel(),T.current=null}),[e]);let E=(0,l.useCallback)(async()=>{let t=T.current;if(t){T.current=null,g(!1),v(!0);try{let n=await t.stop(),r=await e.sample(n);if(!w.current)return;s(r.count),u(r.note)}catch(e){w.current&&b(e.message)}finally{w.current&&v(!1)}}},[e]),D=async()=>{b(null),u(null);let e=t({maxMs:Zw,onLimit:()=>void E(),onError:()=>{T.current=null,w.current&&(g(!1),b(`The microphone stopped working. Try again.`))}});try{await e.start()}catch(e){w.current&&b(e.message);return}if(!w.current)return e.cancel();T.current=e,g(!0)},O=async e=>{b(null),v(!0);try{await e()}catch(e){w.current&&b(e.message)}finally{w.current&&(v(!1),C(``))}},k=async()=>{T.current?.cancel(),T.current=null,await e.cancel().catch(()=>{}),n()},A=i?.min??3,j=o%Xw.length,ee=o<Xw.length?`Phrase ${o+1} of ${Xw.length}`:`Extra recording (${o} so far)`;return(0,p.jsxs)(`div`,{className:`approval enroll glass`,role:`dialog`,"aria-label":`Voice enrollment`,style:{pointerEvents:`auto`},children:[(0,p.jsx)(`div`,{className:`approval-head`,children:(0,p.jsx)(`span`,{className:`approval-title`,children:r?`Teach Friend ${r}'s voice`:`Teach Friend your voice`})}),f===`record`&&(0,p.jsxs)(p.Fragment,{children:[!r&&i?.enrolled&&(0,p.jsx)(`p`,{className:`auth-note`,children:`Your voice is already saved. Saving a new one replaces it.`}),r&&(0,p.jsxs)(`p`,{className:`auth-note`,children:[`Hand the device to `,r,`. `,r,` reads each sentence aloud, close to the microphone, in a quiet room.`]}),(0,p.jsxs)(`p`,{className:`auth-note`,children:[ee,`. Press Record, read this aloud in a quiet room, then press Stop.`]}),(0,p.jsx)(`pre`,{className:`approval-summary`,"data-testid":`enroll-phrase`,style:{whiteSpace:`pre-wrap`},children:Xw[j]}),(0,p.jsxs)(`div`,{role:`status`,className:`auth-note`,children:[o,` recorded`,o>=A?` — enough to save`:` — at least ${A} needed`,`.`,c?` ${c}`:``]}),y&&(0,p.jsx)(`p`,{role:`alert`,className:`auth-error`,children:y}),(0,p.jsxs)(`div`,{className:`approval-actions`,children:[h?(0,p.jsx)(`button`,{type:`button`,style:d,className:`danger`,onClick:()=>void E(),children:`Stop`}):(0,p.jsx)(`button`,{type:`button`,style:d,className:`primary`,disabled:_||!i||o>=(i?.max??8),onClick:()=>void D(),children:_?`Uploading…`:`Record`}),(0,p.jsx)(`button`,{type:`button`,style:d,disabled:h||_||o<A,onClick:()=>m(`pin-save`),children:r?`Save ${r}'s voice`:`Save my voice`}),!r&&i?.enrolled&&(0,p.jsx)(`button`,{type:`button`,style:d,className:`danger`,disabled:h||_,onClick:()=>m(`pin-delete`),children:`Delete my voice`}),(0,p.jsx)(`button`,{type:`button`,style:d,onClick:()=>void k(),children:`Cancel`})]})]}),(f===`pin-save`||f===`pin-delete`)&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(`p`,{className:`auth-note`,children:f===`pin-save`?r?`Enter your PIN to save ${r}'s voice.`:`Enter your PIN to save your voice.`:`Enter your PIN to delete your saved voice.`}),(0,p.jsx)(x,{pin:S,onChange:C,onSubmit:f===`pin-save`?()=>!_&&void O(async()=>{await e.finish(S),w.current&&m(`saved`)}):void 0}),y&&(0,p.jsx)(`p`,{role:`alert`,className:`auth-error`,children:y}),(0,p.jsxs)(`div`,{className:`approval-actions`,children:[f===`pin-save`?(0,p.jsx)(`button`,{type:`button`,style:d,className:`primary`,disabled:_||S.length<6,onClick:()=>void O(async()=>{await e.finish(S),w.current&&m(`saved`)}),children:`Save`}):(0,p.jsx)(`button`,{type:`button`,style:d,className:`danger`,disabled:_||S.length<6,onClick:()=>void O(async()=>{await e.remove(S),w.current&&m(`deleted`)}),children:`Delete`}),(0,p.jsx)(`button`,{type:`button`,style:d,onClick:()=>{C(``),b(null),m(`record`)},children:`Back`})]})]}),(f===`saved`||f===`deleted`)&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(`p`,{className:`auth-note`,children:f===`saved`?r?`${r}'s voice is saved. From now on I will know when ${r} is talking.`:`Your voice is saved. From now on I will tell you apart from other speakers.`:`Your voice was deleted. Every voice is treated as yours until you enroll again.`}),(0,p.jsx)(`div`,{className:`approval-actions`,children:(0,p.jsx)(`button`,{type:`button`,style:d,className:`primary`,onClick:n,children:`Done`})})]})]})}var $w=new Set([`INPUT`,`TEXTAREA`,`SELECT`,`BUTTON`,`A`,`SUMMARY`]),eT=new Set([`button`,`checkbox`,`switch`,`tab`,`menuitem`,`link`,`radio`,`option`,`textbox`,`combobox`,`slider`]),tT=e=>e instanceof HTMLElement&&($w.has(e.tagName)||e.isContentEditable||eT.has(e.getAttribute(`role`)??``)),nT=e=>e.code===`Space`||e.key===` `;function rT(e){let t=(0,l.useRef)(e);t.current=e,(0,l.useEffect)(()=>{let e=!1,n=n=>{!nT(n)||n.ctrlKey||n.metaKey||n.altKey||tT(n.target)||tT(document.activeElement)||(n.preventDefault(),!(n.repeat||e)&&(e=!0,t.current.pressStart()))},r=n=>{nT(n)&&e&&(e=!1,n.preventDefault(),t.current.pressEnd())},i=()=>{e=!1,t.current.blur()},a=()=>{document.hidden&&i()};return window.addEventListener(`keydown`,n),window.addEventListener(`keyup`,r),window.addEventListener(`blur`,i),document.addEventListener(`visibilitychange`,a),()=>{window.removeEventListener(`keydown`,n),window.removeEventListener(`keyup`,r),window.removeEventListener(`blur`,i),document.removeEventListener(`visibilitychange`,a)}},[])}function iT(e){let t=(0,l.useRef)(e);t.current=e,(0,l.useEffect)(()=>{let e=()=>t.current.unlock(),n=[`pointerdown`,`keydown`,`touchend`];for(let t of n)window.addEventListener(t,e,{passive:!0});return()=>{for(let t of n)window.removeEventListener(t,e)}},[])}function aT({onUseHere:e}){return(0,p.jsx)(`div`,{className:`elsewhere`,role:`alertdialog`,"aria-labelledby":`elsewhere-title`,"data-testid":`elsewhere`,children:(0,p.jsxs)(`div`,{className:`elsewhere-card`,children:[(0,p.jsx)(`h2`,{id:`elsewhere-title`,children:`Friend is open in another window`}),(0,p.jsx)(`p`,{children:`Friend works in one window at a time. Click “Use here” to use it in this window instead; the other one will stop.`}),(0,p.jsx)(`button`,{type:`button`,className:`elsewhere-btn`,onClick:e,autoFocus:!0,children:`Use here`})]})})}function oT(e,t,n){let r=Math.max(100,Math.round(e)),i=Math.max(100,Math.round(t));return{width:r,height:i,kind:r<=599?`phone`:n||r<1024?`tablet`:`desktop`,orientation:i>r?`portrait`:`landscape`,touch:n}}var sT=()=>typeof matchMedia==`function`&&matchMedia(`(pointer: coarse)`).matches;function cT(){return oT(window.innerWidth,window.innerHeight,sT())}var lT=(e,t)=>!!e&&e.width===t.width&&e.height===t.height&&e.kind===t.kind&&e.orientation===t.orientation&&e.touch===t.touch;function uT(e,t){let n=(0,l.useRef)(null),r=(0,l.useRef)(e);r.current=e,(0,l.useEffect)(()=>{if(t!==`open`){n.current=null;return}let e=()=>{let e=cT();lT(n.current,e)||(n.current=e,r.current({type:`ui.screen`,...e}))};e();let i=null,a=()=>{i&&clearTimeout(i),i=setTimeout(e,400)};return window.addEventListener(`resize`,a),window.addEventListener(`orientationchange`,a),()=>{i&&clearTimeout(i),window.removeEventListener(`resize`,a),window.removeEventListener(`orientationchange`,a)}},[t])}var dT={401:`Please sign in again.`,403:`Incorrect PIN.`,413:`That recording was too long.`,429:`Too many attempts. Wait a moment.`,503:`Voice is not available. Run: friend voice setup`};async function fT(e,t={}){let n;try{n=await fetch(e,t)}catch{throw Error(`Could not reach Friend. Check the connection.`)}if(!n.ok){let e=``;try{e=(await n.json()).error??``}catch{}throw Error(e||dT[n.status]||`The request failed (${n.status}).`)}return await n.json()}var pT=(e,t)=>({method:e,headers:{"content-type":`application/json`},body:JSON.stringify(t)}),mT={overview:()=>fT(`/api/voice/people`),begin:e=>fT(`/api/voice/people/begin`,pT(`POST`,{name:e})),recording:{state:async()=>(await fT(`/api/voice/people`)).enrolling??{enrolled:!1,samples:0,min:3,max:8},sample:e=>fT(`/api/voice/people/sample`,{method:`POST`,headers:{"content-type":e.type||`audio/webm`},body:e}),finish:e=>fT(`/api/voice/people/finish`,pT(`POST`,{pin:e})),cancel:async()=>{await fT(`/api/voice/people/cancel`,pT(`POST`,{}))},remove:async()=>{}},removePerson:async(e,t)=>{await fT(`/api/voice/people/${e}`,pT(`DELETE`,{pin:t}))}};function hT({api:e=mT,createRecorder:t,onClose:n}){let[r,i]=(0,l.useState)(null),[a,o]=(0,l.useState)({kind:`list`}),[s,c]=(0,l.useState)(``),[u,f]=(0,l.useState)(``),[m,h]=(0,l.useState)(!1),[g,_]=(0,l.useState)(null),v=(0,l.useRef)(!0),y=(0,l.useCallback)(()=>{e.overview().then(e=>v.current&&i(e)).catch(e=>v.current&&_(e.message))},[e]);(0,l.useEffect)(()=>(v.current=!0,y(),()=>{v.current=!1}),[y]);let b=async()=>{_(null),h(!0);try{let t=await e.begin(s.trim());v.current&&o({kind:`record`,name:t.name})}catch(e){v.current&&_(e.message)}finally{v.current&&h(!1)}},S=async t=>{_(null),h(!0);try{if(await e.removePerson(t.id,u),!v.current)return;o({kind:`list`}),y()}catch(e){v.current&&_(e.message)}finally{v.current&&(h(!1),f(``))}};return a.kind===`record`?(0,p.jsx)(Qw,{api:e.recording,who:a.name,...t?{createRecorder:t}:{},onClose:()=>{c(``),o({kind:`list`}),y()}}):(0,p.jsxs)(`div`,{className:`approval enroll glass`,role:`dialog`,"aria-label":`Family voices`,style:{pointerEvents:`auto`},children:[(0,p.jsx)(`div`,{className:`approval-head`,children:(0,p.jsx)(`span`,{className:`approval-title`,children:`Family voices`})}),a.kind===`list`&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(`p`,{className:`auth-note`,children:`Friend knows your voice. Add the people at home, so it knows who is talking and answers them by name. They can chat and ask for the weather, but cannot use your PC or see your private things.`}),r&&!r.ownerEnrolled&&(0,p.jsx)(`p`,{className:`auth-note`,children:`First teach Friend your own voice (Esc, then Enroll my voice).`}),r&&r.people.length===0&&r.ownerEnrolled&&(0,p.jsx)(`p`,{className:`auth-note`,children:`Nobody added yet.`}),r&&r.people.length>0&&(0,p.jsx)(`ul`,{className:`household-list`,"aria-label":`People Friend knows`,children:r.people.map(e=>(0,p.jsxs)(`li`,{className:`household-row`,children:[(0,p.jsx)(`span`,{children:e.name}),(0,p.jsx)(`button`,{type:`button`,style:d,className:`danger`,onClick:()=>o({kind:`remove`,person:e}),"aria-label":`Remove ${e.name}`,children:`Remove`})]},e.id))}),r&&r.ownerEnrolled&&r.people.length<r.max&&(0,p.jsxs)(`label`,{className:`settings-field`,children:[(0,p.jsx)(`span`,{children:`Add a person`}),(0,p.jsx)(`input`,{value:s,maxLength:40,placeholder:`Name, for example Priya`,onChange:e=>c(e.target.value),onKeyDown:e=>e.key===`Enter`&&s.trim()&&!m&&void b()})]}),g&&(0,p.jsx)(`p`,{role:`alert`,className:`auth-error`,children:g}),(0,p.jsxs)(`div`,{className:`approval-actions`,children:[r&&r.ownerEnrolled&&r.people.length<r.max&&(0,p.jsx)(`button`,{type:`button`,style:d,className:`primary`,disabled:m||!s.trim(),onClick:()=>void b(),children:`Record their voice`}),(0,p.jsx)(`button`,{type:`button`,style:d,onClick:n,children:`Close`})]})]}),a.kind===`remove`&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)(`p`,{className:`auth-note`,children:[`Enter your PIN to remove `,a.person.name,`’s voice. Friend will treat their voice as a stranger’s.`]}),(0,p.jsx)(x,{pin:u,onChange:f}),g&&(0,p.jsx)(`p`,{role:`alert`,className:`auth-error`,children:g}),(0,p.jsxs)(`div`,{className:`approval-actions`,children:[(0,p.jsx)(`button`,{type:`button`,style:d,className:`danger`,disabled:m||u.length<6,onClick:()=>void S(a.person),children:`Remove`}),(0,p.jsx)(`button`,{type:`button`,style:d,onClick:()=>{f(``),_(null),o({kind:`list`})},children:`Back`})]})]})]})}function gT({onSignedOut:e}){let{state:t,send:n,dispatch:r,voice:i,useHere:a}=Gw(e);rT(i),iT(i),uT(n,t.connection);let[o,s]=(0,l.useState)(!1),[c,u]=(0,l.useState)(!1),[d,f]=(0,l.useState)(!1),[m,h]=(0,l.useState)(!1),[g,_]=(0,l.useState)(null),[v,y]=(0,l.useState)(!1),[b,x]=(0,l.useState)(0),[S,C]=(0,l.useState)(null),w=de(t.view),T=(0,l.useMemo)(()=>o?{...t,objects:[],captions:``}:t,[o,t]),E=(0,l.useRef)(!1);(0,l.useEffect)(()=>{E.current||!t.voice.available||t.voice.enrolled||(E.current=!0,r({type:`alert.add`,level:`info`,text:`Voice recognition is not set up. Press Esc, then Enroll my voice.`}))},[t.voice.available,t.voice.enrolled,r]);let D=ne(),O=(0,l.useRef)(!1);(0,l.useEffect)(()=>{if(!D||O.current||t.connection!==`open`)return;O.current=!0;let e=!1;try{e=localStorage.getItem(`friend.touchHint`)===`1`,localStorage.setItem(`friend.touchHint`,`1`)}catch{}e||r({type:`alert.add`,level:`info`,text:`Hold the round mic button to talk, or tap it once to start and again to send. The keyboard button types; the menu button opens settings.`})},[D,t.connection,r]);let k=(0,l.useRef)(!1);(0,l.useEffect)(()=>{k.current||t.connection!==`open`||(k.current=!0,fetch(`/api/history`).then(e=>e.ok?e.json():[]).then(e=>r({type:`chat.seed`,lines:e})).catch(()=>{}))},[t.connection,r]),(0,l.useEffect)(()=>{t.connection===`open`&&n({type:`ui.panels`,open:[...w.show.chat?[`chat`]:[],...w.show.tasks?[`tasks`]:[]]})},[t.connection,w.show.chat,w.show.tasks,n]);let j=(0,l.useRef)(!1);(0,l.useEffect)(()=>{t.viewKnown&&t.view===`minimal`&&!j.current&&(j.current=!0,r({type:`alert.add`,level:`info`,text:`The view is Minimal, so the status line and clock are hidden. Press Esc, then View, to bring them back.`}))},[t.viewKnown,t.view,r]);let ee=e=>{Ie.save({view:e}).catch(e=>r({type:`alert.add`,level:`warn`,text:e.message}))},M=(0,l.useMemo)(()=>F({notify:e=>r({type:`alert.add`,level:`info`,text:e}),openSettings:()=>_(`model`),openLook:()=>_(`look`),openConnectors:()=>y(!0),openPlugins:()=>h(!0),openVoice:t.voice.available?()=>u(!0):null,muted:o,setMuted:s,cameraOn:t.camera.enabled,setCamera:e=>r({type:`camera.enable`,value:e}),clearScreen:()=>{for(let e of t.objects)n({type:`stage.event`,objectId:e.id,event:`dismiss`})},statusLine:()=>Ce({connection:t.connection,deviceOnline:t.deviceOnline,approval:t.approval,voiceMode:t.voice.mode,busy:t.busy,activity:t.activity}).text,endSession:t=>{fetch(t===`lock`?`/api/lock`:`/api/logout`,{method:`POST`,headers:{"content-type":`application/json`},body:`{}`}).catch(()=>{}).finally(e)},panelOn:e=>w.show[e],setPanel:(e,t)=>w.toggle(e,t),view:t.view,setView:ee,openHelp:C,resetLayout:()=>{ue(),window.location.reload()}}),[r,n,o,e,w.show,w.toggle,t.view,t.voice.available,t.voice.mode,t.camera.enabled,t.objects,t.connection,t.deviceOnline,t.approval,t.busy,t.activity]);return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(ow,{state:T,send:n,dispatch:r,voice:i,slash:M,onMenu:()=>x(e=>e+1)}),(0,p.jsxs)(Ee,{approval:t.approval,challenge:t.challenge,cameraStream:t.camera.stream,alerts:t.alerts,connection:t.connection,deviceOnline:t.deviceOnline,voiceMode:t.voice.mode,busy:t.busy,activity:t.activity,showStatus:t.viewKnown&&w.show.status,showClock:t.viewKnown&&w.show.clock,profile:t.viewKnown&&w.show.profile?{ownerName:t.ownerName,speaker:t.speaker}:null,send:n,dispatch:r,children:[t.viewKnown&&w.show.chat&&(0,p.jsx)(fe,{lines:t.chat,live:t.chatLive,onSend:e=>n({type:`chat.send`,text:e}),onClose:()=>w.toggle(`chat`,!1)}),t.viewKnown&&w.show.tasks&&(0,p.jsx)(he,{busy:t.busy,onClose:()=>w.toggle(`tasks`,!1)}),c&&(0,p.jsx)(Qw,{onClose:()=>u(!1)}),d&&(0,p.jsx)(hT,{onClose:()=>f(!1)}),m&&(0,p.jsx)(je,{onClose:()=>h(!1)}),g&&(0,p.jsx)(Ay,{initialTab:g,onClose:()=>_(null)}),v&&(0,p.jsx)(A,{onClose:()=>y(!1)}),S&&(0,p.jsx)(I,{commands:S,onClose:()=>C(null)})]}),(0,p.jsx)(V,{muted:o,onToggleMute:()=>s(e=>!e),onSignedOut:e,voiceAvailable:t.voice.available,voiceEnrolled:t.voice.enrolled,onEnroll:()=>u(!0),onHousehold:()=>f(!0),onPlugins:()=>h(!0),onSettings:()=>_(`model`),onConnectors:()=>y(!0),cameraOn:t.camera.enabled,onToggleCamera:()=>r({type:`camera.enable`,value:!t.camera.enabled}),chatOn:w.show.chat,onToggleChat:()=>w.toggle(`chat`),tasksOn:w.show.tasks,onToggleTasks:()=>w.toggle(`tasks`),view:t.view,onView:ee,openRequest:b}),t.connection===`elsewhere`&&(0,p.jsx)(aT,{onUseHere:a})]})}function _T(){let[e,t]=(0,l.useState)(`loading`);return(0,l.useEffect)(()=>{let e=!0,n=n=>e&&t(n);return(async()=>{try{if(!(await(await fetch(`/api/state`)).json()).hasOwner)return n(`setup`);let e=await(await fetch(`/api/session`,{cache:`no-store`})).json();n(e.signedIn===!0?`stage`:`login`)}catch{n(`login`)}})(),()=>{e=!1}},[]),e===`loading`?(0,p.jsx)(`div`,{className:`boot`,"aria-busy":`true`}):e===`setup`?(0,p.jsx)(g,{onDone:()=>t(`login`)}):e===`login`?(0,p.jsx)(h,{onLoggedIn:()=>t(`stage`)}):(0,p.jsx)(gT,{onSignedOut:()=>t(`login`)})}(0,u.createRoot)(document.getElementById(`root`)).render((0,p.jsx)(l.StrictMode,{children:(0,p.jsx)(_T,{})}));
|
|
4453
|
+
Please report this to https://github.com/markedjs/marked.`,e){let e=`<p>An error occurred:</p><pre>`+IS(n.message+``,!0)+`</pre>`;return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}};function tC(e,t){return eC.parse(e,t)}tC.options=tC.setOptions=function(e){return eC.setOptions(e),tC.defaults=eC.defaults,px(tC.defaults),tC},tC.getDefaults=dx,tC.defaults=fx;function nC(...e){return eC.use(...e),tC.defaults=eC.defaults,px(tC.defaults),tC}tC.use=nC,tC.walkTokens=function(e,t){return eC.walkTokens(e,t)},tC.parseInline=eC.parseInline,tC.Parser=QS,tC.parser=QS.parse,tC.Renderer=XS,tC.TextRenderer=ZS,tC.Lexer=YS,tC.lexer=YS.lex,tC.Tokenizer=JS,tC.Hooks=$S,tC.parse=tC,tC.options,tC.setOptions,tC.walkTokens,tC.parseInline,QS.parse,YS.lex;var rC=!1;function iC(){rC||(rC=!0,ux.addHook(`afterSanitizeAttributes`,e=>{e instanceof Element&&e.tagName===`A`&&(e.setAttribute(`rel`,`noopener noreferrer`),e.setAttribute(`target`,`_blank`))}))}function aC(e){iC();let t=tC.parse(e,{async:!1,breaks:!0});return ux.sanitize(t,{ALLOWED_TAGS:[`p`,`br`,`strong`,`em`,`code`,`pre`,`ul`,`ol`,`li`,`a`,`h1`,`h2`,`h3`,`h4`,`blockquote`,`hr`,`del`],ALLOWED_ATTR:[`href`,`rel`,`target`],ALLOWED_URI_REGEXP:/^(?:https?:|mailto:)/i})}var oC=e=>Number.isFinite(e);function sC(e){let t=Math.floor(Math.log10(e)),n=e/10**t;return(n<=1?1:n<=2?2:n<=2.5?2.5:n<=5?5:10)*10**t}function cC(e,t,n=5){let r=oC(e)?e:0,i=oC(t)?t:r+1;r>i&&([r,i]=[i,r]),r===i&&(i=r+(Math.abs(r)||1));let a=sC((i-r)/Math.max(1,n-1)),o=Math.floor(r/a)*a,s=Math.ceil(i/a)*a,c=[];for(let e=o;e<=s+a/2;e+=a)c.push(Math.round(e*1e9)/1e9);return{min:o,max:s,ticks:c}}function lC(e,t){let[n,r]=e,[i,a]=t;return r===n?()=>i:e=>i+(e-n)/(r-n)*(a-i)}function uC(e){return e.map(([e,t],n)=>`${n===0?`M`:`L`}${e.toFixed(2)} ${t.toFixed(2)}`).join(` `)}function dC(e,t,n,r,i){let a=r=>{let i=r*Math.PI/180;return`${(e+n*Math.cos(i)).toFixed(2)} ${(t+n*Math.sin(i)).toFixed(2)}`},o=+(Math.abs(i-r)>180);return`M ${a(r)} A ${n} ${n} 0 ${o} 1 ${a(i)}`}function fC(e,t,n){return!oC(e)||!oC(t)||!oC(n)||n<=t?0:Math.min(1,Math.max(0,(e-t)/(n-t)))*240}function pC(e,t,n,r){let i=t.x-e.x,a=t.y-e.y,o=Math.hypot(i,a);if(o===0||o<=n+r)return{from:{...e},to:{...t}};let s=i/o,c=a/o;return{from:{x:e.x+s*n,y:e.y+c*n},to:{x:t.x-s*r,y:t.y-c*r}}}function mC(e,t,n){if(e<=0)return[];if(e===1)return[{x:t/2,y:n/2}];let r=Math.min(t,n)*.36;return Array.from({length:e},(i,a)=>{let o=-Math.PI/2+a*2*Math.PI/e;return{x:t/2+r*Math.cos(o),y:n/2+r*Math.sin(o)}})}var hC=e=>typeof e==`object`&&!!e&&!Array.isArray(e),gC=e=>typeof e==`string`?e:typeof e==`number`?String(e):``,_C=e=>Array.isArray(e)?e:[],vC=e=>typeof e==`number`?Number.isFinite(e)?e:null:typeof e==`string`&&e.trim()!==``&&Number.isFinite(Number(e))?Number(e):null,yC=(e,t)=>e.length>t?e.slice(0,t)+`…`:e,bC=[`var(--accent)`,`#ffb454`,`#7b8cff`,`#5ef0b0`,`#ff6b7a`,`#c792ea`],xC=({text:e})=>e?(0,p.jsx)(`h3`,{className:`obj-title`,children:e}):null,SC=({text:e=`No data`})=>(0,p.jsx)(`p`,{className:`obj-empty`,children:e}),CC=400,wC=220,TC={l:44,r:12,t:10,b:28};function EC({props:e}){let t=e.kind===`bar`||e.kind===`area`?e.kind:`line`,n=_C(e.labels).slice(0,120).map(gC),r=_C(e.series).slice(0,6).map((e,t)=>{let n=hC(e)?e:{},r=_C(n.values).slice(0,120).map((e,t)=>({idx:t,v:vC(e)})).filter(e=>e.v!==null);return{name:gC(n.name)||`series ${t+1}`,points:r}}).filter(e=>e.points.length>0);if(r.length===0)return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(xC,{text:gC(e.title)}),(0,p.jsx)(SC,{})]});let i=r.flatMap(e=>e.points.map(e=>e.v)),a=Math.min(...i),o=Math.max(...i),s=cC(t===`line`?a:Math.min(0,a),t===`line`?o:Math.max(0,o),5),c=Math.max(n.length,...r.map(e=>e.points[e.points.length-1].idx+1),2),l=lC([s.min,s.max],[wC-TC.b,TC.t]),u=lC([0,c-1],[TC.l,CC-TC.r]),d=(CC-TC.l-TC.r)/c,f=Math.max(1,d*.7/r.length),m=(e,t)=>TC.l+e*d+d*.15+t*f,h=Math.max(1,Math.ceil(c/6)),g=gC(e.unit);return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(xC,{text:gC(e.title)}),(0,p.jsxs)(`svg`,{className:`obj-chart`,viewBox:`0 0 ${CC} ${wC}`,role:`img`,"aria-label":gC(e.title)||`chart`,children:[s.ticks.map(e=>(0,p.jsxs)(`g`,{children:[(0,p.jsx)(`line`,{className:`grid`,x1:TC.l,x2:CC-TC.r,y1:l(e),y2:l(e)}),(0,p.jsxs)(`text`,{className:`tick`,x:TC.l-6,y:l(e)+3,textAnchor:`end`,children:[e,g&&e===s.max?g:``]})]},e)),Array.from({length:c},(e,t)=>t).filter(e=>e%h===0&&n[e]).map(e=>(0,p.jsx)(`text`,{className:`tick`,x:t===`bar`?m(e,0)+f*r.length/2:u(e),y:212,textAnchor:`middle`,children:yC(n[e],8)},e)),r.map((e,n)=>{let r=bC[n%bC.length];if(t===`bar`)return(0,p.jsx)(`g`,{children:e.points.map(e=>(0,p.jsx)(`rect`,{className:`bar`,x:m(e.idx,n),y:Math.min(l(e.v),l(0)),width:f,height:Math.abs(l(e.v)-l(0)),fill:r},e.idx))},n);let i=e.points.map(e=>[u(e.idx),l(e.v)]),a=uC(i),o=`${a} L${i[i.length-1][0].toFixed(2)} ${l(s.min).toFixed(2)} L${i[0][0].toFixed(2)} ${l(s.min).toFixed(2)} Z`;return(0,p.jsxs)(`g`,{children:[t===`area`&&(0,p.jsx)(`path`,{className:`series-area`,d:o,fill:r,opacity:.18}),(0,p.jsx)(`path`,{className:`series-line`,d:a,fill:`none`,stroke:r,strokeWidth:2,strokeLinejoin:`round`})]},n)})]}),r.length>1&&(0,p.jsx)(`ul`,{className:`legend`,children:r.map((e,t)=>(0,p.jsxs)(`li`,{children:[(0,p.jsx)(`i`,{style:{background:bC[t%bC.length]}}),(0,p.jsx)(`span`,{children:e.name})]},t))})]})}function DC({props:e}){let t=vC(e.min)??0,n=vC(e.max)??100,r=vC(e.value)??t,i=fC(r,t,n),a=i/240;return(0,p.jsxs)(`div`,{className:`obj-gauge ${a>.95?`gauge-crit`:a>.8?`gauge-warn`:``}`,children:[(0,p.jsxs)(`svg`,{viewBox:`0 0 200 150`,role:`img`,"aria-label":`${gC(e.label)} ${r}`,children:[(0,p.jsx)(`path`,{className:`track`,d:dC(100,100,70,150,390),fill:`none`,strokeWidth:12,strokeLinecap:`round`}),i>0&&(0,p.jsx)(`path`,{className:`value`,d:dC(100,100,70,150,150+i),fill:`none`,strokeWidth:12,strokeLinecap:`round`})]}),(0,p.jsxs)(`div`,{className:`gauge-read`,children:[(0,p.jsx)(`span`,{className:`gauge-value`,children:String(Math.round(r*100)/100)}),gC(e.unit)&&(0,p.jsx)(`small`,{className:`gauge-unit`,children:gC(e.unit)})]}),(0,p.jsx)(`span`,{className:`obj-label`,children:gC(e.label)})]})}function OC({props:e}){let t=_C(e.items).slice(0,30).map(e=>hC(e)?e:{}).map(e=>({time:gC(e.time),title:gC(e.title),detail:gC(e.detail)})).filter(e=>e.title||e.time);return t.length===0?(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(xC,{text:gC(e.title)}),(0,p.jsx)(SC,{text:`Nothing to show`})]}):(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(xC,{text:gC(e.title)}),(0,p.jsx)(`ol`,{className:`obj-timeline`,children:t.map((e,t)=>(0,p.jsxs)(`li`,{children:[(0,p.jsx)(`time`,{children:e.time}),(0,p.jsx)(`strong`,{children:e.title}),e.detail&&(0,p.jsx)(`span`,{children:e.detail})]},t))})]})}var kC=400,AC=300,jC=12;function MC({props:e}){let t=(0,l.useId)(),n=new Set,r=[];for(let t of _C(e.nodes)){let e=hC(t)?t:{},i=gC(e.id);if(i&&!n.has(i)&&(n.add(i),r.push({id:i,label:gC(e.label)||i}),r.length>=40))break}if(r.length===0)return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(xC,{text:gC(e.title)}),(0,p.jsx)(SC,{text:`Nothing to show`})]});let i=new Map(r.map((e,t)=>[e.id,mC(r.length,kC,AC)[t]])),a=[];for(let t of _C(e.edges)){let e=hC(t)?t:{},n=gC(e.from),r=gC(e.to);if(i.has(n)&&i.has(r)&&n!==r&&(a.push({from:n,to:r,label:gC(e.label)}),a.length>=80))break}return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(xC,{text:gC(e.title)}),(0,p.jsxs)(`svg`,{className:`obj-graph`,viewBox:`0 0 ${kC} ${AC}`,role:`img`,"aria-label":gC(e.title)||`graph`,children:[(0,p.jsx)(`defs`,{children:(0,p.jsx)(`marker`,{id:t,viewBox:`0 0 10 10`,refX:`9`,refY:`5`,markerWidth:`6`,markerHeight:`6`,orient:`auto-start-reverse`,children:(0,p.jsx)(`path`,{d:`M0 0 L10 5 L0 10 z`,className:`arrow`})})}),a.map((e,n)=>{let{from:r,to:a}=pC(i.get(e.from),i.get(e.to),14,17),o=(r.x+a.x)/2+(kC/2-(r.x+a.x)/2)*.25,s=(r.y+a.y)/2+(AC/2-(r.y+a.y)/2)*.25;return(0,p.jsxs)(`g`,{children:[(0,p.jsx)(`path`,{className:`edge`,d:`M ${r.x.toFixed(1)} ${r.y.toFixed(1)} Q ${o.toFixed(1)} ${s.toFixed(1)} ${a.x.toFixed(1)} ${a.y.toFixed(1)}`,fill:`none`,markerEnd:`url(#${t})`}),e.label&&(0,p.jsx)(`text`,{className:`edge-label`,x:o,y:s,textAnchor:`middle`,children:yC(e.label,12)})]},n)}),r.map(e=>{let t=i.get(e.id);return(0,p.jsxs)(`g`,{children:[(0,p.jsx)(`circle`,{className:`node`,cx:t.x,cy:t.y,r:jC}),(0,p.jsx)(`text`,{className:`node-label`,x:t.x,y:t.y+26,textAnchor:`middle`,children:yC(e.label,18)})]},e.id)})]})]})}var NC=[`default-src 'none'`,`script-src 'unsafe-inline'`,`style-src 'unsafe-inline'`,`img-src data: blob:`,`form-action 'none'`,`base-uri 'none'`].join(`; `),PC=`html,body{margin:0;min-height:100%;background:#050b10;color:#d8f6ff;font:15px/1.5 system-ui,sans-serif}`;function FC(e){return`<!doctype html><html><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="${NC}"><meta name="viewport" content="width=device-width,initial-scale=1"><style>${PC}</style></head><body>${e}</body></html>`}function IC({props:e}){let t=typeof e.html==`string`?e.html:``,n=typeof e.title==`string`?e.title:``,r=(0,l.useMemo)(()=>FC(t),[t]);return(0,p.jsxs)(`div`,{className:`obj-sandbox`,children:[n&&(0,p.jsx)(`h3`,{className:`obj-title`,children:n}),(0,p.jsx)(`iframe`,{title:n||`Page made by Friend`,sandbox:`allow-scripts`,referrerPolicy:`no-referrer`,allow:``,srcDoc:r})]})}var LC=e=>typeof e==`string`?e:typeof e==`number`?String(e):``,RC=e=>Array.isArray(e)?e:[];function zC({props:e}){let t=(0,l.useMemo)(()=>aC(LC(e.text)),[e.text]);return(0,p.jsxs)(p.Fragment,{children:[LC(e.title)&&(0,p.jsx)(`h3`,{className:`obj-title`,children:LC(e.title)}),(0,p.jsx)(`div`,{className:`obj-md`,dangerouslySetInnerHTML:{__html:t}})]})}function BC({props:e}){return(0,p.jsxs)(`div`,{className:`obj-stat`,children:[(0,p.jsx)(`span`,{className:`obj-label`,children:LC(e.label)}),(0,p.jsxs)(`span`,{className:`obj-value`,children:[(0,p.jsx)(`span`,{children:LC(e.value)}),LC(e.unit)&&(0,p.jsx)(`small`,{children:LC(e.unit)})]}),LC(e.delta)&&(0,p.jsx)(`span`,{className:`obj-delta`,children:LC(e.delta)})]})}function VC({props:e}){return(0,p.jsxs)(p.Fragment,{children:[LC(e.title)&&(0,p.jsx)(`h3`,{className:`obj-title`,children:LC(e.title)}),(0,p.jsx)(`ul`,{className:`obj-list`,children:RC(e.items).map((e,t)=>{let n=typeof e==`object`&&e?e:{text:e};return(0,p.jsx)(`li`,{className:n.done?`done`:void 0,children:LC(n.text)},t)})})]})}function HC({props:e}){let t=RC(e.columns),n=RC(e.rows);return(0,p.jsxs)(p.Fragment,{children:[LC(e.title)&&(0,p.jsx)(`h3`,{className:`obj-title`,children:LC(e.title)}),(0,p.jsx)(`div`,{className:`obj-table-wrap`,children:(0,p.jsxs)(`table`,{className:`obj-table`,children:[(0,p.jsx)(`thead`,{children:(0,p.jsx)(`tr`,{children:t.map((e,t)=>(0,p.jsx)(`th`,{children:LC(e)},t))})}),(0,p.jsx)(`tbody`,{children:n.map((e,t)=>(0,p.jsx)(`tr`,{children:RC(e).map((e,t)=>(0,p.jsx)(`td`,{children:LC(e)},t))},t))})]})})]})}function UC({props:e}){return(0,p.jsxs)(p.Fragment,{children:[LC(e.title)&&(0,p.jsx)(`h3`,{className:`obj-title`,children:LC(e.title)}),(0,p.jsx)(`pre`,{className:`obj-code`,"data-lang":LC(e.language),children:(0,p.jsx)(`code`,{children:LC(e.code)})})]})}var WC={card:zC,stat:BC,list:VC,table:HC,code:UC,chart:EC,gauge:DC,timeline:OC,graph:MC,sandbox:IC};function GC(e,t){let n=new Map(t.map(e=>[e.id,e])),r=[],i=new Set;for(let t of e){if(i.has(t.object.id))continue;i.add(t.object.id);let e=n.get(t.object.id);r.push(e?{object:e,leaving:!1}:{object:t.object,leaving:!0})}for(let e of t)i.has(e.id)||(i.add(e.id),r.push({object:e,leaving:!1}));return r}function KC(e,t){let[n,r]=(0,l.useState)(()=>e.map(e=>({object:e,leaving:!1}))),i=(0,l.useRef)(new Map);return(0,l.useEffect)(()=>{r(n=>{let a=GC(n,e),o=new Set(n.filter(e=>e.leaving).map(e=>e.object.id));for(let e of a){let n=i.current.get(e.object.id);if(!e.leaving)n&&clearTimeout(n),i.current.delete(e.object.id);else if(!o.has(e.object.id)&&!n){let n=e.object.id;i.current.set(n,setTimeout(()=>{i.current.delete(n),r(e=>e.filter(e=>!(e.object.id===n&&e.leaving)))},Math.max(t,0)))}}return t<=0?a.filter(e=>!e.leaving):a})},[e,t]),(0,l.useEffect)(()=>()=>{for(let e of i.current.values())clearTimeout(e);i.current.clear()},[]),n}var qC=.12,JC=.1,YC=.02,XC=(e,t,n)=>Math.min(Math.max(e,t),n);function ZC(e){let t=XC(e.w,qC,1),n=XC(e.h,JC,1);return{x:XC(e.x,0,1-t),y:XC(e.y,0,1-n),w:t,h:n}}function QC(e,t){let n=t.getBoundingClientRect(),r=e.getBoundingClientRect();return n.width<=0||n.height<=0?null:ZC({x:(r.left-n.left)/n.width,y:(r.top-n.top)/n.height,w:r.width/n.width,h:r.height/n.height})}function $C(e,t,n){let r=(0,l.useRef)(null),[i,a]=(0,l.useState)(null),o=(0,l.useCallback)((n,r)=>{let i=ZC(r);t({type:`object.place`,id:n,placement:{x:i.x,y:i.y,w:i.w,h:i.h}}),e({type:`stage.move`,objectId:n,rect:i})},[e,t]),s=(0,l.useCallback)((e,t,i)=>{if(e.pointerType===`mouse`&&e.button!==0)return;let o=n(),s=e.currentTarget.closest(`[data-object]`);if(!o||!s||r.current)return;let c=QC(s,o);if(!c)return;let l=o.getBoundingClientRect();r.current={id:t.id,mode:i,pointer:e.pointerId,startX:e.clientX,startY:e.clientY,from:c,stageW:l.width,stageH:l.height};try{e.currentTarget.setPointerCapture(e.pointerId)}catch{}e.stopPropagation(),e.preventDefault(),a({id:t.id,box:c})},[n]),c=(e,t)=>{let n=(t.clientX-e.startX)/e.stageW,r=(t.clientY-e.startY)/e.stageH;return e.mode===`move`?ZC({...e.from,x:e.from.x+n,y:e.from.y+r}):ZC({...e.from,w:e.from.w+n,h:e.from.h+r})};return{preview:i,begin:s,move:(0,l.useCallback)(e=>{let t=r.current;t&&t.pointer===e.pointerId&&a({id:t.id,box:c(t,e)})},[]),end:(0,l.useCallback)((e,t)=>{let n=r.current;n&&n.pointer===e.pointerId&&(r.current=null,a(null),t||o(n.id,c(n,e)))},[o]),key:(0,l.useCallback)((e,t)=>{let r={ArrowLeft:[-.02,0],ArrowRight:[YC,0],ArrowUp:[0,-.02],ArrowDown:[0,YC]}[e.key],i=n(),a=e.currentTarget.closest(`[data-object]`);if(!r||!i||!a)return;let s=QC(a,i);s&&(e.preventDefault(),o(t.id,e.shiftKey?{...s,w:s.w+r[0],h:s.h+r[1]}:{...s,x:s.x+r[0],y:s.y+r[1]}))},[o,n])}}function ew(e,t){if(e===`auto`)return{position:`relative`};if(t.w<600)return{position:`relative`,width:`100%`};let[n,r,i,a]=typeof e==`string`?uy[e]:[e.x*100,e.y*100,e.w*100,e.h*100],o={position:`absolute`,left:`${n}%`,top:`${r}%`,width:`${i}%`};return e===`top`||e===`bottom`?{...o,maxHeight:`${a}%`}:{...o,height:`${a}%`}}var tw=350,nw=()=>typeof matchMedia==`function`&&matchMedia(`(prefers-reduced-motion: reduce)`).matches,rw=class extends l.Component{state={failed:!1};static getDerivedStateFromError(){return{failed:!0}}render(){return this.state.failed?null:this.props.children}};function iw(){let e=()=>({w:window.innerWidth,h:window.innerHeight}),[t,n]=(0,l.useState)(e);return(0,l.useEffect)(()=>{let t=()=>n(e());return window.addEventListener(`resize`,t),()=>window.removeEventListener(`resize`,t)},[]),t}var aw=350;function ow({state:e,send:t,dispatch:n,voice:r,slash:i,onMenu:a}){let o=iw(),[s,c]=(0,l.useState)(0),[u,d]=(0,l.useState)(!1),f=ne(),m=o.w<600,h=e.scene===`focus`||e.scene===`multi`,g={"--accent":e.mood.accent,"--intensity":e.mood.intensity},_=e.voice.mode,v=_===`listening`?`listening`:_===`speaking`?`speaking`:_===`transcribing`?`thinking`:e.presence.state,y=gu(e.look.reactivity),b=Math.min(1,(_===`listening`||_===`speaking`?e.voice.level:e.presence.amplitude)*y),x=(0,l.useRef)({timer:null,down:!1,talking:!1,id:null}),S=e=>{if(!(e.target===e.currentTarget||e.target.closest(`[data-presence]`))||e.pointerType===`mouse`&&e.button!==0)return;let t=x.current;if(!(t.down&&t.id!==null&&e.pointerId!==t.id)){t.timer&&clearTimeout(t.timer),t.down=!0,t.talking=!1,t.id=typeof e.pointerId==`number`?e.pointerId:null;try{t.id!==null&&e.currentTarget.setPointerCapture?.(t.id)}catch{}t.timer=r?setTimeout(()=>{t.down&&(t.talking=!0,r.pressStart())},aw):null}},C=(e,t)=>{let n=x.current;n.down&&(n.id===null||typeof e.pointerId!=`number`||e.pointerId===n.id)&&(n.down=!1,n.id=null,n.timer&&clearTimeout(n.timer),n.timer=null,n.talking?(n.talking=!1,t?r?.blur?.():r?.pressEnd()):t||c(e=>e+1))},w=(0,l.useRef)(null),T=$C(t,n,()=>w.current),E=KC(e.objects,nw()?0:tw),D=E.filter(e=>e.object.placement===`auto`),O=E.filter(e=>e.object.placement!==`auto`),k=({object:e,leaving:r})=>{let i=WC[e.type];if(!i)return null;let a=!m&&!r,s=T.preview?.id===e.id?T.preview.box:null,c=s?{x:s.x,y:s.y,w:s.w,h:s.h}:e.placement;return(0,p.jsxs)(`section`,{"data-object":e.id,className:`obj glass${e.style===`alert`?` alert`:``}${e.pinned?` pinned`:``}${s?` dragging`:``}${r?` leaving`:``}`,style:ew(c,o),children:[a&&(0,p.jsx)(`div`,{className:`obj-grip`,role:`button`,tabIndex:0,"aria-label":`Move ${e.id}. Drag, or use the arrow keys; hold Shift to resize.`,onPointerDown:t=>T.begin(t,e,`move`),onPointerMove:T.move,onPointerUp:e=>T.end(e,!1),onPointerCancel:e=>T.end(e,!0),onKeyDown:t=>T.key(t,e)}),(0,p.jsx)(`button`,{className:`obj-pin${e.pinned?` on`:``}`,"aria-label":e.pinned?`Unpin ${e.id}`:`Pin ${e.id} to the screen`,"aria-pressed":!!e.pinned,title:e.pinned?`Pinned: stays on the screen. Click to unpin.`:`Pin: keep this on the screen.`,disabled:r,onClick:()=>t({type:`stage.pin`,objectId:e.id,pinned:!e.pinned}),children:(0,p.jsx)(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:e.pinned?`currentColor`:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":`true`,children:(0,p.jsx)(`path`,{d:`M12 17v5M9 3h6l-1 7 3 3H7l3-3-1-7z`})})}),(0,p.jsx)(`button`,{className:`obj-close`,"aria-label":`Dismiss ${e.id}`,title:e.pinned?`Remove this widget`:`Dismiss`,disabled:r,onClick:()=>{t({type:`stage.event`,objectId:e.id,event:`dismiss`}),n({type:`object.dismiss`,id:e.id})},children:`×`}),a&&(0,p.jsx)(`div`,{className:`obj-resize`,"aria-hidden":`true`,onPointerDown:t=>T.begin(t,e,`resize`),onPointerMove:T.move,onPointerUp:e=>T.end(e,!1),onPointerCancel:e=>T.end(e,!0)}),(0,p.jsx)(rw,{children:(0,p.jsx)(i,{props:e.props})})]},e.id)};return(0,p.jsxs)(`div`,{ref:w,"data-testid":`stage`,className:`stage`,style:g,onPointerDown:S,onPointerUp:e=>C(e,!1),onPointerCancel:e=>C(e,!0),onContextMenu:e=>r&&e.preventDefault(),children:[e.look.ambient&&(0,p.jsx)(`div`,{className:`ambient`,"aria-hidden":`true`,style:{opacity:.35+.65*e.mood.intensity}}),(0,p.jsx)(hu,{state:v,amplitude:b,pulse:e.presence.pulse,mode:h?`corner`:`full`,accent:e.mood.accent,style:e.presence.style,intensity:e.mood.intensity,scale:e.look.size}),(0,p.jsxs)(`div`,{className:m?`objects objects-stack`:`objects`,style:{zIndex:10},children:[D.length>0&&(0,p.jsx)(`div`,{className:`auto-grid`,children:D.map(k)}),O.map(k)]}),e.look.captions&&e.captions&&(0,p.jsx)(`p`,{className:`captions captions-${e.look.captionSize}${e.captionDone?` done`:``}`,"aria-live":`polite`,children:e.captions}),f&&r&&!u&&(0,p.jsx)(Fy,{voice:r,mode:_,onType:()=>c(e=>e+1),onMenu:()=>a?.()}),(0,p.jsx)(My,{requestOpen:s,slash:i,onOpenChange:d,onSend:e=>t({type:`chat.send`,text:e})})]})}function sw(){let e=()=>Math.random().toString(36).slice(2,12)+Date.now().toString(36);try{let t=sessionStorage.getItem(`friend.tab`);return t||(t=e(),sessionStorage.setItem(`friend.tab`,t)),t}catch{return e()}}function cw(){return`${location.protocol===`https:`?`wss`:`ws`}://${location.host}/ws`}function lw(e=cw()){let t=null,n=`connecting`,r=!1,i=0,a=!1,o=sw(),s=null,c=new Set,l=new Set,u=e=>{n=e,l.forEach(t=>t(e))};function d(){if(r)return;u(`connecting`);let n=new WebSocket(`${e}${e.includes(`?`)?`&`:`?`}tab=${o}${a?`&takeover=1`:``}`);t=n,n.onopen=()=>{i=0,a=!1,u(`open`)},n.onmessage=e=>{try{let t=JSON.parse(String(e.data));c.forEach(e=>e(t))}catch{}},n.onclose=e=>{if(!r){if(e.code===4401)return u(`unauthorized`);if(e.code===4409||e.code===4410)return u(`elsewhere`);u(`closed`),s=setTimeout(d,Math.min(500*2**i++,8e3))}},n.onerror=()=>{}}return d(),{send(e){t?.readyState===WebSocket.OPEN&&t.send(JSON.stringify(e))},onMessage(e){return c.add(e),()=>c.delete(e)},onState(e){return l.add(e),()=>l.delete(e)},state:()=>n,takeOver(){r||n!==`elsewhere`||(a=!0,i=0,d())},close(){r=!0,s&&clearTimeout(s),t?.close()}}}var uw={scene:`conversation`,objects:[],captions:``,captionDone:!0,presence:{state:`idle`,style:`sphere`,amplitude:0,pulse:0},mood:{accent:`#3fd8ff`,intensity:.6},approval:null,alerts:[],connection:`connecting`,chat:[],chatLive:!1,view:`companion`,look:{size:1,reactivity:.7,ambient:!0,captions:!0,captionSize:`medium`},viewKnown:!1,busy:!1,activity:null,deviceOnline:!1,voice:{mode:`idle`,level:0,available:!1,enrolled:!1},challenge:null,camera:{enabled:!1,stream:null},speaker:null,ownerName:``},dw=200,fw=e=>e.length>dw?e.slice(-200):e,pw=0,mw=e=>Math.min(1,Math.max(0,e));function hw(e,t){switch(t.cmd){case`scene`:return{...e,scene:t.mode};case`show`:{let n=e.objects.findIndex(e=>e.id===t.object.id),r=n===-1?[...e.objects,t.object]:e.objects.map((e,r)=>r===n?t.object:e);return{...e,objects:r}}case`update`:return{...e,objects:e.objects.map(e=>e.id===t.id?{...e,props:{...e.props,...t.props}}:e)};case`hide`:return{...e,objects:e.objects.filter(e=>e.id!==t.id)};case`clear`:return{...e,objects:e.objects.filter(e=>e.pinned),scene:`conversation`};case`pin`:return{...e,objects:e.objects.map(e=>e.id===t.id?{...e,pinned:!0}:e)};case`unpin`:return{...e,objects:e.objects.map(e=>e.id===t.id?(({pinned:e,...t})=>t)(e):e)};case`place`:return{...e,objects:e.objects.map(e=>e.id===t.id?{...e,placement:t.placement}:e)};case`presence`:return{...e,presence:{...e.presence,state:t.state??e.presence.state,style:t.style??e.presence.style}};case`mood`:return{...e,mood:{accent:t.accent??e.mood.accent,intensity:t.intensity??e.mood.intensity}}}}function gw(e,t){let n=t;if(n&&typeof n==`object`){if(n.type===`conn`)return{...e,connection:n.value};if(n.type===`chat.seed`){let t=Array.isArray(n.lines)?n.lines:[];return e.chat.length===0?{...e,chat:fw(t)}:{...e,chat:fw([...t,...e.chat])}}if(n.type===`alert.add`)return{...e,alerts:[...e.alerts.slice(-3),{id:++pw,level:n.level,text:n.text}]};if(n.type===`camera.enable`)return{...e,camera:{enabled:n.value,stream:n.value?e.camera.stream:null}};if(n.type===`camera.open`)return{...e,camera:{...e.camera,stream:n.stream}};if(n.type===`camera.closed`)return{...e,camera:{...e.camera,stream:null}};if(n.type===`challenge.dismiss`)return e.challenge?.id===n.id?{...e,challenge:null}:e;if(n.type===`voice.mode`)return{...e,voice:{...e.voice,mode:n.mode,level:n.mode===`idle`?0:e.voice.level}};if(n.type===`voice.level`){let t=Number.isFinite(n.level)?Math.min(1,Math.max(0,n.level)):0;return{...e,voice:{...e.voice,level:t}}}if(n.type===`object.dismiss`)return{...e,objects:e.objects.filter(e=>e.id!==n.id)};if(n.type===`object.place`)return{...e,objects:e.objects.map(e=>e.id===n.id?{...e,placement:n.placement}:e)};if(n.type===`alert.dismiss`)return{...e,alerts:e.alerts.filter(e=>e.id!==n.id)}}let r=xy.safeParse(t);if(!r.success)return e;let i=r.data;switch(i.type){case`snapshot`:return{...e,scene:i.stage.scene,objects:i.stage.objects,presence:{...e.presence,state:i.stage.presence.state,style:i.stage.presence.style},mood:i.stage.mood,approval:i.pendingApproval??null};case`stage`:return hw(e,i.command);case`chat.user`:return{...e,chat:fw([...e.chat,{role:`user`,text:i.text}]),chatLive:!1};case`ui`:return{...e,view:i.view,look:i.look??e.look,ownerName:i.ownerName??e.ownerName,viewKnown:!0};case`caption.delta`:{let t=e.captionDone,n=e.chat.at(-1),r=e.chatLive&&n?.role===`assistant`?[...e.chat.slice(0,-1),{role:`assistant`,text:n.text+i.text}]:fw([...e.chat,{role:`assistant`,text:i.text}]);return{...e,chat:r,chatLive:!0,captions:(t?``:e.captions)+i.text,captionDone:!1,presence:{...e.presence,amplitude:mw(.3+i.text.length*.04),pulse:e.presence.pulse+1}}}case`caption.end`:return{...e,chatLive:!1,captionDone:!0,presence:{...e.presence,amplitude:0}};case`approval.request`:{let{type:t,...n}=i;return{...e,approval:n}}case`approval.resolved`:return e.approval?.id===i.id?{...e,approval:null}:e;case`alert`:return{...e,alerts:[...e.alerts.slice(-3),{id:++pw,level:i.level,text:i.text}]};case`activity`:return{...e,activity:i.text};case`status`:return{...e,busy:i.busy,activity:i.busy?e.activity:null,deviceOnline:i.deviceOnline,voice:{...e.voice,available:i.voiceAvailable??e.voice.available,enrolled:i.voiceEnrolled??e.voice.enrolled}};case`error`:return{...e,alerts:[...e.alerts.slice(-3),{id:++pw,level:`error`,text:i.message}]};case`voice.heard`:return{...e,captions:i.trust===`guest`?`Guest: ${i.text}`:i.trust===`member`&&i.speaker?`${i.speaker}: ${i.text}`:i.text,captionDone:!0,speaker:{name:i.trust===`member`?i.speaker??null:null,trust:i.trust??`unverified`,seq:(e.speaker?.seq??0)+1}};case`voice.challenge`:return{...e,challenge:{id:i.id,text:i.text}};case`voice.challenge.end`:return e.challenge?.id===i.id?{...e,challenge:null}:e;case`audio.chunk`:case`audio.stop`:return e;case`camera.request`:case`camera.cancel`:return e}}var _w=1024,vw=.8,yw=300;function bw(e,t){let n=Math.min(1,_w/Math.max(e,t));return{width:Math.round(e*n),height:Math.round(t*n)}}function xw(e){let t=e?.name;return t===`NotAllowedError`||t===`SecurityError`?`permission`:t===`NotFoundError`||t===`OverconstrainedError`?`no-camera`:`busy`}async function Sw(){if(!navigator.mediaDevices?.getUserMedia)throw Object.assign(Error(`This browser cannot use the camera here (use localhost or HTTPS).`),{name:`NotFoundError`});return navigator.mediaDevices.getUserMedia({video:{width:{ideal:1280}},audio:!1})}async function Cw(e,t=yw){let n=document.createElement(`video`);n.muted=!0,n.playsInline=!0,n.srcObject=e,await n.play(),await new Promise(e=>setTimeout(e,t));let{width:r,height:i}=bw(n.videoWidth||640,n.videoHeight||480),a=document.createElement(`canvas`);return a.width=r,a.height=i,a.getContext(`2d`).drawImage(n,0,0,r,i),n.srcObject=null,new Promise((e,t)=>a.toBlob(n=>n?e(n):t(Error(`Could not encode the picture.`)),`image/jpeg`,vw))}var ww=class{opts;active=null;constructor(e){this.opts=e}async handle(e){if(e.type===`camera.cancel`){let t=this.active;t&&t.id===e.id&&(t.cancelled=!0,this.release(t));return}if(!this.opts.enabled())return this.opts.send({type:`camera.denied`,id:e.id,reason:`off`});if(this.active)return this.opts.send({type:`camera.denied`,id:e.id,reason:`busy`});let t={id:e.id,cancelled:!1,stream:null,opened:!1,released:!1};this.active=t;try{let n=await(this.opts.open??Sw)();if(t.stream=n,t.cancelled){n.getTracks().forEach(e=>e.stop());return}t.opened=!0,this.opts.dispatch({type:`camera.open`,stream:n});let r=await(this.opts.frame??(e=>Cw(e)))(n);if(t.cancelled)return;this.release(t);try{await this.opts.upload(e.id,r)}catch{this.opts.dispatch({type:`alert.add`,level:`warn`,text:`Could not send the camera picture to Friend.`})}}catch(n){t.cancelled||this.opts.send({type:`camera.denied`,id:e.id,reason:xw(n)})}finally{this.release(t),this.active===t&&(this.active=null)}}dispose(){let e=this.active;e&&(e.cancelled=!0,this.release(e))}release(e){e.released||(e.released=!0,e.stream?.getTracks().forEach(e=>e.stop()),e.opened&&this.opts.dispatch({type:`camera.closed`}))}},Tw={401:`Please sign in again.`,413:`That recording was too long.`,409:`Friend is busy with the last request. Try again in a moment.`,429:`You're talking too fast. Wait a moment.`,503:`Voice is not available. Run: friend voice setup`};async function Ew(e,t){let n;try{n=await fetch(`/api/voice/utterance`,{method:`POST`,headers:{"content-type":e.type||`audio/webm`},body:e,signal:t})}catch(e){throw e.name===`AbortError`?e:Error(`Could not reach Friend. Check the connection.`)}if(!n.ok){let e=``;try{e=(await n.json()).error??``}catch{}throw Error(e||Tw[n.status]||`The voice request failed (${n.status}).`)}let r=await n.json();return{text:typeof r.text==`string`?r.text:``}}var Dw=0,Ow=new Set;function kw(e){return Ow.add(e),()=>Ow.delete(e)}var Aw=e=>Ow.forEach(t=>{try{t(e)}catch{}}),jw=e=>/not compatible|audio capture|audiosession/i.test(String(e?.message??e));function Mw(e){try{let t=navigator.audioSession;t&&t.type!==e&&(t.type=e)}catch{}}function Nw(){Dw===0&&Mw(`playback`)}function Pw(){Dw++,Dw===1&&Aw(!0),Mw(`play-and-record`);let e=!1;return()=>{e||(e=!0,Dw=Math.max(0,Dw-1),Dw===0&&(Mw(`playback`),Aw(!1)))}}var Fw=4096,Iw=8,Lw=20,Rw=6e6;function zw(e){try{let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n}catch{return null}}var Bw=class{ctx=null;analyser=null;pending=new Map;queue=[];current=null;busy=!1;gen=0;isPlaying=!1;unwatch=null;listeners=new Set;unlock(){Nw();let e=this.ensureContext();e.state!==`running`&&e.resume().catch(()=>{})}locked(){return!this.ctx||this.ctx.state!==`running`}enqueue(e){if(!Number.isInteger(e.seq)||e.seq<0||e.seq>Fw)return;let t=zw(e.data);if(!t)return;let n=this.pending.get(e.id);if(n||(this.pending.size>=Iw&&this.pending.delete(this.pending.keys().next().value),n={parts:new Map,lastSeq:null,bytes:0},this.pending.set(e.id,n)),n.bytes+=t.length,n.bytes>Rw){this.pending.delete(e.id);return}if(n.parts.set(e.seq,t),e.last&&(n.lastSeq=e.seq),n.lastSeq===null||n.parts.size!==n.lastSeq+1)return;this.pending.delete(e.id);let r=new Uint8Array([...n.parts.values()].reduce((e,t)=>e+t.length,0)),i=0;for(let e=0;e<=n.lastSeq;e++){let t=n.parts.get(e);if(!t)return;r.set(t,i),i+=t.length}for(this.queue.push(r.buffer);this.queue.length>Lw;)this.queue.shift();this.pump()}stop(){this.gen++,this.queue=[],this.pending.clear(),this.busy=!1;let e=this.current;if(this.current=null,e){e.onended=null;try{e.stop()}catch{}}this.setPlaying(!1)}dispose(){this.stop(),this.ctx?.close().catch(()=>{}),this.unwatch?.(),this.unwatch=null,this.ctx=null,this.analyser=null}playing(){return this.isPlaying}onChange(e){return this.listeners.add(e),()=>this.listeners.delete(e)}level(){if(!this.analyser||!this.isPlaying)return 0;let e=new Uint8Array(this.analyser.fftSize);this.analyser.getByteTimeDomainData(e);let t=0;for(let n of e)t+=((n-128)/128)**2;return Math.min(1,Math.sqrt(t/e.length)*4)}ensureContext(){if(!this.ctx){let e=window.AudioContext??window.webkitAudioContext;this.ctx=new e,this.analyser=this.ctx.createAnalyser(),this.analyser.fftSize=1024,this.analyser.connect(this.ctx.destination);let t=this.ctx;this.unwatch?.(),this.unwatch=kw(e=>{e&&t.state===`running`?t.suspend().catch(()=>{}):!e&&t.state!==`running`&&t.state!==`closed`&&t.resume().catch(()=>{})})}return this.ctx}setPlaying(e){this.isPlaying!==e&&(this.isPlaying=e,this.listeners.forEach(t=>t(e)))}async pump(){if(this.busy)return;let e=this.queue.shift();if(!e){this.setPlaying(!1);return}this.busy=!0;let t=this.gen;try{let n=this.ensureContext();if(n.state!==`running`){try{await n.resume()}catch{}if(t!==this.gen)return;if(n.state!==`running`){this.queue.unshift(e),this.busy=!1;return}}let r=await n.decodeAudioData(e);if(t!==this.gen)return;let i=n.createBufferSource();i.buffer=r,i.connect(this.analyser),i.onended=()=>{this.current===i&&(this.current=null,this.busy=!1,this.pump())},this.current=i,this.setPlaying(!0),i.start()}catch{if(t!==this.gen)return;this.busy=!1,this.pump()}}},Vw=e=>new Promise(t=>setTimeout(t,e)),Hw=[`audio/webm;codecs=opus`,`audio/webm`,`audio/mp4`,`audio/ogg;codecs=opus`],Uw=3e3;function Ww(e){let t=e;return t?.name===`NotAllowedError`||t?.name===`SecurityError`?Error(`Microphone permission was denied. Allow it in the browser to talk.`):t?.name===`NotFoundError`||t?.name===`OverconstrainedError`?Error(`No microphone was found.`):Error(t?.message||`Could not open the microphone.`)}var Gw=class e{opts;stream=null;rec=null;chunks=[];ctx=null;analyser=null;timer=null;starting=!1;leaveSession=null;constructor(e={}){this.opts=e}static supported(){return typeof navigator<`u`&&!!navigator.mediaDevices?.getUserMedia&&typeof MediaRecorder<`u`}get active(){return this.rec!==null}async start(){if(!(this.rec||this.starting)){if(!e.supported())throw Error(`This browser cannot record audio here (use localhost or HTTPS).`);this.starting=!0;try{this.leaveSession=Pw();try{let e=()=>navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0}});try{this.stream=await e()}catch(t){if(!jw(t))throw t;await Vw(250),this.leaveSession?.(),this.leaveSession=Pw(),this.stream=await e()}}catch(e){throw this.leaveSession?.(),this.leaveSession=null,Ww(e)}try{let e=Hw.find(e=>MediaRecorder.isTypeSupported(e));this.chunks=[];let t=new MediaRecorder(this.stream,e?{mimeType:e}:void 0);t.ondataavailable=e=>{e.data&&e.data.size>0&&this.chunks.push(e.data)},t.onerror=()=>this.failed();for(let e of this.stream.getTracks())e.onended=()=>this.failed();this.openMeter(this.stream),t.start(250),this.rec=t,this.opts.maxMs&&(this.timer=setTimeout(()=>{this.timer=null,this.opts.onLimit?.()},this.opts.maxMs))}catch(e){throw this.release(),Ww(e)}}finally{this.starting=!1}}}stop(){let e=this.rec;return e?new Promise(t=>{let n=!1,r=null,i=()=>{if(n)return;n=!0,r&&clearTimeout(r);let i=new Blob(this.chunks,{type:e.mimeType||`audio/webm`});this.release(),t(i)};if(r=setTimeout(i,Uw),e.onstop=i,e.state===`inactive`)return i();try{e.stop()}catch{i()}}):Promise.reject(Error(`Not recording.`))}cancel(){let e=this.rec;if(e){e.ondataavailable=null,e.onstop=null,e.onerror=null;try{e.state!==`inactive`&&e.stop()}catch{}}this.release()}level(){if(!this.analyser||!this.rec)return 0;let e=new Uint8Array(this.analyser.fftSize);this.analyser.getByteTimeDomainData(e);let t=0;for(let n of e)t+=((n-128)/128)**2;return Math.min(1,Math.sqrt(t/e.length)*4)}failed(){this.rec&&(this.cancel(),this.opts.onError?.())}openMeter(e){try{this.ctx=new AudioContext,this.analyser=this.ctx.createAnalyser(),this.analyser.fftSize=1024,this.ctx.createMediaStreamSource(e).connect(this.analyser)}catch{this.analyser=null}}release(){this.timer&&clearTimeout(this.timer),this.timer=null,this.stream?.getTracks().forEach(e=>{e.onended=null,e.stop()}),this.ctx?.close().catch(()=>{}),this.stream=null,this.rec=null,this.ctx=null,this.analyser=null,this.chunks=[],this.leaveSession?.(),this.leaveSession=null}},Kw=2e3,qw=()=>typeof window<`u`&&window.isSecureContext===!1,Jw=50,Yw=class{deps;recorder;player;mode=`idle`;pressed=!1;disposed=!1;uploads=0;toldAboutSound=!1;abort=null;poll=null;unsubscribe;constructor(e){this.deps=e;let t=()=>this.recorderFailed();this.recorder=e.recorder??e.makeRecorder?.(t)??new Gw({maxMs:6e4,onLimit:()=>void this.pressEnd(),onError:t}),this.player=e.player??new Bw,this.unsubscribe=this.player.onChange(e=>{this.mode!==`listening`&&this.mode!==`transcribing`&&this.setMode(e?`speaking`:`idle`)})}async pressStart(){if(!(this.pressed||this.disposed)){if(!this.deps.available()){this.alert(`warn`,`Voice is not available. Run: friend voice setup`);return}if(!(this.deps.supported?.()??Gw.supported())){this.alert(`warn`,qw()?`This page is not secure, so the browser blocks the microphone. On the PC run: friend lan https on, restart Friend, and open the https:// address.`:`This browser cannot record audio here (use localhost or HTTPS).`);return}this.pressed=!0,this.player.unlock(),this.player.playing()&&(this.deps.send({type:`voice.interrupt`}),this.player.stop());try{await this.recorder.start()}catch(e){this.pressed=!1,this.alert(`error`,e.message),this.setMode(`idle`);return}if(!this.pressed){this.recorder.cancel(),this.setMode(`idle`);return}this.setMode(`listening`)}}async pressEnd(){if(!this.pressed||(this.pressed=!1,!this.recorder.active))return;let e=await this.recorder.stop();if(e.size<Kw){this.alert(`info`,`Hold the key a little longer while you speak.`),this.setMode(`idle`);return}this.setMode(`transcribing`);let t=new AbortController;this.abort=t,this.uploads++;try{let{text:n}=await this.deps.post(e,t.signal);n.trim()||this.alert(`info`,`I didn't catch that.`)}catch(e){e.name!==`AbortError`&&this.alert(`error`,e.message)}finally{this.uploads--,this.abort===t&&(this.abort=null),this.settle()}}blur(){this.pressed&&(this.pressed=!1,this.recorder.cancel(),this.setMode(`idle`))}handleServer(e){if(e.type!==`audio.chunk`)return this.player.stop();this.player.enqueue(e),!this.toldAboutSound&&this.player.locked?.()&&(this.toldAboutSound=!0,this.deps.dispatch({type:`alert.add`,level:`info`,text:`Sound is blocked by the browser. Click or press a key once anywhere on the page to hear me.`}))}unlock(){this.player.unlock()}dispose(){this.disposed=!0,this.pressed=!1,this.stopPolling(),this.abort?.abort(),this.recorder.cancel(),this.player.stop(),this.player.dispose?.(),this.unsubscribe()}recorderFailed(){this.pressed=!1,this.recorder.cancel(),this.alert(`error`,`The microphone stopped working.`),this.setMode(`idle`)}settle(){this.uploads>0||this.mode!==`transcribing`||this.setMode(this.player.playing()?`speaking`:`idle`)}setMode(e){this.disposed||(this.mode=e,this.deps.dispatch({type:`voice.mode`,mode:e}),e===`listening`||e===`speaking`?this.startPolling():this.stopPolling())}startPolling(){this.stopPolling(),this.poll=setInterval(()=>{let e=this.mode===`listening`?this.recorder.level():this.player.level();this.deps.dispatch({type:`voice.level`,level:e})},Jw)}stopPolling(){this.poll&&clearInterval(this.poll),this.poll=null}alert(e,t){this.disposed||this.deps.dispatch({type:`alert.add`,level:e,text:t})}};function Xw(e){let[t,n]=(0,l.useReducer)(gw,uw),r=(0,l.useRef)(null),i=(0,l.useRef)(null),a=(0,l.useRef)(null),o=(0,l.useRef)(e);o.current=e;let s=(0,l.useRef)(t);return s.current=t,(0,l.useEffect)(()=>{let e=lw();r.current=e;let t=new Yw({send:t=>e.send(t),dispatch:e=>n(e),post:Ew,available:()=>s.current.voice.available});i.current=t;let c=new ww({send:t=>e.send(t),dispatch:e=>n(e),enabled:()=>s.current.camera.enabled,upload:async(e,t)=>{let n=await fetch(`/api/camera/frame/${encodeURIComponent(e)}`,{method:`POST`,headers:{"content-type":`image/jpeg`},body:t});if(!n.ok)throw Error(`upload failed (${n.status})`)}});a.current=c;let l=e.onMessage(e=>{n(e);let r=e?.type,i=e?.id;(r===`camera.request`||r===`camera.cancel`)&&typeof i==`string`&&c.handle({type:r,id:i}),(r===`audio.chunk`||r===`audio.stop`)&&t.handleServer(e)}),u=e.onState(e=>{n({type:`conn`,value:e}),e===`unauthorized`&&o.current(),e===`elsewhere`&&(t.blur(),c.dispose())});return()=>{l(),u(),t.dispose(),c.dispose(),e.close(),i.current=null,a.current=null,r.current=null}},[]),(0,l.useEffect)(()=>{t.camera.enabled||a.current?.dispose()},[t.camera.enabled]),{state:t,send:(0,l.useCallback)(e=>r.current?.send(e),[]),dispatch:(0,l.useCallback)(e=>n(e),[]),voice:(0,l.useMemo)(()=>({pressStart:()=>void i.current?.pressStart(),pressEnd:()=>void i.current?.pressEnd(),blur:()=>i.current?.blur(),unlock:()=>i.current?.unlock()}),[]),useHere:(0,l.useCallback)(()=>r.current?.takeOver(),[])}}var Zw={401:`Please sign in again.`,403:`Incorrect PIN.`,413:`That recording was too long.`,429:`Too many attempts. Wait a moment.`,503:`Voice is not available. Run: friend voice setup`};async function Qw(e,t){let n;try{n=await fetch(e,t)}catch{throw Error(`Could not reach Friend. Check the connection.`)}if(!n.ok){let e=``;try{e=(await n.json()).error??``}catch{}throw Error(e||Zw[n.status]||`The request failed (${n.status}).`)}return await n.json()}var $w=(e,t)=>({method:e,headers:{"content-type":`application/json`},body:JSON.stringify(t)}),eT={state:()=>Qw(`/api/voice/enroll`,{}),sample:e=>Qw(`/api/voice/enroll/sample`,{method:`POST`,headers:{"content-type":e.type||`audio/webm`},body:e}),finish:e=>Qw(`/api/voice/enroll/finish`,$w(`POST`,{pin:e})),cancel:async()=>{await Qw(`/api/voice/enroll/cancel`,$w(`POST`,{}))},remove:async e=>{await Qw(`/api/voice/enroll`,$w(`DELETE`,{pin:e}))}},tT=[`Friend, good evening. It is a pleasant day to get things done.`,`Open my projects and show me what changed today.`,`My voice is my key, so please remember it well.`,`Check the weather and read me the latest headlines.`,`Always protect the boss, and never trust a stranger.`],nT=15e3;function rT({api:e=eT,createRecorder:t=e=>new Gw(e),onClose:n,who:r}){let[i,a]=(0,l.useState)(null),[o,s]=(0,l.useState)(0),[c,u]=(0,l.useState)(null),[f,m]=(0,l.useState)(`record`),[h,g]=(0,l.useState)(!1),[_,v]=(0,l.useState)(!1),[y,b]=(0,l.useState)(null),[S,C]=(0,l.useState)(``),w=(0,l.useRef)(!0),T=(0,l.useRef)(null);(0,l.useEffect)(()=>(w.current=!0,e.state().then(e=>{w.current&&(a(e),s(e.samples))}).catch(e=>w.current&&b(e.message)),()=>{w.current=!1,T.current?.cancel(),T.current=null}),[e]);let E=(0,l.useCallback)(async()=>{let t=T.current;if(t){T.current=null,g(!1),v(!0);try{let n=await t.stop(),r=await e.sample(n);if(!w.current)return;s(r.count),u(r.note)}catch(e){w.current&&b(e.message)}finally{w.current&&v(!1)}}},[e]),D=async()=>{b(null),u(null);let e=t({maxMs:nT,onLimit:()=>void E(),onError:()=>{T.current=null,w.current&&(g(!1),b(`The microphone stopped working. Try again.`))}});try{await e.start()}catch(e){w.current&&b(e.message);return}if(!w.current)return e.cancel();T.current=e,g(!0)},O=async e=>{b(null),v(!0);try{await e()}catch(e){w.current&&b(e.message)}finally{w.current&&(v(!1),C(``))}},k=async()=>{T.current?.cancel(),T.current=null,await e.cancel().catch(()=>{}),n()},A=i?.min??3,j=o%tT.length,ee=o<tT.length?`Phrase ${o+1} of ${tT.length}`:`Extra recording (${o} so far)`;return(0,p.jsxs)(`div`,{className:`approval enroll glass`,role:`dialog`,"aria-label":`Voice enrollment`,style:{pointerEvents:`auto`},children:[(0,p.jsx)(`div`,{className:`approval-head`,children:(0,p.jsx)(`span`,{className:`approval-title`,children:r?`Teach Friend ${r}'s voice`:`Teach Friend your voice`})}),f===`record`&&(0,p.jsxs)(p.Fragment,{children:[!r&&i?.enrolled&&(0,p.jsx)(`p`,{className:`auth-note`,children:`Your voice is already saved. Saving a new one replaces it.`}),r&&(0,p.jsxs)(`p`,{className:`auth-note`,children:[`Hand the device to `,r,`. `,r,` reads each sentence aloud, close to the microphone, in a quiet room.`]}),(0,p.jsxs)(`p`,{className:`auth-note`,children:[ee,`. Press Record, read this aloud in a quiet room, then press Stop.`]}),(0,p.jsx)(`pre`,{className:`approval-summary`,"data-testid":`enroll-phrase`,style:{whiteSpace:`pre-wrap`},children:tT[j]}),(0,p.jsxs)(`div`,{role:`status`,className:`auth-note`,children:[o,` recorded`,o>=A?` — enough to save`:` — at least ${A} needed`,`.`,c?` ${c}`:``]}),y&&(0,p.jsx)(`p`,{role:`alert`,className:`auth-error`,children:y}),(0,p.jsxs)(`div`,{className:`approval-actions`,children:[h?(0,p.jsx)(`button`,{type:`button`,style:d,className:`danger`,onClick:()=>void E(),children:`Stop`}):(0,p.jsx)(`button`,{type:`button`,style:d,className:`primary`,disabled:_||!i||o>=(i?.max??8),onClick:()=>void D(),children:_?`Uploading…`:`Record`}),(0,p.jsx)(`button`,{type:`button`,style:d,disabled:h||_||o<A,onClick:()=>m(`pin-save`),children:r?`Save ${r}'s voice`:`Save my voice`}),!r&&i?.enrolled&&(0,p.jsx)(`button`,{type:`button`,style:d,className:`danger`,disabled:h||_,onClick:()=>m(`pin-delete`),children:`Delete my voice`}),(0,p.jsx)(`button`,{type:`button`,style:d,onClick:()=>void k(),children:`Cancel`})]})]}),(f===`pin-save`||f===`pin-delete`)&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(`p`,{className:`auth-note`,children:f===`pin-save`?r?`Enter your PIN to save ${r}'s voice.`:`Enter your PIN to save your voice.`:`Enter your PIN to delete your saved voice.`}),(0,p.jsx)(x,{pin:S,onChange:C,onSubmit:f===`pin-save`?()=>!_&&void O(async()=>{await e.finish(S),w.current&&m(`saved`)}):void 0}),y&&(0,p.jsx)(`p`,{role:`alert`,className:`auth-error`,children:y}),(0,p.jsxs)(`div`,{className:`approval-actions`,children:[f===`pin-save`?(0,p.jsx)(`button`,{type:`button`,style:d,className:`primary`,disabled:_||S.length<6,onClick:()=>void O(async()=>{await e.finish(S),w.current&&m(`saved`)}),children:`Save`}):(0,p.jsx)(`button`,{type:`button`,style:d,className:`danger`,disabled:_||S.length<6,onClick:()=>void O(async()=>{await e.remove(S),w.current&&m(`deleted`)}),children:`Delete`}),(0,p.jsx)(`button`,{type:`button`,style:d,onClick:()=>{C(``),b(null),m(`record`)},children:`Back`})]})]}),(f===`saved`||f===`deleted`)&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(`p`,{className:`auth-note`,children:f===`saved`?r?`${r}'s voice is saved. From now on I will know when ${r} is talking.`:`Your voice is saved. From now on I will tell you apart from other speakers.`:`Your voice was deleted. Every voice is treated as yours until you enroll again.`}),(0,p.jsx)(`div`,{className:`approval-actions`,children:(0,p.jsx)(`button`,{type:`button`,style:d,className:`primary`,onClick:n,children:`Done`})})]})]})}var iT=new Set([`INPUT`,`TEXTAREA`,`SELECT`,`BUTTON`,`A`,`SUMMARY`]),aT=new Set([`button`,`checkbox`,`switch`,`tab`,`menuitem`,`link`,`radio`,`option`,`textbox`,`combobox`,`slider`]),oT=e=>e instanceof HTMLElement&&(iT.has(e.tagName)||e.isContentEditable||aT.has(e.getAttribute(`role`)??``)),sT=e=>e.code===`Space`||e.key===` `;function cT(e){let t=(0,l.useRef)(e);t.current=e,(0,l.useEffect)(()=>{let e=!1,n=n=>{!sT(n)||n.ctrlKey||n.metaKey||n.altKey||oT(n.target)||oT(document.activeElement)||(n.preventDefault(),!(n.repeat||e)&&(e=!0,t.current.pressStart()))},r=n=>{sT(n)&&e&&(e=!1,n.preventDefault(),t.current.pressEnd())},i=()=>{e=!1,t.current.blur()},a=()=>{document.hidden&&i()};return window.addEventListener(`keydown`,n),window.addEventListener(`keyup`,r),window.addEventListener(`blur`,i),document.addEventListener(`visibilitychange`,a),()=>{window.removeEventListener(`keydown`,n),window.removeEventListener(`keyup`,r),window.removeEventListener(`blur`,i),document.removeEventListener(`visibilitychange`,a)}},[])}function lT(e){let t=(0,l.useRef)(e);t.current=e,(0,l.useEffect)(()=>{let e=()=>t.current.unlock(),n=[`pointerdown`,`keydown`,`touchend`];for(let t of n)window.addEventListener(t,e,{passive:!0});return()=>{for(let t of n)window.removeEventListener(t,e)}},[])}function uT({onUseHere:e}){return(0,p.jsx)(`div`,{className:`elsewhere`,role:`alertdialog`,"aria-labelledby":`elsewhere-title`,"data-testid":`elsewhere`,children:(0,p.jsxs)(`div`,{className:`elsewhere-card`,children:[(0,p.jsx)(`h2`,{id:`elsewhere-title`,children:`Friend is open in another window`}),(0,p.jsx)(`p`,{children:`Friend works in one window at a time. Click “Use here” to use it in this window instead; the other one will stop.`}),(0,p.jsx)(`button`,{type:`button`,className:`elsewhere-btn`,onClick:e,autoFocus:!0,children:`Use here`})]})})}function dT(e,t,n){let r=Math.max(100,Math.round(e)),i=Math.max(100,Math.round(t));return{width:r,height:i,kind:r<=599?`phone`:n||r<1024?`tablet`:`desktop`,orientation:i>r?`portrait`:`landscape`,touch:n}}var fT=()=>typeof matchMedia==`function`&&matchMedia(`(pointer: coarse)`).matches;function pT(){return dT(window.innerWidth,window.innerHeight,fT())}var mT=(e,t)=>!!e&&e.width===t.width&&e.height===t.height&&e.kind===t.kind&&e.orientation===t.orientation&&e.touch===t.touch;function hT(e,t){let n=(0,l.useRef)(null),r=(0,l.useRef)(e);r.current=e,(0,l.useEffect)(()=>{if(t!==`open`){n.current=null;return}let e=()=>{let e=pT();mT(n.current,e)||(n.current=e,r.current({type:`ui.screen`,...e}))};e();let i=null,a=()=>{i&&clearTimeout(i),i=setTimeout(e,400)};return window.addEventListener(`resize`,a),window.addEventListener(`orientationchange`,a),()=>{i&&clearTimeout(i),window.removeEventListener(`resize`,a),window.removeEventListener(`orientationchange`,a)}},[t])}var gT={401:`Please sign in again.`,403:`Incorrect PIN.`,413:`That recording was too long.`,429:`Too many attempts. Wait a moment.`,503:`Voice is not available. Run: friend voice setup`};async function _T(e,t={}){let n;try{n=await fetch(e,t)}catch{throw Error(`Could not reach Friend. Check the connection.`)}if(!n.ok){let e=``;try{e=(await n.json()).error??``}catch{}throw Error(e||gT[n.status]||`The request failed (${n.status}).`)}return await n.json()}var vT=(e,t)=>({method:e,headers:{"content-type":`application/json`},body:JSON.stringify(t)}),yT={overview:()=>_T(`/api/voice/people`),begin:e=>_T(`/api/voice/people/begin`,vT(`POST`,{name:e})),recording:{state:async()=>(await _T(`/api/voice/people`)).enrolling??{enrolled:!1,samples:0,min:3,max:8},sample:e=>_T(`/api/voice/people/sample`,{method:`POST`,headers:{"content-type":e.type||`audio/webm`},body:e}),finish:e=>_T(`/api/voice/people/finish`,vT(`POST`,{pin:e})),cancel:async()=>{await _T(`/api/voice/people/cancel`,vT(`POST`,{}))},remove:async()=>{}},removePerson:async(e,t)=>{await _T(`/api/voice/people/${e}`,vT(`DELETE`,{pin:t}))}};function bT({api:e=yT,createRecorder:t,onClose:n}){let[r,i]=(0,l.useState)(null),[a,o]=(0,l.useState)({kind:`list`}),[s,c]=(0,l.useState)(``),[u,f]=(0,l.useState)(``),[m,h]=(0,l.useState)(!1),[g,_]=(0,l.useState)(null),v=(0,l.useRef)(!0),y=(0,l.useCallback)(()=>{e.overview().then(e=>v.current&&i(e)).catch(e=>v.current&&_(e.message))},[e]);(0,l.useEffect)(()=>(v.current=!0,y(),()=>{v.current=!1}),[y]);let b=async()=>{_(null),h(!0);try{let t=await e.begin(s.trim());v.current&&o({kind:`record`,name:t.name})}catch(e){v.current&&_(e.message)}finally{v.current&&h(!1)}},S=async t=>{_(null),h(!0);try{if(await e.removePerson(t.id,u),!v.current)return;o({kind:`list`}),y()}catch(e){v.current&&_(e.message)}finally{v.current&&(h(!1),f(``))}};return a.kind===`record`?(0,p.jsx)(rT,{api:e.recording,who:a.name,...t?{createRecorder:t}:{},onClose:()=>{c(``),o({kind:`list`}),y()}}):(0,p.jsxs)(`div`,{className:`approval enroll glass`,role:`dialog`,"aria-label":`Family voices`,style:{pointerEvents:`auto`},children:[(0,p.jsx)(`div`,{className:`approval-head`,children:(0,p.jsx)(`span`,{className:`approval-title`,children:`Family voices`})}),a.kind===`list`&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(`p`,{className:`auth-note`,children:`Friend knows your voice. Add the people at home, so it knows who is talking and answers them by name. They can chat and ask for the weather, but cannot use your PC or see your private things.`}),r&&!r.ownerEnrolled&&(0,p.jsx)(`p`,{className:`auth-note`,children:`First teach Friend your own voice (Esc, then Enroll my voice).`}),r&&r.people.length===0&&r.ownerEnrolled&&(0,p.jsx)(`p`,{className:`auth-note`,children:`Nobody added yet.`}),r&&r.people.length>0&&(0,p.jsx)(`ul`,{className:`household-list`,"aria-label":`People Friend knows`,children:r.people.map(e=>(0,p.jsxs)(`li`,{className:`household-row`,children:[(0,p.jsx)(`span`,{children:e.name}),(0,p.jsx)(`button`,{type:`button`,style:d,className:`danger`,onClick:()=>o({kind:`remove`,person:e}),"aria-label":`Remove ${e.name}`,children:`Remove`})]},e.id))}),r&&r.ownerEnrolled&&r.people.length<r.max&&(0,p.jsxs)(`label`,{className:`settings-field`,children:[(0,p.jsx)(`span`,{children:`Add a person`}),(0,p.jsx)(`input`,{value:s,maxLength:40,placeholder:`Name, for example Priya`,onChange:e=>c(e.target.value),onKeyDown:e=>e.key===`Enter`&&s.trim()&&!m&&void b()})]}),g&&(0,p.jsx)(`p`,{role:`alert`,className:`auth-error`,children:g}),(0,p.jsxs)(`div`,{className:`approval-actions`,children:[r&&r.ownerEnrolled&&r.people.length<r.max&&(0,p.jsx)(`button`,{type:`button`,style:d,className:`primary`,disabled:m||!s.trim(),onClick:()=>void b(),children:`Record their voice`}),(0,p.jsx)(`button`,{type:`button`,style:d,onClick:n,children:`Close`})]})]}),a.kind===`remove`&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)(`p`,{className:`auth-note`,children:[`Enter your PIN to remove `,a.person.name,`’s voice. Friend will treat their voice as a stranger’s.`]}),(0,p.jsx)(x,{pin:u,onChange:f}),g&&(0,p.jsx)(`p`,{role:`alert`,className:`auth-error`,children:g}),(0,p.jsxs)(`div`,{className:`approval-actions`,children:[(0,p.jsx)(`button`,{type:`button`,style:d,className:`danger`,disabled:m||u.length<6,onClick:()=>void S(a.person),children:`Remove`}),(0,p.jsx)(`button`,{type:`button`,style:d,onClick:()=>{f(``),_(null),o({kind:`list`})},children:`Back`})]})]})]})}function xT({onSignedOut:e}){let{state:t,send:n,dispatch:r,voice:i,useHere:a}=Xw(e);cT(i),lT(i),hT(n,t.connection);let[o,s]=(0,l.useState)(!1),[c,u]=(0,l.useState)(!1),[d,f]=(0,l.useState)(!1),[m,h]=(0,l.useState)(!1),[g,_]=(0,l.useState)(null),[v,y]=(0,l.useState)(!1),[b,x]=(0,l.useState)(0),[S,C]=(0,l.useState)(null),w=de(t.view),T=(0,l.useMemo)(()=>o?{...t,objects:[],captions:``}:t,[o,t]),E=(0,l.useRef)(!1);(0,l.useEffect)(()=>{E.current||!t.voice.available||t.voice.enrolled||(E.current=!0,r({type:`alert.add`,level:`info`,text:`Voice recognition is not set up. Press Esc, then Enroll my voice.`}))},[t.voice.available,t.voice.enrolled,r]);let D=ne(),O=(0,l.useRef)(!1);(0,l.useEffect)(()=>{if(!D||O.current||t.connection!==`open`)return;O.current=!0;let e=!1;try{e=localStorage.getItem(`friend.touchHint`)===`1`,localStorage.setItem(`friend.touchHint`,`1`)}catch{}e||r({type:`alert.add`,level:`info`,text:`Hold the round mic button to talk, or tap it once to start and again to send. The keyboard button types; the menu button opens settings.`})},[D,t.connection,r]);let k=(0,l.useRef)(!1);(0,l.useEffect)(()=>{k.current||t.connection!==`open`||(k.current=!0,fetch(`/api/history`).then(e=>e.ok?e.json():[]).then(e=>r({type:`chat.seed`,lines:e})).catch(()=>{}))},[t.connection,r]),(0,l.useEffect)(()=>{t.connection===`open`&&n({type:`ui.panels`,open:[...w.show.chat?[`chat`]:[],...w.show.tasks?[`tasks`]:[]]})},[t.connection,w.show.chat,w.show.tasks,n]);let j=(0,l.useRef)(!1);(0,l.useEffect)(()=>{t.viewKnown&&t.view===`minimal`&&!j.current&&(j.current=!0,r({type:`alert.add`,level:`info`,text:`The view is Minimal, so the status line and clock are hidden. Press Esc, then View, to bring them back.`}))},[t.viewKnown,t.view,r]);let ee=e=>{Ie.save({view:e}).catch(e=>r({type:`alert.add`,level:`warn`,text:e.message}))},M=(0,l.useMemo)(()=>F({notify:e=>r({type:`alert.add`,level:`info`,text:e}),openSettings:()=>_(`model`),openLook:()=>_(`look`),openConnectors:()=>y(!0),openPlugins:()=>h(!0),openVoice:t.voice.available?()=>u(!0):null,muted:o,setMuted:s,cameraOn:t.camera.enabled,setCamera:e=>r({type:`camera.enable`,value:e}),clearScreen:()=>{for(let e of t.objects)n({type:`stage.event`,objectId:e.id,event:`dismiss`})},statusLine:()=>Ce({connection:t.connection,deviceOnline:t.deviceOnline,approval:t.approval,voiceMode:t.voice.mode,busy:t.busy,activity:t.activity}).text,endSession:t=>{fetch(t===`lock`?`/api/lock`:`/api/logout`,{method:`POST`,headers:{"content-type":`application/json`},body:`{}`}).catch(()=>{}).finally(e)},panelOn:e=>w.show[e],setPanel:(e,t)=>w.toggle(e,t),view:t.view,setView:ee,openHelp:C,resetLayout:()=>{ue(),window.location.reload()}}),[r,n,o,e,w.show,w.toggle,t.view,t.voice.available,t.voice.mode,t.camera.enabled,t.objects,t.connection,t.deviceOnline,t.approval,t.busy,t.activity]);return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(ow,{state:T,send:n,dispatch:r,voice:i,slash:M,onMenu:()=>x(e=>e+1)}),(0,p.jsxs)(Ee,{approval:t.approval,challenge:t.challenge,cameraStream:t.camera.stream,alerts:t.alerts,connection:t.connection,deviceOnline:t.deviceOnline,voiceMode:t.voice.mode,busy:t.busy,activity:t.activity,showStatus:t.viewKnown&&w.show.status,showClock:t.viewKnown&&w.show.clock,profile:t.viewKnown&&w.show.profile?{ownerName:t.ownerName,speaker:t.speaker}:null,send:n,dispatch:r,children:[t.viewKnown&&w.show.chat&&(0,p.jsx)(fe,{lines:t.chat,live:t.chatLive,onSend:e=>n({type:`chat.send`,text:e}),onClose:()=>w.toggle(`chat`,!1)}),t.viewKnown&&w.show.tasks&&(0,p.jsx)(he,{busy:t.busy,onClose:()=>w.toggle(`tasks`,!1)}),c&&(0,p.jsx)(rT,{onClose:()=>u(!1)}),d&&(0,p.jsx)(bT,{onClose:()=>f(!1)}),m&&(0,p.jsx)(je,{onClose:()=>h(!1)}),g&&(0,p.jsx)(Ay,{initialTab:g,onClose:()=>_(null)}),v&&(0,p.jsx)(A,{onClose:()=>y(!1)}),S&&(0,p.jsx)(I,{commands:S,onClose:()=>C(null)})]}),(0,p.jsx)(V,{muted:o,onToggleMute:()=>s(e=>!e),onSignedOut:e,voiceAvailable:t.voice.available,voiceEnrolled:t.voice.enrolled,onEnroll:()=>u(!0),onHousehold:()=>f(!0),onPlugins:()=>h(!0),onSettings:()=>_(`model`),onConnectors:()=>y(!0),cameraOn:t.camera.enabled,onToggleCamera:()=>r({type:`camera.enable`,value:!t.camera.enabled}),chatOn:w.show.chat,onToggleChat:()=>w.toggle(`chat`),tasksOn:w.show.tasks,onToggleTasks:()=>w.toggle(`tasks`),view:t.view,onView:ee,openRequest:b}),t.connection===`elsewhere`&&(0,p.jsx)(uT,{onUseHere:a})]})}function ST(){let[e,t]=(0,l.useState)(`loading`);return(0,l.useEffect)(()=>{let e=!0,n=n=>e&&t(n);return(async()=>{try{if(!(await(await fetch(`/api/state`)).json()).hasOwner)return n(`setup`);let e=await(await fetch(`/api/session`,{cache:`no-store`})).json();n(e.signedIn===!0?`stage`:`login`)}catch{n(`login`)}})(),()=>{e=!1}},[]),e===`loading`?(0,p.jsx)(`div`,{className:`boot`,"aria-busy":`true`}):e===`setup`?(0,p.jsx)(g,{onDone:()=>t(`login`)}):e===`login`?(0,p.jsx)(h,{onLoggedIn:()=>t(`stage`)}):(0,p.jsx)(xT,{onSignedOut:()=>t(`login`)})}(0,u.createRoot)(document.getElementById(`root`)).render((0,p.jsx)(l.StrictMode,{children:(0,p.jsx)(ST,{})}));
|
package/dist/web/index.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
9
9
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
10
10
|
<title>Friend</title>
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-BtrPzwzP.js"></script>
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/assets/index-Dvv5DXnC.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "friend-assistant",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "A self-hosted personal AI assistant for your own PC: a full-screen stage, voice, tools with your approval, and your Claude subscription or a local model.",
|
|
5
5
|
"keywords": ["ai", "assistant", "jarvis", "voice", "claude", "ollama", "mcp", "self-hosted"],
|
|
6
6
|
"license": "UNLICENSED",
|