@xauyxau/hnch 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +20 -4
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,5 +1,21 @@
1
1
  #!/usr/bin/env node
2
- import Gr from'yargs';import {hideBin}from'yargs/helpers';import St,{useState,useEffect,useCallback,useMemo,useRef}from'react';import {render,useApp,useInput,Box,Text}from'ink';import*as Ie from'yjs';import {Awareness}from'y-protocols/awareness';import {mkdirSync,chmodSync,writeFileSync,unlinkSync,existsSync,readFileSync}from'fs';import {homedir}from'os';import {join}from'path';import {randomUUID}from'crypto';import {createInterface}from'readline';import {WebsocketProvider}from'y-websocket';import Zo from'ws';import _r from'clipboardy';import {jsxs,jsx,Fragment}from'react/jsx-runtime';var Mo="https://hnch.dev";function Oe(){let t=process.env.XDG_CONFIG_HOME||join(homedir(),".config");return join(t,"hnch")}function It(){return join(Oe(),"config.json")}function nn(){let e=Oe();existsSync(e)||mkdirSync(e,{recursive:true});}function ge(){let e=It();if(!existsSync(e))return {};try{return JSON.parse(readFileSync(e,"utf-8"))}catch{return {}}}function Y(e){nn();let o={...ge(),...e};writeFileSync(It(),JSON.stringify(o,null,2)+`
3
- `,"utf-8");}async function ko(e){let t=createInterface({input:process.stdin,output:process.stderr});return new Promise(o=>{t.question(e,n=>{t.close(),o(n.trim());});})}function $o(e){return e.startsWith("wss://")?"https://"+e.slice(6):e.startsWith("ws://")?"http://"+e.slice(5):e}async function De(){let e=ge(),t=false;if(e.participantId||(e.participantId=randomUUID(),t=true),!e.displayName){let n=await ko("Enter your display name: ");e.displayName=n||`user-${e.participantId.slice(0,6)}`,t=true;}e.relayUrl||(e.relayUrl=Mo,t=true),t&&Y(e);let o=process.env.HNCH_RELAY_URL||e.relayUrl;return o=$o(o),{participantId:e.participantId,displayName:e.displayName,relayUrl:o,defaultScale:e.defaultScale,defaultSessionName:e.defaultSessionName,defaultPassword:e.defaultPassword,presetOverrides:e.presetOverrides,customScales:e.customScales}}function Me(e){nn();let o={...ge()};for(let[n,r]of Object.entries(e))r===""?delete o[n]:r!==void 0&&(o[n]=r);writeFileSync(It(),JSON.stringify(o,null,2)+`
4
- `,"utf-8");}var Bo={"unknown-room":4001,"wrong-password":4002,"invalid-invite":4003,"expired-invite":4004,"invite-already-redeemed":4005,"room-full":4006,"malformed-credentials":4010},on=new Map(Object.entries(Bo).map(([e,t])=>[t,e]));function he(e){return e.role??"voter"}function W(e){let{revealedAt:t,skippedAt:o}=e;return t===void 0&&o===void 0?null:t===void 0?"skipped":o===void 0||t<=o?"revealed":"skipped"}var ze={id:"fibonacci",cards:["0","1","2","3","5","8","13","21"]},rn={id:"t-shirt",cards:["XS","S","M","L","XL"]},sn={id:"powers-of-2",cards:["1","2","4","8","16","32","64"]},an={id:"linear",cards:["1","2","3","4","5","6","7","8","9","10"]},cn={id:"risk",cards:["Low","Medium","High","Critical"]},te={[ze.id]:ze,[rn.id]:rn,[sn.id]:sn,[an.id]:an,[cn.id]:cn},dn={fibonacci:"Fibonacci","t-shirt":"T-shirt","powers-of-2":"Powers of 2",linear:"Linear (1\u201310)",risk:"Risk"};function ln(e){return e in dn?dn[e]:e.length===0?e:(e.startsWith("custom-")?e.slice(7):e).split("-").map(o=>o.length===0?o:o.charAt(0).toUpperCase()+o.slice(1)).join(" ")}var Ho="meta",ke="settings",Uo="participants",jo="rounds",Lo=["roomUuid","createdAt","createdBy","sessionEndedAt"],_o=["name","defaultScale","hostParticipantId"],Fo=["displayName","joinedAt","role","leftAt","removedBy"],Vo=["id","title","description","ticketId","parentRoundId","scale","timerStartedAt","revealedAt","skippedAt","createdBy","createdAt"];function Qe(e){return e.getMap(Ho)}function Ke(e){return e.getMap(ke)}function Xe(e){return e.getMap(Uo)}function Tt(e){return e.getArray(jo)}function Ze(e,t){let o={};for(let n of t){let r=e.get(n);r!==void 0&&(o[n]=r);}return o}function Yo(e){return Ze(Qe(e),Lo)}function Jo(e){return Ze(Ke(e),_o)}function Wo(e){let t=Xe(e),o=[];return t.forEach((n,r)=>{let s=Ze(n,Fo);o.push({id:r,...s});}),o.sort((n,r)=>n.joinedAt-r.joinedAt||n.id.localeCompare(r.id)),o}function qo(e){let t={};return e.forEach((o,n)=>{t[n]=o;}),t}function Go(e){let t=Ze(e,Vo),o=e.get("votes"),n=o?qo(o):{};return {...t,votes:n}}function zo(e){let t=Tt(e),o=[];return t.forEach(n=>{o.push(Go(n));}),o}function z(e){return {meta:Yo(e),settings:Jo(e),participants:Wo(e),rounds:zo(e)}}function un(e,t){if(Qe(e).has("roomUuid"))throw new Error("initDoc: doc already seeded");e.transact(()=>{let o=Qe(e);o.set("roomUuid",t.roomUuid),o.set("createdAt",t.createdAt),o.set("createdBy",t.participantId);let n=Ke(e);t.sessionName!==void 0&&n.set("name",t.sessionName),n.set("defaultScale",t.defaultScale),n.set("hostParticipantId",t.participantId);let r=new Ie.Map;r.set("displayName",t.displayName),r.set("joinedAt",t.createdAt),Xe(e).set(t.participantId,r);});}function et(e,t){e.transact(()=>{let o=Xe(e),n=o.get(t.participantId),r=n??new Ie.Map;r.set("displayName",t.displayName),n===void 0&&r.set("joinedAt",Date.now()),r.delete("leftAt"),r.delete("removedBy"),n===void 0&&o.set(t.participantId,r);});}function Te(e,t){e.transact(()=>{let o=Ke(e);for(let[n,r]of Object.entries(t))r===void 0?o.delete(n):o.set(n,r);});}function Et(e,t,o){e.transact(()=>{let n=Xe(e),r=n.get(t),s=r??new Ie.Map;s.set("displayName",o.displayName),s.set("joinedAt",o.joinedAt),o.role!==void 0&&s.set("role",o.role),r===void 0&&n.set(t,s);});}function At(e,t){let o=t.scale??Ke(e).get("defaultScale");if(!o)throw new Error("appendRound: no scale (settings.defaultScale missing)");return e.transact(()=>{let n=new Ie.Map;n.set("id",t.id),n.set("createdAt",t.createdAt),n.set("createdBy",t.createdBy),n.set("scale",o),t.title!==void 0&&n.set("title",t.title),t.description!==void 0&&n.set("description",t.description),t.ticketId!==void 0&&n.set("ticketId",t.ticketId),t.parentRoundId!==void 0&&n.set("parentRoundId",t.parentRoundId),n.set("timerStartedAt",t.createdAt),n.set("votes",new Ie.Map),Tt(e).push([n]);}),t.id}function pn(e,t,o){let n=$e(e,t);if(!n)throw new Error(`patchRoundDescriptor: round ${t} not found`);e.transact(()=>{o.title!==void 0&&n.set("title",o.title),o.description!==void 0&&n.set("description",o.description);});}function $e(e,t){let o=Tt(e);for(let n=0;n<o.length;n+=1){let r=o.get(n);if(r.get("id")===t)return r}}function fn(e,t,o,n){let r=$e(e,t);if(!r)throw new Error(`castVote: round ${t} not found`);r.get("revealedAt")===void 0&&e.transact(()=>{r.get("votes").set(o,n);});}function mn(e,t,o){let n=$e(e,t);if(!n)throw new Error(`clearVote: round ${t} not found`);n.get("revealedAt")===void 0&&e.transact(()=>{n.get("votes").delete(o);});}function Nt(e,t,o){let n=$e(e,t);if(!n)throw new Error(`revealRound: round ${t} not found`);if(n.get("skippedAt")!==void 0)throw new Error(`revealRound: round ${t} already skipped`);e.transact(()=>{n.set("revealedAt",o);});}function gn(e,t,o){let n=$e(e,t);if(!n)throw new Error(`skipRound: round ${t} not found`);if(n.get("revealedAt")!==void 0)throw new Error(`skipRound: round ${t} already revealed`);if(n.get("skippedAt")!==void 0)throw new Error(`skipRound: round ${t} already skipped`);e.transact(()=>{n.set("skippedAt",o);});}function hn(e){e.transact(()=>{Qe(e).set("sessionEndedAt",Date.now());});}function X(e){let{rounds:t}=e;if(t.length===0)return null;for(let o=t.length-1;o>=0;o-=1){let n=t[o];if(n.revealedAt===void 0)return n}return t.at(-1)}function Sn(e){let t=Object.values(e.votes);return t.length<2?false:t.every(o=>o===t[0])}function xn(e,t){let o=new Map;for(let a of t)o.set(a.id,a);let n=o.get(e);if(n===void 0||n.parentRoundId===void 0)return null;let r=new Set([n.id]),s=n,u=1;for(;s.parentRoundId!==void 0;){let a=o.get(s.parentRoundId);if(a===void 0||r.has(a.id))return null;r.add(a.id),s=a,u+=1;}let d=s,p=new Map;for(let a of t){if(a.parentRoundId===void 0)continue;let g=p.get(a.parentRoundId);g===void 0?p.set(a.parentRoundId,[a]):g.push(a);}let c=0,i=[d];for(;i.length>0;){let a=i.shift();c+=1;let g=p.get(a.id);g!==void 0&&i.push(...g);}return {chainRoot:d,depth:u,total:c}}var wn="PBKDF2",Qo="SHA-256",yn=new TextEncoder;function Ko(e){return Array.from(new Uint8Array(e)).map(t=>t.toString(16).padStart(2,"0")).join("")}async function Se(e,t){let o=await crypto.subtle.importKey("raw",yn.encode(e),wn,false,["deriveBits"]),n=await crypto.subtle.deriveBits({name:wn,salt:yn.encode(t),iterations:1e5,hash:Qo},o,256);return Ko(n)}var vn="ABCDEFGHJKMNPQRSTUVWXYZ23456789";if(vn.length!==31)throw new Error(`Room code alphabet must be 31 characters, got ${vn.length}`);function Pt(e){return e.trim().replaceAll(/-/g,"").toUpperCase()}function Cn(e,t=true){let o=Pt(e);return !t||o.length!==6?o:`${o.slice(0,3)}-${o.slice(3)}`}function tt(e,t,o){if(e.startsWith("custom-")){let r=(o??[]).find(s=>s.id===e);return r?{id:r.id,cards:r.cards}:void 0}let n=te[e];if(n){let r=(t??{})[e]??[];if(r.length===0)return n;let s=n.cards.filter((u,d)=>!r.includes(d));return s.length===0?void 0:{id:e,cards:s}}}function nt(e,t){let o=Object.keys(te).map(r=>tt(r,e,t)).filter(r=>r!==void 0),n=(t??[]).map(r=>({id:r.id,cards:r.cards}));return [...o,...n]}function xe(e,t){if(e.startsWith("custom-")){let o=(t??[]).find(n=>n.id===e);if(o&&o.label.trim().length>0)return o.label}return ln(e)}async function bn(e,t){let o=await fetch(`${e}/rooms`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})});if(!o.ok)throw new Error(`POST /rooms failed: ${o.status}`);return await o.json()}async function Be(e,t){let o=await fetch(`${e}/rooms/${encodeURIComponent(t)}`);if(o.status===404){let n=await o.json().catch(()=>null);throw new Error(`Room not found: ${n?.reason??"unknown-room"}`)}if(!o.ok)throw new Error(`GET /rooms/:code failed: ${o.status}`);return await o.json()}async function Ot(e,t,o){let n=await fetch(`${e}/rooms/${t}/access`,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify({passwordHash:o})});if(!n.ok)throw new Error(`PATCH /rooms/:uuid/access failed: ${n.status}`)}function Rn(e){let o=`${e.wsBaseUrl.replace(/^http/,"ws")}/ws`,n={did:e.deviceId};e.passwordHash!==void 0&&(n.pw=e.passwordHash);let r=new WebsocketProvider(o,e.roomUuid,e.doc,{params:n,connect:true,...e.awareness!==void 0&&{awareness:e.awareness},WebSocketPolyfill:Zo}),s=c=>{e.onStatusChange?.(c.status);},u=c=>{if(c===null)return;let i=on.get(c.code);i!==void 0&&(e.onRejection?.(i),r.shouldConnect=false,r.disconnect());};r.on("status",s),r.on("connection-close",u);let d=false,p=e.passwordHash;return {doc:e.doc,awareness:r.awareness,updateCredentials(c){if(c.passwordHash===p)return;p=c.passwordHash??void 0,r.shouldConnect=false,r.disconnect();let a={did:r.params.did??""};p!==void 0&&(a.pw=p),r.params=a,r.connect();},disconnect(){d||(d=true,r.off("status",s),r.off("connection-close",u),r.shouldConnect=false,r.disconnect(),r.destroy());}}}var er=5e3;function In(e,t,o){let n=null,r=null;function s(){let i=new Set;return t.getStates().forEach(a=>{let g=a.participantId;typeof g=="string"&&i.add(g);}),i}function u(){return e.getMap(ke).get("hostParticipantId")}function d(){r!==null&&(clearTimeout(r),r=null),n=null;}function p(){let i=u(),a=n;if(n=null,!a||i!==a)return;let g=s();if(g.has(i))return;let T=[...g].toSorted();if(T.length===0)return;let N=T[0];N===o&&Te(e,{hostParticipantId:N});}function c(){let i=u();if(!i)return;if(s().has(i)){d();return}n!==i&&(d(),n=i,r=setTimeout(()=>{r=null,p();},er));}return t.on("change",c),e.getMap(ke).observe(c),c(),{stop(){d(),t.off("change",c),e.getMap(ke).unobserve(c);}}}async function An(e){let t,o=await bn(e.relayUrl);e.password&&(t=await Se(e.password,o.uuid),await Ot(e.relayUrl,o.uuid,t));let n=new Ie.Doc,r=new Awareness(n),s=Date.now();return un(n,{roomUuid:o.uuid,createdAt:s,participantId:e.participantId,displayName:e.displayName,defaultScale:e.defaultScale??ze,...e.sessionName!==void 0&&{sessionName:e.sessionName}}),r.setLocalStateField("participantId",e.participantId),ot({doc:n,awareness:r,roomCode:o.code,roomUuid:o.uuid,participantId:e.participantId,relayUrl:e.relayUrl,passwordHash:t})}async function Nn(e){let t=await Be(e.relayUrl,e.roomCode),o;if(t.accessMode==="password"){if(!e.password)throw new Error("Room requires a password");o=await Se(e.password,t.uuid);}let n=new Ie.Doc,r=new Awareness(n);return et(n,{participantId:e.participantId,displayName:e.displayName}),r.setLocalStateField("participantId",e.participantId),ot({doc:n,awareness:r,roomCode:e.roomCode,roomUuid:t.uuid,participantId:e.participantId,relayUrl:e.relayUrl,passwordHash:o,onRejection:e.onRejection})}function ot(e){let{doc:t,awareness:o,participantId:n,relayUrl:r,roomUuid:s}=e,u=new Set,d=new Set,p=new Set,c=Rn({wsBaseUrl:e.relayUrl,roomUuid:e.roomUuid,doc:t,deviceId:n,passwordHash:e.passwordHash,awareness:o,onStatusChange(l){u.forEach(m=>m(l));},onRejection(l){console.error(`Connection rejected: ${l}`),e.onRejection?.(l);}}),i=In(t,o,n),a=()=>{d.forEach(l=>l());};t.getMap("meta").observeDeep(a),t.getMap("settings").observeDeep(a),t.getMap("participants").observeDeep(a),t.getArray("rounds").observeDeep(a);function g(){let l=new Set;return o.getStates().forEach(m=>{let v=m.participantId;typeof v=="string"&&l.add(v);}),l}let T=new Set,N=setInterval(()=>{let l=z(t),m=X(l);if(!m||m.revealedAt!==void 0||T.has(m.id))return;let v=g();if(v.size===0)return;let w=l.participants.filter(f=>v.has(f.id)&&he(f)==="voter");if(w.length===0)return;w.filter(f=>f.id in m.votes).length>=w.length&&(T.add(m.id),Nt(t,m.id,Date.now()));},250),E=t.getMap("settings").get("hostParticipantId"),I=()=>{let l=t.getMap("settings").get("hostParticipantId");l!==E&&l!==void 0&&p.forEach(m=>m(l)),E=l;};t.getMap("settings").observe(I);let H=false;return {doc:t,awareness:o,roomCode:e.roomCode,roomUuid:e.roomUuid,projection(){return z(t)},current(){return X(z(t))},isHost(){return z(t).settings.hostParticipantId===n},onlineIds:g,vote(l){let m=X(z(t));m&&fn(t,m.id,n,l);},unvote(){let l=X(z(t));l&&mn(t,l.id,n);},reveal(){let l=X(z(t));l&&Nt(t,l.id,Date.now());},skip(){let l=X(z(t));l&&gn(t,l.id,Date.now());},newRound(l){let m=randomUUID();return At(t,{id:m,createdAt:Date.now(),createdBy:n,...l!==void 0&&{title:l}}),m},revote(){let l=X(z(t)),m=randomUUID();return At(t,{id:m,createdAt:Date.now(),createdBy:n,...l?.id!==void 0&&{parentRoundId:l.id},...l?.title!==void 0&&{title:l.title},...l?.description!==void 0&&{description:l.description},...l?.ticketId!==void 0&&{ticketId:l.ticketId}}),m},updateRoundDescriptor(l,m){pn(t,l,m);},updateName(l){Te(t,{name:l});},updateScale(l){Te(t,{defaultScale:l});},updateDisplayName(l){let w=z(t).participants.find(M=>M.id===n)?.joinedAt??Date.now();Et(t,n,{displayName:l,joinedAt:w});},async updateAccess(l,m){let v=null;if(l==="password"){if(!m)throw new Error("Password required for password mode");v=await Se(m,s);}await Ot(r,s,v),c.updateCredentials({passwordHash:v});},handoffHost(l){if(!this.isHost())throw new Error("Only the current host can hand off");if(!g().has(l))throw new Error("Target participant is not online");if(!z(t).participants.some(M=>M.id===l))throw new Error("Target participant does not exist");Te(t,{hostParticipantId:l});},toggleRole(){let m=z(t).participants.find(f=>f.id===n),w=he(m??{})==="viewer"?"voter":"viewer",M=m?.joinedAt??Date.now();Et(t,n,{displayName:m?.displayName??"",joinedAt:M,role:w});},endSessionForEveryone(){if(!this.isHost())throw new Error("Only the current host can end the session");hn(t);},onChange(l){return d.add(l),()=>d.delete(l)},onStatusChange(l){return u.add(l),()=>u.delete(l)},onHostChange(l){return p.add(l),()=>p.delete(l)},disconnect(){H||(H=true,clearInterval(N),i.stop(),t.getMap("meta").unobserveDeep(a),t.getMap("settings").unobserveDeep(a),t.getMap("settings").unobserve(I),t.getMap("participants").unobserveDeep(a),t.getArray("rounds").unobserveDeep(a),c.disconnect());}}}function Pn(e){let t=Pt(e);return t===""?null:t}var ar=1440*60*1e3;function Mt(){return join(Oe(),"last-session.json")}function cr(e){if(typeof e!="object"||e===null)return false;let t=e;return typeof t.roomCode=="string"&&typeof t.roomUuid=="string"&&typeof t.lastSeenAt=="number"&&(t.passwordHash===void 0||typeof t.passwordHash=="string")}function Dn(){let e=Mt();if(!existsSync(e))return null;try{let t=JSON.parse(readFileSync(e,"utf-8"));return !cr(t)||Date.now()-t.lastSeenAt>ar?(F(),null):t}catch{return F(),null}}function rt(e){let t=Oe();mkdirSync(t,{recursive:true,mode:448}),chmodSync(t,448);let o=Mt();writeFileSync(o,JSON.stringify(e,null,2)+`
5
- `,{encoding:"utf-8",mode:384}),chmodSync(o,384);}function F(){let e=Mt();try{unlinkSync(e);}catch(t){if(t.code!=="ENOENT")throw t}}async function st(e){let t=true;try{await e.clearLastSession();}catch(o){t=false,e.logger.error(`Failed to clear cached session: ${o instanceof Error?o.message:String(o)}`);}return t?e.logger.error("Session expired (room no longer exists). Cached session cleared."):e.logger.error("Session expired. Could not delete ~/.config/hnch/last-session.json \u2014 please remove it manually."),e.exit(1)}function it(e){let t=e.doc.getMap("meta"),o=null,n=false;function r(){if(n)return;let s=t.get("sessionEndedAt");if(s==null||s===o)return;o=s;let u=t.get("createdBy"),d=u===e.localParticipantId,p=e.doc.getMap("participants"),c=e.doc.getMap("settings").get("hostParticipantId")??u,i="host";if(c){let g=p.get(c)?.get("displayName");typeof g=="string"&&g.length>0&&(i=g);}setImmediate(()=>{n||e.onEnded({isLocal:d,hostName:i,timestamp:s});});}return t.observe(r),r(),()=>{n=true,t.unobserve(r);}}var dr="@xauyxau/hnch";function at(e){return Cn(e)}function ct(e){return `${process.env.HNCH_SHARE_ORIGIN??"https://hnch.dev"}/join?code=${e}`}function dt(e){return `npx ${dr} join ${e}`}var lr={connecting:"yellow",connected:"green",disconnected:"red"};function $n({roomCode:e,sessionName:t,status:o,isHost:n}){let r=t?`${e} \xB7 ${t}`:e;return jsxs(Box,{borderStyle:"single",borderColor:"gray",paddingX:1,justifyContent:"space-between",children:[jsxs(Text,{bold:true,children:["hnch \xB7 ",r]}),jsxs(Box,{gap:2,children:[n&&jsx(Text,{color:"yellow",children:"[host]"}),jsx(Text,{color:lr[o],children:o})]})]})}function ce({items:e,onSelect:t,onClose:o,title:n}){if(e.length===0)return jsxs(Box,{flexDirection:"column",gap:1,children:[n&&jsx(Text,{bold:true,children:n}),jsx(Text,{dimColor:true,children:"(no items)"})]});let r=e.findIndex(d=>!d.disabled),[s,u]=useState(Math.max(r,0));return useInput((d,p)=>{if(p.upArrow)u(c=>{let i=(c-1+e.length)%e.length,a=0;for(;e[i]?.disabled&&a<e.length;)i=(i-1+e.length)%e.length,a++;return i});else if(p.downArrow)u(c=>{let i=(c+1)%e.length,a=0;for(;e[i]?.disabled&&a<e.length;)i=(i+1)%e.length,a++;return i});else if(p.return){let c=e[s];c&&!c.disabled&&t(c.value);}else p.escape&&o?.();}),jsxs(Box,{flexDirection:"column",gap:1,children:[n&&jsx(Text,{bold:true,children:n}),jsx(Box,{flexDirection:"column",children:e.map((d,p)=>{let c=p===s,i=d.disabled??false;return jsxs(Box,{gap:1,children:[jsx(Text,{...c&&{color:"cyan"},dimColor:i,children:c?">":" "}),jsx(Text,{bold:c,dimColor:i,children:d.label}),d.detail&&jsx(Text,{dimColor:true,children:d.detail})]},d.value)})}),jsx(Text,{dimColor:true,children:"\u2191\u2193 select \u23CE confirm esc close"})]})}function q({value:e,onChange:t,onSubmit:o,onCancel:n,placeholder:r,isActive:s=true,mask:u=false}){useInput((i,a)=>{a.escape?n?.():a.return?o(e):a.backspace||a.delete?t(e.slice(0,-1)):i&&!a.ctrl&&!a.meta&&t(e+i);},{isActive:s});let d=u&&e.length>0?"*".repeat(e.length):e,p=d.length>0?d:r??"",c=e.length===0&&r!==void 0;return jsxs(Text,{children:[jsx(Text,{dimColor:c,children:p}),jsx(Text,{inverse:true,children:" "})]})}function jn({mode:e,password:t,onModeChange:o,onPasswordChange:n,onClose:r}){let[s,u]=useState(e==="open"?0:1),[d,p]=useState(false);return useEffect(()=>{u(e==="open"?0:1);},[e]),useInput((c,i)=>{if(i.leftArrow)u(a=>Math.max(0,a-1));else if(i.rightArrow)u(a=>Math.min(1,a+1));else if(i.return){let a=s===0?"open":"password";a!==e&&o(a),a==="password"&&p(true);}else i.escape&&r?.();},{isActive:!d}),jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Box,{gap:1,children:["open","password"].map((c,i)=>{let a=i===s,g=c===e,T=c==="open"?"Open":"Password";return jsx(Text,{inverse:a,bold:g,...g&&{color:"cyan"},children:g?`[${T}]`:` ${T} `},c)})}),e==="password"&&jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{children:"Password: "}),jsx(q,{value:t,onChange:n,onSubmit:()=>p(false),onCancel:()=>p(false),placeholder:"(required to join)",isActive:d,mask:true}),!d&&jsx(Text,{dimColor:true,children:"\u23CE edit \u2190 \u2192 switch mode esc close"}),d&&jsx(Text,{dimColor:true,children:"\u23CE done esc cancel"})]}),e==="open"&&jsx(Text,{dimColor:true,children:"\u2190\u2192 toggle \u23CE confirm esc close"})]})}function Fn({scaleId:e,cards:t,disabledIndices:o,onOverrideChange:n,onClose:r}){let s=useMemo(()=>new Set(o),[o]),[u,d]=useState(0),p=c=>{let i=new Set(s);if(i.has(c))i.delete(c);else {if(i.size>=t.length-1)return;i.add(c);}let a=Array.from(i).toSorted((g,T)=>g-T);n(e,a);};return useInput((c,i)=>{i.leftArrow?d(a=>Math.max(0,a-1)):i.rightArrow?d(a=>Math.min(t.length-1,a+1)):c===" "?p(u):(i.return||i.escape)&&r();}),jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Box,{gap:1,children:t.map((c,i)=>{let a=i===u,g=s.has(i);return jsx(Text,{inverse:a,bold:!g,...!g&&{color:"cyan"},strikethrough:g,dimColor:g,children:c},c)})}),jsx(Text,{dimColor:true,children:" \u2190\u2192 navigate space toggle \u23CE/esc done"})]})}function _t({initialName:e="",initialCards:t=[],onSave:o,onCancel:n}){let r=e.length>0,[s,u]=useState(e),[d,p]=useState(t.length>0?t:[""]),[c,i]=useState(0),[a,g]=useState("editing-name"),[T,N]=useState(""),E=()=>{let l=s.trim();if(l.length===0||l.length>40){N("Name must be 1-40 characters");return}u(l),N(""),g("editing-cards");},I=()=>{let l=s.trim();if(l.length===0||l.length>40){N("Name must be 1-40 characters");return}let m=d.map(w=>w.trim()).filter(w=>w.length>0),v=Array.from(new Set(m));if(v.length<2){N("Need at least 2 unique non-empty cards");return}N(""),o(l,v);};return useInput((l,m)=>{if(a==="editing-cards"){if(m.leftArrow)i(v=>Math.max(0,v-1));else if(m.rightArrow)i(v=>{let w=v+1;return w>=d.length&&d.length<25?(p(M=>[...M,""]),w):Math.min(w,d.length-1)});else if(m.return)I();else if(m.escape)n();else if(m.tab)g("editing-name");else if(m.backspace){let v=d[c]||"";if(v.length===0){if(d.length>2){let w=d.filter((M,f)=>f!==c);p(w),i(Math.max(0,c-1));}}else {let w=[...d];w[c]=v.slice(0,-1),p(w);}}else if(l&&!m.ctrl&&!m.meta){let v=d[c]||"";if(v.length<10){let w=[...d];w[c]=v+l,p(w);}}}},{isActive:a==="editing-cards"}),jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:r?"\u2500\u2500 Edit Custom Scale \u2500\u2500":"\u2500\u2500 Create Custom Scale \u2500\u2500"}),jsxs(Box,{flexDirection:"column",gap:0,children:[jsx(Text,{children:"Name:"}),jsx(q,{value:s,onChange:u,onSubmit:E,onCancel:n,placeholder:"Scale name",isActive:a==="editing-name"})]}),a==="editing-cards"&&jsxs(Box,{flexDirection:"column",gap:0,children:[jsx(Text,{children:"Cards:"}),jsx(Box,{gap:1,flexWrap:"wrap",children:d.map((l,m)=>{let v=m===c;return jsx(Text,{inverse:v,...!v&&{color:"cyan"},children:l.length>0?l:"\xB7"},m)})})]}),T&&jsx(Text,{color:"red",children:T}),jsx(Text,{dimColor:true,children:a==="editing-name"?"\u23CE next esc cancel":"\u2190\u2192 navigate typing adds card bsp delete \u23CE save tab name esc cancel"})]})}function pt({scales:e,selectedScaleId:t,presetOverrides:o,customScales:n=[],onSelect:r,onOverrideChange:s,onCustomScaleCreate:u,onCustomScaleEdit:d,onCustomScaleDelete:p,onClose:c,showNoneOption:i=false}){let[a,g]=useState(0),[T,N]=useState(null),[E,I]=useState("browsing"),[H,l]=useState(null),[m,v]=useState(null),w=u?[...e,{id:"__create-custom__",cards:[]}]:e,M=w.length;if(useInput((f,k)=>{if(E==="browsing"){if(k.escape){c();return}if(M===0)return;if(k.upArrow)g(b=>(b-1+M)%M);else if(k.downArrow)g(b=>(b+1)%M);else if(k.return){let b=w[a];if(!b)return;if(b.id==="__create-custom__"){I("creating");return}b.id in te?N(T===b.id?null:b.id):r(b.id);}else if(f===" "){let b=w[a];b&&b.id!=="__create-custom__"&&r(b.id);}else if(f==="e"||f==="E"){let b=w[a];d&&b?.id.startsWith("custom-")&&(l(b.id),I("editing"));}else if(f==="d"||f==="D"){let b=w[a];p&&b?.id.startsWith("custom-")&&(v(b.id),I("confirming-delete"));}}else E==="confirming-delete"&&(f==="y"||f==="Y"?(m&&p&&p(m),v(null),I("browsing")):(f==="n"||f==="N"||k.escape)&&(v(null),I("browsing")));},{isActive:E==="browsing"&&T===null||E==="confirming-delete"}),E==="creating")return jsx(_t,{onSave:(f,k)=>{u&&u(f,k),I("browsing");},onCancel:()=>I("browsing")});if(E==="editing"&&H){let f=n?.find(b=>b.id===H);if(e.find(b=>b.id===H)&&f)return jsx(_t,{initialName:f.label,initialCards:f.cards,onSave:(b,L)=>{d&&d(H,b,L),l(null),I("browsing");},onCancel:()=>{l(null),I("browsing");}})}if(E==="confirming-delete"&&m){let f=w.find(b=>b.id===m),k=f?xe(f.id,n):"Unknown";return jsxs(Box,{flexDirection:"column",gap:1,children:[jsxs(Text,{children:["Delete ",k,"? "]}),jsx(Text,{children:"[y] yes [n] no esc cancel"})]})}return w.length===0?jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{dimColor:true,children:"(no scales available)"}),jsx(Text,{dimColor:true,children:"esc back"})]}):jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Box,{flexDirection:"column",children:w.map((f,k)=>{let b=k===a,L=f.id===t,oe=T===f.id,Z=f.id in te,ee=f.id==="__create-custom__",re=f.cards.join(", "),h=i&&f.id==="",C=h?"(none)":ee?"[+ Create custom scale]":xe(f.id,n);return jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{...b&&{color:"cyan"},children:b?">":" "}),jsx(Text,{bold:b,children:C}),!h&&!ee&&jsx(Text,{dimColor:true,children:re}),L&&jsx(Text,{color:"cyan",children:"[current]"})]}),oe&&!h&&!ee&&jsx(Box,{paddingLeft:2,children:Z?jsx(Fn,{scaleId:f.id,cards:te[f.id]?.cards??[],disabledIndices:o?.[f.id]??[],onOverrideChange:s,onClose:()=>N(null)}):null})]},f.id||"none")})}),jsx(Text,{dimColor:true,children:(()=>{let f=a<w.length?w[a]:null;return f?.id.startsWith("custom-")?`\u2191\u2193 navigate space select${d?" [e] edit":""}${p?" [d] delete":""} esc back`:f&&f.id in te?"\u2191\u2193 navigate \u23CE customize space select esc back":"\u2191\u2193 navigate \u23CE/space select esc back"})()})]})}function Rr(e,t){return t?[...e]:e.filter(o=>o.value==="display-name")}function Vn({sessionName:e,displayName:t,currentScale:o,accessMode:n,presetOverrides:r,customScales:s,isHost:u=true,onChangeName:d,onChangeScale:p,onChangeDisplayName:c,onChangeAccess:i,onClose:a}){let[g,T]=useState(r??{}),N=useMemo(()=>nt(g,s),[g,s]),[E,I]=useState("main-menu"),[H,l]=useState(e??""),[m,v]=useState(t),[w,M]=useState(n==="password"?"password":"open"),[f,k]=useState(""),[b,L]=useState(false),oe=[{label:"Room name",value:"room-name",detail:`[${e??"(none)"}]`},{label:"Scale",value:"scale",detail:`[${xe(o.id,s)}]`},{label:"Access",value:"access",detail:`[${n==="open"?"Open":"Password"}]`},{label:"Display name",value:"display-name",detail:`[${t}]`}],Z=Rr(oe,u);function ee(P){!u&&P!=="display-name"||(P==="room-name"?(l(e??""),I("editing-room-name")):P==="scale"?I("picking-scale"):P==="access"?I("picking-access"):P==="display-name"&&(v(t),I("editing-display-name")));}function re(P){let D=P.trim()===""?void 0:P.trim();d(D),I("main-menu");}function h(P){let D=P.trim();D.length>0&&(c(D),process.nextTick(()=>Y({displayName:D}))),I("main-menu");}function C(P){let D=N.find(_=>_.id===P);D&&p(D),I("main-menu");}function A(P,D){let _={...g};D.length===0?delete _[P]:_[P]=D,T(_),Y({presetOverrides:Object.keys(_).length>0?_:void 0});}async function B(P,D){L(true);try{await i(P,D),M(P),k(P==="password"?D??"":"");}catch(_){console.error(`Failed to update access: ${_}`);}finally{L(false),I("main-menu");}}return E==="main-menu"?jsx(ce,{title:"\u2500\u2500 Settings \u2500\u2500",items:Z,onSelect:ee,onClose:a}):E==="editing-room-name"?jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Settings \u2500\u2500"}),jsx(Text,{children:"Room name: "}),jsx(q,{value:H,onChange:l,onSubmit:re,onCancel:()=>I("main-menu"),placeholder:"(leave empty to remove)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]}):E==="editing-display-name"?jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Settings \u2500\u2500"}),jsx(Text,{children:"Display name: "}),jsx(q,{value:m,onChange:v,onSubmit:h,onCancel:()=>I("main-menu"),placeholder:"(required)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]}):E==="picking-scale"?jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Settings \u2500\u2500"}),jsx(pt,{scales:N,selectedScaleId:o.id,presetOverrides:g,onSelect:C,onOverrideChange:A,onClose:()=>I("main-menu")})]}):E==="picking-access"?jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Settings \u2500\u2500"}),b&&jsx(Text,{dimColor:true,children:"Updating access..."}),!b&&jsx(jn,{mode:w,password:f,onModeChange:P=>{P==="open"&&B("open");},onPasswordChange:k,onClose:()=>I("main-menu")})]}):jsx(Box,{})}function gt({participants:e,onlineIds:t,hostId:o,votes:n,revealed:r,participantId:s}){return jsxs(Box,{flexDirection:"column",children:[jsxs(Text,{bold:true,children:["Participants (",e.length,")"]}),e.map(u=>{let d=t.has(u.id),p=d?"\u25CF":"\u25CB",c=d?"green":"gray",i=u.id===o,a=he(u)==="viewer",g=u.id===s,T=u.id in n,N=a?" (viewer)":r?n[u.id]?` \u2192 ${n[u.id]}`:"":T?" \u2713":" \xB7",E=g&&!a&&!(r&&n[u.id]);return jsxs(Box,{gap:1,children:[jsx(Text,{color:c,children:p}),jsxs(Text,{children:[u.displayName,i?" (host)":""]}),E?jsx(Text,{color:"cyan",children:"(voter)"}):jsx(Text,{color:a?"gray":T?"green":"gray",children:N})]},u.id)})]})}var Ir=[{label:"End session for everyone",value:"end-session",detail:"Disconnects all participants. Cannot be undone."}];function Jn({onPick:e,onClose:t}){return jsx(ce,{title:"Host Actions",items:Ir,onSelect:e,onClose:t})}function ht({roomCode:e,onPick:t,onClose:o}){let n=[{label:"Copy code",value:"code",detail:at(e)},{label:"Copy share link",value:"url",detail:ct(e)},{label:"Copy npx command",value:"npx",detail:dt(e)}];return jsx(ce,{title:"Share",items:n,onSelect:r=>t(r),onClose:o})}function Wn(e,t,o){let n=xn(e.id,o);return n===null?`Round ${t}`:`Round ${n.depth}/${n.total} [re-vote]`}function qn(e,t,o){let n=Wn(e,t,o),r=e.title?` \u2014 ${e.title}`:"",s=e.ticketId?` [${e.ticketId}]`:"",u=W(e)==="skipped"?" \u2014 Skipped":" \u2014 Results";return `${n}${r}${s}${u}`}function Gn(e){let t=Object.values(e).map(i=>parseFloat(i)).filter(i=>!isNaN(i)).toSorted((i,a)=>i-a);if(t.length===0)return {average:"-",median:"-",agreement:"-"};let n=t.reduce((i,a)=>i+a,0)/t.length,r=Math.floor(t.length/2),s=t.length%2===0?(t[r-1]+t[r])/2:t[r],u=Object.values(e),d=new Map;for(let i of u)d.set(i,(d.get(i)??0)+1);let p=Math.max(...d.values()),c=u.length>0?Math.round(p/u.length*100):0;return {average:n.toFixed(1),median:s.toFixed(1),agreement:`${c}%`}}function Vt(e,t){let o=Math.floor((t-e)/1e3),n=Math.floor(o/60),r=o%60;return `${n}:${r.toString().padStart(2,"0")}`}function zn(e,t,o=[]){let n=Wn(e,t,o);if(W(e)==="skipped"){let c=e.title?` ${e.title}`:"",i=e.ticketId?` [${e.ticketId}]`:"";return `${n}:${c}${i} \u2014 Skipped`}let r=new Map;for(let c of Object.values(e.votes))r.set(c,(r.get(c)??0)+1);let s=[...r.entries()].toSorted((c,i)=>i[1]-c[1]||c[0].localeCompare(i[0])).map(([c,i])=>`${c}\xD7${i}`).join(", "),u=e.title?` ${e.title}`:"",d=e.ticketId?` [${e.ticketId}]`:"",p=e.timerStartedAt!==void 0&&e.revealedAt!==void 0?` \u2014 ${Vt(e.timerStartedAt,e.revealedAt)}`:"";return `${n}:${u}${d} \u2014 ${s||"(no votes)"}${p}`}function Qn({projection:e,onlineIds:t,isHost:o,participantId:n,rounds:r,currentRound:s,settingsOpen:u,onStartRound:d,onNewRound:p,onRevote:c,onOpenSettings:i,onHandoffHost:a,onEndSession:g,onShare:T,shareOpen:N,onSetShareOpen:E,roomCode:I,confirmingQuit:H,myRole:l,onToggleRole:m,roundTitlePromptActive:v,roundTitleInput:w,onRoundTitleChange:M,onRoundTitleSubmit:f,onRoundTitleCancel:k,descriptorPromptActive:b,descriptorInput:L,onDescriptorChange:oe,onDescriptorSubmit:Z,onDescriptorCancel:ee,onEditDescriptor:re}){let[h,C]=St.useState("idle"),[A,B]=St.useState(null),[P,D]=St.useState(false),[_,me]=St.useState(false);useEffect(()=>{o||(C("idle"),B(null),D(false),me(false));},[o]),useInput(S=>{if(S==="c"||S==="C"){E(true);return}if(S==="w"||S==="W"){m();return}o&&((S==="e"||S==="E")&&i(),(S==="s"||S==="S")&&d(),(S==="h"||S==="H")&&C("picking"),(S==="m"||S==="M")&&D(true),s!==null&&W(s)!==null&&((S==="n"||S==="N")&&p(),(S==="v"||S==="V")&&W(s)!=="skipped"&&c(),(S==="t"||S==="T")&&re()));},{isActive:!u&&!H&&h==="idle"&&!v&&!P&&!_&&!N&&!b}),useInput((S,K)=>{S==="y"||S==="Y"?(g(),me(false)):(S==="n"||S==="N"||K.escape)&&me(false);},{isActive:_});let se=e.participants.filter(S=>S.id!==n&&t.has(S.id));useInput(S=>{if(h==="picking"){let K=parseInt(S,10);!isNaN(K)&&K>=1&&K<=se.length?(B(se[K-1].id),C("confirming")):(S==="Escape"||S==="q"||S==="Q")&&C("idle");}},{isActive:h==="picking"}),useInput(S=>{h==="confirming"&&A&&(S==="y"||S==="Y"?(a(A),C("idle"),B(null)):(S==="n"||S==="N"||S==="Escape")&&(C("idle"),B(null)));},{isActive:h==="confirming"});let Ye=r.length>0,Pe=r.filter(S=>W(S)!==null),Je=s!==null?Pe.slice(0,-1):Pe;return P?jsx(Jn,{onPick:S=>{D(false),S==="end-session"&&me(true);},onClose:()=>D(false)}):N?jsx(ht,{roomCode:I,onPick:S=>{E(false),T(S);},onClose:()=>E(false)}):jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:Ye?"Session":"Waiting for participants..."}),!Ye&&jsx(Text,{dimColor:true,children:"No rounds started yet."}),jsx(gt,{participants:e.participants,onlineIds:t,hostId:e.settings.hostParticipantId,votes:{},revealed:false,participantId:n}),s!==null&&W(s)!==null&&jsx(Nr,{projection:e,round:s,rounds:r}),Je.length>0&&jsxs(Box,{flexDirection:"column",children:[jsx(Text,{dimColor:true,children:"\u2500\u2500 History \u2500\u2500"}),Je.map(S=>{let K=r.indexOf(S)+1;return jsx(Text,{dimColor:true,children:zn(S,K,r)},S.id)})]}),h==="picking"&&se.length>0&&jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{children:"Hand off to:"}),se.map((S,K)=>jsxs(Text,{children:["[",K+1,"] ",S.displayName]},S.id)),jsx(Text,{dimColor:true,children:"[q] Cancel"})]}),h==="picking"&&se.length===0&&jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{dimColor:true,children:"No other online participants"}),jsx(Text,{dimColor:true,children:"[q] Cancel"})]}),h==="confirming"&&A&&jsx(Box,{flexDirection:"column",children:jsxs(Text,{children:["Hand off to ",e.participants.find(S=>S.id===A)?.displayName,"? [y/n]"]})}),v&&jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{children:"Round title (optional, Enter to skip):"}),jsx(q,{value:w,onChange:M,onSubmit:f,onCancel:k,placeholder:"Enter to skip",isActive:v})]}),jsx(Text,{dimColor:true,children:"[Esc] Cancel"})]}),b&&jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{children:"Edit round title:"}),jsx(q,{value:L,onChange:oe,onSubmit:Z,onCancel:ee,placeholder:"Enter to save",isActive:b})]}),jsx(Text,{dimColor:true,children:"[Esc] Cancel \xB7 empty submit cancels"})]}),_&&jsxs(Box,{children:[jsx(Text,{color:"red",children:"End session for everyone? This cannot be undone. "}),jsx(Text,{dimColor:true,children:"y/n [Esc] Cancel"})]}),h==="idle"&&!v&&!b&&!_&&jsxs(Box,{gap:2,children:[o?jsx(Fragment,{children:s!==null&&W(s)!==null?jsxs(Fragment,{children:[W(s)!=="skipped"&&jsx(Text,{color:"yellow",children:"[v] Revote"}),jsx(Text,{color:"yellow",children:"[n] Next"}),jsx(Text,{color:"yellow",children:"[t] Edit title"}),jsx(Text,{color:"yellow",children:"[h] Handoff"}),jsx(Text,{color:"yellow",children:"[e] Settings"}),jsx(Text,{color:"yellow",children:"[c] Share"}),jsxs(Text,{color:"yellow",children:["[w] ",l==="viewer"?"Vote":"Observe"]}),jsx(Text,{color:"yellow",children:"[m] Menu"})]}):jsxs(Fragment,{children:[jsx(Text,{color:"yellow",children:"[s] Start round"}),jsx(Text,{color:"yellow",children:"[h] Handoff"}),jsx(Text,{color:"yellow",children:"[e] Settings"}),jsx(Text,{color:"yellow",children:"[c] Share"}),jsxs(Text,{color:"yellow",children:["[w] ",l==="viewer"?"Vote":"Observe"]}),jsx(Text,{color:"yellow",children:"[m] Menu"})]})}):jsxs(Fragment,{children:[jsx(Text,{dimColor:true,children:"Waiting for host to start a round..."}),jsx(Text,{children:"[c] Share"}),jsxs(Text,{color:"yellow",children:["[w] ",l==="viewer"?"Vote":"Observe"]})]}),jsx(Text,{children:"[q] Quit"})]})]})}function Nr({projection:e,round:t,rounds:o}){let n=o.indexOf(t)+1,r=qn(t,n,o);if(W(t)==="skipped")return jsx(Box,{flexDirection:"column",gap:1,children:jsx(Text,{bold:true,children:r})});let s=Gn(t.votes),u=new Map(e.participants.map(c=>[c.id,c.displayName])),d=t.timerStartedAt!==void 0&&t.revealedAt!==void 0?Vt(t.timerStartedAt,t.revealedAt):null,p=Object.values(t.votes)[0];return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:r}),Sn(t)&&jsxs(Text,{bold:true,color:"green",children:["Consensus! Everyone voted ",p]}),jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:2,children:[jsx(Text,{bold:true,underline:true,children:"Name".padEnd(20)}),jsx(Text,{bold:true,underline:true,children:"Estimate".padEnd(10)})]}),Object.entries(t.votes).map(([c,i])=>jsxs(Box,{gap:2,children:[jsx(Text,{children:(u.get(c)??c).padEnd(20)}),jsx(Text,{color:"cyan",bold:true,children:i.padEnd(10)})]},c)),e.participants.filter(c=>!(c.id in t.votes)).map(c=>jsxs(Box,{gap:2,children:[jsx(Text,{dimColor:true,children:c.displayName.padEnd(20)}),jsx(Text,{dimColor:true,children:"(no vote)".padEnd(10)})]},c.id))]}),jsxs(Box,{gap:2,children:[jsxs(Text,{children:["Avg: ",jsx(Text,{bold:true,children:s.average})]}),jsxs(Text,{children:["Med: ",jsx(Text,{bold:true,children:s.median})]}),jsxs(Text,{children:["Agreement: ",jsx(Text,{bold:true,children:s.agreement})]}),d!==null&&jsxs(Text,{dimColor:true,children:["\u23F1 ",d]})]})]})}function Zn({scale:e,selected:t,onSelect:o,onClear:n,disabled:r}){let[s,u]=useState(0);return useInput((d,p)=>{if(!r)if(p.leftArrow)u(c=>Math.max(0,c-1));else if(p.rightArrow)u(c=>Math.min(e.cards.length-1,c+1));else if(p.return){let c=e.cards[s];c===t?n():o(c);}else (p.backspace||p.delete)&&n();}),jsxs(Box,{flexDirection:"column",gap:0,children:[jsx(Box,{gap:1,children:e.cards.map((d,p)=>{let c=p===s,i=d===t;return jsx(Text,{inverse:c,bold:i,...i&&{color:"cyan"},children:i?`[${d}]`:` ${d} `},d)})}),jsx(Text,{dimColor:true,children:" \u2190\u2192 navigate \u23CE select \u232B clear"})]})}function kr(e){let t=Math.floor(e/1e3),o=Math.floor(t/60),n=t%60;return `${o}:${n.toString().padStart(2,"0")}`}function eo({projection:e,round:t,onlineIds:o,isHost:n,myId:r,myRole:s,elapsedMs:u,roomCode:d,shareOpen:p,settingsOpen:c,onVote:i,onClearVote:a,onReveal:g,onSkip:T,onToggleRole:N,onShare:E,onSetShareOpen:I,onOpenSettings:H,confirmingQuit:l}){if(useInput(f=>{if(f==="e"||f==="E"){H();return}if(f==="c"||f==="C"){I(true);return}n&&(f==="r"||f==="R")&&g(),n&&(f==="s"||f==="S")&&T(),(f==="w"||f==="W")&&N();},{isActive:!l&&!c&&!p}),p)return jsx(ht,{roomCode:d,onPick:f=>{I(false),E(f);},onClose:()=>I(false)});let m=t.votes[r],v=e.rounds.indexOf(t)+1,w=t.title?` \u2014 ${t.title}`:"",M=t.ticketId?` [${t.ticketId}]`:"";return jsxs(Box,{flexDirection:"column",gap:1,children:[jsxs(Box,{gap:2,children:[jsxs(Text,{bold:true,children:["Round ",v,w,M]}),u!==void 0&&jsxs(Text,{dimColor:true,children:["\u23F1 ",kr(u)]})]}),jsxs(Box,{gap:4,children:[jsx(gt,{participants:e.participants,onlineIds:o,hostId:e.settings.hostParticipantId,votes:t.votes,revealed:false}),s==="viewer"?jsx(Box,{flexDirection:"column",children:jsx(Text,{dimColor:true,children:"You're observing"})}):jsx(Zn,{scale:t.scale,selected:m,onSelect:i,onClear:a,disabled:l})]}),jsxs(Box,{gap:2,children:[n&&jsx(Text,{color:"yellow",children:"[r] Reveal"}),n&&jsx(Text,{color:"yellow",children:"[s] Skip"}),jsxs(Text,{color:"yellow",children:["[w] ",s==="viewer"?"Vote":"Observe"]}),jsx(Text,{children:"[e] Settings"}),jsx(Text,{children:"[c] Share"}),jsx(Text,{children:"[q] Quit"})]})]})}var Ur=3e3;function Wt(e=Ur){let[t,o]=useState(null),n=useRef(null),r=useCallback(()=>{n.current!==null&&(clearTimeout(n.current),n.current=null);},[]),s=useCallback(d=>{r(),o(d),n.current=setTimeout(()=>{o(null),n.current=null;},e);},[r,e]),u=useCallback(()=>{r(),o(null);},[r]);return useEffect(()=>()=>{r();},[r]),{notice:t,show:s,dismiss:u}}function Fr(e){let t=X(e);return !t||W(t)!==null?"lobby":"voting"}function vt({session:e,participantId:t,displayName:o,accessMode:n,presetOverrides:r,customScales:s}){let{exit:u}=useApp(),[d,p]=useState(e.projection()),[c,i]=useState("connecting"),[a,g]=useState(e.onlineIds()),[T,N]=useState(),[E,I]=useState(false),[H,l]=useState(n==="password"?"password":"open"),[m,v]=useState(o),[w,M]=useState(false),[f,k]=useState(false),[b,L]=useState(""),[oe,Z]=useState(false),[ee,re]=useState(""),[h,C]=useState(null),A=Wt(3e3),[B,P]=useState(false),D=Wt(3e3);useEffect(()=>{let R=e.onChange(()=>{p(e.projection()),g(e.onlineIds());}),V=e.onStatusChange(i),We=e.onHostChange(qe=>{let Io=e.projection().participants.find(Eo=>Eo.id===qe),To=qe===t?"You are now the host":`${Io?.displayName||"Unknown"} is now the host`;A.show(To);}),Ro=setInterval(()=>{g(e.onlineIds());let qe=e.projection(),Ge=X(qe);Ge?.timerStartedAt!==void 0&&Ge.revealedAt===void 0?N(Date.now()-Ge.timerStartedAt):N(void 0);},1e3);return ()=>{R(),V(),We(),clearInterval(Ro);}},[e,t,A.show]),useInput(R=>{(R==="q"||R==="Q")&&M(true);},{isActive:!E&&!w&&!f&&!oe&&!B}),useInput((R,V)=>{R==="y"||R==="Y"?(e.disconnect(),u()):(R==="n"||R==="N"||V.escape)&&M(false);},{isActive:w});let _=Fr(d),me=X(d),se=d.settings.hostParticipantId===t,Ye=d.participants.find(R=>R.id===t),Pe=he(Ye??{}),Je=useCallback(()=>{L(""),k(true);},[]),S=useCallback(R=>{k(false),L(""),e.newRound(R.trim()||void 0);},[e]),K=useCallback(()=>{k(false),L("");},[]),io=useCallback(()=>{let R=e.current();!R||R.revealedAt===void 0||(C(R.id),re(R.title??""),Z(true));},[e]),ao=useCallback(R=>{let V=h;if(Z(false),C(null),!V)return;let We=R.trim();We!==""&&e.updateRoundDescriptor(V,{title:We});},[e,h]),co=useCallback(()=>{Z(false),C(null);},[]),lo=useCallback(R=>{e.vote(R);},[e]),uo=useCallback(()=>{e.unvote();},[e]),po=useCallback(()=>{e.reveal();},[e]),fo=useCallback(()=>{e.skip();},[e]),mo=useCallback(()=>{L(""),k(true);},[]),go=useCallback(()=>{e.revote();},[e]),Qt=useCallback(()=>{e.toggleRole();},[e]),ho=useCallback(R=>{e.updateName(R);},[e]),So=useCallback(R=>{e.updateScale(R);},[e]),xo=useCallback(R=>{e.updateDisplayName(R),v(R);},[e]),wo=useCallback(async(R,V)=>{await e.updateAccess(R,V),l(R);},[e]),yo=useCallback(R=>{try{e.handoffHost(R);}catch(V){console.error(`Handoff failed: ${V instanceof Error?V.message:String(V)}`);}},[e]),Kt=useCallback(()=>{I(true);},[]),vo=useCallback(()=>{I(false);},[]),Co=useCallback(()=>{e.endSessionForEveryone();},[e]),Xt=useCallback(async R=>{let V=R==="code"?at(e.roomCode):R==="url"?ct(e.roomCode):dt(e.roomCode);try{await _r.write(V),D.show("Copied!");}catch{D.show(`Copy failed \u2014 ${V}`);}},[e.roomCode,D]),Zt=d.rounds.filter(R=>W(R)!==null),bo=Zt.length>0?Zt.at(-1):null;return jsxs(Box,{flexDirection:"column",children:[jsx($n,{roomCode:e.roomCode,sessionName:d.settings.name,status:c,isHost:se}),A.notice&&jsx(Box,{paddingX:1,children:jsx(Text,{color:"yellow",children:A.notice})}),D.notice&&jsx(Box,{paddingX:1,children:jsx(Text,{color:D.notice.startsWith("Copy failed")?"red":"green",children:D.notice})}),E?jsx(Vn,{sessionName:d.settings.name,displayName:m,currentScale:d.settings.defaultScale,accessMode:H,presetOverrides:r,customScales:s,isHost:se,onChangeName:ho,onChangeScale:So,onChangeDisplayName:xo,onChangeAccess:wo,onClose:vo}):_==="lobby"?jsx(Qn,{projection:d,onlineIds:a,isHost:se,participantId:t,rounds:d.rounds,currentRound:bo,settingsOpen:E,onStartRound:Je,onNewRound:mo,roundTitlePromptActive:f,roundTitleInput:b,onRoundTitleChange:L,onRoundTitleSubmit:S,onRoundTitleCancel:K,onRevote:go,onOpenSettings:Kt,onHandoffHost:yo,onEndSession:Co,onShare:Xt,shareOpen:B,onSetShareOpen:P,roomCode:e.roomCode,confirmingQuit:w,myRole:Pe,onToggleRole:Qt,descriptorPromptActive:oe,descriptorInput:ee,onDescriptorChange:re,onDescriptorSubmit:ao,onDescriptorCancel:co,onEditDescriptor:io}):_==="voting"&&me?jsx(eo,{projection:d,round:me,onlineIds:a,isHost:se,myId:t,myRole:Pe,elapsedMs:T,onVote:lo,onClearVote:uo,onReveal:po,onSkip:fo,onToggleRole:Qt,onShare:Xt,onSetShareOpen:P,shareOpen:B,settingsOpen:E,onOpenSettings:Kt,roomCode:e.roomCode,confirmingQuit:w}):null,w&&jsx(Box,{children:jsx(Text,{dimColor:true,children:"Leave session? y/n"})})]})}function no({presetOverrides:e,customScales:t}){let{exit:o}=useApp(),[n,r]=useState("main-menu"),[s,u]=useState(ge()),[d,p]=useState(s.displayName??""),[c,i]=useState(s.relayUrl??""),[a,g]=useState(s.defaultSessionName??""),[T,N]=useState(t??[]),[E,I]=useState(e??{});useInput(h=>{(h==="q"||h==="Q")&&o();},{isActive:n==="main-menu"});let H=useMemo(()=>nt(E,T),[E,T]),l=s.defaultScale,m=l?H.find(h=>h.id===l):void 0,v=[{label:"Display name",value:"display-name",detail:`[${s.displayName??"(none)"}]`},{label:"Relay URL",value:"relay-url",detail:`[${s.relayUrl??"(none)"}]`},{label:"Default scale",value:"default-scale",detail:`[${m?xe(m.id,T):"(none)"}]`},{label:"Default session name",value:"default-session-name",detail:`[${s.defaultSessionName??"(none)"}]`},{label:"Default password",value:"default-password",detail:`[${s.defaultPassword?"on":"off"}]`}];function w(h){h==="display-name"?(p(s.displayName??""),r("editing-display-name")):h==="relay-url"?(i(s.relayUrl??""),r("editing-relay-url")):h==="default-scale"?r("picking-scale"):h==="default-session-name"?(g(s.defaultSessionName??""),r("editing-session-name")):h==="default-password"&&r("picking-default-password");}function M(h){let C=h.trim();if(C.length>0){let A={...s,displayName:C};u(A),Y({displayName:C});}r("main-menu");}function f(h){let C=h.trim();if(C.length>0){let A={...s,relayUrl:C};u(A),Y({relayUrl:C});}r("main-menu");}function k(h){let C=h.trim(),A=C.length>0?{...s,defaultSessionName:C}:{...s};C.length===0?(delete A.defaultSessionName,u(A),Me({defaultSessionName:""})):(u(A),Y({defaultSessionName:C})),r("main-menu");}function b(h){let C=h.length>0?{...s,defaultScale:h}:{...s};h.length===0?(delete C.defaultScale,u(C),Me({defaultScale:""})):(u(C),Y({defaultScale:h})),r("main-menu");}function L(h,C){let A={...E};C.length===0?delete A[h]:A[h]=C,I(A),Y({presetOverrides:Object.keys(A).length>0?A:void 0});}function oe(h){let C=h==="on",A={...s,defaultPassword:C};u(A),Y({defaultPassword:C}),r("main-menu");}function Z(h,C){let A=`custom-${randomUUID()}`,B=[...T,{id:A,label:h,cards:C}];N(B),u({...s,customScales:B}),Y({customScales:B});}function ee(h,C,A){let B=T.map(P=>P.id===h?{id:h,label:C,cards:A}:P);N(B),u({...s,customScales:B}),Y({customScales:B});}function re(h){let C=T.filter(B=>B.id!==h);N(C);let A={...s,customScales:C};s.defaultScale===h?(A.defaultScale="fibonacci",Y({customScales:C,defaultScale:"fibonacci"})):Y({customScales:C}),u(A);}if(n==="main-menu")return jsx(ce,{title:"\u2500\u2500 Config Settings \u2500\u2500",items:v,onSelect:w,onClose:()=>o()});if(n==="editing-display-name")return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(Text,{children:"Display name: "}),jsx(q,{value:d,onChange:p,onSubmit:M,onCancel:()=>r("main-menu"),placeholder:"(required)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]});if(n==="editing-relay-url")return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(Text,{children:"Relay URL: "}),jsx(q,{value:c,onChange:i,onSubmit:f,onCancel:()=>r("main-menu"),placeholder:"(required)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]});if(n==="editing-session-name")return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(Text,{children:"Default session name: "}),jsx(q,{value:a,onChange:g,onSubmit:k,onCancel:()=>r("main-menu"),placeholder:"(leave empty to clear)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]});if(n==="picking-scale"){let h=[{id:"",cards:[]},...H];return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(pt,{scales:h,selectedScaleId:l??"",presetOverrides:E,customScales:T,onSelect:b,onOverrideChange:L,onCustomScaleCreate:Z,onCustomScaleEdit:ee,onCustomScaleDelete:re,onClose:()=>r("main-menu"),showNoneOption:true})]})}if(n==="picking-default-password"){let h=[{label:"On",value:"on",detail:s.defaultPassword?"[current]":void 0},{label:"Off",value:"off",detail:s.defaultPassword?void 0:"[current]"}];return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(ce,{title:"Default password:",items:h,onSelect:oe,onClose:()=>r("main-menu")})]})}return jsx(Box,{})}async function zt(){return new Promise(e=>{let t=createInterface({input:process.stdin,output:process.stderr});process.stderr.write("Password: "),t.question("",o=>{t.close(),e(o);});})}async function Kr(e){let t=await De(),o=e.scale??t.defaultScale??"fibonacci",n=e.name??t.defaultSessionName,r=e.password??t.defaultPassword??false,s;r&&(s=await zt());let u=tt(o,t.presetOverrides,t.customScales);if(!u){let a=Object.keys(te).join(", ");console.error(`Unknown or fully-disabled scale: "${o}". Builtin IDs: ${a}`),process.exit(1);}let d=await An({participantId:t.participantId,displayName:t.displayName,relayUrl:t.relayUrl,sessionName:n,password:s,defaultScale:u}),p;s&&(p=await Se(s,d.roomUuid)),rt({roomCode:d.roomCode,roomUuid:d.roomUuid,passwordHash:p,lastSeenAt:Date.now()}),console.error(`Room created: ${d.roomCode}`),console.error(`Share: hnch join ${d.roomCode}`),console.error("");let{waitUntilExit:c,unmount:i}=render(St.createElement(vt,{session:d,participantId:t.participantId,displayName:t.displayName,accessMode:s?"password":"open",presetOverrides:t.presetOverrides,customScales:t.customScales}));it({doc:d.doc,localParticipantId:t.participantId,onEnded:async a=>{console.error(a.isLocal?"Session ended.":`Session ended by ${a.hostName}.`),i();try{await F();}catch(g){console.error(`Failed to clear cached session: ${g instanceof Error?g.message:String(g)}`);}d.disconnect(),process.exit(0);}}),await c(),d.disconnect(),F(),process.exit(0);}async function Xr(e){let t=await De(),o=Pn(e.code);o===null&&(console.error("invalid room code"),process.exit(1));let n;e.password&&(n=await zt());let r=await Be(t.relayUrl,o),s;r.accessMode==="password"&&!e.password?s=await zt():e.password&&(s=n);let u,d=false,p=await Nn({participantId:t.participantId,displayName:t.displayName,relayUrl:t.relayUrl,roomCode:o,password:s,onRejection(a){if(a==="unknown-room"){st({clearLastSession:F,logger:console,exit:process.exit});return}a==="room-full"&&(d=true,u?.());}}),c;s&&(c=await Se(s,p.roomUuid)),rt({roomCode:p.roomCode,roomUuid:p.roomUuid,passwordHash:c,lastSeenAt:Date.now()}),console.error(`Joined room: ${p.roomCode}`),console.error("");let i=render(St.createElement(vt,{session:p,participantId:t.participantId,displayName:t.displayName,accessMode:r.accessMode,presetOverrides:t.presetOverrides,customScales:t.customScales}));u=i.unmount,it({doc:p.doc,localParticipantId:t.participantId,onEnded:async a=>{console.error(a.isLocal?"Session ended.":`Session ended by ${a.hostName}.`),u?.();try{await F();}catch(g){console.error(`Failed to clear cached session: ${g instanceof Error?g.message:String(g)}`);}p.disconnect(),process.exit(0);}}),await i.waitUntilExit(),d&&(console.error("This room is full (max 20 participants)."),p.disconnect(),F(),process.exit(1)),p.disconnect(),F(),process.exit(0);}async function Zr(){let e=await De(),t=Dn();t||(console.error("No recent session to resume."),process.exit(0));let o;try{o=await Be(e.relayUrl,t.roomCode);}catch(i){if((i instanceof Error?i.message:String(i)).includes("Room not found"))return await st({clearLastSession:F,logger:console,exit:process.exit});console.error("Could not reach relay \u2014 try again later."),process.exit(0);}o.accessMode==="password"&&!t.passwordHash&&(console.error(`Password changed \u2014 rejoin with: hnch join ${t.roomCode} --password`),F(),process.exit(0)),console.error(`Rejoining room ${t.roomCode}\u2026`),console.error("");let n=new Ie.Doc,r=new Awareness(n);et(n,{participantId:e.participantId,displayName:e.displayName}),r.setLocalStateField("participantId",e.participantId);let s=false,u=false,d,p=ot({doc:n,awareness:r,roomCode:t.roomCode,roomUuid:t.roomUuid,participantId:e.participantId,relayUrl:e.relayUrl,passwordHash:t.passwordHash,onRejection(i){if(i==="unknown-room"){st({clearLastSession:F,logger:console,exit:process.exit});return}i==="wrong-password"&&(s=true,d?.()),i==="room-full"&&(u=true,d?.());}});rt({roomCode:p.roomCode,roomUuid:p.roomUuid,passwordHash:t.passwordHash,lastSeenAt:Date.now()});let c=render(St.createElement(vt,{session:p,participantId:e.participantId,displayName:e.displayName,accessMode:o.accessMode,presetOverrides:e.presetOverrides,customScales:e.customScales}));d=c.unmount,it({doc:p.doc,localParticipantId:e.participantId,onEnded:async i=>{console.error(i.isLocal?"Session ended.":`Session ended by ${i.hostName}.`),d?.();try{await F();}catch(a){console.error(`Failed to clear cached session: ${a instanceof Error?a.message:String(a)}`);}p.disconnect(),process.exit(0);}}),await c.waitUntilExit(),u&&(console.error("This room is full (max 20 participants)."),F(),process.exit(1)),s&&(console.error(`Password changed \u2014 rejoin with: hnch join ${t.roomCode} --password`),F(),process.exit(0)),p.disconnect(),F(),process.exit(0);}async function es(e){if(!(e.name!==void 0||e.relayUrl!==void 0||e.scale!==void 0||e.sessionName!==void 0||e.password!==void 0)){let n=await De(),{waitUntilExit:r}=render(St.createElement(no,{presetOverrides:n.presetOverrides,customScales:n.customScales}));await r(),process.exit(0);}let o={};if(e.name!==void 0&&(o.displayName=e.name),e.relayUrl!==void 0&&(o.relayUrl=e.relayUrl),e.scale!==void 0){if(e.scale!==""){let n=ge();if(!tt(e.scale,n.presetOverrides,n.customScales)){console.error(`Unknown scale: "${e.scale}". Builtin IDs: ${Object.keys(te).join(", ")}`);return}}o.defaultScale=e.scale;}e.sessionName!==void 0&&(o.defaultSessionName=e.sessionName),e.password!==void 0&&(o.defaultPassword=e.password),Me(o),console.log("Config updated.");}var so=Gr(hideBin(process.argv)).scriptName("hnch").usage("$0 <command> \u2014 terminal pointing poker").command("create","Create a new room",e=>e.option("name",{type:"string",describe:"Session name"}).option("password",{type:"boolean",describe:"Protect room with a password"}).option("scale",{type:"string",describe:"Estimation scale ID (fibonacci, t-shirt, powers-of-2, linear, risk, or custom-<id>)"}),e=>{Kr(e).catch(t=>{console.error(t instanceof Error?t.message:t),process.exit(1);});}).command("join <code>","Join an existing room",e=>e.positional("code",{type:"string",demandOption:true,describe:"6-character room code"}).option("password",{type:"boolean",describe:"Room requires a password",default:false}),e=>{Xr(e).catch(t=>{console.error(t instanceof Error?t.message:t),process.exit(1);});}).command("resume","Rejoin your last session",e=>e,()=>{Zr().catch(e=>{console.error(e instanceof Error?e.message:e),process.exit(1);});}).command("config","View or update CLI config",e=>e.option("name",{type:"string",describe:"Set display name"}).option("relay-url",{type:"string",describe:"Set relay URL"}).option("scale",{type:"string",describe:"Set default estimation scale ID (empty string clears)"}).option("session-name",{type:"string",describe:"Set default session name (empty string clears)"}).option("password",{type:"boolean",describe:"Set default password prompt on (--password) or off (--no-password)"}),e=>{es(e).catch(t=>{console.error(t instanceof Error?t.message:t),process.exit(1);});}).demandCommand(1,"Specify a command: create, join, resume, or config").strict().help(),Gt=hideBin(process.argv);(Gt.length===0||Gt.length===1&&Gt[0]==="--")&&(so.showHelp(),process.exit(0));so.parse();
2
+ import bs from'yargs';import {hideBin}from'yargs/helpers';import It,{useState,useEffect,useCallback,useMemo,useRef}from'react';import {render,useApp,useInput,Box,Text}from'ink';import*as Oe from'yjs';import {Awareness}from'y-protocols/awareness';import {mkdirSync,chmodSync,writeFileSync,unlinkSync,existsSync,readFileSync}from'fs';import {homedir}from'os';import {join}from'path';import {randomUUID}from'crypto';import {createInterface}from'readline';import {WebsocketProvider}from'y-websocket';import Ar from'ws';import Co from'clipboardy';import {jsxs,jsx,Fragment}from'react/jsx-runtime';var or="https://hnch.dev";function je(){let t=process.env.XDG_CONFIG_HOME||join(homedir(),".config");return join(t,"hnch")}function $t(){return join(je(),"config.json")}function fn(){let e=je();existsSync(e)||mkdirSync(e,{recursive:true});}function Ce(){let e=$t();if(!existsSync(e))return {};try{return JSON.parse(readFileSync(e,"utf-8"))}catch{return {}}}function J(e){fn();let o={...Ce(),...e};writeFileSync($t(),JSON.stringify(o,null,2)+`
3
+ `,"utf-8");}async function rr(e){let t=createInterface({input:process.stdin,output:process.stderr});return new Promise(o=>{t.question(e,n=>{t.close(),o(n.trim());});})}function sr(e){return e.startsWith("wss://")?"https://"+e.slice(6):e.startsWith("ws://")?"http://"+e.slice(5):e}async function Le(){let e=Ce(),t=false;if(e.participantId||(e.participantId=randomUUID(),t=true),!e.displayName){let n=await rr("Enter your display name: ");e.displayName=n||`user-${e.participantId.slice(0,6)}`,t=true;}e.relayUrl||(e.relayUrl=or,t=true),t&&J(e);let o=process.env.HNCH_RELAY_URL||e.relayUrl;return o=sr(o),{participantId:e.participantId,displayName:e.displayName,relayUrl:o,defaultScale:e.defaultScale,defaultSessionName:e.defaultSessionName,defaultPassword:e.defaultPassword,presetOverrides:e.presetOverrides,customScales:e.customScales}}function _e(e){fn();let o={...Ce()};for(let[n,r]of Object.entries(e))r===""?delete o[n]:r!==void 0&&(o[n]=r);writeFileSync($t(),JSON.stringify(o,null,2)+`
4
+ `,"utf-8");}var ir={"unknown-room":4001,"wrong-password":4002,"invalid-invite":4003,"expired-invite":4004,"invite-already-redeemed":4005,"room-full":4006,"malformed-credentials":4010},mn=new Map(Object.entries(ir).map(([e,t])=>[t,e]));function be(e){return e.role??"voter"}function W(e){let{revealedAt:t,skippedAt:o}=e;return t===void 0&&o===void 0?null:t===void 0?"skipped":o===void 0||t<=o?"revealed":"skipped"}var ot={id:"fibonacci",cards:["0","1","2","3","5","8","13","21"]},gn={id:"t-shirt",cards:["XS","S","M","L","XL"]},hn={id:"powers-of-2",cards:["1","2","4","8","16","32","64"]},Sn={id:"linear",cards:["1","2","3","4","5","6","7","8","9","10"]},xn={id:"risk",cards:["Low","Medium","High","Critical"]},se={[ot.id]:ot,[gn.id]:gn,[hn.id]:hn,[Sn.id]:Sn,[xn.id]:xn},wn={fibonacci:"Fibonacci","t-shirt":"T-shirt","powers-of-2":"Powers of 2",linear:"Linear (1\u201310)",risk:"Risk"};function yn(e){return e in wn?wn[e]:e.length===0?e:(e.startsWith("custom-")?e.slice(7):e).split("-").map(o=>o.length===0?o:o.charAt(0).toUpperCase()+o.slice(1)).join(" ")}var ar="meta",Fe="settings",cr="participants",dr="rounds",lr=["roomUuid","createdAt","createdBy","sessionEndedAt"],ur=["name","defaultScale","hostParticipantId"],pr=["displayName","joinedAt","role","leftAt","removedBy"],fr=["id","title","description","ticketId","parentRoundId","scale","timerStartedAt","revealedAt","skippedAt","createdBy","createdAt"];function rt(e){return e.getMap(ar)}function st(e){return e.getMap(Fe)}function it(e){return e.getMap(cr)}function Bt(e){return e.getArray(dr)}function at(e,t){let o={};for(let n of t){let r=e.get(n);r!==void 0&&(o[n]=r);}return o}function mr(e){return at(rt(e),lr)}function gr(e){return at(st(e),ur)}function hr(e){let t=it(e),o=[];return t.forEach((n,r)=>{let s=at(n,pr);o.push({id:r,...s});}),o.sort((n,r)=>n.joinedAt-r.joinedAt||n.id.localeCompare(r.id)),o}function Sr(e){let t={};return e.forEach((o,n)=>{t[n]=o;}),t}function xr(e){let t=at(e,fr),o=e.get("votes"),n=o?Sr(o):{};return {...t,votes:n}}function wr(e){let t=Bt(e),o=[];return t.forEach(n=>{o.push(xr(n));}),o}function Q(e){return {meta:mr(e),settings:gr(e),participants:hr(e),rounds:wr(e)}}function vn(e,t){if(rt(e).has("roomUuid"))throw new Error("initDoc: doc already seeded");e.transact(()=>{let o=rt(e);o.set("roomUuid",t.roomUuid),o.set("createdAt",t.createdAt),o.set("createdBy",t.participantId);let n=st(e);t.sessionName!==void 0&&n.set("name",t.sessionName),n.set("defaultScale",t.defaultScale),n.set("hostParticipantId",t.participantId);let r=new Oe.Map;r.set("displayName",t.displayName),r.set("joinedAt",t.createdAt),it(e).set(t.participantId,r);});}function ct(e,t){e.transact(()=>{let o=it(e),n=o.get(t.participantId),r=n??new Oe.Map;r.set("displayName",t.displayName),n===void 0&&r.set("joinedAt",Date.now()),r.delete("leftAt"),r.delete("removedBy"),n===void 0&&o.set(t.participantId,r);});}function Me(e,t){e.transact(()=>{let o=st(e);for(let[n,r]of Object.entries(t))r===void 0?o.delete(n):o.set(n,r);});}function Ht(e,t,o){e.transact(()=>{let n=it(e),r=n.get(t),s=r??new Oe.Map;s.set("displayName",o.displayName),s.set("joinedAt",o.joinedAt),o.role!==void 0&&s.set("role",o.role),r===void 0&&n.set(t,s);});}function Ut(e,t){let o=t.scale??st(e).get("defaultScale");if(!o)throw new Error("appendRound: no scale (settings.defaultScale missing)");return e.transact(()=>{let n=new Oe.Map;n.set("id",t.id),n.set("createdAt",t.createdAt),n.set("createdBy",t.createdBy),n.set("scale",o),t.title!==void 0&&n.set("title",t.title),t.description!==void 0&&n.set("description",t.description),t.ticketId!==void 0&&n.set("ticketId",t.ticketId),t.parentRoundId!==void 0&&n.set("parentRoundId",t.parentRoundId),n.set("timerStartedAt",t.createdAt),n.set("votes",new Oe.Map),Bt(e).push([n]);}),t.id}function Cn(e,t,o){let n=Ve(e,t);if(!n)throw new Error(`patchRoundDescriptor: round ${t} not found`);e.transact(()=>{o.title!==void 0&&n.set("title",o.title),o.description!==void 0&&n.set("description",o.description);});}function Ve(e,t){let o=Bt(e);for(let n=0;n<o.length;n+=1){let r=o.get(n);if(r.get("id")===t)return r}}function bn(e,t,o,n){let r=Ve(e,t);if(!r)throw new Error(`castVote: round ${t} not found`);r.get("revealedAt")===void 0&&e.transact(()=>{r.get("votes").set(o,n);});}function Rn(e,t,o){let n=Ve(e,t);if(!n)throw new Error(`clearVote: round ${t} not found`);n.get("revealedAt")===void 0&&e.transact(()=>{n.get("votes").delete(o);});}function jt(e,t,o){let n=Ve(e,t);if(!n)throw new Error(`revealRound: round ${t} not found`);if(n.get("skippedAt")!==void 0)throw new Error(`revealRound: round ${t} already skipped`);e.transact(()=>{n.set("revealedAt",o);});}function In(e,t,o){let n=Ve(e,t);if(!n)throw new Error(`skipRound: round ${t} not found`);if(n.get("revealedAt")!==void 0)throw new Error(`skipRound: round ${t} already revealed`);if(n.get("skippedAt")!==void 0)throw new Error(`skipRound: round ${t} already skipped`);e.transact(()=>{n.set("skippedAt",o);});}function Tn(e){e.transact(()=>{rt(e).set("sessionEndedAt",Date.now());});}function ee(e){let{rounds:t}=e;if(t.length===0)return null;for(let o=t.length-1;o>=0;o-=1){let n=t[o];if(n.revealedAt===void 0)return n}return t.at(-1)}function En(e){let t=Object.values(e.votes);return t.length<2?false:t.every(o=>o===t[0])}function An(e){let t=Math.max(0,Math.floor(e/1e3)),o=Math.floor(t/60),n=t%60;return `${o}:${n.toString().padStart(2,"0")}`}function Nn(e){try{let t=new URL(e);return t.protocol==="http:"||t.protocol==="https:"}catch{return false}}function Pn(e,t){let o=new Map;for(let d of t)o.set(d.id,d);let n=o.get(e);if(n===void 0||n.parentRoundId===void 0)return null;let r=new Set([n.id]),s=n,c=1;for(;s.parentRoundId!==void 0;){let d=o.get(s.parentRoundId);if(d===void 0||r.has(d.id))return null;r.add(d.id),s=d,c+=1;}let i=s,u=new Map;for(let d of t){if(d.parentRoundId===void 0)continue;let f=u.get(d.parentRoundId);f===void 0?u.set(d.parentRoundId,[d]):f.push(d);}let l=0,a=[i];for(;a.length>0;){let d=a.shift();l+=1;let f=u.get(d.id);f!==void 0&&a.push(...f);}return {chainRoot:i,depth:c,total:l}}var Dn="PBKDF2",yr="SHA-256",kn=new TextEncoder;function vr(e){return Array.from(new Uint8Array(e)).map(t=>t.toString(16).padStart(2,"0")).join("")}async function Re(e,t){let o=await crypto.subtle.importKey("raw",kn.encode(e),Dn,false,["deriveBits"]),n=await crypto.subtle.deriveBits({name:Dn,salt:kn.encode(t),iterations:1e5,hash:yr},o,256);return vr(n)}var On="ABCDEFGHJKMNPQRSTUVWXYZ23456789";if(On.length!==31)throw new Error(`Room code alphabet must be 31 characters, got ${On.length}`);function Lt(e){return e.trim().replaceAll(/-/g,"").toUpperCase()}function Mn(e,t=true){let o=Lt(e);return !t||o.length!==6?o:`${o.slice(0,3)}-${o.slice(3)}`}function Cr(e,t){let o=new Map;t.forEach((s,c)=>o.set(s,c));let n=[...new Set(e.filter(s=>!o.has(s)))].toSorted(),r=new Map;return n.forEach((s,c)=>r.set(s,t.length+c)),s=>o.get(s)??r.get(s)??Number.MAX_SAFE_INTEGER}function _t(e,t){let o=new Map;for(let c of e)o.set(c,(o.get(c)??0)+1);let n=null,r=0,s=[...o.keys()].toSorted((c,i)=>t(c)-t(i));for(let c of s){let i=o.get(c);i>r&&(r=i,n=c);}return n}function $n(e,t){let o=Object.values(e);if(o.length===0)return null;let n=Cr(o,t.cards);if(t.cards.every(c=>Number.isFinite(parseFloat(c)))){let c=o.map(parseFloat).filter(Number.isFinite);if(c.length===0)return {kind:"categorical",mode:_t(o,n)};let i=c.reduce((C,A)=>C+A,0)/c.length,u=[...c].toSorted((C,A)=>C-A),l=Math.floor(u.length/2),a=u.length%2===1?u[l]:(u[l-1]+u[l])/2,d=_t(o,n),f;if(new Set(o).size===1)f="unanimous";else {let C=o.map(R=>n(R)),A=Math.min(...C);f=Math.max(...C)-A<=1?"close":"spread";}return {kind:"numeric",mean:i,median:a,mode:d,spread:f}}return {kind:"categorical",mode:_t(o,n)}}function Bn(e){return e.replaceAll(/\|/g,"\\|")}function br(e){return e.replaceAll(/\\/g,"\\\\").replaceAll(/]/g,"\\]")}function Rr(e){let o=(e.match(/`+/g)??[]).reduce((n,r)=>Math.max(n,r.length),0);return "`".repeat(o+1)}function Ft(e){if(!e)return "";if(Nn(e))return ` ([${br(e)}](<${e}>))`;let t=Rr(e);return ` (${t}${e}${t})`}function Ir(e){return e instanceof Map?e:new Map(Object.entries(e))}function Tr(e){let t=new Map;for(let r of e)t.set(r.id,r);function o(r){if(!r.parentRoundId)return r;let s=t.get(r.parentRoundId);return s?o(s):r}let n=new Map;for(let r of e){let s=o(r),c=n.get(s.id);c?c.push(r):n.set(s.id,[r]);}return n}function Hn(e,t){let o=new Map;e.scale.cards.forEach((c,i)=>o.set(c,i));let n=new Map;for(let[c,i]of Object.entries(e.votes)){let u=t.get(c)??c,l=n.get(i);l?l.push(u):n.set(i,[u]);}let r=[...n.entries()].map(([c,i])=>({card:c,scaleIndex:o.get(c)??Number.MAX_SAFE_INTEGER,voters:[...i].toSorted().map(Bn)}));return r.sort((c,i)=>{let u=i.voters.length-c.voters.length;return u!==0?u:c.scaleIndex-i.scaleIndex}),["| Vote | Voters |","|------|--------|",...r.map(c=>`| ${Bn(c.card)} | ${c.voters.join(", ")} |`)].join(`
5
+ `)}function Un(e){let t=$n(e.votes,e.scale),o=e.timerStartedAt!=null&&e.revealedAt!=null?An(e.revealedAt-e.timerStartedAt):null,n=[];return t?.kind==="numeric"?n.push(`Avg ${t.mean.toFixed(1)} \xB7 Med ${t.median}`):t?.kind==="categorical"&&n.push(`Most common: ${t.mode}`),o!=null&&n.push(`Elapsed ${o}`),n.join(" \xB7 ")}function jn(e){let{rounds:t}=e,o=Ir(e.participants),n=new Map;for(let[a,d]of o)n.set(a,d.displayName);let r=t.filter(a=>a.revealedAt!=null);if(r.length===0)return "";let s=new Map;t.forEach((a,d)=>s.set(a.id,d+1));let c=Tr(r),i=new Set,u=[];for(let a of r)for(let[d,f]of c)if(f.includes(a)&&!i.has(d)){i.add(d),u.push(d);break}let l=[];for(let a of u){let d=c.get(a),f=d.find(m=>m.id===a),C=d.filter(m=>m.id!==a),A=s.get(f.id)??0,E=f.title?`## Round ${A} \u2014 ${f.title}${Ft(f.ticketId)}`:`## Round ${A}${Ft(f.ticketId)}`,R=Hn(f,n),$=Un(f),p=$?`${E}
6
+
7
+ ${R}
8
+
9
+ ${$}`:`${E}
10
+
11
+ ${R}`;l.push(p);for(let m of C){let w=`### Re-vote ${s.get(m.id)??0}${Ft(m.ticketId)}`,D=Hn(m,n),g=Un(m),O=g?`${w}
12
+
13
+ ${D}
14
+
15
+ ${g}`:`${w}
16
+
17
+ ${D}`;l.push(O);}}return l.join(`
18
+
19
+ `)+`
20
+ `}function dt(e,t,o){if(e.startsWith("custom-")){let r=(o??[]).find(s=>s.id===e);return r?{id:r.id,cards:r.cards}:void 0}let n=se[e];if(n){let r=(t??{})[e]??[];if(r.length===0)return n;let s=n.cards.filter((c,i)=>!r.includes(i));return s.length===0?void 0:{id:e,cards:s}}}function lt(e,t){let o=Object.keys(se).map(r=>dt(r,e,t)).filter(r=>r!==void 0),n=(t??[]).map(r=>({id:r.id,cards:r.cards}));return [...o,...n]}function Ie(e,t){if(e.startsWith("custom-")){let o=(t??[]).find(n=>n.id===e);if(o&&o.label.trim().length>0)return o.label}return yn(e)}async function Ln(e,t){let o=await fetch(`${e}/rooms`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})});if(!o.ok)throw new Error(`POST /rooms failed: ${o.status}`);return await o.json()}async function Ye(e,t){let o=await fetch(`${e}/rooms/${encodeURIComponent(t)}`);if(o.status===404){let n=await o.json().catch(()=>null);throw new Error(`Room not found: ${n?.reason??"unknown-room"}`)}if(!o.ok)throw new Error(`GET /rooms/:code failed: ${o.status}`);return await o.json()}async function Vt(e,t,o){let n=await fetch(`${e}/rooms/${t}/access`,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify({passwordHash:o})});if(!n.ok)throw new Error(`PATCH /rooms/:uuid/access failed: ${n.status}`)}function _n(e){let o=`${e.wsBaseUrl.replace(/^http/,"ws")}/ws`,n={did:e.deviceId};e.passwordHash!==void 0&&(n.pw=e.passwordHash);let r=new WebsocketProvider(o,e.roomUuid,e.doc,{params:n,connect:true,...e.awareness!==void 0&&{awareness:e.awareness},WebSocketPolyfill:Ar}),s=l=>{e.onStatusChange?.(l.status);},c=l=>{if(l===null)return;let a=mn.get(l.code);a!==void 0&&(e.onRejection?.(a),r.shouldConnect=false,r.disconnect());};r.on("status",s),r.on("connection-close",c);let i=false,u=e.passwordHash;return {doc:e.doc,awareness:r.awareness,updateCredentials(l){if(l.passwordHash===u)return;u=l.passwordHash??void 0,r.shouldConnect=false,r.disconnect();let d={did:r.params.did??""};u!==void 0&&(d.pw=u),r.params=d,r.connect();},disconnect(){i||(i=true,r.off("status",s),r.off("connection-close",c),r.shouldConnect=false,r.disconnect(),r.destroy());}}}var Nr=5e3;function Fn(e,t,o){let n=null,r=null;function s(){let a=new Set;return t.getStates().forEach(d=>{let f=d.participantId;typeof f=="string"&&a.add(f);}),a}function c(){return e.getMap(Fe).get("hostParticipantId")}function i(){r!==null&&(clearTimeout(r),r=null),n=null;}function u(){let a=c(),d=n;if(n=null,!d||a!==d)return;let f=s();if(f.has(a))return;let C=[...f].toSorted();if(C.length===0)return;let A=C[0];A===o&&Me(e,{hostParticipantId:A});}function l(){let a=c();if(!a)return;if(s().has(a)){i();return}n!==a&&(i(),n=a,r=setTimeout(()=>{r=null,u();},Nr));}return t.on("change",l),e.getMap(Fe).observe(l),l(),{stop(){i(),t.off("change",l),e.getMap(Fe).unobserve(l);}}}async function Jn(e){let t,o=await Ln(e.relayUrl);e.password&&(t=await Re(e.password,o.uuid),await Vt(e.relayUrl,o.uuid,t));let n=new Oe.Doc,r=new Awareness(n),s=Date.now();return vn(n,{roomUuid:o.uuid,createdAt:s,participantId:e.participantId,displayName:e.displayName,defaultScale:e.defaultScale??ot,...e.sessionName!==void 0&&{sessionName:e.sessionName}}),r.setLocalStateField("participantId",e.participantId),ut({doc:n,awareness:r,roomCode:o.code,roomUuid:o.uuid,participantId:e.participantId,relayUrl:e.relayUrl,passwordHash:t})}async function Wn(e){let t=await Ye(e.relayUrl,e.roomCode),o;if(t.accessMode==="password"){if(!e.password)throw new Error("Room requires a password");o=await Re(e.password,t.uuid);}let n=new Oe.Doc,r=new Awareness(n);return ct(n,{participantId:e.participantId,displayName:e.displayName}),r.setLocalStateField("participantId",e.participantId),ut({doc:n,awareness:r,roomCode:e.roomCode,roomUuid:t.uuid,participantId:e.participantId,relayUrl:e.relayUrl,passwordHash:o,onRejection:e.onRejection})}function ut(e){let{doc:t,awareness:o,participantId:n,relayUrl:r,roomUuid:s}=e,c=new Set,i=new Set,u=new Set,l=_n({wsBaseUrl:e.relayUrl,roomUuid:e.roomUuid,doc:t,deviceId:n,passwordHash:e.passwordHash,awareness:o,onStatusChange(p){c.forEach(m=>m(p));},onRejection(p){console.error(`Connection rejected: ${p}`),e.onRejection?.(p);}}),a=Fn(t,o,n),d=()=>{i.forEach(p=>p());};t.getMap("meta").observeDeep(d),t.getMap("settings").observeDeep(d),t.getMap("participants").observeDeep(d),t.getArray("rounds").observeDeep(d);function f(){let p=new Set;return o.getStates().forEach(m=>{let y=m.participantId;typeof y=="string"&&p.add(y);}),p}let C=new Set,A=setInterval(()=>{let p=Q(t),m=ee(p);if(!m||m.revealedAt!==void 0||C.has(m.id))return;let y=f();if(y.size===0)return;let w=p.participants.filter(g=>y.has(g.id)&&be(g)==="voter");if(w.length===0)return;w.filter(g=>g.id in m.votes).length>=w.length&&(C.add(m.id),jt(t,m.id,Date.now()));},250),E=t.getMap("settings").get("hostParticipantId"),R=()=>{let p=t.getMap("settings").get("hostParticipantId");p!==E&&p!==void 0&&u.forEach(m=>m(p)),E=p;};t.getMap("settings").observe(R);let $=false;return {doc:t,awareness:o,roomCode:e.roomCode,roomUuid:e.roomUuid,projection(){return Q(t)},current(){return ee(Q(t))},isHost(){return Q(t).settings.hostParticipantId===n},onlineIds:f,vote(p){let m=ee(Q(t));m&&bn(t,m.id,n,p);},unvote(){let p=ee(Q(t));p&&Rn(t,p.id,n);},reveal(){let p=ee(Q(t));p&&jt(t,p.id,Date.now());},skip(){let p=ee(Q(t));p&&In(t,p.id,Date.now());},newRound(p,m){let y=randomUUID();return Ut(t,{id:y,createdAt:Date.now(),createdBy:n,...p!==void 0&&{title:p},...m!==void 0&&{ticketId:m}}),y},revote(){let p=ee(Q(t)),m=randomUUID();return Ut(t,{id:m,createdAt:Date.now(),createdBy:n,...p?.id!==void 0&&{parentRoundId:p.id},...p?.title!==void 0&&{title:p.title},...p?.description!==void 0&&{description:p.description},...p?.ticketId!==void 0&&{ticketId:p.ticketId}}),m},updateRoundDescriptor(p,m){Cn(t,p,m);},updateName(p){Me(t,{name:p});},updateScale(p){Me(t,{defaultScale:p});},updateDisplayName(p){let w=Q(t).participants.find(D=>D.id===n)?.joinedAt??Date.now();Ht(t,n,{displayName:p,joinedAt:w});},async updateAccess(p,m){let y=null;if(p==="password"){if(!m)throw new Error("Password required for password mode");y=await Re(m,s);}await Vt(r,s,y),l.updateCredentials({passwordHash:y});},handoffHost(p){if(!this.isHost())throw new Error("Only the current host can hand off");if(!f().has(p))throw new Error("Target participant is not online");if(!Q(t).participants.some(D=>D.id===p))throw new Error("Target participant does not exist");Me(t,{hostParticipantId:p});},toggleRole(){let m=Q(t).participants.find(g=>g.id===n),w=be(m??{})==="viewer"?"voter":"viewer",D=m?.joinedAt??Date.now();Ht(t,n,{displayName:m?.displayName??"",joinedAt:D,role:w});},endSessionForEveryone(){if(!this.isHost())throw new Error("Only the current host can end the session");Tn(t);},onChange(p){return i.add(p),()=>i.delete(p)},onStatusChange(p){return c.add(p),()=>c.delete(p)},onHostChange(p){return u.add(p),()=>u.delete(p)},disconnect(){$||($=true,clearInterval(A),a.stop(),t.getMap("meta").unobserveDeep(d),t.getMap("settings").unobserveDeep(d),t.getMap("settings").unobserve(R),t.getMap("participants").unobserveDeep(d),t.getArray("rounds").unobserveDeep(d),l.disconnect());}}}function qn(e){let t=Lt(e);return t===""?null:t}var Br=1440*60*1e3;function Jt(){return join(je(),"last-session.json")}function Hr(e){if(typeof e!="object"||e===null)return false;let t=e;return typeof t.roomCode=="string"&&typeof t.roomUuid=="string"&&typeof t.lastSeenAt=="number"&&(t.passwordHash===void 0||typeof t.passwordHash=="string")}function zn(){let e=Jt();if(!existsSync(e))return null;try{let t=JSON.parse(readFileSync(e,"utf-8"));return !Hr(t)||Date.now()-t.lastSeenAt>Br?(F(),null):t}catch{return F(),null}}function pt(e){let t=je();mkdirSync(t,{recursive:true,mode:448}),chmodSync(t,448);let o=Jt();writeFileSync(o,JSON.stringify(e,null,2)+`
21
+ `,{encoding:"utf-8",mode:384}),chmodSync(o,384);}function F(){let e=Jt();try{unlinkSync(e);}catch(t){if(t.code!=="ENOENT")throw t}}async function ft(e){let t=true;try{await e.clearLastSession();}catch(o){t=false,e.logger.error(`Failed to clear cached session: ${o instanceof Error?o.message:String(o)}`);}return t?e.logger.error("Session expired (room no longer exists). Cached session cleared."):e.logger.error("Session expired. Could not delete ~/.config/hnch/last-session.json \u2014 please remove it manually."),e.exit(1)}function mt(e){let t=e.doc.getMap("meta"),o=null,n=false;function r(){if(n)return;let s=t.get("sessionEndedAt");if(s==null||s===o)return;o=s;let c=t.get("createdBy"),i=c===e.localParticipantId,u=e.doc.getMap("participants"),l=e.doc.getMap("settings").get("hostParticipantId")??c,a="host";if(l){let f=u.get(l)?.get("displayName");typeof f=="string"&&f.length>0&&(a=f);}setImmediate(()=>{n||e.onEnded({isLocal:i,hostName:a,timestamp:s});});}return t.observe(r),r(),()=>{n=true,t.unobserve(r);}}var Ur="@xauyxau/hnch";function gt(e){return Mn(e)}function ht(e){return `${process.env.HNCH_SHARE_ORIGIN??"https://hnch.dev"}/join?code=${e}`}function St(e){return `npx ${Ur} join ${e}`}function Wt(e){let t=e.trim();return t===""?void 0:t}function Xn(e,t){return [e,Wt(t)]}var jr={connecting:"yellow",connected:"green",disconnected:"red"};function Zn({roomCode:e,sessionName:t,status:o,isHost:n}){let r=t?`${e} \xB7 ${t}`:e;return jsxs(Box,{borderStyle:"single",borderColor:"gray",paddingX:1,justifyContent:"space-between",children:[jsxs(Text,{bold:true,children:["hnch \xB7 ",r]}),jsxs(Box,{gap:2,children:[n&&jsx(Text,{color:"yellow",children:"[host]"}),jsx(Text,{color:jr[o],children:o})]})]})}function pe({items:e,onSelect:t,onClose:o,title:n}){if(e.length===0)return jsxs(Box,{flexDirection:"column",gap:1,children:[n&&jsx(Text,{bold:true,children:n}),jsx(Text,{dimColor:true,children:"(no items)"})]});let r=e.findIndex(i=>!i.disabled),[s,c]=useState(Math.max(r,0));return useInput((i,u)=>{if(u.upArrow)c(l=>{let a=(l-1+e.length)%e.length,d=0;for(;e[a]?.disabled&&d<e.length;)a=(a-1+e.length)%e.length,d++;return a});else if(u.downArrow)c(l=>{let a=(l+1)%e.length,d=0;for(;e[a]?.disabled&&d<e.length;)a=(a+1)%e.length,d++;return a});else if(u.return){let l=e[s];l&&!l.disabled&&t(l.value);}else u.escape&&o?.();}),jsxs(Box,{flexDirection:"column",gap:1,children:[n&&jsx(Text,{bold:true,children:n}),jsx(Box,{flexDirection:"column",children:e.map((i,u)=>{let l=u===s,a=i.disabled??false;return jsxs(Box,{gap:1,children:[jsx(Text,{...l&&{color:"cyan"},dimColor:a,children:l?">":" "}),jsx(Text,{bold:l,dimColor:a,children:i.label}),i.detail&&jsx(Text,{dimColor:true,children:i.detail})]},i.value)})}),jsx(Text,{dimColor:true,children:"\u2191\u2193 select \u23CE confirm esc close"})]})}function q({value:e,onChange:t,onSubmit:o,onCancel:n,placeholder:r,isActive:s=true,mask:c=false}){useInput((a,d)=>{d.escape?n?.():d.return?o(e):d.backspace||d.delete?t(e.slice(0,-1)):a&&!d.ctrl&&!d.meta&&t(e+a);},{isActive:s});let i=c&&e.length>0?"*".repeat(e.length):e,u=i.length>0?i:r??"",l=e.length===0&&r!==void 0;return jsxs(Text,{children:[jsx(Text,{dimColor:l,children:u}),jsx(Text,{inverse:true,children:" "})]})}function oo({mode:e,password:t,onModeChange:o,onPasswordChange:n,onClose:r}){let[s,c]=useState(e==="open"?0:1),[i,u]=useState(false);return useEffect(()=>{c(e==="open"?0:1);},[e]),useInput((l,a)=>{if(a.leftArrow)c(d=>Math.max(0,d-1));else if(a.rightArrow)c(d=>Math.min(1,d+1));else if(a.return){let d=s===0?"open":"password";d!==e&&o(d),d==="password"&&u(true);}else a.escape&&r?.();},{isActive:!i}),jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Box,{gap:1,children:["open","password"].map((l,a)=>{let d=a===s,f=l===e,C=l==="open"?"Open":"Password";return jsx(Text,{inverse:d,bold:f,...f&&{color:"cyan"},children:f?`[${C}]`:` ${C} `},l)})}),e==="password"&&jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{children:"Password: "}),jsx(q,{value:t,onChange:n,onSubmit:()=>u(false),onCancel:()=>u(false),placeholder:"(required to join)",isActive:i,mask:true}),!i&&jsx(Text,{dimColor:true,children:"\u23CE edit \u2190 \u2192 switch mode esc close"}),i&&jsx(Text,{dimColor:true,children:"\u23CE done esc cancel"})]}),e==="open"&&jsx(Text,{dimColor:true,children:"\u2190\u2192 toggle \u23CE confirm esc close"})]})}function io({scaleId:e,cards:t,disabledIndices:o,onOverrideChange:n,onClose:r}){let s=useMemo(()=>new Set(o),[o]),[c,i]=useState(0),u=l=>{let a=new Set(s);if(a.has(l))a.delete(l);else {if(a.size>=t.length-1)return;a.add(l);}let d=Array.from(a).toSorted((f,C)=>f-C);n(e,d);};return useInput((l,a)=>{a.leftArrow?i(d=>Math.max(0,d-1)):a.rightArrow?i(d=>Math.min(t.length-1,d+1)):l===" "?u(c):(a.return||a.escape)&&r();}),jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Box,{gap:1,children:t.map((l,a)=>{let d=a===c,f=s.has(a);return jsx(Text,{inverse:d,bold:!f,...!f&&{color:"cyan"},strikethrough:f,dimColor:f,children:l},l)})}),jsx(Text,{dimColor:true,children:" \u2190\u2192 navigate space toggle \u23CE/esc done"})]})}function en({initialName:e="",initialCards:t=[],onSave:o,onCancel:n}){let r=e.length>0,[s,c]=useState(e),[i,u]=useState(t.length>0?t:[""]),[l,a]=useState(0),[d,f]=useState("editing-name"),[C,A]=useState(""),E=()=>{let p=s.trim();if(p.length===0||p.length>40){A("Name must be 1-40 characters");return}c(p),A(""),f("editing-cards");},R=()=>{let p=s.trim();if(p.length===0||p.length>40){A("Name must be 1-40 characters");return}let m=i.map(w=>w.trim()).filter(w=>w.length>0),y=Array.from(new Set(m));if(y.length<2){A("Need at least 2 unique non-empty cards");return}A(""),o(p,y);};return useInput((p,m)=>{if(d==="editing-cards"){if(m.leftArrow)a(y=>Math.max(0,y-1));else if(m.rightArrow)a(y=>{let w=y+1;return w>=i.length&&i.length<25?(u(D=>[...D,""]),w):Math.min(w,i.length-1)});else if(m.return)R();else if(m.escape)n();else if(m.tab)f("editing-name");else if(m.backspace){let y=i[l]||"";if(y.length===0){if(i.length>2){let w=i.filter((D,g)=>g!==l);u(w),a(Math.max(0,l-1));}}else {let w=[...i];w[l]=y.slice(0,-1),u(w);}}else if(p&&!m.ctrl&&!m.meta){let y=i[l]||"";if(y.length<10){let w=[...i];w[l]=y+p,u(w);}}}},{isActive:d==="editing-cards"}),jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:r?"\u2500\u2500 Edit Custom Scale \u2500\u2500":"\u2500\u2500 Create Custom Scale \u2500\u2500"}),jsxs(Box,{flexDirection:"column",gap:0,children:[jsx(Text,{children:"Name:"}),jsx(q,{value:s,onChange:c,onSubmit:E,onCancel:n,placeholder:"Scale name",isActive:d==="editing-name"})]}),d==="editing-cards"&&jsxs(Box,{flexDirection:"column",gap:0,children:[jsx(Text,{children:"Cards:"}),jsx(Box,{gap:1,flexWrap:"wrap",children:i.map((p,m)=>{let y=m===l;return jsx(Text,{inverse:y,...!y&&{color:"cyan"},children:p.length>0?p:"\xB7"},m)})})]}),C&&jsx(Text,{color:"red",children:C}),jsx(Text,{dimColor:true,children:d==="editing-name"?"\u23CE next esc cancel":"\u2190\u2192 navigate typing adds card bsp delete \u23CE save tab name esc cancel"})]})}function yt({scales:e,selectedScaleId:t,presetOverrides:o,customScales:n=[],onSelect:r,onOverrideChange:s,onCustomScaleCreate:c,onCustomScaleEdit:i,onCustomScaleDelete:u,onClose:l,showNoneOption:a=false}){let[d,f]=useState(0),[C,A]=useState(null),[E,R]=useState("browsing"),[$,p]=useState(null),[m,y]=useState(null),w=c?[...e,{id:"__create-custom__",cards:[]}]:e,D=w.length;if(useInput((g,O)=>{if(E==="browsing"){if(O.escape){l();return}if(D===0)return;if(O.upArrow)f(b=>(b-1+D)%D);else if(O.downArrow)f(b=>(b+1)%D);else if(O.return){let b=w[d];if(!b)return;if(b.id==="__create-custom__"){R("creating");return}b.id in se?A(C===b.id?null:b.id):r(b.id);}else if(g===" "){let b=w[d];b&&b.id!=="__create-custom__"&&r(b.id);}else if(g==="e"||g==="E"){let b=w[d];i&&b?.id.startsWith("custom-")&&(p(b.id),R("editing"));}else if(g==="d"||g==="D"){let b=w[d];u&&b?.id.startsWith("custom-")&&(y(b.id),R("confirming-delete"));}}else E==="confirming-delete"&&(g==="y"||g==="Y"?(m&&u&&u(m),y(null),R("browsing")):(g==="n"||g==="N"||O.escape)&&(y(null),R("browsing")));},{isActive:E==="browsing"&&C===null||E==="confirming-delete"}),E==="creating")return jsx(en,{onSave:(g,O)=>{c&&c(g,O),R("browsing");},onCancel:()=>R("browsing")});if(E==="editing"&&$){let g=n?.find(b=>b.id===$);if(e.find(b=>b.id===$)&&g)return jsx(en,{initialName:g.label,initialCards:g.cards,onSave:(b,j)=>{i&&i($,b,j),p(null),R("browsing");},onCancel:()=>{p(null),R("browsing");}})}if(E==="confirming-delete"&&m){let g=w.find(b=>b.id===m),O=g?Ie(g.id,n):"Unknown";return jsxs(Box,{flexDirection:"column",gap:1,children:[jsxs(Text,{children:["Delete ",O,"? "]}),jsx(Text,{children:"[y] yes [n] no esc cancel"})]})}return w.length===0?jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{dimColor:true,children:"(no scales available)"}),jsx(Text,{dimColor:true,children:"esc back"})]}):jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Box,{flexDirection:"column",children:w.map((g,O)=>{let b=O===d,j=g.id===t,ae=C===g.id,ne=g.id in se,oe=g.id==="__create-custom__",Y=g.cards.join(", "),v=a&&g.id==="",T=v?"(none)":oe?"[+ Create custom scale]":Ie(g.id,n);return jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{...b&&{color:"cyan"},children:b?">":" "}),jsx(Text,{bold:b,children:T}),!v&&!oe&&jsx(Text,{dimColor:true,children:Y}),j&&jsx(Text,{color:"cyan",children:"[current]"})]}),ae&&!v&&!oe&&jsx(Box,{paddingLeft:2,children:ne?jsx(io,{scaleId:g.id,cards:se[g.id]?.cards??[],disabledIndices:o?.[g.id]??[],onOverrideChange:s,onClose:()=>A(null)}):null})]},g.id||"none")})}),jsx(Text,{dimColor:true,children:(()=>{let g=d<w.length?w[d]:null;return g?.id.startsWith("custom-")?`\u2191\u2193 navigate space select${i?" [e] edit":""}${u?" [d] delete":""} esc back`:g&&g.id in se?"\u2191\u2193 navigate \u23CE customize space select esc back":"\u2191\u2193 navigate \u23CE/space select esc back"})()})]})}function Zr(e,t){return t?[...e]:e.filter(o=>o.value==="display-name")}function ao({sessionName:e,displayName:t,currentScale:o,accessMode:n,presetOverrides:r,customScales:s,isHost:c=true,onChangeName:i,onChangeScale:u,onChangeDisplayName:l,onChangeAccess:a,onClose:d}){let[f,C]=useState(r??{}),A=useMemo(()=>lt(f,s),[f,s]),[E,R]=useState("main-menu"),[$,p]=useState(e??""),[m,y]=useState(t),[w,D]=useState(n==="password"?"password":"open"),[g,O]=useState(""),[b,j]=useState(false),ae=[{label:"Room name",value:"room-name",detail:`[${e??"(none)"}]`},{label:"Scale",value:"scale",detail:`[${Ie(o.id,s)}]`},{label:"Access",value:"access",detail:`[${n==="open"?"Open":"Password"}]`},{label:"Display name",value:"display-name",detail:`[${t}]`}],ne=Zr(ae,c);function oe(k){!c&&k!=="display-name"||(k==="room-name"?(p(e??""),R("editing-room-name")):k==="scale"?R("picking-scale"):k==="access"?R("picking-access"):k==="display-name"&&(y(t),R("editing-display-name")));}function Y(k){let B=k.trim()===""?void 0:k.trim();i(B),R("main-menu");}function v(k){let B=k.trim();B.length>0&&(l(B),process.nextTick(()=>J({displayName:B}))),R("main-menu");}function T(k){let B=A.find(z=>z.id===k);B&&u(B),R("main-menu");}function P(k,B){let z={...f};B.length===0?delete z[k]:z[k]=B,C(z),J({presetOverrides:Object.keys(z).length>0?z:void 0});}async function L(k,B){j(true);try{await a(k,B),D(k),O(k==="password"?B??"":"");}catch(z){console.error(`Failed to update access: ${z}`);}finally{j(false),R("main-menu");}}return E==="main-menu"?jsx(pe,{title:"\u2500\u2500 Settings \u2500\u2500",items:ne,onSelect:oe,onClose:d}):E==="editing-room-name"?jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Settings \u2500\u2500"}),jsx(Text,{children:"Room name: "}),jsx(q,{value:$,onChange:p,onSubmit:Y,onCancel:()=>R("main-menu"),placeholder:"(leave empty to remove)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]}):E==="editing-display-name"?jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Settings \u2500\u2500"}),jsx(Text,{children:"Display name: "}),jsx(q,{value:m,onChange:y,onSubmit:v,onCancel:()=>R("main-menu"),placeholder:"(required)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]}):E==="picking-scale"?jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Settings \u2500\u2500"}),jsx(yt,{scales:A,selectedScaleId:o.id,presetOverrides:f,onSelect:T,onOverrideChange:P,onClose:()=>R("main-menu")})]}):E==="picking-access"?jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Settings \u2500\u2500"}),b&&jsx(Text,{dimColor:true,children:"Updating access..."}),!b&&jsx(oo,{mode:w,password:g,onModeChange:k=>{k==="open"&&L("open");},onPasswordChange:O,onClose:()=>R("main-menu")})]}):jsx(Box,{})}function bt({participants:e,onlineIds:t,hostId:o,votes:n,revealed:r,participantId:s}){return jsxs(Box,{flexDirection:"column",children:[jsxs(Text,{bold:true,children:["Participants (",e.length,")"]}),e.map(c=>{let i=t.has(c.id),u=i?"\u25CF":"\u25CB",l=i?"green":"gray",a=c.id===o,d=be(c)==="viewer",f=c.id===s,C=c.id in n,A=d?" (viewer)":r?n[c.id]?` \u2192 ${n[c.id]}`:"":C?" \u2713":" \xB7",E=f&&!d&&!(r&&n[c.id]);return jsxs(Box,{gap:1,children:[jsx(Text,{color:l,children:u}),jsxs(Text,{children:[c.displayName,a?" (host)":""]}),E?jsx(Text,{color:"cyan",children:"(voter)"}):jsx(Text,{color:d?"gray":C?"green":"gray",children:A})]},c.id)})]})}var es=[{label:"End session for everyone",value:"end-session",detail:"Disconnects all participants. Cannot be undone."}];function lo({onPick:e,onClose:t}){return jsx(pe,{title:"Host Actions",items:es,onSelect:e,onClose:t})}function Rt({roomCode:e,onPick:t,onClose:o}){let n=[{label:"Copy code",value:"code",detail:gt(e)},{label:"Copy share link",value:"url",detail:ht(e)},{label:"Copy npx command",value:"npx",detail:St(e)}];return jsx(pe,{title:"Share",items:n,onSelect:r=>t(r),onClose:o})}function os(e){let t=e.rounds.filter(n=>W(n)!==null),o=Object.fromEntries(e.participants.map(n=>[n.id,n]));return jn({rounds:t,participants:o})}async function uo(e,t,o){let n=os(e);try{await t(n),o("Results copied to clipboard");}catch{o("Clipboard write failed");}}function po(e,t,o){let n=Pn(e.id,o);return n===null?`Round ${t}`:`Round ${n.depth}/${n.total} [re-vote]`}function fo(e,t,o){let n=po(e,t,o),r=e.title?` \u2014 ${e.title}`:"",s=e.ticketId?` [${e.ticketId}]`:"",c=W(e)==="skipped"?" \u2014 Skipped":" \u2014 Results";return `${n}${r}${s}${c}`}function mo(e){let t=Object.values(e).map(a=>parseFloat(a)).filter(a=>!isNaN(a)).toSorted((a,d)=>a-d);if(t.length===0)return {average:"-",median:"-",agreement:"-"};let n=t.reduce((a,d)=>a+d,0)/t.length,r=Math.floor(t.length/2),s=t.length%2===0?(t[r-1]+t[r])/2:t[r],c=Object.values(e),i=new Map;for(let a of c)i.set(a,(i.get(a)??0)+1);let u=Math.max(...i.values()),l=c.length>0?Math.round(u/c.length*100):0;return {average:n.toFixed(1),median:s.toFixed(1),agreement:`${l}%`}}function nn(e,t){let o=Math.floor((t-e)/1e3),n=Math.floor(o/60),r=o%60;return `${n}:${r.toString().padStart(2,"0")}`}function go(e,t,o=[]){let n=po(e,t,o);if(W(e)==="skipped"){let l=e.title?` ${e.title}`:"",a=e.ticketId?` [${e.ticketId}]`:"";return `${n}:${l}${a} \u2014 Skipped`}let r=new Map;for(let l of Object.values(e.votes))r.set(l,(r.get(l)??0)+1);let s=[...r.entries()].toSorted((l,a)=>a[1]-l[1]||l[0].localeCompare(a[0])).map(([l,a])=>`${l}\xD7${a}`).join(", "),c=e.title?` ${e.title}`:"",i=e.ticketId?` [${e.ticketId}]`:"",u=e.timerStartedAt!==void 0&&e.revealedAt!==void 0?` \u2014 ${nn(e.timerStartedAt,e.revealedAt)}`:"";return `${n}:${c}${i} \u2014 ${s||"(no votes)"}${u}`}function ho({projection:e,onlineIds:t,isHost:o,participantId:n,rounds:r,currentRound:s,settingsOpen:c,onStartRound:i,onNewRound:u,onRevote:l,onOpenSettings:a,onHandoffHost:d,onEndSession:f,onShare:C,shareOpen:A,onSetShareOpen:E,roomCode:R,confirmingQuit:$,myRole:p,onToggleRole:m,roundTitlePromptActive:y,roundTitleInput:w,onRoundTitleChange:D,onRoundTitleSubmit:g,onRoundTitleCancel:O,roundTicketIdPromptActive:b,roundTicketIdInput:j,onRoundTicketIdChange:ae,onRoundTicketIdSubmit:ne,onRoundTicketIdCancel:oe,descriptorPromptActive:Y,descriptorInput:v,onDescriptorChange:T,onDescriptorSubmit:P,onDescriptorCancel:L,onEditDescriptor:k,canExport:B,onExportResults:z}){let[X,re]=It.useState("idle"),[he,we]=It.useState(null),[ue,ce]=It.useState(false),[ye,ve]=It.useState(false);useEffect(()=>{o||(re("idle"),we(null),ce(false),ve(false));},[o]),useInput(h=>{if(h==="c"||h==="C"){E(true);return}if(h==="w"||h==="W"){m();return}if((h==="x"||h==="X")&&B){z();return}o&&((h==="e"||h==="E")&&a(),(h==="s"||h==="S")&&i(),(h==="h"||h==="H")&&re("picking"),(h==="m"||h==="M")&&ce(true),s!==null&&W(s)!==null&&((h==="n"||h==="N")&&u(),(h==="v"||h==="V")&&W(s)!=="skipped"&&l(),(h==="t"||h==="T")&&k()));},{isActive:!c&&!$&&X==="idle"&&!y&&!b&&!ue&&!ye&&!A&&!Y}),useInput((h,Z)=>{h==="y"||h==="Y"?(f(),ve(false)):(h==="n"||h==="N"||Z.escape)&&ve(false);},{isActive:ye});let de=e.participants.filter(h=>h.id!==n&&t.has(h.id));useInput(h=>{if(X==="picking"){let Z=parseInt(h,10);!isNaN(Z)&&Z>=1&&Z<=de.length?(we(de[Z-1].id),re("confirming")):(h==="Escape"||h==="q"||h==="Q")&&re("idle");}},{isActive:X==="picking"}),useInput(h=>{X==="confirming"&&he&&(h==="y"||h==="Y"?(d(he),re("idle"),we(null)):(h==="n"||h==="N"||h==="Escape")&&(re("idle"),we(null)));},{isActive:X==="confirming"});let Ze=r.length>0,Ue=r.filter(h=>W(h)!==null),et=s!==null?Ue.slice(0,-1):Ue;return ue?jsx(lo,{onPick:h=>{ce(false),h==="end-session"&&ve(true);},onClose:()=>ce(false)}):A?jsx(Rt,{roomCode:R,onPick:h=>{E(false),C(h);},onClose:()=>E(false)}):jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:Ze?"Session":"Waiting for participants..."}),!Ze&&jsx(Text,{dimColor:true,children:"No rounds started yet."}),jsx(bt,{participants:e.participants,onlineIds:t,hostId:e.settings.hostParticipantId,votes:{},revealed:false,participantId:n}),s!==null&&W(s)!==null&&jsx(ss,{projection:e,round:s,rounds:r}),et.length>0&&jsxs(Box,{flexDirection:"column",children:[jsx(Text,{dimColor:true,children:"\u2500\u2500 History \u2500\u2500"}),et.map(h=>{let Z=r.indexOf(h)+1;return jsx(Text,{dimColor:true,children:go(h,Z,r)},h.id)})]}),X==="picking"&&de.length>0&&jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{children:"Hand off to:"}),de.map((h,Z)=>jsxs(Text,{children:["[",Z+1,"] ",h.displayName]},h.id)),jsx(Text,{dimColor:true,children:"[q] Cancel"})]}),X==="picking"&&de.length===0&&jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{dimColor:true,children:"No other online participants"}),jsx(Text,{dimColor:true,children:"[q] Cancel"})]}),X==="confirming"&&he&&jsx(Box,{flexDirection:"column",children:jsxs(Text,{children:["Hand off to ",e.participants.find(h=>h.id===he)?.displayName,"? [y/n]"]})}),y&&jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{children:"Round title (optional, Enter to skip):"}),jsx(q,{value:w,onChange:D,onSubmit:g,onCancel:O,placeholder:"Enter to skip",isActive:y})]}),jsx(Text,{dimColor:true,children:"[Esc] Cancel"})]}),b&&jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{children:"Ticket ID (optional, Enter to skip):"}),jsx(q,{value:j,onChange:ae,onSubmit:ne,onCancel:oe,placeholder:"e.g. PROJ-1234",isActive:b})]}),jsx(Text,{dimColor:true,children:"[Esc] Cancel"})]}),Y&&jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{children:"Edit round title:"}),jsx(q,{value:v,onChange:T,onSubmit:P,onCancel:L,placeholder:"Enter to save",isActive:Y})]}),jsx(Text,{dimColor:true,children:"[Esc] Cancel \xB7 empty submit cancels"})]}),ye&&jsxs(Box,{children:[jsx(Text,{color:"red",children:"End session for everyone? This cannot be undone. "}),jsx(Text,{dimColor:true,children:"y/n [Esc] Cancel"})]}),X==="idle"&&!y&&!b&&!Y&&!ye&&jsxs(Box,{gap:2,children:[o?jsx(Fragment,{children:s!==null&&W(s)!==null?jsxs(Fragment,{children:[W(s)!=="skipped"&&jsx(Text,{color:"yellow",children:"[v] Revote"}),jsx(Text,{color:"yellow",children:"[n] Next"}),jsx(Text,{color:"yellow",children:"[t] Edit title"}),jsx(Text,{color:"yellow",children:"[h] Handoff"}),jsx(Text,{color:"yellow",children:"[e] Settings"}),jsx(Text,{color:"yellow",children:"[c] Share"}),B&&jsx(Text,{color:"yellow",children:"[x] Export"}),jsxs(Text,{color:"yellow",children:["[w] ",p==="viewer"?"Vote":"Observe"]}),jsx(Text,{color:"yellow",children:"[m] Menu"})]}):jsxs(Fragment,{children:[jsx(Text,{color:"yellow",children:"[s] Start round"}),jsx(Text,{color:"yellow",children:"[h] Handoff"}),jsx(Text,{color:"yellow",children:"[e] Settings"}),jsx(Text,{color:"yellow",children:"[c] Share"}),B&&jsx(Text,{color:"yellow",children:"[x] Export"}),jsxs(Text,{color:"yellow",children:["[w] ",p==="viewer"?"Vote":"Observe"]}),jsx(Text,{color:"yellow",children:"[m] Menu"})]})}):jsxs(Fragment,{children:[jsx(Text,{dimColor:true,children:"Waiting for host to start a round..."}),jsx(Text,{children:"[c] Share"}),B&&jsx(Text,{color:"yellow",children:"[x] Export"}),jsxs(Text,{color:"yellow",children:["[w] ",p==="viewer"?"Vote":"Observe"]})]}),jsx(Text,{children:"[q] Quit"})]})]})}function ss({projection:e,round:t,rounds:o}){let n=o.indexOf(t)+1,r=fo(t,n,o);if(W(t)==="skipped")return jsx(Box,{flexDirection:"column",gap:1,children:jsx(Text,{bold:true,children:r})});let s=mo(t.votes),c=new Map(e.participants.map(l=>[l.id,l.displayName])),i=t.timerStartedAt!==void 0&&t.revealedAt!==void 0?nn(t.timerStartedAt,t.revealedAt):null,u=Object.values(t.votes)[0];return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:r}),En(t)&&jsxs(Text,{bold:true,color:"green",children:["Consensus! Everyone voted ",u]}),jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:2,children:[jsx(Text,{bold:true,underline:true,children:"Name".padEnd(20)}),jsx(Text,{bold:true,underline:true,children:"Estimate".padEnd(10)})]}),Object.entries(t.votes).map(([l,a])=>jsxs(Box,{gap:2,children:[jsx(Text,{children:(c.get(l)??l).padEnd(20)}),jsx(Text,{color:"cyan",bold:true,children:a.padEnd(10)})]},l)),e.participants.filter(l=>!(l.id in t.votes)).map(l=>jsxs(Box,{gap:2,children:[jsx(Text,{dimColor:true,children:l.displayName.padEnd(20)}),jsx(Text,{dimColor:true,children:"(no vote)".padEnd(10)})]},l.id))]}),jsxs(Box,{gap:2,children:[jsxs(Text,{children:["Avg: ",jsx(Text,{bold:true,children:s.average})]}),jsxs(Text,{children:["Med: ",jsx(Text,{bold:true,children:s.median})]}),jsxs(Text,{children:["Agreement: ",jsx(Text,{bold:true,children:s.agreement})]}),i!==null&&jsxs(Text,{dimColor:true,children:["\u23F1 ",i]})]})]})}function wo({scale:e,selected:t,onSelect:o,onClear:n,disabled:r}){let[s,c]=useState(0);return useInput((i,u)=>{if(!r)if(u.leftArrow)c(l=>Math.max(0,l-1));else if(u.rightArrow)c(l=>Math.min(e.cards.length-1,l+1));else if(u.return){let l=e.cards[s];l===t?n():o(l);}else (u.backspace||u.delete)&&n();}),jsxs(Box,{flexDirection:"column",gap:0,children:[jsx(Box,{gap:1,children:e.cards.map((i,u)=>{let l=u===s,a=i===t;return jsx(Text,{inverse:l,bold:a,...a&&{color:"cyan"},children:a?`[${i}]`:` ${i} `},i)})}),jsx(Text,{dimColor:true,children:" \u2190\u2192 navigate \u23CE select \u232B clear"})]})}function ls(e){let t=Math.floor(e/1e3),o=Math.floor(t/60),n=t%60;return `${o}:${n.toString().padStart(2,"0")}`}function yo({projection:e,round:t,onlineIds:o,isHost:n,myId:r,myRole:s,elapsedMs:c,roomCode:i,shareOpen:u,settingsOpen:l,onVote:a,onClearVote:d,onReveal:f,onSkip:C,onToggleRole:A,onShare:E,onSetShareOpen:R,onOpenSettings:$,confirmingQuit:p}){if(useInput(g=>{if(g==="e"||g==="E"){$();return}if(g==="c"||g==="C"){R(true);return}n&&(g==="r"||g==="R")&&f(),n&&(g==="s"||g==="S")&&C(),(g==="w"||g==="W")&&A();},{isActive:!p&&!l&&!u}),u)return jsx(Rt,{roomCode:i,onPick:g=>{R(false),E(g);},onClose:()=>R(false)});let m=t.votes[r],y=e.rounds.indexOf(t)+1,w=t.title?` \u2014 ${t.title}`:"",D=t.ticketId?` [${t.ticketId}]`:"";return jsxs(Box,{flexDirection:"column",gap:1,children:[jsxs(Box,{gap:2,children:[jsxs(Text,{bold:true,children:["Round ",y,w,D]}),c!==void 0&&jsxs(Text,{dimColor:true,children:["\u23F1 ",ls(c)]})]}),jsxs(Box,{gap:4,children:[jsx(bt,{participants:e.participants,onlineIds:o,hostId:e.settings.hostParticipantId,votes:t.votes,revealed:false}),s==="viewer"?jsx(Box,{flexDirection:"column",children:jsx(Text,{dimColor:true,children:"You're observing"})}):jsx(wo,{scale:t.scale,selected:m,onSelect:a,onClear:d,disabled:p})]}),jsxs(Box,{gap:2,children:[n&&jsx(Text,{color:"yellow",children:"[r] Reveal"}),n&&jsx(Text,{color:"yellow",children:"[s] Skip"}),jsxs(Text,{color:"yellow",children:["[w] ",s==="viewer"?"Vote":"Observe"]}),jsx(Text,{children:"[e] Settings"}),jsx(Text,{children:"[c] Share"}),jsx(Text,{children:"[q] Quit"})]})]})}var ms=3e3;function At(e=ms){let[t,o]=useState(null),n=useRef(null),r=useCallback(()=>{n.current!==null&&(clearTimeout(n.current),n.current=null);},[]),s=useCallback(i=>{r(),o(i),n.current=setTimeout(()=>{o(null),n.current=null;},e);},[r,e]),c=useCallback(()=>{r(),o(null);},[r]);return useEffect(()=>()=>{r();},[r]),{notice:t,show:s,dismiss:c}}function Ss(e){let t=ee(e);return !t||W(t)!==null?"lobby":"voting"}function Pt({session:e,participantId:t,displayName:o,accessMode:n,presetOverrides:r,customScales:s}){let{exit:c}=useApp(),[i,u]=useState(e.projection()),[l,a]=useState("connecting"),[d,f]=useState(e.onlineIds()),[C,A]=useState(),[E,R]=useState(false),[$,p]=useState(n==="password"?"password":"open"),[m,y]=useState(o),[w,D]=useState(false),[g,O]=useState(false),[b,j]=useState(""),[ae,ne]=useState(false),[oe,Y]=useState(""),[v,T]=useState(),[P,L]=useState(false),[k,B]=useState(""),[z,X]=useState(null),re=At(3e3),[he,we]=useState(false),ue=At(3e3),ce=At(3e3);useEffect(()=>{let I=e.onChange(()=>{u(e.projection()),f(e.onlineIds());}),_=e.onStatusChange(a),ke=e.onHostChange(tt=>{let zo=e.projection().participants.find(Qo=>Qo.id===tt),Xo=tt===t?"You are now the host":`${zo?.displayName||"Unknown"} is now the host`;re.show(Xo);}),Go=setInterval(()=>{f(e.onlineIds());let tt=e.projection(),nt=ee(tt);nt?.timerStartedAt!==void 0&&nt.revealedAt===void 0?A(Date.now()-nt.timerStartedAt):A(void 0);},1e3);return ()=>{I(),_(),ke(),clearInterval(Go);}},[e,t,re.show]),useInput(I=>{(I==="q"||I==="Q")&&D(true);},{isActive:!E&&!w&&!g&&!ae&&!P&&!he}),useInput((I,_)=>{I==="y"||I==="Y"?(e.disconnect(),c()):(I==="n"||I==="N"||_.escape)&&D(false);},{isActive:w});let ye=Ss(i),ve=ee(i),de=i.settings.hostParticipantId===t,Ze=i.participants.find(I=>I.id===t),Ue=be(Ze??{}),et=useCallback(()=>{j(""),O(true);},[]),h=useCallback(I=>{O(false),j(""),T(Wt(I)),Y(""),ne(true);},[]),Z=useCallback(()=>{O(false),j(""),T(void 0);},[]),Eo=useCallback(I=>{ne(false),Y("");let[_,ke]=Xn(v,I);e.newRound(_,ke),T(void 0);},[e,v]),Ao=useCallback(()=>{ne(false),Y(""),T(void 0);},[]),No=useCallback(()=>{let I=e.current();!I||I.revealedAt===void 0||(X(I.id),B(I.title??""),L(true));},[e]),Po=useCallback(I=>{let _=z;if(L(false),X(null),!_)return;let ke=I.trim();ke!==""&&e.updateRoundDescriptor(_,{title:ke});},[e,z]),Do=useCallback(()=>{L(false),X(null);},[]),ko=useCallback(I=>{e.vote(I);},[e]),Oo=useCallback(()=>{e.unvote();},[e]),Mo=useCallback(()=>{e.reveal();},[e]),$o=useCallback(()=>{e.skip();},[e]),Bo=useCallback(()=>{j(""),O(true);},[]),Ho=useCallback(()=>{e.revote();},[e]),cn=useCallback(()=>{e.toggleRole();},[e]),Uo=useCallback(I=>{e.updateName(I);},[e]),jo=useCallback(I=>{e.updateScale(I);},[e]),Lo=useCallback(I=>{e.updateDisplayName(I),y(I);},[e]),_o=useCallback(async(I,_)=>{await e.updateAccess(I,_),p(I);},[e]),Fo=useCallback(I=>{try{e.handoffHost(I);}catch(_){console.error(`Handoff failed: ${_ instanceof Error?_.message:String(_)}`);}},[e]),dn=useCallback(()=>{R(true);},[]),Vo=useCallback(()=>{R(false);},[]),Yo=useCallback(()=>{e.endSessionForEveryone();},[e]),ln=useCallback(async I=>{let _=I==="code"?gt(e.roomCode):I==="url"?ht(e.roomCode):St(e.roomCode);try{await Co.write(_),ue.show("Copied!");}catch{ue.show(`Copy failed \u2014 ${_}`);}},[e.roomCode,ue]),Ot=i.rounds.filter(I=>W(I)!==null),Jo=Ot.length>0?Ot.at(-1):null,Wo=Ot.length>0,qo=useCallback(()=>uo(i,I=>Co.write(I),ce.show),[i,ce.show]);return jsxs(Box,{flexDirection:"column",children:[jsx(Zn,{roomCode:e.roomCode,sessionName:i.settings.name,status:l,isHost:de}),re.notice&&jsx(Box,{paddingX:1,children:jsx(Text,{color:"yellow",children:re.notice})}),ue.notice&&jsx(Box,{paddingX:1,children:jsx(Text,{color:ue.notice.startsWith("Copy failed")?"red":"green",children:ue.notice})}),ce.notice&&jsx(Box,{paddingX:1,children:jsx(Text,{color:ce.notice.startsWith("Clipboard write failed")?"red":"green",children:ce.notice})}),E?jsx(ao,{sessionName:i.settings.name,displayName:m,currentScale:i.settings.defaultScale,accessMode:$,presetOverrides:r,customScales:s,isHost:de,onChangeName:Uo,onChangeScale:jo,onChangeDisplayName:Lo,onChangeAccess:_o,onClose:Vo}):ye==="lobby"?jsx(ho,{projection:i,onlineIds:d,isHost:de,participantId:t,rounds:i.rounds,currentRound:Jo,settingsOpen:E,onStartRound:et,onNewRound:Bo,roundTitlePromptActive:g,roundTitleInput:b,onRoundTitleChange:j,onRoundTitleSubmit:h,onRoundTitleCancel:Z,roundTicketIdPromptActive:ae,roundTicketIdInput:oe,onRoundTicketIdChange:Y,onRoundTicketIdSubmit:Eo,onRoundTicketIdCancel:Ao,onRevote:Ho,onOpenSettings:dn,onHandoffHost:Fo,onEndSession:Yo,onShare:ln,shareOpen:he,onSetShareOpen:we,roomCode:e.roomCode,confirmingQuit:w,myRole:Ue,onToggleRole:cn,descriptorPromptActive:P,descriptorInput:k,onDescriptorChange:B,onDescriptorSubmit:Po,onDescriptorCancel:Do,onEditDescriptor:No,canExport:Wo,onExportResults:qo}):ye==="voting"&&ve?jsx(yo,{projection:i,round:ve,onlineIds:d,isHost:de,myId:t,myRole:Ue,elapsedMs:C,onVote:ko,onClearVote:Oo,onReveal:Mo,onSkip:$o,onToggleRole:cn,onShare:ln,onSetShareOpen:we,shareOpen:he,settingsOpen:E,onOpenSettings:dn,roomCode:e.roomCode,confirmingQuit:w}):null,w&&jsx(Box,{children:jsx(Text,{dimColor:true,children:"Leave session? y/n"})})]})}function bo({presetOverrides:e,customScales:t}){let{exit:o}=useApp(),[n,r]=useState("main-menu"),[s,c]=useState(Ce()),[i,u]=useState(s.displayName??""),[l,a]=useState(s.relayUrl??""),[d,f]=useState(s.defaultSessionName??""),[C,A]=useState(t??[]),[E,R]=useState(e??{});useInput(v=>{(v==="q"||v==="Q")&&o();},{isActive:n==="main-menu"});let $=useMemo(()=>lt(E,C),[E,C]),p=s.defaultScale,m=p?$.find(v=>v.id===p):void 0,y=[{label:"Display name",value:"display-name",detail:`[${s.displayName??"(none)"}]`},{label:"Relay URL",value:"relay-url",detail:`[${s.relayUrl??"(none)"}]`},{label:"Default scale",value:"default-scale",detail:`[${m?Ie(m.id,C):"(none)"}]`},{label:"Default session name",value:"default-session-name",detail:`[${s.defaultSessionName??"(none)"}]`},{label:"Default password",value:"default-password",detail:`[${s.defaultPassword?"on":"off"}]`}];function w(v){v==="display-name"?(u(s.displayName??""),r("editing-display-name")):v==="relay-url"?(a(s.relayUrl??""),r("editing-relay-url")):v==="default-scale"?r("picking-scale"):v==="default-session-name"?(f(s.defaultSessionName??""),r("editing-session-name")):v==="default-password"&&r("picking-default-password");}function D(v){let T=v.trim();if(T.length>0){let P={...s,displayName:T};c(P),J({displayName:T});}r("main-menu");}function g(v){let T=v.trim();if(T.length>0){let P={...s,relayUrl:T};c(P),J({relayUrl:T});}r("main-menu");}function O(v){let T=v.trim(),P=T.length>0?{...s,defaultSessionName:T}:{...s};T.length===0?(delete P.defaultSessionName,c(P),_e({defaultSessionName:""})):(c(P),J({defaultSessionName:T})),r("main-menu");}function b(v){let T=v.length>0?{...s,defaultScale:v}:{...s};v.length===0?(delete T.defaultScale,c(T),_e({defaultScale:""})):(c(T),J({defaultScale:v})),r("main-menu");}function j(v,T){let P={...E};T.length===0?delete P[v]:P[v]=T,R(P),J({presetOverrides:Object.keys(P).length>0?P:void 0});}function ae(v){let T=v==="on",P={...s,defaultPassword:T};c(P),J({defaultPassword:T}),r("main-menu");}function ne(v,T){let P=`custom-${randomUUID()}`,L=[...C,{id:P,label:v,cards:T}];A(L),c({...s,customScales:L}),J({customScales:L});}function oe(v,T,P){let L=C.map(k=>k.id===v?{id:v,label:T,cards:P}:k);A(L),c({...s,customScales:L}),J({customScales:L});}function Y(v){let T=C.filter(L=>L.id!==v);A(T);let P={...s,customScales:T};s.defaultScale===v?(P.defaultScale="fibonacci",J({customScales:T,defaultScale:"fibonacci"})):J({customScales:T}),c(P);}if(n==="main-menu")return jsx(pe,{title:"\u2500\u2500 Config Settings \u2500\u2500",items:y,onSelect:w,onClose:()=>o()});if(n==="editing-display-name")return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(Text,{children:"Display name: "}),jsx(q,{value:i,onChange:u,onSubmit:D,onCancel:()=>r("main-menu"),placeholder:"(required)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]});if(n==="editing-relay-url")return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(Text,{children:"Relay URL: "}),jsx(q,{value:l,onChange:a,onSubmit:g,onCancel:()=>r("main-menu"),placeholder:"(required)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]});if(n==="editing-session-name")return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(Text,{children:"Default session name: "}),jsx(q,{value:d,onChange:f,onSubmit:O,onCancel:()=>r("main-menu"),placeholder:"(leave empty to clear)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]});if(n==="picking-scale"){let v=[{id:"",cards:[]},...$];return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(yt,{scales:v,selectedScaleId:p??"",presetOverrides:E,customScales:C,onSelect:b,onOverrideChange:j,onCustomScaleCreate:ne,onCustomScaleEdit:oe,onCustomScaleDelete:Y,onClose:()=>r("main-menu"),showNoneOption:true})]})}if(n==="picking-default-password"){let v=[{label:"On",value:"on",detail:s.defaultPassword?"[current]":void 0},{label:"Off",value:"off",detail:s.defaultPassword?void 0:"[current]"}];return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(pe,{title:"Default password:",items:v,onSelect:ae,onClose:()=>r("main-menu")})]})}return jsx(Box,{})}async function an(){return new Promise(e=>{let t=createInterface({input:process.stdin,output:process.stderr});process.stderr.write("Password: "),t.question("",o=>{t.close(),e(o);});})}async function Ts(e){let t=await Le(),o=e.scale??t.defaultScale??"fibonacci",n=e.name??t.defaultSessionName,r=e.password??t.defaultPassword??false,s;r&&(s=await an());let c=dt(o,t.presetOverrides,t.customScales);if(!c){let d=Object.keys(se).join(", ");console.error(`Unknown or fully-disabled scale: "${o}". Builtin IDs: ${d}`),process.exit(1);}let i=await Jn({participantId:t.participantId,displayName:t.displayName,relayUrl:t.relayUrl,sessionName:n,password:s,defaultScale:c}),u;s&&(u=await Re(s,i.roomUuid)),pt({roomCode:i.roomCode,roomUuid:i.roomUuid,passwordHash:u,lastSeenAt:Date.now()}),console.error(`Room created: ${i.roomCode}`),console.error(`Share: hnch join ${i.roomCode}`),console.error("");let{waitUntilExit:l,unmount:a}=render(It.createElement(Pt,{session:i,participantId:t.participantId,displayName:t.displayName,accessMode:s?"password":"open",presetOverrides:t.presetOverrides,customScales:t.customScales}));mt({doc:i.doc,localParticipantId:t.participantId,onEnded:async d=>{console.error(d.isLocal?"Session ended.":`Session ended by ${d.hostName}.`),a();try{await F();}catch(f){console.error(`Failed to clear cached session: ${f instanceof Error?f.message:String(f)}`);}i.disconnect(),process.exit(0);}}),await l(),i.disconnect(),F(),process.exit(0);}async function Es(e){let t=await Le(),o=qn(e.code);o===null&&(console.error("invalid room code"),process.exit(1));let n;e.password&&(n=await an());let r=await Ye(t.relayUrl,o),s;r.accessMode==="password"&&!e.password?s=await an():e.password&&(s=n);let c,i=false,u=await Wn({participantId:t.participantId,displayName:t.displayName,relayUrl:t.relayUrl,roomCode:o,password:s,onRejection(d){if(d==="unknown-room"){ft({clearLastSession:F,logger:console,exit:process.exit});return}d==="room-full"&&(i=true,c?.());}}),l;s&&(l=await Re(s,u.roomUuid)),pt({roomCode:u.roomCode,roomUuid:u.roomUuid,passwordHash:l,lastSeenAt:Date.now()}),console.error(`Joined room: ${u.roomCode}`),console.error("");let a=render(It.createElement(Pt,{session:u,participantId:t.participantId,displayName:t.displayName,accessMode:r.accessMode,presetOverrides:t.presetOverrides,customScales:t.customScales}));c=a.unmount,mt({doc:u.doc,localParticipantId:t.participantId,onEnded:async d=>{console.error(d.isLocal?"Session ended.":`Session ended by ${d.hostName}.`),c?.();try{await F();}catch(f){console.error(`Failed to clear cached session: ${f instanceof Error?f.message:String(f)}`);}u.disconnect(),process.exit(0);}}),await a.waitUntilExit(),i&&(console.error("This room is full (max 20 participants)."),u.disconnect(),F(),process.exit(1)),u.disconnect(),F(),process.exit(0);}async function As(){let e=await Le(),t=zn();t||(console.error("No recent session to resume."),process.exit(0));let o;try{o=await Ye(e.relayUrl,t.roomCode);}catch(a){if((a instanceof Error?a.message:String(a)).includes("Room not found"))return await ft({clearLastSession:F,logger:console,exit:process.exit});console.error("Could not reach relay \u2014 try again later."),process.exit(0);}o.accessMode==="password"&&!t.passwordHash&&(console.error(`Password changed \u2014 rejoin with: hnch join ${t.roomCode} --password`),F(),process.exit(0)),console.error(`Rejoining room ${t.roomCode}\u2026`),console.error("");let n=new Oe.Doc,r=new Awareness(n);ct(n,{participantId:e.participantId,displayName:e.displayName}),r.setLocalStateField("participantId",e.participantId);let s=false,c=false,i,u=ut({doc:n,awareness:r,roomCode:t.roomCode,roomUuid:t.roomUuid,participantId:e.participantId,relayUrl:e.relayUrl,passwordHash:t.passwordHash,onRejection(a){if(a==="unknown-room"){ft({clearLastSession:F,logger:console,exit:process.exit});return}a==="wrong-password"&&(s=true,i?.()),a==="room-full"&&(c=true,i?.());}});pt({roomCode:u.roomCode,roomUuid:u.roomUuid,passwordHash:t.passwordHash,lastSeenAt:Date.now()});let l=render(It.createElement(Pt,{session:u,participantId:e.participantId,displayName:e.displayName,accessMode:o.accessMode,presetOverrides:e.presetOverrides,customScales:e.customScales}));i=l.unmount,mt({doc:u.doc,localParticipantId:e.participantId,onEnded:async a=>{console.error(a.isLocal?"Session ended.":`Session ended by ${a.hostName}.`),i?.();try{await F();}catch(d){console.error(`Failed to clear cached session: ${d instanceof Error?d.message:String(d)}`);}u.disconnect(),process.exit(0);}}),await l.waitUntilExit(),c&&(console.error("This room is full (max 20 participants)."),F(),process.exit(1)),s&&(console.error(`Password changed \u2014 rejoin with: hnch join ${t.roomCode} --password`),F(),process.exit(0)),u.disconnect(),F(),process.exit(0);}async function Ns(e){if(!(e.name!==void 0||e.relayUrl!==void 0||e.scale!==void 0||e.sessionName!==void 0||e.password!==void 0)){let n=await Le(),{waitUntilExit:r}=render(It.createElement(bo,{presetOverrides:n.presetOverrides,customScales:n.customScales}));await r(),process.exit(0);}let o={};if(e.name!==void 0&&(o.displayName=e.name),e.relayUrl!==void 0&&(o.relayUrl=e.relayUrl),e.scale!==void 0){if(e.scale!==""){let n=Ce();if(!dt(e.scale,n.presetOverrides,n.customScales)){console.error(`Unknown scale: "${e.scale}". Builtin IDs: ${Object.keys(se).join(", ")}`);return}}o.defaultScale=e.scale;}e.sessionName!==void 0&&(o.defaultSessionName=e.sessionName),e.password!==void 0&&(o.defaultPassword=e.password),_e(o),console.log("Config updated.");}var To=bs(hideBin(process.argv)).scriptName("hnch").usage("$0 <command> \u2014 terminal pointing poker").command("create","Create a new room",e=>e.option("name",{type:"string",describe:"Session name"}).option("password",{type:"boolean",describe:"Protect room with a password"}).option("scale",{type:"string",describe:"Estimation scale ID (fibonacci, t-shirt, powers-of-2, linear, risk, or custom-<id>)"}),e=>{Ts(e).catch(t=>{console.error(t instanceof Error?t.message:t),process.exit(1);});}).command("join <code>","Join an existing room",e=>e.positional("code",{type:"string",demandOption:true,describe:"6-character room code"}).option("password",{type:"boolean",describe:"Room requires a password",default:false}),e=>{Es(e).catch(t=>{console.error(t instanceof Error?t.message:t),process.exit(1);});}).command("resume","Rejoin your last session",e=>e,()=>{As().catch(e=>{console.error(e instanceof Error?e.message:e),process.exit(1);});}).command("config","View or update CLI config",e=>e.option("name",{type:"string",describe:"Set display name"}).option("relay-url",{type:"string",describe:"Set relay URL"}).option("scale",{type:"string",describe:"Set default estimation scale ID (empty string clears)"}).option("session-name",{type:"string",describe:"Set default session name (empty string clears)"}).option("password",{type:"boolean",describe:"Set default password prompt on (--password) or off (--no-password)"}),e=>{Ns(e).catch(t=>{console.error(t instanceof Error?t.message:t),process.exit(1);});}).demandCommand(1,"Specify a command: create, join, resume, or config").strict().help(),sn=hideBin(process.argv);(sn.length===0||sn.length===1&&sn[0]==="--")&&(To.showHelp(),process.exit(0));To.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xauyxau/hnch",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "Pointing poker CLI for agile teams",
5
5
  "keywords": [
6
6
  "pointing-poker",