@brianlovin/hn-cli 0.4.1 → 0.4.2

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 +12 -2
  2. package/dist/cli.js +1 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -41,9 +41,19 @@ bun run start
41
41
  - Click on stories in the sidebar to select them
42
42
  - Click story title/URL to open in browser
43
43
 
44
- ### AI Chat
44
+ ### AI Features
45
45
 
46
- Press `c` on any story to start a conversation with an AI about the story and its comments. The AI has full context of the story content and all comments.
46
+ #### TL;DR Summaries
47
+
48
+ Press `t` on any story to generate an AI-powered summary. The AI reads the article and all comments, then provides:
49
+ - A concise summary of the article
50
+ - Key takeaways from the discussion
51
+
52
+ Summaries are cached locally, so you can revisit them without regenerating.
53
+
54
+ #### Chat
55
+
56
+ Press `c` to start a conversation with an AI about the story. The AI has full context of the article and all comments, so you can ask follow-up questions, get clarification, or dive deeper into specific topics.
47
57
 
48
58
  #### Bring Your Own API Key
49
59
 
package/dist/cli.js CHANGED
@@ -89,7 +89,7 @@ ${A}</tr>
89
89
  `}strong({tokens:A}){return`<strong>${this.parser.parseInline(A)}</strong>`}em({tokens:A}){return`<em>${this.parser.parseInline(A)}</em>`}codespan({text:A}){return`<code>${gg(A,!0)}</code>`}br(A){return"<br>"}del({tokens:A}){return`<del>${this.parser.parseInline(A)}</del>`}link({href:A,title:q,tokens:g}){let E=this.parser.parseInline(g),F=mJ(A);if(F===null)return E;A=F;let K='<a href="'+A+'"';return q&&(K+=' title="'+gg(q)+'"'),K+=">"+E+"</a>",K}image({href:A,title:q,text:g,tokens:E}){E&&(g=this.parser.parseInline(E,this.parser.textRenderer));let F=mJ(A);if(F===null)return gg(g);A=F;let K=`<img src="${A}" alt="${g}"`;return q&&(K+=` title="${gg(q)}"`),K+=">",K}text(A){return"tokens"in A&&A.tokens?this.parser.parseInline(A.tokens):("escaped"in A)&&A.escaped?A.text:gg(A.text)}},GG=class{strong({text:A}){return A}em({text:A}){return A}codespan({text:A}){return A}del({text:A}){return A}html({text:A}){return A}text({text:A}){return A}link({text:A}){return""+A}image({text:A}){return""+A}br(){return""}checkbox({raw:A}){return A}},Iq=class A{options;renderer;textRenderer;constructor(q){this.options=q||j6,this.options.renderer=this.options.renderer||new wZ,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new GG}static parse(q,g){return new A(g).parse(q)}static parseInline(q,g){return new A(g).parseInline(q)}parse(q){let g="";for(let E=0;E<q.length;E++){let F=q[E];if(this.options.extensions?.renderers?.[F.type]){let Z=F,j=this.options.extensions.renderers[Z.type].call({parser:this},Z);if(j!==!1||!["space","hr","heading","code","table","blockquote","list","html","def","paragraph","text"].includes(Z.type)){g+=j||"";continue}}let K=F;switch(K.type){case"space":{g+=this.renderer.space(K);break}case"hr":{g+=this.renderer.hr(K);break}case"heading":{g+=this.renderer.heading(K);break}case"code":{g+=this.renderer.code(K);break}case"table":{g+=this.renderer.table(K);break}case"blockquote":{g+=this.renderer.blockquote(K);break}case"list":{g+=this.renderer.list(K);break}case"checkbox":{g+=this.renderer.checkbox(K);break}case"html":{g+=this.renderer.html(K);break}case"def":{g+=this.renderer.def(K);break}case"paragraph":{g+=this.renderer.paragraph(K);break}case"text":{g+=this.renderer.text(K);break}default:{let Z='Token with "'+K.type+'" type was not found.';if(this.options.silent)return console.error(Z),"";throw new Error(Z)}}}return g}parseInline(q,g=this.renderer){let E="";for(let F=0;F<q.length;F++){let K=q[F];if(this.options.extensions?.renderers?.[K.type]){let j=this.options.extensions.renderers[K.type].call({parser:this},K);if(j!==!1||!["escape","html","link","image","strong","em","codespan","br","del","text"].includes(K.type)){E+=j||"";continue}}let Z=K;switch(Z.type){case"escape":{E+=g.text(Z);break}case"html":{E+=g.html(Z);break}case"link":{E+=g.link(Z);break}case"image":{E+=g.image(Z);break}case"checkbox":{E+=g.checkbox(Z);break}case"strong":{E+=g.strong(Z);break}case"em":{E+=g.em(Z);break}case"codespan":{E+=g.codespan(Z);break}case"br":{E+=g.br(Z);break}case"del":{E+=g.del(Z);break}case"text":{E+=g.text(Z);break}default:{let j='Token with "'+Z.type+'" type was not found.';if(this.options.silent)return console.error(j),"";throw new Error(j)}}}return E}},eE=class{options;block;constructor(A){this.options=A||j6}static passThroughHooks=new Set(["preprocess","postprocess","processAllTokens","emStrongMask"]);static passThroughHooksRespectAsync=new Set(["preprocess","postprocess","processAllTokens"]);preprocess(A){return A}postprocess(A){return A}processAllTokens(A){return A}emStrongMask(A){return A}provideLexer(){return this.block?Pq.lex:Pq.lexInline}provideParser(){return this.block?Iq.parse:Iq.parseInline}},u4=class{defaults=qG();options=this.setOptions;parse=this.parseMarkdown(!0);parseInline=this.parseMarkdown(!1);Parser=Iq;Renderer=wZ;TextRenderer=GG;Lexer=Pq;Tokenizer=BZ;Hooks=eE;constructor(...A){this.use(...A)}walkTokens(A,q){let g=[];for(let E of A)switch(g=g.concat(q.call(this,E)),E.type){case"table":{let F=E;for(let K of F.header)g=g.concat(this.walkTokens(K.tokens,q));for(let K of F.rows)for(let Z of K)g=g.concat(this.walkTokens(Z.tokens,q));break}case"list":{let F=E;g=g.concat(this.walkTokens(F.items,q));break}default:{let F=E;this.defaults.extensions?.childTokens?.[F.type]?this.defaults.extensions.childTokens[F.type].forEach((K)=>{let Z=F[K].flat(1/0);g=g.concat(this.walkTokens(Z,q))}):F.tokens&&(g=g.concat(this.walkTokens(F.tokens,q)))}}return g}use(...A){let q=this.defaults.extensions||{renderers:{},childTokens:{}};return A.forEach((g)=>{let E={...g};if(E.async=this.defaults.async||E.async||!1,g.extensions&&(g.extensions.forEach((F)=>{if(!F.name)throw new Error("extension name required");if("renderer"in F){let K=q.renderers[F.name];K?q.renderers[F.name]=function(...Z){let j=F.renderer.apply(this,Z);return j===!1&&(j=K.apply(this,Z)),j}:q.renderers[F.name]=F.renderer}if("tokenizer"in F){if(!F.level||F.level!=="block"&&F.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");let K=q[F.level];K?K.unshift(F.tokenizer):q[F.level]=[F.tokenizer],F.start&&(F.level==="block"?q.startBlock?q.startBlock.push(F.start):q.startBlock=[F.start]:F.level==="inline"&&(q.startInline?q.startInline.push(F.start):q.startInline=[F.start]))}"childTokens"in F&&F.childTokens&&(q.childTokens[F.name]=F.childTokens)}),E.extensions=q),g.renderer){let F=this.defaults.renderer||new wZ(this.defaults);for(let K in g.renderer){if(!(K in F))throw new Error(`renderer '${K}' does not exist`);if(["options","parser"].includes(K))continue;let Z=K,j=g.renderer[Z],U=F[Z];F[Z]=(...G)=>{let Y=j.apply(F,G);return Y===!1&&(Y=U.apply(F,G)),Y||""}}E.renderer=F}if(g.tokenizer){let F=this.defaults.tokenizer||new BZ(this.defaults);for(let K in g.tokenizer){if(!(K in F))throw new Error(`tokenizer '${K}' does not exist`);if(["options","rules","lexer"].includes(K))continue;let Z=K,j=g.tokenizer[Z],U=F[Z];F[Z]=(...G)=>{let Y=j.apply(F,G);return Y===!1&&(Y=U.apply(F,G)),Y}}E.tokenizer=F}if(g.hooks){let F=this.defaults.hooks||new eE;for(let K in g.hooks){if(!(K in F))throw new Error(`hook '${K}' does not exist`);if(["options","block"].includes(K))continue;let Z=K,j=g.hooks[Z],U=F[Z];eE.passThroughHooks.has(K)?F[Z]=(G)=>{if(this.defaults.async&&eE.passThroughHooksRespectAsync.has(K))return(async()=>{let M=await j.call(F,G);return U.call(F,M)})();let Y=j.call(F,G);return U.call(F,Y)}:F[Z]=(...G)=>{if(this.defaults.async)return(async()=>{let M=await j.apply(F,G);return M===!1&&(M=await U.apply(F,G)),M})();let Y=j.apply(F,G);return Y===!1&&(Y=U.apply(F,G)),Y}}E.hooks=F}if(g.walkTokens){let F=this.defaults.walkTokens,K=g.walkTokens;E.walkTokens=function(Z){let j=[];return j.push(K.call(this,Z)),F&&(j=j.concat(F.call(this,Z))),j}}this.defaults={...this.defaults,...E}}),this}setOptions(A){return this.defaults={...this.defaults,...A},this}lexer(A,q){return Pq.lex(A,q??this.defaults)}parser(A,q){return Iq.parse(A,q??this.defaults)}parseMarkdown(A){return(q,g)=>{let E={...g},F={...this.defaults,...E},K=this.onError(!!F.silent,!!F.async);if(this.defaults.async===!0&&E.async===!1)return K(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof q>"u"||q===null)return K(new Error("marked(): input parameter is undefined or null"));if(typeof q!="string")return K(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(q)+", string expected"));if(F.hooks&&(F.hooks.options=F,F.hooks.block=A),F.async)return(async()=>{let Z=F.hooks?await F.hooks.preprocess(q):q,j=await(F.hooks?await F.hooks.provideLexer():A?Pq.lex:Pq.lexInline)(Z,F),U=F.hooks?await F.hooks.processAllTokens(j):j;F.walkTokens&&await Promise.all(this.walkTokens(U,F.walkTokens));let G=await(F.hooks?await F.hooks.provideParser():A?Iq.parse:Iq.parseInline)(U,F);return F.hooks?await F.hooks.postprocess(G):G})().catch(K);try{F.hooks&&(q=F.hooks.preprocess(q));let Z=(F.hooks?F.hooks.provideLexer():A?Pq.lex:Pq.lexInline)(q,F);F.hooks&&(Z=F.hooks.processAllTokens(Z)),F.walkTokens&&this.walkTokens(Z,F.walkTokens);let j=(F.hooks?F.hooks.provideParser():A?Iq.parse:Iq.parseInline)(Z,F);return F.hooks&&(j=F.hooks.postprocess(j)),j}catch(Z){return K(Z)}}}onError(A,q){return(g)=>{if(g.message+=`
90
90
  Please report this to https://github.com/markedjs/marked.`,A){let E="<p>An error occurred:</p><pre>"+gg(g.message+"",!0)+"</pre>";return q?Promise.resolve(E):E}if(q)return Promise.reject(g);throw g}}},Z6=new u4;function OA(A,q){return Z6.parse(A,q)}OA.options=OA.setOptions=function(A){return Z6.setOptions(A),OA.defaults=Z6.defaults,EO(OA.defaults),OA};OA.getDefaults=qG;OA.defaults=j6;OA.use=function(...A){return Z6.use(...A),OA.defaults=Z6.defaults,EO(OA.defaults),OA};OA.walkTokens=function(A,q){return Z6.walkTokens(A,q)};OA.parseInline=Z6.parseInline;OA.Parser=Iq;OA.parser=Iq.parse;OA.Renderer=wZ;OA.TextRenderer=GG;OA.Lexer=Pq;OA.lexer=Pq.lex;OA.Tokenizer=BZ;OA.Hooks=eE;OA.parse=OA;var{options:JN,setOptions:ON,use:MN,walkTokens:$N,parseInline:XN}=OA,_N=Iq.parse,HN=Pq.lex;var l4=JA.fromHex("#9a9ea3"),i4=JA.fromHex("#252527");class MO extends DA{orientation;_value;_min;_max;_viewPortSize;_backgroundColor;_foregroundColor;_onChange;constructor(A,q){super(A,{flexShrink:0,...q});this.orientation=q.orientation,this._min=q.min??0,this._max=q.max??100,this._value=q.value??this._min,this._viewPortSize=q.viewPortSize??Math.max(1,(this._max-this._min)*0.1),this._onChange=q.onChange,this._backgroundColor=q.backgroundColor?u(q.backgroundColor):i4,this._foregroundColor=q.foregroundColor?u(q.foregroundColor):l4,this.setupMouseHandling()}get value(){return this._value}set value(A){let q=Math.max(this._min,Math.min(this._max,A));if(q!==this._value)this._value=q,this._onChange?.(q),this.emit("change",{value:q}),this.requestRender()}get min(){return this._min}set min(A){if(A!==this._min){if(this._min=A,this._value<A)this.value=A;this.requestRender()}}get max(){return this._max}set max(A){if(A!==this._max){if(this._max=A,this._value>A)this.value=A;this.requestRender()}}set viewPortSize(A){let q=Math.max(0.01,Math.min(A,this._max-this._min));if(q!==this._viewPortSize)this._viewPortSize=q,this.requestRender()}get viewPortSize(){return this._viewPortSize}get backgroundColor(){return this._backgroundColor}set backgroundColor(A){this._backgroundColor=u(A),this.requestRender()}get foregroundColor(){return this._foregroundColor}set foregroundColor(A){this._foregroundColor=u(A),this.requestRender()}calculateDragOffsetVirtual(A){let q=this.orientation==="vertical"?this.y:this.x,g=(this.orientation==="vertical"?A.y:A.x)-q,E=Math.max(0,Math.min((this.orientation==="vertical"?this.height:this.width)*2,g*2)),F=this.getVirtualThumbStart(),K=this.getVirtualThumbSize();return Math.max(0,Math.min(K,E-F))}setupMouseHandling(){let A=!1,q=0;this.onMouseDown=(g)=>{g.stopPropagation(),g.preventDefault();let E=this.getThumbRect();if(g.x>=E.x&&g.x<E.x+E.width&&g.y>=E.y&&g.y<E.y+E.height)A=!0,q=this.calculateDragOffsetVirtual(g);else this.updateValueFromMouseDirect(g),A=!0,q=this.calculateDragOffsetVirtual(g)},this.onMouseDrag=(g)=>{if(!A)return;g.stopPropagation(),this.updateValueFromMouseWithOffset(g,q)},this.onMouseUp=(g)=>{if(A)this.updateValueFromMouseWithOffset(g,q);A=!1}}updateValueFromMouseDirect(A){let q=this.orientation==="vertical"?this.y:this.x,g=this.orientation==="vertical"?this.height:this.width,F=(this.orientation==="vertical"?A.y:A.x)-q,K=Math.max(0,Math.min(g,F)),Z=g===0?0:K/g,j=this._max-this._min,U=this._min+Z*j;this.value=U}updateValueFromMouseWithOffset(A,q){let g=this.orientation==="vertical"?this.y:this.x,E=this.orientation==="vertical"?this.height:this.width,F=this.orientation==="vertical"?A.y:A.x,K=E*2,Z=F-g,U=Math.max(0,Math.min(E,Z))*2,G=this.getVirtualThumbSize(),Y=Math.max(0,K-G),M=U-q;M=Math.max(0,Math.min(Y,M));let Q=Y===0?0:M/Y,$=this._max-this._min,X=this._min+Q*$;this.value=X}getThumbRect(){let A=this.getVirtualThumbSize(),q=this.getVirtualThumbStart(),g=Math.floor(q/2),E=Math.ceil((q+A)/2)-g;if(this.orientation==="vertical")return{x:this.x,y:this.y+g,width:this.width,height:Math.max(1,E)};else return{x:this.x+g,y:this.y,width:Math.max(1,E),height:this.height}}renderSelf(A){if(this.orientation==="horizontal")this.renderHorizontal(A);else this.renderVertical(A)}renderHorizontal(A){let q=this.getVirtualThumbSize(),g=this.getVirtualThumbStart(),E=g+q;A.fillRect(this.x,this.y,this.width,this.height,this._backgroundColor);let F=Math.floor(g/2),K=Math.ceil(E/2)-1,Z=Math.max(0,F),j=Math.min(this.width-1,K);for(let U=Z;U<=j;U++){let G=U*2,Y=G+2,M=Math.max(g,G),$=Math.min(E,Y)-M,X=" ";if($>=2)X="\u2588";else if(M===G)X="\u258C";else X="\u2590";for(let I=0;I<this.height;I++)A.setCellWithAlphaBlending(this.x+U,this.y+I,X,this._foregroundColor,this._backgroundColor)}}renderVertical(A){let q=this.getVirtualThumbSize(),g=this.getVirtualThumbStart(),E=g+q;A.fillRect(this.x,this.y,this.width,this.height,this._backgroundColor);let F=Math.floor(g/2),K=Math.ceil(E/2)-1,Z=Math.max(0,F),j=Math.min(this.height-1,K);for(let U=Z;U<=j;U++){let G=U*2,Y=G+2,M=Math.max(g,G),$=Math.min(E,Y)-M,X=" ";if($>=2)X="\u2588";else if($>0)if(M-G===0)X="\u2580";else X="\u2584";for(let I=0;I<this.width;I++)A.setCellWithAlphaBlending(this.x+I,this.y+U,X,this._foregroundColor,this._backgroundColor)}}getVirtualThumbSize(){let A=this.orientation==="vertical"?this.height*2:this.width*2,q=this._max-this._min;if(q===0)return A;let g=Math.max(1,this._viewPortSize),E=q+g;if(E<=g)return A;let F=g/E,K=Math.floor(A*F);return Math.max(1,Math.min(K,A))}getVirtualThumbStart(){let A=this.orientation==="vertical"?this.height*2:this.width*2,q=this._max-this._min;if(q===0)return 0;let g=(this._value-this._min)/q,E=this.getVirtualThumbSize();return Math.round(g*(A-E))}}class rU extends DA{slider;startArrow;endArrow;orientation;_focusable=!0;_scrollSize=0;_scrollPosition=0;_viewportSize=0;_showArrows=!1;_manualVisibility=!1;_onChange;scrollStep=null;get visible(){return super.visible}set visible(A){this._manualVisibility=!0,super.visible=A}resetVisibilityControl(){this._manualVisibility=!1,this.recalculateVisibility()}get scrollSize(){return this._scrollSize}get scrollPosition(){return this._scrollPosition}get viewportSize(){return this._viewportSize}set scrollSize(A){if(A===this.scrollSize)return;this._scrollSize=A,this.recalculateVisibility(),this.updateSliderFromScrollState(),this.scrollPosition=this.scrollPosition}set scrollPosition(A){let q=Math.round(Math.min(Math.max(0,A),this.scrollSize-this.viewportSize));if(q!==this._scrollPosition)this._scrollPosition=q,this.updateSliderFromScrollState()}set viewportSize(A){if(A===this.viewportSize)return;this._viewportSize=A,this.slider.viewPortSize=Math.max(1,this._viewportSize),this.recalculateVisibility(),this.updateSliderFromScrollState(),this.scrollPosition=this.scrollPosition}get showArrows(){return this._showArrows}set showArrows(A){if(A===this._showArrows)return;this._showArrows=A,this.startArrow.visible=A,this.endArrow.visible=A}constructor(A,{trackOptions:q,arrowOptions:g,orientation:E,showArrows:F=!1,...K}){super(A,{flexDirection:E==="vertical"?"column":"row",alignSelf:"stretch",alignItems:"stretch",...K});this._onChange=K.onChange,this.orientation=E,this._showArrows=F;let Z=Math.max(0,this._scrollSize-this._viewportSize),j=Math.max(1,this._viewportSize),U=q?.viewPortSize??j;this.slider=new MO(A,{orientation:E,min:0,max:Z,value:this._scrollPosition,viewPortSize:U,onChange:(Q)=>{this._scrollPosition=Math.round(Q),this._onChange?.(this._scrollPosition),this.emit("change",{position:this._scrollPosition})},...E==="vertical"?{width:Math.max(1,Math.min(2,this.width)),height:"100%",marginLeft:"auto"}:{width:"100%",height:1,marginTop:"auto"},flexGrow:1,flexShrink:1,...q}),this.updateSliderFromScrollState();let G=g?{foregroundColor:g.backgroundColor,backgroundColor:g.backgroundColor,attributes:g.attributes,...g}:{};this.startArrow=new oU(A,{alignSelf:"center",visible:this.showArrows,direction:this.orientation==="vertical"?"up":"left",height:this.orientation==="vertical"?1:1,...G}),this.endArrow=new oU(A,{alignSelf:"center",visible:this.showArrows,direction:this.orientation==="vertical"?"down":"right",height:this.orientation==="vertical"?1:1,...G}),this.add(this.startArrow),this.add(this.slider),this.add(this.endArrow);let Y=void 0,M=void 0;this.startArrow.onMouseDown=(Q)=>{Q.stopPropagation(),Q.preventDefault(),this.scrollBy(-0.5,"viewport"),Y=setTimeout(()=>{this.scrollBy(-0.5,"viewport"),Y=setInterval(()=>{this.scrollBy(-0.2,"viewport")},200)},500)},this.startArrow.onMouseUp=(Q)=>{Q.stopPropagation(),clearInterval(Y)},this.endArrow.onMouseDown=(Q)=>{Q.stopPropagation(),Q.preventDefault(),this.scrollBy(0.5,"viewport"),M=setTimeout(()=>{this.scrollBy(0.5,"viewport"),M=setInterval(()=>{this.scrollBy(0.2,"viewport")},200)},500)},this.endArrow.onMouseUp=(Q)=>{Q.stopPropagation(),clearInterval(M)}}set arrowOptions(A){Object.assign(this.startArrow,A),Object.assign(this.endArrow,A),this.requestRender()}set trackOptions(A){Object.assign(this.slider,A),this.requestRender()}updateSliderFromScrollState(){let A=Math.max(0,this._scrollSize-this._viewportSize);this.slider.min=0,this.slider.max=A,this.slider.value=Math.min(this._scrollPosition,A)}scrollBy(A,q="absolute"){let E=(q==="viewport"?this.viewportSize:q==="content"?this.scrollSize:q==="step"?this.scrollStep??1:1)*A;this.scrollPosition+=E}recalculateVisibility(){if(!this._manualVisibility){let A=this.scrollSize<=this.viewportSize?1:this.viewportSize/this.scrollSize;super.visible=A<1}}handleKeyPress(A){switch(A.name){case"left":case"h":if(this.orientation!=="horizontal")return!1;return this.scrollBy(-0.2,"viewport"),!0;case"right":case"l":if(this.orientation!=="horizontal")return!1;return this.scrollBy(0.2,"viewport"),!0;case"up":case"k":if(this.orientation!=="vertical")return!1;return this.scrollBy(-0.2,"viewport"),!0;case"down":case"j":if(this.orientation!=="vertical")return!1;return this.scrollBy(0.2,"viewport"),!0;case"pageup":return this.scrollBy(-0.5,"viewport"),!0;case"pagedown":return this.scrollBy(0.5,"viewport"),!0;case"home":return this.scrollBy(-1,"content"),!0;case"end":return this.scrollBy(1,"content"),!0}return!1}}class oU extends DA{_direction;_foregroundColor;_backgroundColor;_attributes;_arrowChars;constructor(A,q){super(A,q);if(this._direction=q.direction,this._foregroundColor=q.foregroundColor?u(q.foregroundColor):JA.fromValues(1,1,1,1),this._backgroundColor=q.backgroundColor?u(q.backgroundColor):JA.fromValues(0,0,0,0),this._attributes=q.attributes??0,this._arrowChars={up:"\u25B2",down:"\u25BC",left:"\u25C0",right:"\u25B6",...q.arrowChars},!q.width)this.width=Bun.stringWidth(this.getArrowChar())}get direction(){return this._direction}set direction(A){if(this._direction!==A)this._direction=A,this.requestRender()}get foregroundColor(){return this._foregroundColor}set foregroundColor(A){if(this._foregroundColor!==A)this._foregroundColor=u(A),this.requestRender()}get backgroundColor(){return this._backgroundColor}set backgroundColor(A){if(this._backgroundColor!==A)this._backgroundColor=u(A),this.requestRender()}get attributes(){return this._attributes}set attributes(A){if(this._attributes!==A)this._attributes=A,this.requestRender()}set arrowChars(A){this._arrowChars={...this._arrowChars,...A},this.requestRender()}renderSelf(A){let q=this.getArrowChar();A.drawText(q,this.x,this.y,this._foregroundColor,this._backgroundColor,this._attributes)}getArrowChar(){switch(this._direction){case"up":return this._arrowChars.up;case"down":return this._arrowChars.down;case"left":return this._arrowChars.left;case"right":return this._arrowChars.right;default:return"?"}}}class $O extends b{viewport;_viewportCulling;constructor(A,q,g,E){super(A,E);this.viewport=q,this._viewportCulling=g}get viewportCulling(){return this._viewportCulling}set viewportCulling(A){this._viewportCulling=A}_getVisibleChildren(){if(this._viewportCulling)return pU(this.viewport,this.getChildrenSortedByPrimaryAxis(),this.primaryAxis,0).map((A)=>A.num);return this.getChildrenSortedByPrimaryAxis().map((A)=>A.num)}}class Cq extends b{static idCounter=0;internalId=0;wrapper;viewport;content;horizontalScrollBar;verticalScrollBar;_focusable=!0;selectionListener;autoScrollMouseX=0;autoScrollMouseY=0;autoScrollThresholdVertical=3;autoScrollThresholdHorizontal=3;autoScrollSpeedSlow=6;autoScrollSpeedMedium=36;autoScrollSpeedFast=72;isAutoScrolling=!1;cachedAutoScrollSpeed=3;autoScrollAccumulatorX=0;autoScrollAccumulatorY=0;scrollAccumulatorX=0;scrollAccumulatorY=0;_stickyScroll;_stickyScrollTop=!1;_stickyScrollBottom=!1;_stickyScrollLeft=!1;_stickyScrollRight=!1;_stickyStart;_hasManualScroll=!1;_isApplyingStickyScroll=!1;scrollAccel;get stickyScroll(){return this._stickyScroll}set stickyScroll(A){this._stickyScroll=A,this.updateStickyState()}get stickyStart(){return this._stickyStart}set stickyStart(A){this._stickyStart=A,this.updateStickyState()}get scrollTop(){return this.verticalScrollBar.scrollPosition}set scrollTop(A){if(this.verticalScrollBar.scrollPosition=A,!this._isApplyingStickyScroll){let q=Math.max(0,this.scrollHeight-this.viewport.height);if(!this.isAtStickyPosition()&&q>1)this._hasManualScroll=!0}this.updateStickyState()}get scrollLeft(){return this.horizontalScrollBar.scrollPosition}set scrollLeft(A){if(this.horizontalScrollBar.scrollPosition=A,!this._isApplyingStickyScroll){let q=Math.max(0,this.scrollWidth-this.viewport.width);if(!this.isAtStickyPosition()&&q>1)this._hasManualScroll=!0}this.updateStickyState()}get scrollWidth(){return this.horizontalScrollBar.scrollSize}get scrollHeight(){return this.verticalScrollBar.scrollSize}updateStickyState(){if(!this._stickyScroll)return;let A=Math.max(0,this.scrollHeight-this.viewport.height),q=Math.max(0,this.scrollWidth-this.viewport.width);if(this.scrollTop<=0){if(this._stickyScrollTop=!0,this._stickyScrollBottom=!1,this._stickyStart==="top"||this._stickyStart==="bottom"&&A===0)this._hasManualScroll=!1}else if(this.scrollTop>=A){if(this._stickyScrollTop=!1,this._stickyScrollBottom=!0,this._stickyStart==="bottom")this._hasManualScroll=!1}else this._stickyScrollTop=!1,this._stickyScrollBottom=!1;if(this.scrollLeft<=0){if(this._stickyScrollLeft=!0,this._stickyScrollRight=!1,this._stickyStart==="left"||this._stickyStart==="right"&&q===0)this._hasManualScroll=!1}else if(this.scrollLeft>=q){if(this._stickyScrollLeft=!1,this._stickyScrollRight=!0,this._stickyStart==="right")this._hasManualScroll=!1}else this._stickyScrollLeft=!1,this._stickyScrollRight=!1}applyStickyStart(A){switch(this._isApplyingStickyScroll=!0,A){case"top":this._stickyScrollTop=!0,this._stickyScrollBottom=!1,this.verticalScrollBar.scrollPosition=0;break;case"bottom":this._stickyScrollTop=!1,this._stickyScrollBottom=!0,this.verticalScrollBar.scrollPosition=Math.max(0,this.scrollHeight-this.viewport.height);break;case"left":this._stickyScrollLeft=!0,this._stickyScrollRight=!1,this.horizontalScrollBar.scrollPosition=0;break;case"right":this._stickyScrollLeft=!1,this._stickyScrollRight=!0,this.horizontalScrollBar.scrollPosition=Math.max(0,this.scrollWidth-this.viewport.width);break}this._isApplyingStickyScroll=!1}constructor(A,{wrapperOptions:q,viewportOptions:g,contentOptions:E,rootOptions:F,scrollbarOptions:K,verticalScrollbarOptions:Z,horizontalScrollbarOptions:j,stickyScroll:U=!1,stickyStart:G,scrollX:Y=!1,scrollY:M=!0,scrollAcceleration:Q,viewportCulling:$=!0,...X}){super(A,{flexDirection:"row",alignItems:"stretch",...X,...F});if(this.internalId=Cq.idCounter++,this._stickyScroll=U,this._stickyStart=G,this.scrollAccel=Q??new BU,this.wrapper=new b(A,{flexDirection:"column",flexGrow:1,...q,id:`scroll-box-wrapper-${this.internalId}`}),super.add(this.wrapper),this.viewport=new b(A,{flexDirection:"column",flexGrow:1,overflow:"hidden",onSizeChange:()=>{this.recalculateBarProps()},...g,id:`scroll-box-viewport-${this.internalId}`}),this.wrapper.add(this.viewport),this.content=new $O(A,this.viewport,$,{alignSelf:"flex-start",flexShrink:0,...Y?{minWidth:"100%"}:{minWidth:"100%",maxWidth:"100%"},...M?{minHeight:"100%"}:{minHeight:"100%",maxHeight:"100%"},onSizeChange:()=>{this.recalculateBarProps()},...E,id:`scroll-box-content-${this.internalId}`}),this.viewport.add(this.content),this.verticalScrollBar=new rU(A,{...K,...Z,arrowOptions:{...K?.arrowOptions,...Z?.arrowOptions},id:`scroll-box-vertical-scrollbar-${this.internalId}`,orientation:"vertical",onChange:(I)=>{if(this.content.translateY=-I,!this._isApplyingStickyScroll){let N=Math.max(0,this.scrollHeight-this.viewport.height);if(!this.isAtStickyPosition()&&N>1)this._hasManualScroll=!0}this.updateStickyState()}}),super.add(this.verticalScrollBar),this.horizontalScrollBar=new rU(A,{...K,...j,arrowOptions:{...K?.arrowOptions,...j?.arrowOptions},id:`scroll-box-horizontal-scrollbar-${this.internalId}`,orientation:"horizontal",onChange:(I)=>{if(this.content.translateX=-I,!this._isApplyingStickyScroll){let N=Math.max(0,this.scrollWidth-this.viewport.width);if(!this.isAtStickyPosition()&&N>1)this._hasManualScroll=!0}this.updateStickyState()}}),this.wrapper.add(this.horizontalScrollBar),this.recalculateBarProps(),G&&U)this.applyStickyStart(G);this.selectionListener=()=>{let I=this._ctx.getSelection();if(!I||!I.isSelecting)this.stopAutoScroll()},this._ctx.on("selection",this.selectionListener)}onUpdate(A){this.handleAutoScroll(A)}scrollBy(A,q="absolute"){if(typeof A==="number")this.verticalScrollBar.scrollBy(A,q);else this.verticalScrollBar.scrollBy(A.y,q),this.horizontalScrollBar.scrollBy(A.x,q)}scrollTo(A){if(typeof A==="number")this.scrollTop=A;else this.scrollTop=A.y,this.scrollLeft=A.x}isAtStickyPosition(){if(!this._stickyScroll||!this._stickyStart)return!1;let A=Math.max(0,this.scrollHeight-this.viewport.height),q=Math.max(0,this.scrollWidth-this.viewport.width);switch(this._stickyStart){case"top":return this.scrollTop===0;case"bottom":return this.scrollTop>=A;case"left":return this.scrollLeft===0;case"right":return this.scrollLeft>=q;default:return!1}}add(A,q){return this.content.add(A,q)}insertBefore(A,q){return this.content.insertBefore(A,q)}remove(A){this.content.remove(A)}getChildren(){return this.content.getChildren()}onMouseEvent(A){if(A.type==="scroll"){let q=A.scroll?.direction;if(A.modifiers.shift)q=q==="up"?"left":q==="down"?"right":q==="right"?"down":"up";let g=A.scroll?.delta??0,E=Date.now(),F=this.scrollAccel.tick(E),K=g*F;if(q==="up"){this.scrollAccumulatorY-=K;let U=Math.trunc(this.scrollAccumulatorY);if(U!==0)this.scrollTop+=U,this.scrollAccumulatorY-=U}else if(q==="down"){this.scrollAccumulatorY+=K;let U=Math.trunc(this.scrollAccumulatorY);if(U!==0)this.scrollTop+=U,this.scrollAccumulatorY-=U}else if(q==="left"){this.scrollAccumulatorX-=K;let U=Math.trunc(this.scrollAccumulatorX);if(U!==0)this.scrollLeft+=U,this.scrollAccumulatorX-=U}else if(q==="right"){this.scrollAccumulatorX+=K;let U=Math.trunc(this.scrollAccumulatorX);if(U!==0)this.scrollLeft+=U,this.scrollAccumulatorX-=U}let Z=Math.max(0,this.scrollHeight-this.viewport.height),j=Math.max(0,this.scrollWidth-this.viewport.width);if(Z>1||j>1)this._hasManualScroll=!0}if(A.type==="drag"&&A.isSelecting)this.updateAutoScroll(A.x,A.y);else if(A.type==="up")this.stopAutoScroll()}handleKeyPress(A){if(this.verticalScrollBar.handleKeyPress(A))return this._hasManualScroll=!0,this.scrollAccel.reset(),this.resetScrollAccumulators(),!0;if(this.horizontalScrollBar.handleKeyPress(A))return this._hasManualScroll=!0,this.scrollAccel.reset(),this.resetScrollAccumulators(),!0;return!1}resetScrollAccumulators(){this.scrollAccumulatorX=0,this.scrollAccumulatorY=0}startAutoScroll(A,q){if(this.stopAutoScroll(),this.autoScrollMouseX=A,this.autoScrollMouseY=q,this.cachedAutoScrollSpeed=this.getAutoScrollSpeed(A,q),this.isAutoScrolling=!0,!this.live)this.live=!0}updateAutoScroll(A,q){this.autoScrollMouseX=A,this.autoScrollMouseY=q,this.cachedAutoScrollSpeed=this.getAutoScrollSpeed(A,q);let g=this.getAutoScrollDirectionX(A),E=this.getAutoScrollDirectionY(q);if(g===0&&E===0)this.stopAutoScroll();else if(!this.isAutoScrolling)this.startAutoScroll(A,q)}stopAutoScroll(){let A=this.isAutoScrolling;if(this.isAutoScrolling=!1,this.autoScrollAccumulatorX=0,this.autoScrollAccumulatorY=0,A&&!this.hasOtherLiveReasons())this.live=!1}hasOtherLiveReasons(){return!1}handleAutoScroll(A){if(!this.isAutoScrolling)return;let q=this.getAutoScrollDirectionX(this.autoScrollMouseX),g=this.getAutoScrollDirectionY(this.autoScrollMouseY),E=this.cachedAutoScrollSpeed*(A/1000),F=!1;if(q!==0){this.autoScrollAccumulatorX+=q*E;let K=Math.trunc(this.autoScrollAccumulatorX);if(K!==0)this.scrollLeft+=K,this.autoScrollAccumulatorX-=K,F=!0}if(g!==0){this.autoScrollAccumulatorY+=g*E;let K=Math.trunc(this.autoScrollAccumulatorY);if(K!==0)this.scrollTop+=K,this.autoScrollAccumulatorY-=K,F=!0}if(F)this._ctx.requestSelectionUpdate();if(q===0&&g===0)this.stopAutoScroll()}getAutoScrollDirectionX(A){let q=A-this.x,g=q,E=this.width-q;if(g<=this.autoScrollThresholdHorizontal)return this.scrollLeft>0?-1:0;else if(E<=this.autoScrollThresholdHorizontal){let F=this.scrollWidth-this.viewport.width;return this.scrollLeft<F?1:0}return 0}getAutoScrollDirectionY(A){let q=A-this.y,g=q,E=this.height-q;if(g<=this.autoScrollThresholdVertical)return this.scrollTop>0?-1:0;else if(E<=this.autoScrollThresholdVertical){let F=this.scrollHeight-this.viewport.height;return this.scrollTop<F?1:0}return 0}getAutoScrollSpeed(A,q){let g=A-this.x,E=q-this.y,F=g,K=this.width-g,Z=E,j=this.height-E,U=Math.min(F,K,Z,j);if(U<=1)return this.autoScrollSpeedFast;else if(U<=2)return this.autoScrollSpeedMedium;else return this.autoScrollSpeedSlow}recalculateBarProps(){let A=this._isApplyingStickyScroll;if(this._isApplyingStickyScroll=!0,this.verticalScrollBar.scrollSize=this.content.height,this.verticalScrollBar.viewportSize=this.viewport.height,this.horizontalScrollBar.scrollSize=this.content.width,this.horizontalScrollBar.viewportSize=this.viewport.width,this._stickyScroll){let q=Math.max(0,this.scrollHeight-this.viewport.height),g=Math.max(0,this.scrollWidth-this.viewport.width);if(this._stickyStart&&!this._hasManualScroll)this.applyStickyStart(this._stickyStart);else{if(this._stickyScrollTop)this.scrollTop=0;else if(this._stickyScrollBottom&&q>0)this.scrollTop=q;if(this._stickyScrollLeft)this.scrollLeft=0;else if(this._stickyScrollRight&&g>0)this.scrollLeft=g}}this._isApplyingStickyScroll=A,process.nextTick(()=>{this.requestRender()})}set rootOptions(A){Object.assign(this,A),this.requestRender()}set wrapperOptions(A){Object.assign(this.wrapper,A),this.requestRender()}set viewportOptions(A){Object.assign(this.viewport,A),this.requestRender()}set contentOptions(A){Object.assign(this.content,A),this.requestRender()}set scrollbarOptions(A){Object.assign(this.verticalScrollBar,A),Object.assign(this.horizontalScrollBar,A),this.requestRender()}set verticalScrollbarOptions(A){Object.assign(this.verticalScrollBar,A),this.requestRender()}set horizontalScrollbarOptions(A){Object.assign(this.horizontalScrollBar,A),this.requestRender()}get scrollAcceleration(){return this.scrollAccel}set scrollAcceleration(A){this.scrollAccel=A}get viewportCulling(){return this.content.viewportCulling}set viewportCulling(A){this.content.viewportCulling=A,this.requestRender()}destroySelf(){if(this.selectionListener)this._ctx.off("selection",this.selectionListener),this.selectionListener=void 0;super.destroySelf()}}var t4;((A)=>{A.SELECTION_CHANGED="selectionChanged",A.ITEM_SELECTED="itemSelected"})(t4||={});var s4;((A)=>{A.SELECTION_CHANGED="selectionChanged",A.ITEM_SELECTED="itemSelected"})(s4||={});import{exec as vV}from"child_process";import{appendFileSync as n4,writeFileSync as r4}from"fs";import{join as o4}from"path";import{homedir as e4}from"os";var _O=o4(e4(),".config","hn-cli","debug.log"),XO=!1;function AH(){if(!XO)try{r4(_O,`--- HN CLI Debug Log Started: ${new Date().toISOString()} ---
91
91
  `),XO=!0}catch{}}function FA(...A){AH();let q=new Date().toISOString(),g=A.map((E)=>{if(typeof E==="object")try{return JSON.stringify(E,null,2)}catch{return String(E)}return String(E)}).join(" ");try{n4(_O,`[${q}] ${g}
92
- `)}catch{}}import{homedir as qH}from"os";import{join as HO}from"path";import{existsSync as VO,mkdirSync as gH,readFileSync as EH,writeFileSync as FH}from"fs";var WO=[{id:"claude-haiku-4-5-20251001",name:"Claude Haiku 4.5"},{id:"claude-sonnet-4-5-20250929",name:"Claude Sonnet 4.5"},{id:"claude-opus-4-5-20251101",name:"Claude Opus 4.5"}],LO=[{id:"gpt-5-nano-2025-08-07",name:"GPT-5 Nano"},{id:"gpt-5-mini-2025-08-07",name:"GPT-5 Mini"},{id:"gpt-5.2-2025-12-11",name:"GPT-5.2"}],KH="claude-haiku-4-5-20251001",ZH="gpt-5-nano-2025-08-07",U6="claude-haiku-4-5-20251001",Rg="gpt-5-nano-2025-08-07",YG=HO(qH(),".config","hn-cli"),QG=HO(YG,"config.json");function NO(A){if(!A.anthropicApiKey)delete A.anthropicModel;if(!A.openaiApiKey)delete A.openaiModel;if(A.provider){if(!(A.provider==="anthropic"?!!A.anthropicApiKey:!!A.openaiApiKey))delete A.provider}return A}function iA(){try{if(VO(QG)){let A=EH(QG,"utf-8"),q=JSON.parse(A);return NO(q)}}catch{}return{}}function Kg(A){if(!VO(YG))gH(YG,{recursive:!0});let q=NO(A);FH(QG,JSON.stringify(q,null,2))}function tA(A){if(A==="anthropic"){if(process.env.ANTHROPIC_API_KEY)return process.env.ANTHROPIC_API_KEY}else if(A==="openai"){if(process.env.OPENAI_API_KEY)return process.env.OPENAI_API_KEY}let q=iA();if(A==="anthropic")return q.anthropicApiKey;else return q.openaiApiKey}function Gq(){if(process.env.ANTHROPIC_API_KEY)return"anthropic";if(process.env.OPENAI_API_KEY)return"openai";let A=iA();if(A.provider&&tA(A.provider))return A.provider;if(A.anthropicApiKey)return"anthropic";if(A.openaiApiKey)return"openai";return}function TO(){return Gq()!==void 0}function FF(A){let q=iA();if(A==="anthropic")return q.anthropicModel||KH;else return q.openaiModel||ZH}function PO(A,q){let g=iA();if(A==="anthropic")g.anthropicModel=q;else g.openaiModel=q;Kg(g)}function IO(){let A=iA();Kg({telemetryEnabled:A.telemetryEnabled,userId:A.userId,filterSettings:A.filterSettings})}function G6(){return iA().telemetryEnabled!==!1}function yZ(A){let q=iA();q.telemetryEnabled=A,Kg(q)}function xO(){let A=iA();if(A.userId)return A.userId;let q=crypto.randomUUID();return A.userId=q,Kg(A),q}var SO="0.4.1";var hO={name:"@brianlovin/hn-cli",version:SO,description:"Interactive terminal UI for browsing Hacker News",module:"src/index.ts",type:"module",bin:{hn:"./dist/cli.js"},files:["dist"],scripts:{start:"bun run src/index.ts",dev:"bun run --watch src/index.ts","dev:update":"HN_SIMULATE_VERSION=0.1.0 HN_SIMULATE_LATEST=0.3.0 bun run --watch src/index.ts",build:"bun build src/index.ts --outdir dist --target bun --minify --entry-naming cli.js",prepublishOnly:"bun run build && bun run typecheck",test:"bun test",typecheck:"bun tsc --noEmit",debug:"bun run src/debug.ts"},repository:{type:"git",url:"git+https://github.com/brianlovin/hn-cli.git"},homepage:"https://github.com/brianlovin/hn-cli",bugs:"https://github.com/brianlovin/hn-cli/issues",author:"Brian Lovin",license:"MIT",keywords:["hacker-news","cli","terminal","tui","hn"],engines:{bun:">=1.0.0"},publishConfig:{access:"public",provenance:!0},devDependencies:{"@types/bun":"latest"},peerDependencies:{typescript:"^5"},dependencies:{"@anthropic-ai/sdk":"^0.71.2","@opentui/core":"^0.1.75",openai:"^6.16.0"}};var jH=hO.version;function aO(){if(QH)return!1;if(process.argv.includes("--telemetry-test"))return!1;if(process.argv.includes("--watch"))return!0;let A=process.argv[1]||"";if(A.includes("/src/")||A.endsWith("/src/index.ts"))return!0;return!1}var UH="https://brianlovin.com/api/hn-cli/telemetry",MG=10,GH=5000,YH=1500,JG=3,xq=[],$G=!1,QH=!1,OG=!1,r6=0;function zO(){$G=!0}function nA(A,q){if(!$G||!G6()||aO())return;if(xq.push({event:A,timestamp:Date.now(),properties:q}),xq.length>=MG)JH().catch(()=>{})}async function JH(){await wO(GH)}async function BO(){await wO(YH)}async function wO(A){if(!$G||!G6()||aO()||xq.length===0)return;if(OG)return;if(r6>=JG)return;OG=!0;let q=[...xq];xq=[];try{let g=xO(),E=new AbortController,F=setTimeout(()=>E.abort(),A),K=await fetch(UH,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({userId:g,version:jH,events:q}),signal:E.signal});if(clearTimeout(F),!K.ok){if(r6++,xq.length<MG*2&&r6<JG)xq=[...q,...xq]}else r6=0}catch{if(r6++,xq.length<MG*2&&r6<JG)xq=[...q,...xq]}finally{OG=!1}}var Y6={maxPosts:24,fetchLimit:200,hoursWindow:24,minPoints:50,minComments:20,commentWeight:0.75,recencyBonusMax:100,maxRootComments:12,maxChildComments:8,maxCommentLevel:3,storiesTtlMinutes:5},o6={maxPosts:{min:1,max:50,step:1,label:"Max Stories",description:"Maximum number of stories to display"},fetchLimit:{min:50,max:500,step:50,label:"Fetch Limit",description:"Number of posts to fetch from HN API"},hoursWindow:{min:1,max:168,step:1,label:"Time Window (hours)",description:"Only show stories that are trending in this window"},minPoints:{min:0,max:500,step:10,label:"Min Points",description:"The minimum number of votes required"},minComments:{min:0,max:100,step:5,label:"Min Comments",description:"The minimum number of comments required"},commentWeight:{min:0,max:2,step:0.25,label:"Comment Weight",description:"Weight of comments in ranking (0-2)"},recencyBonusMax:{min:0,max:200,step:10,label:"Recency Bonus",description:"Maximum bonus points for new posts"},maxRootComments:{min:1,max:50,step:1,label:"Root Comments",description:"How many root comments to display per story"},maxChildComments:{min:1,max:20,step:1,label:"Child Comments",description:"The maximum number of child comments to display per root comment"},maxCommentLevel:{min:1,max:10,step:1,label:"Nesting Depth",description:"The maximum depth of nested replies to display"},storiesTtlMinutes:{min:1,max:60,step:1,label:"Cache TTL (min)",description:"How long to cache stories before refresh"}},CO=[{key:"filtering",label:"Story Filtering",settings:["maxPosts","hoursWindow","minPoints","minComments"]},{key:"comments",label:"Comments",settings:["maxRootComments","maxChildComments","maxCommentLevel"]}];function DO(A,q){let g=o6[A],E=Math.max(g.min,Math.min(g.max,q)),F=Math.round((E-g.min)/g.step);return g.min+F*g.step}function yO(A){let q={...Y6};for(let g of Object.keys(Y6))if(A[g]!==void 0)q[g]=DO(g,A[g]);return q}function Yq(){let A=iA();if(!A.filterSettings)return{...Y6};return yO(A.filterSettings)}function bO(A){let q=iA();q.filterSettings=yO(A),Kg(q)}function vO(A,q){let g=Yq();return g[A]=DO(A,q),bO(g),g}function mO(){return bO(Y6),{...Y6}}function fO(A,q){if(A==="commentWeight")return q.toFixed(2);return String(q)}var OH="https://hacker-news.firebaseio.com/v0",cO="https://api.hnpwa.com/v0";async function MH(){let A=await fetch(`${OH}/topstories.json`);if(!A.ok)throw new Error("Failed to fetch top stories");return A.json()}function dO(A,q){if(!A)return null;if(A.level>q.maxCommentLevel)return null;return{...A,comments:A.comments.slice(0,q.maxChildComments).map((g)=>dO(g,q)).filter(Boolean)}}function pO(A,q,g){let E=A.comments.slice(0,g.maxRootComments).map((F)=>dO(F,g)).filter(Boolean);return{...A,comments:q?E:[]}}async function XG(A,q){let g=q??Yq();try{let E=await fetch(`${cO}/item/${A}.json`);if(!E.ok)return null;let F=await E.json();return pO(F,!0,g)}catch{return null}}async function uO(A){let q=A??Yq(),E=(await MH()).sort((Q,$)=>$-Q).slice(0,q.fetchLimit),F=await Promise.all(E.map(async(Q)=>{try{let $=await fetch(`${cO}/item/${Q}.json`);if(!$.ok)return null;let X=await $.json();return pO(X,!1,q)}catch{return null}})),K=Date.now()/1000,Z=K-3600*q.hoursWindow;return F.filter((Q)=>Q!==null).filter((Q)=>Q.type==="link").filter((Q)=>Q.time>Z).filter((Q)=>(Q.points||0)>=q.minPoints||Q.comments_count>=q.minComments).sort((Q,$)=>{let X=(K-Q.time)/3600,I=(K-$.time)/3600,N=Math.max(0,q.recencyBonusMax*(1-X/q.hoursWindow)),W=Math.max(0,q.recencyBonusMax*(1-I/q.hoursWindow)),x=(Q.points||0)+Q.comments_count*q.commentWeight+N;return($.points||0)+$.comments_count*q.commentWeight+W-x}).slice(0,q.maxPosts)}var $H={bg:void 0,bgSelected:"#2a2a2a",border:"#3a3a3a",textPrimary:"#e0e0e0",textSecondary:"#888888",textTertiary:"#666666",accent:"#ff6600",link:"#6699ff",success:"#4ade80",error:"#ef4444",hint:"#a855f7",commentL1:"#555555",commentL2:"#444444",commentL3:"#333333"},XH={bg:void 0,bgSelected:"#e8e8e8",border:"#cccccc",textPrimary:"#1a1a1a",textSecondary:"#666666",textTertiary:"#888888",accent:"#ff6600",link:"#0066cc",success:"#22c55e",error:"#dc2626",hint:"#9333ea",commentL1:"#cccccc",commentL2:"#dddddd",commentL3:"#eeeeee"},V={...$H};function _H(A){if(!A)return!1;let q=A.replace("#",""),g=parseInt(q.substr(0,2),16),E=parseInt(q.substr(2,2),16),F=parseInt(q.substr(4,2),16);return(0.299*g+0.587*E+0.114*F)/255>0.5}async function lO(A){try{let q=await A.getPalette({timeout:100});if(q.defaultBackground&&_H(q.defaultBackground))Object.assign(V,XH)}catch{}}function iO(A){return{0:V.accent,1:V.commentL1,2:V.commentL2,3:V.commentL3}[A]??V.commentL3}var yA=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];function tO(A,q){if(A.length<=q)return A;return A.substring(0,q-3)+"..."}function Sg(A){return A.replace(/<\/p>\s*<p>/g,`
92
+ `)}catch{}}import{homedir as qH}from"os";import{join as HO}from"path";import{existsSync as VO,mkdirSync as gH,readFileSync as EH,writeFileSync as FH}from"fs";var WO=[{id:"claude-haiku-4-5-20251001",name:"Claude Haiku 4.5"},{id:"claude-sonnet-4-5-20250929",name:"Claude Sonnet 4.5"},{id:"claude-opus-4-5-20251101",name:"Claude Opus 4.5"}],LO=[{id:"gpt-5-nano-2025-08-07",name:"GPT-5 Nano"},{id:"gpt-5-mini-2025-08-07",name:"GPT-5 Mini"},{id:"gpt-5.2-2025-12-11",name:"GPT-5.2"}],KH="claude-haiku-4-5-20251001",ZH="gpt-5-nano-2025-08-07",U6="claude-haiku-4-5-20251001",Rg="gpt-5-nano-2025-08-07",YG=HO(qH(),".config","hn-cli"),QG=HO(YG,"config.json");function NO(A){if(!A.anthropicApiKey)delete A.anthropicModel;if(!A.openaiApiKey)delete A.openaiModel;if(A.provider){if(!(A.provider==="anthropic"?!!A.anthropicApiKey:!!A.openaiApiKey))delete A.provider}return A}function iA(){try{if(VO(QG)){let A=EH(QG,"utf-8"),q=JSON.parse(A);return NO(q)}}catch{}return{}}function Kg(A){if(!VO(YG))gH(YG,{recursive:!0});let q=NO(A);FH(QG,JSON.stringify(q,null,2))}function tA(A){if(A==="anthropic"){if(process.env.ANTHROPIC_API_KEY)return process.env.ANTHROPIC_API_KEY}else if(A==="openai"){if(process.env.OPENAI_API_KEY)return process.env.OPENAI_API_KEY}let q=iA();if(A==="anthropic")return q.anthropicApiKey;else return q.openaiApiKey}function Gq(){if(process.env.ANTHROPIC_API_KEY)return"anthropic";if(process.env.OPENAI_API_KEY)return"openai";let A=iA();if(A.provider&&tA(A.provider))return A.provider;if(A.anthropicApiKey)return"anthropic";if(A.openaiApiKey)return"openai";return}function TO(){return Gq()!==void 0}function FF(A){let q=iA();if(A==="anthropic")return q.anthropicModel||KH;else return q.openaiModel||ZH}function PO(A,q){let g=iA();if(A==="anthropic")g.anthropicModel=q;else g.openaiModel=q;Kg(g)}function IO(){let A=iA();Kg({telemetryEnabled:A.telemetryEnabled,userId:A.userId,filterSettings:A.filterSettings})}function G6(){return iA().telemetryEnabled!==!1}function yZ(A){let q=iA();q.telemetryEnabled=A,Kg(q)}function xO(){let A=iA();if(A.userId)return A.userId;let q=crypto.randomUUID();return A.userId=q,Kg(A),q}var SO="0.4.2";var hO={name:"@brianlovin/hn-cli",version:SO,description:"Interactive terminal UI for browsing Hacker News",module:"src/index.ts",type:"module",bin:{hn:"./dist/cli.js"},files:["dist"],scripts:{start:"bun run src/index.ts",dev:"bun run --watch src/index.ts","dev:update":"HN_SIMULATE_VERSION=0.1.0 HN_SIMULATE_LATEST=0.3.0 bun run --watch src/index.ts",build:"bun build src/index.ts --outdir dist --target bun --minify --entry-naming cli.js",prepublishOnly:"bun run build && bun run typecheck",test:"bun test",typecheck:"bun tsc --noEmit",debug:"bun run src/debug.ts"},repository:{type:"git",url:"git+https://github.com/brianlovin/hn-cli.git"},homepage:"https://github.com/brianlovin/hn-cli",bugs:"https://github.com/brianlovin/hn-cli/issues",author:"Brian Lovin",license:"MIT",keywords:["hacker-news","cli","terminal","tui","hn"],engines:{bun:">=1.0.0"},publishConfig:{access:"public",provenance:!0},devDependencies:{"@types/bun":"latest"},peerDependencies:{typescript:"^5"},dependencies:{"@anthropic-ai/sdk":"^0.71.2","@opentui/core":"^0.1.75",openai:"^6.16.0"}};var jH=hO.version;function aO(){if(QH)return!1;if(process.argv.includes("--telemetry-test"))return!1;if(process.argv.includes("--watch"))return!0;let A=process.argv[1]||"";if(A.includes("/src/")||A.endsWith("/src/index.ts"))return!0;return!1}var UH="https://brianlovin.com/api/hn-cli/telemetry",MG=10,GH=5000,YH=1500,JG=3,xq=[],$G=!1,QH=!1,OG=!1,r6=0;function zO(){$G=!0}function nA(A,q){if(!$G||!G6()||aO())return;if(xq.push({event:A,timestamp:Date.now(),properties:q}),xq.length>=MG)JH().catch(()=>{})}async function JH(){await wO(GH)}async function BO(){await wO(YH)}async function wO(A){if(!$G||!G6()||aO()||xq.length===0)return;if(OG)return;if(r6>=JG)return;OG=!0;let q=[...xq];xq=[];try{let g=xO(),E=new AbortController,F=setTimeout(()=>E.abort(),A),K=await fetch(UH,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({userId:g,version:jH,events:q}),signal:E.signal});if(clearTimeout(F),!K.ok){if(r6++,xq.length<MG*2&&r6<JG)xq=[...q,...xq]}else r6=0}catch{if(r6++,xq.length<MG*2&&r6<JG)xq=[...q,...xq]}finally{OG=!1}}var Y6={maxPosts:24,fetchLimit:200,hoursWindow:24,minPoints:50,minComments:20,commentWeight:0.75,recencyBonusMax:100,maxRootComments:12,maxChildComments:8,maxCommentLevel:3,storiesTtlMinutes:5},o6={maxPosts:{min:1,max:50,step:1,label:"Max Stories",description:"Maximum number of stories to display"},fetchLimit:{min:50,max:500,step:50,label:"Fetch Limit",description:"Number of posts to fetch from HN API"},hoursWindow:{min:1,max:168,step:1,label:"Time Window (hours)",description:"Only show stories that are trending in this window"},minPoints:{min:0,max:500,step:10,label:"Min Points",description:"The minimum number of votes required"},minComments:{min:0,max:100,step:5,label:"Min Comments",description:"The minimum number of comments required"},commentWeight:{min:0,max:2,step:0.25,label:"Comment Weight",description:"Weight of comments in ranking (0-2)"},recencyBonusMax:{min:0,max:200,step:10,label:"Recency Bonus",description:"Maximum bonus points for new posts"},maxRootComments:{min:1,max:50,step:1,label:"Root Comments",description:"How many root comments to display per story"},maxChildComments:{min:1,max:20,step:1,label:"Child Comments",description:"The maximum number of child comments to display per root comment"},maxCommentLevel:{min:1,max:10,step:1,label:"Nesting Depth",description:"The maximum depth of nested replies to display"},storiesTtlMinutes:{min:1,max:60,step:1,label:"Cache TTL (min)",description:"How long to cache stories before refresh"}},CO=[{key:"filtering",label:"Story Filtering",settings:["maxPosts","hoursWindow","minPoints","minComments"]},{key:"comments",label:"Comments",settings:["maxRootComments","maxChildComments","maxCommentLevel"]}];function DO(A,q){let g=o6[A],E=Math.max(g.min,Math.min(g.max,q)),F=Math.round((E-g.min)/g.step);return g.min+F*g.step}function yO(A){let q={...Y6};for(let g of Object.keys(Y6))if(A[g]!==void 0)q[g]=DO(g,A[g]);return q}function Yq(){let A=iA();if(!A.filterSettings)return{...Y6};return yO(A.filterSettings)}function bO(A){let q=iA();q.filterSettings=yO(A),Kg(q)}function vO(A,q){let g=Yq();return g[A]=DO(A,q),bO(g),g}function mO(){return bO(Y6),{...Y6}}function fO(A,q){if(A==="commentWeight")return q.toFixed(2);return String(q)}var OH="https://hacker-news.firebaseio.com/v0",cO="https://api.hnpwa.com/v0";async function MH(){let A=await fetch(`${OH}/topstories.json`);if(!A.ok)throw new Error("Failed to fetch top stories");return A.json()}function dO(A,q){if(!A)return null;if(A.level>q.maxCommentLevel)return null;return{...A,comments:A.comments.slice(0,q.maxChildComments).map((g)=>dO(g,q)).filter(Boolean)}}function pO(A,q,g){let E=A.comments.slice(0,g.maxRootComments).map((F)=>dO(F,g)).filter(Boolean);return{...A,comments:q?E:[]}}async function XG(A,q){let g=q??Yq();try{let E=await fetch(`${cO}/item/${A}.json`);if(!E.ok)return null;let F=await E.json();return pO(F,!0,g)}catch{return null}}async function uO(A){let q=A??Yq(),E=(await MH()).sort((Q,$)=>$-Q).slice(0,q.fetchLimit),F=await Promise.all(E.map(async(Q)=>{try{let $=await fetch(`${cO}/item/${Q}.json`);if(!$.ok)return null;let X=await $.json();return pO(X,!1,q)}catch{return null}})),K=Date.now()/1000,Z=K-3600*q.hoursWindow;return F.filter((Q)=>Q!==null).filter((Q)=>Q.type==="link").filter((Q)=>Q.time>Z).filter((Q)=>(Q.points||0)>=q.minPoints||Q.comments_count>=q.minComments).sort((Q,$)=>{let X=(K-Q.time)/3600,I=(K-$.time)/3600,N=Math.max(0,q.recencyBonusMax*(1-X/q.hoursWindow)),W=Math.max(0,q.recencyBonusMax*(1-I/q.hoursWindow)),x=(Q.points||0)+Q.comments_count*q.commentWeight+N;return($.points||0)+$.comments_count*q.commentWeight+W-x}).slice(0,q.maxPosts)}var $H={bg:void 0,bgSelected:"#2a2a2a",border:"#3a3a3a",textPrimary:"#e0e0e0",textSecondary:"#888888",textTertiary:"#666666",accent:"#ff6600",link:"#6699ff",success:"#4ade80",error:"#ef4444",hint:"#a855f7",commentL1:"#555555",commentL2:"#444444",commentL3:"#333333"},XH={bg:void 0,bgSelected:"#e8e8e8",border:"#cccccc",textPrimary:"#1a1a1a",textSecondary:"#666666",textTertiary:"#888888",accent:"#ff6600",link:"#0066cc",success:"#22c55e",error:"#dc2626",hint:"#9333ea",commentL1:"#cccccc",commentL2:"#dddddd",commentL3:"#eeeeee"},V={...$H};function _H(A){if(!A)return!1;let q=A.replace("#",""),g=parseInt(q.substr(0,2),16),E=parseInt(q.substr(2,2),16),F=parseInt(q.substr(4,2),16);return(0.299*g+0.587*E+0.114*F)/255>0.5}async function lO(A){try{let q=await A.getPalette({timeout:100});if(q.defaultBackground&&_H(q.defaultBackground))Object.assign(V,XH)}catch{}}function iO(A){return{0:V.accent,1:V.commentL1,2:V.commentL2,3:V.commentL3}[A]??V.commentL3}var yA=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];function tO(A,q){if(A.length<=q)return A;return A.substring(0,q-3)+"..."}function Sg(A){return A.replace(/<\/p>\s*<p>/g,`
93
93
 
94
94
  `).replace(/<p>/g,`
95
95
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brianlovin/hn-cli",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Interactive terminal UI for browsing Hacker News",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",