silvery 0.3.0 → 0.4.1

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 (120) hide show
  1. package/README.md +41 -145
  2. package/dist/chalk.js +3 -0
  3. package/dist/chalk.js.map +11 -0
  4. package/dist/index.js +340 -0
  5. package/dist/index.js.map +282 -0
  6. package/dist/ink.js +129 -0
  7. package/dist/ink.js.map +140 -0
  8. package/dist/runtime.js +394 -0
  9. package/dist/runtime.js.map +286 -0
  10. package/dist/theme.js +343 -0
  11. package/dist/theme.js.map +286 -0
  12. package/dist/ui/animation.js +3 -0
  13. package/dist/ui/animation.js.map +15 -0
  14. package/dist/ui/ansi.js +3 -0
  15. package/dist/ui/ansi.js.map +10 -0
  16. package/dist/ui/cli.js +8 -0
  17. package/dist/ui/cli.js.map +14 -0
  18. package/dist/ui/display.js +4 -0
  19. package/dist/ui/display.js.map +10 -0
  20. package/dist/ui/image.js +4 -0
  21. package/dist/ui/image.js.map +15 -0
  22. package/dist/ui/input.js +3 -0
  23. package/dist/ui/input.js.map +11 -0
  24. package/dist/ui/progress.js +8 -0
  25. package/dist/ui/progress.js.map +20 -0
  26. package/dist/ui/react.js +3 -0
  27. package/dist/ui/react.js.map +15 -0
  28. package/dist/ui/utils.js +3 -0
  29. package/dist/ui/utils.js.map +10 -0
  30. package/dist/ui/wrappers.js +14 -0
  31. package/dist/ui/wrappers.js.map +19 -0
  32. package/dist/ui.js +17 -0
  33. package/dist/ui.js.map +20 -0
  34. package/package.json +67 -15
  35. package/src/index.ts +67 -1
  36. package/src/runtime.ts +4 -0
  37. package/src/theme.ts +4 -0
  38. package/src/ui/animation.ts +2 -0
  39. package/src/ui/ansi.ts +2 -0
  40. package/src/ui/cli.ts +2 -0
  41. package/src/ui/display.ts +2 -0
  42. package/src/ui/image.ts +2 -0
  43. package/src/ui/input.ts +2 -0
  44. package/src/ui/progress.ts +2 -0
  45. package/src/ui/react.ts +2 -0
  46. package/src/ui/utils.ts +2 -0
  47. package/src/ui/wrappers.ts +2 -0
  48. package/src/ui.ts +4 -0
  49. package/examples/CLAUDE.md +0 -75
  50. package/examples/_banner.tsx +0 -60
  51. package/examples/cli.ts +0 -228
  52. package/examples/index.md +0 -101
  53. package/examples/inline/inline-nontty.tsx +0 -98
  54. package/examples/inline/inline-progress.tsx +0 -79
  55. package/examples/inline/inline-simple.tsx +0 -63
  56. package/examples/inline/scrollback.tsx +0 -185
  57. package/examples/interactive/_input-debug.tsx +0 -110
  58. package/examples/interactive/_stdin-test.ts +0 -71
  59. package/examples/interactive/_textarea-bare.tsx +0 -45
  60. package/examples/interactive/aichat/components.tsx +0 -468
  61. package/examples/interactive/aichat/index.tsx +0 -207
  62. package/examples/interactive/aichat/script.ts +0 -460
  63. package/examples/interactive/aichat/state.ts +0 -326
  64. package/examples/interactive/aichat/types.ts +0 -19
  65. package/examples/interactive/app-todo.tsx +0 -198
  66. package/examples/interactive/async-data.tsx +0 -208
  67. package/examples/interactive/cli-wizard.tsx +0 -332
  68. package/examples/interactive/clipboard.tsx +0 -183
  69. package/examples/interactive/components.tsx +0 -463
  70. package/examples/interactive/data-explorer.tsx +0 -506
  71. package/examples/interactive/dev-tools.tsx +0 -379
  72. package/examples/interactive/explorer.tsx +0 -747
  73. package/examples/interactive/gallery.tsx +0 -652
  74. package/examples/interactive/inline-bench.tsx +0 -136
  75. package/examples/interactive/kanban.tsx +0 -267
  76. package/examples/interactive/layout-ref.tsx +0 -185
  77. package/examples/interactive/outline.tsx +0 -171
  78. package/examples/interactive/paste-demo.tsx +0 -198
  79. package/examples/interactive/scroll.tsx +0 -77
  80. package/examples/interactive/search-filter.tsx +0 -240
  81. package/examples/interactive/task-list.tsx +0 -279
  82. package/examples/interactive/terminal.tsx +0 -798
  83. package/examples/interactive/textarea.tsx +0 -103
  84. package/examples/interactive/theme.tsx +0 -336
  85. package/examples/interactive/transform.tsx +0 -256
  86. package/examples/interactive/virtual-10k.tsx +0 -413
  87. package/examples/kitty/canvas.tsx +0 -519
  88. package/examples/kitty/generate-samples.ts +0 -236
  89. package/examples/kitty/image-component.tsx +0 -273
  90. package/examples/kitty/images.tsx +0 -604
  91. package/examples/kitty/input.tsx +0 -371
  92. package/examples/kitty/keys.tsx +0 -378
  93. package/examples/kitty/paint.tsx +0 -1017
  94. package/examples/layout/dashboard.tsx +0 -551
  95. package/examples/layout/live-resize.tsx +0 -290
  96. package/examples/layout/overflow.tsx +0 -51
  97. package/examples/playground/README.md +0 -69
  98. package/examples/playground/build.ts +0 -61
  99. package/examples/playground/index.html +0 -420
  100. package/examples/playground/playground-app.tsx +0 -416
  101. package/examples/runtime/elm-counter.tsx +0 -206
  102. package/examples/runtime/hello-runtime.tsx +0 -73
  103. package/examples/runtime/pipe-composition.tsx +0 -184
  104. package/examples/runtime/run-counter.tsx +0 -78
  105. package/examples/runtime/runtime-counter.tsx +0 -197
  106. package/examples/screenshots/generate.tsx +0 -563
  107. package/examples/scrollback-perf.tsx +0 -230
  108. package/examples/viewer.tsx +0 -654
  109. package/examples/web/build.ts +0 -365
  110. package/examples/web/canvas-app.tsx +0 -80
  111. package/examples/web/canvas.html +0 -89
  112. package/examples/web/dom-app.tsx +0 -81
  113. package/examples/web/dom.html +0 -113
  114. package/examples/web/showcase-app.tsx +0 -107
  115. package/examples/web/showcase.html +0 -34
  116. package/examples/web/showcases/index.tsx +0 -56
  117. package/examples/web/viewer-app.tsx +0 -555
  118. package/examples/web/viewer.html +0 -30
  119. package/examples/web/xterm-app.tsx +0 -105
  120. package/examples/web/xterm.html +0 -118
@@ -0,0 +1,8 @@
1
+ import chalk2 from"chalk";var CURSOR_HIDE="\x1B[?25l",CURSOR_SHOW="\x1B[?25h",CURSOR_TO_START="\r",CLEAR_LINE_END="\x1B[K",CLEAR_LINE="\x1B[2K";var cursorUp=(n=1)=>`\x1B[${n}A`;function write(text,stream=process.stdout){stream.write(text)}function isTTY(stream=process.stdout){if(process.env.FORCE_TTY==="1")return!0;return stream.isTTY??!1}function getTerminalWidth(stream=process.stdout){return stream.columns??80}import chalk from"chalk";var SPINNER_FRAMES={dots:["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"],line:["-","\\","|","/"],arc:["◜","◠","◝","◞","◡","◟"],bounce:["⠁","⠂","⠄","⠂"],pulse:["█","▓","▒","░","▒","▓"]},SPINNER_INTERVALS={dots:80,line:120,arc:100,bounce:120,pulse:100};class Spinner{text;style;color;stream;hideCursor;interval;frameIndex=0;timer=null;isSpinning=!1;constructor(textOrOptions){let options=typeof textOrOptions==="string"?{text:textOrOptions}:textOrOptions??{};this.text=options.text??"",this.style=options.style??"dots",this.color=options.color??"cyan",this.stream=options.stream??process.stdout,this.hideCursor=options.hideCursor??!0,this.interval=options.interval??SPINNER_INTERVALS[this.style]}get currentText(){return this.text}set currentText(value){if(this.text=value,this.isSpinning)this.render()}get spinning(){return this.isSpinning}start(text){if(text!==void 0)this.text=text;if(this.isSpinning)return this;if(this.isSpinning=!0,this.frameIndex=0,this.hideCursor&&isTTY(this.stream))write(CURSOR_HIDE,this.stream);return this.render(),this.timer=setInterval(()=>{this.frameIndex=(this.frameIndex+1)%SPINNER_FRAMES[this.style].length,this.render()},this.interval),this}stop(){if(!this.isSpinning)return this;if(this.isSpinning=!1,this.timer)clearInterval(this.timer),this.timer=null;if(this.clear(),this.hideCursor&&isTTY(this.stream))write(CURSOR_SHOW,this.stream);return this}succeed(text){return this.stopWithSymbol(chalk.green("✔"),text??this.text)}fail(text){return this.stopWithSymbol(chalk.red("✖"),text??this.text)}warn(text){return this.stopWithSymbol(chalk.yellow("⚠"),text??this.text)}info(text){return this.stopWithSymbol(chalk.blue("ℹ"),text??this.text)}clear(){if(isTTY(this.stream))write(`${CURSOR_TO_START}${CLEAR_LINE_END}`,this.stream);return this}render(){let frame=SPINNER_FRAMES[this.style][this.frameIndex],colorFn=chalk[this.color],coloredFrame=colorFn?colorFn(frame):frame,output=this.text?`${coloredFrame} ${this.text}`:coloredFrame;if(isTTY(this.stream))write(`${CURSOR_TO_START}${output}${CLEAR_LINE_END}`,this.stream)}stopWithSymbol(symbol,text){return this.stop(),write(`${symbol} ${text}
2
+ `,this.stream),this}[Symbol.dispose](){this.stop()}static start(textOrOptions){let spinner=new Spinner(textOrOptions);return spinner.start(),()=>spinner.stop()}}function createSpinner(options){let spinner=new Spinner({...options,text:""}),callable=(text)=>{if(!spinner.spinning)spinner.start(text);else spinner.currentText=text};return callable.stop=()=>spinner.stop(),callable.succeed=(text)=>spinner.succeed(text),callable.fail=(text)=>spinner.fail(text),callable.warn=(text)=>spinner.warn(text),callable.info=(text)=>spinner.info(text),callable[Symbol.dispose]=()=>spinner.stop(),callable}var STATUS_ICONS={pending:chalk2.gray("○"),running:"",completed:chalk2.green("✔"),failed:chalk2.red("✖"),skipped:chalk2.yellow("⊘")};class MultiProgress{tasks=new Map;taskOrder=[];stream;isActive=!1;timer=null;frameIndex=0;renderedLines=0;constructor(stream=process.stdout){this.stream=stream}add(title,options={}){let id=`task-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,task={id,title,type:options.type??"spinner",status:"pending",total:options.total,current:0,spinnerStyle:options.spinnerStyle??"dots",indent:options.indent??0};if(this.tasks.set(id,task),options.insertAfter){let afterIndex=this.taskOrder.indexOf(options.insertAfter);if(afterIndex>=0)this.taskOrder.splice(afterIndex+1,0,id);else this.taskOrder.push(id)}else this.taskOrder.push(id);if(this.isActive)this.render();return new TaskHandle(this,id)}start(){if(this.isActive)return this;if(this.isActive=!0,isTTY(this.stream))write(CURSOR_HIDE,this.stream);return this.render(),this.timer=setInterval(()=>{this.frameIndex=(this.frameIndex+1)%10,this.render()},80),this}[Symbol.dispose](){this.stop()}stop(clear=!1){if(!this.isActive)return this;if(this.isActive=!1,this.timer)clearInterval(this.timer),this.timer=null;if(clear&&isTTY(this.stream)){if(this.renderedLines>0){write(cursorUp(this.renderedLines),this.stream);for(let i=0;i<this.renderedLines;i++)write(`${CLEAR_LINE}
3
+ `,this.stream);write(cursorUp(this.renderedLines),this.stream)}}else this.render(),write(`
4
+ `,this.stream);if(isTTY(this.stream))write(CURSOR_SHOW,this.stream);return this}_updateTask(id,updates){let task=this.tasks.get(id);if(task){if(Object.assign(task,updates),this.isActive&&updates.status)this.render()}}_getTask(id){return this.tasks.get(id)}render(){if(!isTTY(this.stream))return;if(this.renderedLines>0)write(cursorUp(this.renderedLines),this.stream);let lines=[];for(let id of this.taskOrder){let task=this.tasks.get(id);if(!task)continue;let icon;if(task.status==="running")if(task.type==="group")icon=STATUS_ICONS.pending;else{let frames=SPINNER_FRAMES[task.spinnerStyle??"dots"];icon=chalk2.cyan(frames[this.frameIndex%frames.length])}else icon=STATUS_ICONS[task.status];let line=`${" ".repeat(task.indent??0)}${icon} ${task.title}`;if(task.type==="bar"&&task.total&&task.total>0){let percent=task.current/task.total,barWidth=20,filled=Math.round(20*percent),empty=20-filled,bar=chalk2.cyan("█".repeat(filled))+chalk2.gray("░".repeat(empty));line+=` ${bar} ${Math.round(percent*100)}%`}if(task.status==="completed"&&task.completionTime!==void 0)line+=chalk2.dim(` ${task.completionTime}ms`);lines.push(line)}for(let line of lines)write(`${CLEAR_LINE}${line}
5
+ `,this.stream);this.renderedLines=lines.length}}class TaskHandle{multi;_id;constructor(multi,_id){this.multi=multi;this._id=_id}get id(){return this._id}start(){return this.multi._updateTask(this._id,{status:"running"}),this}update(current){return this.multi._updateTask(this._id,{current}),this}complete(titleOrTime){let updates={status:"completed"};if(typeof titleOrTime==="number")updates.completionTime=titleOrTime;else if(titleOrTime)updates.title=titleOrTime;return this.multi._updateTask(this._id,updates),this}fail(title){let updates={status:"failed"};if(title)updates.title=title;return this.multi._updateTask(this._id,updates),this}skip(title){let updates={status:"skipped"};if(title)updates.title=title;return this.multi._updateTask(this._id,updates),this}setTitle(title){return this.multi._updateTask(this._id,{title}),this}setType(type){return this.multi._updateTask(this._id,{type}),this}get status(){return this.multi._getTask(this._id)?.status??"pending"}}import{AsyncLocalStorage}from"node:async_hooks";var stepContext=new AsyncLocalStorage;function step(){return stepContext.getStore()??NO_OP_CONTEXT}function runWithStepContext(ctx,fn){return stepContext.run(ctx,fn)}function createStepContext(label,handle,onSubStep){let currentSubLabel,currentSubHandle=null,subStepStartTime=0,declaredHandles=new Map;return{get label(){return label},get handle(){return handle},progress(current,total){if(currentSubHandle)currentSubHandle.setTitle(`${currentSubLabel} (${current}/${total})`);else handle.setTitle(`${label} (${current}/${total})`)},sub(subLabel){if(this._completeSubStep(),currentSubLabel=subLabel,subStepStartTime=Date.now(),onSubStep)currentSubHandle=onSubStep(subLabel),currentSubHandle.start()},_addSubHandle(subLabel,subHandle){declaredHandles.set(subLabel,subHandle)},_getSubHandle(subLabel){return declaredHandles.get(subLabel)},_setCurrentSubHandle(subLabel,subHandle){currentSubLabel=subLabel,currentSubHandle=subHandle,subStepStartTime=Date.now()},_completeSubStep(){if(currentSubHandle&&currentSubLabel){let elapsed=Date.now()-subStepStartTime;currentSubHandle.complete(elapsed),currentSubHandle=null,currentSubLabel=void 0}}}}var NO_OP_CONTEXT={progress:()=>{},sub:()=>{},get label(){return""}};function parseStepsDef(def,indent=0){let nodes=[];for(let[key,value]of Object.entries(def))if(typeof value==="function")nodes.push({key,label:generateLabel(key),work:value,indent});else if(Array.isArray(value)&&value.length===2){let[label,work]=value;nodes.push({key,label,work,indent})}else if(typeof value==="object"&&value!==null){let children=parseStepsDef(value,indent+1);nodes.push({key,label:generateLabel(key),children,indent})}return nodes}function flattenStepNodes(nodes){let result=[];for(let node of nodes)if(result.push(node),node.children)result.push(...flattenStepNodes(node.children));return result}function getLeafNodes(nodes){let result=[];for(let node of nodes){if(node.work)result.push(node);if(node.children)result.push(...getLeafNodes(node.children))}return result}function generateLabel(fnName){return fnName.replace(/([A-Z])/g," $1").replace(/(\d+)/g," $1").toLowerCase().trim().replace(/\s+/g," ").replace(/^./,(s)=>s.toUpperCase())}function stepsDeclarative(def){let rootNodes=parseStepsDef(def),allNodes=flattenStepNodes(rootNodes),multi=null,handles=new Map,groupLeaves=new Map,leafToGroups=new Map;for(let node of allNodes)if(node.children){let leaves=getLeafNodes([node]);groupLeaves.set(node,leaves);for(let leaf of leaves){let groups=leafToGroups.get(leaf)??[];groups.push(node),leafToGroups.set(leaf,groups)}}return{get _steps(){return rootNodes},async run(options){multi=new MultiProgress,registerAllSteps(allNodes,multi,handles);let groupStartTimes=new Map,completedLeaves=new Set;multi.start(),await new Promise((resolve)=>setImmediate(resolve));let results={};try{for(let node of allNodes)if(node.work){let groups=leafToGroups.get(node)??[];for(let group of groups)if(!groupStartTimes.has(group))groupStartTimes.set(group,Date.now()),handles.get(group)?.start();let result=await executeStep(node,handles,multi);setNestedResult(results,node.key,result),completedLeaves.add(node);for(let group of groups)if((groupLeaves.get(group)??[]).every((l)=>completedLeaves.has(l))){let elapsed=Date.now()-groupStartTimes.get(group);handles.get(group)?.complete(elapsed)}}}finally{multi.stop(options?.clear??!1)}return results},async pipe(options){multi=new MultiProgress,registerAllSteps(allNodes,multi,handles);let groupStartTimes=new Map,completedLeaves=new Set;multi.start(),await new Promise((resolve)=>setImmediate(resolve));let previousResult=void 0;try{for(let node of allNodes)if(node.work){let groups=leafToGroups.get(node)??[];for(let group of groups)if(!groupStartTimes.has(group))groupStartTimes.set(group,Date.now()),handles.get(group)?.start();previousResult=await executeStep(node,handles,multi,previousResult),completedLeaves.add(node);for(let group of groups)if((groupLeaves.get(group)??[]).every((l)=>completedLeaves.has(l))){let elapsed=Date.now()-groupStartTimes.get(group);handles.get(group)?.complete(elapsed)}}}finally{multi.stop(options?.clear??!1)}return previousResult},done(options){if(multi)multi.stop(options?.clear??!1),multi=null}}}function registerAllSteps(nodes,multi,handles){for(let node of nodes){let isGroup=node.children&&!node.work,handle=multi.add(node.label,{type:isGroup?"group":"spinner",indent:node.indent});handles.set(node,handle)}}async function executeStep(node,handles,multi,input){let handle=handles.get(node),startTime=Date.now();await new Promise((resolve)=>setImmediate(resolve));let ctx=createStepContext(node.label,handle,(subLabel)=>{return multi.add(subLabel,{type:"spinner",indent:node.indent+1,insertAfter:handle.id})});handle.start();try{let result=await runWithStepContext(ctx,()=>{if(input!==void 0)return node.work(input);return node.work()});if(isGenerator(result))return await runGenerator(result,ctx,node,multi);if(isAsyncGenerator(result))return await runAsyncGenerator(result,ctx,node,multi);ctx._completeSubStep();let elapsed=Date.now()-startTime;return handle.complete(elapsed),result}catch(error){throw handle.fail(),error}}async function runGenerator(gen,ctx,node,multi){let startTime=Date.now(),result=gen.next(),hasSubSteps=!1,lastInsertedId=ctx.handle.id;while(!result.done){let value=result.value;if(isDeclareSteps(value)){if(!hasSubSteps)hasSubSteps=!0,ctx.handle.setType("group");for(let label of value.declare){let subHandle=multi.add(label,{type:"spinner",indent:node.indent+1,insertAfter:lastInsertedId});lastInsertedId=subHandle.id,ctx._addSubHandle(label,subHandle)}}else if(typeof value==="string"){if(ctx._completeSubStep(),!hasSubSteps)hasSubSteps=!0,ctx.handle.setType("group");let existingHandle=ctx._getSubHandle?.(value);if(existingHandle)ctx._setCurrentSubHandle(value,existingHandle),existingHandle.start();else{let subHandle=multi.add(value,{type:"spinner",indent:node.indent+1,insertAfter:lastInsertedId});lastInsertedId=subHandle.id,ctx._addSubHandle(value,subHandle),subHandle.start()}}else if(isProgressUpdate(value))ctx.progress(value.current??0,value.total??0);await new Promise((resolve)=>setTimeout(resolve,0)),result=gen.next()}ctx._completeSubStep();let elapsed=Date.now()-startTime;return ctx.handle.complete(elapsed),result.value}async function runAsyncGenerator(gen,ctx,node,multi){let startTime=Date.now(),result=await gen.next(),hasSubSteps=!1,lastInsertedId=ctx.handle.id;while(!result.done){let value=result.value;if(isDeclareSteps(value)){if(!hasSubSteps)hasSubSteps=!0,ctx.handle.setType("group");for(let label of value.declare){let subHandle=multi.add(label,{type:"spinner",indent:node.indent+1,insertAfter:lastInsertedId});lastInsertedId=subHandle.id,ctx._addSubHandle(label,subHandle)}}else if(typeof value==="string"){if(ctx._completeSubStep(),!hasSubSteps)hasSubSteps=!0,ctx.handle.setType("group");let existingHandle=ctx._getSubHandle(value);if(existingHandle)ctx._setCurrentSubHandle(value,existingHandle),existingHandle.start();else{let subHandle=multi.add(value,{type:"spinner",indent:node.indent+1,insertAfter:lastInsertedId});lastInsertedId=subHandle.id,ctx._addSubHandle(value,subHandle),subHandle.start()}}else if(isProgressUpdate(value))ctx.progress(value.current??0,value.total??0);await new Promise((resolve)=>setTimeout(resolve,0)),result=await gen.next()}ctx._completeSubStep();let elapsed=Date.now()-startTime;return ctx.handle.complete(elapsed),result.value}function setNestedResult(results,key,value){results[key]=value}function isGenerator(value){return value!==null&&typeof value==="object"&&typeof value.next==="function"&&typeof value[Symbol.iterator]==="function"}function isAsyncGenerator(value){return value!==null&&typeof value==="object"&&typeof value.next==="function"&&typeof value[Symbol.asyncIterator]==="function"}function isProgressUpdate(value){return value!==null&&typeof value==="object"&&!Array.isArray(value)&&(("current"in value)||("total"in value))}function isDeclareSteps(value){return value!==null&&typeof value==="object"&&"declare"in value&&Array.isArray(value.declare)}function steps(def){if(def!==void 0)return stepsDeclarative(def);return createFluentBuilder()}function createNoopStepController(){let controller=(_label)=>{};return controller.progress=(_current,_total)=>{},controller.done=()=>{},controller}function createFluentBuilder(){let stepList=[],builder={run(title,work){return stepList.push({title,work}),builder},async execute(options){let multi=new MultiProgress,handles=new Map,results={};for(let step2 of stepList)handles.set(step2.title,multi.add(step2.title,{type:"spinner"}));multi.start();try{for(let step2 of stepList){let handle=handles.get(step2.title);await new Promise((resolve)=>setImmediate(resolve));let result=step2.work(createNoopStepController());if(isAsyncGenerator2(result))results[step2.title]=await runAsyncGenerator2(result,handle,step2.title,multi);else if(isSyncGenerator(result))results[step2.title]=await runSyncGenerator(result,handle,step2.title,multi);else if(isPromiseLike(result))handle.start(),results[step2.title]=await result,handle.complete();else handle.start(),results[step2.title]=result,handle.complete()}}finally{multi.stop(options?.clear??!1)}return results}};return builder}function processYield(value,state,multi){if(isDeclareSteps2(value)){for(let label of value.declare){let handle=multi.add(label,{type:"spinner",indent:1,insertAfter:state.lastInsertId});state.lastInsertId=handle.id,state.declaredSteps.set(label,handle)}return}if(typeof value==="string"){if(state.currentHandle&&state.currentLabel){let elapsed=Date.now()-state.subStepStartTime;state.currentHandle.complete(elapsed)}state.currentLabel=value,state.subStepStartTime=Date.now();let declared=state.declaredSteps.get(value);if(declared)state.currentHandle=declared,state.currentHandle.start();else state.currentHandle=multi.add(value,{type:"spinner",indent:1,insertAfter:state.lastInsertId}),state.lastInsertId=state.currentHandle.id,state.currentHandle.start()}else if(value&&typeof value==="object"){let{current,total}=value;if(state.currentHandle&&total&&total>0)state.currentHandle.setTitle(`${state.currentLabel} (${current??0}/${total})`)}}function isDeclareSteps2(value){return value!==null&&typeof value==="object"&&"declare"in value&&Array.isArray(value.declare)}async function runAsyncGenerator2(gen,parentHandle,parentTitle,multi){let state={currentLabel:void 0,currentHandle:null,lastInsertId:parentHandle.id,subStepStartTime:Date.now(),startTime:Date.now(),declaredSteps:new Map},result=await gen.next();while(!result.done)processYield(result.value,state,multi),await new Promise((resolve)=>setTimeout(resolve,0)),result=await gen.next();if(state.currentHandle&&state.currentLabel){let elapsed=Date.now()-state.subStepStartTime;state.currentHandle.complete(elapsed)}let totalElapsed=Date.now()-state.startTime;return parentHandle.complete(totalElapsed),result.value}async function runSyncGenerator(gen,parentHandle,parentTitle,multi){let state={currentLabel:void 0,currentHandle:null,lastInsertId:parentHandle.id,subStepStartTime:Date.now(),startTime:Date.now(),declaredSteps:new Map},result=gen.next();while(!result.done)processYield(result.value,state,multi),await new Promise((resolve)=>setTimeout(resolve,0)),result=gen.next();if(state.currentHandle&&state.currentLabel){let elapsed=Date.now()-state.subStepStartTime;state.currentHandle.complete(elapsed)}let totalElapsed=Date.now()-state.startTime;return parentHandle.complete(totalElapsed),result.value}function isAsyncGenerator2(value){return value!==null&&typeof value==="object"&&typeof value.next==="function"&&typeof value[Symbol.asyncIterator]==="function"}function isSyncGenerator(value){return value!==null&&typeof value==="object"&&typeof value.next==="function"&&typeof value[Symbol.iterator]==="function"}function isPromiseLike(value){return value!==null&&typeof value==="object"&&typeof value.then==="function"}var PHASE_LABELS={reading:"Reading events",applying:"Applying events",rules:"Evaluating rules",scanning:"Scanning files",reconciling:"Reconciling changes",board:"Building view"};function task(title){return{async wrap(work){let spinner=createSpinner();spinner(title);try{if(typeof work==="function"){let result=work();if(isGenerator2(result))return await runGenerator2(result,spinner,title);if(isPromiseLike2(result)){let value=await result;return spinner.succeed(title),value}return spinner.succeed(title),result}if(isPromiseLike2(work)){let value=await work;return spinner.succeed(title),value}return spinner.succeed(title),work}catch(error){throw spinner.fail(title),error}}}}async function runGenerator2(gen,spinner,baseTitle){let result=gen.next();while(!result.done){let info=result.value,phase=info.phase??"",phaseLabel=PHASE_LABELS[phase]??(phase||baseTitle);if(info.total&&info.total>0)spinner(`${phaseLabel} (${info.current}/${info.total})`);else spinner(phaseLabel);await new Promise((resolve)=>setImmediate(resolve)),result=gen.next()}return spinner.succeed(baseTitle),result.value}function isGenerator2(value){return value!==null&&typeof value==="object"&&typeof value.next==="function"&&typeof value.throw==="function"}function isPromiseLike2(value){return value!==null&&typeof value==="object"&&typeof value.then==="function"}var PHASE_LABELS2={discover:"Discovering files",parse:"Parsing markdown",apply:"Applying changes",resolve:"Resolving links",materialize:"Evaluating rules",board:"Building view",reading:"Reading events",applying:"Applying events",rules:"Evaluating rules",scanning:"Scanning files",reconciling:"Reconciling changes"};function tasks(){let taskList=[],builder={add(title,work){return taskList.push({title,work}),builder},async run(options){let multi=new MultiProgress,handles=new Map,results={};for(let task2 of taskList)handles.set(task2.title,multi.add(task2.title,{type:"spinner"}));multi.start();try{for(let task2 of taskList){let handle=handles.get(task2.title);await new Promise((resolve)=>setImmediate(resolve));let result=task2.work();if(isGenerator3(result))results[task2.title]=await runGenerator3(result,handle,task2.title,multi);else if(isPromiseLike3(result))handle.start(),results[task2.title]=await result,handle.complete();else handle.start(),results[task2.title]=result,handle.complete()}}finally{multi.stop(options?.clear??!1)}return results}};return builder}async function runGenerator3(gen,parentHandle,baseTitle,multi){let result=gen.next(),currentPhase,currentPhaseHandle=null,lastInsertId=parentHandle.id,phaseStartTime=Date.now(),taskStartTime=Date.now();while(!result.done){let info=result.value,phase=info.phase??"";if(phase&&phase!==currentPhase){if(currentPhaseHandle&&currentPhase){let elapsed=Date.now()-phaseStartTime,prevLabel=PHASE_LABELS2[currentPhase]??currentPhase;currentPhaseHandle.complete(`${prevLabel} (${elapsed}ms)`)}currentPhase=phase,phaseStartTime=Date.now();let phaseLabel=PHASE_LABELS2[phase]??phase;currentPhaseHandle=multi.add(phaseLabel,{type:"spinner",indent:1,insertAfter:lastInsertId}),lastInsertId=currentPhaseHandle.id,currentPhaseHandle.start()}if(currentPhaseHandle&&info.total&&info.total>0){let phaseLabel=PHASE_LABELS2[phase]??phase;currentPhaseHandle.setTitle(`${phaseLabel} (${info.current}/${info.total})`)}await new Promise((resolve)=>setTimeout(resolve,0)),result=gen.next()}if(currentPhaseHandle&&currentPhase){let elapsed=Date.now()-phaseStartTime,finalLabel=PHASE_LABELS2[currentPhase]??currentPhase;currentPhaseHandle.complete(`${finalLabel} (${elapsed}ms)`)}let totalElapsed=Date.now()-taskStartTime;return parentHandle.complete(`${baseTitle} (${totalElapsed}ms)`),result.value}function isGenerator3(value){return value!==null&&typeof value==="object"&&typeof value.next==="function"&&typeof value.throw==="function"}function isPromiseLike3(value){return value!==null&&typeof value==="object"&&typeof value.then==="function"}import chalk3 from"chalk";function calculateETA(buffer,current,total){if(buffer.length<2)return null;let first=buffer[0],last=buffer[buffer.length-1],elapsed=(last.time-first.time)/1000,progress=last.value-first.value;if(elapsed<=0||progress<=0)return null;let rate=progress/elapsed;return(total-current)/rate}function formatETA(eta){if(eta===null||!isFinite(eta))return"--:--";if(eta>86400)return">1d";let hours=Math.floor(eta/3600),minutes=Math.floor(eta%3600/60),seconds=Math.floor(eta%60);if(hours>0)return`${hours}:${minutes.toString().padStart(2,"0")}:${seconds.toString().padStart(2,"0")}`;return`${minutes}:${seconds.toString().padStart(2,"0")}`}var DEFAULT_ETA_BUFFER_SIZE=10;var DEFAULT_FORMAT=":bar :percent | :current/:total | ETA: :eta";class ProgressBar{total;format;width;complete;incomplete;stream;hideCursor;phases;current=0;phase=null;startTime=null;isActive=!1;etaBuffer=[];constructor(options={}){this.total=options.total??100,this.format=options.format??DEFAULT_FORMAT,this.width=options.width??40,this.complete=options.complete??"█",this.incomplete=options.incomplete??"░",this.stream=options.stream??process.stdout,this.hideCursor=options.hideCursor??!0,this.phases=options.phases??{}}start(initialValue=0,initialTotal){if(initialTotal!==void 0)this.total=initialTotal;if(this.current=initialValue,this.startTime=Date.now(),this.isActive=!0,this.etaBuffer=[{time:this.startTime,value:initialValue}],this.hideCursor&&isTTY(this.stream))write(CURSOR_HIDE,this.stream);return this.render(),this}update(value,tokens){this.current=Math.min(value,this.total);let now=Date.now();if(this.etaBuffer.push({time:now,value:this.current}),this.etaBuffer.length>DEFAULT_ETA_BUFFER_SIZE)this.etaBuffer.shift();if(this.isActive)this.render(tokens);return this}increment(amount=1,tokens){return this.update(this.current+amount,tokens)}setPhase(phaseName,options){if(this.phase=phaseName,options?.total!==void 0)this.total=options.total;if(options?.current!==void 0)this.current=options.current,this.etaBuffer=[{time:Date.now(),value:this.current}];if(this.isActive)this.render();return this}stop(clear=!1){if(!this.isActive)return this;if(this.isActive=!1,clear&&isTTY(this.stream))write(`${CURSOR_TO_START}${CLEAR_LINE_END}`,this.stream);else write(`
6
+ `,this.stream);if(this.hideCursor&&isTTY(this.stream))write(CURSOR_SHOW,this.stream);return this}getETASeconds(){return calculateETA(this.etaBuffer,this.current,this.total)}render(tokens){let percent=this.total>0?this.current/this.total:0,eta=this.getETASeconds(),completeLength=Math.round(this.width*percent),incompleteLength=this.width-completeLength,bar=this.complete.repeat(completeLength)+this.incomplete.repeat(incompleteLength),phaseDisplay=this.phase?this.phases[this.phase]??this.phase:"",elapsed=this.startTime?(Date.now()-this.startTime)/1000:0,rate=elapsed>0?this.current/elapsed:0,output=this.format.replace(":bar",chalk3.cyan(bar)).replace(":percent",`${Math.round(percent*100)}%`.padStart(4)).replace(":current",String(this.current)).replace(":total",String(this.total)).replace(":eta",formatETA(eta)).replace(":elapsed",formatETA(elapsed)).replace(":rate",rate.toFixed(1)).replace(":phase",chalk3.dim(phaseDisplay));if(tokens)for(let[key,value]of Object.entries(tokens))output=output.replace(`:${key}`,String(value));let termWidth=getTerminalWidth(this.stream);if(output.length>termWidth)output=output.slice(0,termWidth-1);if(isTTY(this.stream))write(`${CURSOR_TO_START}${output}${CLEAR_LINE_END}`,this.stream)}get ratio(){return this.total>0?this.current/this.total:0}get percentage(){return Math.round(this.ratio*100)}[Symbol.dispose](){this.stop()}}export{tasks,task,steps,step,createSpinner,Spinner,ProgressBar,MultiProgress};
7
+
8
+ //# debugId=E192A1E0E9E2C66264756E2164756E21
@@ -0,0 +1,20 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../packages/ag-react/src/ui/cli/multi-progress.ts", "../../packages/ag-react/src/ui/cli/ansi.ts", "../../packages/ag-react/src/ui/cli/spinner.ts", "../../packages/ag-react/src/ui/progress/als-context.ts", "../../packages/ag-react/src/ui/progress/step-node.ts", "../../packages/ag-react/src/ui/progress/declarative.ts", "../../packages/ag-react/src/ui/progress/steps.ts", "../../packages/ag-react/src/ui/progress/task.ts", "../../packages/ag-react/src/ui/progress/tasks.ts", "../../packages/ag-react/src/ui/cli/progress-bar.ts", "../../packages/ag-react/src/ui/utils/eta.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * MultiProgress - Container for managing multiple concurrent progress indicators\n */\n\nimport chalk from \"chalk\"\nimport type { SpinnerStyle, TaskStatus } from \"../types.js\"\nimport { CURSOR_HIDE, CURSOR_SHOW, CLEAR_LINE, cursorUp, write, isTTY } from \"./ansi\"\nimport { Spinner, SPINNER_FRAMES } from \"./spinner\"\nimport { ProgressBar } from \"./progress-bar\"\n\n/** Status icons */\nconst STATUS_ICONS: Record<TaskStatus, string> = {\n pending: chalk.gray(\"○\"),\n running: \"\", // Will be replaced with spinner frame\n completed: chalk.green(\"✔\"),\n failed: chalk.red(\"✖\"),\n skipped: chalk.yellow(\"⊘\"),\n}\n\n/** Task configuration */\ninterface TaskConfig {\n title: string\n type: \"spinner\" | \"bar\" | \"group\"\n status: TaskStatus\n total?: number\n current?: number\n spinnerStyle?: SpinnerStyle\n indent?: number\n}\n\n/** Internal task state */\ninterface TaskState extends TaskConfig {\n id: string\n /** Completion time in ms (shown dimmed after title on completion) */\n completionTime?: number\n}\n\n/**\n * MultiProgress - Manage multiple concurrent progress indicators\n *\n * @example\n * ```ts\n * const multi = new MultiProgress();\n *\n * const download = multi.add(\"Downloading files\", { type: \"bar\", total: 100 });\n * const process = multi.add(\"Processing\", { type: \"spinner\" });\n *\n * download.start();\n * download.update(50);\n * download.complete();\n *\n * process.start();\n * process.complete();\n *\n * multi.stop();\n * ```\n */\nexport class MultiProgress {\n private tasks: Map<string, TaskState> = new Map()\n private taskOrder: string[] = []\n private stream: NodeJS.WriteStream\n private isActive = false\n private timer: ReturnType<typeof setInterval> | null = null\n private frameIndex = 0\n private renderedLines = 0\n\n constructor(stream: NodeJS.WriteStream = process.stdout) {\n this.stream = stream\n }\n\n /**\n * Add a new task\n * @param insertAfter - ID of task to insert after (for hierarchical display)\n */\n add(\n title: string,\n options: {\n type?: \"spinner\" | \"bar\" | \"group\"\n total?: number\n spinnerStyle?: SpinnerStyle\n indent?: number\n insertAfter?: string\n } = {},\n ): TaskHandle {\n const id = `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`\n\n const task: TaskState = {\n id,\n title,\n type: options.type ?? \"spinner\",\n status: \"pending\",\n total: options.total,\n current: 0,\n spinnerStyle: options.spinnerStyle ?? \"dots\",\n indent: options.indent ?? 0,\n }\n\n this.tasks.set(id, task)\n\n // Insert after specified task, or append to end\n if (options.insertAfter) {\n const afterIndex = this.taskOrder.indexOf(options.insertAfter)\n if (afterIndex >= 0) {\n this.taskOrder.splice(afterIndex + 1, 0, id)\n } else {\n this.taskOrder.push(id)\n }\n } else {\n this.taskOrder.push(id)\n }\n\n if (this.isActive) {\n this.render()\n }\n\n return new TaskHandle(this, id)\n }\n\n /**\n * Start the multi-progress display\n */\n start(): this {\n if (this.isActive) {\n return this\n }\n\n this.isActive = true\n\n if (isTTY(this.stream)) {\n write(CURSOR_HIDE, this.stream)\n }\n\n this.render()\n\n // Start animation timer\n this.timer = setInterval(() => {\n this.frameIndex = (this.frameIndex + 1) % 10\n this.render()\n }, 80)\n\n return this\n }\n\n /**\n * Dispose the multi-progress display (calls stop)\n */\n [Symbol.dispose](): void {\n this.stop()\n }\n\n /**\n * Stop the multi-progress display\n * @param clear - If true, clear all task lines from terminal\n */\n stop(clear = false): this {\n if (!this.isActive) {\n return this\n }\n\n this.isActive = false\n\n if (this.timer) {\n clearInterval(this.timer)\n this.timer = null\n }\n\n if (clear && isTTY(this.stream)) {\n // Clear all rendered lines\n if (this.renderedLines > 0) {\n write(cursorUp(this.renderedLines), this.stream)\n for (let i = 0; i < this.renderedLines; i++) {\n write(`${CLEAR_LINE}\\n`, this.stream)\n }\n write(cursorUp(this.renderedLines), this.stream)\n }\n } else {\n // Final render\n this.render()\n write(\"\\n\", this.stream)\n }\n\n if (isTTY(this.stream)) {\n write(CURSOR_SHOW, this.stream)\n }\n\n return this\n }\n\n /** @internal Update task state */\n _updateTask(id: string, updates: Partial<TaskState>): void {\n const task = this.tasks.get(id)\n if (task) {\n Object.assign(task, updates)\n // Only render immediately for status changes (complete/fail/etc.)\n // Progress updates (current/total) are debounced by the 80ms animation timer\n if (this.isActive && updates.status) {\n this.render()\n }\n }\n }\n\n /** @internal Get task state */\n _getTask(id: string): TaskState | undefined {\n return this.tasks.get(id)\n }\n\n private render(): void {\n if (!isTTY(this.stream)) {\n return\n }\n\n // Move cursor up to clear previous render\n if (this.renderedLines > 0) {\n write(cursorUp(this.renderedLines), this.stream)\n }\n\n const lines: string[] = []\n\n for (const id of this.taskOrder) {\n const task = this.tasks.get(id)\n if (!task) continue\n\n let icon: string\n if (task.status === \"running\") {\n if (task.type === \"group\") {\n // Groups don't animate - keep pending icon while running\n icon = STATUS_ICONS.pending\n } else {\n const frames = SPINNER_FRAMES[task.spinnerStyle ?? \"dots\"]\n icon = chalk.cyan(frames[this.frameIndex % frames.length])\n }\n } else {\n icon = STATUS_ICONS[task.status]\n }\n\n const indent = \" \".repeat(task.indent ?? 0)\n let line = `${indent}${icon} ${task.title}`\n\n // Add progress bar for bar type\n if (task.type === \"bar\" && task.total && task.total > 0) {\n const percent = task.current! / task.total\n const barWidth = 20\n const filled = Math.round(barWidth * percent)\n const empty = barWidth - filled\n const bar = chalk.cyan(\"█\".repeat(filled)) + chalk.gray(\"░\".repeat(empty))\n line += ` ${bar} ${Math.round(percent * 100)}%`\n }\n\n // Add completion time in dimmed text\n if (task.status === \"completed\" && task.completionTime !== undefined) {\n line += chalk.dim(` ${task.completionTime}ms`)\n }\n\n lines.push(line)\n }\n\n // Clear and write each line\n for (const line of lines) {\n write(`${CLEAR_LINE}${line}\\n`, this.stream)\n }\n\n this.renderedLines = lines.length\n }\n}\n\n/**\n * Handle for controlling an individual task\n */\nclass TaskHandle {\n constructor(\n private multi: MultiProgress,\n private _id: string,\n ) {}\n\n /** Get task ID (for insertAfter) */\n get id(): string {\n return this._id\n }\n\n /** Start the task (set status to running) */\n start(): this {\n this.multi._updateTask(this._id, { status: \"running\" })\n return this\n }\n\n /** Update progress (for bar type) */\n update(current: number): this {\n this.multi._updateTask(this._id, { current })\n return this\n }\n\n /** Mark task as completed */\n complete(titleOrTime?: string | number): this {\n const updates: Partial<TaskState> = { status: \"completed\" }\n if (typeof titleOrTime === \"number\") {\n // Numeric = completion time in ms (preserves current title)\n updates.completionTime = titleOrTime\n } else if (titleOrTime) {\n // String = new title (legacy behavior)\n updates.title = titleOrTime\n }\n this.multi._updateTask(this._id, updates)\n return this\n }\n\n /** Mark task as failed */\n fail(title?: string): this {\n const updates: Partial<TaskState> = { status: \"failed\" }\n if (title) updates.title = title\n this.multi._updateTask(this._id, updates)\n return this\n }\n\n /** Mark task as skipped */\n skip(title?: string): this {\n const updates: Partial<TaskState> = { status: \"skipped\" }\n if (title) updates.title = title\n this.multi._updateTask(this._id, updates)\n return this\n }\n\n /** Update task title */\n setTitle(title: string): this {\n this.multi._updateTask(this._id, { title })\n return this\n }\n\n /** Change task type (e.g., from spinner to group when sub-steps are added) */\n setType(type: \"spinner\" | \"bar\" | \"group\"): this {\n this.multi._updateTask(this._id, { type })\n return this\n }\n\n /** Get current status */\n get status(): TaskStatus {\n return this.multi._getTask(this._id)?.status ?? \"pending\"\n }\n}\n\nexport type { TaskHandle }\n",
6
+ "/**\n * ANSI escape code utilities for terminal control\n */\n\n/** Hide the cursor */\nexport const CURSOR_HIDE = \"\\x1b[?25l\"\n\n/** Show the cursor */\nexport const CURSOR_SHOW = \"\\x1b[?25h\"\n\n/** Move cursor to beginning of line */\nexport const CURSOR_TO_START = \"\\r\"\n\n/** Clear from cursor to end of line */\nexport const CLEAR_LINE_END = \"\\x1b[K\"\n\n/** Clear entire line */\nexport const CLEAR_LINE = \"\\x1b[2K\"\n\n/** Clear screen and move to top-left */\nexport const CLEAR_SCREEN = \"\\x1b[2J\\x1b[H\"\n\n/** Move cursor up N lines */\nexport const cursorUp = (n: number = 1): string => `\\x1b[${n}A`\n\n/** Move cursor down N lines */\nexport const cursorDown = (n: number = 1): string => `\\x1b[${n}B`\n\n/** Save cursor position */\nexport const CURSOR_SAVE = \"\\x1b[s\"\n\n/** Restore cursor position */\nexport const CURSOR_RESTORE = \"\\x1b[u\"\n\n/**\n * Write to stream with proper handling\n */\nexport function write(text: string, stream: NodeJS.WriteStream = process.stdout): void {\n stream.write(text)\n}\n\n/**\n * Clear the current line and write new text\n */\nexport function writeLine(text: string, stream: NodeJS.WriteStream = process.stdout): void {\n stream.write(`${CURSOR_TO_START}${text}${CLEAR_LINE_END}`)\n}\n\n/**\n * Wrap a function to handle cursor visibility\n * Hides cursor on start, shows on completion/error\n */\nexport function withCursor<T>(fn: () => T | Promise<T>, stream: NodeJS.WriteStream = process.stdout): Promise<T> {\n stream.write(CURSOR_HIDE)\n\n const restore = () => stream.write(CURSOR_SHOW)\n\n try {\n const result = fn()\n if (result instanceof Promise) {\n return result.finally(restore)\n }\n restore()\n return Promise.resolve(result)\n } catch (error) {\n restore()\n throw error\n }\n}\n\n/**\n * Check if stream is a TTY (supports ANSI codes)\n * Also respects FORCE_TTY environment variable for testing\n */\nexport function isTTY(stream: NodeJS.WriteStream = process.stdout): boolean {\n if (process.env.FORCE_TTY === \"1\") return true\n return stream.isTTY ?? false\n}\n\n/**\n * Get terminal width\n */\nexport function getTerminalWidth(stream: NodeJS.WriteStream = process.stdout): number {\n return stream.columns ?? 80\n}\n",
7
+ "/**\n * CLI Spinner - Animated indeterminate progress indicator\n */\n\nimport chalk from \"chalk\"\nimport type { SpinnerOptions, SpinnerStyle } from \"../types.js\"\nimport { CURSOR_HIDE, CURSOR_SHOW, CURSOR_TO_START, CLEAR_LINE_END, write, isTTY } from \"./ansi\"\n\n/** Spinner animation frames by style */\nexport const SPINNER_FRAMES: Record<SpinnerStyle, string[]> = {\n dots: [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"],\n line: [\"-\", \"\\\\\", \"|\", \"/\"],\n arc: [\"◜\", \"◠\", \"◝\", \"◞\", \"◡\", \"◟\"],\n bounce: [\"⠁\", \"⠂\", \"⠄\", \"⠂\"],\n pulse: [\"█\", \"▓\", \"▒\", \"░\", \"▒\", \"▓\"],\n}\n\n/** Default intervals for each style (ms) */\nexport const SPINNER_INTERVALS: Record<SpinnerStyle, number> = {\n dots: 80,\n line: 120,\n arc: 100,\n bounce: 120,\n pulse: 100,\n}\n\n/**\n * Spinner class for CLI progress indication\n *\n * @example\n * ```ts\n * const spinner = new Spinner(\"Loading...\");\n * spinner.start();\n * await doWork();\n * spinner.succeed(\"Done!\");\n * ```\n */\nexport class Spinner {\n private text: string\n private style: SpinnerStyle\n private color: string\n private stream: NodeJS.WriteStream\n private hideCursor: boolean\n private interval: number\n\n private frameIndex = 0\n private timer: ReturnType<typeof setInterval> | null = null\n private isSpinning = false\n\n constructor(textOrOptions?: string | SpinnerOptions) {\n const options: SpinnerOptions = typeof textOrOptions === \"string\" ? { text: textOrOptions } : (textOrOptions ?? {})\n\n this.text = options.text ?? \"\"\n this.style = options.style ?? \"dots\"\n this.color = options.color ?? \"cyan\"\n this.stream = options.stream ?? process.stdout\n this.hideCursor = options.hideCursor ?? true\n this.interval = options.interval ?? SPINNER_INTERVALS[this.style]\n }\n\n /** Get current spinner text */\n get currentText(): string {\n return this.text\n }\n\n /** Set spinner text (updates immediately if spinning) */\n set currentText(value: string) {\n this.text = value\n if (this.isSpinning) {\n this.render()\n }\n }\n\n /** Check if spinner is currently active */\n get spinning(): boolean {\n return this.isSpinning\n }\n\n /**\n * Start the spinner animation\n */\n start(text?: string): this {\n if (text !== undefined) {\n this.text = text\n }\n\n if (this.isSpinning) {\n return this\n }\n\n this.isSpinning = true\n this.frameIndex = 0\n\n if (this.hideCursor && isTTY(this.stream)) {\n write(CURSOR_HIDE, this.stream)\n }\n\n this.render()\n this.timer = setInterval(() => {\n this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES[this.style].length\n this.render()\n }, this.interval)\n\n return this\n }\n\n /**\n * Stop the spinner\n */\n stop(): this {\n if (!this.isSpinning) {\n return this\n }\n\n this.isSpinning = false\n\n if (this.timer) {\n clearInterval(this.timer)\n this.timer = null\n }\n\n this.clear()\n\n if (this.hideCursor && isTTY(this.stream)) {\n write(CURSOR_SHOW, this.stream)\n }\n\n return this\n }\n\n /**\n * Stop with success message (green checkmark)\n */\n succeed(text?: string): this {\n return this.stopWithSymbol(chalk.green(\"✔\"), text ?? this.text)\n }\n\n /**\n * Stop with failure message (red X)\n */\n fail(text?: string): this {\n return this.stopWithSymbol(chalk.red(\"✖\"), text ?? this.text)\n }\n\n /**\n * Stop with warning message (yellow warning)\n */\n warn(text?: string): this {\n return this.stopWithSymbol(chalk.yellow(\"⚠\"), text ?? this.text)\n }\n\n /**\n * Stop with info message (blue info)\n */\n info(text?: string): this {\n return this.stopWithSymbol(chalk.blue(\"ℹ\"), text ?? this.text)\n }\n\n /**\n * Clear the spinner line\n */\n clear(): this {\n if (isTTY(this.stream)) {\n write(`${CURSOR_TO_START}${CLEAR_LINE_END}`, this.stream)\n }\n return this\n }\n\n private render(): void {\n const frame = SPINNER_FRAMES[this.style][this.frameIndex]\n const colorFn = (chalk as unknown as Record<string, (s: string) => string>)[this.color]\n const coloredFrame = colorFn ? colorFn(frame!) : frame!\n const output = this.text ? `${coloredFrame} ${this.text}` : coloredFrame\n\n if (isTTY(this.stream)) {\n write(`${CURSOR_TO_START}${output}${CLEAR_LINE_END}`, this.stream)\n }\n }\n\n private stopWithSymbol(symbol: string, text: string): this {\n this.stop()\n write(`${symbol} ${text}\\n`, this.stream)\n return this\n }\n\n /**\n * Dispose the spinner (calls stop)\n */\n [Symbol.dispose](): void {\n this.stop()\n }\n\n /**\n * Static helper to quickly start a spinner\n * Returns a stop function\n *\n * @example\n * ```ts\n * const stop = Spinner.start(\"Loading...\");\n * await doWork();\n * stop();\n * ```\n */\n static start(textOrOptions?: string | SpinnerOptions): () => void {\n const spinner = new Spinner(textOrOptions)\n spinner.start()\n return () => spinner.stop()\n }\n}\n\n/**\n * Callable spinner interface - call with text to show/update\n *\n * @example\n * ```ts\n * {\n * using spinner = createSpinner({ style: \"dots\" });\n * spinner(\"Loading...\"); // Shows spinner with text\n * spinner(\"Still loading...\"); // Updates text\n * } // Auto-stops via Symbol.dispose\n * ```\n */\nexport interface CallableSpinner extends Disposable {\n /** Call with text to show/update the spinner */\n (text: string): void\n /** Stop the spinner */\n stop(): void\n /** Stop with success message (green checkmark) */\n succeed(text?: string): void\n /** Stop with failure message (red X) */\n fail(text?: string): void\n /** Stop with warning message (yellow warning) */\n warn(text?: string): void\n /** Stop with info message (blue info) */\n info(text?: string): void\n}\n\n/**\n * Create a callable, disposable spinner\n *\n * The spinner is lazy - it won't show anything until you call it with text.\n * Use with `using` for automatic cleanup:\n *\n * @example\n * ```ts\n * {\n * using spinner = createSpinner({ style: \"dots\" });\n * // Nothing visible yet\n *\n * spinner(\"Loading repo...\"); // Now shows spinner\n * spinner(\"Applying events...\"); // Updates text\n * } // Auto-stops via Symbol.dispose\n * ```\n */\nexport function createSpinner(options?: SpinnerOptions): CallableSpinner {\n const spinner = new Spinner({ ...options, text: \"\" })\n\n const callable = ((text: string) => {\n // Always restart if not spinning (handles initial call and after succeed/fail/etc)\n if (!spinner.spinning) {\n spinner.start(text)\n } else {\n spinner.currentText = text\n }\n }) as CallableSpinner\n\n callable.stop = () => spinner.stop()\n callable.succeed = (text) => spinner.succeed(text)\n callable.fail = (text) => spinner.fail(text)\n callable.warn = (text) => spinner.warn(text)\n callable.info = (text) => spinner.info(text)\n callable[Symbol.dispose] = () => spinner.stop()\n\n return callable\n}\n",
8
+ "/**\n * AsyncLocalStorage context for step progress reporting\n *\n * Provides a `step()` function that work functions can call to report progress.\n * Returns a no-op context when called outside of a steps() execution context,\n * so functions work in tests without the progress UI.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\"\nimport type { TaskHandle } from \"../cli/multi-progress\"\n\n/**\n * Context available to work functions during step execution\n */\nexport interface StepContext {\n /** Update progress on current step */\n progress(current: number, total: number): void\n\n /** Create a sub-step (auto-completes previous sub-step) */\n sub(label: string): void\n\n /** Get current step label (for debugging) */\n readonly label: string\n}\n\n/**\n * Internal context with additional fields for the runner\n */\nexport interface InternalStepContext extends StepContext {\n /** TaskHandle for this step */\n readonly handle: TaskHandle\n\n /** Add a sub-step handle (called by runner) */\n _addSubHandle(label: string, handle: TaskHandle): void\n\n /** Get a pre-declared sub-step handle by label */\n _getSubHandle(label: string): TaskHandle | undefined\n\n /** Set the current sub-step (when starting a pre-declared step) */\n _setCurrentSubHandle(label: string, handle: TaskHandle): void\n\n /** Complete current sub-step (called by runner) */\n _completeSubStep(): void\n}\n\n// AsyncLocalStorage instance\nconst stepContext = new AsyncLocalStorage<InternalStepContext>()\n\n/**\n * Get the current step context\n *\n * Safe to call anywhere - returns a no-op context when called outside\n * of a steps() execution context.\n *\n * @example\n * ```typescript\n * async function processFiles(files: string[]) {\n * for (let i = 0; i < files.length; i++) {\n * step().progress(i + 1, files.length);\n * await process(files[i]);\n * }\n * }\n *\n * // In tests (no steps context)\n * await processFiles([\"a.md\", \"b.md\"]); // step() returns no-op, no errors\n *\n * // In production (with steps context)\n * await steps({ process: processFiles }).run(); // Shows progress\n * ```\n */\nexport function step(): StepContext {\n return stepContext.getStore() ?? NO_OP_CONTEXT\n}\n\n/**\n * Run a function with step context (internal use by runner)\n */\nexport function runWithStepContext<T>(ctx: InternalStepContext, fn: () => T): T {\n return stepContext.run(ctx, fn)\n}\n\n/**\n * Create an internal step context for the runner\n */\nexport function createStepContext(\n label: string,\n handle: TaskHandle,\n onSubStep?: (label: string) => TaskHandle,\n): InternalStepContext {\n let currentSubLabel: string | undefined\n let currentSubHandle: TaskHandle | null = null\n let subStepStartTime = 0\n const declaredHandles = new Map<string, TaskHandle>()\n\n return {\n get label() {\n return label\n },\n\n get handle() {\n return handle\n },\n\n progress(current: number, total: number) {\n if (currentSubHandle) {\n currentSubHandle.setTitle(`${currentSubLabel} (${current}/${total})`)\n } else {\n handle.setTitle(`${label} (${current}/${total})`)\n }\n },\n\n sub(subLabel: string) {\n // Complete previous sub-step if any\n this._completeSubStep()\n\n currentSubLabel = subLabel\n subStepStartTime = Date.now()\n\n if (onSubStep) {\n currentSubHandle = onSubStep(subLabel)\n currentSubHandle.start()\n }\n },\n\n _addSubHandle(subLabel: string, subHandle: TaskHandle) {\n declaredHandles.set(subLabel, subHandle)\n },\n\n _getSubHandle(subLabel: string) {\n return declaredHandles.get(subLabel)\n },\n\n _setCurrentSubHandle(subLabel: string, subHandle: TaskHandle) {\n currentSubLabel = subLabel\n currentSubHandle = subHandle\n subStepStartTime = Date.now()\n },\n\n _completeSubStep() {\n if (currentSubHandle && currentSubLabel) {\n const elapsed = Date.now() - subStepStartTime\n // Use numeric timing - preserves current title (which may have progress info)\n currentSubHandle.complete(elapsed)\n currentSubHandle = null\n currentSubLabel = undefined\n }\n },\n }\n}\n\n/**\n * No-op context for when step() is called outside execution context\n */\nconst NO_OP_CONTEXT: StepContext = {\n progress: () => {},\n sub: () => {},\n get label() {\n return \"\"\n },\n}\n",
9
+ "/**\n * Step node tree structure for declarative steps\n *\n * Parses the user's declarative object structure into an internal tree\n * that can be rendered and executed.\n */\n\n/**\n * A single step in the tree\n */\nexport interface StepNode {\n /** Display label (auto-generated or custom) */\n label: string\n\n /** Object key from the declaration */\n key: string\n\n /** Work function (if leaf node) */\n work?: (...args: unknown[]) => unknown\n\n /** Child steps (if group node) */\n children?: StepNode[]\n\n /** Indentation level for display */\n indent: number\n}\n\n/**\n * What users can declare as a step value\n */\nexport type StepValue =\n | ((...args: unknown[]) => unknown) // Function (auto-named)\n | [string, (...args: unknown[]) => unknown] // [label, function]\n | StepsDef // Nested group\n\n/**\n * The declarative structure users provide\n */\nexport type StepsDef = {\n [key: string]: StepValue\n}\n\n/**\n * Parse a declarative steps definition into a tree of StepNodes\n *\n * @param def - The declarative structure\n * @param indent - Current indentation level (internal)\n * @returns Array of StepNodes\n */\nexport function parseStepsDef(def: StepsDef, indent = 0): StepNode[] {\n const nodes: StepNode[] = []\n\n for (const [key, value] of Object.entries(def)) {\n if (typeof value === \"function\") {\n // Function: auto-generate label from key\n nodes.push({\n key,\n label: generateLabel(key),\n work: value,\n indent,\n })\n } else if (Array.isArray(value) && value.length === 2) {\n // Tuple: [label, function]\n const [label, work] = value as [string, (...args: unknown[]) => unknown]\n nodes.push({\n key,\n label,\n work,\n indent,\n })\n } else if (typeof value === \"object\" && value !== null) {\n // Nested group\n const children = parseStepsDef(value as StepsDef, indent + 1)\n nodes.push({\n key,\n label: generateLabel(key),\n children,\n indent,\n })\n }\n }\n\n return nodes\n}\n\n/**\n * Flatten the tree for sequential execution\n *\n * Returns nodes in depth-first order, with groups followed by their children.\n */\nexport function flattenStepNodes(nodes: StepNode[]): StepNode[] {\n const result: StepNode[] = []\n\n for (const node of nodes) {\n result.push(node)\n if (node.children) {\n result.push(...flattenStepNodes(node.children))\n }\n }\n\n return result\n}\n\n/**\n * Get only leaf nodes (nodes with work functions)\n */\nexport function getLeafNodes(nodes: StepNode[]): StepNode[] {\n const result: StepNode[] = []\n\n for (const node of nodes) {\n if (node.work) {\n result.push(node)\n }\n if (node.children) {\n result.push(...getLeafNodes(node.children))\n }\n }\n\n return result\n}\n\n/**\n * Generate a display label from a camelCase function name\n *\n * @example\n * generateLabel(\"loadModules\") // \"Load modules\"\n * generateLabel(\"parseMarkdown\") // \"Parse markdown\"\n * generateLabel(\"initBoardStateGenerator\") // \"Init board state generator\"\n */\nexport function generateLabel(fnName: string): string {\n return fnName\n .replace(/([A-Z])/g, \" $1\") // Insert space before capitals\n .replace(/(\\d+)/g, \" $1\") // Insert space before numbers\n .toLowerCase() // Convert all to lowercase\n .trim() // Remove leading/trailing spaces\n .replace(/\\s+/g, \" \") // Collapse multiple spaces\n .replace(/^./, (s) => s.toUpperCase()) // Capitalize only first letter\n}\n\n/**\n * Check if a value is a StepsDef (nested group)\n */\nexport function isStepsDef(value: unknown): value is StepsDef {\n return typeof value === \"object\" && value !== null && !Array.isArray(value) && typeof value !== \"function\"\n}\n\n/**\n * Check if a value is a tuple [label, function]\n */\nexport function isLabelTuple(value: unknown): value is [string, (...args: unknown[]) => unknown] {\n return Array.isArray(value) && value.length === 2 && typeof value[0] === \"string\" && typeof value[1] === \"function\"\n}\n",
10
+ "/**\n * Declarative steps implementation\n *\n * Provides the declarative overload for steps() that accepts an object\n * structure and shows all steps upfront before execution.\n */\n\nimport { MultiProgress, type TaskHandle } from \"../cli/multi-progress\"\nimport { step as getStepContext, createStepContext, runWithStepContext, type InternalStepContext } from \"./als-context\"\nimport { parseStepsDef, flattenStepNodes, getLeafNodes, type StepNode, type StepsDef } from \"./step-node\"\n\n// Re-export step() for convenience\nexport { step } from \"./als-context\"\n\n// Node.js globals for yielding to event loop\ndeclare function setImmediate(callback: (value?: unknown) => void): unknown\ndeclare function setTimeout(callback: (value?: unknown) => void, ms: number): unknown\n\n/**\n * Options for run() and pipe() execution\n */\nexport interface ExecuteOptions {\n /** Clear progress display after completion (default: false) */\n clear?: boolean\n}\n\n/**\n * Extract the return type from a generator or async generator\n */\ntype GeneratorReturn<T> =\n T extends Generator<unknown, infer R, unknown> ? R : T extends AsyncGenerator<unknown, infer R, unknown> ? R : T\n\n/**\n * Unwrap the result type, handling generators specially\n */\ntype UnwrapResult<T> = Awaited<GeneratorReturn<Awaited<T>>>\n\n/**\n * Result type: maps step keys to their return values\n */\ntype StepResults<T extends StepsDef> = {\n [K in keyof T]: T[K] extends (...args: unknown[]) => infer R\n ? UnwrapResult<R>\n : T[K] extends [string, (...args: unknown[]) => infer R]\n ? UnwrapResult<R>\n : T[K] extends StepsDef\n ? StepResults<T[K]>\n : unknown\n}\n\n/**\n * The runner object returned by steps()\n */\nexport interface StepsRunner<T extends StepsDef> {\n /** Internal: the parsed step nodes (for testing) */\n readonly _steps: StepNode[]\n\n /**\n * Execute all steps sequentially\n * @returns Results keyed by step name\n */\n run(options?: ExecuteOptions): Promise<StepResults<T>>\n\n /**\n * Execute all steps in a pipeline (each receives previous result)\n * @returns Final step's result\n */\n pipe(options?: ExecuteOptions): Promise<unknown>\n\n /**\n * Manually signal completion (for manual execution mode)\n */\n done(options?: { clear?: boolean }): void\n}\n\n/**\n * Create a declarative steps runner\n *\n * @param def - Object structure defining steps\n * @returns StepsRunner with run(), pipe(), and done() methods\n *\n * @example\n * ```typescript\n * const loader = stepsDeclarative({\n * loadModules, // \"Load modules\"\n * loadRepo: { // \"Load repo\" (group)\n * discover, // \"Discover\"\n * parse, // \"Parse\"\n * },\n * });\n *\n * const results = await loader.run({ clear: true });\n * ```\n */\nexport function stepsDeclarative<T extends StepsDef>(def: T): StepsRunner<T> {\n const rootNodes = parseStepsDef(def)\n const allNodes = flattenStepNodes(rootNodes)\n\n let multi: MultiProgress | null = null\n const handles = new Map<StepNode, TaskHandle>()\n\n // Build group tracking: map each group to its leaf nodes\n const groupLeaves = new Map<StepNode, StepNode[]>()\n const leafToGroups = new Map<StepNode, StepNode[]>()\n\n for (const node of allNodes) {\n if (node.children) {\n const leaves = getLeafNodes([node])\n groupLeaves.set(node, leaves)\n for (const leaf of leaves) {\n const groups = leafToGroups.get(leaf) ?? []\n groups.push(node)\n leafToGroups.set(leaf, groups)\n }\n }\n }\n\n return {\n get _steps() {\n return rootNodes\n },\n\n async run(options?: ExecuteOptions): Promise<StepResults<T>> {\n multi = new MultiProgress()\n\n // Register all steps upfront (shows pending state)\n registerAllSteps(allNodes, multi, handles)\n\n // Group timing tracking\n const groupStartTimes = new Map<StepNode, number>()\n const completedLeaves = new Set<StepNode>()\n\n multi.start()\n\n // Yield to event loop to ensure initial render is displayed\n // before we start modifying task states\n await new Promise((resolve) => setImmediate(resolve))\n\n const results: Record<string, unknown> = {}\n\n try {\n // Execute each step with work\n for (const node of allNodes) {\n if (node.work) {\n // Start parent groups if not started\n const groups = leafToGroups.get(node) ?? []\n for (const group of groups) {\n if (!groupStartTimes.has(group)) {\n groupStartTimes.set(group, Date.now())\n handles.get(group)?.start()\n }\n }\n\n const result = await executeStep(node, handles, multi)\n setNestedResult(results, node.key, result)\n\n // Mark leaf as complete and check group completion\n completedLeaves.add(node)\n for (const group of groups) {\n const leaves = groupLeaves.get(group) ?? []\n if (leaves.every((l) => completedLeaves.has(l))) {\n const elapsed = Date.now() - groupStartTimes.get(group)!\n handles.get(group)?.complete(elapsed)\n }\n }\n }\n }\n } finally {\n multi.stop(options?.clear ?? false)\n }\n\n return results as StepResults<T>\n },\n\n async pipe(options?: ExecuteOptions): Promise<unknown> {\n multi = new MultiProgress()\n\n // Register all steps upfront\n registerAllSteps(allNodes, multi, handles)\n\n // Group timing tracking\n const groupStartTimes = new Map<StepNode, number>()\n const completedLeaves = new Set<StepNode>()\n\n multi.start()\n\n // Yield to event loop to ensure initial render is displayed\n // before we start modifying task states\n await new Promise((resolve) => setImmediate(resolve))\n\n let previousResult: unknown = undefined\n\n try {\n // Execute each step, passing previous result\n for (const node of allNodes) {\n if (node.work) {\n // Start parent groups if not started\n const groups = leafToGroups.get(node) ?? []\n for (const group of groups) {\n if (!groupStartTimes.has(group)) {\n groupStartTimes.set(group, Date.now())\n handles.get(group)?.start()\n }\n }\n\n previousResult = await executeStep(node, handles, multi, previousResult)\n\n // Mark leaf as complete and check group completion\n completedLeaves.add(node)\n for (const group of groups) {\n const leaves = groupLeaves.get(group) ?? []\n if (leaves.every((l) => completedLeaves.has(l))) {\n const elapsed = Date.now() - groupStartTimes.get(group)!\n handles.get(group)?.complete(elapsed)\n }\n }\n }\n }\n } finally {\n multi.stop(options?.clear ?? false)\n }\n\n return previousResult\n },\n\n done(options?: { clear?: boolean }) {\n if (multi) {\n multi.stop(options?.clear ?? false)\n multi = null\n }\n },\n }\n}\n\n/**\n * Register all steps with MultiProgress upfront\n */\nfunction registerAllSteps(nodes: StepNode[], multi: MultiProgress, handles: Map<StepNode, TaskHandle>): void {\n // Register in order without insertAfter - simpler and correct\n for (const node of nodes) {\n const isGroup = node.children && !node.work\n const handle = multi.add(node.label, {\n type: isGroup ? \"group\" : \"spinner\",\n indent: node.indent,\n })\n handles.set(node, handle)\n }\n}\n\n/**\n * Execute a single step\n */\nasync function executeStep(\n node: StepNode,\n handles: Map<StepNode, TaskHandle>,\n multi: MultiProgress,\n input?: unknown,\n): Promise<unknown> {\n const handle = handles.get(node)!\n const startTime = Date.now()\n\n // Yield to event loop before starting\n await new Promise((resolve) => setImmediate(resolve))\n\n // Create step context for ALS\n const ctx = createStepContext(node.label, handle, (subLabel) => {\n // Create sub-step handle when step().sub() is called\n return multi.add(subLabel, {\n type: \"spinner\",\n indent: node.indent + 1,\n insertAfter: handle.id,\n })\n })\n\n handle.start()\n\n try {\n // Run work function with ALS context\n const result = await runWithStepContext(ctx, () => {\n if (input !== undefined) {\n return (node.work as (input: unknown) => unknown)(input)\n }\n return node.work!()\n })\n\n // Handle generator results\n if (isGenerator(result)) {\n return await runGenerator(result, ctx, node, multi)\n }\n\n if (isAsyncGenerator(result)) {\n return await runAsyncGenerator(result, ctx, node, multi)\n }\n\n // Complete any remaining sub-step\n ctx._completeSubStep()\n\n // Complete the step with timing\n const elapsed = Date.now() - startTime\n handle.complete(elapsed)\n\n return result\n } catch (error) {\n handle.fail()\n throw error\n }\n}\n\n/**\n * Run a sync generator step\n */\nasync function runGenerator<T>(\n gen: Generator<unknown, T, unknown>,\n ctx: InternalStepContext,\n node: StepNode,\n multi: MultiProgress,\n): Promise<T> {\n const startTime = Date.now()\n let result = gen.next()\n let hasSubSteps = false\n // Track last inserted handle to maintain correct order\n // Each new sub-step inserts after the previous one, not after parent\n let lastInsertedId = ctx.handle.id\n\n while (!result.done) {\n const value = result.value\n\n // Handle yielded values\n if (isDeclareSteps(value)) {\n // Declare all sub-steps upfront (show as pending)\n if (!hasSubSteps) {\n hasSubSteps = true\n ctx.handle.setType(\"group\")\n }\n for (const label of value.declare) {\n const subHandle = multi.add(label, {\n type: \"spinner\",\n indent: node.indent + 1,\n insertAfter: lastInsertedId,\n })\n lastInsertedId = subHandle.id\n ctx._addSubHandle(label, subHandle)\n }\n } else if (typeof value === \"string\") {\n // String = start a sub-step with this label\n ctx._completeSubStep()\n\n // First sub-step: change parent from spinner to group (no animation)\n if (!hasSubSteps) {\n hasSubSteps = true\n ctx.handle.setType(\"group\")\n }\n\n // Check if already declared, otherwise create new\n const existingHandle = ctx._getSubHandle?.(value)\n if (existingHandle) {\n ctx._setCurrentSubHandle(value, existingHandle)\n existingHandle.start()\n } else {\n const subHandle = multi.add(value, {\n type: \"spinner\",\n indent: node.indent + 1,\n insertAfter: lastInsertedId,\n })\n lastInsertedId = subHandle.id\n ctx._addSubHandle(value, subHandle)\n subHandle.start()\n }\n } else if (isProgressUpdate(value)) {\n // Progress update\n ctx.progress(value.current ?? 0, value.total ?? 0)\n }\n\n // Yield to event loop for animation\n await new Promise((resolve) => setTimeout(resolve, 0))\n\n result = gen.next()\n }\n\n // Complete any remaining sub-step\n ctx._completeSubStep()\n\n // Complete the step with timing\n const elapsed = Date.now() - startTime\n ctx.handle.complete(elapsed)\n\n return result.value\n}\n\n/**\n * Run an async generator step\n */\nasync function runAsyncGenerator<T>(\n gen: AsyncGenerator<unknown, T, unknown>,\n ctx: InternalStepContext,\n node: StepNode,\n multi: MultiProgress,\n): Promise<T> {\n const startTime = Date.now()\n let result = await gen.next()\n let hasSubSteps = false\n // Track last inserted handle to maintain correct order\n // Each new sub-step inserts after the previous one, not after parent\n let lastInsertedId = ctx.handle.id\n\n while (!result.done) {\n const value = result.value\n\n // Handle yielded values\n if (isDeclareSteps(value)) {\n // Declare all sub-steps upfront (show as pending)\n if (!hasSubSteps) {\n hasSubSteps = true\n ctx.handle.setType(\"group\")\n }\n for (const label of value.declare) {\n const subHandle = multi.add(label, {\n type: \"spinner\",\n indent: node.indent + 1,\n insertAfter: lastInsertedId,\n })\n lastInsertedId = subHandle.id\n ctx._addSubHandle(label, subHandle)\n }\n } else if (typeof value === \"string\") {\n // String = start a sub-step with this label\n ctx._completeSubStep()\n\n // First sub-step: change parent from spinner to group (no animation)\n if (!hasSubSteps) {\n hasSubSteps = true\n ctx.handle.setType(\"group\")\n }\n\n // Check if already declared, otherwise create new\n const existingHandle = ctx._getSubHandle(value)\n if (existingHandle) {\n ctx._setCurrentSubHandle(value, existingHandle)\n existingHandle.start()\n } else {\n const subHandle = multi.add(value, {\n type: \"spinner\",\n indent: node.indent + 1,\n insertAfter: lastInsertedId,\n })\n lastInsertedId = subHandle.id\n ctx._addSubHandle(value, subHandle)\n subHandle.start()\n }\n } else if (isProgressUpdate(value)) {\n // Progress update\n ctx.progress(value.current ?? 0, value.total ?? 0)\n }\n\n // Yield to event loop for animation\n await new Promise((resolve) => setTimeout(resolve, 0))\n\n result = await gen.next()\n }\n\n // Complete any remaining sub-step\n ctx._completeSubStep()\n\n // Complete the step with timing\n const elapsed = Date.now() - startTime\n ctx.handle.complete(elapsed)\n\n return result.value\n}\n\n/**\n * Set a nested result value by key path\n */\nfunction setNestedResult(results: Record<string, unknown>, key: string, value: unknown): void {\n // For now, flat keys only - nested groups would need path handling\n results[key] = value\n}\n\n/**\n * Type guards\n */\nfunction isGenerator(value: unknown): value is Generator<unknown, unknown, unknown> {\n return (\n value !== null &&\n typeof value === \"object\" &&\n typeof (value as Generator).next === \"function\" &&\n typeof (value as Generator)[Symbol.iterator] === \"function\"\n )\n}\n\nfunction isAsyncGenerator(value: unknown): value is AsyncGenerator<unknown, unknown, unknown> {\n return (\n value !== null &&\n typeof value === \"object\" &&\n typeof (value as AsyncGenerator).next === \"function\" &&\n typeof (value as AsyncGenerator)[Symbol.asyncIterator] === \"function\"\n )\n}\n\ninterface ProgressUpdate {\n current?: number\n total?: number\n}\n\ninterface DeclareSteps {\n declare: string[]\n}\n\nfunction isProgressUpdate(value: unknown): value is ProgressUpdate {\n return (\n value !== null && typeof value === \"object\" && !Array.isArray(value) && (\"current\" in value || \"total\" in value)\n )\n}\n\nfunction isDeclareSteps(value: unknown): value is DeclareSteps {\n return (\n value !== null && typeof value === \"object\" && \"declare\" in value && Array.isArray((value as DeclareSteps).declare)\n )\n}\n",
11
+ "/**\n * Sequential step runner with progress display\n *\n * Supports two modes:\n *\n * ## Declarative Mode (recommended)\n *\n * Pass an object to declare all steps upfront:\n *\n * ```typescript\n * import { steps, step } from \"@silvery/ag-react/ui/progress\";\n *\n * const loader = steps({\n * loadModules, // Auto-named: \"Load modules\"\n * loadRepo: { // Group: \"Load repo\"\n * discover, // \"Discover\"\n * parse, // \"Parse\"\n * },\n * });\n *\n * const results = await loader.run({ clear: true });\n *\n * // Inside work functions, use step() to report progress:\n * async function discover() {\n * const files = await glob(\"**\\/*.md\");\n * for (let i = 0; i < files.length; i++) {\n * step().progress(i + 1, files.length);\n * await process(files[i]);\n * }\n * return files;\n * }\n * ```\n *\n * ## Fluent Mode (legacy)\n *\n * Chain steps with `.run()` and execute with `.execute()`:\n *\n * ```typescript\n * await steps()\n * .run(\"Loading modules\", () => import(\"./app\"))\n * .run(\"Building view\", async () => buildView())\n * .execute();\n * ```\n *\n * Generators can yield sub-step progress:\n * - Yield a **string** to create a new sub-step\n * - Yield an **object** `{ current, total }` to update progress on current sub-step\n */\n\nimport { MultiProgress, type TaskHandle } from \"../cli/multi-progress\"\nimport { stepsDeclarative, type StepsRunner } from \"./declarative\"\nimport type { StepsDef } from \"./step-node\"\n\n// Re-export step() context helper\nexport { step } from \"./als-context\"\n\n// Re-export types from declarative\nexport type { StepsRunner } from \"./declarative\"\nexport type { StepsDef, StepNode } from \"./step-node\"\nexport type { StepContext } from \"./als-context\"\n\n// Node.js globals for yielding to event loop\ndeclare function setImmediate(callback: (value?: unknown) => void): unknown\ndeclare function setTimeout(callback: (value?: unknown) => void, ms: number): unknown\n\n/** Progress update (object form) */\ninterface ProgressUpdate {\n current?: number\n total?: number\n}\n\n/** Declare all sub-steps upfront (optional, yield as first value) */\ninterface DeclareSteps {\n declare: string[]\n}\n\n/** What generators can yield */\ntype StepYield = string | ProgressUpdate | DeclareSteps\n\n/**\n * Controller for creating and updating sub-steps\n *\n * Passed to work functions that need to report sub-step progress.\n */\nexport interface StepController {\n /** Create a new sub-step (auto-completes previous sub-step) */\n new (label: string): void\n\n /** Update progress on current sub-step */\n progress(current: number, total: number): void\n\n /** Explicitly complete current sub-step (optional - new() auto-completes) */\n done(): void\n}\n\n/** Work function types */\ntype SyncWork<T> = () => T\ntype AsyncWork<T> = () => PromiseLike<T>\ntype SyncGeneratorWork<T> = () => Generator<StepYield, T, unknown>\ntype AsyncGeneratorWork<T> = () => AsyncGenerator<StepYield, T, unknown>\n/** Work function with step controller for sub-step progress */\ntype WorkWithStep<T> = (step: StepController) => T | PromiseLike<T>\n\ntype WorkFn<T> = SyncWork<T> | AsyncWork<T> | SyncGeneratorWork<T> | AsyncGeneratorWork<T> | WorkWithStep<T>\n\n/** Step definition */\ninterface StepDef<T = unknown> {\n title: string\n work: WorkFn<T>\n}\n\n/** Options for execute() */\nexport interface ExecuteOptions {\n /** Clear progress display after completion (default: false) */\n clear?: boolean\n}\n\nexport interface StepBuilder {\n /**\n * Add a step to run\n *\n * @param title - Display title for this step\n * @param work - Function to execute. Can be:\n * - Sync function: `() => result`\n * - Async function: `async () => result`\n * - Sync generator: `function* () { yield \"sub-step\"; return result; }`\n * - Async generator: `async function* () { yield \"sub-step\"; return result; }`\n *\n * Generators can yield:\n * - `\"string\"` - Creates a new sub-step with that label\n * - `{ current: N, total: M }` - Updates progress on current sub-step\n */\n run<T>(title: string, work: WorkFn<T>): StepBuilder\n\n /**\n * Execute all steps in sequence\n * @param options - Execution options\n * @returns Results keyed by step title\n */\n execute(options?: ExecuteOptions): Promise<Record<string, unknown>>\n}\n\n/**\n * Create a step runner\n *\n * @overload Declarative mode - pass an object to declare steps upfront\n * @overload Fluent mode - chain steps with .run() and execute with .execute()\n *\n * @example Declarative mode (recommended)\n * ```typescript\n * const loader = steps({\n * loadModules,\n * loadRepo: { discover, parse },\n * });\n * const results = await loader.run({ clear: true });\n * ```\n *\n * @example Fluent mode\n * ```typescript\n * await steps()\n * .run(\"Step 1\", doStep1)\n * .run(\"Step 2\", doStep2)\n * .execute();\n * ```\n */\nexport function steps<T extends StepsDef>(def: T): StepsRunner<T>\nexport function steps(): StepBuilder\nexport function steps<T extends StepsDef>(def?: T): StepsRunner<T> | StepBuilder {\n // Declarative mode: object passed\n if (def !== undefined) {\n return stepsDeclarative(def)\n }\n\n // Fluent mode: no arguments\n return createFluentBuilder()\n}\n\n/**\n * Create a no-op StepController for work functions that don't use sub-steps.\n * This satisfies the type union while being harmless if not used.\n */\nfunction createNoopStepController(): StepController {\n const controller = (_label: string) => {}\n controller.progress = (_current: number, _total: number) => {}\n controller.done = () => {}\n // StepController uses `new` as a callable signature, not a constructor\n return controller as unknown as StepController\n}\n\n/**\n * Create the fluent step builder (legacy mode)\n */\nfunction createFluentBuilder(): StepBuilder {\n const stepList: StepDef[] = []\n\n const builder: StepBuilder = {\n run<T>(title: string, work: WorkFn<T>): StepBuilder {\n stepList.push({ title, work: work as WorkFn<unknown> })\n return builder\n },\n\n async execute(options?: ExecuteOptions): Promise<Record<string, unknown>> {\n const multi = new MultiProgress()\n const handles = new Map<string, TaskHandle>()\n const results: Record<string, unknown> = {}\n\n // Register all steps upfront (shows pending state)\n for (const step of stepList) {\n handles.set(step.title, multi.add(step.title, { type: \"spinner\" }))\n }\n\n multi.start()\n\n try {\n for (const step of stepList) {\n const handle = handles.get(step.title)!\n\n // Yield to event loop before potentially blocking work\n await new Promise((resolve) => setImmediate(resolve))\n\n const result = step.work(createNoopStepController())\n\n if (isAsyncGenerator(result)) {\n // Async generator: parent stays static while sub-steps animate\n results[step.title] = await runAsyncGenerator(result, handle, step.title, multi)\n } else if (isSyncGenerator(result)) {\n // Sync generator: same handling\n results[step.title] = await runSyncGenerator(result, handle, step.title, multi)\n } else if (isPromiseLike(result)) {\n handle.start()\n results[step.title] = await result\n handle.complete()\n } else {\n handle.start()\n results[step.title] = result\n handle.complete()\n }\n }\n } finally {\n multi.stop(options?.clear ?? false)\n }\n\n return results\n },\n }\n\n return builder\n}\n\n/**\n * Process a yielded value:\n * - { declare: [...] } = declare all sub-steps upfront (show as pending)\n * - string = start/create a sub-step with that label\n * - { current, total } = update progress on current sub-step\n */\nfunction processYield(value: StepYield, state: GeneratorState, multi: MultiProgress): void {\n // Handle declaration of all sub-steps upfront\n if (isDeclareSteps(value)) {\n for (const label of value.declare) {\n const handle = multi.add(label, {\n type: \"spinner\",\n indent: 1,\n insertAfter: state.lastInsertId,\n })\n state.lastInsertId = handle.id\n state.declaredSteps.set(label, handle)\n }\n return\n }\n\n if (typeof value === \"string\") {\n // String = start a sub-step with this label\n if (state.currentHandle && state.currentLabel) {\n const elapsed = Date.now() - state.subStepStartTime\n state.currentHandle.complete(elapsed)\n }\n\n state.currentLabel = value\n state.subStepStartTime = Date.now()\n\n // Use pre-declared handle if available, otherwise create new one\n const declared = state.declaredSteps.get(value)\n if (declared) {\n state.currentHandle = declared\n state.currentHandle.start()\n } else {\n state.currentHandle = multi.add(value, {\n type: \"spinner\",\n indent: 1,\n insertAfter: state.lastInsertId,\n })\n state.lastInsertId = state.currentHandle.id\n state.currentHandle.start()\n }\n } else if (value && typeof value === \"object\") {\n // Object = progress update on current sub-step\n const { current, total } = value as ProgressUpdate\n if (state.currentHandle && total && total > 0) {\n state.currentHandle.setTitle(`${state.currentLabel} (${current ?? 0}/${total})`)\n }\n }\n}\n\nfunction isDeclareSteps(value: StepYield): value is DeclareSteps {\n return (\n value !== null && typeof value === \"object\" && \"declare\" in value && Array.isArray((value as DeclareSteps).declare)\n )\n}\n\n/** State for generator processing */\ninterface GeneratorState {\n currentLabel: string | undefined\n currentHandle: TaskHandle | null\n lastInsertId: string\n subStepStartTime: number\n startTime: number\n /** Pre-declared sub-steps (pending until started) */\n declaredSteps: Map<string, TaskHandle>\n}\n\n/**\n * Run an async generator step\n */\nasync function runAsyncGenerator<T>(\n gen: AsyncGenerator<StepYield, T, unknown>,\n parentHandle: TaskHandle,\n parentTitle: string,\n multi: MultiProgress,\n): Promise<T> {\n const state: GeneratorState = {\n currentLabel: undefined,\n currentHandle: null,\n lastInsertId: parentHandle.id,\n subStepStartTime: Date.now(),\n startTime: Date.now(),\n declaredSteps: new Map(),\n }\n\n let result = await gen.next()\n\n while (!result.done) {\n processYield(result.value, state, multi)\n\n // Yield to event loop for animation\n await new Promise((resolve) => setTimeout(resolve, 0))\n\n result = await gen.next()\n }\n\n // Complete final sub-step\n if (state.currentHandle && state.currentLabel) {\n const elapsed = Date.now() - state.subStepStartTime\n state.currentHandle.complete(elapsed)\n }\n\n // Complete parent step\n const totalElapsed = Date.now() - state.startTime\n parentHandle.complete(totalElapsed)\n\n return result.value\n}\n\n/**\n * Run a sync generator step\n */\nasync function runSyncGenerator<T>(\n gen: Generator<StepYield, T, unknown>,\n parentHandle: TaskHandle,\n parentTitle: string,\n multi: MultiProgress,\n): Promise<T> {\n const state: GeneratorState = {\n currentLabel: undefined,\n currentHandle: null,\n lastInsertId: parentHandle.id,\n subStepStartTime: Date.now(),\n startTime: Date.now(),\n declaredSteps: new Map(),\n }\n\n let result = gen.next()\n\n while (!result.done) {\n processYield(result.value, state, multi)\n\n // Yield to event loop for animation\n await new Promise((resolve) => setTimeout(resolve, 0))\n\n result = gen.next()\n }\n\n // Complete final sub-step\n if (state.currentHandle && state.currentLabel) {\n const elapsed = Date.now() - state.subStepStartTime\n state.currentHandle.complete(elapsed)\n }\n\n // Complete parent step\n const totalElapsed = Date.now() - state.startTime\n parentHandle.complete(totalElapsed)\n\n return result.value\n}\n\nfunction isAsyncGenerator(value: unknown): value is AsyncGenerator<StepYield, unknown, unknown> {\n return (\n value !== null &&\n typeof value === \"object\" &&\n typeof (value as AsyncGenerator).next === \"function\" &&\n typeof (value as AsyncGenerator)[Symbol.asyncIterator] === \"function\"\n )\n}\n\nfunction isSyncGenerator(value: unknown): value is Generator<StepYield, unknown, unknown> {\n return (\n value !== null &&\n typeof value === \"object\" &&\n typeof (value as Generator).next === \"function\" &&\n typeof (value as Generator)[Symbol.iterator] === \"function\"\n )\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return value !== null && typeof value === \"object\" && typeof (value as PromiseLike<unknown>).then === \"function\"\n}\n",
12
+ "/**\n * Fluent single-task wrapper\n *\n * @deprecated Use `steps()` from `@silvery/ag-react/ui/progress` instead.\n *\n * @example\n * ```typescript\n * // OLD (deprecated):\n * import { task } from \"@silvery/ag-react/ui/progress\";\n * const data = await task(\"Loading data\").wrap(fetchData());\n *\n * // NEW:\n * import { steps } from \"@silvery/ag-react/ui/progress\";\n * const results = await steps({ loadData: fetchData }).run();\n * ```\n */\n\nimport type { ProgressInfo } from \"../types.js\"\nimport { createSpinner } from \"../cli/spinner\"\n\n/** Phase labels for common operations */\nconst PHASE_LABELS: Record<string, string> = {\n reading: \"Reading events\",\n applying: \"Applying events\",\n rules: \"Evaluating rules\",\n scanning: \"Scanning files\",\n reconciling: \"Reconciling changes\",\n board: \"Building view\",\n}\n\nexport interface TaskWrapper {\n /**\n * Wrap work with a spinner indicator\n * @param work - Promise, function, or generator\n */\n wrap<T>(\n work: T | PromiseLike<T> | (() => T | PromiseLike<T>) | (() => Generator<ProgressInfo, T, unknown>),\n ): Promise<T>\n}\n\n/**\n * Create a task wrapper with spinner\n *\n * @param title - Display title for the task\n * @returns TaskWrapper with wrap() method\n */\nexport function task(title: string): TaskWrapper {\n return {\n async wrap<T>(\n work: T | PromiseLike<T> | (() => T | PromiseLike<T>) | (() => Generator<ProgressInfo, T, unknown>),\n ): Promise<T> {\n const spinner = createSpinner()\n spinner(title)\n\n try {\n // If it's a function, call it\n if (typeof work === \"function\") {\n const result = (work as () => unknown)()\n\n // Check if it's a generator\n if (isGenerator(result)) {\n return await runGenerator(result as Generator<ProgressInfo, T, unknown>, spinner, title)\n }\n\n // Check if it's a promise\n if (isPromiseLike(result)) {\n const value = await result\n spinner.succeed(title)\n return value as T\n }\n\n // Sync function\n spinner.succeed(title)\n return result as T\n }\n\n // If it's a promise-like, await it\n if (isPromiseLike(work)) {\n const value = await work\n spinner.succeed(title)\n return value as T\n }\n\n // Otherwise it's a direct value\n spinner.succeed(title)\n return work as T\n } catch (error) {\n spinner.fail(title)\n throw error\n }\n },\n }\n}\n\n/**\n * Run a generator with progress updates\n */\nasync function runGenerator<T>(\n gen: Generator<ProgressInfo, T, unknown>,\n spinner: ReturnType<typeof createSpinner>,\n baseTitle: string,\n): Promise<T> {\n let result = gen.next()\n\n while (!result.done) {\n const info = result.value\n const phase = info.phase ?? \"\"\n const phaseLabel = PHASE_LABELS[phase] ?? (phase || baseTitle)\n\n // Update spinner with phase and progress count\n if (info.total && info.total > 0) {\n spinner(`${phaseLabel} (${info.current}/${info.total})`)\n } else {\n spinner(phaseLabel)\n }\n\n // Yield to event loop for animation\n await new Promise((resolve) => setImmediate(resolve))\n\n result = gen.next()\n }\n\n spinner.succeed(baseTitle)\n return result.value\n}\n\nfunction isGenerator(value: unknown): value is Generator<ProgressInfo, unknown, unknown> {\n return (\n value !== null &&\n typeof value === \"object\" &&\n typeof (value as Generator).next === \"function\" &&\n typeof (value as Generator).throw === \"function\"\n )\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return value !== null && typeof value === \"object\" && typeof (value as PromiseLike<unknown>).then === \"function\"\n}\n",
13
+ "/**\n * Fluent sequential task builder\n *\n * @deprecated Use `steps()` from `@silvery/ag-react/ui/progress` instead.\n *\n * @example\n * ```typescript\n * // OLD (deprecated):\n * import { tasks } from \"@silvery/ag-react/ui/progress\";\n * const results = await tasks()\n * .add(\"Loading\", loadModules)\n * .add(\"Processing\", processData)\n * .run({ clear: true });\n *\n * // NEW:\n * import { steps } from \"@silvery/ag-react/ui/progress\";\n * const results = await steps({\n * loadModules,\n * processData,\n * }).run({ clear: true });\n * ```\n */\n\nimport type { ProgressInfo } from \"../types.js\"\nimport { MultiProgress, type TaskHandle } from \"../cli/multi-progress\"\n\n// Node.js globals for yielding to event loop\ndeclare function setImmediate(callback: (value?: unknown) => void): unknown\ndeclare function setTimeout(callback: (value?: unknown) => void, ms: number): unknown\n\n/** Phase labels for common operations */\nconst PHASE_LABELS: Record<string, string> = {\n // Repo loading phases\n discover: \"Discovering files\",\n parse: \"Parsing markdown\",\n apply: \"Applying changes\",\n resolve: \"Resolving links\",\n materialize: \"Evaluating rules\",\n // Board building\n board: \"Building view\",\n // Legacy/alternative names\n reading: \"Reading events\",\n applying: \"Applying events\",\n rules: \"Evaluating rules\",\n scanning: \"Scanning files\",\n reconciling: \"Reconciling changes\",\n}\n\n/** Task definition */\ninterface TaskDef<T = unknown> {\n title: string\n work: () => T | PromiseLike<T> | Generator<ProgressInfo, T, unknown>\n}\n\n/** Options for run() */\nexport interface RunOptions {\n /** Clear progress display after completion (default: false) */\n clear?: boolean\n}\n\nexport interface TaskBuilder {\n /**\n * Add a task to the sequence\n * @param title - Display title\n * @param work - Function, async function, or generator\n */\n add<T>(title: string, work: () => T | PromiseLike<T> | Generator<ProgressInfo, T, unknown>): TaskBuilder\n\n /**\n * Run all tasks in sequence\n * @param options - Run options\n * @returns Results keyed by task title\n */\n run(options?: RunOptions): Promise<Record<string, unknown>>\n}\n\n/**\n * Create a sequential task builder\n *\n * @returns TaskBuilder with add() and run() methods\n */\nexport function tasks(): TaskBuilder {\n const taskList: TaskDef[] = []\n\n const builder: TaskBuilder = {\n add<T>(title: string, work: () => T | PromiseLike<T> | Generator<ProgressInfo, T, unknown>): TaskBuilder {\n taskList.push({ title, work })\n return builder\n },\n\n async run(options?: RunOptions): Promise<Record<string, unknown>> {\n const multi = new MultiProgress()\n const handles = new Map<string, TaskHandle>()\n const results: Record<string, unknown> = {}\n\n // Register all tasks upfront (shows pending state)\n for (const task of taskList) {\n handles.set(task.title, multi.add(task.title, { type: \"spinner\" }))\n }\n\n multi.start()\n\n try {\n for (const task of taskList) {\n const handle = handles.get(task.title)!\n\n // Force render before potentially blocking operation\n await new Promise((resolve) => setImmediate(resolve))\n\n const result = task.work()\n\n if (isGenerator(result)) {\n // Generator: parent stays static, phases animate underneath\n results[task.title] = await runGenerator(result, handle, task.title, multi)\n } else if (isPromiseLike(result)) {\n handle.start()\n results[task.title] = await result\n handle.complete()\n } else {\n handle.start()\n results[task.title] = result\n handle.complete()\n }\n }\n } finally {\n multi.stop(options?.clear ?? false)\n }\n\n return results\n },\n }\n\n return builder\n}\n\n/**\n * Run a generator task with progress updates\n * Parent task stays visible while sub-phases are indented below\n */\nasync function runGenerator<T>(\n gen: Generator<ProgressInfo, T, unknown>,\n parentHandle: TaskHandle,\n baseTitle: string,\n multi: MultiProgress,\n): Promise<T> {\n let result = gen.next()\n let currentPhase: string | undefined\n let currentPhaseHandle: TaskHandle | null = null\n let lastInsertId = parentHandle.id // Insert phases after parent (then after each other)\n let phaseStartTime = Date.now()\n const taskStartTime = Date.now()\n\n while (!result.done) {\n const info = result.value\n const phase = info.phase ?? \"\"\n\n // When phase changes, complete current phase and start new one (indented)\n if (phase && phase !== currentPhase) {\n if (currentPhaseHandle && currentPhase) {\n // Complete previous phase with timing\n const elapsed = Date.now() - phaseStartTime\n const prevLabel = PHASE_LABELS[currentPhase] ?? currentPhase\n currentPhaseHandle.complete(`${prevLabel} (${elapsed}ms)`)\n }\n\n // Start new phase line (indented under parent, inserted after last phase)\n currentPhase = phase\n phaseStartTime = Date.now()\n const phaseLabel = PHASE_LABELS[phase] ?? phase\n currentPhaseHandle = multi.add(phaseLabel, {\n type: \"spinner\",\n indent: 1,\n insertAfter: lastInsertId,\n })\n lastInsertId = currentPhaseHandle.id\n currentPhaseHandle.start()\n }\n\n // Update progress count on current phase line\n if (currentPhaseHandle && info.total && info.total > 0) {\n const phaseLabel = PHASE_LABELS[phase] ?? phase\n currentPhaseHandle.setTitle(`${phaseLabel} (${info.current}/${info.total})`)\n }\n\n // Yield to event loop for animation\n await new Promise((resolve) => setTimeout(resolve, 0))\n\n result = gen.next()\n }\n\n // Complete final phase\n if (currentPhaseHandle && currentPhase) {\n const elapsed = Date.now() - phaseStartTime\n const finalLabel = PHASE_LABELS[currentPhase] ?? currentPhase\n currentPhaseHandle.complete(`${finalLabel} (${elapsed}ms)`)\n }\n\n // Complete parent task with total timing\n const totalElapsed = Date.now() - taskStartTime\n parentHandle.complete(`${baseTitle} (${totalElapsed}ms)`)\n\n return result.value\n}\n\nfunction isGenerator(value: unknown): value is Generator<ProgressInfo, unknown, unknown> {\n return (\n value !== null &&\n typeof value === \"object\" &&\n typeof (value as Generator).next === \"function\" &&\n typeof (value as Generator).throw === \"function\"\n )\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return value !== null && typeof value === \"object\" && typeof (value as PromiseLike<unknown>).then === \"function\"\n}\n",
14
+ "/**\n * CLI ProgressBar - Determinate progress indicator with ETA\n */\n\nimport chalk from \"chalk\"\nimport type { ProgressBarOptions } from \"../types.js\"\nimport { CURSOR_HIDE, CURSOR_SHOW, CURSOR_TO_START, CLEAR_LINE_END, write, isTTY, getTerminalWidth } from \"./ansi\"\nimport { calculateETA, formatETA, DEFAULT_ETA_BUFFER_SIZE, type ETASample } from \"../utils/eta\"\n\n/** Default format string */\nconst DEFAULT_FORMAT = \":bar :percent | :current/:total | ETA: :eta\"\n\n/**\n * ProgressBar class for CLI progress indication\n *\n * @example\n * ```ts\n * const bar = new ProgressBar({ total: 100 });\n * bar.start();\n * for (let i = 0; i <= 100; i++) {\n * await doWork();\n * bar.update(i);\n * }\n * bar.stop();\n * ```\n */\nexport class ProgressBar {\n private total: number\n private format: string\n private width: number\n private complete: string\n private incomplete: string\n private stream: NodeJS.WriteStream\n private hideCursor: boolean\n private phases: Record<string, string>\n\n private current = 0\n private phase: string | null = null\n private startTime: number | null = null\n private isActive = false\n\n // ETA smoothing - track last N update times\n private etaBuffer: ETASample[] = []\n\n constructor(options: ProgressBarOptions = {}) {\n this.total = options.total ?? 100\n this.format = options.format ?? DEFAULT_FORMAT\n this.width = options.width ?? 40\n this.complete = options.complete ?? \"█\"\n this.incomplete = options.incomplete ?? \"░\"\n this.stream = options.stream ?? process.stdout\n this.hideCursor = options.hideCursor ?? true\n this.phases = options.phases ?? {}\n }\n\n /**\n * Start the progress bar\n */\n start(initialValue = 0, initialTotal?: number): this {\n if (initialTotal !== undefined) {\n this.total = initialTotal\n }\n\n this.current = initialValue\n this.startTime = Date.now()\n this.isActive = true\n this.etaBuffer = [{ time: this.startTime, value: initialValue }]\n\n if (this.hideCursor && isTTY(this.stream)) {\n write(CURSOR_HIDE, this.stream)\n }\n\n this.render()\n return this\n }\n\n /**\n * Update progress value\n */\n update(value: number, tokens?: Record<string, string | number>): this {\n this.current = Math.min(value, this.total)\n\n // Update ETA buffer\n const now = Date.now()\n this.etaBuffer.push({ time: now, value: this.current })\n if (this.etaBuffer.length > DEFAULT_ETA_BUFFER_SIZE) {\n this.etaBuffer.shift()\n }\n\n if (this.isActive) {\n this.render(tokens)\n }\n\n return this\n }\n\n /**\n * Increment progress by amount (default: 1)\n */\n increment(amount = 1, tokens?: Record<string, string | number>): this {\n return this.update(this.current + amount, tokens)\n }\n\n /**\n * Set the current phase (for multi-phase progress)\n */\n setPhase(phaseName: string, options?: { current?: number; total?: number }): this {\n this.phase = phaseName\n\n if (options?.total !== undefined) {\n this.total = options.total\n }\n if (options?.current !== undefined) {\n this.current = options.current\n // Reset ETA buffer on phase change\n this.etaBuffer = [{ time: Date.now(), value: this.current }]\n }\n\n if (this.isActive) {\n this.render()\n }\n\n return this\n }\n\n /**\n * Stop the progress bar\n */\n stop(clear = false): this {\n if (!this.isActive) {\n return this\n }\n\n this.isActive = false\n\n if (clear && isTTY(this.stream)) {\n write(`${CURSOR_TO_START}${CLEAR_LINE_END}`, this.stream)\n } else {\n write(\"\\n\", this.stream)\n }\n\n if (this.hideCursor && isTTY(this.stream)) {\n write(CURSOR_SHOW, this.stream)\n }\n\n return this\n }\n\n /** Get ETA in seconds using smoothed rate */\n private getETASeconds(): number | null {\n return calculateETA(this.etaBuffer, this.current, this.total)\n }\n\n /**\n * Render the progress bar\n */\n private render(tokens?: Record<string, string | number>): void {\n const percent = this.total > 0 ? this.current / this.total : 0\n const eta = this.getETASeconds()\n\n // Build the bar\n const completeLength = Math.round(this.width * percent)\n const incompleteLength = this.width - completeLength\n const bar = this.complete.repeat(completeLength) + this.incomplete.repeat(incompleteLength)\n\n // Get phase display name\n const phaseDisplay = this.phase ? (this.phases[this.phase] ?? this.phase) : \"\"\n\n // Calculate rate\n const elapsed = this.startTime ? (Date.now() - this.startTime) / 1000 : 0\n const rate = elapsed > 0 ? this.current / elapsed : 0\n\n // Replace tokens in format string\n let output = this.format\n .replace(\":bar\", chalk.cyan(bar))\n .replace(\":percent\", `${Math.round(percent * 100)}%`.padStart(4))\n .replace(\":current\", String(this.current))\n .replace(\":total\", String(this.total))\n .replace(\":eta\", formatETA(eta))\n .replace(\":elapsed\", formatETA(elapsed))\n .replace(\":rate\", rate.toFixed(1))\n .replace(\":phase\", chalk.dim(phaseDisplay))\n\n // Replace custom tokens\n if (tokens) {\n for (const [key, value] of Object.entries(tokens)) {\n output = output.replace(`:${key}`, String(value))\n }\n }\n\n // Truncate to terminal width\n const termWidth = getTerminalWidth(this.stream)\n if (output.length > termWidth) {\n output = output.slice(0, termWidth - 1)\n }\n\n if (isTTY(this.stream)) {\n write(`${CURSOR_TO_START}${output}${CLEAR_LINE_END}`, this.stream)\n }\n }\n\n /**\n * Get current progress ratio (0-1)\n */\n get ratio(): number {\n return this.total > 0 ? this.current / this.total : 0\n }\n\n /**\n * Get current progress percentage (0-100)\n */\n get percentage(): number {\n return Math.round(this.ratio * 100)\n }\n\n /**\n * Dispose the progress bar (calls stop)\n */\n [Symbol.dispose](): void {\n this.stop()\n }\n}\n",
15
+ "/**\n * Shared ETA calculation utilities\n */\n\n/** Sample point for ETA calculation */\nexport interface ETASample {\n time: number\n value: number\n}\n\n/** ETA calculation result */\nexport interface ETAResult {\n /** Estimated seconds remaining, or null if insufficient data */\n seconds: number | null\n /** Formatted ETA string (e.g., \"1:30\", \"2:15:30\", \"--:--\") */\n formatted: string\n}\n\n/**\n * Calculate ETA from a buffer of samples\n *\n * @param buffer - Array of {time, value} samples\n * @param current - Current progress value\n * @param total - Total target value\n * @returns ETA in seconds (null if insufficient data)\n *\n * @example\n * ```ts\n * const buffer = [\n * { time: 1000, value: 0 },\n * { time: 2000, value: 10 },\n * ];\n * const eta = calculateETA(buffer, 10, 100);\n * // eta = 9 (9 seconds remaining at 10 items/sec)\n * ```\n */\nexport function calculateETA(buffer: ETASample[], current: number, total: number): number | null {\n if (buffer.length < 2) {\n return null\n }\n\n const first = buffer[0]!\n const last = buffer[buffer.length - 1]!\n\n const elapsed = (last.time - first.time) / 1000 // seconds\n const progress = last.value - first.value\n\n if (elapsed <= 0 || progress <= 0) {\n return null\n }\n\n const rate = progress / elapsed // items per second\n const remaining = total - current\n\n return remaining / rate\n}\n\n/**\n * Format ETA seconds as human-readable string\n *\n * @param eta - ETA in seconds (null for unknown)\n * @returns Formatted string (e.g., \"1:30\", \"2:15:30\", \"--:--\", \">1d\")\n *\n * @example\n * ```ts\n * formatETA(90) // \"1:30\"\n * formatETA(3665) // \"1:01:05\"\n * formatETA(null) // \"--:--\"\n * formatETA(100000) // \">1d\"\n * ```\n */\nexport function formatETA(eta: number | null): string {\n if (eta === null || !isFinite(eta)) {\n return \"--:--\"\n }\n\n if (eta > 86400) {\n // > 24 hours\n return \">1d\"\n }\n\n const hours = Math.floor(eta / 3600)\n const minutes = Math.floor((eta % 3600) / 60)\n const seconds = Math.floor(eta % 60)\n\n if (hours > 0) {\n return `${hours}:${minutes.toString().padStart(2, \"0\")}:${seconds.toString().padStart(2, \"0\")}`\n }\n\n return `${minutes}:${seconds.toString().padStart(2, \"0\")}`\n}\n\n/**\n * Calculate and format ETA in one call\n *\n * @param buffer - Array of {time, value} samples\n * @param current - Current progress value\n * @param total - Total target value\n * @returns Object with seconds (number|null) and formatted string\n */\nexport function getETA(buffer: ETASample[], current: number, total: number): ETAResult {\n const seconds = calculateETA(buffer, current, total)\n return {\n seconds,\n formatted: formatETA(seconds),\n }\n}\n\n/** Default buffer size for ETA smoothing */\nexport const DEFAULT_ETA_BUFFER_SIZE = 10\n\n/**\n * Create an ETA tracker with automatic buffer management\n *\n * @param bufferSize - Number of samples to keep (default: 10)\n * @returns ETA tracker object\n *\n * @example\n * ```ts\n * const tracker = createETATracker();\n * tracker.record(0);\n * // ... later ...\n * tracker.record(50);\n * const eta = tracker.getETA(50, 100);\n * console.log(eta.formatted); // \"0:30\"\n * ```\n */\nexport function createETATracker(bufferSize = DEFAULT_ETA_BUFFER_SIZE) {\n const buffer: ETASample[] = []\n\n return {\n /** Record a new sample */\n record(value: number): void {\n buffer.push({ time: Date.now(), value })\n if (buffer.length > bufferSize) {\n buffer.shift()\n }\n },\n\n /** Get current ETA */\n getETA(current: number, total: number): ETAResult {\n return getETA(buffer, current, total)\n },\n\n /** Reset the buffer */\n reset(): void {\n buffer.length = 0\n },\n\n /** Get buffer for external use */\n getBuffer(): readonly ETASample[] {\n return buffer\n },\n }\n}\n"
16
+ ],
17
+ "mappings": "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",
18
+ "debugId": "E192A1E0E9E2C66264756E2164756E21",
19
+ "names": []
20
+ }
@@ -0,0 +1,3 @@
1
+ import{useState,useEffect}from"react";import chalk from"chalk";var SPINNER_FRAMES={dots:["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"],line:["-","\\","|","/"],arc:["◜","◠","◝","◞","◡","◟"],bounce:["⠁","⠂","⠄","⠂"],pulse:["█","▓","▒","░","▒","▓"]},SPINNER_INTERVALS={dots:80,line:120,arc:100,bounce:120,pulse:100};import{jsxDEV}from"react/jsx-dev-runtime";function Spinner({label,style="dots",color="cyan"}){let[frameIndex,setFrameIndex]=useState(0),frames=SPINNER_FRAMES[style],interval=SPINNER_INTERVALS[style];useEffect(()=>{let timer=setInterval(()=>{setFrameIndex((i)=>(i+1)%frames.length)},interval);return()=>clearInterval(timer)},[frames.length,interval]);let frame=frames[frameIndex];return jsxDEV("span",{"data-progressx-spinner":!0,"data-color":color,children:[frame,label&&jsxDEV("span",{children:[" ",label]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}function useSpinnerFrame(style="dots"){let[frameIndex,setFrameIndex]=useState(0),frames=SPINNER_FRAMES[style],interval=SPINNER_INTERVALS[style];return useEffect(()=>{let timer=setInterval(()=>{setFrameIndex((i)=>(i+1)%frames.length)},interval);return()=>clearInterval(timer)},[frames.length,interval]),frames[frameIndex]}import{useState as useState2,useEffect as useEffect2,useRef}from"react";function calculateETA(buffer,current,total){if(buffer.length<2)return null;let first=buffer[0],last=buffer[buffer.length-1],elapsed=(last.time-first.time)/1000,progress=last.value-first.value;if(elapsed<=0||progress<=0)return null;let rate=progress/elapsed;return(total-current)/rate}function formatETA(eta){if(eta===null||!isFinite(eta))return"--:--";if(eta>86400)return">1d";let hours=Math.floor(eta/3600),minutes=Math.floor(eta%3600/60),seconds=Math.floor(eta%60);if(hours>0)return`${hours}:${minutes.toString().padStart(2,"0")}:${seconds.toString().padStart(2,"0")}`;return`${minutes}:${seconds.toString().padStart(2,"0")}`}function getETA(buffer,current,total){let seconds=calculateETA(buffer,current,total);return{seconds,formatted:formatETA(seconds)}}var DEFAULT_ETA_BUFFER_SIZE=10;import{jsxDEV as jsxDEV2}from"react/jsx-dev-runtime";function ProgressBar({value,total,width=40,showPercentage=!0,showETA=!1,label,color="cyan"}){let[eta,setEta]=useState2("--:--"),etaBuffer=useRef([]);useEffect2(()=>{let now=Date.now();if(etaBuffer.current.push({time:now,value}),etaBuffer.current.length>DEFAULT_ETA_BUFFER_SIZE)etaBuffer.current.shift();let result=getETA(etaBuffer.current,value,total);setEta(result.formatted)},[value,total]);let percent=total>0?value/total:0,percentDisplay=`${Math.round(percent*100)}%`,filledWidth=Math.round(width*percent),emptyWidth=width-filledWidth,bar="█".repeat(filledWidth)+"░".repeat(emptyWidth),parts=[];if(label)parts.push(label);if(parts.push(`[${bar}]`),showPercentage)parts.push(percentDisplay.padStart(4));if(showETA)parts.push(`ETA: ${eta}`);return jsxDEV2("span",{"data-progressx-bar":!0,"data-color":color,"data-percent":percent,children:parts.join(" ")},void 0,!1,void 0,this)}function useProgressBar(initialTotal){let[value,setValue]=useState2(0),[total,setTotal]=useState2(initialTotal),etaBuffer=useRef([]),[eta,setEta]=useState2("--:--"),update=(newValue)=>{setValue(newValue);let now=Date.now();if(etaBuffer.current.push({time:now,value:newValue}),etaBuffer.current.length>DEFAULT_ETA_BUFFER_SIZE)etaBuffer.current.shift();let result=getETA(etaBuffer.current,newValue,total);setEta(result.formatted)},increment=(amount=1)=>update(value+amount),reset=(newTotal)=>{if(setValue(0),newTotal!==void 0)setTotal(newTotal);etaBuffer.current=[],setEta("--:--")},percent=total>0?Math.round(value/total*100):0;return{value,total,percent,eta,update,increment,reset,setTotal}}import React3 from"react";import{jsxDEV as jsxDEV3}from"react/jsx-dev-runtime";var STATUS_ICONS={pending:"○",running:"",completed:"✔",failed:"✖",skipped:"⊘"},STATUS_COLORS={pending:"gray",running:"cyan",completed:"green",failed:"red",skipped:"yellow"};function Task({title,status,children}){let spinnerFrame=useSpinnerFrame("dots"),icon=status==="running"?spinnerFrame:STATUS_ICONS[status],color=STATUS_COLORS[status];return jsxDEV3("div",{"data-progressx-task":!0,"data-status":status,"data-color":color,children:[jsxDEV3("span",{"data-icon":!0,children:icon},void 0,!1,void 0,this),jsxDEV3("span",{"data-title":!0,children:[" ",title]},void 0,!0,void 0,this),children!=null?jsxDEV3("div",{"data-children":!0,children},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}function Tasks({children}){return jsxDEV3("div",{"data-progressx-tasks":!0,children},void 0,!1,void 0,this)}function useTasks(initialTasks){let[tasks,setTasks]=React3.useState(initialTasks.map((t)=>({...t,status:"pending"}))),updateTask=(id,updates)=>{setTasks((prev)=>prev.map((t)=>t.id===id?{...t,...updates}:t))},start=(id)=>updateTask(id,{status:"running"}),complete=(id,title)=>updateTask(id,{status:"completed",...title&&{title}}),fail=(id,title)=>updateTask(id,{status:"failed",...title&&{title}}),skip=(id,title)=>updateTask(id,{status:"skipped",...title&&{title}}),updateProgress=(id,progress)=>updateTask(id,{progress}),getTask=(id)=>tasks.find((t)=>t.id===id),allCompleted=tasks.every((t)=>t.status==="completed"||t.status==="skipped"),hasFailed=tasks.some((t)=>t.status==="failed");return{tasks,start,complete,fail,skip,updateProgress,updateTask,getTask,allCompleted,hasFailed}}import{createContext,useContext,useState as useState3,useCallback}from"react";import{jsxDEV as jsxDEV4}from"react/jsx-dev-runtime";var ProgressContext=createContext(null);function ProgressProvider({children}){let[isLoading,setIsLoading]=useState3(!1),[loadingText,setLoadingText]=useState3(""),[spinnerStyle,setSpinnerStyle]=useState3("dots"),[progress,setProgress]=useState3(null),showSpinner=useCallback((text,style="dots")=>{setLoadingText(text),setSpinnerStyle(style),setIsLoading(!0)},[]),hideSpinner=useCallback(()=>{setIsLoading(!1),setLoadingText("")},[]),updateProgress=useCallback((current,total)=>{setProgress((prev)=>({current,total:total??prev?.total??100}))},[]),clearProgress=useCallback(()=>{setProgress(null)},[]),value={isLoading,loadingText,spinnerStyle,showSpinner,hideSpinner,progress,updateProgress,clearProgress};return jsxDEV4(ProgressContext.Provider,{value,children},void 0,!1,void 0,this)}function useProgress(){let context=useContext(ProgressContext);if(!context)throw Error("useProgress must be used within a ProgressProvider");return context}function ProgressIndicator(){let{isLoading,loadingText,spinnerStyle}=useProgress();if(!isLoading)return null;return jsxDEV4(Spinner,{label:loadingText,style:spinnerStyle},void 0,!1,void 0,this)}export{useTasks,useSpinnerFrame,useProgressBar,useProgress,Tasks,Task,Spinner,ProgressProvider,ProgressIndicator,ProgressBar};
2
+
3
+ //# debugId=610D1483D32BB48464756E2164756E21
@@ -0,0 +1,15 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../packages/ag-react/src/ui/react/Spinner.tsx", "../../packages/ag-react/src/ui/cli/spinner.ts", "../../packages/ag-react/src/ui/react/ProgressBar.tsx", "../../packages/ag-react/src/ui/utils/eta.ts", "../../packages/ag-react/src/ui/react/Tasks.tsx", "../../packages/ag-react/src/ui/react/context.tsx"],
4
+ "sourcesContent": [
5
+ "/**\n * React Spinner component for silvery/Ink TUI apps\n */\n\nimport React, { useState, useEffect } from \"react\"\nimport type { SpinnerProps, SpinnerStyle } from \"../types.js\"\nimport { SPINNER_FRAMES, SPINNER_INTERVALS } from \"../cli/spinner\"\n\n/**\n * Animated spinner component for React TUI apps\n *\n * @example\n * ```tsx\n * import { Spinner } from \"@silvery/ag-react/ui/react\";\n *\n * function LoadingView() {\n * return <Spinner label=\"Loading...\" />;\n * }\n *\n * // With style\n * <Spinner label=\"Processing...\" style=\"arc\" color=\"yellow\" />\n * ```\n */\nexport function Spinner({ label, style = \"dots\", color = \"cyan\" }: SpinnerProps): React.ReactElement {\n const [frameIndex, setFrameIndex] = useState(0)\n const frames = SPINNER_FRAMES[style]\n const interval = SPINNER_INTERVALS[style]\n\n useEffect(() => {\n const timer = setInterval(() => {\n setFrameIndex((i) => (i + 1) % frames.length)\n }, interval)\n\n return () => clearInterval(timer)\n }, [frames.length, interval])\n\n const frame = frames[frameIndex]\n\n // Note: In a real silvery app, you'd use <Text color={color}> etc.\n // This is a generic React component that can be styled by the consumer\n return (\n <span data-progressx-spinner data-color={color}>\n {frame}\n {label && <span> {label}</span>}\n </span>\n )\n}\n\n/**\n * Hook for using spinner state in custom components\n *\n * @example\n * ```tsx\n * function CustomSpinner() {\n * const frame = useSpinnerFrame(\"dots\");\n * return <Text color=\"cyan\">{frame}</Text>;\n * }\n * ```\n */\nexport function useSpinnerFrame(style: SpinnerStyle = \"dots\"): string {\n const [frameIndex, setFrameIndex] = useState(0)\n const frames = SPINNER_FRAMES[style]\n const interval = SPINNER_INTERVALS[style]\n\n useEffect(() => {\n const timer = setInterval(() => {\n setFrameIndex((i) => (i + 1) % frames.length)\n }, interval)\n\n return () => clearInterval(timer)\n }, [frames.length, interval])\n\n return frames[frameIndex]!\n}\n",
6
+ "/**\n * CLI Spinner - Animated indeterminate progress indicator\n */\n\nimport chalk from \"chalk\"\nimport type { SpinnerOptions, SpinnerStyle } from \"../types.js\"\nimport { CURSOR_HIDE, CURSOR_SHOW, CURSOR_TO_START, CLEAR_LINE_END, write, isTTY } from \"./ansi\"\n\n/** Spinner animation frames by style */\nexport const SPINNER_FRAMES: Record<SpinnerStyle, string[]> = {\n dots: [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"],\n line: [\"-\", \"\\\\\", \"|\", \"/\"],\n arc: [\"◜\", \"◠\", \"◝\", \"◞\", \"◡\", \"◟\"],\n bounce: [\"⠁\", \"⠂\", \"⠄\", \"⠂\"],\n pulse: [\"█\", \"▓\", \"▒\", \"░\", \"▒\", \"▓\"],\n}\n\n/** Default intervals for each style (ms) */\nexport const SPINNER_INTERVALS: Record<SpinnerStyle, number> = {\n dots: 80,\n line: 120,\n arc: 100,\n bounce: 120,\n pulse: 100,\n}\n\n/**\n * Spinner class for CLI progress indication\n *\n * @example\n * ```ts\n * const spinner = new Spinner(\"Loading...\");\n * spinner.start();\n * await doWork();\n * spinner.succeed(\"Done!\");\n * ```\n */\nexport class Spinner {\n private text: string\n private style: SpinnerStyle\n private color: string\n private stream: NodeJS.WriteStream\n private hideCursor: boolean\n private interval: number\n\n private frameIndex = 0\n private timer: ReturnType<typeof setInterval> | null = null\n private isSpinning = false\n\n constructor(textOrOptions?: string | SpinnerOptions) {\n const options: SpinnerOptions = typeof textOrOptions === \"string\" ? { text: textOrOptions } : (textOrOptions ?? {})\n\n this.text = options.text ?? \"\"\n this.style = options.style ?? \"dots\"\n this.color = options.color ?? \"cyan\"\n this.stream = options.stream ?? process.stdout\n this.hideCursor = options.hideCursor ?? true\n this.interval = options.interval ?? SPINNER_INTERVALS[this.style]\n }\n\n /** Get current spinner text */\n get currentText(): string {\n return this.text\n }\n\n /** Set spinner text (updates immediately if spinning) */\n set currentText(value: string) {\n this.text = value\n if (this.isSpinning) {\n this.render()\n }\n }\n\n /** Check if spinner is currently active */\n get spinning(): boolean {\n return this.isSpinning\n }\n\n /**\n * Start the spinner animation\n */\n start(text?: string): this {\n if (text !== undefined) {\n this.text = text\n }\n\n if (this.isSpinning) {\n return this\n }\n\n this.isSpinning = true\n this.frameIndex = 0\n\n if (this.hideCursor && isTTY(this.stream)) {\n write(CURSOR_HIDE, this.stream)\n }\n\n this.render()\n this.timer = setInterval(() => {\n this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES[this.style].length\n this.render()\n }, this.interval)\n\n return this\n }\n\n /**\n * Stop the spinner\n */\n stop(): this {\n if (!this.isSpinning) {\n return this\n }\n\n this.isSpinning = false\n\n if (this.timer) {\n clearInterval(this.timer)\n this.timer = null\n }\n\n this.clear()\n\n if (this.hideCursor && isTTY(this.stream)) {\n write(CURSOR_SHOW, this.stream)\n }\n\n return this\n }\n\n /**\n * Stop with success message (green checkmark)\n */\n succeed(text?: string): this {\n return this.stopWithSymbol(chalk.green(\"✔\"), text ?? this.text)\n }\n\n /**\n * Stop with failure message (red X)\n */\n fail(text?: string): this {\n return this.stopWithSymbol(chalk.red(\"✖\"), text ?? this.text)\n }\n\n /**\n * Stop with warning message (yellow warning)\n */\n warn(text?: string): this {\n return this.stopWithSymbol(chalk.yellow(\"⚠\"), text ?? this.text)\n }\n\n /**\n * Stop with info message (blue info)\n */\n info(text?: string): this {\n return this.stopWithSymbol(chalk.blue(\"ℹ\"), text ?? this.text)\n }\n\n /**\n * Clear the spinner line\n */\n clear(): this {\n if (isTTY(this.stream)) {\n write(`${CURSOR_TO_START}${CLEAR_LINE_END}`, this.stream)\n }\n return this\n }\n\n private render(): void {\n const frame = SPINNER_FRAMES[this.style][this.frameIndex]\n const colorFn = (chalk as unknown as Record<string, (s: string) => string>)[this.color]\n const coloredFrame = colorFn ? colorFn(frame!) : frame!\n const output = this.text ? `${coloredFrame} ${this.text}` : coloredFrame\n\n if (isTTY(this.stream)) {\n write(`${CURSOR_TO_START}${output}${CLEAR_LINE_END}`, this.stream)\n }\n }\n\n private stopWithSymbol(symbol: string, text: string): this {\n this.stop()\n write(`${symbol} ${text}\\n`, this.stream)\n return this\n }\n\n /**\n * Dispose the spinner (calls stop)\n */\n [Symbol.dispose](): void {\n this.stop()\n }\n\n /**\n * Static helper to quickly start a spinner\n * Returns a stop function\n *\n * @example\n * ```ts\n * const stop = Spinner.start(\"Loading...\");\n * await doWork();\n * stop();\n * ```\n */\n static start(textOrOptions?: string | SpinnerOptions): () => void {\n const spinner = new Spinner(textOrOptions)\n spinner.start()\n return () => spinner.stop()\n }\n}\n\n/**\n * Callable spinner interface - call with text to show/update\n *\n * @example\n * ```ts\n * {\n * using spinner = createSpinner({ style: \"dots\" });\n * spinner(\"Loading...\"); // Shows spinner with text\n * spinner(\"Still loading...\"); // Updates text\n * } // Auto-stops via Symbol.dispose\n * ```\n */\nexport interface CallableSpinner extends Disposable {\n /** Call with text to show/update the spinner */\n (text: string): void\n /** Stop the spinner */\n stop(): void\n /** Stop with success message (green checkmark) */\n succeed(text?: string): void\n /** Stop with failure message (red X) */\n fail(text?: string): void\n /** Stop with warning message (yellow warning) */\n warn(text?: string): void\n /** Stop with info message (blue info) */\n info(text?: string): void\n}\n\n/**\n * Create a callable, disposable spinner\n *\n * The spinner is lazy - it won't show anything until you call it with text.\n * Use with `using` for automatic cleanup:\n *\n * @example\n * ```ts\n * {\n * using spinner = createSpinner({ style: \"dots\" });\n * // Nothing visible yet\n *\n * spinner(\"Loading repo...\"); // Now shows spinner\n * spinner(\"Applying events...\"); // Updates text\n * } // Auto-stops via Symbol.dispose\n * ```\n */\nexport function createSpinner(options?: SpinnerOptions): CallableSpinner {\n const spinner = new Spinner({ ...options, text: \"\" })\n\n const callable = ((text: string) => {\n // Always restart if not spinning (handles initial call and after succeed/fail/etc)\n if (!spinner.spinning) {\n spinner.start(text)\n } else {\n spinner.currentText = text\n }\n }) as CallableSpinner\n\n callable.stop = () => spinner.stop()\n callable.succeed = (text) => spinner.succeed(text)\n callable.fail = (text) => spinner.fail(text)\n callable.warn = (text) => spinner.warn(text)\n callable.info = (text) => spinner.info(text)\n callable[Symbol.dispose] = () => spinner.stop()\n\n return callable\n}\n",
7
+ "/**\n * React ProgressBar component for silvery/Ink TUI apps\n */\n\nimport React, { useState, useEffect, useRef } from \"react\"\nimport type { ProgressBarProps } from \"../types.js\"\nimport { getETA, DEFAULT_ETA_BUFFER_SIZE, type ETASample } from \"../utils/eta\"\n\n/**\n * Progress bar component for React TUI apps\n *\n * @example\n * ```tsx\n * import { ProgressBar } from \"@silvery/ag-react/ui/react\";\n *\n * function DownloadProgress({ current, total }) {\n * return (\n * <ProgressBar\n * value={current}\n * total={total}\n * showPercentage\n * showETA\n * />\n * );\n * }\n * ```\n */\nexport function ProgressBar({\n value,\n total,\n width = 40,\n showPercentage = true,\n showETA = false,\n label,\n color = \"cyan\",\n}: ProgressBarProps): React.ReactElement {\n // ETA calculation state\n const [eta, setEta] = useState<string>(\"--:--\")\n const etaBuffer = useRef<ETASample[]>([])\n\n // Update ETA buffer when value changes\n useEffect(() => {\n const now = Date.now()\n etaBuffer.current.push({ time: now, value })\n\n if (etaBuffer.current.length > DEFAULT_ETA_BUFFER_SIZE) {\n etaBuffer.current.shift()\n }\n\n // Calculate ETA using shared utility\n const result = getETA(etaBuffer.current, value, total)\n setEta(result.formatted)\n }, [value, total])\n\n const percent = total > 0 ? value / total : 0\n const percentDisplay = `${Math.round(percent * 100)}%`\n\n const filledWidth = Math.round(width * percent)\n const emptyWidth = width - filledWidth\n\n const bar = \"█\".repeat(filledWidth) + \"░\".repeat(emptyWidth)\n\n // Build the display parts\n const parts: string[] = []\n\n if (label) {\n parts.push(label)\n }\n\n parts.push(`[${bar}]`)\n\n if (showPercentage) {\n parts.push(percentDisplay.padStart(4))\n }\n\n if (showETA) {\n parts.push(`ETA: ${eta}`)\n }\n\n return (\n <span data-progressx-bar data-color={color} data-percent={percent}>\n {parts.join(\" \")}\n </span>\n )\n}\n\n/**\n * Hook for progress bar state management\n *\n * @example\n * ```tsx\n * function MyProgress() {\n * const { value, total, update, increment, eta, percent } = useProgressBar(100);\n *\n * useEffect(() => {\n * const timer = setInterval(() => increment(), 100);\n * return () => clearInterval(timer);\n * }, []);\n *\n * return <Text>{percent}% - ETA: {eta}</Text>;\n * }\n * ```\n */\nexport function useProgressBar(initialTotal: number) {\n const [value, setValue] = useState(0)\n const [total, setTotal] = useState(initialTotal)\n const etaBuffer = useRef<ETASample[]>([])\n const [eta, setEta] = useState<string>(\"--:--\")\n\n const update = (newValue: number) => {\n setValue(newValue)\n\n // Update ETA buffer\n const now = Date.now()\n etaBuffer.current.push({ time: now, value: newValue })\n if (etaBuffer.current.length > DEFAULT_ETA_BUFFER_SIZE) {\n etaBuffer.current.shift()\n }\n\n // Calculate ETA using shared utility\n const result = getETA(etaBuffer.current, newValue, total)\n setEta(result.formatted)\n }\n\n const increment = (amount = 1) => update(value + amount)\n\n const reset = (newTotal?: number) => {\n setValue(0)\n if (newTotal !== undefined) setTotal(newTotal)\n etaBuffer.current = []\n setEta(\"--:--\")\n }\n\n const percent = total > 0 ? Math.round((value / total) * 100) : 0\n\n return {\n value,\n total,\n percent,\n eta,\n update,\n increment,\n reset,\n setTotal,\n }\n}\n",
8
+ "/**\n * Shared ETA calculation utilities\n */\n\n/** Sample point for ETA calculation */\nexport interface ETASample {\n time: number\n value: number\n}\n\n/** ETA calculation result */\nexport interface ETAResult {\n /** Estimated seconds remaining, or null if insufficient data */\n seconds: number | null\n /** Formatted ETA string (e.g., \"1:30\", \"2:15:30\", \"--:--\") */\n formatted: string\n}\n\n/**\n * Calculate ETA from a buffer of samples\n *\n * @param buffer - Array of {time, value} samples\n * @param current - Current progress value\n * @param total - Total target value\n * @returns ETA in seconds (null if insufficient data)\n *\n * @example\n * ```ts\n * const buffer = [\n * { time: 1000, value: 0 },\n * { time: 2000, value: 10 },\n * ];\n * const eta = calculateETA(buffer, 10, 100);\n * // eta = 9 (9 seconds remaining at 10 items/sec)\n * ```\n */\nexport function calculateETA(buffer: ETASample[], current: number, total: number): number | null {\n if (buffer.length < 2) {\n return null\n }\n\n const first = buffer[0]!\n const last = buffer[buffer.length - 1]!\n\n const elapsed = (last.time - first.time) / 1000 // seconds\n const progress = last.value - first.value\n\n if (elapsed <= 0 || progress <= 0) {\n return null\n }\n\n const rate = progress / elapsed // items per second\n const remaining = total - current\n\n return remaining / rate\n}\n\n/**\n * Format ETA seconds as human-readable string\n *\n * @param eta - ETA in seconds (null for unknown)\n * @returns Formatted string (e.g., \"1:30\", \"2:15:30\", \"--:--\", \">1d\")\n *\n * @example\n * ```ts\n * formatETA(90) // \"1:30\"\n * formatETA(3665) // \"1:01:05\"\n * formatETA(null) // \"--:--\"\n * formatETA(100000) // \">1d\"\n * ```\n */\nexport function formatETA(eta: number | null): string {\n if (eta === null || !isFinite(eta)) {\n return \"--:--\"\n }\n\n if (eta > 86400) {\n // > 24 hours\n return \">1d\"\n }\n\n const hours = Math.floor(eta / 3600)\n const minutes = Math.floor((eta % 3600) / 60)\n const seconds = Math.floor(eta % 60)\n\n if (hours > 0) {\n return `${hours}:${minutes.toString().padStart(2, \"0\")}:${seconds.toString().padStart(2, \"0\")}`\n }\n\n return `${minutes}:${seconds.toString().padStart(2, \"0\")}`\n}\n\n/**\n * Calculate and format ETA in one call\n *\n * @param buffer - Array of {time, value} samples\n * @param current - Current progress value\n * @param total - Total target value\n * @returns Object with seconds (number|null) and formatted string\n */\nexport function getETA(buffer: ETASample[], current: number, total: number): ETAResult {\n const seconds = calculateETA(buffer, current, total)\n return {\n seconds,\n formatted: formatETA(seconds),\n }\n}\n\n/** Default buffer size for ETA smoothing */\nexport const DEFAULT_ETA_BUFFER_SIZE = 10\n\n/**\n * Create an ETA tracker with automatic buffer management\n *\n * @param bufferSize - Number of samples to keep (default: 10)\n * @returns ETA tracker object\n *\n * @example\n * ```ts\n * const tracker = createETATracker();\n * tracker.record(0);\n * // ... later ...\n * tracker.record(50);\n * const eta = tracker.getETA(50, 100);\n * console.log(eta.formatted); // \"0:30\"\n * ```\n */\nexport function createETATracker(bufferSize = DEFAULT_ETA_BUFFER_SIZE) {\n const buffer: ETASample[] = []\n\n return {\n /** Record a new sample */\n record(value: number): void {\n buffer.push({ time: Date.now(), value })\n if (buffer.length > bufferSize) {\n buffer.shift()\n }\n },\n\n /** Get current ETA */\n getETA(current: number, total: number): ETAResult {\n return getETA(buffer, current, total)\n },\n\n /** Reset the buffer */\n reset(): void {\n buffer.length = 0\n },\n\n /** Get buffer for external use */\n getBuffer(): readonly ETASample[] {\n return buffer\n },\n }\n}\n",
9
+ "/**\n * React Tasks component - listr2-style task list for TUI apps\n */\n\nimport React from \"react\"\nimport type { TaskProps, TaskStatus } from \"../types.js\"\nimport { useSpinnerFrame } from \"./Spinner\"\n\n/** Status icons for tasks */\nconst STATUS_ICONS: Record<TaskStatus, string> = {\n pending: \"○\",\n running: \"\", // Will use spinner\n completed: \"✔\",\n failed: \"✖\",\n skipped: \"⊘\",\n}\n\n/** Status colors */\nconst STATUS_COLORS: Record<TaskStatus, string> = {\n pending: \"gray\",\n running: \"cyan\",\n completed: \"green\",\n failed: \"red\",\n skipped: \"yellow\",\n}\n\n/**\n * Single task component\n *\n * @example\n * ```tsx\n * <Task title=\"Downloading files\" status=\"running\">\n * <ProgressBar value={50} total={100} />\n * </Task>\n * ```\n */\nexport function Task({ title, status, children }: TaskProps): React.ReactElement {\n const spinnerFrame = useSpinnerFrame(\"dots\")\n const icon = status === \"running\" ? spinnerFrame : STATUS_ICONS[status]\n const color = STATUS_COLORS[status]\n\n return (\n <div data-progressx-task data-status={status} data-color={color}>\n <span data-icon>{icon}</span>\n <span data-title> {title}</span>\n {children != null ? <div data-children>{children as React.ReactNode}</div> : null}\n </div>\n )\n}\n\n/**\n * Container for multiple tasks\n *\n * @example\n * ```tsx\n * <Tasks>\n * <Task title=\"Scanning files\" status=\"completed\" />\n * <Task title=\"Processing\" status=\"running\">\n * <ProgressBar value={current} total={total} />\n * </Task>\n * <Task title=\"Cleanup\" status=\"pending\" />\n * </Tasks>\n * ```\n */\nexport function Tasks({ children }: { children: React.ReactNode }): React.ReactElement {\n return <div data-progressx-tasks>{children}</div>\n}\n\n/**\n * Hook for managing task state\n *\n * @example\n * ```tsx\n * function MyTasks() {\n * const { tasks, start, complete, fail, updateProgress } = useTasks([\n * { id: 'scan', title: 'Scanning' },\n * { id: 'process', title: 'Processing' },\n * ]);\n *\n * useEffect(() => {\n * start('scan');\n * doScan().then(() => {\n * complete('scan');\n * start('process');\n * });\n * }, []);\n *\n * return (\n * <Tasks>\n * {tasks.map(t => <Task key={t.id} title={t.title} status={t.status} />)}\n * </Tasks>\n * );\n * }\n * ```\n */\nexport function useTasks(initialTasks: Array<{ id: string; title: string }>) {\n const [tasks, setTasks] = React.useState<\n Array<{\n id: string\n title: string\n status: TaskStatus\n progress?: { current: number; total: number }\n }>\n >(\n initialTasks.map((t) => ({\n ...t,\n status: \"pending\" as TaskStatus,\n })),\n )\n\n const updateTask = (\n id: string,\n updates: Partial<{\n status: TaskStatus\n title: string\n progress: { current: number; total: number }\n }>,\n ) => {\n setTasks((prev) => prev.map((t) => (t.id === id ? { ...t, ...updates } : t)))\n }\n\n const start = (id: string) => updateTask(id, { status: \"running\" })\n const complete = (id: string, title?: string) => updateTask(id, { status: \"completed\", ...(title && { title }) })\n const fail = (id: string, title?: string) => updateTask(id, { status: \"failed\", ...(title && { title }) })\n const skip = (id: string, title?: string) => updateTask(id, { status: \"skipped\", ...(title && { title }) })\n const updateProgress = (id: string, progress: { current: number; total: number }) => updateTask(id, { progress })\n\n const getTask = (id: string) => tasks.find((t) => t.id === id)\n const allCompleted = tasks.every((t) => t.status === \"completed\" || t.status === \"skipped\")\n const hasFailed = tasks.some((t) => t.status === \"failed\")\n\n return {\n tasks,\n start,\n complete,\n fail,\n skip,\n updateProgress,\n updateTask,\n getTask,\n allCompleted,\n hasFailed,\n }\n}\n",
10
+ "/**\n * React context for progress state management\n */\n\nimport React, { createContext, useContext, useState, useCallback } from \"react\"\nimport type { SpinnerStyle } from \"../types.js\"\nimport { Spinner } from \"./Spinner\"\n\n/** Progress context state */\ninterface ProgressContextState {\n /** Currently showing a spinner */\n isLoading: boolean\n /** Loading message */\n loadingText: string\n /** Spinner style */\n spinnerStyle: SpinnerStyle\n\n /** Show a spinner with message */\n showSpinner: (text: string, style?: SpinnerStyle) => void\n /** Hide the spinner */\n hideSpinner: () => void\n\n /** Progress bar state */\n progress: { current: number; total: number } | null\n /** Update progress */\n updateProgress: (current: number, total?: number) => void\n /** Clear progress */\n clearProgress: () => void\n}\n\nconst ProgressContext = createContext<ProgressContextState | null>(null)\n\n/**\n * Progress context provider\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <ProgressProvider>\n * <MyApp />\n * </ProgressProvider>\n * );\n * }\n *\n * function DeepComponent() {\n * const { showSpinner, hideSpinner } = useProgress();\n *\n * const handleLoad = async () => {\n * showSpinner(\"Loading...\");\n * await loadData();\n * hideSpinner();\n * };\n * }\n * ```\n */\nexport function ProgressProvider({ children }: { children: React.ReactNode }): React.ReactElement {\n const [isLoading, setIsLoading] = useState(false)\n const [loadingText, setLoadingText] = useState(\"\")\n const [spinnerStyle, setSpinnerStyle] = useState<SpinnerStyle>(\"dots\")\n const [progress, setProgress] = useState<{\n current: number\n total: number\n } | null>(null)\n\n const showSpinner = useCallback((text: string, style: SpinnerStyle = \"dots\") => {\n setLoadingText(text)\n setSpinnerStyle(style)\n setIsLoading(true)\n }, [])\n\n const hideSpinner = useCallback(() => {\n setIsLoading(false)\n setLoadingText(\"\")\n }, [])\n\n const updateProgress = useCallback((current: number, total?: number) => {\n setProgress((prev) => ({\n current,\n total: total ?? prev?.total ?? 100,\n }))\n }, [])\n\n const clearProgress = useCallback(() => {\n setProgress(null)\n }, [])\n\n const value: ProgressContextState = {\n isLoading,\n loadingText,\n spinnerStyle,\n showSpinner,\n hideSpinner,\n progress,\n updateProgress,\n clearProgress,\n }\n\n return <ProgressContext.Provider value={value}>{children}</ProgressContext.Provider>\n}\n\n/**\n * Hook to access progress context\n *\n * @example\n * ```tsx\n * function LoadingOverlay() {\n * const { isLoading, loadingText, spinnerStyle } = useProgress();\n *\n * if (!isLoading) return null;\n *\n * return <Spinner label={loadingText} style={spinnerStyle} />;\n * }\n * ```\n */\nexport function useProgress(): ProgressContextState {\n const context = useContext(ProgressContext)\n\n if (!context) {\n throw new Error(\"useProgress must be used within a ProgressProvider\")\n }\n\n return context\n}\n\n/**\n * Component that renders spinner when loading\n *\n * @example\n * ```tsx\n * <ProgressProvider>\n * <ProgressIndicator />\n * <MainContent />\n * </ProgressProvider>\n * ```\n */\nexport function ProgressIndicator(): React.ReactElement | null {\n const { isLoading, loadingText, spinnerStyle } = useProgress()\n\n if (!isLoading) {\n return null\n }\n\n return <Spinner label={loadingText} style={spinnerStyle} />\n}\n"
11
+ ],
12
+ "mappings": "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",
13
+ "debugId": "610D1483D32BB48464756E2164756E21",
14
+ "names": []
15
+ }
@@ -0,0 +1,3 @@
1
+ function calculateETA(buffer,current,total){if(buffer.length<2)return null;let first=buffer[0],last=buffer[buffer.length-1],elapsed=(last.time-first.time)/1000,progress=last.value-first.value;if(elapsed<=0||progress<=0)return null;let rate=progress/elapsed;return(total-current)/rate}function formatETA(eta){if(eta===null||!isFinite(eta))return"--:--";if(eta>86400)return">1d";let hours=Math.floor(eta/3600),minutes=Math.floor(eta%3600/60),seconds=Math.floor(eta%60);if(hours>0)return`${hours}:${minutes.toString().padStart(2,"0")}:${seconds.toString().padStart(2,"0")}`;return`${minutes}:${seconds.toString().padStart(2,"0")}`}function getETA(buffer,current,total){let seconds=calculateETA(buffer,current,total);return{seconds,formatted:formatETA(seconds)}}var DEFAULT_ETA_BUFFER_SIZE=10;function createETATracker(bufferSize=DEFAULT_ETA_BUFFER_SIZE){let buffer=[];return{record(value){if(buffer.push({time:Date.now(),value}),buffer.length>bufferSize)buffer.shift()},getETA(current,total){return getETA(buffer,current,total)},reset(){buffer.length=0},getBuffer(){return buffer}}}export{getETA,formatETA,createETATracker,calculateETA,DEFAULT_ETA_BUFFER_SIZE};
2
+
3
+ //# debugId=DF478A989C9202A464756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../packages/ag-react/src/ui/utils/eta.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * Shared ETA calculation utilities\n */\n\n/** Sample point for ETA calculation */\nexport interface ETASample {\n time: number\n value: number\n}\n\n/** ETA calculation result */\nexport interface ETAResult {\n /** Estimated seconds remaining, or null if insufficient data */\n seconds: number | null\n /** Formatted ETA string (e.g., \"1:30\", \"2:15:30\", \"--:--\") */\n formatted: string\n}\n\n/**\n * Calculate ETA from a buffer of samples\n *\n * @param buffer - Array of {time, value} samples\n * @param current - Current progress value\n * @param total - Total target value\n * @returns ETA in seconds (null if insufficient data)\n *\n * @example\n * ```ts\n * const buffer = [\n * { time: 1000, value: 0 },\n * { time: 2000, value: 10 },\n * ];\n * const eta = calculateETA(buffer, 10, 100);\n * // eta = 9 (9 seconds remaining at 10 items/sec)\n * ```\n */\nexport function calculateETA(buffer: ETASample[], current: number, total: number): number | null {\n if (buffer.length < 2) {\n return null\n }\n\n const first = buffer[0]!\n const last = buffer[buffer.length - 1]!\n\n const elapsed = (last.time - first.time) / 1000 // seconds\n const progress = last.value - first.value\n\n if (elapsed <= 0 || progress <= 0) {\n return null\n }\n\n const rate = progress / elapsed // items per second\n const remaining = total - current\n\n return remaining / rate\n}\n\n/**\n * Format ETA seconds as human-readable string\n *\n * @param eta - ETA in seconds (null for unknown)\n * @returns Formatted string (e.g., \"1:30\", \"2:15:30\", \"--:--\", \">1d\")\n *\n * @example\n * ```ts\n * formatETA(90) // \"1:30\"\n * formatETA(3665) // \"1:01:05\"\n * formatETA(null) // \"--:--\"\n * formatETA(100000) // \">1d\"\n * ```\n */\nexport function formatETA(eta: number | null): string {\n if (eta === null || !isFinite(eta)) {\n return \"--:--\"\n }\n\n if (eta > 86400) {\n // > 24 hours\n return \">1d\"\n }\n\n const hours = Math.floor(eta / 3600)\n const minutes = Math.floor((eta % 3600) / 60)\n const seconds = Math.floor(eta % 60)\n\n if (hours > 0) {\n return `${hours}:${minutes.toString().padStart(2, \"0\")}:${seconds.toString().padStart(2, \"0\")}`\n }\n\n return `${minutes}:${seconds.toString().padStart(2, \"0\")}`\n}\n\n/**\n * Calculate and format ETA in one call\n *\n * @param buffer - Array of {time, value} samples\n * @param current - Current progress value\n * @param total - Total target value\n * @returns Object with seconds (number|null) and formatted string\n */\nexport function getETA(buffer: ETASample[], current: number, total: number): ETAResult {\n const seconds = calculateETA(buffer, current, total)\n return {\n seconds,\n formatted: formatETA(seconds),\n }\n}\n\n/** Default buffer size for ETA smoothing */\nexport const DEFAULT_ETA_BUFFER_SIZE = 10\n\n/**\n * Create an ETA tracker with automatic buffer management\n *\n * @param bufferSize - Number of samples to keep (default: 10)\n * @returns ETA tracker object\n *\n * @example\n * ```ts\n * const tracker = createETATracker();\n * tracker.record(0);\n * // ... later ...\n * tracker.record(50);\n * const eta = tracker.getETA(50, 100);\n * console.log(eta.formatted); // \"0:30\"\n * ```\n */\nexport function createETATracker(bufferSize = DEFAULT_ETA_BUFFER_SIZE) {\n const buffer: ETASample[] = []\n\n return {\n /** Record a new sample */\n record(value: number): void {\n buffer.push({ time: Date.now(), value })\n if (buffer.length > bufferSize) {\n buffer.shift()\n }\n },\n\n /** Get current ETA */\n getETA(current: number, total: number): ETAResult {\n return getETA(buffer, current, total)\n },\n\n /** Reset the buffer */\n reset(): void {\n buffer.length = 0\n },\n\n /** Get buffer for external use */\n getBuffer(): readonly ETASample[] {\n return buffer\n },\n }\n}\n"
6
+ ],
7
+ "mappings": "AAoCO,SAAS,YAAY,CAAC,OAAqB,QAAiB,MAA8B,CAC/F,GAAI,OAAO,OAAS,EAClB,OAAO,KAGT,IAAM,MAAQ,OAAO,GACf,KAAO,OAAO,OAAO,OAAS,GAE9B,SAAW,KAAK,KAAO,MAAM,MAAQ,KACrC,SAAW,KAAK,MAAQ,MAAM,MAEpC,GAAI,SAAW,GAAK,UAAY,EAC9B,OAAO,KAGT,IAAM,KAAO,SAAW,QAGxB,OAFkB,MAAQ,SAEP,KAiBd,SAAS,SAAS,CAAC,IAA4B,CACpD,GAAI,MAAQ,MAAQ,CAAC,SAAS,GAAG,EAC/B,MAAO,QAGT,GAAI,IAAM,MAER,MAAO,MAGT,IAAM,MAAQ,KAAK,MAAM,IAAM,IAAI,EAC7B,QAAU,KAAK,MAAO,IAAM,KAAQ,EAAE,EACtC,QAAU,KAAK,MAAM,IAAM,EAAE,EAEnC,GAAI,MAAQ,EACV,MAAO,GAAG,SAAS,QAAQ,SAAS,EAAE,SAAS,EAAG,GAAG,KAAK,QAAQ,SAAS,EAAE,SAAS,EAAG,GAAG,IAG9F,MAAO,GAAG,WAAW,QAAQ,SAAS,EAAE,SAAS,EAAG,GAAG,IAWlD,SAAS,MAAM,CAAC,OAAqB,QAAiB,MAA0B,CACrF,IAAM,QAAU,aAAa,OAAQ,QAAS,KAAK,EACnD,MAAO,CACL,QACA,UAAW,UAAU,OAAO,CAC9B,EAIK,IAAM,wBAA0B,GAkBhC,SAAS,gBAAgB,CAAC,WAAa,wBAAyB,CACrE,IAAM,OAAsB,CAAC,EAE7B,MAAO,CAEL,MAAM,CAAC,MAAqB,CAE1B,GADA,OAAO,KAAK,CAAE,KAAM,KAAK,IAAI,EAAG,KAAM,CAAC,EACnC,OAAO,OAAS,WAClB,OAAO,MAAM,GAKjB,MAAM,CAAC,QAAiB,MAA0B,CAChD,OAAO,OAAO,OAAQ,QAAS,KAAK,GAItC,KAAK,EAAS,CACZ,OAAO,OAAS,GAIlB,SAAS,EAAyB,CAChC,OAAO,OAEX",
8
+ "debugId": "DF478A989C9202A464756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,14 @@
1
+ import chalk from"chalk";var CURSOR_HIDE="\x1B[?25l",CURSOR_SHOW="\x1B[?25h",CURSOR_TO_START="\r",CLEAR_LINE_END="\x1B[K";var cursorUp=(n=1)=>`\x1B[${n}A`;function write(text,stream=process.stdout){stream.write(text)}function isTTY(stream=process.stdout){if(process.env.FORCE_TTY==="1")return!0;return stream.isTTY??!1}function getTerminalWidth(stream=process.stdout){return stream.columns??80}var SPINNER_FRAMES={dots:["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"],line:["-","\\","|","/"],arc:["◜","◠","◝","◞","◡","◟"],bounce:["⠁","⠂","⠄","⠂"],pulse:["█","▓","▒","░","▒","▓"]},SPINNER_INTERVALS={dots:80,line:120,arc:100,bounce:120,pulse:100};class Spinner{text;style;color;stream;hideCursor;interval;frameIndex=0;timer=null;isSpinning=!1;constructor(textOrOptions){let options=typeof textOrOptions==="string"?{text:textOrOptions}:textOrOptions??{};this.text=options.text??"",this.style=options.style??"dots",this.color=options.color??"cyan",this.stream=options.stream??process.stdout,this.hideCursor=options.hideCursor??!0,this.interval=options.interval??SPINNER_INTERVALS[this.style]}get currentText(){return this.text}set currentText(value){if(this.text=value,this.isSpinning)this.render()}get spinning(){return this.isSpinning}start(text){if(text!==void 0)this.text=text;if(this.isSpinning)return this;if(this.isSpinning=!0,this.frameIndex=0,this.hideCursor&&isTTY(this.stream))write(CURSOR_HIDE,this.stream);return this.render(),this.timer=setInterval(()=>{this.frameIndex=(this.frameIndex+1)%SPINNER_FRAMES[this.style].length,this.render()},this.interval),this}stop(){if(!this.isSpinning)return this;if(this.isSpinning=!1,this.timer)clearInterval(this.timer),this.timer=null;if(this.clear(),this.hideCursor&&isTTY(this.stream))write(CURSOR_SHOW,this.stream);return this}succeed(text){return this.stopWithSymbol(chalk.green("✔"),text??this.text)}fail(text){return this.stopWithSymbol(chalk.red("✖"),text??this.text)}warn(text){return this.stopWithSymbol(chalk.yellow("⚠"),text??this.text)}info(text){return this.stopWithSymbol(chalk.blue("ℹ"),text??this.text)}clear(){if(isTTY(this.stream))write(`${CURSOR_TO_START}${CLEAR_LINE_END}`,this.stream);return this}render(){let frame=SPINNER_FRAMES[this.style][this.frameIndex],colorFn=chalk[this.color],coloredFrame=colorFn?colorFn(frame):frame,output=this.text?`${coloredFrame} ${this.text}`:coloredFrame;if(isTTY(this.stream))write(`${CURSOR_TO_START}${output}${CLEAR_LINE_END}`,this.stream)}stopWithSymbol(symbol,text){return this.stop(),write(`${symbol} ${text}
2
+ `,this.stream),this}[Symbol.dispose](){this.stop()}static start(textOrOptions){let spinner=new Spinner(textOrOptions);return spinner.start(),()=>spinner.stop()}}async function withSpinner(promise,text,options={}){let spinner=new Spinner({text:typeof text==="string"?text:text(0),style:options.style,color:options.color}),timer=null,startTime=Date.now();if(spinner.start(),typeof text==="function")timer=setInterval(()=>{let elapsed=Math.floor((Date.now()-startTime)/1000);spinner.currentText=text(elapsed)},1000);try{let result=await(typeof promise==="function"?promise():promise);if(timer)clearInterval(timer);if(options.clearOnComplete)spinner.stop();else spinner.succeed();return result}catch(error){if(timer)clearInterval(timer);throw spinner.fail(error instanceof Error?error.message:"Failed"),error}}function attachSpinner(promise,text,options={}){let spinner=new Spinner({text,style:options.style,color:options.color});spinner.start();async function wrapPromise(){try{return await promise}catch(error){throw spinner.fail(error instanceof Error?error.message:"Failed"),error}}return[wrapPromise(),spinner]}import chalk2 from"chalk";function calculateETA(buffer,current,total){if(buffer.length<2)return null;let first=buffer[0],last=buffer[buffer.length-1],elapsed=(last.time-first.time)/1000,progress=last.value-first.value;if(elapsed<=0||progress<=0)return null;let rate=progress/elapsed;return(total-current)/rate}function formatETA(eta){if(eta===null||!isFinite(eta))return"--:--";if(eta>86400)return">1d";let hours=Math.floor(eta/3600),minutes=Math.floor(eta%3600/60),seconds=Math.floor(eta%60);if(hours>0)return`${hours}:${minutes.toString().padStart(2,"0")}:${seconds.toString().padStart(2,"0")}`;return`${minutes}:${seconds.toString().padStart(2,"0")}`}var DEFAULT_ETA_BUFFER_SIZE=10;var DEFAULT_FORMAT=":bar :percent | :current/:total | ETA: :eta";class ProgressBar{total;format;width;complete;incomplete;stream;hideCursor;phases;current=0;phase=null;startTime=null;isActive=!1;etaBuffer=[];constructor(options={}){this.total=options.total??100,this.format=options.format??DEFAULT_FORMAT,this.width=options.width??40,this.complete=options.complete??"█",this.incomplete=options.incomplete??"░",this.stream=options.stream??process.stdout,this.hideCursor=options.hideCursor??!0,this.phases=options.phases??{}}start(initialValue=0,initialTotal){if(initialTotal!==void 0)this.total=initialTotal;if(this.current=initialValue,this.startTime=Date.now(),this.isActive=!0,this.etaBuffer=[{time:this.startTime,value:initialValue}],this.hideCursor&&isTTY(this.stream))write(CURSOR_HIDE,this.stream);return this.render(),this}update(value,tokens){this.current=Math.min(value,this.total);let now=Date.now();if(this.etaBuffer.push({time:now,value:this.current}),this.etaBuffer.length>DEFAULT_ETA_BUFFER_SIZE)this.etaBuffer.shift();if(this.isActive)this.render(tokens);return this}increment(amount=1,tokens){return this.update(this.current+amount,tokens)}setPhase(phaseName,options){if(this.phase=phaseName,options?.total!==void 0)this.total=options.total;if(options?.current!==void 0)this.current=options.current,this.etaBuffer=[{time:Date.now(),value:this.current}];if(this.isActive)this.render();return this}stop(clear=!1){if(!this.isActive)return this;if(this.isActive=!1,clear&&isTTY(this.stream))write(`${CURSOR_TO_START}${CLEAR_LINE_END}`,this.stream);else write(`
3
+ `,this.stream);if(this.hideCursor&&isTTY(this.stream))write(CURSOR_SHOW,this.stream);return this}getETASeconds(){return calculateETA(this.etaBuffer,this.current,this.total)}render(tokens){let percent=this.total>0?this.current/this.total:0,eta=this.getETASeconds(),completeLength=Math.round(this.width*percent),incompleteLength=this.width-completeLength,bar=this.complete.repeat(completeLength)+this.incomplete.repeat(incompleteLength),phaseDisplay=this.phase?this.phases[this.phase]??this.phase:"",elapsed=this.startTime?(Date.now()-this.startTime)/1000:0,rate=elapsed>0?this.current/elapsed:0,output=this.format.replace(":bar",chalk2.cyan(bar)).replace(":percent",`${Math.round(percent*100)}%`.padStart(4)).replace(":current",String(this.current)).replace(":total",String(this.total)).replace(":eta",formatETA(eta)).replace(":elapsed",formatETA(elapsed)).replace(":rate",rate.toFixed(1)).replace(":phase",chalk2.dim(phaseDisplay));if(tokens)for(let[key,value]of Object.entries(tokens))output=output.replace(`:${key}`,String(value));let termWidth=getTerminalWidth(this.stream);if(output.length>termWidth)output=output.slice(0,termWidth-1);if(isTTY(this.stream))write(`${CURSOR_TO_START}${output}${CLEAR_LINE_END}`,this.stream)}get ratio(){return this.total>0?this.current/this.total:0}get percentage(){return Math.round(this.ratio*100)}[Symbol.dispose](){this.stop()}}async function withProgress(fn,options={}){let stream=process.stdout,isTty=isTTY(stream),format=options.format??(options.phases?":phase [:bar] :current/:total":"[:bar] :current/:total :percent"),bar=new ProgressBar({format,phases:options.phases??{},hideCursor:!0}),lastPhase=null,started=!1,showAfter=options.showAfter??1000,initialMessage=options.initialMessage??"Loading...",spinner=null,spinnerTimerId=null;if(isTty)write(CURSOR_HIDE,stream);if(isTty&&showAfter>=0)spinnerTimerId=setTimeout(()=>{if(!started)spinner=new Spinner({text:initialMessage}),spinner.start()},showAfter);let onProgress=(info)=>{if(spinner)spinner.stop(),spinner=null;if(spinnerTimerId!==null)clearTimeout(spinnerTimerId),spinnerTimerId=null;if(info.phase&&info.phase!==lastPhase){if(lastPhase!==null&&isTty)write(`
4
+ `,stream);if(lastPhase=info.phase,!started)bar.start(info.current,info.total),started=!0;bar.setPhase(info.phase,{current:info.current,total:info.total})}else{if(!started)bar.start(info.current,info.total),started=!0;bar.update(info.current)}};try{let result=await fn(onProgress);if(spinnerTimerId!==null)clearTimeout(spinnerTimerId);let pendingSpinner=spinner;if(pendingSpinner)pendingSpinner.stop();if(started)bar.stop(options.clearOnComplete);if(isTty)write(CURSOR_SHOW,stream);return result}catch(error){if(spinnerTimerId!==null)clearTimeout(spinnerTimerId);let errorSpinner=spinner;if(errorSpinner)errorSpinner.stop();if(started)bar.stop();if(isTty)write(CURSOR_SHOW,stream);throw error}}function createProgressCallback(options={}){let stream=process.stdout,isTty=isTTY(stream),format=options.format??(options.phases?":phase [:bar] :current/:total":"[:bar] :current/:total :percent"),bar=new ProgressBar({format,phases:options.phases??{},hideCursor:!0}),lastPhase=null,started=!1;if(isTty)write(CURSOR_HIDE,stream);return[(info)=>{if(info.phase&&info.phase!==lastPhase){if(lastPhase!==null&&isTty)write(`
5
+ `,stream);if(lastPhase=info.phase,!started)bar.start(info.current,info.total),started=!0;bar.setPhase(info.phase,{current:info.current,total:info.total})}else{if(!started)bar.start(info.current,info.total),started=!0;bar.update(info.current)}},()=>{if(started)bar.stop(options.clearOnComplete);if(isTty)write(CURSOR_SHOW,stream)}]}async function wrapGenerator(generator,textOrFormat,options={}){let stream=process.stdout,isTty=isTTY(stream),label=typeof textOrFormat==="function"?"":textOrFormat,bar=new ProgressBar({format:label?`${label} [:bar] :current/:total :percent`:":bar :current/:total :percent",hideCursor:!0});if(isTty)write(CURSOR_HIDE,stream);let started=!1,result;try{while(!0){if(result=generator.next(),result.done)break;let{current,total}=result.value;if(!started)bar.start(current,total),started=!0;else bar.update(current)}if(started)bar.stop(options.clearOnComplete);if(isTty)write(CURSOR_SHOW,stream);return result.value}catch(error){if(started)bar.stop();if(isTty)write(CURSOR_SHOW,stream);throw error}}async function*withIterableProgress(iterable,label,options={}){let stream=process.stdout,isTty=isTTY(stream),total=(Array.isArray(iterable)?iterable:null)?.length??0,bar=new ProgressBar({format:`${label} [:bar] :current/:total :percent`,total,hideCursor:!0});if(isTty)write(CURSOR_HIDE,stream);let current=0;bar.start(0,total);try{for await(let item of iterable)yield item,current++,bar.update(current);if(bar.stop(options.clearOnComplete),isTty)write(CURSOR_SHOW,stream)}catch(error){if(bar.stop(),isTty)write(CURSOR_SHOW,stream);throw error}}function wrapEmitter(emitter,config){let spinner=new Spinner(config.initialText??""),handlers=new Map;spinner.start();for(let[eventName,eventConfig]of Object.entries(config.events)){let handler=(data)=>{if(eventConfig.getText)spinner.currentText=eventConfig.getText(data);else if(eventConfig.text)spinner.currentText=eventConfig.text;if(eventConfig.succeed)spinner.succeed(),cleanup();else if(eventConfig.fail){let message=data instanceof Error?data.message:String(data??"Failed");spinner.fail(message),cleanup()}else if(eventConfig.stop)spinner.stop(),cleanup()};handlers.set(eventName,handler),emitter.on(eventName,handler)}function cleanup(){for(let[eventName,handler]of handlers)emitter.off(eventName,handler);handlers.clear()}return()=>{spinner.stop(),cleanup()}}async function waitForEvent(emitter,eventName,text,options={}){return new Promise((resolve,reject)=>{let spinner=new Spinner(text);spinner.start();let timer=null,cleanup=()=>{if(emitter.off(eventName,successHandler),options.errorEvent)emitter.off(options.errorEvent,errorHandler);if(timer)clearTimeout(timer)},successHandler=(data)=>{cleanup(),spinner.succeed(),resolve(data)},errorHandler=(error)=>{cleanup(),spinner.fail(error instanceof Error?error.message:"Error"),reject(error instanceof Error?error:Error(String(error)))};if(emitter.once(eventName,successHandler),options.errorEvent)emitter.once(options.errorEvent,errorHandler);if(options.timeout)timer=setTimeout(()=>{cleanup(),spinner.fail("Timeout"),reject(Error(`Timeout waiting for ${eventName}`))},options.timeout)})}import chalk3 from"chalk";async function withSelect(prompt,options,selectOptions={}){let{initial=0,maxVisible=10}=selectOptions,stream=process.stdout,stdin=process.stdin;if(!isTTY(stream)||!stdin.isTTY)return options[initial]?.value??options[0].value;return new Promise((resolve,reject)=>{let highlightIndex=Math.min(Math.max(0,initial),options.length-1),linesRendered=0;stdin.setRawMode(!0),stdin.resume(),stdin.setEncoding("utf8"),write(CURSOR_HIDE,stream);function render(){if(linesRendered>0)write(cursorUp(linesRendered),stream);let scrollOffset=Math.max(0,Math.min(highlightIndex-Math.floor(maxVisible/2),options.length-maxVisible)),visibleCount=Math.min(maxVisible,options.length),visibleOptions=options.slice(scrollOffset,scrollOffset+visibleCount),hasMoreAbove=scrollOffset>0,hasMoreBelow=scrollOffset+visibleCount<options.length;write(`${CURSOR_TO_START}${chalk3.bold(prompt)}${CLEAR_LINE_END}
6
+ `,stream);let lines=1;if(hasMoreAbove)write(`${CURSOR_TO_START} ${chalk3.dim("...")}${CLEAR_LINE_END}
7
+ `,stream),lines++;for(let i=0;i<visibleOptions.length;i++){let option=visibleOptions[i],isHighlighted=scrollOffset+i===highlightIndex,indicator=isHighlighted?chalk3.cyan(">"):" ",label=isHighlighted?chalk3.cyan(option.label):option.label;write(`${CURSOR_TO_START}${indicator} ${label}${CLEAR_LINE_END}
8
+ `,stream),lines++}if(hasMoreBelow)write(`${CURSOR_TO_START} ${chalk3.dim("...")}${CLEAR_LINE_END}
9
+ `,stream),lines++;linesRendered=lines}function cleanup(){stdin.setRawMode(!1),stdin.pause(),stdin.removeListener("data",onKeypress),write(CURSOR_SHOW,stream)}function onKeypress(key){let keyCode=key.charCodeAt(0);if(key==="\x03"){cleanup(),reject(Error("User cancelled"));return}if(key==="\r"||key===`
10
+ `){cleanup(),resolve(options[highlightIndex].value);return}if(key==="\x1B"&&key.length===1){cleanup(),reject(Error("User cancelled"));return}if(key.startsWith("\x1B[")){let code=key.slice(2);if(code==="A")highlightIndex=Math.max(0,highlightIndex-1),render();else if(code==="B")highlightIndex=Math.min(options.length-1,highlightIndex+1),render();return}if(key==="j"||key==="J"){highlightIndex=Math.min(options.length-1,highlightIndex+1),render();return}if(key==="k"||key==="K"){highlightIndex=Math.max(0,highlightIndex-1),render();return}if(key===" "){cleanup(),resolve(options[highlightIndex].value);return}}stdin.on("data",onKeypress),render()})}function createSelect(defaultOptions={}){return(prompt,options,overrides={})=>withSelect(prompt,options,{...defaultOptions,...overrides})}import chalk4 from"chalk";async function withTextInput(prompt,options={}){let stream=options.stream??process.stdout,inputStream=options.inputStream??process.stdin,isTty=isTTY(stream),value=options.defaultValue??"",cursorPosition=value.length,errorMessage;if(inputStream.isTTY)inputStream.setRawMode(!0);inputStream.resume();let render=()=>{let displayValue=options.mask?options.mask.repeat(value.length):value,suggestion=getAutocompleteSuggestion(value,options.autocomplete),suggestionSuffix=suggestion?chalk4.dim(suggestion.slice(value.length)):"",beforeCursor=displayValue.slice(0,cursorPosition),cursorChar=displayValue[cursorPosition]??" ",afterCursor=displayValue.slice(cursorPosition+1),inputDisplay=!value&&options.placeholder?chalk4.dim(options.placeholder)+chalk4.inverse(" "):beforeCursor+chalk4.inverse(cursorChar)+afterCursor+suggestionSuffix,errorDisplay=errorMessage?chalk4.red(` (${errorMessage})`):"",line=`${chalk4.cyan("?")} ${chalk4.bold(prompt)} ${inputDisplay}${errorDisplay}`;if(isTty)write(`${CURSOR_TO_START}${line}${CLEAR_LINE_END}`,stream)};if(isTty)write(CURSOR_HIDE,stream);return render(),new Promise((resolve,reject)=>{let cleanup=()=>{if(inputStream.removeListener("data",onData),inputStream.removeListener("error",onError),inputStream.isTTY)inputStream.setRawMode(!1);if(inputStream.pause(),isTty)write(CURSOR_SHOW,stream)},submit=()=>{if(options.validate){let error=options.validate(value);if(error){errorMessage=error,render();return}}cleanup();let displayValue=options.mask?options.mask.repeat(value.length):value;write(`${CURSOR_TO_START}${chalk4.green("✔")} ${chalk4.bold(prompt)} ${chalk4.dim(displayValue)}${CLEAR_LINE_END}
11
+ `,stream),resolve(value)},onError=(err)=>{cleanup(),reject(err)},onData=(data)=>{let input=data.toString();errorMessage=void 0;for(let i=0;i<input.length;i++){let char=input[i],code=char.charCodeAt(0);if(code===13||code===10){submit();return}if(code===3){cleanup(),write(`
12
+ `,stream),reject(Error("User aborted"));return}if(code===27){if(input[i+1]==="["){let arrowCode=input[i+2];if(arrowCode==="D"){cursorPosition=Math.max(0,cursorPosition-1),i+=2;continue}if(arrowCode==="C"){cursorPosition=Math.min(value.length,cursorPosition+1),i+=2;continue}if(arrowCode==="H"){cursorPosition=0,i+=2;continue}if(arrowCode==="F"){cursorPosition=value.length,i+=2;continue}i+=2;continue}value="",cursorPosition=0;continue}if(code===127||code===8){if(cursorPosition>0)value=value.slice(0,cursorPosition-1)+value.slice(cursorPosition),cursorPosition--;continue}if(code===4){if(cursorPosition<value.length)value=value.slice(0,cursorPosition)+value.slice(cursorPosition+1);continue}if(code===9){let suggestion=getAutocompleteSuggestion(value,options.autocomplete);if(suggestion)value=suggestion,cursorPosition=value.length;continue}if(code===1){cursorPosition=0;continue}if(code===5){cursorPosition=value.length;continue}if(code===21){value=value.slice(cursorPosition),cursorPosition=0;continue}if(code===11){value=value.slice(0,cursorPosition);continue}if(code===23){let before=value.slice(0,cursorPosition),after=value.slice(cursorPosition),trimmed=before.trimEnd(),lastSpace=trimmed.lastIndexOf(" "),newBefore=lastSpace===-1?"":trimmed.slice(0,lastSpace+1);value=newBefore+after,cursorPosition=newBefore.length;continue}if(code>=32&&code<127){value=value.slice(0,cursorPosition)+char+value.slice(cursorPosition),cursorPosition++;continue}if(code>127){value=value.slice(0,cursorPosition)+char+value.slice(cursorPosition),cursorPosition++;continue}}render()};inputStream.on("data",onData),inputStream.on("error",onError)})}function createTextInput(prompt,options={}){let stream=options.stream??process.stdout,isTty=isTTY(stream),value=options.defaultValue??"",cursorPosition=value.length;return{get value(){return value},set value(v){value=v,cursorPosition=Math.min(cursorPosition,v.length)},get cursorPosition(){return cursorPosition},set cursorPosition(pos){cursorPosition=Math.max(0,Math.min(value.length,pos))},render:()=>{let displayValue=options.mask?options.mask.repeat(value.length):value,suggestion=getAutocompleteSuggestion(value,options.autocomplete),suggestionSuffix=suggestion?chalk4.dim(suggestion.slice(value.length)):"",beforeCursor=displayValue.slice(0,cursorPosition),cursorChar=displayValue[cursorPosition]??" ",afterCursor=displayValue.slice(cursorPosition+1),inputDisplay=!value&&options.placeholder?chalk4.dim(options.placeholder)+chalk4.inverse(" "):beforeCursor+chalk4.inverse(cursorChar)+afterCursor+suggestionSuffix,line=`${chalk4.cyan("?")} ${chalk4.bold(prompt)} ${inputDisplay}`;if(isTty)write(`${CURSOR_TO_START}${line}${CLEAR_LINE_END}`,stream)},insert(char){value=value.slice(0,cursorPosition)+char+value.slice(cursorPosition),cursorPosition+=char.length},backspace(){if(cursorPosition>0)value=value.slice(0,cursorPosition-1)+value.slice(cursorPosition),cursorPosition--},delete(){if(cursorPosition<value.length)value=value.slice(0,cursorPosition)+value.slice(cursorPosition+1)},clear(){value="",cursorPosition=0},acceptSuggestion(){let suggestion=getAutocompleteSuggestion(value,options.autocomplete);if(suggestion)value=suggestion,cursorPosition=value.length}}}function getAutocompleteSuggestion(value,autocomplete){if(!value||!autocomplete?.length)return;let lowerValue=value.toLowerCase();return autocomplete.find((item)=>item.toLowerCase().startsWith(lowerValue)&&item.length>value.length)}export{wrapGenerator,wrapEmitter,withTextInput,withSpinner,withSelect,withProgress,withIterableProgress,waitForEvent,createTextInput,createSelect,createProgressCallback,attachSpinner};
13
+
14
+ //# debugId=E1BC722563022ECD64756E2164756E21