@xauyxau/hnch 1.12.0 → 1.14.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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +16 -12
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -17,7 +17,7 @@ Terminal client for hnch (pointing poker for agile teams). Speaks the same Yjs-o
17
17
 
18
18
  ## Default relay
19
19
 
20
- `wss://hnch.dev`. Override with the `HNCH_RELAY_URL` env var or `hnch config --relay-url <url>`.
20
+ `https://hnch.dev`. Override with the `HNCH_RELAY_URL` env var or `hnch config --relay-url <url>`.
21
21
 
22
22
  ## License
23
23
 
package/dist/index.js CHANGED
@@ -1,21 +1,25 @@
1
1
  #!/usr/bin/env node
2
- import Os from'yargs';import {hideBin}from'yargs/helpers';import zt,{useState,useEffect,useCallback,useMemo,useRef}from'react';import {render,useApp,useInput,Box,Text}from'ink';import*as $e 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 Pi from'ws';import or from'clipboardy';import {jsxs,jsx,Fragment}from'react/jsx-runtime';var It="hnch_abstain";function me(e){return e===It}var Yr={"unknown-room":4001,"wrong-password":4002,"invalid-invite":4003,"expired-invite":4004,"invite-already-redeemed":4005,"room-full":4006,"malformed-credentials":4010},_n=new Map(Object.entries(Yr).map(([e,t])=>[t,e]));var Ge={"racing-green":{base:"#0B3D2E",soft:"rgba(11,61,46,0.14)",ink:"#fff",label:"Racing Green"},dijon:{base:"#C9A227",soft:"rgba(201,162,39,0.18)",ink:"#000",label:"Dijon"},blue:{base:"#0A84FF",soft:"rgba(10,132,255,0.14)",ink:"#fff",label:"Blue"},indigo:{base:"#5E5CE6",soft:"rgba(94,92,230,0.14)",ink:"#fff",label:"Indigo"},violet:{base:"#BF5AF2",soft:"rgba(191,90,242,0.14)",ink:"#fff",label:"Violet"},pink:{base:"#FF375F",soft:"rgba(255,55,95,0.14)",ink:"#fff",label:"Pink"},red:{base:"#FF453A",soft:"rgba(255,69,58,0.14)",ink:"#fff",label:"Red"},orange:{base:"#FF9F0A",soft:"rgba(255,159,10,0.16)",ink:"#000",label:"Orange"},amber:{base:"#FFD60A",soft:"rgba(255,214,10,0.18)",ink:"#000",label:"Amber"},green:{base:"#30D158",soft:"rgba(48,209,88,0.14)",ink:"#000",label:"Green"},teal:{base:"#40C8E0",soft:"rgba(64,200,224,0.14)",ink:"#000",label:"Teal"},graphite:{base:"#1C1C1E",soft:"rgba(28,28,30,0.10)",ink:"#fff",label:"Graphite"}};var it=["racing-green","dijon","blue","indigo","violet","pink","red","orange","amber","green","teal","graphite"],Jr=/^#[0-9a-fA-F]{6}$/;function Kr(e){return Object.hasOwn(Ge,e)}function Oe(e){return e?Kr(e)?Ge[e].base:Jr.test(e)?e.toLowerCase():null:null}function Fn(e){let t=5381;for(let o=0;o<e.length;o++)t=(t<<5)+t+e.charCodeAt(o)|0;let n=Math.abs(t)%it.length;return it[n]}function Me(e){return e.role??"voter"}function J(e){let{revealedAt:t,skippedAt:n}=e;return t===void 0&&n===void 0?null:t===void 0?"skipped":n===void 0||t<=n?"revealed":"skipped"}var At={id:"fibonacci",cards:["0","1","2","3","5","8","13","21"]},Vn={id:"t-shirt",cards:["XS","S","M","L","XL"]},Yn={id:"powers-of-2",cards:["1","2","4","8","16","32","64"]},Jn={id:"linear",cards:["1","2","3","4","5","6","7","8","9","10"]},Kn={id:"risk",cards:["Low","Medium","High","Critical"]},se={[At.id]:At,[Vn.id]:Vn,[Yn.id]:Yn,[Jn.id]:Jn,[Kn.id]:Kn},Wn={fibonacci:"Fibonacci","t-shirt":"T-shirt","powers-of-2":"Powers of 2",linear:"Linear (1\u201310)",risk:"Risk"};function Gn(e){return e in Wn?Wn[e]:e.length===0?e:(e.startsWith("custom-")?e.slice(7):e).split("-").map(n=>n.length===0?n:n.charAt(0).toUpperCase()+n.slice(1)).join(" ")}var Ae="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",Tt=Ae.length,cn=Math.floor(Tt/2);function st(e){let t=Ae.indexOf(e);if(t===-1)throw new Error(`fractional-index: invalid character '${e}'`);return t}function qn(e,t){return e.length>=t?e:e+"0".repeat(t-e.length)}function Wr(e){let t=e.length-1;for(;t>0&&e[t]==="0";)t--;return e.slice(0,t+1)}function Gr(e,t){let n=e.length,o=Array.from({length:n+1}).fill(0),r=0;for(let a=n-1;a>=0;a--){let l=e[a]+t[a]+r;o[a+1]=l%Tt,r=Math.floor(l/Tt);}o[0]=r;let i=Array.from({length:n+1}).fill(0),s=0;for(let a=0;a<=n;a++){let l=o[a]+s*Tt;i[a]=Math.floor(l/2),s=l%2;}return i.slice(1)}function ln(e,t){let n=Ae[cn];if(e===null&&t===null)return n+n;if(t===null)return e+n+n+n+n;if(e===null)return qr(t);if(e>=t)throw new Error(`keyBetween: a must be < b (a='${e}', b='${t}')`);return zr(e,t)}function qr(e){for(let t=e.length-1;t>=0;t--){let n=st(e[t]);if(n>0)return e.slice(0,t)+Ae[n-1]+Ae[cn]}if(e.length>1)return e.slice(0,-1);throw new Error(`keyBetween: cannot generate key before '${e}' \u2014 use keyBetween(null, null) for the initial key`)}function zr(e,t){let n=Math.max(e.length,t.length),o=qn(e,n).split("").map(l=>st(l)),r=qn(t,n).split("").map(l=>st(l)),s=Gr(o,r).map(l=>Ae[l]).join(""),a=Wr(s);return a>e&&a<t?a:s>e&&s<t?s:Xr(e,t)}function Xr(e,t){for(let n=0;n<t.length;n++){let o=n<e.length?st(e[n]):0;if(st(t[n])===o+1)return (n<e.length?e.slice(0,n+1):e+Ae[0])+Ae[cn]}throw new Error(`keyBetween: space exhausted between '${e}' and '${t}'`)}var Qr="meta",at="settings",Zr="participants",ei="rounds",ti="agenda",ni=["roomUuid","createdAt","createdBy","sessionEndedAt"],oi=["name","defaultScale","hostParticipantId"],ri=["displayName","joinedAt","role","leftAt","removedBy","avatarColor"],ii=["id","title","description","ticketId","state","source","externalId","sortKey","createdAt","consumedAt","consumedByRoundId"],si=["id","title","description","ticketId","parentRoundId","scale","timerStartedAt","revealedAt","skippedAt","createdBy","createdAt","agendaItemId"];function Rt(e){return e.getMap(Qr)}function kt(e){return e.getMap(at)}function Nt(e){return e.getMap(Zr)}function un(e){return e.getArray(ei)}function Et(e){return e.getArray(ti)}function dt(e,t){let n={};for(let o of t){let r=e.get(o);r!==void 0&&(n[o]=r);}return n}function ai(e){return dt(Rt(e),ni)}function di(e){return dt(kt(e),oi)}function ci(e){let t=Nt(e),n=[];return t.forEach((o,r)=>{let i=dt(o,ri);n.push({id:r,...i});}),n.sort((o,r)=>o.joinedAt-r.joinedAt||o.id.localeCompare(r.id)),n}function li(e){let t={};return e.forEach((n,o)=>{t[o]=n;}),t}function ui(e){let t=dt(e,si),n=e.get("votes"),o=n?li(n):{};return {...t,votes:o}}function pi(e){let t=un(e),n=[];return t.forEach(o=>{n.push(ui(o));}),n}function fi(e){return dt(e,ii)}function mi(e){let t=Et(e),n=[];return t.forEach(o=>{n.push(fi(o));}),n.sort((o,r)=>o.sortKey.localeCompare(r.sortKey)),n}function K(e){return {meta:ai(e),settings:di(e),participants:ci(e),rounds:pi(e),agenda:mi(e)}}function zn(e,t){if(Rt(e).has("roomUuid"))throw new Error("initDoc: doc already seeded");e.transact(()=>{let n=Rt(e);n.set("roomUuid",t.roomUuid),n.set("createdAt",t.createdAt),n.set("createdBy",t.participantId);let o=kt(e);t.sessionName!==void 0&&o.set("name",t.sessionName),o.set("defaultScale",t.defaultScale),o.set("hostParticipantId",t.participantId);let r=new $e.Map;r.set("displayName",t.displayName),r.set("joinedAt",t.createdAt),t.avatarColor!==void 0&&r.set("avatarColor",t.avatarColor),Nt(e).set(t.participantId,r);});}function qe(e,t){e.transact(()=>{let n=Nt(e),o=n.get(t.participantId),r=o??new $e.Map;r.set("displayName",t.displayName),o===void 0&&r.set("joinedAt",Date.now()),t.avatarColor!==void 0&&r.set("avatarColor",t.avatarColor),r.delete("leftAt"),r.delete("removedBy"),o===void 0&&n.set(t.participantId,r);});}function ze(e,t){e.transact(()=>{let n=kt(e);for(let[o,r]of Object.entries(t))r===void 0?n.delete(o):n.set(o,r);});}function pn(e,t,n){e.transact(()=>{let o=Nt(e),r=o.get(t),i=r??new $e.Map;i.set("displayName",n.displayName),i.set("joinedAt",n.joinedAt),n.role!==void 0&&i.set("role",n.role),n.avatarColor!==void 0&&i.set("avatarColor",n.avatarColor),r===void 0&&o.set(t,i);});}function fn(e,t){let n=t.scale??kt(e).get("defaultScale");if(!n)throw new Error("appendRound: no scale (settings.defaultScale missing)");return e.transact(()=>{let o=new $e.Map;o.set("id",t.id),o.set("createdAt",t.createdAt),o.set("createdBy",t.createdBy),o.set("scale",n),t.title!==void 0&&o.set("title",t.title),t.description!==void 0&&o.set("description",t.description),t.ticketId!==void 0&&o.set("ticketId",t.ticketId),t.parentRoundId!==void 0&&o.set("parentRoundId",t.parentRoundId),t.agendaItemId!==void 0&&o.set("agendaItemId",t.agendaItemId),o.set("timerStartedAt",t.createdAt),o.set("votes",new $e.Map),un(e).push([o]);}),t.id}function Xn(e,t,n){let o=ct(e,t);if(!o)throw new Error(`patchRoundDescriptor: round ${t} not found`);e.transact(()=>{n.title!==void 0&&o.set("title",n.title),n.description!==void 0&&o.set("description",n.description);});}function ct(e,t){let n=un(e);for(let o=0;o<n.length;o+=1){let r=n.get(o);if(r.get("id")===t)return r}}function Qn(e,t,n,o){let r=ct(e,t);if(!r)throw new Error(`castVote: round ${t} not found`);r.get("revealedAt")===void 0&&e.transact(()=>{r.get("votes").set(n,o);});}function Zn(e,t,n){let o=ct(e,t);if(!o)throw new Error(`clearVote: round ${t} not found`);o.get("revealedAt")===void 0&&e.transact(()=>{o.get("votes").delete(n);});}function mn(e,t,n){let o=ct(e,t);if(!o)throw new Error(`revealRound: round ${t} not found`);if(o.get("skippedAt")!==void 0)throw new Error(`revealRound: round ${t} already skipped`);e.transact(()=>{o.set("revealedAt",n);});}function eo(e,t,n){let o=ct(e,t);if(!o)throw new Error(`skipRound: round ${t} not found`);if(o.get("revealedAt")!==void 0)throw new Error(`skipRound: round ${t} already revealed`);if(o.get("skippedAt")!==void 0)throw new Error(`skipRound: round ${t} already skipped`);e.transact(()=>{o.set("skippedAt",n);});}function to(e){e.transact(()=>{Rt(e).set("sessionEndedAt",Date.now());});}function te(e){let{rounds:t}=e;if(t.length===0)return null;for(let n=t.length-1;n>=0;n-=1){let o=t[n];if(o.revealedAt===void 0)return o}return t.at(-1)}function no(e){let t=Object.values(e.votes);return t.length<2?false:t.every(n=>n===t[0])}function oo(e,t){let n=Et(e);for(let o=0;o<n.length;o++){let r=n.get(o);if(r.get("id")===t)return r}}function Pt(e,t){if(!t.title?.trim()&&!t.ticketId?.trim())throw new Error("appendAgendaItem: at least one of {title, ticketId} is required");let n=crypto.randomUUID(),o=Et(e),r=null;o.forEach(s=>{if(s.get("state")==="pending"){let a=s.get("sortKey");a!==void 0&&(r===null||a>r)&&(r=a);}});let i=ln(r,null);return e.transact(()=>{let s=new $e.Map;s.set("id",n),s.set("state","pending"),s.set("source",t.source??"manual"),s.set("sortKey",i),s.set("createdAt",t.createdAt),t.title!==void 0&&s.set("title",t.title),t.description!==void 0&&s.set("description",t.description),t.ticketId!==void 0&&s.set("ticketId",t.ticketId),t.externalId!==void 0&&s.set("externalId",t.externalId),Et(e).push([s]);}),n}function Dt(e,t,n,o={}){let r=oo(e,t);if(!r)throw new Error(`setAgendaItemState: item ${t} not found`);let i=r.get("state");if(!(i===n&&n==="consumed")){if(i!=="pending")throw new Error(`setAgendaItemState: invalid transition '${i}' \u2192 '${n}' for item ${t}`);e.transact(()=>{r.set("state",n),n==="consumed"&&(r.set("consumedAt",o.timestamp??Date.now()),o.consumedByRoundId!==void 0&&r.set("consumedByRoundId",o.consumedByRoundId));});}}function ro(e,t,n,o){Dt(e,t,"consumed",{consumedByRoundId:n,timestamp:o});}function io(e,t,n,o){let r=oo(e,t);if(!r)throw new Error(`reorderAgendaItem: item ${t} not found`);let i=r.get("state");if(i!=="pending")throw new Error(`reorderAgendaItem: item ${t} is ${i}; only pending items may be reordered`);let s=ln(n??null,o??null);e.transact(()=>{r.set("sortKey",s);});}function Xe(e){for(let t of e.agenda)if(t.state==="pending")return t;return null}function so(e){let t=Math.max(0,Math.floor(e/1e3)),n=Math.floor(t/60),o=t%60;return `${n}:${o.toString().padStart(2,"0")}`}function ao(e){try{let t=new URL(e);return t.protocol==="http:"||t.protocol==="https:"}catch{return false}}function co(e,t){let n=new Map;for(let d of t)n.set(d.id,d);let o=n.get(e);if(o===void 0||o.parentRoundId===void 0)return null;let r=new Set([o.id]),i=o,s=1;for(;i.parentRoundId!==void 0;){let d=n.get(i.parentRoundId);if(d===void 0||r.has(d.id))return null;r.add(d.id),i=d,s+=1;}let a=i,l=new Map;for(let d of t){if(d.parentRoundId===void 0)continue;let p=l.get(d.parentRoundId);p===void 0?l.set(d.parentRoundId,[d]):p.push(d);}let u=0,c=[a];for(;c.length>0;){let d=c.shift();u+=1;let p=l.get(d.id);p!==void 0&&c.push(...p);}return {chainRoot:a,depth:s,total:u}}var lo="PBKDF2",gi="SHA-256",uo=new TextEncoder;function hi(e){return Array.from(new Uint8Array(e)).map(t=>t.toString(16).padStart(2,"0")).join("")}async function Be(e,t){let n=await crypto.subtle.importKey("raw",uo.encode(e),lo,false,["deriveBits"]),o=await crypto.subtle.deriveBits({name:lo,salt:uo.encode(t),iterations:1e5,hash:gi},n,256);return hi(o)}var po="ABCDEFGHJKMNPQRSTUVWXYZ23456789";if(po.length!==31)throw new Error(`Room code alphabet must be 31 characters, got ${po.length}`);function gn(e){return e.trim().replaceAll(/-/g,"").toUpperCase()}function fo(e,t=true){let n=gn(e);return !t||n.length!==6?n:`${n.slice(0,3)}-${n.slice(3)}`}function mo(e){let t=new Set(e).size;return t===1?"unanimous":t===2?"close":"spread"}function xi(e,t){let n=new Map;t.forEach((i,s)=>n.set(i,s));let o=[...new Set(e.filter(i=>!n.has(i)))].toSorted(),r=new Map;return o.forEach((i,s)=>r.set(i,t.length+s)),i=>n.get(i)??r.get(i)??Number.MAX_SAFE_INTEGER}function hn(e,t){let n=new Map;for(let s of e)n.set(s,(n.get(s)??0)+1);let o=null,r=0,i=[...n.keys()].toSorted((s,a)=>t(s)-t(a));for(let s of i){let a=n.get(s);a>r&&(r=a,o=s);}return o}function go(e,t){let n=Object.values(e);if(n.length===0)return null;let o=xi(n,t.cards);if(t.cards.every(s=>Number.isFinite(parseFloat(s)))){let s=n.map(parseFloat).filter(Number.isFinite);if(s.length===0)return {kind:"categorical",mode:hn(n,o),spread:mo(n)};let a=s.reduce((R,k)=>R+k,0)/s.length,l=[...s].toSorted((R,k)=>R-k),u=Math.floor(l.length/2),c=l.length%2===1?l[u]:(l[u-1]+l[u])/2,d=hn(n,o),p;if(new Set(n).size===1)p="unanimous";else {let R=n.map(A=>o(A)),k=Math.min(...R);p=Math.max(...R)-k<=1?"close":"spread";}return {kind:"numeric",mean:a,median:c,mode:d,spread:p}}return {kind:"categorical",mode:hn(n,o),spread:mo(n)}}function ho(e){return e.replaceAll(/\|/g,"\\|")}function Si(e){return e.replaceAll(/\\/g,"\\\\").replaceAll(/]/g,"\\]")}function wi(e){let n=(e.match(/`+/g)??[]).reduce((o,r)=>Math.max(o,r.length),0);return "`".repeat(n+1)}function xn(e){if(!e)return "";if(ao(e))return ` ([${Si(e)}](<${e}>))`;let t=wi(e);return ` (${t}${e}${t})`}function yi(e){return e instanceof Map?e:new Map(Object.entries(e))}function Ci(e){let t=new Map;for(let r of e)t.set(r.id,r);function n(r){if(!r.parentRoundId)return r;let i=t.get(r.parentRoundId);return i?n(i):r}let o=new Map;for(let r of e){let i=n(r),s=o.get(i.id);s?s.push(r):o.set(i.id,[r]);}return o}function xo(e,t){let n=new Map;e.scale.cards.forEach((s,a)=>n.set(s,a));let o=new Map;for(let[s,a]of Object.entries(e.votes)){let l=t.get(s)??s,u=o.get(a);u?u.push(l):o.set(a,[l]);}let r=[...o.entries()].map(([s,a])=>({card:s,scaleIndex:n.get(s)??Number.MAX_SAFE_INTEGER,voters:[...a].toSorted().map(ho)}));return r.sort((s,a)=>{let l=a.voters.length-s.voters.length;return l!==0?l:s.scaleIndex-a.scaleIndex}),["| Vote | Voters |","|------|--------|",...r.map(s=>`| ${ho(s.card)} | ${s.voters.join(", ")} |`)].join(`
3
- `)}function So(e){let t=go(e.votes,e.scale),n=e.timerStartedAt!=null&&e.revealedAt!=null?so(e.revealedAt-e.timerStartedAt):null,o=[];return t?.kind==="numeric"?o.push(`Avg ${t.mean.toFixed(1)} \xB7 Med ${t.median}`):t?.kind==="categorical"&&o.push(`Most common: ${t.mode}`),n!=null&&o.push(`Elapsed ${n}`),o.join(" \xB7 ")}function wo(e){let{rounds:t}=e,n=yi(e.participants),o=new Map;for(let[c,d]of n)o.set(c,d.displayName);let r=t.filter(c=>c.revealedAt!=null);if(r.length===0)return "";let i=new Map;t.forEach((c,d)=>i.set(c.id,d+1));let s=Ci(r),a=new Set,l=[];for(let c of r)for(let[d,p]of s)if(p.includes(c)&&!a.has(d)){a.add(d),l.push(d);break}let u=[];for(let c of l){let d=s.get(c),p=d.find(g=>g.id===c),R=d.filter(g=>g.id!==c),k=i.get(p.id)??0,T=p.title?`## Round ${k} \u2014 ${p.title}${xn(p.ticketId)}`:`## Round ${k}${xn(p.ticketId)}`,A=xo(p,o),B=So(p),f=B?`${T}
2
+ import oa from'yargs';import {hideBin}from'yargs/helpers';import Zt,{useState,useEffect,useCallback,useMemo,useRef}from'react';import {render,useApp,useInput,Box,Text}from'ink';import*as $e from'yjs';import {Awareness}from'y-protocols/awareness';import {existsSync,readFileSync,writeFileSync,unlinkSync,mkdirSync,chmodSync}from'fs';import {homedir}from'os';import {join}from'path';import {randomUUID}from'crypto';import {createInterface}from'readline';import {WebsocketProvider}from'y-websocket';import os from'ws';import Ar from'clipboardy';import {jsxs,jsx,Fragment}from'react/jsx-runtime';var Tt="hnch_abstain";function J(e){return e===Tt}var gi={"unknown-room":4001,"wrong-password":4002,"invalid-invite":4003,"expired-invite":4004,"invite-already-redeemed":4005,"room-full":4006,"malformed-credentials":4010},to=new Map(Object.entries(gi).map(([e,t])=>[t,e]));function Ae(e){return e.role??"voter"}function Et(e){return e.leftAt===void 0}var Rt={id:"fibonacci",cards:["0","1","2","3","5","8","13","21"]},no={id:"t-shirt",cards:["XS","S","M","L","XL"]},oo={id:"powers-of-2",cards:["1","2","4","8","16","32","64"]},ro={id:"linear",cards:["1","2","3","4","5","6","7","8","9","10"]},io={id:"risk",cards:["Low","Medium","High","Critical"]},ae={[Rt.id]:Rt,[no.id]:no,[oo.id]:oo,[ro.id]:ro,[io.id]:io},so={fibonacci:"Fibonacci","t-shirt":"T-shirt","powers-of-2":"Powers of 2",linear:"Linear (1\u201310)",risk:"Risk"};function ao(e){return e in so?so[e]:e.length===0?e:(e.startsWith("custom-")?e.slice(7):e).split("-").map(n=>n.length===0?n:n.charAt(0).toUpperCase()+n.slice(1)).join(" ")}function co(e,t,n){if(!e||e.status!=="voting"&&e.status!=="pending")return false;let o=new Set(t.filter(d=>Et(d)&&n.has(d.id)&&Ae(d)!=="viewer").map(d=>d.id));if(o.size===0)return false;let r=Object.entries(e.votes).filter(([d])=>o.has(d)),i=r.filter(([,d])=>J(d)).length,s=r.length-i,a=o.size-i;return a<=0?false:s>=a}var ze={"racing-green":{base:"#0B3D2E",soft:"rgba(11,61,46,0.14)",ink:"#fff",label:"Racing Green"},dijon:{base:"#C9A227",soft:"rgba(201,162,39,0.18)",ink:"#000",label:"Dijon"},blue:{base:"#0A84FF",soft:"rgba(10,132,255,0.14)",ink:"#fff",label:"Blue"},indigo:{base:"#5E5CE6",soft:"rgba(94,92,230,0.14)",ink:"#fff",label:"Indigo"},violet:{base:"#BF5AF2",soft:"rgba(191,90,242,0.14)",ink:"#fff",label:"Violet"},pink:{base:"#FF375F",soft:"rgba(255,55,95,0.14)",ink:"#fff",label:"Pink"},red:{base:"#FF453A",soft:"rgba(255,69,58,0.14)",ink:"#fff",label:"Red"},orange:{base:"#FF9F0A",soft:"rgba(255,159,10,0.16)",ink:"#000",label:"Orange"},amber:{base:"#FFD60A",soft:"rgba(255,214,10,0.18)",ink:"#000",label:"Amber"},green:{base:"#30D158",soft:"rgba(48,209,88,0.14)",ink:"#000",label:"Green"},teal:{base:"#40C8E0",soft:"rgba(64,200,224,0.14)",ink:"#000",label:"Teal"},graphite:{base:"#1C1C1E",soft:"rgba(28,28,30,0.10)",ink:"#fff",label:"Graphite"}};var dt=["racing-green","dijon","blue","indigo","violet","pink","red","orange","amber","green","teal","graphite"],hi=/^#[0-9a-fA-F]{6}$/;function Si(e){return Object.hasOwn(ze,e)}function Me(e){return e?Si(e)?ze[e].base:hi.test(e)?e.toLowerCase():null:null}function lo(e){let t=5381;for(let o=0;o<e.length;o++)t=(t<<5)+t+e.charCodeAt(o)|0;let n=Math.abs(t)%dt.length;return dt[n]}var Te="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",kt=Te.length,pn=Math.floor(kt/2);function ct(e){let t=Te.indexOf(e);if(t===-1)throw new Error(`fractional-index: invalid character '${e}'`);return t}function uo(e,t){return e.length>=t?e:e+"0".repeat(t-e.length)}function xi(e){let t=e.length-1;for(;t>0&&e[t]==="0";)t--;return e.slice(0,t+1)}function wi(e,t){let n=e.length,o=Array.from({length:n+1}).fill(0),r=0;for(let a=n-1;a>=0;a--){let d=e[a]+t[a]+r;o[a+1]=d%kt,r=Math.floor(d/kt);}o[0]=r;let i=Array.from({length:n+1}).fill(0),s=0;for(let a=0;a<=n;a++){let d=o[a]+s*kt;i[a]=Math.floor(d/2),s=d%2;}return i.slice(1)}function fn(e,t){let n=Te[pn];if(e===null&&t===null)return n+n;if(t===null)return e+n+n+n+n;if(e===null)return yi(t);if(e>=t)throw new Error(`keyBetween: a must be < b (a='${e}', b='${t}')`);return vi(e,t)}function yi(e){for(let t=e.length-1;t>=0;t--){let n=ct(e[t]);if(n>0)return e.slice(0,t)+Te[n-1]+Te[pn]}if(e.length>1)return e.slice(0,-1);throw new Error(`keyBetween: cannot generate key before '${e}' \u2014 use keyBetween(null, null) for the initial key`)}function vi(e,t){let n=Math.max(e.length,t.length),o=uo(e,n).split("").map(d=>ct(d)),r=uo(t,n).split("").map(d=>ct(d)),s=wi(o,r).map(d=>Te[d]).join(""),a=xi(s);return a>e&&a<t?a:s>e&&s<t?s:Ci(e,t)}function Ci(e,t){for(let n=0;n<t.length;n++){let o=n<e.length?ct(e[n]):0;if(ct(t[n])===o+1)return (n<e.length?e.slice(0,n+1):e+Te[0])+Te[pn]}throw new Error(`keyBetween: space exhausted between '${e}' and '${t}'`)}function mn(e){let{revealedAt:t,skippedAt:n,discardedAt:o}=e;if(t===void 0&&n===void 0&&o===void 0)return null;let r=[];t!==void 0&&r.push({kind:"revealed",at:t}),n!==void 0&&r.push({kind:"skipped",at:n}),o!==void 0&&r.push({kind:"discarded",at:o});let i={revealed:0,skipped:1,discarded:2};return r.sort((s,a)=>s.at-a.at||i[s.kind]-i[a.kind]),r[0].kind}var bi="meta",Dt="settings",Ii="participants",Ai="rounds",Ti="agenda",Ri=["roomUuid","createdAt","createdBy","sessionEndedAt"],Ei=["name","defaultScale","hostParticipantId"],ki=["displayName","joinedAt","role","leftAt","removedBy","avatarColor"],Ni=["id","title","description","ticketId","state","source","externalId","sortKey","createdAt","consumedAt","consumedByRoundId"],Pi=["id","title","description","ticketId","parentRoundId","scale","timerStartedAt","revealedAt","skippedAt","discardedAt","discardedBy","createdBy","createdAt","agendaItemId"];function Nt(e){return e.getMap(bi)}function Ot(e){return e.getMap(Dt)}function Mt(e){return e.getMap(Ii)}function gn(e){return e.getArray(Ai)}function Pt(e){return e.getArray(Ti)}function lt(e,t){let n={};for(let o of t){let r=e.get(o);r!==void 0&&(n[o]=r);}return n}function Di(e){return lt(Nt(e),Ri)}function Oi(e){return lt(Ot(e),Ei)}function Mi(e){let t=Mt(e),n=[];return t.forEach((o,r)=>{let i=lt(o,ki);n.push({id:r,...i});}),n.sort((o,r)=>o.joinedAt-r.joinedAt||o.id.localeCompare(r.id)),n}function $i(e){let t={};return e.forEach((n,o)=>{t[o]=n;}),t}function Ui(e){let t=lt(e,Pi),n=e.get("votes"),o=n?$i(n):{},r=mn(t),{revealedAt:i,skippedAt:s,discardedAt:a,...d}=t,u=r??(t.timerStartedAt!==void 0?"voting":"pending"),c=r==="revealed"?i:r==="skipped"?s:r==="discarded"?a:void 0;return c===void 0?{...d,votes:o,status:u}:{...d,votes:o,status:u,terminalAt:c}}function Bi(e){let t=gn(e),n=[];return t.forEach(o=>{n.push(Ui(o));}),n}function Hi(e){return lt(e,Ni)}function Li(e){let t=Pt(e),n=[];return t.forEach(o=>{n.push(Hi(o));}),n.sort((o,r)=>o.sortKey.localeCompare(r.sortKey)),n}function j(e){return {meta:Di(e),settings:Oi(e),participants:Mi(e),rounds:Bi(e),agenda:Li(e)}}function po(e,t){if(Nt(e).has("roomUuid"))throw new Error("initDoc: doc already seeded");e.transact(()=>{let n=Nt(e);n.set("roomUuid",t.roomUuid),n.set("createdAt",t.createdAt),n.set("createdBy",t.participantId);let o=Ot(e);t.sessionName!==void 0&&o.set("name",t.sessionName),o.set("defaultScale",t.defaultScale),o.set("hostParticipantId",t.participantId);let r=new $e.Map;r.set("displayName",t.displayName),r.set("joinedAt",t.createdAt),t.avatarColor!==void 0&&r.set("avatarColor",t.avatarColor),Mt(e).set(t.participantId,r);});}function Xe(e,t){e.transact(()=>{let n=Mt(e),o=n.get(t.participantId),r=o??new $e.Map;r.set("displayName",t.displayName),o===void 0&&r.set("joinedAt",Date.now()),t.avatarColor!==void 0&&r.set("avatarColor",t.avatarColor),r.delete("leftAt"),r.delete("removedBy"),o===void 0&&n.set(t.participantId,r);});}function Qe(e,t){e.transact(()=>{let n=Ot(e);for(let[o,r]of Object.entries(t))r===void 0?n.delete(o):n.set(o,r);});}function hn(e,t,n){e.transact(()=>{let o=Mt(e),r=o.get(t),i=r??new $e.Map;i.set("displayName",n.displayName),i.set("joinedAt",n.joinedAt),n.role!==void 0&&i.set("role",n.role),n.avatarColor!==void 0&&i.set("avatarColor",n.avatarColor),r===void 0&&o.set(t,i);});}function Sn(e,t){let n=t.scale??Ot(e).get("defaultScale");if(!n)throw new Error("appendRound: no scale (settings.defaultScale missing)");if(t.parentRoundId!==void 0){let o=Ue(e,t.parentRoundId);if(o!==void 0&&o.get("discardedAt")!==void 0)throw new Error(`appendRound: parentRoundId ${t.parentRoundId} points at a discarded round`)}return e.transact(()=>{let o=new $e.Map;o.set("id",t.id),o.set("createdAt",t.createdAt),o.set("createdBy",t.createdBy),o.set("scale",n),t.title!==void 0&&o.set("title",t.title),t.description!==void 0&&o.set("description",t.description),t.ticketId!==void 0&&o.set("ticketId",t.ticketId),t.parentRoundId!==void 0&&o.set("parentRoundId",t.parentRoundId),t.agendaItemId!==void 0&&o.set("agendaItemId",t.agendaItemId),o.set("timerStartedAt",t.createdAt),o.set("votes",new $e.Map),gn(e).push([o]);}),t.id}function fo(e,t,n){let o=Ue(e,t);if(!o)throw new Error(`patchRoundDescriptor: round ${t} not found`);e.transact(()=>{n.title!==void 0&&o.set("title",n.title),n.description!==void 0&&o.set("description",n.description);});}function Ue(e,t){let n=gn(e);for(let o=0;o<n.length;o+=1){let r=n.get(o);if(r.get("id")===t)return r}}function xn(e){return mn({revealedAt:e.get("revealedAt"),skippedAt:e.get("skippedAt"),discardedAt:e.get("discardedAt")})}function mo(e,t,n,o){let r=Ue(e,t);if(!r)throw new Error(`castVote: round ${t} not found`);r.get("revealedAt")===void 0&&e.transact(()=>{r.get("votes").set(n,o);});}function go(e,t,n){let o=Ue(e,t);if(!o)throw new Error(`clearVote: round ${t} not found`);o.get("revealedAt")===void 0&&e.transact(()=>{o.get("votes").delete(n);});}function ho(e,t,n,o){let r=t.get("agendaItemId");if(r===void 0)return;let i=yn(e,r);i!==void 0&&i.get("state")==="pending"&&(i.set("state","consumed"),i.set("consumedAt",o),i.set("consumedByRoundId",n));}function wn(e,t,n){let o=Ue(e,t);if(!o)throw new Error(`revealRound: round ${t} not found`);let r=xn(o);if(r!==null)throw new Error(`revealRound: round ${t} already terminal (${r})`);e.transact(()=>{o.set("revealedAt",n),ho(e,o,t,n);});}function So(e,t,n){let o=Ue(e,t);if(!o)throw new Error(`skipRound: round ${t} not found`);let r=xn(o);if(r!==null)throw new Error(`skipRound: round ${t} already terminal (${r})`);e.transact(()=>{o.set("skippedAt",n),ho(e,o,t,n);});}function xo(e,t,n,o){let r=Ue(e,t);if(!r)throw new Error(`discardRound: round ${t} not found`);let i=xn(r);if(i!==null)throw new Error(`discardRound: round ${t} already terminal (${i})`);e.transact(()=>{r.set("discardedAt",n),r.set("discardedBy",o);});}function wo(e){e.transact(()=>{Nt(e).set("sessionEndedAt",Date.now());});}function G(e){let{rounds:t}=e;if(t.length===0)return null;for(let n=t.length-1;n>=0;n-=1){let o=t[n];if(o.status==="pending"||o.status==="voting")return o}for(let n=t.length-1;n>=0;n-=1){let o=t[n];if(o.status!=="discarded")return o}return null}function yo(e){let t=Object.values(e.votes);return t.length<2?false:t.every(n=>n===t[0])}function yn(e,t){let n=Pt(e);for(let o=0;o<n.length;o++){let r=n.get(o);if(r.get("id")===t)return r}}function $t(e,t){if(!t.title?.trim()&&!t.ticketId?.trim())throw new Error("appendAgendaItem: at least one of {title, ticketId} is required");let n=crypto.randomUUID(),o=Pt(e),r=null;o.forEach(s=>{if(s.get("state")==="pending"){let a=s.get("sortKey");a!==void 0&&(r===null||a>r)&&(r=a);}});let i=fn(r,null);return e.transact(()=>{let s=new $e.Map;s.set("id",n),s.set("state","pending"),s.set("source",t.source??"manual"),s.set("sortKey",i),s.set("createdAt",t.createdAt),t.title!==void 0&&s.set("title",t.title),t.description!==void 0&&s.set("description",t.description),t.ticketId!==void 0&&s.set("ticketId",t.ticketId),t.externalId!==void 0&&s.set("externalId",t.externalId),Pt(e).push([s]);}),n}function vn(e,t,n,o={}){let r=yn(e,t);if(!r)throw new Error(`setAgendaItemState: item ${t} not found`);let i=r.get("state");if(!(i===n&&n==="consumed")){if(i!=="pending")throw new Error(`setAgendaItemState: invalid transition '${i}' \u2192 '${n}' for item ${t}`);e.transact(()=>{r.set("state",n),n==="consumed"&&(r.set("consumedAt",o.timestamp??Date.now()),o.consumedByRoundId!==void 0&&r.set("consumedByRoundId",o.consumedByRoundId));});}}function vo(e,t,n,o){let r=yn(e,t);if(!r)throw new Error(`reorderAgendaItem: item ${t} not found`);let i=r.get("state");if(i!=="pending")throw new Error(`reorderAgendaItem: item ${t} is ${i}; only pending items may be reordered`);let s=fn(n??null,o??null);e.transact(()=>{r.set("sortKey",s);});}function Ze(e){for(let t of e.agenda)if(t.state==="pending")return t;return null}var Co=5e3;function bo(e,t){let n=new Set;for(let o of e.participants)t.has(o.id)&&Et(o)&&n.add(o.id);return n}function Io(e,t,n){let o=e.settings.hostParticipantId;return o?bo(e,t).has(o)?{kind:"cancel"}:n.missingHostId!==o?{kind:"start-debounce",hostId:o}:{kind:"none"}:{kind:"none"}}function Ao(e,t,n,o){let r=e.settings.hostParticipantId;if(!o||r!==o)return {promote:false};let i=bo(e,t);if(i.has(r))return {promote:false};let s=[...i].toSorted();if(s.length===0)return {promote:false};let a=s[0];return a!==n?{promote:false}:{promote:true,winner:a}}function To(e){let t=Math.max(0,Math.floor(e/1e3)),n=Math.floor(t/60),o=t%60;return `${n}:${o.toString().padStart(2,"0")}`}function Ro(e){try{let t=new URL(e);return t.protocol==="http:"||t.protocol==="https:"}catch{return false}}function Cn(e){if(e===null||typeof e!="object"||Array.isArray(e))return {version:2,entries:{}};let t=e;if(t.version===2&&ko(t.entries)){let n=t.entries,o={};for(let[r,i]of Object.entries(n)){let s=ji(i);s!==null&&(o[r]=s);}return {version:2,entries:o}}if(typeof t.roomCode=="string"&&typeof t.roomUuid=="string"&&typeof t.lastSeenAt=="number"){let n={roomCode:t.roomCode,roomUuid:t.roomUuid,lastSeenAt:t.lastSeenAt,...typeof t.passwordHash=="string"?{passwordHash:t.passwordHash}:{}};return {version:2,entries:{[n.roomUuid]:n}}}return {version:2,entries:{}}}function Eo(e,t,n=864e5){let o={};for(let[r,i]of Object.entries(e.entries))t-i.lastSeenAt>n||(i.lastSeenAt>t?o[r]={...i,lastSeenAt:t}:o[r]=i);return {version:2,entries:o}}function ko(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function ji(e){if(!ko(e))return null;let t=e;return typeof t.roomCode!="string"||typeof t.roomUuid!="string"||typeof t.lastSeenAt!="number"?null:{roomCode:t.roomCode,roomUuid:t.roomUuid,lastSeenAt:t.lastSeenAt,...typeof t.passwordHash=="string"?{passwordHash:t.passwordHash}:{}}}function No(e,t){let n=t.filter(p=>p.status!=="discarded"),o=new Map;for(let p of n)o.set(p.id,p);let r=o.get(e);if(r===void 0||r.parentRoundId===void 0)return null;let i=new Set([r.id]),s=r,a=1;for(;s.parentRoundId!==void 0;){let p=o.get(s.parentRoundId);if(p===void 0||i.has(p.id))return null;i.add(p.id),s=p,a+=1;}let d=s,u=new Map;for(let p of n){if(p.parentRoundId===void 0)continue;let A=u.get(p.parentRoundId);A===void 0?u.set(p.parentRoundId,[p]):A.push(p);}let c=0,l=[d];for(;l.length>0;){let p=l.shift();c+=1;let A=u.get(p.id);A!==void 0&&l.push(...A);}return {chainRoot:d,depth:a,total:c}}var Po="PBKDF2",_i="SHA-256",Do=new TextEncoder;function Fi(e){return Array.from(new Uint8Array(e)).map(t=>t.toString(16).padStart(2,"0")).join("")}async function Be(e,t){let n=await crypto.subtle.importKey("raw",Do.encode(e),Po,false,["deriveBits"]),o=await crypto.subtle.deriveBits({name:Po,salt:Do.encode(t),iterations:1e5,hash:_i},n,256);return Fi(o)}var Oo="ABCDEFGHJKMNPQRSTUVWXYZ23456789";if(Oo.length!==31)throw new Error(`Room code alphabet must be 31 characters, got ${Oo.length}`);function bn(e){return e.trim().replaceAll(/-/g,"").toUpperCase()}function Mo(e,t=true){let n=bn(e);return !t||n.length!==6?n:`${n.slice(0,3)}-${n.slice(3)}`}function $o(e){let t=new Set(e).size;return t===1?"unanimous":t===2?"close":"spread"}function Vi(e,t){let n=new Map;t.forEach((i,s)=>n.set(i,s));let o=[...new Set(e.filter(i=>!n.has(i)))].toSorted(),r=new Map;return o.forEach((i,s)=>r.set(i,t.length+s)),i=>n.get(i)??r.get(i)??Number.MAX_SAFE_INTEGER}function In(e,t){let n=new Map;for(let s of e)n.set(s,(n.get(s)??0)+1);let o=null,r=0,i=[...n.keys()].toSorted((s,a)=>t(s)-t(a));for(let s of i){let a=n.get(s);a>r&&(r=a,o=s);}return o}function Uo(e,t){let n=Object.values(e);if(n.length===0)return null;let o=Vi(n,t.cards);if(t.cards.every(s=>Number.isFinite(parseFloat(s)))){let s=n.map(parseFloat).filter(Number.isFinite);if(s.length===0)return {kind:"categorical",mode:In(n,o),spread:$o(n)};let a=s.reduce((A,N)=>A+N,0)/s.length,d=[...s].toSorted((A,N)=>A-N),u=Math.floor(d.length/2),c=d.length%2===1?d[u]:(d[u-1]+d[u])/2,l=In(n,o),p;if(new Set(n).size===1)p="unanimous";else {let A=n.map(R=>o(R)),N=Math.min(...A);p=Math.max(...A)-N<=1?"close":"spread";}return {kind:"numeric",mean:a,median:c,mode:l,spread:p}}return {kind:"categorical",mode:In(n,o),spread:$o(n)}}function An(e){return e.replaceAll(/\|/g,"\\|")}function Yi(e){return e.replaceAll(/\\/g,"\\\\").replaceAll(/]/g,"\\]")}function Ji(e){let n=(e.match(/`+/g)??[]).reduce((o,r)=>Math.max(o,r.length),0);return "`".repeat(n+1)}function Tn(e){if(!e)return "";if(Ro(e))return ` ([${Yi(e)}](<${e}>))`;let t=Ji(e);return ` (${t}${e}${t})`}function Wi(e){return e instanceof Map?e:new Map(Object.entries(e))}function Ki(e){let t=new Map;for(let r of e)t.set(r.id,r);function n(r){if(!r.parentRoundId)return r;let i=t.get(r.parentRoundId);return i?n(i):r}let o=new Map;for(let r of e){let i=n(r),s=o.get(i.id);s?s.push(r):o.set(i.id,[r]);}return o}function Bo(e,t){let n=new Map;e.scale.cards.forEach((a,d)=>n.set(a,d));let o=new Map,r=[];for(let[a,d]of Object.entries(e.votes)){let u=t.get(a)??a;if(J(d)){r.push(u);continue}let c=o.get(d);c?c.push(u):o.set(d,[u]);}let i=[...o.entries()].map(([a,d])=>({card:a,scaleIndex:n.get(a)??Number.MAX_SAFE_INTEGER,voters:[...d].toSorted().map(An)}));i.sort((a,d)=>{let u=d.voters.length-a.voters.length;return u!==0?u:a.scaleIndex-d.scaleIndex});let s=i.map(a=>`| ${An(a.card)} | ${a.voters.join(", ")} |`);return r.length>0&&s.push(`| Abstained | ${[...r].toSorted().map(An).join(", ")} |`),["| Vote | Voters |","|------|--------|",...s].join(`
3
+ `)}function Ho(e){let t=Object.fromEntries(Object.entries(e.votes).filter(([,i])=>!J(i))),n=Uo(t,e.scale),o=e.timerStartedAt!=null&&e.status==="revealed"&&e.terminalAt!=null?To(e.terminalAt-e.timerStartedAt):null,r=[];return n?.kind==="numeric"?r.push(`Avg ${n.mean.toFixed(1)} \xB7 Med ${n.median}`):n?.kind==="categorical"&&r.push(`Most common: ${n.mode}`),o!=null&&r.push(`Elapsed ${o}`),r.join(" \xB7 ")}function Lo(e){let{rounds:t}=e,n=Wi(e.participants),o=new Map;for(let[c,l]of n)o.set(c,l.displayName);let r=t.filter(c=>c.status==="revealed");if(r.length===0)return "";let i=new Map;t.forEach((c,l)=>i.set(c.id,l+1));let s=Ki(r),a=new Set,d=[];for(let c of r)for(let[l,p]of s)if(p.includes(c)&&!a.has(l)){a.add(l),d.push(l);break}let u=[];for(let c of d){let l=s.get(c),p=l.find(h=>h.id===c),A=l.filter(h=>h.id!==c),N=i.get(p.id)??0,T=p.title?`## Round ${N} \u2014 ${p.title}${Tn(p.ticketId)}`:`## Round ${N}${Tn(p.ticketId)}`,R=Bo(p,o),U=Ho(p),f=U?`${T}
4
4
 
5
- ${A}
5
+ ${R}
6
6
 
7
- ${B}`:`${T}
7
+ ${U}`:`${T}
8
8
 
9
- ${A}`;u.push(f);for(let g of R){let v=`### Re-vote ${i.get(g.id)??0}${xn(g.ticketId)}`,P=xo(g,o),b=So(g),O=b?`${v}
9
+ ${R}`;u.push(f);for(let h of A){let y=`### Re-vote ${i.get(h.id)??0}${Tn(h.ticketId)}`,O=Bo(h,o),C=Ho(h),M=C?`${y}
10
10
 
11
- ${P}
11
+ ${O}
12
12
 
13
- ${b}`:`${v}
13
+ ${C}`:`${y}
14
14
 
15
- ${P}`;u.push(O);}}return u.join(`
15
+ ${O}`;u.push(M);}}return u.join(`
16
16
 
17
17
  `)+`
18
- `}function Ot(e,t,n){if(e.startsWith("custom-")){let r=(n??[]).find(i=>i.id===e);return r?{id:r.id,cards:r.cards}:void 0}let o=se[e];if(o){let r=(t??{})[e]??[];if(r.length===0)return o;let i=o.cards.filter((s,a)=>!r.includes(a));return i.length===0?void 0:{id:e,cards:i}}}function Mt(e,t){let n=Object.keys(se).map(r=>Ot(r,e,t)).filter(r=>r!==void 0),o=(t??[]).map(r=>({id:r.id,cards:r.cards}));return [...n,...o]}function Ue(e,t){if(e.startsWith("custom-")){let n=(t??[]).find(o=>o.id===e);if(n&&n.label.trim().length>0)return n.label}return Gn(e)}var Ri="https://hnch.dev";function lt(){let t=process.env.XDG_CONFIG_HOME||join(homedir(),".config");return join(t,"hnch")}function wn(){return join(lt(),"config.json")}function vo(){let e=lt();existsSync(e)||mkdirSync(e,{recursive:true});}function He(){let e=wn();if(!existsSync(e))return {};try{return JSON.parse(readFileSync(e,"utf-8"))}catch{return {}}}function _(e){vo();let n={...He(),...e};writeFileSync(wn(),JSON.stringify(n,null,2)+`
19
- `,"utf-8");}async function Ei(e){let t=createInterface({input:process.stdin,output:process.stderr});return new Promise(n=>{t.question(e,o=>{t.close(),n(o.trim());});})}function ki(e){return e.startsWith("wss://")?"https://"+e.slice(6):e.startsWith("ws://")?"http://"+e.slice(5):e}async function ae(){let e=He(),t=false;if(e.participantId||(e.participantId=randomUUID(),t=true),!e.displayName){let o=await Ei("Enter your display name: ");e.displayName=o||`user-${e.participantId.slice(0,6)}`,t=true;}e.relayUrl||(e.relayUrl=Ri,t=true),(!e.avatarColor||Oe(e.avatarColor)===null)&&(e.avatarColor=Fn(e.participantId),t=true),t&&_(e);let n=process.env.HNCH_RELAY_URL||e.relayUrl;return n=ki(n),{participantId:e.participantId,displayName:e.displayName,relayUrl:n,defaultScale:e.defaultScale,defaultSessionName:e.defaultSessionName,defaultPassword:e.defaultPassword,presetOverrides:e.presetOverrides,customScales:e.customScales,avatarColor:e.avatarColor}}function ut(e){vo();let n={...He()};for(let[o,r]of Object.entries(e))r===""?delete n[o]:r!==void 0&&(n[o]=r);writeFileSync(wn(),JSON.stringify(n,null,2)+`
20
- `,"utf-8");}async function bo(e,t){let n=await fetch(`${e}/rooms`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})});if(!n.ok)throw new Error(`POST /rooms failed: ${n.status}`);return await n.json()}async function de(e,t){let n=await fetch(`${e}/rooms/${encodeURIComponent(t)}`);if(n.status===404){let o=await n.json().catch(()=>null);throw new Error(`Room not found: ${o?.reason??"unknown-room"}`)}if(!n.ok)throw new Error(`GET /rooms/:code failed: ${n.status}`);return await n.json()}async function yn(e,t,n){let o=await fetch(`${e}/rooms/${t}/access`,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify({passwordHash:n})});if(!o.ok)throw new Error(`PATCH /rooms/:uuid/access failed: ${o.status}`)}function Io(e){let n=`${e.wsBaseUrl.replace(/^http/,"ws")}/ws`,o={did:e.deviceId};e.passwordHash!==void 0&&(o.pw=e.passwordHash);let r=new WebsocketProvider(n,e.roomUuid,e.doc,{params:o,connect:true,...e.awareness!==void 0&&{awareness:e.awareness},WebSocketPolyfill:Pi}),i=u=>{e.onStatusChange?.(u.status);},s=u=>{if(u===null)return;let c=_n.get(u.code);c!==void 0&&(e.onRejection?.(c),r.shouldConnect=false,r.disconnect());};r.on("status",i),r.on("connection-close",s);let a=false,l=e.passwordHash;return {doc:e.doc,awareness:r.awareness,updateCredentials(u){if(u.passwordHash===l)return;l=u.passwordHash??void 0,r.shouldConnect=false,r.disconnect();let d={did:r.params.did??""};l!==void 0&&(d.pw=l),r.params=d,r.connect();},disconnect(){a||(a=true,r.off("status",i),r.off("connection-close",s),r.shouldConnect=false,r.disconnect(),r.destroy());}}}var Di=5e3;function Ao(e,t,n){let o=null,r=null;function i(){let c=new Set;return t.getStates().forEach(d=>{let p=d.participantId;typeof p=="string"&&c.add(p);}),c}function s(){return e.getMap(at).get("hostParticipantId")}function a(){r!==null&&(clearTimeout(r),r=null),o=null;}function l(){let c=s(),d=o;if(o=null,!d||c!==d)return;let p=i();if(p.has(c))return;let R=[...p].toSorted();if(R.length===0)return;let k=R[0];k===n&&ze(e,{hostParticipantId:k});}function u(){let c=s();if(!c)return;if(i().has(c)){a();return}o!==c&&(a(),o=c,r=setTimeout(()=>{r=null,l();},Di));}return t.on("change",u),e.getMap(at).observe(u),u(),{stop(){a(),t.off("change",u),e.getMap(at).unobserve(u);}}}async function Eo(e){let t,n=await bo(e.relayUrl);e.password&&(t=await Be(e.password,n.uuid),await yn(e.relayUrl,n.uuid,t));let o=new $e.Doc,r=new Awareness(o),i=Date.now();return zn(o,{roomUuid:n.uuid,createdAt:i,participantId:e.participantId,displayName:e.displayName,defaultScale:e.defaultScale??At,...e.sessionName!==void 0&&{sessionName:e.sessionName},...e.avatarColor!==void 0&&{avatarColor:e.avatarColor}}),r.setLocalStateField("participantId",e.participantId),Qe({doc:o,awareness:r,roomCode:n.code,roomUuid:n.uuid,participantId:e.participantId,relayUrl:e.relayUrl,passwordHash:t})}async function ko(e){let t=await de(e.relayUrl,e.roomCode),n;if(t.accessMode==="password"){if(!e.password)throw new Error("Room requires a password");n=await Be(e.password,t.uuid);}let o=new $e.Doc,r=new Awareness(o);return qe(o,{participantId:e.participantId,displayName:e.displayName,...e.avatarColor!==void 0&&{avatarColor:e.avatarColor}}),r.setLocalStateField("participantId",e.participantId),Qe({doc:o,awareness:r,roomCode:e.roomCode,roomUuid:t.uuid,participantId:e.participantId,relayUrl:e.relayUrl,passwordHash:n,onRejection:e.onRejection})}function Qe(e){let{doc:t,awareness:n,participantId:o,relayUrl:r,roomUuid:i}=e,s=new Set,a=new Set,l=new Set,u=Io({wsBaseUrl:e.relayUrl,roomUuid:e.roomUuid,doc:t,deviceId:o,passwordHash:e.passwordHash,awareness:n,onStatusChange(f){s.forEach(g=>g(f));},onRejection(f){console.error(`Connection rejected: ${f}`),e.onRejection?.(f);}}),c=Ao(t,n,o),d=()=>{a.forEach(f=>f());};t.getMap("meta").observeDeep(d),t.getMap("settings").observeDeep(d),t.getMap("participants").observeDeep(d),t.getArray("rounds").observeDeep(d);function p(){let f=new Set;return n.getStates().forEach(g=>{let y=g.participantId;typeof y=="string"&&f.add(y);}),f}let R=new Set,k=setInterval(()=>{let f=K(t),g=te(f);if(!g||g.revealedAt!==void 0||R.has(g.id))return;let y=p();if(y.size===0)return;let v=f.participants.filter(b=>y.has(b.id)&&Me(b)==="voter");if(v.length===0)return;v.filter(b=>b.id in g.votes).length>=v.length&&(R.add(g.id),mn(t,g.id,Date.now()));},250),T=t.getMap("settings").get("hostParticipantId"),A=()=>{let f=t.getMap("settings").get("hostParticipantId");f!==T&&f!==void 0&&l.forEach(g=>g(f)),T=f;};t.getMap("settings").observe(A);let B=false;return {doc:t,awareness:n,roomCode:e.roomCode,roomUuid:e.roomUuid,projection(){return K(t)},current(){return te(K(t))},isHost(){return K(t).settings.hostParticipantId===o},onlineIds:p,vote(f){let g=te(K(t));g&&Qn(t,g.id,o,f);},unvote(){let f=te(K(t));f&&Zn(t,f.id,o);},reveal(){let f=te(K(t));f&&mn(t,f.id,Date.now());},skip(){let f=te(K(t));f&&eo(t,f.id,Date.now());},newRound(f,g,y){let v=randomUUID(),P=Date.now();return t.transact(()=>{fn(t,{id:v,createdAt:P,createdBy:o,...f!==void 0&&{title:f},...g!==void 0&&{ticketId:g},...y!==void 0&&{agendaItemId:y}}),y!==void 0&&ro(t,y,v,P);}),v},revote(){let f=te(K(t)),g=randomUUID();return fn(t,{id:g,createdAt:Date.now(),createdBy:o,...f?.id!==void 0&&{parentRoundId:f.id},...f?.title!==void 0&&{title:f.title},...f?.description!==void 0&&{description:f.description},...f?.ticketId!==void 0&&{ticketId:f.ticketId},...f?.agendaItemId!==void 0&&{agendaItemId:f.agendaItemId}}),g},updateRoundDescriptor(f,g){Xn(t,f,g);},updateName(f){ze(t,{name:f});},updateScale(f){ze(t,{defaultScale:f});},updateDisplayName(f){let v=K(t).participants.find(P=>P.id===o)?.joinedAt??Date.now();pn(t,o,{displayName:f,joinedAt:v});},async updateAccess(f,g){let y=null;if(f==="password"){if(!g)throw new Error("Password required for password mode");y=await Be(g,i);}await yn(r,i,y),u.updateCredentials({passwordHash:y});},handoffHost(f){if(!this.isHost())throw new Error("Only the current host can hand off");if(!p().has(f))throw new Error("Target participant is not online");if(!K(t).participants.some(P=>P.id===f))throw new Error("Target participant does not exist");ze(t,{hostParticipantId:f});},toggleRole(){let g=K(t).participants.find(b=>b.id===o),v=Me(g??{})==="viewer"?"voter":"viewer",P=g?.joinedAt??Date.now();pn(t,o,{displayName:g?.displayName??"",joinedAt:P,role:v});},endSessionForEveryone(){if(!this.isHost())throw new Error("Only the current host can end the session");to(t);},onChange(f){return a.add(f),()=>a.delete(f)},onStatusChange(f){return s.add(f),()=>s.delete(f)},onHostChange(f){return l.add(f),()=>l.delete(f)},disconnect(){B||(B=true,clearInterval(k),c.stop(),t.getMap("meta").unobserveDeep(d),t.getMap("settings").unobserveDeep(d),t.getMap("settings").unobserve(A),t.getMap("participants").unobserveDeep(d),t.getArray("rounds").unobserveDeep(d),u.disconnect());}}}function No(e){let t=gn(e);return t===""?null:t}var Li=1440*60*1e3;function vn(){return join(lt(),"last-session.json")}function ji(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 $t(){let e=vn();if(!existsSync(e))return null;try{let t=JSON.parse(readFileSync(e,"utf-8"));return !ji(t)||Date.now()-t.lastSeenAt>Li?(F(),null):t}catch{return F(),null}}function Bt(e){let t=lt();mkdirSync(t,{recursive:true,mode:448}),chmodSync(t,448);let n=vn();writeFileSync(n,JSON.stringify(e,null,2)+`
21
- `,{encoding:"utf-8",mode:384}),chmodSync(n,384);}function F(){let e=vn();try{unlinkSync(e);}catch(t){if(t.code!=="ENOENT")throw t}}async function Ut(e){let t=true;try{await e.clearLastSession();}catch(n){t=false,e.logger.error(`Failed to clear cached session: ${n instanceof Error?n.message:String(n)}`);}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 Ht(e){let t=e.doc.getMap("meta"),n=null,o=false;function r(){if(o)return;let i=t.get("sessionEndedAt");if(i==null||i===n)return;n=i;let s=t.get("createdBy"),a=s===e.localParticipantId,l=e.doc.getMap("participants"),u=e.doc.getMap("settings").get("hostParticipantId")??s,c="host";if(u){let p=l.get(u)?.get("displayName");typeof p=="string"&&p.length>0&&(c=p);}setImmediate(()=>{o||e.onEnded({isLocal:a,hostName:c,timestamp:i});});}return t.observe(r),r(),()=>{o=true,t.unobserve(r);}}var _i="@xauyxau/hnch";function Lt(e){return fo(e)}function jt(e){return `${process.env.HNCH_SHARE_ORIGIN??"https://hnch.dev"}/join?code=${e}`}function _t(e){return `npx ${_i} join ${e}`}function bn(e){let t=e.trim();return t===""?void 0:t}function Ft(e){return e?{title:e.title??"",ticketId:e.ticketId??"",agendaItemId:e.id}:{title:"",ticketId:"",agendaItemId:void 0}}function Do(e,t,n){return [e,bn(t),n]}var Fi={connecting:"yellow",connected:"green",disconnected:"red"};function $o({roomCode:e,sessionName:t,status:n,isHost:o}){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:[o&&jsx(Text,{color:"yellow",children:"[host]"}),jsx(Text,{color:Fi[n],children:n})]})]})}function ge({items:e,onSelect:t,onClose:n,title:o}){if(e.length===0)return jsxs(Box,{flexDirection:"column",gap:1,children:[o&&jsx(Text,{bold:true,children:o}),jsx(Text,{dimColor:true,children:"(no items)"})]});let r=e.findIndex(a=>!a.disabled),[i,s]=useState(Math.max(r,0));return useInput((a,l)=>{if(l.upArrow)s(u=>{let c=(u-1+e.length)%e.length,d=0;for(;e[c]?.disabled&&d<e.length;)c=(c-1+e.length)%e.length,d++;return c});else if(l.downArrow)s(u=>{let c=(u+1)%e.length,d=0;for(;e[c]?.disabled&&d<e.length;)c=(c+1)%e.length,d++;return c});else if(l.return){let u=e[i];u&&!u.disabled&&t(u.value);}else l.escape&&n?.();}),jsxs(Box,{flexDirection:"column",gap:1,children:[o&&jsx(Text,{bold:true,children:o}),jsx(Box,{flexDirection:"column",children:e.map((a,l)=>{let u=l===i,c=a.disabled??false;return jsxs(Box,{gap:1,children:[jsx(Text,{...u&&{color:"cyan"},dimColor:c,children:u?">":" "}),jsx(Text,{bold:u,dimColor:c,children:a.label}),a.detail&&jsx(Text,{dimColor:true,children:a.detail})]},a.value)})}),jsx(Text,{dimColor:true,children:"\u2191\u2193 select \u23CE confirm esc close"})]})}function L({value:e,onChange:t,onSubmit:n,onCancel:o,placeholder:r,isActive:i=true,mask:s=false}){useInput((c,d)=>{d.escape?o?.():d.return?n(e):d.backspace||d.delete?t(e.slice(0,-1)):c&&!d.ctrl&&!d.meta&&t(e+c);},{isActive:i});let a=s&&e.length>0?"*".repeat(e.length):e,l=a.length>0?a:r??"",u=e.length===0&&r!==void 0;return jsxs(Text,{children:[jsx(Text,{dimColor:u,children:l}),jsx(Text,{inverse:true,children:" "})]})}function Lo({mode:e,password:t,onModeChange:n,onPasswordChange:o,onClose:r}){let[i,s]=useState(e==="open"?0:1),[a,l]=useState(false);return useEffect(()=>{s(e==="open"?0:1);},[e]),useInput((u,c)=>{if(c.leftArrow)s(d=>Math.max(0,d-1));else if(c.rightArrow)s(d=>Math.min(1,d+1));else if(c.return){let d=i===0?"open":"password";d!==e&&n(d),d==="password"&&l(true);}else c.escape&&r?.();},{isActive:!a}),jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Box,{gap:1,children:["open","password"].map((u,c)=>{let d=c===i,p=u===e,R=u==="open"?"Open":"Password";return jsx(Text,{inverse:d,bold:p,...p&&{color:"cyan"},children:p?`[${R}]`:` ${R} `},u)})}),e==="password"&&jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{children:"Password: "}),jsx(L,{value:t,onChange:o,onSubmit:()=>l(false),onCancel:()=>l(false),placeholder:"(required to join)",isActive:a,mask:true}),!a&&jsx(Text,{dimColor:true,children:"\u23CE edit \u2190 \u2192 switch mode esc close"}),a&&jsx(Text,{dimColor:true,children:"\u23CE done esc cancel"})]}),e==="open"&&jsx(Text,{dimColor:true,children:"\u2190\u2192 toggle \u23CE confirm esc close"})]})}function Fo({scaleId:e,cards:t,disabledIndices:n,onOverrideChange:o,onClose:r}){let i=useMemo(()=>new Set(n),[n]),[s,a]=useState(0),l=u=>{let c=new Set(i);if(c.has(u))c.delete(u);else {if(c.size>=t.length-1)return;c.add(u);}let d=Array.from(c).toSorted((p,R)=>p-R);o(e,d);};return useInput((u,c)=>{c.leftArrow?a(d=>Math.max(0,d-1)):c.rightArrow?a(d=>Math.min(t.length-1,d+1)):u===" "?l(s):(c.return||c.escape)&&r();}),jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Box,{gap:1,children:t.map((u,c)=>{let d=c===s,p=i.has(c);return jsx(Text,{inverse:d,bold:!p,...!p&&{color:"cyan"},strikethrough:p,dimColor:p,children:u},u)})}),jsx(Text,{dimColor:true,children:" \u2190\u2192 navigate space toggle \u23CE/esc done"})]})}function Pn({initialName:e="",initialCards:t=[],onSave:n,onCancel:o}){let r=e.length>0,[i,s]=useState(e),[a,l]=useState(t.length>0?t:[""]),[u,c]=useState(0),[d,p]=useState("editing-name"),[R,k]=useState(""),T=()=>{let f=i.trim();if(f.length===0||f.length>40){k("Name must be 1-40 characters");return}s(f),k(""),p("editing-cards");},A=()=>{let f=i.trim();if(f.length===0||f.length>40){k("Name must be 1-40 characters");return}let g=a.map(v=>v.trim()).filter(v=>v.length>0),y=Array.from(new Set(g));if(y.length<2){k("Need at least 2 unique non-empty cards");return}k(""),n(f,y);};return useInput((f,g)=>{if(d==="editing-cards"){if(g.leftArrow)c(y=>Math.max(0,y-1));else if(g.rightArrow)c(y=>{let v=y+1;return v>=a.length&&a.length<25?(l(P=>[...P,""]),v):Math.min(v,a.length-1)});else if(g.return)A();else if(g.escape)o();else if(g.tab)p("editing-name");else if(g.backspace){let y=a[u]||"";if(y.length===0){if(a.length>2){let v=a.filter((P,b)=>b!==u);l(v),c(Math.max(0,u-1));}}else {let v=[...a];v[u]=y.slice(0,-1),l(v);}}else if(f&&!g.ctrl&&!g.meta){let y=a[u]||"";if(y.length<10){let v=[...a];v[u]=y+f,l(v);}}}},{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(L,{value:i,onChange:s,onSubmit:T,onCancel:o,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:a.map((f,g)=>{let y=g===u;return jsx(Text,{inverse:y,...!y&&{color:"cyan"},children:f.length>0?f:"\xB7"},g)})})]}),R&&jsx(Text,{color:"red",children:R}),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 Jt({scales:e,selectedScaleId:t,presetOverrides:n,customScales:o=[],onSelect:r,onOverrideChange:i,onCustomScaleCreate:s,onCustomScaleEdit:a,onCustomScaleDelete:l,onClose:u,showNoneOption:c=false}){let[d,p]=useState(0),[R,k]=useState(null),[T,A]=useState("browsing"),[B,f]=useState(null),[g,y]=useState(null),v=s?[...e,{id:"__create-custom__",cards:[]}]:e,P=v.length;if(useInput((b,O)=>{if(T==="browsing"){if(O.escape){u();return}if(P===0)return;if(O.upArrow)p(w=>(w-1+P)%P);else if(O.downArrow)p(w=>(w+1)%P);else if(O.return){let w=v[d];if(!w)return;if(w.id==="__create-custom__"){A("creating");return}w.id in se?k(R===w.id?null:w.id):r(w.id);}else if(b===" "){let w=v[d];w&&w.id!=="__create-custom__"&&r(w.id);}else if(b==="e"||b==="E"){let w=v[d];a&&w?.id.startsWith("custom-")&&(f(w.id),A("editing"));}else if(b==="d"||b==="D"){let w=v[d];l&&w?.id.startsWith("custom-")&&(y(w.id),A("confirming-delete"));}}else T==="confirming-delete"&&(b==="y"||b==="Y"?(g&&l&&l(g),y(null),A("browsing")):(b==="n"||b==="N"||O.escape)&&(y(null),A("browsing")));},{isActive:T==="browsing"&&R===null||T==="confirming-delete"}),T==="creating")return jsx(Pn,{onSave:(b,O)=>{s&&s(b,O),A("browsing");},onCancel:()=>A("browsing")});if(T==="editing"&&B){let b=o?.find(w=>w.id===B);if(e.find(w=>w.id===B)&&b)return jsx(Pn,{initialName:b.label,initialCards:b.cards,onSave:(w,j)=>{a&&a(B,w,j),f(null),A("browsing");},onCancel:()=>{f(null),A("browsing");}})}if(T==="confirming-delete"&&g){let b=v.find(w=>w.id===g),O=b?Ue(b.id,o):"Unknown";return jsxs(Box,{flexDirection:"column",gap:1,children:[jsxs(Text,{children:["Delete ",O,"? "]}),jsx(Text,{children:"[y] yes [n] no esc cancel"})]})}return v.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:v.map((b,O)=>{let w=O===d,j=b.id===t,ue=R===b.id,oe=b.id in se,re=b.id==="__create-custom__",V=b.cards.join(", "),z=c&&b.id==="",X=z?"(none)":re?"[+ Create custom scale]":Ue(b.id,o);return jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{...w&&{color:"cyan"},children:w?">":" "}),jsx(Text,{bold:w,children:X}),!z&&!re&&jsx(Text,{dimColor:true,children:V}),j&&jsx(Text,{color:"cyan",children:"[current]"})]}),ue&&!z&&!re&&jsx(Box,{paddingLeft:2,children:oe?jsx(Fo,{scaleId:b.id,cards:se[b.id]?.cards??[],disabledIndices:n?.[b.id]??[],onOverrideChange:i,onClose:()=>k(null)}):null})]},b.id||"none")})}),jsx(Text,{dimColor:true,children:(()=>{let b=d<v.length?v[d]:null;return b?.id.startsWith("custom-")?`\u2191\u2193 navigate space select${a?" [e] edit":""}${l?" [d] delete":""} esc back`:b&&b.id in se?"\u2191\u2193 navigate \u23CE customize space select esc back":"\u2191\u2193 navigate \u23CE/space select esc back"})()})]})}function ns(e,t){return t?[...e]:e.filter(n=>n.value==="display-name")}function Vo({sessionName:e,displayName:t,currentScale:n,accessMode:o,presetOverrides:r,customScales:i,isHost:s=true,onChangeName:a,onChangeScale:l,onChangeDisplayName:u,onChangeAccess:c,onClose:d}){let[p,R]=useState(r??{}),k=useMemo(()=>Mt(p,i),[p,i]),[T,A]=useState("main-menu"),[B,f]=useState(e??""),[g,y]=useState(t),[v,P]=useState(o==="password"?"password":"open"),[b,O]=useState(""),[w,j]=useState(false),ue=[{label:"Room name",value:"room-name",detail:`[${e??"(none)"}]`},{label:"Scale",value:"scale",detail:`[${Ue(n.id,i)}]`},{label:"Access",value:"access",detail:`[${o==="open"?"Open":"Password"}]`},{label:"Display name",value:"display-name",detail:`[${t}]`}],oe=ns(ue,s);function re(m){!s&&m!=="display-name"||(m==="room-name"?(f(e??""),A("editing-room-name")):m==="scale"?A("picking-scale"):m==="access"?A("picking-access"):m==="display-name"&&(y(t),A("editing-display-name")));}function V(m){let S=m.trim()===""?void 0:m.trim();a(S),A("main-menu");}function z(m){let S=m.trim();S.length>0&&(u(S),process.nextTick(()=>_({displayName:S}))),A("main-menu");}function X(m){let S=k.find(E=>E.id===m);S&&l(S),A("main-menu");}function Ce(m,S){let E={...p};S.length===0?delete E[m]:E[m]=S,R(E),_({presetOverrides:Object.keys(E).length>0?E:void 0});}async function he(m,S){j(true);try{await c(m,S),P(m),O(m==="password"?S??"":"");}catch(E){console.error(`Failed to update access: ${E}`);}finally{j(false),A("main-menu");}}return T==="main-menu"?jsx(ge,{title:"\u2500\u2500 Settings \u2500\u2500",items:oe,onSelect:re,onClose:d}):T==="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(L,{value:B,onChange:f,onSubmit:V,onCancel:()=>A("main-menu"),placeholder:"(leave empty to remove)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]}):T==="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(L,{value:g,onChange:y,onSubmit:z,onCancel:()=>A("main-menu"),placeholder:"(required)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]}):T==="picking-scale"?jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Settings \u2500\u2500"}),jsx(Jt,{scales:k,selectedScaleId:n.id,presetOverrides:p,onSelect:X,onOverrideChange:Ce,onClose:()=>A("main-menu")})]}):T==="picking-access"?jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Settings \u2500\u2500"}),w&&jsx(Text,{dimColor:true,children:"Updating access..."}),!w&&jsx(Lo,{mode:v,password:b,onModeChange:m=>{m==="open"&&he("open");},onPasswordChange:O,onClose:()=>A("main-menu")})]}):jsx(Box,{})}function Gt({participants:e,onlineIds:t,hostId:n,votes:o,revealed:r,participantId:i}){return jsxs(Box,{flexDirection:"column",children:[jsxs(Text,{bold:true,children:["Participants (",e.length,")"]}),e.map(s=>{let a=t.has(s.id),l=a?"\u25CF":"\u25CB",u=a?"green":"gray",c=s.id===n,d=Me(s)==="viewer",p=s.id===i,R=s.id in o,k=d?" (viewer)":r?o[s.id]?` \u2192 ${o[s.id]}`:"":R?" \u2713":" \xB7",T=p&&!d&&!(r&&o[s.id]);return jsxs(Box,{gap:1,children:[jsx(Text,{color:u,children:l}),jsxs(Text,{children:[s.displayName,c?" (host)":""]}),T?jsx(Text,{color:"cyan",children:"(voter)"}):jsx(Text,{color:d?"gray":R?"green":"gray",children:k})]},s.id)})]})}var os=[{label:"End session for everyone",value:"end-session",detail:"Disconnects all participants. Cannot be undone."}];function Jo({onPick:e,onClose:t}){return jsx(ge,{title:"Host Actions",items:os,onSelect:e,onClose:t})}function qt({roomCode:e,onPick:t,onClose:n}){let o=[{label:"Copy code",value:"code",detail:Lt(e)},{label:"Copy share link",value:"url",detail:jt(e)},{label:"Copy npx command",value:"npx",detail:_t(e)}];return jsx(ge,{title:"Share",items:o,onSelect:r=>t(r),onClose:n})}function ss(e){let t=e.rounds.filter(o=>J(o)!==null),n=Object.fromEntries(e.participants.map(o=>[o.id,o]));return wo({rounds:t,participants:n})}async function Ko(e,t,n){let o=ss(e);try{await t(o),n("Results copied to clipboard");}catch{n("Clipboard write failed");}}function Wo(e,t,n){let o=co(e.id,n);return o===null?`Round ${t}`:`Round ${o.depth}/${o.total} [re-vote]`}function Go(e,t,n){let o=Wo(e,t,n),r=e.title?` \u2014 ${e.title}`:"",i=e.ticketId?` [${e.ticketId}]`:"",s=J(e)==="skipped"?" \u2014 Skipped":" \u2014 Results";return `${o}${r}${i}${s}`}function qo(e){let t=Object.values(e).filter(c=>!me(c)).map(c=>parseFloat(c)).filter(c=>!isNaN(c)).toSorted((c,d)=>c-d);if(t.length===0)return {average:"-",median:"-",agreement:"-"};let o=t.reduce((c,d)=>c+d,0)/t.length,r=Math.floor(t.length/2),i=t.length%2===0?(t[r-1]+t[r])/2:t[r],s=Object.values(e).filter(c=>!me(c)),a=new Map;for(let c of s)a.set(c,(a.get(c)??0)+1);let l=Math.max(...a.values()),u=s.length>0?Math.round(l/s.length*100):0;return {average:o.toFixed(1),median:i.toFixed(1),agreement:`${u}%`}}function On(e,t){let n=Math.floor((t-e)/1e3),o=Math.floor(n/60),r=n%60;return `${o}:${r.toString().padStart(2,"0")}`}function zo(e,t,n=[]){let o=Wo(e,t,n);if(J(e)==="skipped"){let d=e.title?` ${e.title}`:"",p=e.ticketId?` [${e.ticketId}]`:"";return `${o}:${d}${p} \u2014 Skipped`}let r=new Map;for(let d of Object.values(e.votes))me(d)||r.set(d,(r.get(d)??0)+1);let i=[...r.entries()].toSorted((d,p)=>p[1]-d[1]||d[0].localeCompare(p[0])).map(([d,p])=>`${d}\xD7${p}`).join(", "),s=Object.values(e.votes).filter(me).length,a=s>0?` (${s} abstained)`:"",l=e.title?` ${e.title}`:"",u=e.ticketId?` [${e.ticketId}]`:"",c=e.timerStartedAt!==void 0&&e.revealedAt!==void 0?` \u2014 ${On(e.timerStartedAt,e.revealedAt)}`:"";return `${o}:${l}${u} \u2014 ${i||"(no votes)"}${a}${c}`}function Xo({projection:e,onlineIds:t,isHost:n,participantId:o,rounds:r,currentRound:i,settingsOpen:s,onStartRound:a,onNewRound:l,onRevote:u,onOpenSettings:c,onHandoffHost:d,onEndSession:p,onShare:R,shareOpen:k,onSetShareOpen:T,roomCode:A,confirmingQuit:B,myRole:f,onToggleRole:g,roundTitlePromptActive:y,roundTitleInput:v,onRoundTitleChange:P,onRoundTitleSubmit:b,onRoundTitleCancel:O,roundTicketIdPromptActive:w,roundTicketIdInput:j,onRoundTicketIdChange:ue,onRoundTicketIdSubmit:oe,onRoundTicketIdCancel:re,descriptorPromptActive:V,descriptorInput:z,onDescriptorChange:X,onDescriptorSubmit:Ce,onDescriptorCancel:he,onEditDescriptor:m,canExport:S,onExportResults:E,onAgendaAdd:Y,agendaAddPromptActive:pe,agendaAddTitleInput:nt,onAgendaAddTitleChange:rn,onAgendaAddTitleSubmit:Ye,onAgendaAddTitleCancel:yt,agendaAddTicketIdPromptActive:ve,agendaAddTicketIdInput:sn,onAgendaAddTicketIdChange:Je,onAgendaAddTicketIdSubmit:Ke,onAgendaAddTicketIdCancel:Ne}){let[Q,Z]=zt.useState("idle"),[xe,be]=zt.useState(null),[ot,ie]=zt.useState(false),[fe,Pe]=zt.useState(false);useEffect(()=>{n||(Z("idle"),be(null),ie(false),Pe(false));},[n]),useInput(C=>{if(C==="c"||C==="C"){T(true);return}if(C==="w"||C==="W"){g();return}if((C==="x"||C==="X")&&S){E();return}if(n){if(C==="a"||C==="A"){Y();return}(C==="e"||C==="E")&&c(),(C==="s"||C==="S")&&a(),(C==="h"||C==="H")&&Z("picking"),(C==="m"||C==="M")&&ie(true),i!==null&&J(i)!==null&&((C==="n"||C==="N")&&l(),(C==="v"||C==="V")&&J(i)!=="skipped"&&u(),(C==="t"||C==="T")&&m());}},{isActive:!s&&!B&&Q==="idle"&&!y&&!w&&!ot&&!fe&&!k&&!V&&!pe&&!ve}),useInput((C,ee)=>{C==="y"||C==="Y"?(p(),Pe(false)):(C==="n"||C==="N"||ee.escape)&&Pe(false);},{isActive:fe});let Ie=e.participants.filter(C=>C.id!==o&&t.has(C.id));useInput(C=>{if(Q==="picking"){let ee=parseInt(C,10);!isNaN(ee)&&ee>=1&&ee<=Ie.length?(be(Ie[ee-1].id),Z("confirming")):(C==="Escape"||C==="q"||C==="Q")&&Z("idle");}},{isActive:Q==="picking"}),useInput(C=>{Q==="confirming"&&xe&&(C==="y"||C==="Y"?(d(xe),Z("idle"),be(null)):(C==="n"||C==="N"||C==="Escape")&&(Z("idle"),be(null)));},{isActive:Q==="confirming"});let De=r.length>0,Ct=r.filter(C=>J(C)!==null),rt=i!==null?Ct.slice(0,-1):Ct;return ot?jsx(Jo,{onPick:C=>{ie(false),C==="end-session"&&Pe(true);},onClose:()=>ie(false)}):k?jsx(qt,{roomCode:A,onPick:C=>{T(false),R(C);},onClose:()=>T(false)}):jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:De?"Session":"Waiting for participants..."}),!De&&jsx(Text,{dimColor:true,children:"No rounds started yet."}),jsx(Gt,{participants:e.participants,onlineIds:t,hostId:e.settings.hostParticipantId,votes:{},revealed:false,participantId:o}),e.agenda.length>0&&jsx(us,{items:e.agenda,nextPending:Xe(e)}),i!==null&&J(i)!==null&&jsx(ds,{projection:e,round:i,rounds:r}),rt.length>0&&jsxs(Box,{flexDirection:"column",children:[jsx(Text,{dimColor:true,children:"\u2500\u2500 History \u2500\u2500"}),rt.map(C=>{let ee=r.indexOf(C)+1;return jsx(Text,{dimColor:true,children:zo(C,ee,r)},C.id)})]}),Q==="picking"&&Ie.length>0&&jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{children:"Hand off to:"}),Ie.map((C,ee)=>jsxs(Text,{children:["[",ee+1,"] ",C.displayName]},C.id)),jsx(Text,{dimColor:true,children:"[q] Cancel"})]}),Q==="picking"&&Ie.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"})]}),Q==="confirming"&&xe&&jsx(Box,{flexDirection:"column",children:jsxs(Text,{children:["Hand off to ",e.participants.find(C=>C.id===xe)?.displayName,"? [y/n]"]})}),y&&jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{children:"Round title (optional, Enter to skip):"}),jsx(L,{value:v,onChange:P,onSubmit:b,onCancel:O,placeholder:"Enter to skip",isActive:y})]}),jsx(Text,{dimColor:true,children:"[Esc] Cancel"})]}),w&&jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{children:"Ticket ID (optional, Enter to skip):"}),jsx(L,{value:j,onChange:ue,onSubmit:oe,onCancel:re,placeholder:"e.g. PROJ-1234",isActive:w})]}),jsx(Text,{dimColor:true,children:"[Esc] Cancel"})]}),V&&jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{children:"Edit round title:"}),jsx(L,{value:z,onChange:X,onSubmit:Ce,onCancel:he,placeholder:"Enter to save",isActive:V})]}),jsx(Text,{dimColor:true,children:"[Esc] Cancel \xB7 empty submit cancels"})]}),fe&&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"})]}),pe&&jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{children:"Agenda title (optional, Enter to skip):"}),jsx(L,{value:nt,onChange:rn,onSubmit:Ye,onCancel:yt,placeholder:"Enter to skip",isActive:pe})]}),jsx(Text,{dimColor:true,children:"[Esc] Cancel"})]}),ve&&jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{children:"Agenda ticket ID (optional, Enter to skip):"}),jsx(L,{value:sn,onChange:Je,onSubmit:Ke,onCancel:Ne,placeholder:"e.g. PROJ-1234",isActive:ve})]}),jsx(Text,{dimColor:true,children:"[Esc] Cancel"})]}),Q==="idle"&&!y&&!w&&!V&&!fe&&!pe&&!ve&&jsxs(Box,{gap:2,children:[n?jsx(Fragment,{children:i!==null&&J(i)!==null?jsxs(Fragment,{children:[J(i)!=="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"}),S&&jsx(Text,{color:"yellow",children:"[x] Export"}),jsxs(Text,{color:"yellow",children:["[w] ",f==="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:"[a] Agenda"}),jsx(Text,{color:"yellow",children:"[h] Handoff"}),jsx(Text,{color:"yellow",children:"[e] Settings"}),jsx(Text,{color:"yellow",children:"[c] Share"}),S&&jsx(Text,{color:"yellow",children:"[x] Export"}),jsxs(Text,{color:"yellow",children:["[w] ",f==="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"}),S&&jsx(Text,{color:"yellow",children:"[x] Export"}),jsxs(Text,{color:"yellow",children:["[w] ",f==="viewer"?"Vote":"Observe"]})]}),jsx(Text,{children:"[q] Quit"})]})]})}function ds({projection:e,round:t,rounds:n}){let o=n.indexOf(t)+1,r=Go(t,o,n);if(J(t)==="skipped")return jsx(Box,{flexDirection:"column",gap:1,children:jsx(Text,{bold:true,children:r})});let i=qo(t.votes),s=new Map(e.participants.map(p=>[p.id,p.displayName])),a=t.timerStartedAt!==void 0&&t.revealedAt!==void 0?On(t.timerStartedAt,t.revealedAt):null,l=Object.entries(t.votes),u=l.filter(([,p])=>!me(p)),c=l.filter(([,p])=>me(p)).map(([p])=>p),d=u[0]?.[1];return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:r}),no(t)&&d!==void 0&&jsxs(Text,{bold:true,color:"green",children:["Consensus! Everyone voted ",d]}),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)})]}),u.map(([p,R])=>jsxs(Box,{gap:2,children:[jsx(Text,{children:(s.get(p)??p).padEnd(20)}),jsx(Text,{color:"cyan",bold:true,children:R.padEnd(10)})]},p)),c.map(p=>jsxs(Box,{gap:2,children:[jsx(Text,{dimColor:true,children:(s.get(p)??p).padEnd(20)}),jsx(Text,{dimColor:true,children:"(abstained)".padEnd(10)})]},p)),e.participants.filter(p=>!(p.id in t.votes)).map(p=>jsxs(Box,{gap:2,children:[jsx(Text,{dimColor:true,children:p.displayName.padEnd(20)}),jsx(Text,{dimColor:true,children:"(no vote)".padEnd(10)})]},p.id))]}),jsxs(Box,{gap:2,children:[jsxs(Text,{children:["Avg: ",jsx(Text,{bold:true,children:i.average})]}),jsxs(Text,{children:["Med: ",jsx(Text,{bold:true,children:i.median})]}),jsxs(Text,{children:["Agreement: ",jsx(Text,{bold:true,children:i.agreement})]}),a!==null&&jsxs(Text,{dimColor:true,children:["\u23F1 ",a]})]})]})}var cs={pending:"\u2022",consumed:"\u2713",skipped:"~",cancelled:"\u2717"},ls={consumed:"green",skipped:"yellow",cancelled:"red"};function us({items:e,nextPending:t}){let n=e.filter(o=>o.state!=="cancelled");return jsxs(Box,{flexDirection:"column",children:[jsx(Text,{dimColor:true,children:"\u2500\u2500 Agenda \u2500\u2500"}),n.map(o=>{let r=cs[o.state]??"?",i=ls[o.state],s=t!==null&&o.id===t.id,a=[o.ticketId?`[${o.ticketId}]`:"",o.title??""].filter(Boolean).join(" ").trim()||o.id;return jsxs(Box,{gap:1,children:[i!==void 0?jsx(Text,{color:i,children:r}):jsx(Text,{children:r}),i!==void 0?jsx(Text,{color:i,children:a}):jsx(Text,{children:a}),s&&jsx(Text,{color:"cyan",children:"(next)"})]},o.id)}),n.length===0&&jsx(Text,{dimColor:true,children:"No pending items."})]})}function er({scale:e,selected:t,onSelect:n,onClear:o,disabled:r}){let[i,s]=useState(0);return useInput((a,l)=>{if(!r)if(l.leftArrow)s(u=>Math.max(0,u-1));else if(l.rightArrow)s(u=>Math.min(e.cards.length-1,u+1));else if(l.return){let u=e.cards[i];u===t?o():n(u);}else (l.backspace||l.delete)&&o();}),jsxs(Box,{flexDirection:"column",gap:0,children:[jsx(Box,{gap:1,children:e.cards.map((a,l)=>{let u=l===i,c=a===t;return jsx(Text,{inverse:u,bold:c,...c&&{color:"cyan"},children:c?`[${a}]`:` ${a} `},a)})}),jsx(Text,{dimColor:true,children:" \u2190\u2192 navigate \u23CE select \u232B clear"})]})}function hs(e){let t=Math.floor(e/1e3),n=Math.floor(t/60),o=t%60;return `${n}:${o.toString().padStart(2,"0")}`}function tr({projection:e,round:t,onlineIds:n,isHost:o,myId:r,myRole:i,elapsedMs:s,roomCode:a,shareOpen:l,settingsOpen:u,onVote:c,onClearVote:d,onReveal:p,onSkip:R,onToggleRole:k,onShare:T,onSetShareOpen:A,onOpenSettings:B,confirmingQuit:f}){let g=t.votes[r],y=g!==void 0&&me(g);if(useInput(w=>{if(w==="e"||w==="E"){B();return}if(w==="c"||w==="C"){A(true);return}if(i==="voter"&&(w==="a"||w==="A")){y?d():c(It);return}o&&(w==="r"||w==="R")&&p(),o&&(w==="s"||w==="S")&&R(),(w==="w"||w==="W")&&k();},{isActive:!f&&!u&&!l}),l)return jsx(qt,{roomCode:a,onPick:w=>{A(false),T(w);},onClose:()=>A(false)});let v=g,P=e.rounds.indexOf(t)+1,b=t.title?` \u2014 ${t.title}`:"",O=t.ticketId?` [${t.ticketId}]`:"";return jsxs(Box,{flexDirection:"column",gap:1,children:[jsxs(Box,{gap:2,children:[jsxs(Text,{bold:true,children:["Round ",P,b,O]}),s!==void 0&&jsxs(Text,{dimColor:true,children:["\u23F1 ",hs(s)]})]}),jsxs(Box,{gap:4,children:[jsx(Gt,{participants:e.participants,onlineIds:n,hostId:e.settings.hostParticipantId,votes:t.votes,revealed:false}),i==="viewer"?jsx(Box,{flexDirection:"column",children:jsx(Text,{dimColor:true,children:"You're observing"})}):jsxs(Box,{flexDirection:"column",gap:1,children:[y&&jsx(Text,{dimColor:true,children:"(abstaining this round \u2014 press [a] to vote)"}),jsx(er,{scale:t.scale,selected:y?void 0:v,onSelect:c,onClear:d,disabled:f||y})]})]}),jsxs(Box,{gap:2,children:[o&&jsx(Text,{color:"yellow",children:"[r] Reveal"}),o&&jsx(Text,{color:"yellow",children:"[s] Skip"}),i==="voter"&&jsxs(Text,{color:"yellow",children:["[a] ",y?"Vote":"Abstain"]}),jsxs(Text,{color:"yellow",children:["[w] ",i==="viewer"?"Vote":"Observe"]}),jsx(Text,{children:"[e] Settings"}),jsx(Text,{children:"[c] Share"}),jsx(Text,{children:"[q] Quit"})]})]})}var ys=3e3;function Zt(e=ys){let[t,n]=useState(null),o=useRef(null),r=useCallback(()=>{o.current!==null&&(clearTimeout(o.current),o.current=null);},[]),i=useCallback(a=>{r(),n(a),o.current=setTimeout(()=>{n(null),o.current=null;},e);},[r,e]),s=useCallback(()=>{r(),n(null);},[r]);return useEffect(()=>()=>{r();},[r]),{notice:t,show:i,dismiss:s}}function bs(e){let t=te(e);return !t||J(t)!==null?"lobby":"voting"}function tn({session:e,participantId:t,displayName:n,accessMode:o,presetOverrides:r,customScales:i}){let{exit:s}=useApp(),[a,l]=useState(e.projection()),[u,c]=useState("connecting"),[d,p]=useState(e.onlineIds()),[R,k]=useState(),[T,A]=useState(false),[B,f]=useState(o==="password"?"password":"open"),[g,y]=useState(n),[v,P]=useState(false),[b,O]=useState(false),[w,j]=useState(""),[ue,oe]=useState(false),[re,V]=useState(""),[z,X]=useState(),[Ce,he]=useState(false),[m,S]=useState(""),[E,Y]=useState(null),[pe,nt]=useState(false),[rn,Ye]=useState(""),[yt,ve]=useState(false),[sn,Je]=useState(""),[Ke,Ne]=useState(),[Q,Z]=useState(),xe=Zt(3e3),[be,ot]=useState(false),ie=Zt(3e3),fe=Zt(3e3);useEffect(()=>{let I=e.onChange(()=>{l(e.projection()),p(e.onlineIds());}),$=e.onStatusChange(c),We=e.onHostChange(vt=>{let _r=e.projection().participants.find(Vr=>Vr.id===vt),Fr=vt===t?"You are now the host":`${_r?.displayName||"Unknown"} is now the host`;xe.show(Fr);}),dn=setInterval(()=>{p(e.onlineIds());let vt=e.projection(),bt=te(vt);bt?.timerStartedAt!==void 0&&bt.revealedAt===void 0?k(Date.now()-bt.timerStartedAt):k(void 0);},1e3);return ()=>{I(),$(),We(),clearInterval(dn);}},[e,t,xe.show]),useInput(I=>{(I==="q"||I==="Q")&&P(true);},{isActive:!T&&!v&&!b&&!ue&&!Ce&&!be&&!pe&&!yt}),useInput((I,$)=>{I==="y"||I==="Y"?(e.disconnect(),s()):(I==="n"||I==="N"||$.escape)&&P(false);},{isActive:v});let Pe=bs(a),Ie=te(a),De=a.settings.hostParticipantId===t,Ct=a.participants.find(I=>I.id===t),rt=Me(Ct??{}),C=useCallback(()=>{let I=Ft(Xe(a));j(I.title),Z(I.agendaItemId),O(true);},[a]),ee=useCallback(I=>{O(false),j(""),X(bn(I));let $=Ft(Xe(a));V($.ticketId),oe(true);},[a]),mr=useCallback(()=>{O(false),j(""),X(void 0),Z(void 0);},[]),gr=useCallback(I=>{oe(false),V("");let[$,We,dn]=Do(z,I,Q);e.newRound($,We,dn),X(void 0),Z(void 0);},[e,z,Q]),hr=useCallback(()=>{oe(false),V(""),X(void 0),Z(void 0);},[]),xr=useCallback(()=>{let I=e.current();!I||I.revealedAt===void 0||(Y(I.id),S(I.title??""),he(true));},[e]),Sr=useCallback(I=>{let $=E;if(he(false),Y(null),!$)return;let We=I.trim();We!==""&&e.updateRoundDescriptor($,{title:We});},[e,E]),wr=useCallback(()=>{he(false),Y(null);},[]),yr=useCallback(I=>{e.vote(I);},[e]),Cr=useCallback(()=>{e.unvote();},[e]),vr=useCallback(()=>{e.reveal();},[e]),br=useCallback(()=>{e.skip();},[e]),Ir=useCallback(()=>{let I=Ft(Xe(a));j(I.title),Z(I.agendaItemId),O(true);},[a]),Ar=useCallback(()=>{e.revote();},[e]),Hn=useCallback(()=>{e.toggleRole();},[e]),Tr=useCallback(I=>{e.updateName(I);},[e]),Rr=useCallback(I=>{e.updateScale(I);},[e]),Er=useCallback(I=>{e.updateDisplayName(I),y(I);},[e]),kr=useCallback(async(I,$)=>{await e.updateAccess(I,$),f(I);},[e]),Nr=useCallback(I=>{try{e.handoffHost(I);}catch($){console.error(`Handoff failed: ${$ instanceof Error?$.message:String($)}`);}},[e]),Ln=useCallback(()=>{A(true);},[]),Pr=useCallback(()=>{A(false);},[]),Dr=useCallback(()=>{e.endSessionForEveryone();},[e]),jn=useCallback(async I=>{let $=I==="code"?Lt(e.roomCode):I==="url"?jt(e.roomCode):_t(e.roomCode);try{await or.write($),ie.show("Copied!");}catch{ie.show(`Copy failed \u2014 ${$}`);}},[e.roomCode,ie]),an=a.rounds.filter(I=>J(I)!==null),Or=an.length>0?an.at(-1):null,Mr=an.length>0,$r=useCallback(()=>Ko(a,I=>or.write(I),fe.show),[a,fe.show]),Br=useCallback(()=>{Ye(""),nt(true);},[]),Ur=useCallback(I=>{nt(false),Ye("");let $=I.trim()===""?void 0:I.trim();Ne($),Je(""),ve(true);},[]),Hr=useCallback(()=>{nt(false),Ye(""),Ne(void 0);},[]),Lr=useCallback(I=>{ve(false),Je("");let $=I.trim()===""?void 0:I.trim();if(!Ke&&!$){Ne(void 0);return}Pt(e.doc,{...Ke!==void 0&&{title:Ke},...$!==void 0&&{ticketId:$},createdAt:Date.now()}),Ne(void 0);},[e.doc,Ke]),jr=useCallback(()=>{ve(false),Je(""),Ne(void 0);},[]);return jsxs(Box,{flexDirection:"column",children:[jsx($o,{roomCode:e.roomCode,sessionName:a.settings.name,status:u,isHost:De}),xe.notice&&jsx(Box,{paddingX:1,children:jsx(Text,{color:"yellow",children:xe.notice})}),ie.notice&&jsx(Box,{paddingX:1,children:jsx(Text,{color:ie.notice.startsWith("Copy failed")?"red":"green",children:ie.notice})}),fe.notice&&jsx(Box,{paddingX:1,children:jsx(Text,{color:fe.notice.startsWith("Clipboard write failed")?"red":"green",children:fe.notice})}),T?jsx(Vo,{sessionName:a.settings.name,displayName:g,currentScale:a.settings.defaultScale,accessMode:B,presetOverrides:r,customScales:i,isHost:De,onChangeName:Tr,onChangeScale:Rr,onChangeDisplayName:Er,onChangeAccess:kr,onClose:Pr}):Pe==="lobby"?jsx(Xo,{projection:a,onlineIds:d,isHost:De,participantId:t,rounds:a.rounds,currentRound:Or,settingsOpen:T,onStartRound:C,onNewRound:Ir,roundTitlePromptActive:b,roundTitleInput:w,onRoundTitleChange:j,onRoundTitleSubmit:ee,onRoundTitleCancel:mr,roundTicketIdPromptActive:ue,roundTicketIdInput:re,onRoundTicketIdChange:V,onRoundTicketIdSubmit:gr,onRoundTicketIdCancel:hr,onRevote:Ar,onOpenSettings:Ln,onHandoffHost:Nr,onEndSession:Dr,onShare:jn,shareOpen:be,onSetShareOpen:ot,roomCode:e.roomCode,confirmingQuit:v,myRole:rt,onToggleRole:Hn,descriptorPromptActive:Ce,descriptorInput:m,onDescriptorChange:S,onDescriptorSubmit:Sr,onDescriptorCancel:wr,onEditDescriptor:xr,canExport:Mr,onExportResults:$r,onAgendaAdd:Br,agendaAddPromptActive:pe,agendaAddTitleInput:rn,onAgendaAddTitleChange:Ye,onAgendaAddTitleSubmit:Ur,onAgendaAddTitleCancel:Hr,agendaAddTicketIdPromptActive:yt,agendaAddTicketIdInput:sn,onAgendaAddTicketIdChange:Je,onAgendaAddTicketIdSubmit:Lr,onAgendaAddTicketIdCancel:jr}):Pe==="voting"&&Ie?jsx(tr,{projection:a,round:Ie,onlineIds:d,isHost:De,myId:t,myRole:rt,elapsedMs:R,onVote:yr,onClearVote:Cr,onReveal:vr,onSkip:br,onToggleRole:Hn,onShare:jn,onSetShareOpen:ot,shareOpen:be,settingsOpen:T,onOpenSettings:Ln,roomCode:e.roomCode,confirmingQuit:v}):null,v&&jsx(Box,{children:jsx(Text,{dimColor:true,children:"Leave session? y/n"})})]})}function rr({presetOverrides:e,customScales:t}){let{exit:n}=useApp(),[o,r]=useState("main-menu"),[i,s]=useState(He()),[a,l]=useState(i.displayName??""),[u,c]=useState(i.relayUrl??""),[d,p]=useState(i.defaultSessionName??""),[R,k]=useState(i.avatarColor?.startsWith("#")?i.avatarColor:""),[T,A]=useState(t??[]),[B,f]=useState(e??{});useInput(m=>{(m==="q"||m==="Q")&&n();},{isActive:o==="main-menu"});let g=useMemo(()=>Mt(B,T),[B,T]),y=i.defaultScale,v=y?g.find(m=>m.id===y):void 0,P=[{label:"Display name",value:"display-name",detail:`[${i.displayName??"(none)"}]`},{label:"Relay URL",value:"relay-url",detail:`[${i.relayUrl??"(none)"}]`},{label:"Default scale",value:"default-scale",detail:`[${v?Ue(v.id,T):"(none)"}]`},{label:"Default session name",value:"default-session-name",detail:`[${i.defaultSessionName??"(none)"}]`},{label:"Default password",value:"default-password",detail:`[${i.defaultPassword?"on":"off"}]`},{label:"Avatar color",value:"avatar-color",detail:`[${ks(i.avatarColor)}]`}];function b(m){m==="display-name"?(l(i.displayName??""),r("editing-display-name")):m==="relay-url"?(c(i.relayUrl??""),r("editing-relay-url")):m==="default-scale"?r("picking-scale"):m==="default-session-name"?(p(i.defaultSessionName??""),r("editing-session-name")):m==="default-password"?r("picking-default-password"):m==="avatar-color"&&r("picking-avatar-color");}function O(m){if(m==="custom-hex"){k(i.avatarColor?.startsWith("#")?i.avatarColor:""),r("editing-avatar-hex");return}if(Oe(m)===null){r("main-menu");return}s({...i,avatarColor:m}),_({avatarColor:m}),r("main-menu");}function w(m){let S=m.trim();if(S.length===0){r("picking-avatar-color");return}let E=S.startsWith("#")?S.toLowerCase():`#${S.toLowerCase()}`;Oe(E)!==null&&(s({...i,avatarColor:E}),_({avatarColor:E}),r("main-menu"));}function j(m){let S=m.trim();if(S.length>0){let E={...i,displayName:S};s(E),_({displayName:S});}r("main-menu");}function ue(m){let S=m.trim();if(S.length>0){let E={...i,relayUrl:S};s(E),_({relayUrl:S});}r("main-menu");}function oe(m){let S=m.trim(),E=S.length>0?{...i,defaultSessionName:S}:{...i};S.length===0?(delete E.defaultSessionName,s(E),ut({defaultSessionName:""})):(s(E),_({defaultSessionName:S})),r("main-menu");}function re(m){let S=m.length>0?{...i,defaultScale:m}:{...i};m.length===0?(delete S.defaultScale,s(S),ut({defaultScale:""})):(s(S),_({defaultScale:m})),r("main-menu");}function V(m,S){let E={...B};S.length===0?delete E[m]:E[m]=S,f(E),_({presetOverrides:Object.keys(E).length>0?E:void 0});}function z(m){let S=m==="on",E={...i,defaultPassword:S};s(E),_({defaultPassword:S}),r("main-menu");}function X(m,S){let E=`custom-${randomUUID()}`,Y=[...T,{id:E,label:m,cards:S}];A(Y),s({...i,customScales:Y}),_({customScales:Y});}function Ce(m,S,E){let Y=T.map(pe=>pe.id===m?{id:m,label:S,cards:E}:pe);A(Y),s({...i,customScales:Y}),_({customScales:Y});}function he(m){let S=T.filter(Y=>Y.id!==m);A(S);let E={...i,customScales:S};i.defaultScale===m?(E.defaultScale="fibonacci",_({customScales:S,defaultScale:"fibonacci"})):_({customScales:S}),s(E);}if(o==="main-menu")return jsx(ge,{title:"\u2500\u2500 Config Settings \u2500\u2500",items:P,onSelect:b,onClose:()=>n()});if(o==="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(L,{value:a,onChange:l,onSubmit:j,onCancel:()=>r("main-menu"),placeholder:"(required)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]});if(o==="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(L,{value:u,onChange:c,onSubmit:ue,onCancel:()=>r("main-menu"),placeholder:"(required)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]});if(o==="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(L,{value:d,onChange:p,onSubmit:oe,onCancel:()=>r("main-menu"),placeholder:"(leave empty to clear)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]});if(o==="picking-scale"){let m=[{id:"",cards:[]},...g];return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(Jt,{scales:m,selectedScaleId:y??"",presetOverrides:B,customScales:T,onSelect:re,onOverrideChange:V,onCustomScaleCreate:X,onCustomScaleEdit:Ce,onCustomScaleDelete:he,onClose:()=>r("main-menu"),showNoneOption:true})]})}if(o==="picking-avatar-color"){let m=it.map(S=>({label:Ge[S].label,value:S,detail:i.avatarColor===S?"[current]":Ge[S].base}));return m.push({label:"Custom hex\u2026",value:"custom-hex",detail:i.avatarColor?.startsWith("#")?`[${i.avatarColor}]`:void 0}),jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(ge,{title:"Avatar color:",items:m,onSelect:O,onClose:()=>r("main-menu")})]})}if(o==="editing-avatar-hex")return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(Text,{children:"Custom hex color: "}),jsx(L,{value:R,onChange:k,onSubmit:w,onCancel:()=>r("picking-avatar-color"),placeholder:"#rrggbb",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]});if(o==="picking-default-password"){let m=[{label:"On",value:"on",detail:i.defaultPassword?"[current]":void 0},{label:"Off",value:"off",detail:i.defaultPassword?void 0:"[current]"}];return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(ge,{title:"Default password:",items:m,onSelect:z,onClose:()=>r("main-menu")})]})}return jsx(Box,{})}function ks(e){return e?e.startsWith("#")?e:it.includes(e)?Ge[e].label:e:"(auto)"}var Ps={pending:"\u2022",consumed:"\u2713",skipped:"~",cancelled:"\u2717"};function Ds(e){let t=Ps[e.state]??"?",n=[];e.ticketId&&n.push(`[${e.ticketId}]`),e.title&&n.push(e.title);let o=n.join(" ")||e.id;return `${t} ${o}`}async function St(e){let t=new $e.Doc,n=new Awareness(t);qe(t,{participantId:e.participantId,displayName:e.displayName}),n.setLocalStateField("participantId",e.participantId);let o=Qe({doc:t,awareness:n,roomCode:e.roomCode,roomUuid:e.roomUuid,participantId:e.participantId,relayUrl:e.relayUrl,...e.passwordHash!==void 0&&{passwordHash:e.passwordHash}});return await new Promise(r=>{let i=setTimeout(r,2e3),s=o.onStatusChange(a=>{a==="connected"&&setTimeout(()=>{clearTimeout(i),s(),r();},200);});}),{doc:t,disconnect:()=>o.disconnect()}}async function wt(){let e=$t();return e||(console.error("No active session. Join a room first with: hnch join <code>"),process.exit(1)),{roomCode:e.roomCode,roomUuid:e.roomUuid,...e.passwordHash!==void 0&&{passwordHash:e.passwordHash}}}async function sr(e){!e.title?.trim()&&!e.ticket?.trim()&&(console.error("At least one of --title or --ticket is required"),process.exit(1));let t=await ae(),n=await wt();try{await de(t.relayUrl,n.roomCode);}catch{console.error(`Room ${n.roomCode} not found on relay \u2014 session may have expired.`),process.exit(1);}let{doc:o,disconnect:r}=await St({relayUrl:t.relayUrl,participantId:t.participantId,displayName:t.displayName,roomCode:n.roomCode,roomUuid:n.roomUuid,...n.passwordHash!==void 0&&{passwordHash:n.passwordHash}});try{let i=Pt(o,{...e.title?.trim()?{title:e.title.trim()}:{},...e.ticket?.trim()?{ticketId:e.ticket.trim()}:{},...e.description?.trim()?{description:e.description.trim()}:{},createdAt:Date.now()});await new Promise(s=>{setTimeout(s,300);}),console.log(`Added agenda item: ${i}`);}finally{r();}}async function ar(){let e=await ae(),t=await wt();try{await de(e.relayUrl,t.roomCode);}catch{console.error(`Room ${t.roomCode} not found on relay \u2014 session may have expired.`),process.exit(1);}let{doc:n,disconnect:o}=await St({relayUrl:e.relayUrl,participantId:e.participantId,displayName:e.displayName,roomCode:t.roomCode,roomUuid:t.roomUuid,...t.passwordHash!==void 0&&{passwordHash:t.passwordHash}});try{let i=K(n).agenda;if(i.length===0){console.log("No agenda items.");return}i.forEach((s,a)=>{console.log(`${String(a+1).padStart(2)}. ${Ds(s)}`);});}finally{o();}}async function dr(e){let t=await ae(),n=await wt();try{await de(t.relayUrl,n.roomCode);}catch{console.error(`Room ${n.roomCode} not found on relay \u2014 session may have expired.`),process.exit(1);}let{doc:o,disconnect:r}=await St({relayUrl:t.relayUrl,participantId:t.participantId,displayName:t.displayName,roomCode:n.roomCode,roomUuid:n.roomUuid,...n.passwordHash!==void 0&&{passwordHash:n.passwordHash}});try{Dt(o,e.id,"skipped",{timestamp:Date.now()}),await new Promise(i=>{setTimeout(i,300);}),console.log(`Skipped agenda item: ${e.id}`);}catch(i){console.error(`Skip failed: ${i instanceof Error?i.message:String(i)}`),process.exit(1);}finally{r();}}async function cr(e){let t=await ae(),n=await wt();try{await de(t.relayUrl,n.roomCode);}catch{console.error(`Room ${n.roomCode} not found on relay \u2014 session may have expired.`),process.exit(1);}let{doc:o,disconnect:r}=await St({relayUrl:t.relayUrl,participantId:t.participantId,displayName:t.displayName,roomCode:n.roomCode,roomUuid:n.roomUuid,...n.passwordHash!==void 0&&{passwordHash:n.passwordHash}});try{Dt(o,e.id,"cancelled",{timestamp:Date.now()}),await new Promise(i=>{setTimeout(i,300);}),console.log(`Cancelled agenda item: ${e.id}`);}catch(i){console.error(`Cancel failed: ${i instanceof Error?i.message:String(i)}`),process.exit(1);}finally{r();}}async function lr(e){let t=await ae(),n=await wt();try{await de(t.relayUrl,n.roomCode);}catch{console.error(`Room ${n.roomCode} not found on relay \u2014 session may have expired.`),process.exit(1);}let{doc:o,disconnect:r}=await St({relayUrl:t.relayUrl,participantId:t.participantId,displayName:t.displayName,roomCode:n.roomCode,roomUuid:n.roomUuid,...n.passwordHash!==void 0&&{passwordHash:n.passwordHash}});try{io(o,e.id,e.before,e.after),await new Promise(i=>{setTimeout(i,300);}),console.log(`Reordered agenda item: ${e.id}`);}catch(i){console.error(`Move failed: ${i instanceof Error?i.message:String(i)}`),process.exit(1);}finally{r();}}async function Un(){return new Promise(e=>{let t=createInterface({input:process.stdin,output:process.stderr});process.stderr.write("Password: "),t.question("",n=>{t.close(),e(n);});})}async function Bs(e){let t=await ae(),n=e.scale??t.defaultScale??"fibonacci",o=e.name??t.defaultSessionName,r=e.password??t.defaultPassword??false,i;r&&(i=await Un());let s=Ot(n,t.presetOverrides,t.customScales);if(!s){let d=Object.keys(se).join(", ");console.error(`Unknown or fully-disabled scale: "${n}". Builtin IDs: ${d}`),process.exit(1);}let a=await Eo({participantId:t.participantId,displayName:t.displayName,relayUrl:t.relayUrl,sessionName:o,password:i,defaultScale:s,...t.avatarColor!==void 0&&{avatarColor:t.avatarColor}}),l;i&&(l=await Be(i,a.roomUuid)),Bt({roomCode:a.roomCode,roomUuid:a.roomUuid,passwordHash:l,lastSeenAt:Date.now()}),console.error(`Room created: ${a.roomCode}`),console.error(`Share: hnch join ${a.roomCode}`),console.error("");let{waitUntilExit:u,unmount:c}=render(zt.createElement(tn,{session:a,participantId:t.participantId,displayName:t.displayName,accessMode:i?"password":"open",presetOverrides:t.presetOverrides,customScales:t.customScales}));Ht({doc:a.doc,localParticipantId:t.participantId,onEnded:async d=>{console.error(d.isLocal?"Session ended.":`Session ended by ${d.hostName}.`),c();try{await F();}catch(p){console.error(`Failed to clear cached session: ${p instanceof Error?p.message:String(p)}`);}a.disconnect(),process.exit(0);}}),await u(),a.disconnect(),F(),process.exit(0);}async function Us(e){let t=await ae(),n=No(e.code);n===null&&(console.error("invalid room code"),process.exit(1));let o;e.password&&(o=await Un());let r=await de(t.relayUrl,n),i;r.accessMode==="password"&&!e.password?i=await Un():e.password&&(i=o);let s,a=false,l=await ko({participantId:t.participantId,displayName:t.displayName,relayUrl:t.relayUrl,roomCode:n,password:i,...t.avatarColor!==void 0&&{avatarColor:t.avatarColor},onRejection(d){if(d==="unknown-room"){Ut({clearLastSession:F,logger:console,exit:process.exit});return}d==="room-full"&&(a=true,s?.());}}),u;i&&(u=await Be(i,l.roomUuid)),Bt({roomCode:l.roomCode,roomUuid:l.roomUuid,passwordHash:u,lastSeenAt:Date.now()}),console.error(`Joined room: ${l.roomCode}`),console.error("");let c=render(zt.createElement(tn,{session:l,participantId:t.participantId,displayName:t.displayName,accessMode:r.accessMode,presetOverrides:t.presetOverrides,customScales:t.customScales}));s=c.unmount,Ht({doc:l.doc,localParticipantId:t.participantId,onEnded:async d=>{console.error(d.isLocal?"Session ended.":`Session ended by ${d.hostName}.`),s?.();try{await F();}catch(p){console.error(`Failed to clear cached session: ${p instanceof Error?p.message:String(p)}`);}l.disconnect(),process.exit(0);}}),await c.waitUntilExit(),a&&(console.error("This room is full (max 20 participants)."),l.disconnect(),F(),process.exit(1)),l.disconnect(),F(),process.exit(0);}async function Hs(){let e=await ae(),t=$t();t||(console.error("No recent session to resume."),process.exit(0));let n;try{n=await de(e.relayUrl,t.roomCode);}catch(c){if((c instanceof Error?c.message:String(c)).includes("Room not found"))return await Ut({clearLastSession:F,logger:console,exit:process.exit});console.error("Could not reach relay \u2014 try again later."),process.exit(0);}n.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 o=new $e.Doc,r=new Awareness(o);qe(o,{participantId:e.participantId,displayName:e.displayName,...e.avatarColor!==void 0&&{avatarColor:e.avatarColor}}),r.setLocalStateField("participantId",e.participantId);let i=false,s=false,a,l=Qe({doc:o,awareness:r,roomCode:t.roomCode,roomUuid:t.roomUuid,participantId:e.participantId,relayUrl:e.relayUrl,passwordHash:t.passwordHash,onRejection(c){if(c==="unknown-room"){Ut({clearLastSession:F,logger:console,exit:process.exit});return}c==="wrong-password"&&(i=true,a?.()),c==="room-full"&&(s=true,a?.());}});Bt({roomCode:l.roomCode,roomUuid:l.roomUuid,passwordHash:t.passwordHash,lastSeenAt:Date.now()});let u=render(zt.createElement(tn,{session:l,participantId:e.participantId,displayName:e.displayName,accessMode:n.accessMode,presetOverrides:e.presetOverrides,customScales:e.customScales}));a=u.unmount,Ht({doc:l.doc,localParticipantId:e.participantId,onEnded:async c=>{console.error(c.isLocal?"Session ended.":`Session ended by ${c.hostName}.`),a?.();try{await F();}catch(d){console.error(`Failed to clear cached session: ${d instanceof Error?d.message:String(d)}`);}l.disconnect(),process.exit(0);}}),await u.waitUntilExit(),s&&(console.error("This room is full (max 20 participants)."),F(),process.exit(1)),i&&(console.error(`Password changed \u2014 rejoin with: hnch join ${t.roomCode} --password`),F(),process.exit(0)),l.disconnect(),F(),process.exit(0);}async function Ls(e){if(!(e.name!==void 0||e.relayUrl!==void 0||e.scale!==void 0||e.sessionName!==void 0||e.password!==void 0||e.color!==void 0)){let o=await ae(),{waitUntilExit:r}=render(zt.createElement(rr,{presetOverrides:o.presetOverrides,customScales:o.customScales}));await r(),process.exit(0);}let n={};if(e.name!==void 0&&(n.displayName=e.name),e.relayUrl!==void 0&&(n.relayUrl=e.relayUrl),e.scale!==void 0){if(e.scale!==""){let o=He();if(!Ot(e.scale,o.presetOverrides,o.customScales)){console.error(`Unknown scale: "${e.scale}". Builtin IDs: ${Object.keys(se).join(", ")}`);return}}n.defaultScale=e.scale;}if(e.sessionName!==void 0&&(n.defaultSessionName=e.sessionName),e.password!==void 0&&(n.defaultPassword=e.password),e.color!==void 0){let o=e.color.startsWith("#")||e.color===""?e.color:e.color.toLowerCase();o!==""&&Oe(o)===null&&(console.error(`Invalid color: "${e.color}". Pass a palette key or #rrggbb hex literal.`),process.exit(1)),n.avatarColor=o;}ut(n),console.log("Config updated.");}var fr=Os(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=>{Bs(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=>{Us(e).catch(t=>{console.error(t instanceof Error?t.message:t),process.exit(1);});}).command("resume","Rejoin your last session",e=>e,()=>{Hs().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)"}).option("color",{type:"string",describe:'Set avatar color: a palette key (e.g. "blue", "racing-green") or a #rrggbb hex literal'}),e=>{Ls(e).catch(t=>{console.error(t instanceof Error?t.message:t),process.exit(1);});}).command("agenda","Manage the pre-session story agenda",e=>e.command("add","Add an agenda item",t=>t.option("title",{type:"string",describe:"Story title"}).option("ticket",{type:"string",describe:"Issue tracker reference (e.g. PROJ-123)"}).option("description",{type:"string",describe:"Story description"}).check(n=>{if(!n.title?.trim()&&!n.ticket?.trim())throw new Error("At least one of --title or --ticket is required");return true}),t=>{sr(t).catch(n=>{console.error(n instanceof Error?n.message:n),process.exit(1);});}).command("list","List agenda items in order",t=>t,()=>{ar().catch(t=>{console.error(t instanceof Error?t.message:t),process.exit(1);});}).command("skip <id>","Mark an agenda item as skipped",t=>t.positional("id",{type:"string",demandOption:true,describe:"Agenda item ID"}),t=>{dr(t).catch(n=>{console.error(n instanceof Error?n.message:n),process.exit(1);});}).command("cancel <id>","Soft-delete (cancel) an agenda item",t=>t.positional("id",{type:"string",demandOption:true,describe:"Agenda item ID"}),t=>{cr(t).catch(n=>{console.error(n instanceof Error?n.message:n),process.exit(1);});}).command("mv <id>","Reorder an agenda item (--before <id> | --after <id>)",t=>t.positional("id",{type:"string",demandOption:true,describe:"Agenda item ID to move"}).option("before",{type:"string",describe:"sortKey of item that should come before the moved item"}).option("after",{type:"string",describe:"sortKey of item that should come after the moved item"}).check(n=>{if(n.before!==void 0&&n.after!==void 0)throw new Error("--before and --after are mutually exclusive");if(n.before===void 0&&n.after===void 0)throw new Error("Specify either --before <id> or --after <id>");return true}),t=>{lr(t).catch(n=>{console.error(n instanceof Error?n.message:n),process.exit(1);});}).demandCommand(1,"Specify an agenda subcommand: add, list, skip, cancel, mv").strict().help(),()=>{}).demandCommand(1,"Specify a command: create, join, resume, config, or agenda").strict().help(),Bn=hideBin(process.argv);(Bn.length===0||Bn.length===1&&Bn[0]==="--")&&(fr.showHelp(),process.exit(0));fr.parse();
18
+ `}function de(e){return e==="revealed"||e==="skipped"||e==="discarded"}function et(e){return !de(e)}function Ut(e,t,n){if(e.startsWith("custom-")){let r=(n??[]).find(i=>i.id===e);return r?{id:r.id,cards:r.cards}:void 0}let o=ae[e];if(o){let r=(t??{})[e]??[];if(r.length===0)return o;let i=o.cards.filter((s,a)=>!r.includes(a));return i.length===0?void 0:{id:e,cards:i}}}function Bt(e,t){let n=Object.keys(ae).map(r=>Ut(r,e,t)).filter(r=>r!==void 0),o=(t??[]).map(r=>({id:r.id,cards:r.cards}));return [...n,...o]}function He(e,t){if(e.startsWith("custom-")){let n=(t??[]).find(o=>o.id===e);if(n&&n.label.trim().length>0)return n.label}return ao(e)}var Zi="https://hnch.dev";function ut(){let t=process.env.XDG_CONFIG_HOME||join(homedir(),".config");return join(t,"hnch")}function En(){return join(ut(),"config.json")}function Fo(){let e=ut();existsSync(e)||mkdirSync(e,{recursive:true});}function Le(){let e=En();if(!existsSync(e))return {};try{return JSON.parse(readFileSync(e,"utf-8"))}catch{return {}}}function F(e){Fo();let n={...Le(),...e};writeFileSync(En(),JSON.stringify(n,null,2)+`
19
+ `,"utf-8");}async function es(e){let t=createInterface({input:process.stdin,output:process.stderr});return new Promise(n=>{t.question(e,o=>{t.close(),n(o.trim());});})}function ts(e){return e.startsWith("wss://")?"https://"+e.slice(6):e.startsWith("ws://")?"http://"+e.slice(5):e}async function ce(){let e=Le(),t=false;if(e.participantId||(e.participantId=randomUUID(),t=true),!e.displayName){let o=await es("Enter your display name: ");e.displayName=o||`user-${e.participantId.slice(0,6)}`,t=true;}e.relayUrl||(e.relayUrl=Zi,t=true),(!e.avatarColor||Me(e.avatarColor)===null)&&(e.avatarColor=lo(e.participantId),t=true),t&&F(e);let n=process.env.HNCH_RELAY_URL||e.relayUrl;return n=ts(n),{participantId:e.participantId,displayName:e.displayName,relayUrl:n,defaultScale:e.defaultScale,defaultSessionName:e.defaultSessionName,defaultPassword:e.defaultPassword,presetOverrides:e.presetOverrides,customScales:e.customScales,avatarColor:e.avatarColor}}function pt(e){Fo();let n={...Le()};for(let[o,r]of Object.entries(e))r===""?delete n[o]:r!==void 0&&(n[o]=r);writeFileSync(En(),JSON.stringify(n,null,2)+`
20
+ `,"utf-8");}async function Vo(e,t){let n=await fetch(`${e}/rooms`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})});if(!n.ok)throw new Error(`POST /rooms failed: ${n.status}`);return await n.json()}async function le(e,t){let n=await fetch(`${e}/rooms/${encodeURIComponent(t)}`);if(n.status===404){let o=await n.json().catch(()=>null);throw new Error(`Room not found: ${o?.reason??"unknown-room"}`)}if(!n.ok)throw new Error(`GET /rooms/:code failed: ${n.status}`);return await n.json()}async function kn(e,t,n){let o=await fetch(`${e}/rooms/${t}/access`,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify({passwordHash:n})});if(!o.ok)throw new Error(`PATCH /rooms/:uuid/access failed: ${o.status}`)}function Yo(e){let n=`${e.wsBaseUrl.replace(/^http/,"ws")}/ws`,o={did:e.deviceId};e.passwordHash!==void 0&&(o.pw=e.passwordHash);let r=new WebsocketProvider(n,e.roomUuid,e.doc,{params:o,connect:true,...e.awareness!==void 0&&{awareness:e.awareness},WebSocketPolyfill:os}),i=u=>{e.onStatusChange?.(u.status);},s=u=>{if(u===null)return;let c=to.get(u.code);c!==void 0&&(e.onRejection?.(c),r.shouldConnect=false,r.disconnect());};r.on("status",i),r.on("connection-close",s);let a=false,d=e.passwordHash;return {doc:e.doc,awareness:r.awareness,updateCredentials(u){if(u.passwordHash===d)return;d=u.passwordHash??void 0,r.shouldConnect=false,r.disconnect();let l={did:r.params.did??""};d!==void 0&&(l.pw=d),r.params=l,r.connect();},disconnect(){a||(a=true,r.off("status",i),r.off("connection-close",s),r.shouldConnect=false,r.disconnect(),r.destroy());}}}function Jo(e,t,n){let o=null,r=null;function i(){let u=new Set;return t.getStates().forEach(c=>{let l=c.participantId;typeof l=="string"&&u.add(l);}),u}function s(){r!==null&&(clearTimeout(r),r=null),o=null;}function a(){r=null;let u=o;o=null;let c=Ao(j(e),i(),n,u);c.promote&&Qe(e,{hostParticipantId:c.winner});}function d(){let u=Io(j(e),i(),{missingHostId:o});switch(u.kind){case "cancel":s();break;case "start-debounce":s(),o=u.hostId,r=setTimeout(a,Co);break;}}return t.on("change",d),e.getMap(Dt).observe(d),d(),{stop(){s(),t.off("change",d),e.getMap(Dt).unobserve(d);}}}async function Go(e){let t,n=await Vo(e.relayUrl);e.password&&(t=await Be(e.password,n.uuid),await kn(e.relayUrl,n.uuid,t));let o=new $e.Doc,r=new Awareness(o),i=Date.now();return po(o,{roomUuid:n.uuid,createdAt:i,participantId:e.participantId,displayName:e.displayName,defaultScale:e.defaultScale??Rt,...e.sessionName!==void 0&&{sessionName:e.sessionName},...e.avatarColor!==void 0&&{avatarColor:e.avatarColor}}),r.setLocalStateField("participantId",e.participantId),tt({doc:o,awareness:r,roomCode:n.code,roomUuid:n.uuid,participantId:e.participantId,relayUrl:e.relayUrl,passwordHash:t})}async function qo(e){let t=await le(e.relayUrl,e.roomCode),n;if(t.accessMode==="password"){if(!e.password)throw new Error("Room requires a password");n=await Be(e.password,t.uuid);}let o=new $e.Doc,r=new Awareness(o);return Xe(o,{participantId:e.participantId,displayName:e.displayName,...e.avatarColor!==void 0&&{avatarColor:e.avatarColor}}),r.setLocalStateField("participantId",e.participantId),tt({doc:o,awareness:r,roomCode:e.roomCode,roomUuid:t.uuid,participantId:e.participantId,relayUrl:e.relayUrl,passwordHash:n,onRejection:e.onRejection})}function tt(e){let{doc:t,awareness:n,participantId:o,relayUrl:r,roomUuid:i}=e,s=new Set,a=new Set,d=new Set,u=Yo({wsBaseUrl:e.relayUrl,roomUuid:e.roomUuid,doc:t,deviceId:o,passwordHash:e.passwordHash,awareness:n,onStatusChange(f){s.forEach(h=>h(f));},onRejection(f){console.error(`Connection rejected: ${f}`),e.onRejection?.(f);}}),c=Jo(t,n,o),l=()=>{a.forEach(f=>f());};t.getMap("meta").observeDeep(l),t.getMap("settings").observeDeep(l),t.getMap("participants").observeDeep(l),t.getArray("rounds").observeDeep(l);function p(){let f=new Set;return n.getStates().forEach(h=>{let b=h.participantId;typeof b=="string"&&f.add(b);}),f}let A=new Set,N=setInterval(()=>{let f=j(t),h=G(f);!h||A.has(h.id)||co(h,f.participants,p())&&(A.add(h.id),wn(t,h.id,Date.now()));},250),T=t.getMap("settings").get("hostParticipantId"),R=()=>{let f=t.getMap("settings").get("hostParticipantId");f!==T&&f!==void 0&&d.forEach(h=>h(f)),T=f;};t.getMap("settings").observe(R);let U=false;return {doc:t,awareness:n,roomCode:e.roomCode,roomUuid:e.roomUuid,projection(){return j(t)},current(){return G(j(t))},isHost(){return j(t).settings.hostParticipantId===o},onlineIds:p,vote(f){let h=G(j(t));h&&mo(t,h.id,o,f);},unvote(){let f=G(j(t));f&&go(t,f.id,o);},reveal(){let f=G(j(t));f&&et(f.status)&&wn(t,f.id,Date.now());},skip(){let f=G(j(t));f&&et(f.status)&&So(t,f.id,Date.now());},newRound(f,h,b){let y=randomUUID(),O=Date.now();return Sn(t,{id:y,createdAt:O,createdBy:o,...f!==void 0&&{title:f},...h!==void 0&&{ticketId:h},...b!==void 0&&{agendaItemId:b}}),y},discardCurrentRound(){let f=G(j(t));f&&et(f.status)&&xo(t,f.id,Date.now(),o);},revote(){let f=G(j(t)),h=randomUUID();return Sn(t,{id:h,createdAt:Date.now(),createdBy:o,...f?.id!==void 0&&{parentRoundId:f.id},...f?.title!==void 0&&{title:f.title},...f?.description!==void 0&&{description:f.description},...f?.ticketId!==void 0&&{ticketId:f.ticketId},...f?.agendaItemId!==void 0&&{agendaItemId:f.agendaItemId}}),h},updateRoundDescriptor(f,h){fo(t,f,h);},updateName(f){Qe(t,{name:f});},updateScale(f){Qe(t,{defaultScale:f});},updateDisplayName(f){let y=j(t).participants.find(O=>O.id===o)?.joinedAt??Date.now();hn(t,o,{displayName:f,joinedAt:y});},async updateAccess(f,h){let b=null;if(f==="password"){if(!h)throw new Error("Password required for password mode");b=await Be(h,i);}await kn(r,i,b),u.updateCredentials({passwordHash:b});},handoffHost(f){if(!this.isHost())throw new Error("Only the current host can hand off");if(!p().has(f))throw new Error("Target participant is not online");if(!j(t).participants.some(O=>O.id===f))throw new Error("Target participant does not exist");Qe(t,{hostParticipantId:f});},toggleRole(){let h=j(t).participants.find(C=>C.id===o),y=Ae(h??{})==="viewer"?"voter":"viewer",O=h?.joinedAt??Date.now();hn(t,o,{displayName:h?.displayName??"",joinedAt:O,role:y});},endSessionForEveryone(){if(!this.isHost())throw new Error("Only the current host can end the session");wo(t);},onChange(f){return a.add(f),()=>a.delete(f)},onStatusChange(f){return s.add(f),()=>s.delete(f)},onHostChange(f){return d.add(f),()=>d.delete(f)},disconnect(){U||(U=true,clearInterval(N),c.stop(),t.getMap("meta").unobserveDeep(l),t.getMap("settings").unobserveDeep(l),t.getMap("settings").unobserve(R),t.getMap("participants").unobserveDeep(l),t.getArray("rounds").unobserveDeep(l),u.disconnect());}}}function zo(e){let t=bn(e);return t===""?null:t}function je(){return join(ut(),"last-session.json")}function ds(e,t){let n=ut();mkdirSync(n,{recursive:true,mode:448}),chmodSync(n,448),writeFileSync(e,t,{encoding:"utf-8",mode:384}),chmodSync(e,384);}function Pn(){let e=je();try{unlinkSync(e);}catch(t){if(t.code!=="ENOENT")throw t}}function Dn(){let e=je();if(!existsSync(e))return {};try{let t=JSON.parse(readFileSync(e,"utf-8"));return {...Cn(t).entries}}catch{return {}}}function On(e){let t={version:2,entries:e};ds(je(),JSON.stringify(t,null,2)+`
21
+ `);}function Lt(e){let t=Date.now();if(!existsSync(je()))return [];let n;try{n=JSON.parse(readFileSync(je(),"utf-8"));}catch{return []}let o=Cn(n),r=Eo(o,t),i=Object.values(r.entries);return i.length===0?(Pn(),[]):i.toSorted((s,a)=>a.lastSeenAt-s.lastSeenAt)}function cs(e,t){return e!==void 0?{passwordHash:e}:t!==void 0?{passwordHash:t}:{}}function Mn(e){let t=Date.now(),n=Dn(),o=n[e.roomUuid];n[e.roomUuid]={roomCode:e.roomCode,roomUuid:e.roomUuid,lastSeenAt:e.lastSeenAt??t,...cs(e.passwordHash,o?.passwordHash)};let r=Object.keys(n);if(r.length>5){let i=r.toSorted((s,a)=>(n[s]?.lastSeenAt??0)-(n[a]?.lastSeenAt??0)).slice(0,r.length-5);for(let s of i)delete n[s];}On(n);}function ft(e){if(!existsSync(je()))return;let t=Dn();if(delete t[e],Object.keys(t).length===0){Pn();return}On(t);}function ls(){Pn();}function $n(e){if(!existsSync(je()))return;let t=Dn(),n=t[e];if(!n)return;let{passwordHash:o,...r}=n;t[e]=r,On(t);}function Un(e){Mn(e);}function Se(){ls();}async function jt(e){let t=true;try{await e.clearLastSession();}catch(n){t=false,e.logger.error(`Failed to clear cached session: ${n instanceof Error?n.message:String(n)}`);}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 _t(e){let t=e.doc.getMap("meta"),n=null,o=false;function r(){if(o)return;let i=t.get("sessionEndedAt");if(i==null||i===n)return;n=i;let s=t.get("createdBy"),a=s===e.localParticipantId,d=e.doc.getMap("participants"),u=e.doc.getMap("settings").get("hostParticipantId")??s,c="host";if(u){let p=d.get(u)?.get("displayName");typeof p=="string"&&p.length>0&&(c=p);}setImmediate(()=>{o||e.onEnded({isLocal:a,hostName:c,timestamp:i});});}return t.observe(r),r(),()=>{o=true,t.unobserve(r);}}var us="@xauyxau/hnch";function Ft(e){return Mo(e)}function Vt(e){return `${process.env.HNCH_SHARE_ORIGIN??"https://hnch.dev"}/join?code=${e}`}function Yt(e){return `npx ${us} join ${e}`}function Bn(e){let t=e.trim();return t===""?void 0:t}function Jt(e){return e?{title:e.title??"",ticketId:e.ticketId??"",agendaItemId:e.id}:{title:"",ticketId:"",agendaItemId:void 0}}function er(e,t,n){return [e,Bn(t),n]}var ps={connecting:"yellow",connected:"green",disconnected:"red"};function or({roomCode:e,sessionName:t,status:n,isHost:o}){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:[o&&jsx(Text,{color:"yellow",children:"[host]"}),jsx(Text,{color:ps[n],children:n})]})]})}function me({items:e,onSelect:t,onClose:n,title:o}){if(e.length===0)return jsxs(Box,{flexDirection:"column",gap:1,children:[o&&jsx(Text,{bold:true,children:o}),jsx(Text,{dimColor:true,children:"(no items)"})]});let r=e.findIndex(a=>!a.disabled),[i,s]=useState(Math.max(r,0));return useInput((a,d)=>{if(d.upArrow)s(u=>{let c=(u-1+e.length)%e.length,l=0;for(;e[c]?.disabled&&l<e.length;)c=(c-1+e.length)%e.length,l++;return c});else if(d.downArrow)s(u=>{let c=(u+1)%e.length,l=0;for(;e[c]?.disabled&&l<e.length;)c=(c+1)%e.length,l++;return c});else if(d.return){let u=e[i];u&&!u.disabled&&t(u.value);}else d.escape&&n?.();}),jsxs(Box,{flexDirection:"column",gap:1,children:[o&&jsx(Text,{bold:true,children:o}),jsx(Box,{flexDirection:"column",children:e.map((a,d)=>{let u=d===i,c=a.disabled??false;return jsxs(Box,{gap:1,children:[jsx(Text,{...u&&{color:"cyan"},dimColor:c,children:u?">":" "}),jsx(Text,{bold:u,dimColor:c,children:a.label}),a.detail&&jsx(Text,{dimColor:true,children:a.detail})]},a.value)})}),jsx(Text,{dimColor:true,children:"\u2191\u2193 select \u23CE confirm esc close"})]})}function _({value:e,onChange:t,onSubmit:n,onCancel:o,placeholder:r,isActive:i=true,mask:s=false}){useInput((c,l)=>{l.escape?o?.():l.return?n(e):l.backspace||l.delete?t(e.slice(0,-1)):c&&!l.ctrl&&!l.meta&&t(e+c);},{isActive:i});let a=s&&e.length>0?"*".repeat(e.length):e,d=a.length>0?a:r??"",u=e.length===0&&r!==void 0;return jsxs(Text,{children:[jsx(Text,{dimColor:u,children:d}),jsx(Text,{inverse:true,children:" "})]})}function ar({mode:e,password:t,onModeChange:n,onPasswordChange:o,onClose:r}){let[i,s]=useState(e==="open"?0:1),[a,d]=useState(false);return useEffect(()=>{s(e==="open"?0:1);},[e]),useInput((u,c)=>{if(c.leftArrow)s(l=>Math.max(0,l-1));else if(c.rightArrow)s(l=>Math.min(1,l+1));else if(c.return){let l=i===0?"open":"password";l!==e&&n(l),l==="password"&&d(true);}else c.escape&&r?.();},{isActive:!a}),jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Box,{gap:1,children:["open","password"].map((u,c)=>{let l=c===i,p=u===e,A=u==="open"?"Open":"Password";return jsx(Text,{inverse:l,bold:p,...p&&{color:"cyan"},children:p?`[${A}]`:` ${A} `},u)})}),e==="password"&&jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{children:"Password: "}),jsx(_,{value:t,onChange:o,onSubmit:()=>d(false),onCancel:()=>d(false),placeholder:"(required to join)",isActive:a,mask:true}),!a&&jsx(Text,{dimColor:true,children:"\u23CE edit \u2190 \u2192 switch mode esc close"}),a&&jsx(Text,{dimColor:true,children:"\u23CE done esc cancel"})]}),e==="open"&&jsx(Text,{dimColor:true,children:"\u2190\u2192 toggle \u23CE confirm esc close"})]})}function lr({scaleId:e,cards:t,disabledIndices:n,onOverrideChange:o,onClose:r}){let i=useMemo(()=>new Set(n),[n]),[s,a]=useState(0),d=u=>{let c=new Set(i);if(c.has(u))c.delete(u);else {if(c.size>=t.length-1)return;c.add(u);}let l=Array.from(c).toSorted((p,A)=>p-A);o(e,l);};return useInput((u,c)=>{c.leftArrow?a(l=>Math.max(0,l-1)):c.rightArrow?a(l=>Math.min(t.length-1,l+1)):u===" "?d(s):(c.return||c.escape)&&r();}),jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Box,{gap:1,children:t.map((u,c)=>{let l=c===s,p=i.has(c);return jsx(Text,{inverse:l,bold:!p,...!p&&{color:"cyan"},strikethrough:p,dimColor:p,children:u},u)})}),jsx(Text,{dimColor:true,children:" \u2190\u2192 navigate space toggle \u23CE/esc done"})]})}function Jn({initialName:e="",initialCards:t=[],onSave:n,onCancel:o}){let r=e.length>0,[i,s]=useState(e),[a,d]=useState(t.length>0?t:[""]),[u,c]=useState(0),[l,p]=useState("editing-name"),[A,N]=useState(""),T=()=>{let f=i.trim();if(f.length===0||f.length>40){N("Name must be 1-40 characters");return}s(f),N(""),p("editing-cards");},R=()=>{let f=i.trim();if(f.length===0||f.length>40){N("Name must be 1-40 characters");return}let h=a.map(y=>y.trim()).filter(y=>y.length>0),b=Array.from(new Set(h));if(b.length<2){N("Need at least 2 unique non-empty cards");return}N(""),n(f,b);};return useInput((f,h)=>{if(l==="editing-cards"){if(h.leftArrow)c(b=>Math.max(0,b-1));else if(h.rightArrow)c(b=>{let y=b+1;return y>=a.length&&a.length<25?(d(O=>[...O,""]),y):Math.min(y,a.length-1)});else if(h.return)R();else if(h.escape)o();else if(h.tab)p("editing-name");else if(h.backspace){let b=a[u]||"";if(b.length===0){if(a.length>2){let y=a.filter((O,C)=>C!==u);d(y),c(Math.max(0,u-1));}}else {let y=[...a];y[u]=b.slice(0,-1),d(y);}}else if(f&&!h.ctrl&&!h.meta){let b=a[u]||"";if(b.length<10){let y=[...a];y[u]=b+f,d(y);}}}},{isActive:l==="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(_,{value:i,onChange:s,onSubmit:T,onCancel:o,placeholder:"Scale name",isActive:l==="editing-name"})]}),l==="editing-cards"&&jsxs(Box,{flexDirection:"column",gap:0,children:[jsx(Text,{children:"Cards:"}),jsx(Box,{gap:1,flexWrap:"wrap",children:a.map((f,h)=>{let b=h===u;return jsx(Text,{inverse:b,...!b&&{color:"cyan"},children:f.length>0?f:"\xB7"},h)})})]}),A&&jsx(Text,{color:"red",children:A}),jsx(Text,{dimColor:true,children:l==="editing-name"?"\u23CE next esc cancel":"\u2190\u2192 navigate typing adds card bsp delete \u23CE save tab name esc cancel"})]})}function Gt({scales:e,selectedScaleId:t,presetOverrides:n,customScales:o=[],onSelect:r,onOverrideChange:i,onCustomScaleCreate:s,onCustomScaleEdit:a,onCustomScaleDelete:d,onClose:u,showNoneOption:c=false}){let[l,p]=useState(0),[A,N]=useState(null),[T,R]=useState("browsing"),[U,f]=useState(null),[h,b]=useState(null),y=s?[...e,{id:"__create-custom__",cards:[]}]:e,O=y.length;if(useInput((C,M)=>{if(T==="browsing"){if(M.escape){u();return}if(O===0)return;if(M.upArrow)p(I=>(I-1+O)%O);else if(M.downArrow)p(I=>(I+1)%O);else if(M.return){let I=y[l];if(!I)return;if(I.id==="__create-custom__"){R("creating");return}I.id in ae?N(A===I.id?null:I.id):r(I.id);}else if(C===" "){let I=y[l];I&&I.id!=="__create-custom__"&&r(I.id);}else if(C==="e"||C==="E"){let I=y[l];a&&I?.id.startsWith("custom-")&&(f(I.id),R("editing"));}else if(C==="d"||C==="D"){let I=y[l];d&&I?.id.startsWith("custom-")&&(b(I.id),R("confirming-delete"));}}else T==="confirming-delete"&&(C==="y"||C==="Y"?(h&&d&&d(h),b(null),R("browsing")):(C==="n"||C==="N"||M.escape)&&(b(null),R("browsing")));},{isActive:T==="browsing"&&A===null||T==="confirming-delete"}),T==="creating")return jsx(Jn,{onSave:(C,M)=>{s&&s(C,M),R("browsing");},onCancel:()=>R("browsing")});if(T==="editing"&&U){let C=o?.find(I=>I.id===U);if(e.find(I=>I.id===U)&&C)return jsx(Jn,{initialName:C.label,initialCards:C.cards,onSave:(I,P)=>{a&&a(U,I,P),f(null),R("browsing");},onCancel:()=>{f(null),R("browsing");}})}if(T==="confirming-delete"&&h){let C=y.find(I=>I.id===h),M=C?He(C.id,o):"Unknown";return jsxs(Box,{flexDirection:"column",gap:1,children:[jsxs(Text,{children:["Delete ",M,"? "]}),jsx(Text,{children:"[y] yes [n] no esc cancel"})]})}return y.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:y.map((C,M)=>{let I=M===l,P=C.id===t,ue=A===C.id,re=C.id in ae,ie=C.id==="__create-custom__",V=C.cards.join(", "),z=c&&C.id==="",X=z?"(none)":ie?"[+ Create custom scale]":He(C.id,o);return jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{...I&&{color:"cyan"},children:I?">":" "}),jsx(Text,{bold:I,children:X}),!z&&!ie&&jsx(Text,{dimColor:true,children:V}),P&&jsx(Text,{color:"cyan",children:"[current]"})]}),ue&&!z&&!ie&&jsx(Box,{paddingLeft:2,children:re?jsx(lr,{scaleId:C.id,cards:ae[C.id]?.cards??[],disabledIndices:n?.[C.id]??[],onOverrideChange:i,onClose:()=>N(null)}):null})]},C.id||"none")})}),jsx(Text,{dimColor:true,children:(()=>{let C=l<y.length?y[l]:null;return C?.id.startsWith("custom-")?`\u2191\u2193 navigate space select${a?" [e] edit":""}${d?" [d] delete":""} esc back`:C&&C.id in ae?"\u2191\u2193 navigate \u23CE customize space select esc back":"\u2191\u2193 navigate \u23CE/space select esc back"})()})]})}function Ts(e,t){return t?[...e]:e.filter(n=>n.value==="display-name")}function ur({sessionName:e,displayName:t,currentScale:n,accessMode:o,presetOverrides:r,customScales:i,isHost:s=true,onChangeName:a,onChangeScale:d,onChangeDisplayName:u,onChangeAccess:c,onClose:l}){let[p,A]=useState(r??{}),N=useMemo(()=>Bt(p,i),[p,i]),[T,R]=useState("main-menu"),[U,f]=useState(e??""),[h,b]=useState(t),[y,O]=useState(o==="password"?"password":"open"),[C,M]=useState(""),[I,P]=useState(false),ue=[{label:"Room name",value:"room-name",detail:`[${e??"(none)"}]`},{label:"Scale",value:"scale",detail:`[${He(n.id,i)}]`},{label:"Access",value:"access",detail:`[${o==="open"?"Open":"Password"}]`},{label:"Display name",value:"display-name",detail:`[${t}]`}],re=Ts(ue,s);function ie(m){!s&&m!=="display-name"||(m==="room-name"?(f(e??""),R("editing-room-name")):m==="scale"?R("picking-scale"):m==="access"?R("picking-access"):m==="display-name"&&(b(t),R("editing-display-name")));}function V(m){let x=m.trim()===""?void 0:m.trim();a(x),R("main-menu");}function z(m){let x=m.trim();x.length>0&&(u(x),process.nextTick(()=>F({displayName:x}))),R("main-menu");}function X(m){let x=N.find(E=>E.id===m);x&&d(x),R("main-menu");}function ve(m,x){let E={...p};x.length===0?delete E[m]:E[m]=x,A(E),F({presetOverrides:Object.keys(E).length>0?E:void 0});}async function ge(m,x){P(true);try{await c(m,x),O(m),M(m==="password"?x??"":"");}catch(E){console.error(`Failed to update access: ${E}`);}finally{P(false),R("main-menu");}}return T==="main-menu"?jsx(me,{title:"\u2500\u2500 Settings \u2500\u2500",items:re,onSelect:ie,onClose:l}):T==="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(_,{value:U,onChange:f,onSubmit:V,onCancel:()=>R("main-menu"),placeholder:"(leave empty to remove)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]}):T==="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(_,{value:h,onChange:b,onSubmit:z,onCancel:()=>R("main-menu"),placeholder:"(required)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]}):T==="picking-scale"?jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Settings \u2500\u2500"}),jsx(Gt,{scales:N,selectedScaleId:n.id,presetOverrides:p,onSelect:X,onOverrideChange:ve,onClose:()=>R("main-menu")})]}):T==="picking-access"?jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Settings \u2500\u2500"}),I&&jsx(Text,{dimColor:true,children:"Updating access..."}),!I&&jsx(ar,{mode:y,password:C,onModeChange:m=>{m==="open"&&ge("open");},onPasswordChange:M,onClose:()=>R("main-menu")})]}):jsx(Box,{})}function Xt({participants:e,onlineIds:t,hostId:n,votes:o,revealed:r,participantId:i}){return jsxs(Box,{flexDirection:"column",children:[jsxs(Text,{bold:true,children:["Participants (",e.length,")"]}),e.map(s=>{let a=t.has(s.id),d=a?"\u25CF":"\u25CB",u=a?"green":"gray",c=s.id===n,l=Ae(s)==="viewer",p=s.id===i,A=s.id in o,N=l?" (viewer)":r?o[s.id]?` \u2192 ${o[s.id]}`:"":A?" \u2713":" \xB7",T=p&&!l&&!(r&&o[s.id]);return jsxs(Box,{gap:1,children:[jsx(Text,{color:u,children:d}),jsxs(Text,{children:[s.displayName,c?" (host)":""]}),T?jsx(Text,{color:"cyan",children:"(voter)"}):jsx(Text,{color:l?"gray":A?"green":"gray",children:N})]},s.id)})]})}var Rs=[{label:"End session for everyone",value:"end-session",detail:"Disconnects all participants. Cannot be undone."}];function fr({onPick:e,onClose:t}){return jsx(me,{title:"Host Actions",items:Rs,onSelect:e,onClose:t})}function Qt({roomCode:e,onPick:t,onClose:n}){let o=[{label:"Copy code",value:"code",detail:Ft(e)},{label:"Copy share link",value:"url",detail:Vt(e)},{label:"Copy npx command",value:"npx",detail:Yt(e)}];return jsx(me,{title:"Share",items:o,onSelect:r=>t(r),onClose:n})}function Ns(e){let t=e.rounds.filter(o=>de(o.status)),n=Object.fromEntries(e.participants.map(o=>[o.id,o]));return Lo({rounds:t,participants:n})}async function mr(e,t,n){let o=Ns(e);try{await t(o),n("Results copied to clipboard");}catch{n("Clipboard write failed");}}function gr(e,t,n){let o=No(e.id,n);return o===null?`Round ${t}`:`Round ${o.depth}/${o.total} [re-vote]`}function hr(e,t,n){let o=gr(e,t,n),r=e.title?` \u2014 ${e.title}`:"",i=e.ticketId?` [${e.ticketId}]`:"",s=e.status==="skipped"?" \u2014 Skipped":" \u2014 Results";return `${o}${r}${i}${s}`}function Sr(e){let t=Object.values(e).filter(c=>!J(c)).map(c=>parseFloat(c)).filter(c=>!isNaN(c)).toSorted((c,l)=>c-l);if(t.length===0)return {average:"-",median:"-",agreement:"-"};let o=t.reduce((c,l)=>c+l,0)/t.length,r=Math.floor(t.length/2),i=t.length%2===0?(t[r-1]+t[r])/2:t[r],s=Object.values(e).filter(c=>!J(c)),a=new Map;for(let c of s)a.set(c,(a.get(c)??0)+1);let d=Math.max(...a.values()),u=s.length>0?Math.round(d/s.length*100):0;return {average:o.toFixed(1),median:i.toFixed(1),agreement:`${u}%`}}function Kn(e,t){let n=Math.floor((t-e)/1e3),o=Math.floor(n/60),r=n%60;return `${o}:${r.toString().padStart(2,"0")}`}function xr(e,t,n=[]){let o=gr(e,t,n);if(e.status==="skipped"){let l=e.title?` ${e.title}`:"",p=e.ticketId?` [${e.ticketId}]`:"";return `${o}:${l}${p} \u2014 Skipped`}let r=new Map;for(let l of Object.values(e.votes))J(l)||r.set(l,(r.get(l)??0)+1);let i=[...r.entries()].toSorted((l,p)=>p[1]-l[1]||l[0].localeCompare(p[0])).map(([l,p])=>`${l}\xD7${p}`).join(", "),s=Object.values(e.votes).filter(J).length,a=s>0?` (${s} abstained)`:"",d=e.title?` ${e.title}`:"",u=e.ticketId?` [${e.ticketId}]`:"",c=e.timerStartedAt!==void 0&&e.status==="revealed"&&e.terminalAt!==void 0?` \u2014 ${Kn(e.timerStartedAt,e.terminalAt)}`:"";return `${o}:${d}${u} \u2014 ${i||"(no votes)"}${a}${c}`}function wr({projection:e,onlineIds:t,isHost:n,participantId:o,rounds:r,currentRound:i,settingsOpen:s,onStartRound:a,onNewRound:d,onRevote:u,onOpenSettings:c,onHandoffHost:l,onEndSession:p,onShare:A,shareOpen:N,onSetShareOpen:T,roomCode:R,confirmingQuit:U,myRole:f,onToggleRole:h,roundTitlePromptActive:b,roundTitleInput:y,onRoundTitleChange:O,onRoundTitleSubmit:C,onRoundTitleCancel:M,roundTicketIdPromptActive:I,roundTicketIdInput:P,onRoundTicketIdChange:ue,onRoundTicketIdSubmit:re,onRoundTicketIdCancel:ie,descriptorPromptActive:V,descriptorInput:z,onDescriptorChange:X,onDescriptorSubmit:ve,onDescriptorCancel:ge,onEditDescriptor:m,canExport:x,onExportResults:E,onAgendaAdd:Y,agendaAddPromptActive:pe,agendaAddTitleInput:it,onAgendaAddTitleChange:dn,onAgendaAddTitleSubmit:We,onAgendaAddTitleCancel:Ct,agendaAddTicketIdPromptActive:Ce,agendaAddTicketIdInput:cn,onAgendaAddTicketIdChange:Ke,onAgendaAddTicketIdSubmit:Ge,onAgendaAddTicketIdCancel:Pe}){let[Q,Z]=Zt.useState("idle"),[he,be]=Zt.useState(null),[st,se]=Zt.useState(false),[fe,De]=Zt.useState(false);useEffect(()=>{n||(Z("idle"),be(null),se(false),De(false));},[n]),useInput(w=>{if(w==="c"||w==="C"){T(true);return}if(w==="w"||w==="W"){h();return}if((w==="x"||w==="X")&&x){E();return}if(n){if(w==="a"||w==="A"){Y();return}(w==="e"||w==="E")&&c(),(w==="s"||w==="S")&&a(),(w==="h"||w==="H")&&Z("picking"),(w==="m"||w==="M")&&se(true),i!==null&&de(i.status)&&((w==="n"||w==="N")&&d(),(w==="v"||w==="V")&&i.status!=="skipped"&&u(),(w==="t"||w==="T")&&m());}},{isActive:!s&&!U&&Q==="idle"&&!b&&!I&&!st&&!fe&&!N&&!V&&!pe&&!Ce}),useInput((w,ee)=>{w==="y"||w==="Y"?(p(),De(false)):(w==="n"||w==="N"||ee.escape)&&De(false);},{isActive:fe});let Ie=e.participants.filter(w=>w.id!==o&&t.has(w.id));useInput(w=>{if(Q==="picking"){let ee=parseInt(w,10);!isNaN(ee)&&ee>=1&&ee<=Ie.length?(be(Ie[ee-1].id),Z("confirming")):(w==="Escape"||w==="q"||w==="Q")&&Z("idle");}},{isActive:Q==="picking"}),useInput(w=>{Q==="confirming"&&he&&(w==="y"||w==="Y"?(l(he),Z("idle"),be(null)):(w==="n"||w==="N"||w==="Escape")&&(Z("idle"),be(null)));},{isActive:Q==="confirming"});let Oe=r.length>0,bt=r.filter(w=>de(w.status)),at=i!==null?bt.slice(0,-1):bt;return st?jsx(fr,{onPick:w=>{se(false),w==="end-session"&&De(true);},onClose:()=>se(false)}):N?jsx(Qt,{roomCode:R,onPick:w=>{T(false),A(w);},onClose:()=>T(false)}):jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:Oe?"Session":"Waiting for participants..."}),!Oe&&jsx(Text,{dimColor:true,children:"No rounds started yet."}),jsx(Xt,{participants:e.participants,onlineIds:t,hostId:e.settings.hostParticipantId,votes:{},revealed:false,participantId:o}),e.agenda.length>0&&jsx($s,{items:e.agenda,nextPending:Ze(e)}),i!==null&&de(i.status)&&jsx(Ds,{projection:e,round:i,rounds:r}),at.length>0&&jsxs(Box,{flexDirection:"column",children:[jsx(Text,{dimColor:true,children:"\u2500\u2500 History \u2500\u2500"}),at.map(w=>{let ee=r.indexOf(w)+1;return jsx(Text,{dimColor:true,children:xr(w,ee,r)},w.id)})]}),Q==="picking"&&Ie.length>0&&jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{children:"Hand off to:"}),Ie.map((w,ee)=>jsxs(Text,{children:["[",ee+1,"] ",w.displayName]},w.id)),jsx(Text,{dimColor:true,children:"[q] Cancel"})]}),Q==="picking"&&Ie.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"})]}),Q==="confirming"&&he&&jsx(Box,{flexDirection:"column",children:jsxs(Text,{children:["Hand off to ",e.participants.find(w=>w.id===he)?.displayName,"? [y/n]"]})}),b&&jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{children:"Round title (optional, Enter to skip):"}),jsx(_,{value:y,onChange:O,onSubmit:C,onCancel:M,placeholder:"Enter to skip",isActive:b})]}),jsx(Text,{dimColor:true,children:"[Esc] Cancel"})]}),I&&jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{children:"Ticket ID (optional, Enter to skip):"}),jsx(_,{value:P,onChange:ue,onSubmit:re,onCancel:ie,placeholder:"e.g. PROJ-1234",isActive:I})]}),jsx(Text,{dimColor:true,children:"[Esc] Cancel"})]}),V&&jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{children:"Edit round title:"}),jsx(_,{value:z,onChange:X,onSubmit:ve,onCancel:ge,placeholder:"Enter to save",isActive:V})]}),jsx(Text,{dimColor:true,children:"[Esc] Cancel \xB7 empty submit cancels"})]}),fe&&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"})]}),pe&&jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{children:"Agenda title (optional, Enter to skip):"}),jsx(_,{value:it,onChange:dn,onSubmit:We,onCancel:Ct,placeholder:"Enter to skip",isActive:pe})]}),jsx(Text,{dimColor:true,children:"[Esc] Cancel"})]}),Ce&&jsxs(Box,{flexDirection:"column",children:[jsxs(Box,{gap:1,children:[jsx(Text,{children:"Agenda ticket ID (optional, Enter to skip):"}),jsx(_,{value:cn,onChange:Ke,onSubmit:Ge,onCancel:Pe,placeholder:"e.g. PROJ-1234",isActive:Ce})]}),jsx(Text,{dimColor:true,children:"[Esc] Cancel"})]}),Q==="idle"&&!b&&!I&&!V&&!fe&&!pe&&!Ce&&jsxs(Box,{gap:2,children:[n?jsx(Fragment,{children:i!==null&&de(i.status)?jsxs(Fragment,{children:[i.status!=="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"}),x&&jsx(Text,{color:"yellow",children:"[x] Export"}),jsxs(Text,{color:"yellow",children:["[w] ",f==="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:"[a] Agenda"}),jsx(Text,{color:"yellow",children:"[h] Handoff"}),jsx(Text,{color:"yellow",children:"[e] Settings"}),jsx(Text,{color:"yellow",children:"[c] Share"}),x&&jsx(Text,{color:"yellow",children:"[x] Export"}),jsxs(Text,{color:"yellow",children:["[w] ",f==="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"}),x&&jsx(Text,{color:"yellow",children:"[x] Export"}),jsxs(Text,{color:"yellow",children:["[w] ",f==="viewer"?"Vote":"Observe"]})]}),jsx(Text,{children:"[q] Quit"})]})]})}function Ds({projection:e,round:t,rounds:n}){let o=n.indexOf(t)+1,r=hr(t,o,n);if(t.status==="skipped")return jsx(Box,{flexDirection:"column",gap:1,children:jsx(Text,{bold:true,children:r})});let i=Sr(t.votes),s=new Map(e.participants.map(p=>[p.id,p.displayName])),a=t.timerStartedAt!==void 0&&t.status==="revealed"&&t.terminalAt!==void 0?Kn(t.timerStartedAt,t.terminalAt):null,d=Object.entries(t.votes),u=d.filter(([,p])=>!J(p)),c=d.filter(([,p])=>J(p)).map(([p])=>p),l=u[0]?.[1];return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:r}),yo(t)&&l!==void 0&&jsxs(Text,{bold:true,color:"green",children:["Consensus! Everyone voted ",l]}),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)})]}),u.map(([p,A])=>jsxs(Box,{gap:2,children:[jsx(Text,{children:(s.get(p)??p).padEnd(20)}),jsx(Text,{color:"cyan",bold:true,children:A.padEnd(10)})]},p)),c.map(p=>jsxs(Box,{gap:2,children:[jsx(Text,{dimColor:true,children:(s.get(p)??p).padEnd(20)}),jsx(Text,{dimColor:true,children:"(abstained)".padEnd(10)})]},p)),e.participants.filter(p=>!(p.id in t.votes)).map(p=>jsxs(Box,{gap:2,children:[jsx(Text,{dimColor:true,children:p.displayName.padEnd(20)}),jsx(Text,{dimColor:true,children:"(no vote)".padEnd(10)})]},p.id))]}),jsxs(Box,{gap:2,children:[jsxs(Text,{children:["Avg: ",jsx(Text,{bold:true,children:i.average})]}),jsxs(Text,{children:["Med: ",jsx(Text,{bold:true,children:i.median})]}),jsxs(Text,{children:["Agreement: ",jsx(Text,{bold:true,children:i.agreement})]}),a!==null&&jsxs(Text,{dimColor:true,children:["\u23F1 ",a]})]})]})}var Os={pending:"\u2022",consumed:"\u2713",skipped:"~",cancelled:"\u2717"},Ms={consumed:"green",skipped:"yellow",cancelled:"red"};function $s({items:e,nextPending:t}){let n=e.filter(o=>o.state!=="cancelled");return jsxs(Box,{flexDirection:"column",children:[jsx(Text,{dimColor:true,children:"\u2500\u2500 Agenda \u2500\u2500"}),n.map(o=>{let r=Os[o.state]??"?",i=Ms[o.state],s=t!==null&&o.id===t.id,a=[o.ticketId?`[${o.ticketId}]`:"",o.title??""].filter(Boolean).join(" ").trim()||o.id;return jsxs(Box,{gap:1,children:[i!==void 0?jsx(Text,{color:i,children:r}):jsx(Text,{children:r}),i!==void 0?jsx(Text,{color:i,children:a}):jsx(Text,{children:a}),s&&jsx(Text,{color:"cyan",children:"(next)"})]},o.id)}),n.length===0&&jsx(Text,{dimColor:true,children:"No pending items."})]})}function Cr({scale:e,selected:t,onSelect:n,onClear:o,disabled:r}){let[i,s]=useState(0);return useInput((a,d)=>{if(!r)if(d.leftArrow)s(u=>Math.max(0,u-1));else if(d.rightArrow)s(u=>Math.min(e.cards.length-1,u+1));else if(d.return){let u=e.cards[i];u===t?o():n(u);}else (d.backspace||d.delete)&&o();}),jsxs(Box,{flexDirection:"column",gap:0,children:[jsx(Box,{gap:1,children:e.cards.map((a,d)=>{let u=d===i,c=a===t;return jsx(Text,{inverse:u,bold:c,...c&&{color:"cyan"},children:c?`[${a}]`:` ${a} `},a)})}),jsx(Text,{dimColor:true,children:" \u2190\u2192 navigate \u23CE select \u232B clear"})]})}function js(e){let t=Math.floor(e/1e3),n=Math.floor(t/60),o=t%60;return `${n}:${o.toString().padStart(2,"0")}`}function br({projection:e,round:t,onlineIds:n,isHost:o,myId:r,myRole:i,elapsedMs:s,roomCode:a,shareOpen:d,settingsOpen:u,onVote:c,onClearVote:l,onReveal:p,onSkip:A,onDiscard:N,onToggleRole:T,onShare:R,onSetShareOpen:U,onOpenSettings:f,confirmingQuit:h}){let b=t.votes[r],y=b!==void 0&&J(b);if(useInput(P=>{if(P==="e"||P==="E"){f();return}if(P==="c"||P==="C"){U(true);return}if(i==="voter"&&(P==="a"||P==="A")){y?l():c(Tt);return}o&&(P==="r"||P==="R")&&p(),o&&(P==="s"||P==="S")&&A(),o&&(P==="d"||P==="D")&&N(),(P==="w"||P==="W")&&T();},{isActive:!h&&!u&&!d}),d)return jsx(Qt,{roomCode:a,onPick:P=>{U(false),R(P);},onClose:()=>U(false)});let O=b,C=e.rounds.indexOf(t)+1,M=t.title?` \u2014 ${t.title}`:"",I=t.ticketId?` [${t.ticketId}]`:"";return jsxs(Box,{flexDirection:"column",gap:1,children:[jsxs(Box,{gap:2,children:[jsxs(Text,{bold:true,children:["Round ",C,M,I]}),s!==void 0&&jsxs(Text,{dimColor:true,children:["\u23F1 ",js(s)]})]}),jsxs(Box,{gap:4,children:[jsx(Xt,{participants:e.participants,onlineIds:n,hostId:e.settings.hostParticipantId,votes:t.votes,revealed:false}),i==="viewer"?jsx(Box,{flexDirection:"column",children:jsx(Text,{dimColor:true,children:"You're observing"})}):jsxs(Box,{flexDirection:"column",gap:1,children:[y&&jsx(Text,{dimColor:true,children:"(abstaining this round \u2014 press [a] to vote)"}),jsx(Cr,{scale:t.scale,selected:y?void 0:O,onSelect:c,onClear:l,disabled:h||y})]})]}),jsxs(Box,{gap:2,children:[o&&jsx(Text,{color:"yellow",children:"[r] Reveal"}),o&&jsx(Text,{color:"yellow",children:"[s] Skip"}),o&&jsx(Text,{color:"yellow",children:"[d] Discard"}),i==="voter"&&jsxs(Text,{color:"yellow",children:["[a] ",y?"Vote":"Abstain"]}),jsxs(Text,{color:"yellow",children:["[w] ",i==="viewer"?"Vote":"Observe"]}),jsx(Text,{children:"[e] Settings"}),jsx(Text,{children:"[c] Share"}),jsx(Text,{children:"[q] Quit"})]})]})}var Ys=3e3;function nn(e=Ys){let[t,n]=useState(null),o=useRef(null),r=useCallback(()=>{o.current!==null&&(clearTimeout(o.current),o.current=null);},[]),i=useCallback(a=>{r(),n(a),o.current=setTimeout(()=>{n(null),o.current=null;},e);},[r,e]),s=useCallback(()=>{r(),n(null);},[r]);return useEffect(()=>()=>{r();},[r]),{notice:t,show:i,dismiss:s}}function Ks(e){let t=G(e);return !t||de(t.status)?"lobby":"voting"}function rn({session:e,participantId:t,displayName:n,accessMode:o,presetOverrides:r,customScales:i}){let{exit:s}=useApp(),[a,d]=useState(e.projection()),[u,c]=useState("connecting"),[l,p]=useState(e.onlineIds()),[A,N]=useState(),[T,R]=useState(false),[U,f]=useState(o==="password"?"password":"open"),[h,b]=useState(n),[y,O]=useState(false),[C,M]=useState(false),[I,P]=useState(""),[ue,re]=useState(false),[ie,V]=useState(""),[z,X]=useState(),[ve,ge]=useState(false),[m,x]=useState(""),[E,Y]=useState(null),[pe,it]=useState(false),[dn,We]=useState(""),[Ct,Ce]=useState(false),[cn,Ke]=useState(""),[Ge,Pe]=useState(),[Q,Z]=useState(),he=nn(3e3),[be,st]=useState(false),se=nn(3e3),fe=nn(3e3);useEffect(()=>{let v=e.onChange(()=>{d(e.projection()),p(e.onlineIds());}),B=e.onStatusChange(c),qe=e.onHostChange(It=>{let pi=e.projection().participants.find(mi=>mi.id===It),fi=It===t?"You are now the host":`${pi?.displayName||"Unknown"} is now the host`;he.show(fi);}),un=setInterval(()=>{p(e.onlineIds());let It=e.projection(),At=G(It);At?.timerStartedAt!==void 0&&et(At.status)?N(Date.now()-At.timerStartedAt):N(void 0);},1e3);return ()=>{v(),B(),qe(),clearInterval(un);}},[e,t,he.show]),useInput(v=>{(v==="q"||v==="Q")&&O(true);},{isActive:!T&&!y&&!C&&!ue&&!ve&&!be&&!pe&&!Ct}),useInput((v,B)=>{v==="y"||v==="Y"?(e.disconnect(),s()):(v==="n"||v==="N"||B.escape)&&O(false);},{isActive:y});let De=Ks(a),Ie=G(a),Oe=a.settings.hostParticipantId===t,bt=a.participants.find(v=>v.id===t),at=Ae(bt??{}),w=useCallback(()=>{let v=Jt(Ze(a));P(v.title),Z(v.agendaItemId),M(true);},[a]),ee=useCallback(v=>{M(false),P(""),X(Bn(v));let B=Jt(Ze(a));V(B.ticketId),re(true);},[a]),Hr=useCallback(()=>{M(false),P(""),X(void 0),Z(void 0);},[]),Lr=useCallback(v=>{re(false),V("");let[B,qe,un]=er(z,v,Q);e.newRound(B,qe,un),X(void 0),Z(void 0);},[e,z,Q]),jr=useCallback(()=>{re(false),V(""),X(void 0),Z(void 0);},[]),_r=useCallback(()=>{let v=e.current();!v||v.status!=="revealed"||(Y(v.id),x(v.title??""),ge(true));},[e]),Fr=useCallback(v=>{let B=E;if(ge(false),Y(null),!B)return;let qe=v.trim();qe!==""&&e.updateRoundDescriptor(B,{title:qe});},[e,E]),Vr=useCallback(()=>{ge(false),Y(null);},[]),Yr=useCallback(v=>{e.vote(v);},[e]),Jr=useCallback(()=>{e.unvote();},[e]),Wr=useCallback(()=>{e.reveal();},[e]),Kr=useCallback(()=>{e.skip();},[e]),Gr=useCallback(()=>{e.discardCurrentRound();},[e]),qr=useCallback(()=>{let v=Jt(Ze(a));P(v.title),Z(v.agendaItemId),M(true);},[a]),zr=useCallback(()=>{e.revote();},[e]),Qn=useCallback(()=>{e.toggleRole();},[e]),Xr=useCallback(v=>{e.updateName(v);},[e]),Qr=useCallback(v=>{e.updateScale(v);},[e]),Zr=useCallback(v=>{e.updateDisplayName(v),b(v);},[e]),ei=useCallback(async(v,B)=>{await e.updateAccess(v,B),f(v);},[e]),ti=useCallback(v=>{try{e.handoffHost(v);}catch(B){console.error(`Handoff failed: ${B instanceof Error?B.message:String(B)}`);}},[e]),Zn=useCallback(()=>{R(true);},[]),ni=useCallback(()=>{R(false);},[]),oi=useCallback(()=>{e.endSessionForEveryone();},[e]),eo=useCallback(async v=>{let B=v==="code"?Ft(e.roomCode):v==="url"?Vt(e.roomCode):Yt(e.roomCode);try{await Ar.write(B),se.show("Copied!");}catch{se.show(`Copy failed \u2014 ${B}`);}},[e.roomCode,se]),ln=a.rounds.filter(v=>de(v.status)),ri=ln.length>0?ln.at(-1):null,ii=ln.length>0,si=useCallback(()=>mr(a,v=>Ar.write(v),fe.show),[a,fe.show]),ai=useCallback(()=>{We(""),it(true);},[]),di=useCallback(v=>{it(false),We("");let B=v.trim()===""?void 0:v.trim();Pe(B),Ke(""),Ce(true);},[]),ci=useCallback(()=>{it(false),We(""),Pe(void 0);},[]),li=useCallback(v=>{Ce(false),Ke("");let B=v.trim()===""?void 0:v.trim();if(!Ge&&!B){Pe(void 0);return}$t(e.doc,{...Ge!==void 0&&{title:Ge},...B!==void 0&&{ticketId:B},createdAt:Date.now()}),Pe(void 0);},[e.doc,Ge]),ui=useCallback(()=>{Ce(false),Ke(""),Pe(void 0);},[]);return jsxs(Box,{flexDirection:"column",children:[jsx(or,{roomCode:e.roomCode,sessionName:a.settings.name,status:u,isHost:Oe}),he.notice&&jsx(Box,{paddingX:1,children:jsx(Text,{color:"yellow",children:he.notice})}),se.notice&&jsx(Box,{paddingX:1,children:jsx(Text,{color:se.notice.startsWith("Copy failed")?"red":"green",children:se.notice})}),fe.notice&&jsx(Box,{paddingX:1,children:jsx(Text,{color:fe.notice.startsWith("Clipboard write failed")?"red":"green",children:fe.notice})}),T?jsx(ur,{sessionName:a.settings.name,displayName:h,currentScale:a.settings.defaultScale,accessMode:U,presetOverrides:r,customScales:i,isHost:Oe,onChangeName:Xr,onChangeScale:Qr,onChangeDisplayName:Zr,onChangeAccess:ei,onClose:ni}):De==="lobby"?jsx(wr,{projection:a,onlineIds:l,isHost:Oe,participantId:t,rounds:a.rounds,currentRound:ri,settingsOpen:T,onStartRound:w,onNewRound:qr,roundTitlePromptActive:C,roundTitleInput:I,onRoundTitleChange:P,onRoundTitleSubmit:ee,onRoundTitleCancel:Hr,roundTicketIdPromptActive:ue,roundTicketIdInput:ie,onRoundTicketIdChange:V,onRoundTicketIdSubmit:Lr,onRoundTicketIdCancel:jr,onRevote:zr,onOpenSettings:Zn,onHandoffHost:ti,onEndSession:oi,onShare:eo,shareOpen:be,onSetShareOpen:st,roomCode:e.roomCode,confirmingQuit:y,myRole:at,onToggleRole:Qn,descriptorPromptActive:ve,descriptorInput:m,onDescriptorChange:x,onDescriptorSubmit:Fr,onDescriptorCancel:Vr,onEditDescriptor:_r,canExport:ii,onExportResults:si,onAgendaAdd:ai,agendaAddPromptActive:pe,agendaAddTitleInput:dn,onAgendaAddTitleChange:We,onAgendaAddTitleSubmit:di,onAgendaAddTitleCancel:ci,agendaAddTicketIdPromptActive:Ct,agendaAddTicketIdInput:cn,onAgendaAddTicketIdChange:Ke,onAgendaAddTicketIdSubmit:li,onAgendaAddTicketIdCancel:ui}):De==="voting"&&Ie?jsx(br,{projection:a,round:Ie,onlineIds:l,isHost:Oe,myId:t,myRole:at,elapsedMs:A,onVote:Yr,onClearVote:Jr,onReveal:Wr,onSkip:Kr,onDiscard:Gr,onToggleRole:Qn,onShare:eo,onSetShareOpen:st,shareOpen:be,settingsOpen:T,onOpenSettings:Zn,roomCode:e.roomCode,confirmingQuit:y}):null,y&&jsx(Box,{children:jsx(Text,{dimColor:true,children:"Leave session? y/n"})})]})}function Tr({presetOverrides:e,customScales:t}){let{exit:n}=useApp(),[o,r]=useState("main-menu"),[i,s]=useState(Le()),[a,d]=useState(i.displayName??""),[u,c]=useState(i.relayUrl??""),[l,p]=useState(i.defaultSessionName??""),[A,N]=useState(i.avatarColor?.startsWith("#")?i.avatarColor:""),[T,R]=useState(t??[]),[U,f]=useState(e??{});useInput(m=>{(m==="q"||m==="Q")&&n();},{isActive:o==="main-menu"});let h=useMemo(()=>Bt(U,T),[U,T]),b=i.defaultScale,y=b?h.find(m=>m.id===b):void 0,O=[{label:"Display name",value:"display-name",detail:`[${i.displayName??"(none)"}]`},{label:"Relay URL",value:"relay-url",detail:`[${i.relayUrl??"(none)"}]`},{label:"Default scale",value:"default-scale",detail:`[${y?He(y.id,T):"(none)"}]`},{label:"Default session name",value:"default-session-name",detail:`[${i.defaultSessionName??"(none)"}]`},{label:"Default password",value:"default-password",detail:`[${i.defaultPassword?"on":"off"}]`},{label:"Avatar color",value:"avatar-color",detail:`[${Zs(i.avatarColor)}]`}];function C(m){m==="display-name"?(d(i.displayName??""),r("editing-display-name")):m==="relay-url"?(c(i.relayUrl??""),r("editing-relay-url")):m==="default-scale"?r("picking-scale"):m==="default-session-name"?(p(i.defaultSessionName??""),r("editing-session-name")):m==="default-password"?r("picking-default-password"):m==="avatar-color"&&r("picking-avatar-color");}function M(m){if(m==="custom-hex"){N(i.avatarColor?.startsWith("#")?i.avatarColor:""),r("editing-avatar-hex");return}if(Me(m)===null){r("main-menu");return}s({...i,avatarColor:m}),F({avatarColor:m}),r("main-menu");}function I(m){let x=m.trim();if(x.length===0){r("picking-avatar-color");return}let E=x.startsWith("#")?x.toLowerCase():`#${x.toLowerCase()}`;Me(E)!==null&&(s({...i,avatarColor:E}),F({avatarColor:E}),r("main-menu"));}function P(m){let x=m.trim();if(x.length>0){let E={...i,displayName:x};s(E),F({displayName:x});}r("main-menu");}function ue(m){let x=m.trim();if(x.length>0){let E={...i,relayUrl:x};s(E),F({relayUrl:x});}r("main-menu");}function re(m){let x=m.trim(),E=x.length>0?{...i,defaultSessionName:x}:{...i};x.length===0?(delete E.defaultSessionName,s(E),pt({defaultSessionName:""})):(s(E),F({defaultSessionName:x})),r("main-menu");}function ie(m){let x=m.length>0?{...i,defaultScale:m}:{...i};m.length===0?(delete x.defaultScale,s(x),pt({defaultScale:""})):(s(x),F({defaultScale:m})),r("main-menu");}function V(m,x){let E={...U};x.length===0?delete E[m]:E[m]=x,f(E),F({presetOverrides:Object.keys(E).length>0?E:void 0});}function z(m){let x=m==="on",E={...i,defaultPassword:x};s(E),F({defaultPassword:x}),r("main-menu");}function X(m,x){let E=`custom-${randomUUID()}`,Y=[...T,{id:E,label:m,cards:x}];R(Y),s({...i,customScales:Y}),F({customScales:Y});}function ve(m,x,E){let Y=T.map(pe=>pe.id===m?{id:m,label:x,cards:E}:pe);R(Y),s({...i,customScales:Y}),F({customScales:Y});}function ge(m){let x=T.filter(Y=>Y.id!==m);R(x);let E={...i,customScales:x};i.defaultScale===m?(E.defaultScale="fibonacci",F({customScales:x,defaultScale:"fibonacci"})):F({customScales:x}),s(E);}if(o==="main-menu")return jsx(me,{title:"\u2500\u2500 Config Settings \u2500\u2500",items:O,onSelect:C,onClose:()=>n()});if(o==="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(_,{value:a,onChange:d,onSubmit:P,onCancel:()=>r("main-menu"),placeholder:"(required)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]});if(o==="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(_,{value:u,onChange:c,onSubmit:ue,onCancel:()=>r("main-menu"),placeholder:"(required)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]});if(o==="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(_,{value:l,onChange:p,onSubmit:re,onCancel:()=>r("main-menu"),placeholder:"(leave empty to clear)",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]});if(o==="picking-scale"){let m=[{id:"",cards:[]},...h];return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(Gt,{scales:m,selectedScaleId:b??"",presetOverrides:U,customScales:T,onSelect:ie,onOverrideChange:V,onCustomScaleCreate:X,onCustomScaleEdit:ve,onCustomScaleDelete:ge,onClose:()=>r("main-menu"),showNoneOption:true})]})}if(o==="picking-avatar-color"){let m=dt.map(x=>({label:ze[x].label,value:x,detail:i.avatarColor===x?"[current]":ze[x].base}));return m.push({label:"Custom hex\u2026",value:"custom-hex",detail:i.avatarColor?.startsWith("#")?`[${i.avatarColor}]`:void 0}),jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(me,{title:"Avatar color:",items:m,onSelect:M,onClose:()=>r("main-menu")})]})}if(o==="editing-avatar-hex")return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(Text,{children:"Custom hex color: "}),jsx(_,{value:A,onChange:N,onSubmit:I,onCancel:()=>r("picking-avatar-color"),placeholder:"#rrggbb",isActive:true}),jsx(Text,{dimColor:true,children:"\u23CE confirm esc back"})]});if(o==="picking-default-password"){let m=[{label:"On",value:"on",detail:i.defaultPassword?"[current]":void 0},{label:"Off",value:"off",detail:i.defaultPassword?void 0:"[current]"}];return jsxs(Box,{flexDirection:"column",gap:1,children:[jsx(Text,{bold:true,children:"\u2500\u2500 Config Settings \u2500\u2500"}),jsx(me,{title:"Default password:",items:m,onSelect:z,onClose:()=>r("main-menu")})]})}return jsx(Box,{})}function Zs(e){return e?e.startsWith("#")?e:dt.includes(e)?ze[e].label:e:"(auto)"}var ta={pending:"\u2022",consumed:"\u2713",skipped:"~",cancelled:"\u2717"};function na(e){let t=ta[e.state]??"?",n=[];e.ticketId&&n.push(`[${e.ticketId}]`),e.title&&n.push(e.title);let o=n.join(" ")||e.id;return `${t} ${o}`}async function yt(e){let t=new $e.Doc,n=new Awareness(t);Xe(t,{participantId:e.participantId,displayName:e.displayName}),n.setLocalStateField("participantId",e.participantId);let o=tt({doc:t,awareness:n,roomCode:e.roomCode,roomUuid:e.roomUuid,participantId:e.participantId,relayUrl:e.relayUrl,...e.passwordHash!==void 0&&{passwordHash:e.passwordHash}});return await new Promise(r=>{let i=setTimeout(r,2e3),s=o.onStatusChange(a=>{a==="connected"&&setTimeout(()=>{clearTimeout(i),s(),r();},200);});}),{doc:t,disconnect:()=>o.disconnect()}}async function vt(){let e=Lt();e.length===0&&(console.error("No active session. Join a room first with: hnch join <code>"),process.exit(1));let t=e[0];return {roomCode:t.roomCode,roomUuid:t.roomUuid,...t.passwordHash!==void 0&&{passwordHash:t.passwordHash}}}async function Er(e){!e.title?.trim()&&!e.ticket?.trim()&&(console.error("At least one of --title or --ticket is required"),process.exit(1));let t=await ce(),n=await vt();try{await le(t.relayUrl,n.roomCode);}catch{console.error(`Room ${n.roomCode} not found on relay \u2014 session may have expired.`),process.exit(1);}let{doc:o,disconnect:r}=await yt({relayUrl:t.relayUrl,participantId:t.participantId,displayName:t.displayName,roomCode:n.roomCode,roomUuid:n.roomUuid,...n.passwordHash!==void 0&&{passwordHash:n.passwordHash}});try{let i=$t(o,{...e.title?.trim()?{title:e.title.trim()}:{},...e.ticket?.trim()?{ticketId:e.ticket.trim()}:{},...e.description?.trim()?{description:e.description.trim()}:{},createdAt:Date.now()});await new Promise(s=>{setTimeout(s,300);}),console.log(`Added agenda item: ${i}`);}finally{r();}}async function kr(){let e=await ce(),t=await vt();try{await le(e.relayUrl,t.roomCode);}catch{console.error(`Room ${t.roomCode} not found on relay \u2014 session may have expired.`),process.exit(1);}let{doc:n,disconnect:o}=await yt({relayUrl:e.relayUrl,participantId:e.participantId,displayName:e.displayName,roomCode:t.roomCode,roomUuid:t.roomUuid,...t.passwordHash!==void 0&&{passwordHash:t.passwordHash}});try{let i=j(n).agenda;if(i.length===0){console.log("No agenda items.");return}i.forEach((s,a)=>{console.log(`${String(a+1).padStart(2)}. ${na(s)}`);});}finally{o();}}async function Nr(e){let t=await ce(),n=await vt();try{await le(t.relayUrl,n.roomCode);}catch{console.error(`Room ${n.roomCode} not found on relay \u2014 session may have expired.`),process.exit(1);}let{doc:o,disconnect:r}=await yt({relayUrl:t.relayUrl,participantId:t.participantId,displayName:t.displayName,roomCode:n.roomCode,roomUuid:n.roomUuid,...n.passwordHash!==void 0&&{passwordHash:n.passwordHash}});try{vn(o,e.id,"skipped",{timestamp:Date.now()}),await new Promise(i=>{setTimeout(i,300);}),console.log(`Skipped agenda item: ${e.id}`);}catch(i){console.error(`Skip failed: ${i instanceof Error?i.message:String(i)}`),process.exit(1);}finally{r();}}async function Pr(e){let t=await ce(),n=await vt();try{await le(t.relayUrl,n.roomCode);}catch{console.error(`Room ${n.roomCode} not found on relay \u2014 session may have expired.`),process.exit(1);}let{doc:o,disconnect:r}=await yt({relayUrl:t.relayUrl,participantId:t.participantId,displayName:t.displayName,roomCode:n.roomCode,roomUuid:n.roomUuid,...n.passwordHash!==void 0&&{passwordHash:n.passwordHash}});try{vn(o,e.id,"cancelled",{timestamp:Date.now()}),await new Promise(i=>{setTimeout(i,300);}),console.log(`Cancelled agenda item: ${e.id}`);}catch(i){console.error(`Cancel failed: ${i instanceof Error?i.message:String(i)}`),process.exit(1);}finally{r();}}async function Dr(e){let t=await ce(),n=await vt();try{await le(t.relayUrl,n.roomCode);}catch{console.error(`Room ${n.roomCode} not found on relay \u2014 session may have expired.`),process.exit(1);}let{doc:o,disconnect:r}=await yt({relayUrl:t.relayUrl,participantId:t.participantId,displayName:t.displayName,roomCode:n.roomCode,roomUuid:n.roomUuid,...n.passwordHash!==void 0&&{passwordHash:n.passwordHash}});try{vo(o,e.id,e.before,e.after),await new Promise(i=>{setTimeout(i,300);}),console.log(`Reordered agenda item: ${e.id}`);}catch(i){console.error(`Move failed: ${i instanceof Error?i.message:String(i)}`),process.exit(1);}finally{r();}}async function Xn(){return new Promise(e=>{let t=createInterface({input:process.stdin,output:process.stderr});process.stderr.write("Password: "),t.question("",n=>{t.close(),e(n);});})}async function ia(e){let t=await ce(),n=e.scale??t.defaultScale??"fibonacci",o=e.name??t.defaultSessionName,r=e.password??t.defaultPassword??false,i;r&&(i=await Xn());let s=Ut(n,t.presetOverrides,t.customScales);if(!s){let l=Object.keys(ae).join(", ");console.error(`Unknown or fully-disabled scale: "${n}". Builtin IDs: ${l}`),process.exit(1);}let a=await Go({participantId:t.participantId,displayName:t.displayName,relayUrl:t.relayUrl,sessionName:o,password:i,defaultScale:s,...t.avatarColor!==void 0&&{avatarColor:t.avatarColor}}),d;i&&(d=await Be(i,a.roomUuid)),Un({roomCode:a.roomCode,roomUuid:a.roomUuid,lastSeenAt:Date.now(),...d!==void 0&&{passwordHash:d}}),console.error(`Room created: ${a.roomCode}`),console.error(`Share: hnch join ${a.roomCode}`),console.error("");let{waitUntilExit:u,unmount:c}=render(Zt.createElement(rn,{session:a,participantId:t.participantId,displayName:t.displayName,accessMode:i?"password":"open",presetOverrides:t.presetOverrides,customScales:t.customScales}));_t({doc:a.doc,localParticipantId:t.participantId,onEnded:async l=>{console.error(l.isLocal?"Session ended.":`Session ended by ${l.hostName}.`),c();try{await Se();}catch(p){console.error(`Failed to clear cached session: ${p instanceof Error?p.message:String(p)}`);}a.disconnect(),process.exit(0);}}),await u(),a.disconnect(),Se(),process.exit(0);}async function sa(e){let t=await ce(),n=zo(e.code);n===null&&(console.error("invalid room code"),process.exit(1));let o;e.password&&(o=await Xn());let r=await le(t.relayUrl,n),i;r.accessMode==="password"&&!e.password?i=await Xn():e.password&&(i=o);let s,a=false,d=await qo({participantId:t.participantId,displayName:t.displayName,relayUrl:t.relayUrl,roomCode:n,password:i,...t.avatarColor!==void 0&&{avatarColor:t.avatarColor},onRejection(l){if(l==="unknown-room"){jt({clearLastSession:Se,logger:console,exit:process.exit});return}l==="room-full"&&(a=true,s?.());}}),u;i&&(u=await Be(i,d.roomUuid)),Un({roomCode:d.roomCode,roomUuid:d.roomUuid,lastSeenAt:Date.now(),...u!==void 0&&{passwordHash:u}}),console.error(`Joined room: ${d.roomCode}`),console.error("");let c=render(Zt.createElement(rn,{session:d,participantId:t.participantId,displayName:t.displayName,accessMode:r.accessMode,presetOverrides:t.presetOverrides,customScales:t.customScales}));s=c.unmount,_t({doc:d.doc,localParticipantId:t.participantId,onEnded:async l=>{console.error(l.isLocal?"Session ended.":`Session ended by ${l.hostName}.`),s?.();try{await Se();}catch(p){console.error(`Failed to clear cached session: ${p instanceof Error?p.message:String(p)}`);}d.disconnect(),process.exit(0);}}),await c.waitUntilExit(),a&&(console.error("This room is full (max 20 participants)."),d.disconnect(),Se(),process.exit(1)),d.disconnect(),Se(),process.exit(0);}function aa(e,t=Date.now()){let n=t-e,o=Math.floor(n/6e4);return o<60?`${o}m ago`:`${Math.floor(o/60)}h ago`}async function da(e){return new Promise((t,n)=>{let o=createInterface({input:process.stdin,output:process.stderr}),r=()=>{process.stderr.write(`Select [1-${e}]: `),o.question("",i=>{let s=parseInt(i.trim(),10);Number.isInteger(s)&&s>=1&&s<=e?(o.close(),t(s)):(process.stderr.write(`Invalid choice \u2014 enter a number between 1 and ${e}.
22
+ `),r());});};o.on("close",()=>n(new Error("stdin closed"))),r();})}async function Or(e,t){let n;try{n=await le(e.relayUrl,t.roomCode);}catch(c){if((c instanceof Error?c.message:String(c)).includes("Room not found"))return ft(t.roomUuid),await jt({clearLastSession:Se,logger:console,exit:process.exit});console.error("Could not reach relay \u2014 try again later."),process.exit(0);}n.accessMode==="password"&&!t.passwordHash&&(console.error(`Password changed \u2014 rejoin with: hnch join ${t.roomCode} --password`),$n(t.roomUuid),process.exit(0)),console.error(`Rejoining room ${t.roomCode}\u2026`),console.error("");let o=new $e.Doc,r=new Awareness(o);Xe(o,{participantId:e.participantId,displayName:e.displayName,...e.avatarColor!==void 0&&{avatarColor:e.avatarColor}}),r.setLocalStateField("participantId",e.participantId);let i=false,s=false,a,d=tt({doc:o,awareness:r,roomCode:t.roomCode,roomUuid:t.roomUuid,participantId:e.participantId,relayUrl:e.relayUrl,passwordHash:t.passwordHash,onRejection(c){if(c==="unknown-room"){ft(t.roomUuid),jt({clearLastSession:Se,logger:console,exit:process.exit});return}c==="wrong-password"&&(i=true,a?.()),c==="room-full"&&(s=true,a?.());}});Mn({roomCode:d.roomCode,roomUuid:d.roomUuid,lastSeenAt:Date.now(),...t.passwordHash!==void 0&&{passwordHash:t.passwordHash}});let u=render(Zt.createElement(rn,{session:d,participantId:e.participantId,displayName:e.displayName,accessMode:n.accessMode,presetOverrides:e.presetOverrides,customScales:e.customScales}));a=u.unmount,_t({doc:d.doc,localParticipantId:e.participantId,onEnded:async c=>{console.error(c.isLocal?"Session ended.":`Session ended by ${c.hostName}.`),a?.();try{ft(t.roomUuid);}catch(l){console.error(`Failed to clear cached session: ${l instanceof Error?l.message:String(l)}`);}d.disconnect(),process.exit(0);}}),await u.waitUntilExit(),s&&(console.error("This room is full (max 20 participants)."),d.disconnect(),process.exit(1)),i&&($n(t.roomUuid),console.error(`Password changed \u2014 rejoin with: hnch join ${t.roomCode} --password`),d.disconnect(),process.exit(0)),d.disconnect(),ft(t.roomUuid),process.exit(0);}async function ca(){let e=await ce(),t=Lt();if(t.length===0&&(console.error("No recent session to resume."),process.exit(0)),t.length===1)return Or(e,t[0]);let n=Date.now();process.stderr.write(`Recent rooms:
23
+ `);for(let[r,i]of t.entries()){let s=aa(i.lastSeenAt,n);process.stderr.write(` ${r+1}. ${i.roomCode} (${s})
24
+ `);}process.stderr.write(`
25
+ `);let o;try{o=await da(t.length);}catch{console.error("No selection made."),process.exit(1);}return Or(e,t[o-1])}async function la(e){if(!(e.name!==void 0||e.relayUrl!==void 0||e.scale!==void 0||e.sessionName!==void 0||e.password!==void 0||e.color!==void 0)){let o=await ce(),{waitUntilExit:r}=render(Zt.createElement(Tr,{presetOverrides:o.presetOverrides,customScales:o.customScales}));await r(),process.exit(0);}let n={};if(e.name!==void 0&&(n.displayName=e.name),e.relayUrl!==void 0&&(n.relayUrl=e.relayUrl),e.scale!==void 0){if(e.scale!==""){let o=Le();if(!Ut(e.scale,o.presetOverrides,o.customScales)){console.error(`Unknown scale: "${e.scale}". Builtin IDs: ${Object.keys(ae).join(", ")}`);return}}n.defaultScale=e.scale;}if(e.sessionName!==void 0&&(n.defaultSessionName=e.sessionName),e.password!==void 0&&(n.defaultPassword=e.password),e.color!==void 0){let o=e.color.startsWith("#")||e.color===""?e.color:e.color.toLowerCase();o!==""&&Me(o)===null&&(console.error(`Invalid color: "${e.color}". Pass a palette key or #rrggbb hex literal.`),process.exit(1)),n.avatarColor=o;}pt(n),console.log("Config updated.");}var Br=oa(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=>{ia(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=>{sa(e).catch(t=>{console.error(t instanceof Error?t.message:t),process.exit(1);});}).command("resume","Rejoin your last session",e=>e,()=>{ca().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)"}).option("color",{type:"string",describe:'Set avatar color: a palette key (e.g. "blue", "racing-green") or a #rrggbb hex literal'}),e=>{la(e).catch(t=>{console.error(t instanceof Error?t.message:t),process.exit(1);});}).command("agenda","Manage the pre-session story agenda",e=>e.command("add","Add an agenda item",t=>t.option("title",{type:"string",describe:"Story title"}).option("ticket",{type:"string",describe:"Issue tracker reference (e.g. PROJ-123)"}).option("description",{type:"string",describe:"Story description"}).check(n=>{if(!n.title?.trim()&&!n.ticket?.trim())throw new Error("At least one of --title or --ticket is required");return true}),t=>{Er(t).catch(n=>{console.error(n instanceof Error?n.message:n),process.exit(1);});}).command("list","List agenda items in order",t=>t,()=>{kr().catch(t=>{console.error(t instanceof Error?t.message:t),process.exit(1);});}).command("skip <id>","Mark an agenda item as skipped",t=>t.positional("id",{type:"string",demandOption:true,describe:"Agenda item ID"}),t=>{Nr(t).catch(n=>{console.error(n instanceof Error?n.message:n),process.exit(1);});}).command("cancel <id>","Soft-delete (cancel) an agenda item",t=>t.positional("id",{type:"string",demandOption:true,describe:"Agenda item ID"}),t=>{Pr(t).catch(n=>{console.error(n instanceof Error?n.message:n),process.exit(1);});}).command("mv <id>","Reorder an agenda item (--before <id> | --after <id>)",t=>t.positional("id",{type:"string",demandOption:true,describe:"Agenda item ID to move"}).option("before",{type:"string",describe:"sortKey of item that should come before the moved item"}).option("after",{type:"string",describe:"sortKey of item that should come after the moved item"}).check(n=>{if(n.before!==void 0&&n.after!==void 0)throw new Error("--before and --after are mutually exclusive");if(n.before===void 0&&n.after===void 0)throw new Error("Specify either --before <id> or --after <id>");return true}),t=>{Dr(t).catch(n=>{console.error(n instanceof Error?n.message:n),process.exit(1);});}).demandCommand(1,"Specify an agenda subcommand: add, list, skip, cancel, mv").strict().help(),()=>{}).demandCommand(1,"Specify a command: create, join, resume, config, or agenda").strict().help(),zn=hideBin(process.argv);(zn.length===0||zn.length===1&&zn[0]==="--")&&(Br.showHelp(),process.exit(0));Br.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xauyxau/hnch",
3
- "version": "1.12.0",
3
+ "version": "1.14.0",
4
4
  "description": "Pointing poker CLI for agile teams",
5
5
  "keywords": [
6
6
  "pointing-poker",
@@ -51,7 +51,7 @@
51
51
  "clipboardy": "^4.0.0",
52
52
  "ink": "^5.2.0",
53
53
  "react": "^18.3.1",
54
- "ws": "^8.20.0",
54
+ "ws": "^8.20.1",
55
55
  "y-protocols": "^1.0.7",
56
56
  "y-websocket": "^3.0.0",
57
57
  "yargs": "^17.7.2",