@agimon-ai/doompi-user-feedback 0.0.1-alpha.47 → 0.0.1-alpha.53

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.
@@ -2,7 +2,7 @@ import "@deepseek-ai/cordis";
2
2
  import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
3
  //#region src/adapters/pi/extension.d.ts
4
4
  /** The package's sole Pi factory; Pi reloads it and Cordis owns all package resources. */
5
- declare function userFeedbackExtension(pi: ExtensionAPI): Promise<void>;
5
+ export declare function userFeedbackExtension(pi: ExtensionAPI): Promise<void>;
6
6
  //#endregion
7
- export { userFeedbackExtension as default, userFeedbackExtension };
7
+ export { userFeedbackExtension as default };
8
8
  //# sourceMappingURL=extension.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;iBA+JsB,sBAAsB,IAAI,eAAe"}
1
+ {"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;wBA+JsB,sBAAsB,IAAI,eAAe"}
@@ -2,7 +2,7 @@ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
  import "@deepseek-ai/cordis";
3
3
  //#region src/adapters/pi/extension.d.ts
4
4
  /** The package's sole Pi factory; Pi reloads it and Cordis owns all package resources. */
5
- declare function userFeedbackExtension(pi: ExtensionAPI): Promise<void>;
5
+ export declare function userFeedbackExtension(pi: ExtensionAPI): Promise<void>;
6
6
  //#endregion
7
- export { userFeedbackExtension as default, userFeedbackExtension };
7
+ export { userFeedbackExtension as default };
8
8
  //# sourceMappingURL=extension.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;iBA+JsB,sBAAsB,IAAI,eAAe"}
1
+ {"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;wBA+JsB,sBAAsB,IAAI,eAAe"}
@@ -1,6 +1,6 @@
1
1
  import { Static, Type } from "typebox";
2
2
  //#region src/schemas/questionnaire.d.ts
3
- declare const QuestionParamsSchema: Type.TObject<{
3
+ export declare const QuestionParamsSchema: Type.TObject<{
4
4
  questions: Type.TArray<Type.TObject<{
5
5
  question: Type.TString;
6
6
  header: Type.TString;
@@ -12,7 +12,6 @@ declare const QuestionParamsSchema: Type.TObject<{
12
12
  multiSelect: Type.TOptional<Type.TBoolean>;
13
13
  }>>;
14
14
  }>;
15
- type QuestionParams = Static<typeof QuestionParamsSchema>;
15
+ export type QuestionParams = Static<typeof QuestionParamsSchema>;
16
16
  //#endregion
17
- export { QuestionParams, QuestionParamsSchema };
18
17
  //# sourceMappingURL=questionnaire.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"questionnaire.d.cts","names":[],"sources":["../../src/schemas/questionnaire.ts"],"mappings":";;cA6Ca,sBAAoB,KAAA;;;;;;;;;;;;KAarB,iBAAiB,cAAc"}
1
+ {"version":3,"file":"questionnaire.d.cts","names":[],"sources":["../../src/schemas/questionnaire.ts"],"mappings":";;qBA6Ca,sBAAoB,KAAA;;;;;;;;;;;;YAarB,iBAAiB,cAAc"}
@@ -1,6 +1,6 @@
1
1
  import { Static, Type } from "typebox";
2
2
  //#region src/schemas/questionnaire.d.ts
3
- declare const QuestionParamsSchema: Type.TObject<{
3
+ export declare const QuestionParamsSchema: Type.TObject<{
4
4
  questions: Type.TArray<Type.TObject<{
5
5
  question: Type.TString;
6
6
  header: Type.TString;
@@ -12,7 +12,6 @@ declare const QuestionParamsSchema: Type.TObject<{
12
12
  multiSelect: Type.TOptional<Type.TBoolean>;
13
13
  }>>;
14
14
  }>;
15
- type QuestionParams = Static<typeof QuestionParamsSchema>;
15
+ export type QuestionParams = Static<typeof QuestionParamsSchema>;
16
16
  //#endregion
17
- export { QuestionParams, QuestionParamsSchema };
18
17
  //# sourceMappingURL=questionnaire.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"questionnaire.d.mts","names":[],"sources":["../../src/schemas/questionnaire.ts"],"mappings":";;cA6Ca,sBAAoB,KAAA;;;;;;;;;;;;KAarB,iBAAiB,cAAc"}
1
+ {"version":3,"file":"questionnaire.d.mts","names":[],"sources":["../../src/schemas/questionnaire.ts"],"mappings":";;qBA6Ca,sBAAoB,KAAA;;;;;;;;;;;;YAarB,iBAAiB,cAAc"}
@@ -1,2 +1,2 @@
1
- function e(e){return{answers:e.latestAnswers.map(e=>({...e})),cancelled:!0}}var t=class{pending=[];running=new Set;active;nextRequestId=1;accepting=!0;enqueue(e,t){return!this.accepting||t?.aborted?Promise.resolve({answers:[],cancelled:!0}):new Promise((n,r)=>{let i={id:this.nextRequestId,runner:e,controller:new AbortController,...t?{externalSignal:t}:{},latestAnswers:[],state:`queued`,resolve:n,reject:r};this.nextRequestId+=1;let a=()=>this.cancel(i);t?.addEventListener(`abort`,a,{once:!0}),i.removeAbortListener=()=>t?.removeEventListener(`abort`,a),this.pending.push(i),this.startNext()})}shutdown(){if(!(!this.accepting&&!this.active&&this.pending.length===0))for(this.accepting=!1,this.active&&this.cancel(this.active);this.pending.length>0;){let e=this.pending[0];if(!e)break;this.cancel(e)}}async waitForIdle(){for(;this.running.size>0;)await Promise.allSettled(this.running)}startNext(){if(!this.accepting||this.active)return;let e=this.pending.shift();if(!e)return;e.state=`active`,this.active=e;let t=t=>{this.active?.id===e.id&&e.state===`active`&&(e.latestAnswers=t.answers.map(e=>({...e})))},n;try{n=e.runner({signal:e.controller.signal,reportProgress:t})}catch(e){n=Promise.reject(e)}this.running.add(n),n.then(t=>this.complete(e,t),t=>this.fail(e,t)).finally(()=>this.running.delete(n))}complete(e,t){this.active?.id===e.id&&e.state===`active`&&(this.releaseActive(e),e.resolve(t),this.startNext())}fail(e,t){this.active?.id===e.id&&e.state===`active`&&(this.releaseActive(e),e.reject(t),this.startNext())}cancel(t){if(t.state===`settled`)return;if(t.state===`queued`){let n=this.pending.indexOf(t);this.pending.splice(n,1),t.state=`settled`,t.removeAbortListener?.(),t.controller.abort(),t.resolve(e(t));return}let n=e(t);this.releaseActive(t),t.controller.abort(),t.resolve(n),this.startNext()}releaseActive(e){e.state=`settled`,e.removeAbortListener?.(),this.active=void 0}};exports.QuestionnaireCoordinator=t;
1
+ function e(e){return{answers:e.latestAnswers.map(e=>({...e})),cancelled:!0}}var t=class{pending=[];running=new Set;active;nextRequestId=1;accepting=!0;enqueue(e,t){return!this.accepting||t?.aborted?Promise.resolve({answers:[],cancelled:!0}):new Promise((n,r)=>{let i={id:this.nextRequestId,runner:e,controller:new AbortController,...t?{externalSignal:t}:{},latestAnswers:[],state:`queued`,resolve:n,reject:r};this.nextRequestId+=1;let a=()=>this.cancel(i);t?.addEventListener(`abort`,a,{once:!0}),i.removeAbortListener=()=>t?.removeEventListener(`abort`,a),this.pending.push(i),this.startNext()})}shutdown(){if(this.accepting||this.active||this.pending.length!==0)for(this.accepting=!1,this.active&&this.cancel(this.active);this.pending.length>0;){let e=this.pending[0];if(!e)break;this.cancel(e)}}async waitForIdle(){for(;this.running.size>0;)await Promise.allSettled(this.running)}startNext(){if(!this.accepting||this.active)return;let e=this.pending.shift();if(!e)return;e.state=`active`,this.active=e;let t=t=>{this.active?.id===e.id&&e.state===`active`&&(e.latestAnswers=t.answers.map(e=>({...e})))},n;try{n=e.runner({signal:e.controller.signal,reportProgress:t})}catch(e){n=Promise.reject(e)}this.running.add(n),n.then(t=>this.complete(e,t),t=>this.fail(e,t)).finally(()=>this.running.delete(n))}complete(e,t){this.active?.id===e.id&&e.state===`active`&&(this.releaseActive(e),e.resolve(t),this.startNext())}fail(e,t){this.active?.id===e.id&&e.state===`active`&&(this.releaseActive(e),e.reject(t),this.startNext())}cancel(t){if(t.state===`settled`)return;if(t.state===`queued`){let n=this.pending.indexOf(t);this.pending.splice(n,1),t.state=`settled`,t.removeAbortListener?.(),t.controller.abort(),t.resolve(e(t));return}let n=e(t);this.releaseActive(t),t.controller.abort(),t.resolve(n),this.startNext()}releaseActive(e){e.state=`settled`,e.removeAbortListener?.(),this.active=void 0}};exports.QuestionnaireCoordinator=t;
2
2
  //# sourceMappingURL=questionnaireCoordinator.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"questionnaireCoordinator.cjs","names":[],"sources":["../../src/services/questionnaireCoordinator.ts"],"sourcesContent":["import type { QuestionnaireResult } from '../types/questionnaire.js';\n\nexport interface QuestionnaireRunContext {\n signal: AbortSignal;\n reportProgress: (result: QuestionnaireResult) => void;\n}\n\nexport type QuestionnaireRunner = (context: QuestionnaireRunContext) => Promise<QuestionnaireResult>;\n\ninterface QueueEntry {\n id: number;\n runner: QuestionnaireRunner;\n controller: AbortController;\n externalSignal?: AbortSignal;\n removeAbortListener?: () => void;\n latestAnswers: QuestionnaireResult['answers'];\n state: 'queued' | 'active' | 'settled';\n resolve(result: QuestionnaireResult): void;\n reject(error: unknown): void;\n}\n\nfunction cancelledResult(entry: QueueEntry): QuestionnaireResult {\n return {\n answers: entry.latestAnswers.map((answer) => ({ ...answer })),\n cancelled: true,\n };\n}\n\nexport class QuestionnaireCoordinator {\n private readonly pending: QueueEntry[] = [];\n private readonly running = new Set<Promise<QuestionnaireResult>>();\n private active: QueueEntry | undefined;\n private nextRequestId = 1;\n private accepting = true;\n\n enqueue(runner: QuestionnaireRunner, externalSignal?: AbortSignal): Promise<QuestionnaireResult> {\n if (!this.accepting || externalSignal?.aborted) {\n return Promise.resolve({ answers: [], cancelled: true });\n }\n\n return new Promise<QuestionnaireResult>((resolve, reject) => {\n const entry: QueueEntry = {\n id: this.nextRequestId,\n runner,\n controller: new AbortController(),\n ...(externalSignal ? { externalSignal } : {}),\n latestAnswers: [],\n state: 'queued',\n resolve,\n reject,\n };\n this.nextRequestId += 1;\n const abort = (): void => this.cancel(entry);\n externalSignal?.addEventListener('abort', abort, { once: true });\n entry.removeAbortListener = () => externalSignal?.removeEventListener('abort', abort);\n this.pending.push(entry);\n this.startNext();\n });\n }\n\n shutdown(): void {\n if (!this.accepting && !this.active && this.pending.length === 0) return;\n this.accepting = false;\n if (this.active) this.cancel(this.active);\n while (this.pending.length > 0) {\n const entry = this.pending[0];\n if (!entry) break;\n this.cancel(entry);\n }\n }\n\n /** Waits for every started presenter to observe cancellation and finish. */\n async waitForIdle(): Promise<void> {\n while (this.running.size > 0) await Promise.allSettled(this.running);\n }\n\n private startNext(): void {\n if (!this.accepting || this.active) return;\n const entry = this.pending.shift();\n if (!entry) return;\n entry.state = 'active';\n this.active = entry;\n const reportProgress = (result: QuestionnaireResult): void => {\n if (this.active?.id !== entry.id || entry.state !== 'active') return;\n entry.latestAnswers = result.answers.map((answer) => ({ ...answer }));\n };\n let operation: Promise<QuestionnaireResult>;\n try {\n operation = entry.runner({ signal: entry.controller.signal, reportProgress });\n } catch (error) {\n operation = Promise.reject(error);\n }\n this.running.add(operation);\n void operation\n .then(\n (result) => this.complete(entry, result),\n (error: unknown) => this.fail(entry, error),\n )\n .finally(() => this.running.delete(operation));\n }\n\n private complete(entry: QueueEntry, result: QuestionnaireResult): void {\n if (this.active?.id !== entry.id || entry.state !== 'active') return;\n this.releaseActive(entry);\n entry.resolve(result);\n this.startNext();\n }\n\n private fail(entry: QueueEntry, error: unknown): void {\n if (this.active?.id !== entry.id || entry.state !== 'active') return;\n this.releaseActive(entry);\n entry.reject(error);\n this.startNext();\n }\n\n private cancel(entry: QueueEntry): void {\n if (entry.state === 'settled') return;\n if (entry.state === 'queued') {\n const index = this.pending.indexOf(entry);\n this.pending.splice(index, 1);\n entry.state = 'settled';\n entry.removeAbortListener?.();\n entry.controller.abort();\n entry.resolve(cancelledResult(entry));\n return;\n }\n\n const result = cancelledResult(entry);\n this.releaseActive(entry);\n entry.controller.abort();\n entry.resolve(result);\n this.startNext();\n }\n\n private releaseActive(entry: QueueEntry): void {\n entry.state = 'settled';\n entry.removeAbortListener?.();\n this.active = undefined;\n }\n}\n"],"mappings":"AAqBA,SAAS,EAAgB,EAAwC,CAC/D,MAAO,CACL,QAAS,EAAM,cAAc,IAAK,IAAY,CAAE,GAAG,CAAO,EAAE,EAC5D,UAAW,EACb,CACF,CAEA,IAAa,EAAb,KAAsC,CACpC,QAAyC,CAAC,EAC1C,QAA2B,IAAI,IAC/B,OACA,cAAwB,EACxB,UAAoB,GAEpB,QAAQ,EAA6B,EAA4D,CAK/F,MAJI,CAAC,KAAK,WAAa,GAAgB,QAC9B,QAAQ,QAAQ,CAAE,QAAS,CAAC,EAAG,UAAW,EAAK,CAAC,EAGlD,IAAI,SAA8B,EAAS,IAAW,CAC3D,IAAM,EAAoB,CACxB,GAAI,KAAK,cACT,SACA,WAAY,IAAI,gBAChB,GAAI,EAAiB,CAAE,gBAAe,EAAI,CAAC,EAC3C,cAAe,CAAC,EAChB,MAAO,SACP,UACA,QACF,EACA,KAAK,eAAiB,EACtB,IAAM,MAAoB,KAAK,OAAO,CAAK,EAC3C,GAAgB,iBAAiB,QAAS,EAAO,CAAE,KAAM,EAAK,CAAC,EAC/D,EAAM,wBAA4B,GAAgB,oBAAoB,QAAS,CAAK,EACpF,KAAK,QAAQ,KAAK,CAAK,EACvB,KAAK,UAAU,CACjB,CAAC,CACH,CAEA,UAAiB,CACX,MAAC,KAAK,WAAa,CAAC,KAAK,QAAU,KAAK,QAAQ,SAAW,GAG/D,IAFA,KAAK,UAAY,GACb,KAAK,QAAQ,KAAK,OAAO,KAAK,MAAM,EACjC,KAAK,QAAQ,OAAS,GAAG,CAC9B,IAAM,EAAQ,KAAK,QAAQ,GAC3B,GAAI,CAAC,EAAO,MACZ,KAAK,OAAO,CAAK,CACnB,CACF,CAGA,MAAM,aAA6B,CACjC,KAAO,KAAK,QAAQ,KAAO,GAAG,MAAM,QAAQ,WAAW,KAAK,OAAO,CACrE,CAEA,WAA0B,CACxB,GAAI,CAAC,KAAK,WAAa,KAAK,OAAQ,OACpC,IAAM,EAAQ,KAAK,QAAQ,MAAM,EACjC,GAAI,CAAC,EAAO,OACZ,EAAM,MAAQ,SACd,KAAK,OAAS,EACd,IAAM,EAAkB,GAAsC,CACxD,KAAK,QAAQ,KAAO,EAAM,IAAM,EAAM,QAAU,WACpD,EAAM,cAAgB,EAAO,QAAQ,IAAK,IAAY,CAAE,GAAG,CAAO,EAAE,EACtE,EACI,EACJ,GAAI,CACF,EAAY,EAAM,OAAO,CAAE,OAAQ,EAAM,WAAW,OAAQ,gBAAe,CAAC,CAC9E,OAAS,EAAO,CACd,EAAY,QAAQ,OAAO,CAAK,CAClC,CACA,KAAK,QAAQ,IAAI,CAAS,EAC1B,EACG,KACE,GAAW,KAAK,SAAS,EAAO,CAAM,EACtC,GAAmB,KAAK,KAAK,EAAO,CAAK,CAC5C,CAAC,CACA,YAAc,KAAK,QAAQ,OAAO,CAAS,CAAC,CACjD,CAEA,SAAiB,EAAmB,EAAmC,CACjE,KAAK,QAAQ,KAAO,EAAM,IAAM,EAAM,QAAU,WACpD,KAAK,cAAc,CAAK,EACxB,EAAM,QAAQ,CAAM,EACpB,KAAK,UAAU,EACjB,CAEA,KAAa,EAAmB,EAAsB,CAChD,KAAK,QAAQ,KAAO,EAAM,IAAM,EAAM,QAAU,WACpD,KAAK,cAAc,CAAK,EACxB,EAAM,OAAO,CAAK,EAClB,KAAK,UAAU,EACjB,CAEA,OAAe,EAAyB,CACtC,GAAI,EAAM,QAAU,UAAW,OAC/B,GAAI,EAAM,QAAU,SAAU,CAC5B,IAAM,EAAQ,KAAK,QAAQ,QAAQ,CAAK,EACxC,KAAK,QAAQ,OAAO,EAAO,CAAC,EAC5B,EAAM,MAAQ,UACd,EAAM,sBAAsB,EAC5B,EAAM,WAAW,MAAM,EACvB,EAAM,QAAQ,EAAgB,CAAK,CAAC,EACpC,MACF,CAEA,IAAM,EAAS,EAAgB,CAAK,EACpC,KAAK,cAAc,CAAK,EACxB,EAAM,WAAW,MAAM,EACvB,EAAM,QAAQ,CAAM,EACpB,KAAK,UAAU,CACjB,CAEA,cAAsB,EAAyB,CAC7C,EAAM,MAAQ,UACd,EAAM,sBAAsB,EAC5B,KAAK,OAAS,IAAA,EAChB,CACF"}
1
+ {"version":3,"file":"questionnaireCoordinator.cjs","names":[],"sources":["../../src/services/questionnaireCoordinator.ts"],"sourcesContent":["import type { QuestionnaireResult } from '../types/questionnaire.js';\n\nexport interface QuestionnaireRunContext {\n signal: AbortSignal;\n reportProgress: (result: QuestionnaireResult) => void;\n}\n\nexport type QuestionnaireRunner = (context: QuestionnaireRunContext) => Promise<QuestionnaireResult>;\n\ninterface QueueEntry {\n id: number;\n runner: QuestionnaireRunner;\n controller: AbortController;\n externalSignal?: AbortSignal;\n removeAbortListener?: () => void;\n latestAnswers: QuestionnaireResult['answers'];\n state: 'queued' | 'active' | 'settled';\n resolve(result: QuestionnaireResult): void;\n reject(error: unknown): void;\n}\n\nfunction cancelledResult(entry: QueueEntry): QuestionnaireResult {\n return {\n answers: entry.latestAnswers.map((answer) => ({ ...answer })),\n cancelled: true,\n };\n}\n\nexport class QuestionnaireCoordinator {\n private readonly pending: QueueEntry[] = [];\n private readonly running = new Set<Promise<QuestionnaireResult>>();\n private active: QueueEntry | undefined;\n private nextRequestId = 1;\n private accepting = true;\n\n enqueue(runner: QuestionnaireRunner, externalSignal?: AbortSignal): Promise<QuestionnaireResult> {\n if (!this.accepting || externalSignal?.aborted) {\n return Promise.resolve({ answers: [], cancelled: true });\n }\n\n return new Promise<QuestionnaireResult>((resolve, reject) => {\n const entry: QueueEntry = {\n id: this.nextRequestId,\n runner,\n controller: new AbortController(),\n ...(externalSignal ? { externalSignal } : {}),\n latestAnswers: [],\n state: 'queued',\n resolve,\n reject,\n };\n this.nextRequestId += 1;\n const abort = (): void => this.cancel(entry);\n externalSignal?.addEventListener('abort', abort, { once: true });\n entry.removeAbortListener = () => externalSignal?.removeEventListener('abort', abort);\n this.pending.push(entry);\n this.startNext();\n });\n }\n\n shutdown(): void {\n if (!this.accepting && !this.active && this.pending.length === 0) return;\n this.accepting = false;\n if (this.active) this.cancel(this.active);\n while (this.pending.length > 0) {\n const entry = this.pending[0];\n if (!entry) break;\n this.cancel(entry);\n }\n }\n\n /** Waits for every started presenter to observe cancellation and finish. */\n async waitForIdle(): Promise<void> {\n while (this.running.size > 0) await Promise.allSettled(this.running);\n }\n\n private startNext(): void {\n if (!this.accepting || this.active) return;\n const entry = this.pending.shift();\n if (!entry) return;\n entry.state = 'active';\n this.active = entry;\n const reportProgress = (result: QuestionnaireResult): void => {\n if (this.active?.id !== entry.id || entry.state !== 'active') return;\n entry.latestAnswers = result.answers.map((answer) => ({ ...answer }));\n };\n let operation: Promise<QuestionnaireResult>;\n try {\n operation = entry.runner({ signal: entry.controller.signal, reportProgress });\n } catch (error) {\n operation = Promise.reject(error);\n }\n this.running.add(operation);\n void operation\n .then(\n (result) => this.complete(entry, result),\n (error: unknown) => this.fail(entry, error),\n )\n .finally(() => this.running.delete(operation));\n }\n\n private complete(entry: QueueEntry, result: QuestionnaireResult): void {\n if (this.active?.id !== entry.id || entry.state !== 'active') return;\n this.releaseActive(entry);\n entry.resolve(result);\n this.startNext();\n }\n\n private fail(entry: QueueEntry, error: unknown): void {\n if (this.active?.id !== entry.id || entry.state !== 'active') return;\n this.releaseActive(entry);\n entry.reject(error);\n this.startNext();\n }\n\n private cancel(entry: QueueEntry): void {\n if (entry.state === 'settled') return;\n if (entry.state === 'queued') {\n const index = this.pending.indexOf(entry);\n this.pending.splice(index, 1);\n entry.state = 'settled';\n entry.removeAbortListener?.();\n entry.controller.abort();\n entry.resolve(cancelledResult(entry));\n return;\n }\n\n const result = cancelledResult(entry);\n this.releaseActive(entry);\n entry.controller.abort();\n entry.resolve(result);\n this.startNext();\n }\n\n private releaseActive(entry: QueueEntry): void {\n entry.state = 'settled';\n entry.removeAbortListener?.();\n this.active = undefined;\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- function e(e){return{answers:e.latestAnswers.map(e=>({...e})),cancelled:!0}}var t=class{pending=[];running=new Set;active;nextRequestId=1;accepting=!0;enqueue(e,t){return!this.accepting||t?.aborted?Promise.resolve({answers:[],cancelled:!0}):new Promise((n,r)=>{let i={id:this.nextRequestId,runner:e,controller:new AbortController,...t?{externalSignal:t}:{},latestAnswers:[],state:`queued`,resolve:n,reject:r};this.nextRequestId+=1;let a=()=>this.cancel(i);t?.addEventListener(`abort`,a,{once:!0}),i.removeAbortListener=()=>t?.removeEventListener(`abort`,a),this.pending.push(i),this.startNext()})}shutdown(){if(!(!this.accepting&&!this.active&&this.pending.length===0))for(this.accepting=!1,this.active&&this.cancel(this.active);this.pending.length>0;){let e=this.pending[0];if(!e)break;this.cancel(e)}}async waitForIdle(){for(;this.running.size>0;)await Promise.allSettled(this.running)}startNext(){if(!this.accepting||this.active)return;let e=this.pending.shift();if(!e)return;e.state=`active`,this.active=e;let t=t=>{this.active?.id===e.id&&e.state===`active`&&(e.latestAnswers=t.answers.map(e=>({...e})))},n;try{n=e.runner({signal:e.controller.signal,reportProgress:t})}catch(e){n=Promise.reject(e)}this.running.add(n),n.then(t=>this.complete(e,t),t=>this.fail(e,t)).finally(()=>this.running.delete(n))}complete(e,t){this.active?.id===e.id&&e.state===`active`&&(this.releaseActive(e),e.resolve(t),this.startNext())}fail(e,t){this.active?.id===e.id&&e.state===`active`&&(this.releaseActive(e),e.reject(t),this.startNext())}cancel(t){if(t.state===`settled`)return;if(t.state===`queued`){let n=this.pending.indexOf(t);this.pending.splice(n,1),t.state=`settled`,t.removeAbortListener?.(),t.controller.abort(),t.resolve(e(t));return}let n=e(t);this.releaseActive(t),t.controller.abort(),t.resolve(n),this.startNext()}releaseActive(e){e.state=`settled`,e.removeAbortListener?.(),this.active=void 0}};export{t as QuestionnaireCoordinator};
1
+ function e(e){return{answers:e.latestAnswers.map(e=>({...e})),cancelled:!0}}var t=class{pending=[];running=new Set;active;nextRequestId=1;accepting=!0;enqueue(e,t){return!this.accepting||t?.aborted?Promise.resolve({answers:[],cancelled:!0}):new Promise((n,r)=>{let i={id:this.nextRequestId,runner:e,controller:new AbortController,...t?{externalSignal:t}:{},latestAnswers:[],state:`queued`,resolve:n,reject:r};this.nextRequestId+=1;let a=()=>this.cancel(i);t?.addEventListener(`abort`,a,{once:!0}),i.removeAbortListener=()=>t?.removeEventListener(`abort`,a),this.pending.push(i),this.startNext()})}shutdown(){if(this.accepting||this.active||this.pending.length!==0)for(this.accepting=!1,this.active&&this.cancel(this.active);this.pending.length>0;){let e=this.pending[0];if(!e)break;this.cancel(e)}}async waitForIdle(){for(;this.running.size>0;)await Promise.allSettled(this.running)}startNext(){if(!this.accepting||this.active)return;let e=this.pending.shift();if(!e)return;e.state=`active`,this.active=e;let t=t=>{this.active?.id===e.id&&e.state===`active`&&(e.latestAnswers=t.answers.map(e=>({...e})))},n;try{n=e.runner({signal:e.controller.signal,reportProgress:t})}catch(e){n=Promise.reject(e)}this.running.add(n),n.then(t=>this.complete(e,t),t=>this.fail(e,t)).finally(()=>this.running.delete(n))}complete(e,t){this.active?.id===e.id&&e.state===`active`&&(this.releaseActive(e),e.resolve(t),this.startNext())}fail(e,t){this.active?.id===e.id&&e.state===`active`&&(this.releaseActive(e),e.reject(t),this.startNext())}cancel(t){if(t.state===`settled`)return;if(t.state===`queued`){let n=this.pending.indexOf(t);this.pending.splice(n,1),t.state=`settled`,t.removeAbortListener?.(),t.controller.abort(),t.resolve(e(t));return}let n=e(t);this.releaseActive(t),t.controller.abort(),t.resolve(n),this.startNext()}releaseActive(e){e.state=`settled`,e.removeAbortListener?.(),this.active=void 0}};export{t as QuestionnaireCoordinator};
2
2
  //# sourceMappingURL=questionnaireCoordinator.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"questionnaireCoordinator.mjs","names":[],"sources":["../../src/services/questionnaireCoordinator.ts"],"sourcesContent":["import type { QuestionnaireResult } from '../types/questionnaire.js';\n\nexport interface QuestionnaireRunContext {\n signal: AbortSignal;\n reportProgress: (result: QuestionnaireResult) => void;\n}\n\nexport type QuestionnaireRunner = (context: QuestionnaireRunContext) => Promise<QuestionnaireResult>;\n\ninterface QueueEntry {\n id: number;\n runner: QuestionnaireRunner;\n controller: AbortController;\n externalSignal?: AbortSignal;\n removeAbortListener?: () => void;\n latestAnswers: QuestionnaireResult['answers'];\n state: 'queued' | 'active' | 'settled';\n resolve(result: QuestionnaireResult): void;\n reject(error: unknown): void;\n}\n\nfunction cancelledResult(entry: QueueEntry): QuestionnaireResult {\n return {\n answers: entry.latestAnswers.map((answer) => ({ ...answer })),\n cancelled: true,\n };\n}\n\nexport class QuestionnaireCoordinator {\n private readonly pending: QueueEntry[] = [];\n private readonly running = new Set<Promise<QuestionnaireResult>>();\n private active: QueueEntry | undefined;\n private nextRequestId = 1;\n private accepting = true;\n\n enqueue(runner: QuestionnaireRunner, externalSignal?: AbortSignal): Promise<QuestionnaireResult> {\n if (!this.accepting || externalSignal?.aborted) {\n return Promise.resolve({ answers: [], cancelled: true });\n }\n\n return new Promise<QuestionnaireResult>((resolve, reject) => {\n const entry: QueueEntry = {\n id: this.nextRequestId,\n runner,\n controller: new AbortController(),\n ...(externalSignal ? { externalSignal } : {}),\n latestAnswers: [],\n state: 'queued',\n resolve,\n reject,\n };\n this.nextRequestId += 1;\n const abort = (): void => this.cancel(entry);\n externalSignal?.addEventListener('abort', abort, { once: true });\n entry.removeAbortListener = () => externalSignal?.removeEventListener('abort', abort);\n this.pending.push(entry);\n this.startNext();\n });\n }\n\n shutdown(): void {\n if (!this.accepting && !this.active && this.pending.length === 0) return;\n this.accepting = false;\n if (this.active) this.cancel(this.active);\n while (this.pending.length > 0) {\n const entry = this.pending[0];\n if (!entry) break;\n this.cancel(entry);\n }\n }\n\n /** Waits for every started presenter to observe cancellation and finish. */\n async waitForIdle(): Promise<void> {\n while (this.running.size > 0) await Promise.allSettled(this.running);\n }\n\n private startNext(): void {\n if (!this.accepting || this.active) return;\n const entry = this.pending.shift();\n if (!entry) return;\n entry.state = 'active';\n this.active = entry;\n const reportProgress = (result: QuestionnaireResult): void => {\n if (this.active?.id !== entry.id || entry.state !== 'active') return;\n entry.latestAnswers = result.answers.map((answer) => ({ ...answer }));\n };\n let operation: Promise<QuestionnaireResult>;\n try {\n operation = entry.runner({ signal: entry.controller.signal, reportProgress });\n } catch (error) {\n operation = Promise.reject(error);\n }\n this.running.add(operation);\n void operation\n .then(\n (result) => this.complete(entry, result),\n (error: unknown) => this.fail(entry, error),\n )\n .finally(() => this.running.delete(operation));\n }\n\n private complete(entry: QueueEntry, result: QuestionnaireResult): void {\n if (this.active?.id !== entry.id || entry.state !== 'active') return;\n this.releaseActive(entry);\n entry.resolve(result);\n this.startNext();\n }\n\n private fail(entry: QueueEntry, error: unknown): void {\n if (this.active?.id !== entry.id || entry.state !== 'active') return;\n this.releaseActive(entry);\n entry.reject(error);\n this.startNext();\n }\n\n private cancel(entry: QueueEntry): void {\n if (entry.state === 'settled') return;\n if (entry.state === 'queued') {\n const index = this.pending.indexOf(entry);\n this.pending.splice(index, 1);\n entry.state = 'settled';\n entry.removeAbortListener?.();\n entry.controller.abort();\n entry.resolve(cancelledResult(entry));\n return;\n }\n\n const result = cancelledResult(entry);\n this.releaseActive(entry);\n entry.controller.abort();\n entry.resolve(result);\n this.startNext();\n }\n\n private releaseActive(entry: QueueEntry): void {\n entry.state = 'settled';\n entry.removeAbortListener?.();\n this.active = undefined;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"questionnaireCoordinator.mjs","names":[],"sources":["../../src/services/questionnaireCoordinator.ts"],"sourcesContent":["import type { QuestionnaireResult } from '../types/questionnaire.js';\n\nexport interface QuestionnaireRunContext {\n signal: AbortSignal;\n reportProgress: (result: QuestionnaireResult) => void;\n}\n\nexport type QuestionnaireRunner = (context: QuestionnaireRunContext) => Promise<QuestionnaireResult>;\n\ninterface QueueEntry {\n id: number;\n runner: QuestionnaireRunner;\n controller: AbortController;\n externalSignal?: AbortSignal;\n removeAbortListener?: () => void;\n latestAnswers: QuestionnaireResult['answers'];\n state: 'queued' | 'active' | 'settled';\n resolve(result: QuestionnaireResult): void;\n reject(error: unknown): void;\n}\n\nfunction cancelledResult(entry: QueueEntry): QuestionnaireResult {\n return {\n answers: entry.latestAnswers.map((answer) => ({ ...answer })),\n cancelled: true,\n };\n}\n\nexport class QuestionnaireCoordinator {\n private readonly pending: QueueEntry[] = [];\n private readonly running = new Set<Promise<QuestionnaireResult>>();\n private active: QueueEntry | undefined;\n private nextRequestId = 1;\n private accepting = true;\n\n enqueue(runner: QuestionnaireRunner, externalSignal?: AbortSignal): Promise<QuestionnaireResult> {\n if (!this.accepting || externalSignal?.aborted) {\n return Promise.resolve({ answers: [], cancelled: true });\n }\n\n return new Promise<QuestionnaireResult>((resolve, reject) => {\n const entry: QueueEntry = {\n id: this.nextRequestId,\n runner,\n controller: new AbortController(),\n ...(externalSignal ? { externalSignal } : {}),\n latestAnswers: [],\n state: 'queued',\n resolve,\n reject,\n };\n this.nextRequestId += 1;\n const abort = (): void => this.cancel(entry);\n externalSignal?.addEventListener('abort', abort, { once: true });\n entry.removeAbortListener = () => externalSignal?.removeEventListener('abort', abort);\n this.pending.push(entry);\n this.startNext();\n });\n }\n\n shutdown(): void {\n if (!this.accepting && !this.active && this.pending.length === 0) return;\n this.accepting = false;\n if (this.active) this.cancel(this.active);\n while (this.pending.length > 0) {\n const entry = this.pending[0];\n if (!entry) break;\n this.cancel(entry);\n }\n }\n\n /** Waits for every started presenter to observe cancellation and finish. */\n async waitForIdle(): Promise<void> {\n while (this.running.size > 0) await Promise.allSettled(this.running);\n }\n\n private startNext(): void {\n if (!this.accepting || this.active) return;\n const entry = this.pending.shift();\n if (!entry) return;\n entry.state = 'active';\n this.active = entry;\n const reportProgress = (result: QuestionnaireResult): void => {\n if (this.active?.id !== entry.id || entry.state !== 'active') return;\n entry.latestAnswers = result.answers.map((answer) => ({ ...answer }));\n };\n let operation: Promise<QuestionnaireResult>;\n try {\n operation = entry.runner({ signal: entry.controller.signal, reportProgress });\n } catch (error) {\n operation = Promise.reject(error);\n }\n this.running.add(operation);\n void operation\n .then(\n (result) => this.complete(entry, result),\n (error: unknown) => this.fail(entry, error),\n )\n .finally(() => this.running.delete(operation));\n }\n\n private complete(entry: QueueEntry, result: QuestionnaireResult): void {\n if (this.active?.id !== entry.id || entry.state !== 'active') return;\n this.releaseActive(entry);\n entry.resolve(result);\n this.startNext();\n }\n\n private fail(entry: QueueEntry, error: unknown): void {\n if (this.active?.id !== entry.id || entry.state !== 'active') return;\n this.releaseActive(entry);\n entry.reject(error);\n this.startNext();\n }\n\n private cancel(entry: QueueEntry): void {\n if (entry.state === 'settled') return;\n if (entry.state === 'queued') {\n const index = this.pending.indexOf(entry);\n this.pending.splice(index, 1);\n entry.state = 'settled';\n entry.removeAbortListener?.();\n entry.controller.abort();\n entry.resolve(cancelledResult(entry));\n return;\n }\n\n const result = cancelledResult(entry);\n this.releaseActive(entry);\n entry.controller.abort();\n entry.resolve(result);\n this.startNext();\n }\n\n private releaseActive(entry: QueueEntry): void {\n entry.state = 'settled';\n entry.removeAbortListener?.();\n this.active = undefined;\n }\n}\n"],"mappings":"AAqBA,SAAS,EAAgB,EAAwC,CAC/D,MAAO,CACL,QAAS,EAAM,cAAc,IAAK,IAAY,CAAE,GAAG,CAAO,EAAE,EAC5D,UAAW,EACb,CACF,CAEA,IAAa,EAAb,KAAsC,CACpC,QAAyC,CAAC,EAC1C,QAA2B,IAAI,IAC/B,OACA,cAAwB,EACxB,UAAoB,GAEpB,QAAQ,EAA6B,EAA4D,CAK/F,MAJI,CAAC,KAAK,WAAa,GAAgB,QAC9B,QAAQ,QAAQ,CAAE,QAAS,CAAC,EAAG,UAAW,EAAK,CAAC,EAGlD,IAAI,SAA8B,EAAS,IAAW,CAC3D,IAAM,EAAoB,CACxB,GAAI,KAAK,cACT,SACA,WAAY,IAAI,gBAChB,GAAI,EAAiB,CAAE,gBAAe,EAAI,CAAC,EAC3C,cAAe,CAAC,EAChB,MAAO,SACP,UACA,QACF,EACA,KAAK,eAAiB,EACtB,IAAM,MAAoB,KAAK,OAAO,CAAK,EAC3C,GAAgB,iBAAiB,QAAS,EAAO,CAAE,KAAM,EAAK,CAAC,EAC/D,EAAM,wBAA4B,GAAgB,oBAAoB,QAAS,CAAK,EACpF,KAAK,QAAQ,KAAK,CAAK,EACvB,KAAK,UAAU,CACjB,CAAC,CACH,CAEA,UAAiB,CACX,GAAC,KAAK,WAAc,KAAK,QAAU,KAAK,QAAQ,SAAW,EAG/D,IAFA,KAAK,UAAY,GACb,KAAK,QAAQ,KAAK,OAAO,KAAK,MAAM,EACjC,KAAK,QAAQ,OAAS,GAAG,CAC9B,IAAM,EAAQ,KAAK,QAAQ,GAC3B,GAAI,CAAC,EAAO,MACZ,KAAK,OAAO,CAAK,CACnB,CACF,CAGA,MAAM,aAA6B,CACjC,KAAO,KAAK,QAAQ,KAAO,GAAG,MAAM,QAAQ,WAAW,KAAK,OAAO,CACrE,CAEA,WAA0B,CACxB,GAAI,CAAC,KAAK,WAAa,KAAK,OAAQ,OACpC,IAAM,EAAQ,KAAK,QAAQ,MAAM,EACjC,GAAI,CAAC,EAAO,OACZ,EAAM,MAAQ,SACd,KAAK,OAAS,EACd,IAAM,EAAkB,GAAsC,CACxD,KAAK,QAAQ,KAAO,EAAM,IAAM,EAAM,QAAU,WACpD,EAAM,cAAgB,EAAO,QAAQ,IAAK,IAAY,CAAE,GAAG,CAAO,EAAE,EACtE,EACI,EACJ,GAAI,CACF,EAAY,EAAM,OAAO,CAAE,OAAQ,EAAM,WAAW,OAAQ,gBAAe,CAAC,CAC9E,OAAS,EAAO,CACd,EAAY,QAAQ,OAAO,CAAK,CAClC,CACA,KAAK,QAAQ,IAAI,CAAS,EAC1B,EACG,KACE,GAAW,KAAK,SAAS,EAAO,CAAM,EACtC,GAAmB,KAAK,KAAK,EAAO,CAAK,CAC5C,CAAC,CACA,YAAc,KAAK,QAAQ,OAAO,CAAS,CAAC,CACjD,CAEA,SAAiB,EAAmB,EAAmC,CACjE,KAAK,QAAQ,KAAO,EAAM,IAAM,EAAM,QAAU,WACpD,KAAK,cAAc,CAAK,EACxB,EAAM,QAAQ,CAAM,EACpB,KAAK,UAAU,EACjB,CAEA,KAAa,EAAmB,EAAsB,CAChD,KAAK,QAAQ,KAAO,EAAM,IAAM,EAAM,QAAU,WACpD,KAAK,cAAc,CAAK,EACxB,EAAM,OAAO,CAAK,EAClB,KAAK,UAAU,EACjB,CAEA,OAAe,EAAyB,CACtC,GAAI,EAAM,QAAU,UAAW,OAC/B,GAAI,EAAM,QAAU,SAAU,CAC5B,IAAM,EAAQ,KAAK,QAAQ,QAAQ,CAAK,EACxC,KAAK,QAAQ,OAAO,EAAO,CAAC,EAC5B,EAAM,MAAQ,UACd,EAAM,sBAAsB,EAC5B,EAAM,WAAW,MAAM,EACvB,EAAM,QAAQ,EAAgB,CAAK,CAAC,EACpC,MACF,CAEA,IAAM,EAAS,EAAgB,CAAK,EACpC,KAAK,cAAc,CAAK,EACxB,EAAM,WAAW,MAAM,EACvB,EAAM,QAAQ,CAAM,EACpB,KAAK,UAAU,CACjB,CAEA,cAAsB,EAAyB,CAC7C,EAAM,MAAQ,UACd,EAAM,sBAAsB,EAC5B,KAAK,OAAS,IAAA,EAChB,CACF"}
@@ -1,2 +1,2 @@
1
- let e=require("@earendil-works/pi-tui");const t=`tui.select.cancel`;var n=class{options;answers=new Map;selectedOptions=new Map;notes=new Map;editor;currentQuestion=0;currentOption=0;editorMode;collapsed=!1;settled=!1;externalEditorError;cachedLines;cachedWidth;isFocused=!1;constructor(t){this.options=t,this.editor=new e.Editor(t.tui,{borderColor:e=>t.theme.fg(`accent`,e),selectList:{selectedPrefix:e=>t.theme.fg(`accent`,e),selectedText:e=>t.theme.fg(`accent`,e),description:e=>t.theme.fg(`muted`,e),scrollInfo:e=>t.theme.fg(`dim`,e),noMatch:e=>t.theme.fg(`warning`,e)}}),this.editor.onSubmit=e=>this.submitEditor(e)}get focused(){return this.isFocused}set focused(e){this.isFocused=e,this.editor.focused=e,this.invalidate()}invalidate(){this.cachedLines=void 0,this.cachedWidth=void 0}cancel(){this.finish(!0)}handleInput(n){if(!this.settled){if(this.options.collapseKey!==`off`&&(0,e.matchesKey)(n,this.options.collapseKey)){this.collapsed=!this.collapsed,this.refresh();return}if(!this.collapsed){if(this.editorMode){if(this.options.keybindings.matches(n,t)){this.editorMode=void 0,this.externalEditorError=void 0,this.editor.setText(``),this.refresh();return}if(this.options.keybindings.matches(n,`app.editor.external`)){this.openExternalEditor();return}this.editor.handleInput(n),this.refresh();return}if(this.options.keybindings.matches(n,t)){this.cancel();return}if((0,e.matchesKey)(n,e.Key.up)){this.moveOption(-1);return}if((0,e.matchesKey)(n,e.Key.down)){this.moveOption(1);return}if((0,e.matchesKey)(n,e.Key.tab)||(0,e.matchesKey)(n,e.Key.right)){this.moveQuestion(1);return}if((0,e.matchesKey)(n,e.Key.shift(`tab`))||(0,e.matchesKey)(n,e.Key.left)){this.moveQuestion(-1);return}if((0,e.matchesKey)(n,`n`)||(0,e.matchesKey)(n,e.Key.shift(`n`))){this.openEditor(`notes`);return}if((0,e.matchesKey)(n,e.Key.space)||n===` `){this.toggleCurrentOption();return}(0,e.matchesKey)(n,e.Key.enter)&&this.confirmCurrentSelection()}}}render(e){let t=Math.max(1,e);if(this.cachedLines&&this.cachedWidth===t)return this.cachedLines;if(this.cachedWidth=t,this.collapsed){let e=[],n=this.answers.size;return this.addWrapped(e,``,this.options.theme.fg(`accent`,`▸ Questionnaire (${n}/${this.options.params.questions.length} answered) — ${this.options.collapseKey} to expand`),t),this.cachedLines=e,e}let n=[],r=this.question(),i=this.options.theme.fg(`accent`,`─`.repeat(t));return n.push(i),this.addWrapped(n,` `,this.renderTabs(),t),n.push(``),this.addWrapped(n,` `,this.options.theme.bold(r.question),t),n.push(``),this.renderOptions(n,r,t),this.renderPreview(n,r,t),this.renderNotes(n,t),this.renderEditor(n,t),n.push(``),this.addWrapped(n,` `,this.helpText(r),t),n.push(i),this.cachedLines=n,n}question(){let e=this.options.params.questions[this.currentQuestion];if(!e)throw Error(`Questionnaire has no active question`);return e}optionCount(e){return e.options.length+1}renderTabs(){return this.options.params.questions.map((e,t)=>{let n=` ${this.answers.has(t)?`✓`:`○`} ${e.header} `;return t===this.currentQuestion?this.options.theme.bg(`selectedBg`,this.options.theme.fg(`text`,n)):this.options.theme.fg(this.answers.has(t)?`success`:`muted`,n)}).join(` `)}renderOptions(e,t,n){let r=this.selectedOptions.get(this.currentQuestion)??new Set;t.options.forEach((i,a)=>{let o=a===this.currentOption,s=t.multiSelect?r.has(a)?`[x]`:`[ ]`:`${a+1}.`,c=o?this.options.theme.fg(`accent`,`> `):` `,l=`${s} ${i.label}`;this.addWrapped(e,c,this.options.theme.fg(o?`accent`:`text`,l),n),i.description&&this.addWrapped(e,` `,this.options.theme.fg(`muted`,i.description),n)});let i=t.options.length,a=this.currentOption===i;this.addWrapped(e,a?this.options.theme.fg(`accent`,`> `):` `,this.options.theme.fg(a?`accent`:`text`,`✎ Type something.`),n)}renderPreview(t,n,r){if(this.currentOption>=n.options.length)return;let i=n.options[this.currentOption]?.preview;if(!i)return;t.push(``),this.addWrapped(t,` `,this.options.theme.fg(`accent`,this.options.theme.bold(`Preview`)),r);let a=new e.Markdown(i,0,0,this.options.markdownTheme,{color:e=>this.options.theme.fg(`muted`,e)},{preserveOrderedListMarkers:!0,preserveBackslashEscapes:!0});for(let e of a.render(Math.max(1,r-3)))this.addWrapped(t,` │ `,e||` `,r)}renderNotes(e,t){let n=this.notes.get(this.currentQuestion);!n||this.editorMode===`notes`||(e.push(``),this.addWrapped(e,` `,this.options.theme.fg(`muted`,`Note: ${n}`),t))}renderEditor(t,n){if(!this.editorMode)return;t.push(``);let r=this.editorMode===`custom`?`Your answer:`:`Optional note:`;this.addWrapped(t,` `,this.options.theme.fg(`muted`,r),n);for(let r of this.editor.render(Math.max(1,n-2)))t.push((0,e.truncateToWidth)(` ${r}`,n,``));this.externalEditorError&&this.addWrapped(t,` `,this.options.theme.fg(`warning`,this.externalEditorError),n)}helpText(e){return this.editorMode?`Enter submit • Esc/Ctrl+C return • Ctrl+G external editor`:`Tab/←→ questions • ↑↓ options • ${e.multiSelect?`Space toggle • Enter confirm`:`Enter select`} • N note • Esc/Ctrl+C cancel`}addWrapped(t,n,r,i){let a=Math.min((0,e.visibleWidth)(n),Math.max(0,i-1)),o=(0,e.truncateToWidth)(n,a,``),s=Math.max(1,i-a),c=(0,e.wrapTextWithAnsi)(r,s);if(c.length===0){t.push(o);return}for(let n=0;n<c.length;n+=1){let r=n===0?o:` `.repeat(a);t.push((0,e.truncateToWidth)(`${r}${c[n]}`,i,``))}}moveOption(e){let t=this.optionCount(this.question());this.currentOption=(this.currentOption+e+t)%t,this.refresh()}moveQuestion(e){let t=this.options.params.questions.length;this.currentQuestion=(this.currentQuestion+e+t)%t,this.currentOption=0,this.refresh()}toggleCurrentOption(){let e=this.question();if(!e.multiSelect||this.currentOption>=e.options.length)return;let t=this.selectedOptions.get(this.currentQuestion)??new Set;t.has(this.currentOption)?t.delete(this.currentOption):t.add(this.currentOption),this.selectedOptions.set(this.currentQuestion,t),this.refresh()}confirmCurrentSelection(){let e=this.question();if(this.currentOption===e.options.length){this.openEditor(`custom`);return}if(e.multiSelect){let t=this.selectedOptions.get(this.currentQuestion)??new Set;t.size===0&&t.add(this.currentOption);let n=[...t].sort((e,t)=>e-t).flatMap(t=>e.options[t]?[e.options[t].label]:[]);this.recordAnswer({questionIndex:this.currentQuestion,question:e.question,kind:`multi`,answer:null,selected:n,...this.noteFields()});return}let t=e.options[this.currentOption];t&&this.recordAnswer({questionIndex:this.currentQuestion,question:e.question,kind:`option`,answer:t.label,...t.preview?{preview:t.preview}:{},...this.noteFields()})}openEditor(e){this.editorMode=e,this.externalEditorError=void 0,this.editor.setText(e===`notes`?this.notes.get(this.currentQuestion)??``:``),this.refresh()}async openExternalEditor(){let e=this.editorMode;if(e){this.options.tui.stop();try{let t=await this.options.editExternally(this.editor.getText());if(this.settled||this.editorMode!==e)return;t.status===`complete`?(this.editor.setText(t.content),this.externalEditorError=void 0):this.externalEditorError=t.message}catch(e){this.externalEditorError=`External editor failed: ${e instanceof Error?e.message:String(e)}`}finally{this.options.tui.start(),this.refresh()}}}submitEditor(e){let t=this.editorMode;if(this.editorMode=void 0,this.editor.setText(``),t===`notes`){let t=e.trim();t?this.notes.set(this.currentQuestion,t):this.notes.delete(this.currentQuestion);let n=this.answers.get(this.currentQuestion);n&&(this.answers.set(this.currentQuestion,{...n,...t?{notes:t}:{notes:void 0}}),this.reportProgress()),this.refresh();return}t===`custom`&&this.recordAnswer({questionIndex:this.currentQuestion,question:this.question().question,kind:`custom`,answer:e,...this.noteFields()})}noteFields(){let e=this.notes.get(this.currentQuestion);return e?{notes:e}:{}}recordAnswer(e){if(this.answers.set(this.currentQuestion,e),this.reportProgress(),this.answers.size===this.options.params.questions.length){this.finish(!1);return}let t=this.options.params.questions.findIndex((e,t)=>t>this.currentQuestion&&!this.answers.has(t));this.currentQuestion=t>=0?t:this.options.params.questions.findIndex((e,t)=>!this.answers.has(t)),this.currentOption=0,this.refresh()}orderedAnswers(){return[...this.answers.values()].sort((e,t)=>e.questionIndex-t.questionIndex).map(e=>({...e}))}reportProgress(){this.options.reportProgress?.({answers:this.orderedAnswers(),cancelled:!1})}finish(e){this.settled||(this.settled=!0,this.options.done({answers:this.orderedAnswers(),cancelled:e}))}refresh(){this.invalidate(),this.options.tui.requestRender()}};exports.QuestionnaireComponent=n;
1
+ let e=require("@earendil-works/pi-tui");const t=`tui.select.cancel`;var n=class{options;answers=new Map;selectedOptions=new Map;notes=new Map;editor;currentQuestion=0;currentOption=0;editorMode;collapsed=!1;settled=!1;externalEditorError;cachedLines;cachedWidth;isFocused=!1;constructor(t){this.options=t,this.editor=new e.Editor(t.tui,{borderColor:e=>t.theme.fg(`accent`,e),selectList:{selectedPrefix:e=>t.theme.fg(`accent`,e),selectedText:e=>t.theme.fg(`accent`,e),description:e=>t.theme.fg(`muted`,e),scrollInfo:e=>t.theme.fg(`dim`,e),noMatch:e=>t.theme.fg(`warning`,e)}}),this.editor.onSubmit=e=>this.submitEditor(e)}get focused(){return this.isFocused}set focused(e){this.isFocused=e,this.editor.focused=e,this.invalidate()}invalidate(){this.cachedLines=void 0,this.cachedWidth=void 0}cancel(){this.finish(!0)}handleInput(n){if(!this.settled){if(this.options.collapseKey!==`off`&&(0,e.matchesKey)(n,this.options.collapseKey)){this.collapsed=!this.collapsed,this.refresh();return}if(!this.collapsed){if(this.editorMode){if(this.options.keybindings.matches(n,t)){this.editorMode=void 0,this.externalEditorError=void 0,this.editor.setText(``),this.refresh();return}if(this.options.keybindings.matches(n,`app.editor.external`)){this.openExternalEditor();return}this.editor.handleInput(n),this.refresh();return}if(this.options.keybindings.matches(n,t)){this.cancel();return}if((0,e.matchesKey)(n,e.Key.up)){this.moveOption(-1);return}if((0,e.matchesKey)(n,e.Key.down)){this.moveOption(1);return}if((0,e.matchesKey)(n,e.Key.tab)||(0,e.matchesKey)(n,e.Key.right)){this.moveQuestion(1);return}if((0,e.matchesKey)(n,e.Key.shift(`tab`))||(0,e.matchesKey)(n,e.Key.left)){this.moveQuestion(-1);return}if((0,e.matchesKey)(n,`n`)||(0,e.matchesKey)(n,e.Key.shift(`n`))){this.openEditor(`notes`);return}if((0,e.matchesKey)(n,e.Key.space)||n===` `){this.toggleCurrentOption();return}(0,e.matchesKey)(n,e.Key.enter)&&this.confirmCurrentSelection()}}}render(e){let t=Math.max(1,e);if(this.cachedLines&&this.cachedWidth===t)return this.cachedLines;if(this.cachedWidth=t,this.collapsed){let e=[],n=this.answers.size;return this.addWrapped(e,``,this.options.theme.fg(`accent`,`▸ Questionnaire (${n}/${this.options.params.questions.length} answered) — ${this.options.collapseKey} to expand`),t),this.cachedLines=e,e}let n=[],r=this.question(),i=this.options.theme.fg(`accent`,`─`.repeat(t));return n.push(i),this.addWrapped(n,` `,this.renderTabs(),t),n.push(``),this.addWrapped(n,` `,this.options.theme.bold(r.question),t),n.push(``),this.renderOptions(n,r,t),this.renderPreview(n,r,t),this.renderNotes(n,t),this.renderEditor(n,t),n.push(``),this.addWrapped(n,` `,this.helpText(r),t),n.push(i),this.cachedLines=n,n}question(){let e=this.options.params.questions[this.currentQuestion];if(!e)throw Error(`Questionnaire has no active question`);return e}optionCount(e){return e.options.length+1}renderTabs(){return this.options.params.questions.map((e,t)=>{let n=` ${this.answers.has(t)?`✓`:`○`} ${e.header} `;return t===this.currentQuestion?this.options.theme.bg(`selectedBg`,this.options.theme.fg(`text`,n)):this.options.theme.fg(this.answers.has(t)?`success`:`muted`,n)}).join(` `)}renderOptions(e,t,n){let r=this.selectedOptions.get(this.currentQuestion)??new Set;t.options.forEach((i,a)=>{let o=a===this.currentOption,s=t.multiSelect?r.has(a)?`[x]`:`[ ]`:`${a+1}.`,c=o?this.options.theme.fg(`accent`,`> `):` `,l=`${s} ${i.label}`;this.addWrapped(e,c,this.options.theme.fg(o?`accent`:`text`,l),n),i.description&&this.addWrapped(e,` `,this.options.theme.fg(`muted`,i.description),n)});let i=t.options.length,a=this.currentOption===i;this.addWrapped(e,a?this.options.theme.fg(`accent`,`> `):` `,this.options.theme.fg(a?`accent`:`text`,`✎ Type something.`),n)}renderPreview(t,n,r){if(this.currentOption>=n.options.length)return;let i=n.options[this.currentOption]?.preview;if(!i)return;t.push(``),this.addWrapped(t,` `,this.options.theme.fg(`accent`,this.options.theme.bold(`Preview`)),r);let a=new e.Markdown(i,0,0,this.options.markdownTheme,{color:e=>this.options.theme.fg(`muted`,e)},{preserveOrderedListMarkers:!0,preserveBackslashEscapes:!0});for(let e of a.render(Math.max(1,r-3)))this.addWrapped(t,` │ `,e||` `,r)}renderNotes(e,t){let n=this.notes.get(this.currentQuestion);n&&this.editorMode!==`notes`&&(e.push(``),this.addWrapped(e,` `,this.options.theme.fg(`muted`,`Note: ${n}`),t))}renderEditor(t,n){if(!this.editorMode)return;t.push(``);let r=this.editorMode===`custom`?`Your answer:`:`Optional note:`;this.addWrapped(t,` `,this.options.theme.fg(`muted`,r),n);for(let r of this.editor.render(Math.max(1,n-2)))t.push((0,e.truncateToWidth)(` ${r}`,n,``));this.externalEditorError&&this.addWrapped(t,` `,this.options.theme.fg(`warning`,this.externalEditorError),n)}helpText(e){return this.editorMode?`Enter submit • Esc/Ctrl+C return • Ctrl+G external editor`:`Tab/←→ questions • ↑↓ options • ${e.multiSelect?`Space toggle • Enter confirm`:`Enter select`} • N note • Esc/Ctrl+C cancel`}addWrapped(t,n,r,i){let a=Math.min((0,e.visibleWidth)(n),Math.max(0,i-1)),o=(0,e.truncateToWidth)(n,a,``),s=Math.max(1,i-a),c=(0,e.wrapTextWithAnsi)(r,s);if(c.length===0){t.push(o);return}for(let n=0;n<c.length;n+=1){let r=n===0?o:` `.repeat(a);t.push((0,e.truncateToWidth)(`${r}${c[n]}`,i,``))}}moveOption(e){let t=this.optionCount(this.question());this.currentOption=(this.currentOption+e+t)%t,this.refresh()}moveQuestion(e){let t=this.options.params.questions.length;this.currentQuestion=(this.currentQuestion+e+t)%t,this.currentOption=0,this.refresh()}toggleCurrentOption(){let e=this.question();if(!e.multiSelect||this.currentOption>=e.options.length)return;let t=this.selectedOptions.get(this.currentQuestion)??new Set;t.has(this.currentOption)?t.delete(this.currentOption):t.add(this.currentOption),this.selectedOptions.set(this.currentQuestion,t),this.refresh()}confirmCurrentSelection(){let e=this.question();if(this.currentOption===e.options.length){this.openEditor(`custom`);return}if(e.multiSelect){let t=this.selectedOptions.get(this.currentQuestion)??new Set;t.size===0&&t.add(this.currentOption);let n=[...t].sort((e,t)=>e-t).flatMap(t=>e.options[t]?[e.options[t].label]:[]);this.recordAnswer({questionIndex:this.currentQuestion,question:e.question,kind:`multi`,answer:null,selected:n,...this.noteFields()});return}let t=e.options[this.currentOption];t&&this.recordAnswer({questionIndex:this.currentQuestion,question:e.question,kind:`option`,answer:t.label,...t.preview?{preview:t.preview}:{},...this.noteFields()})}openEditor(e){this.editorMode=e,this.externalEditorError=void 0,this.editor.setText(e===`notes`?this.notes.get(this.currentQuestion)??``:``),this.refresh()}async openExternalEditor(){let e=this.editorMode;if(e){this.options.tui.stop();try{let t=await this.options.editExternally(this.editor.getText());if(this.settled||this.editorMode!==e)return;t.status===`complete`?(this.editor.setText(t.content),this.externalEditorError=void 0):this.externalEditorError=t.message}catch(e){this.externalEditorError=`External editor failed: ${e instanceof Error?e.message:String(e)}`}finally{this.options.tui.start(),this.refresh()}}}submitEditor(e){let t=this.editorMode;if(this.editorMode=void 0,this.editor.setText(``),t===`notes`){let t=e.trim();t?this.notes.set(this.currentQuestion,t):this.notes.delete(this.currentQuestion);let n=this.answers.get(this.currentQuestion);n&&(this.answers.set(this.currentQuestion,{...n,...t?{notes:t}:{notes:void 0}}),this.reportProgress()),this.refresh();return}t===`custom`&&this.recordAnswer({questionIndex:this.currentQuestion,question:this.question().question,kind:`custom`,answer:e,...this.noteFields()})}noteFields(){let e=this.notes.get(this.currentQuestion);return e?{notes:e}:{}}recordAnswer(e){if(this.answers.set(this.currentQuestion,e),this.reportProgress(),this.answers.size===this.options.params.questions.length){this.finish(!1);return}let t=this.options.params.questions.findIndex((e,t)=>t>this.currentQuestion&&!this.answers.has(t));this.currentQuestion=t>=0?t:this.options.params.questions.findIndex((e,t)=>!this.answers.has(t)),this.currentOption=0,this.refresh()}orderedAnswers(){return[...this.answers.values()].sort((e,t)=>e.questionIndex-t.questionIndex).map(e=>({...e}))}reportProgress(){this.options.reportProgress?.({answers:this.orderedAnswers(),cancelled:!1})}finish(e){this.settled||(this.settled=!0,this.options.done({answers:this.orderedAnswers(),cancelled:e}))}refresh(){this.invalidate(),this.options.tui.requestRender()}};exports.QuestionnaireComponent=n;
2
2
  //# sourceMappingURL=questionnaireComponent.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"questionnaireComponent.cjs","names":["Editor","matchesKey","Key","Markdown","truncateToWidth","visibleWidth","wrapTextWithAnsi"],"sources":["../../src/tui/questionnaireComponent.ts"],"sourcesContent":["import type { KeybindingsManager, Theme } from '@earendil-works/pi-coding-agent';\nimport {\n Editor,\n Key,\n Markdown,\n matchesKey,\n type Focusable,\n type MarkdownTheme,\n type TUI,\n truncateToWidth,\n visibleWidth,\n wrapTextWithAnsi,\n} from '@earendil-works/pi-tui';\nimport type { QuestionData, QuestionParams } from '../schemas/questionnaire.js';\nimport type { ExternalEditResult, QuestionAnswer, QuestionnaireResult } from '../types/questionnaire.js';\n\nexport interface QuestionnaireComponentOptions {\n tui: TUI;\n theme: Theme;\n markdownTheme: MarkdownTheme;\n keybindings: KeybindingsManager;\n params: QuestionParams;\n collapseKey: string;\n done: (result: QuestionnaireResult) => void;\n editExternally: (content: string) => Promise<ExternalEditResult>;\n reportProgress?: (result: QuestionnaireResult) => void;\n}\n\ntype EditorMode = 'custom' | 'notes';\n\nconst SELECT_CANCEL_KEYBINDING = 'tui.select.cancel';\nconst EXTERNAL_EDITOR_KEYBINDING = 'app.editor.external';\n\nexport class QuestionnaireComponent implements Focusable {\n private readonly answers = new Map<number, QuestionAnswer>();\n private readonly selectedOptions = new Map<number, Set<number>>();\n private readonly notes = new Map<number, string>();\n private readonly editor: Editor;\n private currentQuestion = 0;\n private currentOption = 0;\n private editorMode: EditorMode | undefined;\n private collapsed = false;\n private settled = false;\n private externalEditorError: string | undefined;\n private cachedLines: string[] | undefined;\n private cachedWidth: number | undefined;\n private isFocused = false;\n\n constructor(private readonly options: QuestionnaireComponentOptions) {\n this.editor = new Editor(options.tui, {\n borderColor: (text) => options.theme.fg('accent', text),\n selectList: {\n selectedPrefix: (text) => options.theme.fg('accent', text),\n selectedText: (text) => options.theme.fg('accent', text),\n description: (text) => options.theme.fg('muted', text),\n scrollInfo: (text) => options.theme.fg('dim', text),\n noMatch: (text) => options.theme.fg('warning', text),\n },\n });\n this.editor.onSubmit = (value) => this.submitEditor(value);\n }\n\n get focused(): boolean {\n return this.isFocused;\n }\n\n set focused(value: boolean) {\n this.isFocused = value;\n this.editor.focused = value;\n this.invalidate();\n }\n\n invalidate(): void {\n this.cachedLines = undefined;\n this.cachedWidth = undefined;\n }\n\n cancel(): void {\n this.finish(true);\n }\n\n handleInput(data: string): void {\n if (this.settled) return;\n\n if (\n this.options.collapseKey !== 'off' &&\n matchesKey(data, this.options.collapseKey as Parameters<typeof matchesKey>[1])\n ) {\n this.collapsed = !this.collapsed;\n this.refresh();\n return;\n }\n\n if (this.collapsed) return;\n\n if (this.editorMode) {\n if (this.options.keybindings.matches(data, SELECT_CANCEL_KEYBINDING)) {\n this.editorMode = undefined;\n this.externalEditorError = undefined;\n this.editor.setText('');\n this.refresh();\n return;\n }\n if (this.options.keybindings.matches(data, EXTERNAL_EDITOR_KEYBINDING)) {\n void this.openExternalEditor();\n return;\n }\n this.editor.handleInput(data);\n this.refresh();\n return;\n }\n\n if (this.options.keybindings.matches(data, SELECT_CANCEL_KEYBINDING)) {\n this.cancel();\n return;\n }\n if (matchesKey(data, Key.up)) {\n this.moveOption(-1);\n return;\n }\n if (matchesKey(data, Key.down)) {\n this.moveOption(1);\n return;\n }\n if (matchesKey(data, Key.tab) || matchesKey(data, Key.right)) {\n this.moveQuestion(1);\n return;\n }\n if (matchesKey(data, Key.shift('tab')) || matchesKey(data, Key.left)) {\n this.moveQuestion(-1);\n return;\n }\n if (matchesKey(data, 'n') || matchesKey(data, Key.shift('n'))) {\n this.openEditor('notes');\n return;\n }\n if (matchesKey(data, Key.space) || data === ' ') {\n this.toggleCurrentOption();\n return;\n }\n if (matchesKey(data, Key.enter)) {\n this.confirmCurrentSelection();\n }\n }\n\n render(width: number): string[] {\n const renderWidth = Math.max(1, width);\n if (this.cachedLines && this.cachedWidth === renderWidth) return this.cachedLines;\n this.cachedWidth = renderWidth;\n if (this.collapsed) {\n const lines: string[] = [];\n const answerCount = this.answers.size;\n this.addWrapped(\n lines,\n '',\n this.options.theme.fg(\n 'accent',\n `▸ Questionnaire (${answerCount}/${this.options.params.questions.length} answered) — ${this.options.collapseKey} to expand`,\n ),\n renderWidth,\n );\n this.cachedLines = lines;\n return lines;\n }\n\n const lines: string[] = [];\n const question = this.question();\n const border = this.options.theme.fg('accent', '─'.repeat(renderWidth));\n lines.push(border);\n this.addWrapped(lines, ' ', this.renderTabs(), renderWidth);\n lines.push('');\n this.addWrapped(lines, ' ', this.options.theme.bold(question.question), renderWidth);\n lines.push('');\n\n this.renderOptions(lines, question, renderWidth);\n this.renderPreview(lines, question, renderWidth);\n this.renderNotes(lines, renderWidth);\n this.renderEditor(lines, renderWidth);\n lines.push('');\n this.addWrapped(lines, ' ', this.helpText(question), renderWidth);\n lines.push(border);\n this.cachedLines = lines;\n return lines;\n }\n\n private question(): QuestionData {\n const question = this.options.params.questions[this.currentQuestion];\n if (!question) throw new Error('Questionnaire has no active question');\n return question;\n }\n\n private optionCount(question: QuestionData): number {\n return question.options.length + 1;\n }\n\n private renderTabs(): string {\n const tabs = this.options.params.questions.map((question, index) => {\n const mark = this.answers.has(index) ? '✓' : '○';\n const label = ` ${mark} ${question.header} `;\n return index === this.currentQuestion\n ? this.options.theme.bg('selectedBg', this.options.theme.fg('text', label))\n : this.options.theme.fg(this.answers.has(index) ? 'success' : 'muted', label);\n });\n return tabs.join(' ');\n }\n\n private renderOptions(lines: string[], question: QuestionData, width: number): void {\n const selected = this.selectedOptions.get(this.currentQuestion) ?? new Set<number>();\n question.options.forEach((option, index) => {\n const focused = index === this.currentOption;\n const checked = question.multiSelect ? (selected.has(index) ? '[x]' : '[ ]') : `${index + 1}.`;\n const prefix = focused ? this.options.theme.fg('accent', '> ') : ' ';\n const label = `${checked} ${option.label}`;\n this.addWrapped(lines, prefix, this.options.theme.fg(focused ? 'accent' : 'text', label), width);\n if (option.description) {\n this.addWrapped(lines, ' ', this.options.theme.fg('muted', option.description), width);\n }\n });\n\n const customIndex = question.options.length;\n const customFocused = this.currentOption === customIndex;\n this.addWrapped(\n lines,\n customFocused ? this.options.theme.fg('accent', '> ') : ' ',\n this.options.theme.fg(customFocused ? 'accent' : 'text', '✎ Type something.'),\n width,\n );\n }\n\n private renderPreview(lines: string[], question: QuestionData, width: number): void {\n if (this.currentOption >= question.options.length) return;\n const preview = question.options[this.currentOption]?.preview;\n if (!preview) return;\n lines.push('');\n this.addWrapped(lines, ' ', this.options.theme.fg('accent', this.options.theme.bold('Preview')), width);\n const markdown = new Markdown(\n preview,\n 0,\n 0,\n this.options.markdownTheme,\n { color: (text) => this.options.theme.fg('muted', text) },\n { preserveOrderedListMarkers: true, preserveBackslashEscapes: true },\n );\n for (const previewLine of markdown.render(Math.max(1, width - 3))) {\n this.addWrapped(lines, ' │ ', previewLine || ' ', width);\n }\n }\n\n private renderNotes(lines: string[], width: number): void {\n const note = this.notes.get(this.currentQuestion);\n if (!note || this.editorMode === 'notes') return;\n lines.push('');\n this.addWrapped(lines, ' ', this.options.theme.fg('muted', `Note: ${note}`), width);\n }\n\n private renderEditor(lines: string[], width: number): void {\n if (!this.editorMode) return;\n lines.push('');\n const label = this.editorMode === 'custom' ? 'Your answer:' : 'Optional note:';\n this.addWrapped(lines, ' ', this.options.theme.fg('muted', label), width);\n for (const line of this.editor.render(Math.max(1, width - 2))) {\n lines.push(truncateToWidth(` ${line}`, width, ''));\n }\n if (this.externalEditorError) {\n this.addWrapped(lines, ' ', this.options.theme.fg('warning', this.externalEditorError), width);\n }\n }\n\n private helpText(question: QuestionData): string {\n if (this.editorMode) return 'Enter submit • Esc/Ctrl+C return • Ctrl+G external editor';\n const selectionHelp = question.multiSelect ? 'Space toggle • Enter confirm' : 'Enter select';\n return `Tab/←→ questions • ↑↓ options • ${selectionHelp} • N note • Esc/Ctrl+C cancel`;\n }\n\n private addWrapped(lines: string[], prefix: string, text: string, width: number): void {\n const prefixWidth = Math.min(visibleWidth(prefix), Math.max(0, width - 1));\n const renderedPrefix = truncateToWidth(prefix, prefixWidth, '');\n const available = Math.max(1, width - prefixWidth);\n const wrapped = wrapTextWithAnsi(text, available);\n if (wrapped.length === 0) {\n lines.push(renderedPrefix);\n return;\n }\n for (let index = 0; index < wrapped.length; index += 1) {\n const linePrefix = index === 0 ? renderedPrefix : ' '.repeat(prefixWidth);\n lines.push(truncateToWidth(`${linePrefix}${wrapped[index]}`, width, ''));\n }\n }\n\n private moveOption(delta: number): void {\n const count = this.optionCount(this.question());\n this.currentOption = (this.currentOption + delta + count) % count;\n this.refresh();\n }\n\n private moveQuestion(delta: number): void {\n const count = this.options.params.questions.length;\n this.currentQuestion = (this.currentQuestion + delta + count) % count;\n this.currentOption = 0;\n this.refresh();\n }\n\n private toggleCurrentOption(): void {\n const question = this.question();\n if (!question.multiSelect || this.currentOption >= question.options.length) return;\n const selected = this.selectedOptions.get(this.currentQuestion) ?? new Set<number>();\n if (selected.has(this.currentOption)) selected.delete(this.currentOption);\n else selected.add(this.currentOption);\n this.selectedOptions.set(this.currentQuestion, selected);\n this.refresh();\n }\n\n private confirmCurrentSelection(): void {\n const question = this.question();\n if (this.currentOption === question.options.length) {\n this.openEditor('custom');\n return;\n }\n\n if (question.multiSelect) {\n const selected = this.selectedOptions.get(this.currentQuestion) ?? new Set<number>();\n if (selected.size === 0) selected.add(this.currentOption);\n const labels = [...selected]\n .sort((left, right) => left - right)\n .flatMap((index) => (question.options[index] ? [question.options[index].label] : []));\n this.recordAnswer({\n questionIndex: this.currentQuestion,\n question: question.question,\n kind: 'multi',\n answer: null,\n selected: labels,\n ...this.noteFields(),\n });\n return;\n }\n\n const option = question.options[this.currentOption];\n if (!option) return;\n this.recordAnswer({\n questionIndex: this.currentQuestion,\n question: question.question,\n kind: 'option',\n answer: option.label,\n ...(option.preview ? { preview: option.preview } : {}),\n ...this.noteFields(),\n });\n }\n\n private openEditor(mode: EditorMode): void {\n this.editorMode = mode;\n this.externalEditorError = undefined;\n this.editor.setText(mode === 'notes' ? (this.notes.get(this.currentQuestion) ?? '') : '');\n this.refresh();\n }\n\n private async openExternalEditor(): Promise<void> {\n const editorMode = this.editorMode;\n if (!editorMode) return;\n this.options.tui.stop();\n try {\n const result = await this.options.editExternally(this.editor.getText());\n if (this.settled || this.editorMode !== editorMode) return;\n if (result.status === 'complete') {\n this.editor.setText(result.content);\n this.externalEditorError = undefined;\n } else {\n this.externalEditorError = result.message;\n }\n } catch (error) {\n this.externalEditorError = `External editor failed: ${error instanceof Error ? error.message : String(error)}`;\n } finally {\n this.options.tui.start();\n this.refresh();\n }\n }\n\n private submitEditor(value: string): void {\n const mode = this.editorMode;\n this.editorMode = undefined;\n this.editor.setText('');\n if (mode === 'notes') {\n const trimmed = value.trim();\n if (trimmed) this.notes.set(this.currentQuestion, trimmed);\n else this.notes.delete(this.currentQuestion);\n const answer = this.answers.get(this.currentQuestion);\n if (answer) {\n this.answers.set(this.currentQuestion, {\n ...answer,\n ...(trimmed ? { notes: trimmed } : { notes: undefined }),\n });\n this.reportProgress();\n }\n this.refresh();\n return;\n }\n if (mode === 'custom') {\n this.recordAnswer({\n questionIndex: this.currentQuestion,\n question: this.question().question,\n kind: 'custom',\n answer: value,\n ...this.noteFields(),\n });\n }\n }\n\n private noteFields(): { notes?: string } {\n const note = this.notes.get(this.currentQuestion);\n return note ? { notes: note } : {};\n }\n\n private recordAnswer(answer: QuestionAnswer): void {\n this.answers.set(this.currentQuestion, answer);\n this.reportProgress();\n if (this.answers.size === this.options.params.questions.length) {\n this.finish(false);\n return;\n }\n const next = this.options.params.questions.findIndex(\n (_question, index) => index > this.currentQuestion && !this.answers.has(index),\n );\n this.currentQuestion =\n next >= 0 ? next : this.options.params.questions.findIndex((_question, index) => !this.answers.has(index));\n this.currentOption = 0;\n this.refresh();\n }\n\n private orderedAnswers(): QuestionAnswer[] {\n return [...this.answers.values()]\n .sort((left, right) => left.questionIndex - right.questionIndex)\n .map((answer) => ({ ...answer }));\n }\n\n private reportProgress(): void {\n this.options.reportProgress?.({ answers: this.orderedAnswers(), cancelled: false });\n }\n\n private finish(cancelled: boolean): void {\n if (this.settled) return;\n this.settled = true;\n this.options.done({ answers: this.orderedAnswers(), cancelled });\n }\n\n private refresh(): void {\n this.invalidate();\n this.options.tui.requestRender();\n }\n}\n"],"mappings":"wCA8BA,MAAM,EAA2B,oBAGjC,IAAa,EAAb,KAAyD,CAe1B,QAd7B,QAA2B,IAAI,IAC/B,gBAAmC,IAAI,IACvC,MAAyB,IAAI,IAC7B,OACA,gBAA0B,EAC1B,cAAwB,EACxB,WACA,UAAoB,GACpB,QAAkB,GAClB,oBACA,YACA,YACA,UAAoB,GAEpB,YAAY,EAAyD,CAAxC,KAAA,QAAA,EAC3B,KAAK,OAAS,IAAIA,EAAAA,OAAO,EAAQ,IAAK,CACpC,YAAc,GAAS,EAAQ,MAAM,GAAG,SAAU,CAAI,EACtD,WAAY,CACV,eAAiB,GAAS,EAAQ,MAAM,GAAG,SAAU,CAAI,EACzD,aAAe,GAAS,EAAQ,MAAM,GAAG,SAAU,CAAI,EACvD,YAAc,GAAS,EAAQ,MAAM,GAAG,QAAS,CAAI,EACrD,WAAa,GAAS,EAAQ,MAAM,GAAG,MAAO,CAAI,EAClD,QAAU,GAAS,EAAQ,MAAM,GAAG,UAAW,CAAI,CACrD,CACF,CAAC,EACD,KAAK,OAAO,SAAY,GAAU,KAAK,aAAa,CAAK,CAC3D,CAEA,IAAI,SAAmB,CACrB,OAAO,KAAK,SACd,CAEA,IAAI,QAAQ,EAAgB,CAC1B,KAAK,UAAY,EACjB,KAAK,OAAO,QAAU,EACtB,KAAK,WAAW,CAClB,CAEA,YAAmB,CACjB,KAAK,YAAc,IAAA,GACnB,KAAK,YAAc,IAAA,EACrB,CAEA,QAAe,CACb,KAAK,OAAO,EAAI,CAClB,CAEA,YAAY,EAAoB,CAC1B,SAAK,QAET,IACE,KAAK,QAAQ,cAAgB,QAAA,EAC7BC,EAAAA,WAAAA,CAAW,EAAM,KAAK,QAAQ,WAA+C,EAC7E,CACA,KAAK,UAAY,CAAC,KAAK,UACvB,KAAK,QAAQ,EACb,MACF,CAEI,SAAK,UAET,IAAI,KAAK,WAAY,CACnB,GAAI,KAAK,QAAQ,YAAY,QAAQ,EAAM,CAAwB,EAAG,CACpE,KAAK,WAAa,IAAA,GAClB,KAAK,oBAAsB,IAAA,GAC3B,KAAK,OAAO,QAAQ,EAAE,EACtB,KAAK,QAAQ,EACb,MACF,CACA,GAAI,KAAK,QAAQ,YAAY,QAAQ,EAAM,qBAA0B,EAAG,CACtE,KAAU,mBAAmB,EAC7B,MACF,CACA,KAAK,OAAO,YAAY,CAAI,EAC5B,KAAK,QAAQ,EACb,MACF,CAEA,GAAI,KAAK,QAAQ,YAAY,QAAQ,EAAM,CAAwB,EAAG,CACpE,KAAK,OAAO,EACZ,MACF,CACA,IAAA,EAAIA,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,EAAE,EAAG,CAC5B,KAAK,WAAW,EAAE,EAClB,MACF,CACA,IAAA,EAAID,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,IAAI,EAAG,CAC9B,KAAK,WAAW,CAAC,EACjB,MACF,CACA,IAAA,EAAID,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,GAAG,IAAA,EAAKD,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,KAAK,EAAG,CAC5D,KAAK,aAAa,CAAC,EACnB,MACF,CACA,IAAA,EAAID,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,MAAM,KAAK,CAAC,IAAA,EAAKD,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,IAAI,EAAG,CACpE,KAAK,aAAa,EAAE,EACpB,MACF,CACA,IAAA,EAAID,EAAAA,WAAAA,CAAW,EAAM,GAAG,IAAA,EAAKA,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,MAAM,GAAG,CAAC,EAAG,CAC7D,KAAK,WAAW,OAAO,EACvB,MACF,CACA,IAAA,EAAID,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,KAAK,GAAK,IAAS,IAAK,CAC/C,KAAK,oBAAoB,EACzB,MACF,EACA,EAAID,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,KAAK,GAC5B,KAAK,wBAAwB,CA/B/B,CAnBA,CAoDF,CAEA,OAAO,EAAyB,CAC9B,IAAM,EAAc,KAAK,IAAI,EAAG,CAAK,EACrC,GAAI,KAAK,aAAe,KAAK,cAAgB,EAAa,OAAO,KAAK,YAEtE,GADA,KAAK,YAAc,EACf,KAAK,UAAW,CAClB,IAAM,EAAkB,CAAC,EACnB,EAAc,KAAK,QAAQ,KAWjC,OAVA,KAAK,WACH,EACA,GACA,KAAK,QAAQ,MAAM,GACjB,SACA,oBAAoB,EAAY,GAAG,KAAK,QAAQ,OAAO,UAAU,OAAO,eAAe,KAAK,QAAQ,YAAY,WAClH,EACA,CACF,EACA,KAAK,YAAc,EACZ,CACT,CAEA,IAAM,EAAkB,CAAC,EACnB,EAAW,KAAK,SAAS,EACzB,EAAS,KAAK,QAAQ,MAAM,GAAG,SAAU,IAAI,OAAO,CAAW,CAAC,EAetE,OAdA,EAAM,KAAK,CAAM,EACjB,KAAK,WAAW,EAAO,IAAK,KAAK,WAAW,EAAG,CAAW,EAC1D,EAAM,KAAK,EAAE,EACb,KAAK,WAAW,EAAO,IAAK,KAAK,QAAQ,MAAM,KAAK,EAAS,QAAQ,EAAG,CAAW,EACnF,EAAM,KAAK,EAAE,EAEb,KAAK,cAAc,EAAO,EAAU,CAAW,EAC/C,KAAK,cAAc,EAAO,EAAU,CAAW,EAC/C,KAAK,YAAY,EAAO,CAAW,EACnC,KAAK,aAAa,EAAO,CAAW,EACpC,EAAM,KAAK,EAAE,EACb,KAAK,WAAW,EAAO,IAAK,KAAK,SAAS,CAAQ,EAAG,CAAW,EAChE,EAAM,KAAK,CAAM,EACjB,KAAK,YAAc,EACZ,CACT,CAEA,UAAiC,CAC/B,IAAM,EAAW,KAAK,QAAQ,OAAO,UAAU,KAAK,iBACpD,GAAI,CAAC,EAAU,MAAU,MAAM,sCAAsC,EACrE,OAAO,CACT,CAEA,YAAoB,EAAgC,CAClD,OAAO,EAAS,QAAQ,OAAS,CACnC,CAEA,YAA6B,CAQ3B,OAPa,KAAK,QAAQ,OAAO,UAAU,KAAK,EAAU,IAAU,CAElE,IAAM,EAAQ,IADD,KAAK,QAAQ,IAAI,CAAK,EAAI,IAAM,IACtB,GAAG,EAAS,OAAO,GAC1C,OAAO,IAAU,KAAK,gBAClB,KAAK,QAAQ,MAAM,GAAG,aAAc,KAAK,QAAQ,MAAM,GAAG,OAAQ,CAAK,CAAC,EACxE,KAAK,QAAQ,MAAM,GAAG,KAAK,QAAQ,IAAI,CAAK,EAAI,UAAY,QAAS,CAAK,CAChF,CACU,CAAC,CAAC,KAAK,GAAG,CACtB,CAEA,cAAsB,EAAiB,EAAwB,EAAqB,CAClF,IAAM,EAAW,KAAK,gBAAgB,IAAI,KAAK,eAAe,GAAK,IAAI,IACvE,EAAS,QAAQ,SAAS,EAAQ,IAAU,CAC1C,IAAM,EAAU,IAAU,KAAK,cACzB,EAAU,EAAS,YAAe,EAAS,IAAI,CAAK,EAAI,MAAQ,MAAS,GAAG,EAAQ,EAAE,GACtF,EAAS,EAAU,KAAK,QAAQ,MAAM,GAAG,SAAU,IAAI,EAAI,KAC3D,EAAQ,GAAG,EAAQ,GAAG,EAAO,QACnC,KAAK,WAAW,EAAO,EAAQ,KAAK,QAAQ,MAAM,GAAG,EAAU,SAAW,OAAQ,CAAK,EAAG,CAAK,EAC3F,EAAO,aACT,KAAK,WAAW,EAAO,SAAU,KAAK,QAAQ,MAAM,GAAG,QAAS,EAAO,WAAW,EAAG,CAAK,CAE9F,CAAC,EAED,IAAM,EAAc,EAAS,QAAQ,OAC/B,EAAgB,KAAK,gBAAkB,EAC7C,KAAK,WACH,EACA,EAAgB,KAAK,QAAQ,MAAM,GAAG,SAAU,IAAI,EAAI,KACxD,KAAK,QAAQ,MAAM,GAAG,EAAgB,SAAW,OAAQ,mBAAmB,EAC5E,CACF,CACF,CAEA,cAAsB,EAAiB,EAAwB,EAAqB,CAClF,GAAI,KAAK,eAAiB,EAAS,QAAQ,OAAQ,OACnD,IAAM,EAAU,EAAS,QAAQ,KAAK,cAAc,EAAE,QACtD,GAAI,CAAC,EAAS,OACd,EAAM,KAAK,EAAE,EACb,KAAK,WAAW,EAAO,IAAK,KAAK,QAAQ,MAAM,GAAG,SAAU,KAAK,QAAQ,MAAM,KAAK,SAAS,CAAC,EAAG,CAAK,EACtG,IAAM,EAAW,IAAIC,EAAAA,SACnB,EACA,EACA,EACA,KAAK,QAAQ,cACb,CAAE,MAAQ,GAAS,KAAK,QAAQ,MAAM,GAAG,QAAS,CAAI,CAAE,EACxD,CAAE,2BAA4B,GAAM,yBAA0B,EAAK,CACrE,EACA,IAAK,IAAM,KAAe,EAAS,OAAO,KAAK,IAAI,EAAG,EAAQ,CAAC,CAAC,EAC9D,KAAK,WAAW,EAAO,MAAO,GAAe,IAAK,CAAK,CAE3D,CAEA,YAAoB,EAAiB,EAAqB,CACxD,IAAM,EAAO,KAAK,MAAM,IAAI,KAAK,eAAe,EAC5C,CAAC,GAAQ,KAAK,aAAe,UACjC,EAAM,KAAK,EAAE,EACb,KAAK,WAAW,EAAO,IAAK,KAAK,QAAQ,MAAM,GAAG,QAAS,SAAS,GAAM,EAAG,CAAK,EACpF,CAEA,aAAqB,EAAiB,EAAqB,CACzD,GAAI,CAAC,KAAK,WAAY,OACtB,EAAM,KAAK,EAAE,EACb,IAAM,EAAQ,KAAK,aAAe,SAAW,eAAiB,iBAC9D,KAAK,WAAW,EAAO,IAAK,KAAK,QAAQ,MAAM,GAAG,QAAS,CAAK,EAAG,CAAK,EACxE,IAAK,IAAM,KAAQ,KAAK,OAAO,OAAO,KAAK,IAAI,EAAG,EAAQ,CAAC,CAAC,EAC1D,EAAM,MAAA,EAAKC,EAAAA,gBAAAA,CAAgB,IAAI,IAAQ,EAAO,EAAE,CAAC,EAE/C,KAAK,qBACP,KAAK,WAAW,EAAO,IAAK,KAAK,QAAQ,MAAM,GAAG,UAAW,KAAK,mBAAmB,EAAG,CAAK,CAEjG,CAEA,SAAiB,EAAgC,CAG/C,OAFI,KAAK,WAAmB,4DAErB,mCADe,EAAS,YAAc,+BAAiC,eACtB,8BAC1D,CAEA,WAAmB,EAAiB,EAAgB,EAAc,EAAqB,CACrF,IAAM,EAAc,KAAK,KAAA,EAAIC,EAAAA,aAAAA,CAAa,CAAM,EAAG,KAAK,IAAI,EAAG,EAAQ,CAAC,CAAC,EACnE,GAAA,EAAiBD,EAAAA,gBAAAA,CAAgB,EAAQ,EAAa,EAAE,EACxD,EAAY,KAAK,IAAI,EAAG,EAAQ,CAAW,EAC3C,GAAA,EAAUE,EAAAA,iBAAAA,CAAiB,EAAM,CAAS,EAChD,GAAI,EAAQ,SAAW,EAAG,CACxB,EAAM,KAAK,CAAc,EACzB,MACF,CACA,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAQ,OAAQ,GAAS,EAAG,CACtD,IAAM,EAAa,IAAU,EAAI,EAAiB,IAAI,OAAO,CAAW,EACxE,EAAM,MAAA,EAAKF,EAAAA,gBAAAA,CAAgB,GAAG,IAAa,EAAQ,KAAU,EAAO,EAAE,CAAC,CACzE,CACF,CAEA,WAAmB,EAAqB,CACtC,IAAM,EAAQ,KAAK,YAAY,KAAK,SAAS,CAAC,EAC9C,KAAK,eAAiB,KAAK,cAAgB,EAAQ,GAAS,EAC5D,KAAK,QAAQ,CACf,CAEA,aAAqB,EAAqB,CACxC,IAAM,EAAQ,KAAK,QAAQ,OAAO,UAAU,OAC5C,KAAK,iBAAmB,KAAK,gBAAkB,EAAQ,GAAS,EAChE,KAAK,cAAgB,EACrB,KAAK,QAAQ,CACf,CAEA,qBAAoC,CAClC,IAAM,EAAW,KAAK,SAAS,EAC/B,GAAI,CAAC,EAAS,aAAe,KAAK,eAAiB,EAAS,QAAQ,OAAQ,OAC5E,IAAM,EAAW,KAAK,gBAAgB,IAAI,KAAK,eAAe,GAAK,IAAI,IACnE,EAAS,IAAI,KAAK,aAAa,EAAG,EAAS,OAAO,KAAK,aAAa,EACnE,EAAS,IAAI,KAAK,aAAa,EACpC,KAAK,gBAAgB,IAAI,KAAK,gBAAiB,CAAQ,EACvD,KAAK,QAAQ,CACf,CAEA,yBAAwC,CACtC,IAAM,EAAW,KAAK,SAAS,EAC/B,GAAI,KAAK,gBAAkB,EAAS,QAAQ,OAAQ,CAClD,KAAK,WAAW,QAAQ,EACxB,MACF,CAEA,GAAI,EAAS,YAAa,CACxB,IAAM,EAAW,KAAK,gBAAgB,IAAI,KAAK,eAAe,GAAK,IAAI,IACnE,EAAS,OAAS,GAAG,EAAS,IAAI,KAAK,aAAa,EACxD,IAAM,EAAS,CAAC,GAAG,CAAQ,CAAC,CACzB,MAAM,EAAM,IAAU,EAAO,CAAK,CAAC,CACnC,QAAS,GAAW,EAAS,QAAQ,GAAS,CAAC,EAAS,QAAQ,EAAM,CAAC,KAAK,EAAI,CAAC,CAAE,EACtF,KAAK,aAAa,CAChB,cAAe,KAAK,gBACpB,SAAU,EAAS,SACnB,KAAM,QACN,OAAQ,KACR,SAAU,EACV,GAAG,KAAK,WAAW,CACrB,CAAC,EACD,MACF,CAEA,IAAM,EAAS,EAAS,QAAQ,KAAK,eAChC,GACL,KAAK,aAAa,CAChB,cAAe,KAAK,gBACpB,SAAU,EAAS,SACnB,KAAM,SACN,OAAQ,EAAO,MACf,GAAI,EAAO,QAAU,CAAE,QAAS,EAAO,OAAQ,EAAI,CAAC,EACpD,GAAG,KAAK,WAAW,CACrB,CAAC,CACH,CAEA,WAAmB,EAAwB,CACzC,KAAK,WAAa,EAClB,KAAK,oBAAsB,IAAA,GAC3B,KAAK,OAAO,QAAQ,IAAS,QAAW,KAAK,MAAM,IAAI,KAAK,eAAe,GAAK,GAAM,EAAE,EACxF,KAAK,QAAQ,CACf,CAEA,MAAc,oBAAoC,CAChD,IAAM,EAAa,KAAK,WACnB,KACL,MAAK,QAAQ,IAAI,KAAK,EACtB,GAAI,CACF,IAAM,EAAS,MAAM,KAAK,QAAQ,eAAe,KAAK,OAAO,QAAQ,CAAC,EACtE,GAAI,KAAK,SAAW,KAAK,aAAe,EAAY,OAChD,EAAO,SAAW,YACpB,KAAK,OAAO,QAAQ,EAAO,OAAO,EAClC,KAAK,oBAAsB,IAAA,IAE3B,KAAK,oBAAsB,EAAO,OAEtC,OAAS,EAAO,CACd,KAAK,oBAAsB,2BAA2B,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,GAC7G,QAAU,CACR,KAAK,QAAQ,IAAI,MAAM,EACvB,KAAK,QAAQ,CACf,CAfsB,CAgBxB,CAEA,aAAqB,EAAqB,CACxC,IAAM,EAAO,KAAK,WAGlB,GAFA,KAAK,WAAa,IAAA,GAClB,KAAK,OAAO,QAAQ,EAAE,EAClB,IAAS,QAAS,CACpB,IAAM,EAAU,EAAM,KAAK,EACvB,EAAS,KAAK,MAAM,IAAI,KAAK,gBAAiB,CAAO,EACpD,KAAK,MAAM,OAAO,KAAK,eAAe,EAC3C,IAAM,EAAS,KAAK,QAAQ,IAAI,KAAK,eAAe,EAChD,IACF,KAAK,QAAQ,IAAI,KAAK,gBAAiB,CACrC,GAAG,EACH,GAAI,EAAU,CAAE,MAAO,CAAQ,EAAI,CAAE,MAAO,IAAA,EAAU,CACxD,CAAC,EACD,KAAK,eAAe,GAEtB,KAAK,QAAQ,EACb,MACF,CACI,IAAS,UACX,KAAK,aAAa,CAChB,cAAe,KAAK,gBACpB,SAAU,KAAK,SAAS,CAAC,CAAC,SAC1B,KAAM,SACN,OAAQ,EACR,GAAG,KAAK,WAAW,CACrB,CAAC,CAEL,CAEA,YAAyC,CACvC,IAAM,EAAO,KAAK,MAAM,IAAI,KAAK,eAAe,EAChD,OAAO,EAAO,CAAE,MAAO,CAAK,EAAI,CAAC,CACnC,CAEA,aAAqB,EAA8B,CAGjD,GAFA,KAAK,QAAQ,IAAI,KAAK,gBAAiB,CAAM,EAC7C,KAAK,eAAe,EAChB,KAAK,QAAQ,OAAS,KAAK,QAAQ,OAAO,UAAU,OAAQ,CAC9D,KAAK,OAAO,EAAK,EACjB,MACF,CACA,IAAM,EAAO,KAAK,QAAQ,OAAO,UAAU,WACxC,EAAW,IAAU,EAAQ,KAAK,iBAAmB,CAAC,KAAK,QAAQ,IAAI,CAAK,CAC/E,EACA,KAAK,gBACH,GAAQ,EAAI,EAAO,KAAK,QAAQ,OAAO,UAAU,WAAW,EAAW,IAAU,CAAC,KAAK,QAAQ,IAAI,CAAK,CAAC,EAC3G,KAAK,cAAgB,EACrB,KAAK,QAAQ,CACf,CAEA,gBAA2C,CACzC,MAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,CAAC,CAC9B,MAAM,EAAM,IAAU,EAAK,cAAgB,EAAM,aAAa,CAAC,CAC/D,IAAK,IAAY,CAAE,GAAG,CAAO,EAAE,CACpC,CAEA,gBAA+B,CAC7B,KAAK,QAAQ,iBAAiB,CAAE,QAAS,KAAK,eAAe,EAAG,UAAW,EAAM,CAAC,CACpF,CAEA,OAAe,EAA0B,CACnC,KAAK,UACT,KAAK,QAAU,GACf,KAAK,QAAQ,KAAK,CAAE,QAAS,KAAK,eAAe,EAAG,WAAU,CAAC,EACjE,CAEA,SAAwB,CACtB,KAAK,WAAW,EAChB,KAAK,QAAQ,IAAI,cAAc,CACjC,CACF"}
1
+ {"version":3,"file":"questionnaireComponent.cjs","names":["Editor","matchesKey","Key","Markdown","truncateToWidth","visibleWidth","wrapTextWithAnsi"],"sources":["../../src/tui/questionnaireComponent.ts"],"sourcesContent":["import type { KeybindingsManager, Theme } from '@earendil-works/pi-coding-agent';\nimport {\n Editor,\n Key,\n Markdown,\n matchesKey,\n type Focusable,\n type MarkdownTheme,\n type TUI,\n truncateToWidth,\n visibleWidth,\n wrapTextWithAnsi,\n} from '@earendil-works/pi-tui';\nimport type { QuestionData, QuestionParams } from '../schemas/questionnaire.js';\nimport type { ExternalEditResult, QuestionAnswer, QuestionnaireResult } from '../types/questionnaire.js';\n\nexport interface QuestionnaireComponentOptions {\n tui: TUI;\n theme: Theme;\n markdownTheme: MarkdownTheme;\n keybindings: KeybindingsManager;\n params: QuestionParams;\n collapseKey: string;\n done: (result: QuestionnaireResult) => void;\n editExternally: (content: string) => Promise<ExternalEditResult>;\n reportProgress?: (result: QuestionnaireResult) => void;\n}\n\ntype EditorMode = 'custom' | 'notes';\n\nconst SELECT_CANCEL_KEYBINDING = 'tui.select.cancel';\nconst EXTERNAL_EDITOR_KEYBINDING = 'app.editor.external';\n\nexport class QuestionnaireComponent implements Focusable {\n private readonly answers = new Map<number, QuestionAnswer>();\n private readonly selectedOptions = new Map<number, Set<number>>();\n private readonly notes = new Map<number, string>();\n private readonly editor: Editor;\n private currentQuestion = 0;\n private currentOption = 0;\n private editorMode: EditorMode | undefined;\n private collapsed = false;\n private settled = false;\n private externalEditorError: string | undefined;\n private cachedLines: string[] | undefined;\n private cachedWidth: number | undefined;\n private isFocused = false;\n\n constructor(private readonly options: QuestionnaireComponentOptions) {\n this.editor = new Editor(options.tui, {\n borderColor: (text) => options.theme.fg('accent', text),\n selectList: {\n selectedPrefix: (text) => options.theme.fg('accent', text),\n selectedText: (text) => options.theme.fg('accent', text),\n description: (text) => options.theme.fg('muted', text),\n scrollInfo: (text) => options.theme.fg('dim', text),\n noMatch: (text) => options.theme.fg('warning', text),\n },\n });\n this.editor.onSubmit = (value) => this.submitEditor(value);\n }\n\n get focused(): boolean {\n return this.isFocused;\n }\n\n set focused(value: boolean) {\n this.isFocused = value;\n this.editor.focused = value;\n this.invalidate();\n }\n\n invalidate(): void {\n this.cachedLines = undefined;\n this.cachedWidth = undefined;\n }\n\n cancel(): void {\n this.finish(true);\n }\n\n handleInput(data: string): void {\n if (this.settled) return;\n\n if (\n this.options.collapseKey !== 'off' &&\n matchesKey(data, this.options.collapseKey as Parameters<typeof matchesKey>[1])\n ) {\n this.collapsed = !this.collapsed;\n this.refresh();\n return;\n }\n\n if (this.collapsed) return;\n\n if (this.editorMode) {\n if (this.options.keybindings.matches(data, SELECT_CANCEL_KEYBINDING)) {\n this.editorMode = undefined;\n this.externalEditorError = undefined;\n this.editor.setText('');\n this.refresh();\n return;\n }\n if (this.options.keybindings.matches(data, EXTERNAL_EDITOR_KEYBINDING)) {\n void this.openExternalEditor();\n return;\n }\n this.editor.handleInput(data);\n this.refresh();\n return;\n }\n\n if (this.options.keybindings.matches(data, SELECT_CANCEL_KEYBINDING)) {\n this.cancel();\n return;\n }\n if (matchesKey(data, Key.up)) {\n this.moveOption(-1);\n return;\n }\n if (matchesKey(data, Key.down)) {\n this.moveOption(1);\n return;\n }\n if (matchesKey(data, Key.tab) || matchesKey(data, Key.right)) {\n this.moveQuestion(1);\n return;\n }\n if (matchesKey(data, Key.shift('tab')) || matchesKey(data, Key.left)) {\n this.moveQuestion(-1);\n return;\n }\n if (matchesKey(data, 'n') || matchesKey(data, Key.shift('n'))) {\n this.openEditor('notes');\n return;\n }\n if (matchesKey(data, Key.space) || data === ' ') {\n this.toggleCurrentOption();\n return;\n }\n if (matchesKey(data, Key.enter)) {\n this.confirmCurrentSelection();\n }\n }\n\n render(width: number): string[] {\n const renderWidth = Math.max(1, width);\n if (this.cachedLines && this.cachedWidth === renderWidth) return this.cachedLines;\n this.cachedWidth = renderWidth;\n if (this.collapsed) {\n const lines: string[] = [];\n const answerCount = this.answers.size;\n this.addWrapped(\n lines,\n '',\n this.options.theme.fg(\n 'accent',\n `▸ Questionnaire (${answerCount}/${this.options.params.questions.length} answered) — ${this.options.collapseKey} to expand`,\n ),\n renderWidth,\n );\n this.cachedLines = lines;\n return lines;\n }\n\n const lines: string[] = [];\n const question = this.question();\n const border = this.options.theme.fg('accent', '─'.repeat(renderWidth));\n lines.push(border);\n this.addWrapped(lines, ' ', this.renderTabs(), renderWidth);\n lines.push('');\n this.addWrapped(lines, ' ', this.options.theme.bold(question.question), renderWidth);\n lines.push('');\n\n this.renderOptions(lines, question, renderWidth);\n this.renderPreview(lines, question, renderWidth);\n this.renderNotes(lines, renderWidth);\n this.renderEditor(lines, renderWidth);\n lines.push('');\n this.addWrapped(lines, ' ', this.helpText(question), renderWidth);\n lines.push(border);\n this.cachedLines = lines;\n return lines;\n }\n\n private question(): QuestionData {\n const question = this.options.params.questions[this.currentQuestion];\n if (!question) throw new Error('Questionnaire has no active question');\n return question;\n }\n\n private optionCount(question: QuestionData): number {\n return question.options.length + 1;\n }\n\n private renderTabs(): string {\n const tabs = this.options.params.questions.map((question, index) => {\n const mark = this.answers.has(index) ? '✓' : '○';\n const label = ` ${mark} ${question.header} `;\n return index === this.currentQuestion\n ? this.options.theme.bg('selectedBg', this.options.theme.fg('text', label))\n : this.options.theme.fg(this.answers.has(index) ? 'success' : 'muted', label);\n });\n return tabs.join(' ');\n }\n\n private renderOptions(lines: string[], question: QuestionData, width: number): void {\n const selected = this.selectedOptions.get(this.currentQuestion) ?? new Set<number>();\n question.options.forEach((option, index) => {\n const focused = index === this.currentOption;\n const checked = question.multiSelect ? (selected.has(index) ? '[x]' : '[ ]') : `${index + 1}.`;\n const prefix = focused ? this.options.theme.fg('accent', '> ') : ' ';\n const label = `${checked} ${option.label}`;\n this.addWrapped(lines, prefix, this.options.theme.fg(focused ? 'accent' : 'text', label), width);\n if (option.description) {\n this.addWrapped(lines, ' ', this.options.theme.fg('muted', option.description), width);\n }\n });\n\n const customIndex = question.options.length;\n const customFocused = this.currentOption === customIndex;\n this.addWrapped(\n lines,\n customFocused ? this.options.theme.fg('accent', '> ') : ' ',\n this.options.theme.fg(customFocused ? 'accent' : 'text', '✎ Type something.'),\n width,\n );\n }\n\n private renderPreview(lines: string[], question: QuestionData, width: number): void {\n if (this.currentOption >= question.options.length) return;\n const preview = question.options[this.currentOption]?.preview;\n if (!preview) return;\n lines.push('');\n this.addWrapped(lines, ' ', this.options.theme.fg('accent', this.options.theme.bold('Preview')), width);\n const markdown = new Markdown(\n preview,\n 0,\n 0,\n this.options.markdownTheme,\n { color: (text) => this.options.theme.fg('muted', text) },\n { preserveOrderedListMarkers: true, preserveBackslashEscapes: true },\n );\n for (const previewLine of markdown.render(Math.max(1, width - 3))) {\n this.addWrapped(lines, ' │ ', previewLine || ' ', width);\n }\n }\n\n private renderNotes(lines: string[], width: number): void {\n const note = this.notes.get(this.currentQuestion);\n if (!note || this.editorMode === 'notes') return;\n lines.push('');\n this.addWrapped(lines, ' ', this.options.theme.fg('muted', `Note: ${note}`), width);\n }\n\n private renderEditor(lines: string[], width: number): void {\n if (!this.editorMode) return;\n lines.push('');\n const label = this.editorMode === 'custom' ? 'Your answer:' : 'Optional note:';\n this.addWrapped(lines, ' ', this.options.theme.fg('muted', label), width);\n for (const line of this.editor.render(Math.max(1, width - 2))) {\n lines.push(truncateToWidth(` ${line}`, width, ''));\n }\n if (this.externalEditorError) {\n this.addWrapped(lines, ' ', this.options.theme.fg('warning', this.externalEditorError), width);\n }\n }\n\n private helpText(question: QuestionData): string {\n if (this.editorMode) return 'Enter submit • Esc/Ctrl+C return • Ctrl+G external editor';\n const selectionHelp = question.multiSelect ? 'Space toggle • Enter confirm' : 'Enter select';\n return `Tab/←→ questions • ↑↓ options • ${selectionHelp} • N note • Esc/Ctrl+C cancel`;\n }\n\n private addWrapped(lines: string[], prefix: string, text: string, width: number): void {\n const prefixWidth = Math.min(visibleWidth(prefix), Math.max(0, width - 1));\n const renderedPrefix = truncateToWidth(prefix, prefixWidth, '');\n const available = Math.max(1, width - prefixWidth);\n const wrapped = wrapTextWithAnsi(text, available);\n if (wrapped.length === 0) {\n lines.push(renderedPrefix);\n return;\n }\n for (let index = 0; index < wrapped.length; index += 1) {\n const linePrefix = index === 0 ? renderedPrefix : ' '.repeat(prefixWidth);\n lines.push(truncateToWidth(`${linePrefix}${wrapped[index]}`, width, ''));\n }\n }\n\n private moveOption(delta: number): void {\n const count = this.optionCount(this.question());\n this.currentOption = (this.currentOption + delta + count) % count;\n this.refresh();\n }\n\n private moveQuestion(delta: number): void {\n const count = this.options.params.questions.length;\n this.currentQuestion = (this.currentQuestion + delta + count) % count;\n this.currentOption = 0;\n this.refresh();\n }\n\n private toggleCurrentOption(): void {\n const question = this.question();\n if (!question.multiSelect || this.currentOption >= question.options.length) return;\n const selected = this.selectedOptions.get(this.currentQuestion) ?? new Set<number>();\n if (selected.has(this.currentOption)) selected.delete(this.currentOption);\n else selected.add(this.currentOption);\n this.selectedOptions.set(this.currentQuestion, selected);\n this.refresh();\n }\n\n private confirmCurrentSelection(): void {\n const question = this.question();\n if (this.currentOption === question.options.length) {\n this.openEditor('custom');\n return;\n }\n\n if (question.multiSelect) {\n const selected = this.selectedOptions.get(this.currentQuestion) ?? new Set<number>();\n if (selected.size === 0) selected.add(this.currentOption);\n const labels = [...selected]\n .sort((left, right) => left - right)\n .flatMap((index) => (question.options[index] ? [question.options[index].label] : []));\n this.recordAnswer({\n questionIndex: this.currentQuestion,\n question: question.question,\n kind: 'multi',\n answer: null,\n selected: labels,\n ...this.noteFields(),\n });\n return;\n }\n\n const option = question.options[this.currentOption];\n if (!option) return;\n this.recordAnswer({\n questionIndex: this.currentQuestion,\n question: question.question,\n kind: 'option',\n answer: option.label,\n ...(option.preview ? { preview: option.preview } : {}),\n ...this.noteFields(),\n });\n }\n\n private openEditor(mode: EditorMode): void {\n this.editorMode = mode;\n this.externalEditorError = undefined;\n this.editor.setText(mode === 'notes' ? (this.notes.get(this.currentQuestion) ?? '') : '');\n this.refresh();\n }\n\n private async openExternalEditor(): Promise<void> {\n const editorMode = this.editorMode;\n if (!editorMode) return;\n this.options.tui.stop();\n try {\n const result = await this.options.editExternally(this.editor.getText());\n if (this.settled || this.editorMode !== editorMode) return;\n if (result.status === 'complete') {\n this.editor.setText(result.content);\n this.externalEditorError = undefined;\n } else {\n this.externalEditorError = result.message;\n }\n } catch (error) {\n this.externalEditorError = `External editor failed: ${error instanceof Error ? error.message : String(error)}`;\n } finally {\n this.options.tui.start();\n this.refresh();\n }\n }\n\n private submitEditor(value: string): void {\n const mode = this.editorMode;\n this.editorMode = undefined;\n this.editor.setText('');\n if (mode === 'notes') {\n const trimmed = value.trim();\n if (trimmed) this.notes.set(this.currentQuestion, trimmed);\n else this.notes.delete(this.currentQuestion);\n const answer = this.answers.get(this.currentQuestion);\n if (answer) {\n this.answers.set(this.currentQuestion, {\n ...answer,\n ...(trimmed ? { notes: trimmed } : { notes: undefined }),\n });\n this.reportProgress();\n }\n this.refresh();\n return;\n }\n if (mode === 'custom') {\n this.recordAnswer({\n questionIndex: this.currentQuestion,\n question: this.question().question,\n kind: 'custom',\n answer: value,\n ...this.noteFields(),\n });\n }\n }\n\n private noteFields(): { notes?: string } {\n const note = this.notes.get(this.currentQuestion);\n return note ? { notes: note } : {};\n }\n\n private recordAnswer(answer: QuestionAnswer): void {\n this.answers.set(this.currentQuestion, answer);\n this.reportProgress();\n if (this.answers.size === this.options.params.questions.length) {\n this.finish(false);\n return;\n }\n const next = this.options.params.questions.findIndex(\n (_question, index) => index > this.currentQuestion && !this.answers.has(index),\n );\n this.currentQuestion =\n next >= 0 ? next : this.options.params.questions.findIndex((_question, index) => !this.answers.has(index));\n this.currentOption = 0;\n this.refresh();\n }\n\n private orderedAnswers(): QuestionAnswer[] {\n return [...this.answers.values()]\n .sort((left, right) => left.questionIndex - right.questionIndex)\n .map((answer) => ({ ...answer }));\n }\n\n private reportProgress(): void {\n this.options.reportProgress?.({ answers: this.orderedAnswers(), cancelled: false });\n }\n\n private finish(cancelled: boolean): void {\n if (this.settled) return;\n this.settled = true;\n this.options.done({ answers: this.orderedAnswers(), cancelled });\n }\n\n private refresh(): void {\n this.invalidate();\n this.options.tui.requestRender();\n }\n}\n"],"mappings":"wCA8BA,MAAM,EAA2B,oBAGjC,IAAa,EAAb,KAAyD,CAe1B,QAd7B,QAA2B,IAAI,IAC/B,gBAAmC,IAAI,IACvC,MAAyB,IAAI,IAC7B,OACA,gBAA0B,EAC1B,cAAwB,EACxB,WACA,UAAoB,GACpB,QAAkB,GAClB,oBACA,YACA,YACA,UAAoB,GAEpB,YAAY,EAAyD,CAAxC,KAAA,QAAA,EAC3B,KAAK,OAAS,IAAIA,EAAAA,OAAO,EAAQ,IAAK,CACpC,YAAc,GAAS,EAAQ,MAAM,GAAG,SAAU,CAAI,EACtD,WAAY,CACV,eAAiB,GAAS,EAAQ,MAAM,GAAG,SAAU,CAAI,EACzD,aAAe,GAAS,EAAQ,MAAM,GAAG,SAAU,CAAI,EACvD,YAAc,GAAS,EAAQ,MAAM,GAAG,QAAS,CAAI,EACrD,WAAa,GAAS,EAAQ,MAAM,GAAG,MAAO,CAAI,EAClD,QAAU,GAAS,EAAQ,MAAM,GAAG,UAAW,CAAI,CACrD,CACF,CAAC,EACD,KAAK,OAAO,SAAY,GAAU,KAAK,aAAa,CAAK,CAC3D,CAEA,IAAI,SAAmB,CACrB,OAAO,KAAK,SACd,CAEA,IAAI,QAAQ,EAAgB,CAC1B,KAAK,UAAY,EACjB,KAAK,OAAO,QAAU,EACtB,KAAK,WAAW,CAClB,CAEA,YAAmB,CACjB,KAAK,YAAc,IAAA,GACnB,KAAK,YAAc,IAAA,EACrB,CAEA,QAAe,CACb,KAAK,OAAO,EAAI,CAClB,CAEA,YAAY,EAAoB,CAC1B,SAAK,QAET,IACE,KAAK,QAAQ,cAAgB,QAAA,EAC7BC,EAAAA,WAAAA,CAAW,EAAM,KAAK,QAAQ,WAA+C,EAC7E,CACA,KAAK,UAAY,CAAC,KAAK,UACvB,KAAK,QAAQ,EACb,MACF,CAEI,SAAK,UAET,IAAI,KAAK,WAAY,CACnB,GAAI,KAAK,QAAQ,YAAY,QAAQ,EAAM,CAAwB,EAAG,CACpE,KAAK,WAAa,IAAA,GAClB,KAAK,oBAAsB,IAAA,GAC3B,KAAK,OAAO,QAAQ,EAAE,EACtB,KAAK,QAAQ,EACb,MACF,CACA,GAAI,KAAK,QAAQ,YAAY,QAAQ,EAAM,qBAA0B,EAAG,CACtE,KAAU,mBAAmB,EAC7B,MACF,CACA,KAAK,OAAO,YAAY,CAAI,EAC5B,KAAK,QAAQ,EACb,MACF,CAEA,GAAI,KAAK,QAAQ,YAAY,QAAQ,EAAM,CAAwB,EAAG,CACpE,KAAK,OAAO,EACZ,MACF,CACA,IAAA,EAAIA,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,EAAE,EAAG,CAC5B,KAAK,WAAW,EAAE,EAClB,MACF,CACA,IAAA,EAAID,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,IAAI,EAAG,CAC9B,KAAK,WAAW,CAAC,EACjB,MACF,CACA,IAAA,EAAID,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,GAAG,IAAA,EAAKD,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,KAAK,EAAG,CAC5D,KAAK,aAAa,CAAC,EACnB,MACF,CACA,IAAA,EAAID,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,MAAM,KAAK,CAAC,IAAA,EAAKD,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,IAAI,EAAG,CACpE,KAAK,aAAa,EAAE,EACpB,MACF,CACA,IAAA,EAAID,EAAAA,WAAAA,CAAW,EAAM,GAAG,IAAA,EAAKA,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,MAAM,GAAG,CAAC,EAAG,CAC7D,KAAK,WAAW,OAAO,EACvB,MACF,CACA,IAAA,EAAID,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,KAAK,GAAK,IAAS,IAAK,CAC/C,KAAK,oBAAoB,EACzB,MACF,EACA,EAAID,EAAAA,WAAAA,CAAW,EAAMC,EAAAA,IAAI,KAAK,GAC5B,KAAK,wBAAwB,CA/B/B,CAnBA,CAoDF,CAEA,OAAO,EAAyB,CAC9B,IAAM,EAAc,KAAK,IAAI,EAAG,CAAK,EACrC,GAAI,KAAK,aAAe,KAAK,cAAgB,EAAa,OAAO,KAAK,YAEtE,GADA,KAAK,YAAc,EACf,KAAK,UAAW,CAClB,IAAM,EAAkB,CAAC,EACnB,EAAc,KAAK,QAAQ,KAWjC,OAVA,KAAK,WACH,EACA,GACA,KAAK,QAAQ,MAAM,GACjB,SACA,oBAAoB,EAAY,GAAG,KAAK,QAAQ,OAAO,UAAU,OAAO,eAAe,KAAK,QAAQ,YAAY,WAClH,EACA,CACF,EACA,KAAK,YAAc,EACZ,CACT,CAEA,IAAM,EAAkB,CAAC,EACnB,EAAW,KAAK,SAAS,EACzB,EAAS,KAAK,QAAQ,MAAM,GAAG,SAAU,IAAI,OAAO,CAAW,CAAC,EAetE,OAdA,EAAM,KAAK,CAAM,EACjB,KAAK,WAAW,EAAO,IAAK,KAAK,WAAW,EAAG,CAAW,EAC1D,EAAM,KAAK,EAAE,EACb,KAAK,WAAW,EAAO,IAAK,KAAK,QAAQ,MAAM,KAAK,EAAS,QAAQ,EAAG,CAAW,EACnF,EAAM,KAAK,EAAE,EAEb,KAAK,cAAc,EAAO,EAAU,CAAW,EAC/C,KAAK,cAAc,EAAO,EAAU,CAAW,EAC/C,KAAK,YAAY,EAAO,CAAW,EACnC,KAAK,aAAa,EAAO,CAAW,EACpC,EAAM,KAAK,EAAE,EACb,KAAK,WAAW,EAAO,IAAK,KAAK,SAAS,CAAQ,EAAG,CAAW,EAChE,EAAM,KAAK,CAAM,EACjB,KAAK,YAAc,EACZ,CACT,CAEA,UAAiC,CAC/B,IAAM,EAAW,KAAK,QAAQ,OAAO,UAAU,KAAK,iBACpD,GAAI,CAAC,EAAU,MAAU,MAAM,sCAAsC,EACrE,OAAO,CACT,CAEA,YAAoB,EAAgC,CAClD,OAAO,EAAS,QAAQ,OAAS,CACnC,CAEA,YAA6B,CAQ3B,OAPa,KAAK,QAAQ,OAAO,UAAU,KAAK,EAAU,IAAU,CAElE,IAAM,EAAQ,IADD,KAAK,QAAQ,IAAI,CAAK,EAAI,IAAM,IACtB,GAAG,EAAS,OAAO,GAC1C,OAAO,IAAU,KAAK,gBAClB,KAAK,QAAQ,MAAM,GAAG,aAAc,KAAK,QAAQ,MAAM,GAAG,OAAQ,CAAK,CAAC,EACxE,KAAK,QAAQ,MAAM,GAAG,KAAK,QAAQ,IAAI,CAAK,EAAI,UAAY,QAAS,CAAK,CAChF,CACU,CAAC,CAAC,KAAK,GAAG,CACtB,CAEA,cAAsB,EAAiB,EAAwB,EAAqB,CAClF,IAAM,EAAW,KAAK,gBAAgB,IAAI,KAAK,eAAe,GAAK,IAAI,IACvE,EAAS,QAAQ,SAAS,EAAQ,IAAU,CAC1C,IAAM,EAAU,IAAU,KAAK,cACzB,EAAU,EAAS,YAAe,EAAS,IAAI,CAAK,EAAI,MAAQ,MAAS,GAAG,EAAQ,EAAE,GACtF,EAAS,EAAU,KAAK,QAAQ,MAAM,GAAG,SAAU,IAAI,EAAI,KAC3D,EAAQ,GAAG,EAAQ,GAAG,EAAO,QACnC,KAAK,WAAW,EAAO,EAAQ,KAAK,QAAQ,MAAM,GAAG,EAAU,SAAW,OAAQ,CAAK,EAAG,CAAK,EAC3F,EAAO,aACT,KAAK,WAAW,EAAO,SAAU,KAAK,QAAQ,MAAM,GAAG,QAAS,EAAO,WAAW,EAAG,CAAK,CAE9F,CAAC,EAED,IAAM,EAAc,EAAS,QAAQ,OAC/B,EAAgB,KAAK,gBAAkB,EAC7C,KAAK,WACH,EACA,EAAgB,KAAK,QAAQ,MAAM,GAAG,SAAU,IAAI,EAAI,KACxD,KAAK,QAAQ,MAAM,GAAG,EAAgB,SAAW,OAAQ,mBAAmB,EAC5E,CACF,CACF,CAEA,cAAsB,EAAiB,EAAwB,EAAqB,CAClF,GAAI,KAAK,eAAiB,EAAS,QAAQ,OAAQ,OACnD,IAAM,EAAU,EAAS,QAAQ,KAAK,cAAc,EAAE,QACtD,GAAI,CAAC,EAAS,OACd,EAAM,KAAK,EAAE,EACb,KAAK,WAAW,EAAO,IAAK,KAAK,QAAQ,MAAM,GAAG,SAAU,KAAK,QAAQ,MAAM,KAAK,SAAS,CAAC,EAAG,CAAK,EACtG,IAAM,EAAW,IAAIC,EAAAA,SACnB,EACA,EACA,EACA,KAAK,QAAQ,cACb,CAAE,MAAQ,GAAS,KAAK,QAAQ,MAAM,GAAG,QAAS,CAAI,CAAE,EACxD,CAAE,2BAA4B,GAAM,yBAA0B,EAAK,CACrE,EACA,IAAK,IAAM,KAAe,EAAS,OAAO,KAAK,IAAI,EAAG,EAAQ,CAAC,CAAC,EAC9D,KAAK,WAAW,EAAO,MAAO,GAAe,IAAK,CAAK,CAE3D,CAEA,YAAoB,EAAiB,EAAqB,CACxD,IAAM,EAAO,KAAK,MAAM,IAAI,KAAK,eAAe,EAC3C,GAAQ,KAAK,aAAe,UACjC,EAAM,KAAK,EAAE,EACb,KAAK,WAAW,EAAO,IAAK,KAAK,QAAQ,MAAM,GAAG,QAAS,SAAS,GAAM,EAAG,CAAK,EACpF,CAEA,aAAqB,EAAiB,EAAqB,CACzD,GAAI,CAAC,KAAK,WAAY,OACtB,EAAM,KAAK,EAAE,EACb,IAAM,EAAQ,KAAK,aAAe,SAAW,eAAiB,iBAC9D,KAAK,WAAW,EAAO,IAAK,KAAK,QAAQ,MAAM,GAAG,QAAS,CAAK,EAAG,CAAK,EACxE,IAAK,IAAM,KAAQ,KAAK,OAAO,OAAO,KAAK,IAAI,EAAG,EAAQ,CAAC,CAAC,EAC1D,EAAM,MAAA,EAAKC,EAAAA,gBAAAA,CAAgB,IAAI,IAAQ,EAAO,EAAE,CAAC,EAE/C,KAAK,qBACP,KAAK,WAAW,EAAO,IAAK,KAAK,QAAQ,MAAM,GAAG,UAAW,KAAK,mBAAmB,EAAG,CAAK,CAEjG,CAEA,SAAiB,EAAgC,CAG/C,OAFI,KAAK,WAAmB,4DAErB,mCADe,EAAS,YAAc,+BAAiC,eACtB,8BAC1D,CAEA,WAAmB,EAAiB,EAAgB,EAAc,EAAqB,CACrF,IAAM,EAAc,KAAK,KAAA,EAAIC,EAAAA,aAAAA,CAAa,CAAM,EAAG,KAAK,IAAI,EAAG,EAAQ,CAAC,CAAC,EACnE,GAAA,EAAiBD,EAAAA,gBAAAA,CAAgB,EAAQ,EAAa,EAAE,EACxD,EAAY,KAAK,IAAI,EAAG,EAAQ,CAAW,EAC3C,GAAA,EAAUE,EAAAA,iBAAAA,CAAiB,EAAM,CAAS,EAChD,GAAI,EAAQ,SAAW,EAAG,CACxB,EAAM,KAAK,CAAc,EACzB,MACF,CACA,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAQ,OAAQ,GAAS,EAAG,CACtD,IAAM,EAAa,IAAU,EAAI,EAAiB,IAAI,OAAO,CAAW,EACxE,EAAM,MAAA,EAAKF,EAAAA,gBAAAA,CAAgB,GAAG,IAAa,EAAQ,KAAU,EAAO,EAAE,CAAC,CACzE,CACF,CAEA,WAAmB,EAAqB,CACtC,IAAM,EAAQ,KAAK,YAAY,KAAK,SAAS,CAAC,EAC9C,KAAK,eAAiB,KAAK,cAAgB,EAAQ,GAAS,EAC5D,KAAK,QAAQ,CACf,CAEA,aAAqB,EAAqB,CACxC,IAAM,EAAQ,KAAK,QAAQ,OAAO,UAAU,OAC5C,KAAK,iBAAmB,KAAK,gBAAkB,EAAQ,GAAS,EAChE,KAAK,cAAgB,EACrB,KAAK,QAAQ,CACf,CAEA,qBAAoC,CAClC,IAAM,EAAW,KAAK,SAAS,EAC/B,GAAI,CAAC,EAAS,aAAe,KAAK,eAAiB,EAAS,QAAQ,OAAQ,OAC5E,IAAM,EAAW,KAAK,gBAAgB,IAAI,KAAK,eAAe,GAAK,IAAI,IACnE,EAAS,IAAI,KAAK,aAAa,EAAG,EAAS,OAAO,KAAK,aAAa,EACnE,EAAS,IAAI,KAAK,aAAa,EACpC,KAAK,gBAAgB,IAAI,KAAK,gBAAiB,CAAQ,EACvD,KAAK,QAAQ,CACf,CAEA,yBAAwC,CACtC,IAAM,EAAW,KAAK,SAAS,EAC/B,GAAI,KAAK,gBAAkB,EAAS,QAAQ,OAAQ,CAClD,KAAK,WAAW,QAAQ,EACxB,MACF,CAEA,GAAI,EAAS,YAAa,CACxB,IAAM,EAAW,KAAK,gBAAgB,IAAI,KAAK,eAAe,GAAK,IAAI,IACnE,EAAS,OAAS,GAAG,EAAS,IAAI,KAAK,aAAa,EACxD,IAAM,EAAS,CAAC,GAAG,CAAQ,CAAC,CACzB,MAAM,EAAM,IAAU,EAAO,CAAK,CAAC,CACnC,QAAS,GAAW,EAAS,QAAQ,GAAS,CAAC,EAAS,QAAQ,EAAM,CAAC,KAAK,EAAI,CAAC,CAAE,EACtF,KAAK,aAAa,CAChB,cAAe,KAAK,gBACpB,SAAU,EAAS,SACnB,KAAM,QACN,OAAQ,KACR,SAAU,EACV,GAAG,KAAK,WAAW,CACrB,CAAC,EACD,MACF,CAEA,IAAM,EAAS,EAAS,QAAQ,KAAK,eAChC,GACL,KAAK,aAAa,CAChB,cAAe,KAAK,gBACpB,SAAU,EAAS,SACnB,KAAM,SACN,OAAQ,EAAO,MACf,GAAI,EAAO,QAAU,CAAE,QAAS,EAAO,OAAQ,EAAI,CAAC,EACpD,GAAG,KAAK,WAAW,CACrB,CAAC,CACH,CAEA,WAAmB,EAAwB,CACzC,KAAK,WAAa,EAClB,KAAK,oBAAsB,IAAA,GAC3B,KAAK,OAAO,QAAQ,IAAS,QAAW,KAAK,MAAM,IAAI,KAAK,eAAe,GAAK,GAAM,EAAE,EACxF,KAAK,QAAQ,CACf,CAEA,MAAc,oBAAoC,CAChD,IAAM,EAAa,KAAK,WACnB,KACL,MAAK,QAAQ,IAAI,KAAK,EACtB,GAAI,CACF,IAAM,EAAS,MAAM,KAAK,QAAQ,eAAe,KAAK,OAAO,QAAQ,CAAC,EACtE,GAAI,KAAK,SAAW,KAAK,aAAe,EAAY,OAChD,EAAO,SAAW,YACpB,KAAK,OAAO,QAAQ,EAAO,OAAO,EAClC,KAAK,oBAAsB,IAAA,IAE3B,KAAK,oBAAsB,EAAO,OAEtC,OAAS,EAAO,CACd,KAAK,oBAAsB,2BAA2B,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,GAC7G,QAAU,CACR,KAAK,QAAQ,IAAI,MAAM,EACvB,KAAK,QAAQ,CACf,CAfsB,CAgBxB,CAEA,aAAqB,EAAqB,CACxC,IAAM,EAAO,KAAK,WAGlB,GAFA,KAAK,WAAa,IAAA,GAClB,KAAK,OAAO,QAAQ,EAAE,EAClB,IAAS,QAAS,CACpB,IAAM,EAAU,EAAM,KAAK,EACvB,EAAS,KAAK,MAAM,IAAI,KAAK,gBAAiB,CAAO,EACpD,KAAK,MAAM,OAAO,KAAK,eAAe,EAC3C,IAAM,EAAS,KAAK,QAAQ,IAAI,KAAK,eAAe,EAChD,IACF,KAAK,QAAQ,IAAI,KAAK,gBAAiB,CACrC,GAAG,EACH,GAAI,EAAU,CAAE,MAAO,CAAQ,EAAI,CAAE,MAAO,IAAA,EAAU,CACxD,CAAC,EACD,KAAK,eAAe,GAEtB,KAAK,QAAQ,EACb,MACF,CACI,IAAS,UACX,KAAK,aAAa,CAChB,cAAe,KAAK,gBACpB,SAAU,KAAK,SAAS,CAAC,CAAC,SAC1B,KAAM,SACN,OAAQ,EACR,GAAG,KAAK,WAAW,CACrB,CAAC,CAEL,CAEA,YAAyC,CACvC,IAAM,EAAO,KAAK,MAAM,IAAI,KAAK,eAAe,EAChD,OAAO,EAAO,CAAE,MAAO,CAAK,EAAI,CAAC,CACnC,CAEA,aAAqB,EAA8B,CAGjD,GAFA,KAAK,QAAQ,IAAI,KAAK,gBAAiB,CAAM,EAC7C,KAAK,eAAe,EAChB,KAAK,QAAQ,OAAS,KAAK,QAAQ,OAAO,UAAU,OAAQ,CAC9D,KAAK,OAAO,EAAK,EACjB,MACF,CACA,IAAM,EAAO,KAAK,QAAQ,OAAO,UAAU,WACxC,EAAW,IAAU,EAAQ,KAAK,iBAAmB,CAAC,KAAK,QAAQ,IAAI,CAAK,CAC/E,EACA,KAAK,gBACH,GAAQ,EAAI,EAAO,KAAK,QAAQ,OAAO,UAAU,WAAW,EAAW,IAAU,CAAC,KAAK,QAAQ,IAAI,CAAK,CAAC,EAC3G,KAAK,cAAgB,EACrB,KAAK,QAAQ,CACf,CAEA,gBAA2C,CACzC,MAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,CAAC,CAC9B,MAAM,EAAM,IAAU,EAAK,cAAgB,EAAM,aAAa,CAAC,CAC/D,IAAK,IAAY,CAAE,GAAG,CAAO,EAAE,CACpC,CAEA,gBAA+B,CAC7B,KAAK,QAAQ,iBAAiB,CAAE,QAAS,KAAK,eAAe,EAAG,UAAW,EAAM,CAAC,CACpF,CAEA,OAAe,EAA0B,CACnC,KAAK,UACT,KAAK,QAAU,GACf,KAAK,QAAQ,KAAK,CAAE,QAAS,KAAK,eAAe,EAAG,WAAU,CAAC,EACjE,CAEA,SAAwB,CACtB,KAAK,WAAW,EAChB,KAAK,QAAQ,IAAI,cAAc,CACjC,CACF"}
@@ -1,2 +1,2 @@
1
- import{Editor as e,Key as t,Markdown as n,matchesKey as r,truncateToWidth as i,visibleWidth as a,wrapTextWithAnsi as o}from"@earendil-works/pi-tui";const s=`tui.select.cancel`;var c=class{options;answers=new Map;selectedOptions=new Map;notes=new Map;editor;currentQuestion=0;currentOption=0;editorMode;collapsed=!1;settled=!1;externalEditorError;cachedLines;cachedWidth;isFocused=!1;constructor(t){this.options=t,this.editor=new e(t.tui,{borderColor:e=>t.theme.fg(`accent`,e),selectList:{selectedPrefix:e=>t.theme.fg(`accent`,e),selectedText:e=>t.theme.fg(`accent`,e),description:e=>t.theme.fg(`muted`,e),scrollInfo:e=>t.theme.fg(`dim`,e),noMatch:e=>t.theme.fg(`warning`,e)}}),this.editor.onSubmit=e=>this.submitEditor(e)}get focused(){return this.isFocused}set focused(e){this.isFocused=e,this.editor.focused=e,this.invalidate()}invalidate(){this.cachedLines=void 0,this.cachedWidth=void 0}cancel(){this.finish(!0)}handleInput(e){if(!this.settled){if(this.options.collapseKey!==`off`&&r(e,this.options.collapseKey)){this.collapsed=!this.collapsed,this.refresh();return}if(!this.collapsed){if(this.editorMode){if(this.options.keybindings.matches(e,s)){this.editorMode=void 0,this.externalEditorError=void 0,this.editor.setText(``),this.refresh();return}if(this.options.keybindings.matches(e,`app.editor.external`)){this.openExternalEditor();return}this.editor.handleInput(e),this.refresh();return}if(this.options.keybindings.matches(e,s)){this.cancel();return}if(r(e,t.up)){this.moveOption(-1);return}if(r(e,t.down)){this.moveOption(1);return}if(r(e,t.tab)||r(e,t.right)){this.moveQuestion(1);return}if(r(e,t.shift(`tab`))||r(e,t.left)){this.moveQuestion(-1);return}if(r(e,`n`)||r(e,t.shift(`n`))){this.openEditor(`notes`);return}if(r(e,t.space)||e===` `){this.toggleCurrentOption();return}r(e,t.enter)&&this.confirmCurrentSelection()}}}render(e){let t=Math.max(1,e);if(this.cachedLines&&this.cachedWidth===t)return this.cachedLines;if(this.cachedWidth=t,this.collapsed){let e=[],n=this.answers.size;return this.addWrapped(e,``,this.options.theme.fg(`accent`,`▸ Questionnaire (${n}/${this.options.params.questions.length} answered) — ${this.options.collapseKey} to expand`),t),this.cachedLines=e,e}let n=[],r=this.question(),i=this.options.theme.fg(`accent`,`─`.repeat(t));return n.push(i),this.addWrapped(n,` `,this.renderTabs(),t),n.push(``),this.addWrapped(n,` `,this.options.theme.bold(r.question),t),n.push(``),this.renderOptions(n,r,t),this.renderPreview(n,r,t),this.renderNotes(n,t),this.renderEditor(n,t),n.push(``),this.addWrapped(n,` `,this.helpText(r),t),n.push(i),this.cachedLines=n,n}question(){let e=this.options.params.questions[this.currentQuestion];if(!e)throw Error(`Questionnaire has no active question`);return e}optionCount(e){return e.options.length+1}renderTabs(){return this.options.params.questions.map((e,t)=>{let n=` ${this.answers.has(t)?`✓`:`○`} ${e.header} `;return t===this.currentQuestion?this.options.theme.bg(`selectedBg`,this.options.theme.fg(`text`,n)):this.options.theme.fg(this.answers.has(t)?`success`:`muted`,n)}).join(` `)}renderOptions(e,t,n){let r=this.selectedOptions.get(this.currentQuestion)??new Set;t.options.forEach((i,a)=>{let o=a===this.currentOption,s=t.multiSelect?r.has(a)?`[x]`:`[ ]`:`${a+1}.`,c=o?this.options.theme.fg(`accent`,`> `):` `,l=`${s} ${i.label}`;this.addWrapped(e,c,this.options.theme.fg(o?`accent`:`text`,l),n),i.description&&this.addWrapped(e,` `,this.options.theme.fg(`muted`,i.description),n)});let i=t.options.length,a=this.currentOption===i;this.addWrapped(e,a?this.options.theme.fg(`accent`,`> `):` `,this.options.theme.fg(a?`accent`:`text`,`✎ Type something.`),n)}renderPreview(e,t,r){if(this.currentOption>=t.options.length)return;let i=t.options[this.currentOption]?.preview;if(!i)return;e.push(``),this.addWrapped(e,` `,this.options.theme.fg(`accent`,this.options.theme.bold(`Preview`)),r);let a=new n(i,0,0,this.options.markdownTheme,{color:e=>this.options.theme.fg(`muted`,e)},{preserveOrderedListMarkers:!0,preserveBackslashEscapes:!0});for(let t of a.render(Math.max(1,r-3)))this.addWrapped(e,` │ `,t||` `,r)}renderNotes(e,t){let n=this.notes.get(this.currentQuestion);!n||this.editorMode===`notes`||(e.push(``),this.addWrapped(e,` `,this.options.theme.fg(`muted`,`Note: ${n}`),t))}renderEditor(e,t){if(!this.editorMode)return;e.push(``);let n=this.editorMode===`custom`?`Your answer:`:`Optional note:`;this.addWrapped(e,` `,this.options.theme.fg(`muted`,n),t);for(let n of this.editor.render(Math.max(1,t-2)))e.push(i(` ${n}`,t,``));this.externalEditorError&&this.addWrapped(e,` `,this.options.theme.fg(`warning`,this.externalEditorError),t)}helpText(e){return this.editorMode?`Enter submit • Esc/Ctrl+C return • Ctrl+G external editor`:`Tab/←→ questions • ↑↓ options • ${e.multiSelect?`Space toggle • Enter confirm`:`Enter select`} • N note • Esc/Ctrl+C cancel`}addWrapped(e,t,n,r){let s=Math.min(a(t),Math.max(0,r-1)),c=i(t,s,``),l=Math.max(1,r-s),u=o(n,l);if(u.length===0){e.push(c);return}for(let t=0;t<u.length;t+=1){let n=t===0?c:` `.repeat(s);e.push(i(`${n}${u[t]}`,r,``))}}moveOption(e){let t=this.optionCount(this.question());this.currentOption=(this.currentOption+e+t)%t,this.refresh()}moveQuestion(e){let t=this.options.params.questions.length;this.currentQuestion=(this.currentQuestion+e+t)%t,this.currentOption=0,this.refresh()}toggleCurrentOption(){let e=this.question();if(!e.multiSelect||this.currentOption>=e.options.length)return;let t=this.selectedOptions.get(this.currentQuestion)??new Set;t.has(this.currentOption)?t.delete(this.currentOption):t.add(this.currentOption),this.selectedOptions.set(this.currentQuestion,t),this.refresh()}confirmCurrentSelection(){let e=this.question();if(this.currentOption===e.options.length){this.openEditor(`custom`);return}if(e.multiSelect){let t=this.selectedOptions.get(this.currentQuestion)??new Set;t.size===0&&t.add(this.currentOption);let n=[...t].sort((e,t)=>e-t).flatMap(t=>e.options[t]?[e.options[t].label]:[]);this.recordAnswer({questionIndex:this.currentQuestion,question:e.question,kind:`multi`,answer:null,selected:n,...this.noteFields()});return}let t=e.options[this.currentOption];t&&this.recordAnswer({questionIndex:this.currentQuestion,question:e.question,kind:`option`,answer:t.label,...t.preview?{preview:t.preview}:{},...this.noteFields()})}openEditor(e){this.editorMode=e,this.externalEditorError=void 0,this.editor.setText(e===`notes`?this.notes.get(this.currentQuestion)??``:``),this.refresh()}async openExternalEditor(){let e=this.editorMode;if(e){this.options.tui.stop();try{let t=await this.options.editExternally(this.editor.getText());if(this.settled||this.editorMode!==e)return;t.status===`complete`?(this.editor.setText(t.content),this.externalEditorError=void 0):this.externalEditorError=t.message}catch(e){this.externalEditorError=`External editor failed: ${e instanceof Error?e.message:String(e)}`}finally{this.options.tui.start(),this.refresh()}}}submitEditor(e){let t=this.editorMode;if(this.editorMode=void 0,this.editor.setText(``),t===`notes`){let t=e.trim();t?this.notes.set(this.currentQuestion,t):this.notes.delete(this.currentQuestion);let n=this.answers.get(this.currentQuestion);n&&(this.answers.set(this.currentQuestion,{...n,...t?{notes:t}:{notes:void 0}}),this.reportProgress()),this.refresh();return}t===`custom`&&this.recordAnswer({questionIndex:this.currentQuestion,question:this.question().question,kind:`custom`,answer:e,...this.noteFields()})}noteFields(){let e=this.notes.get(this.currentQuestion);return e?{notes:e}:{}}recordAnswer(e){if(this.answers.set(this.currentQuestion,e),this.reportProgress(),this.answers.size===this.options.params.questions.length){this.finish(!1);return}let t=this.options.params.questions.findIndex((e,t)=>t>this.currentQuestion&&!this.answers.has(t));this.currentQuestion=t>=0?t:this.options.params.questions.findIndex((e,t)=>!this.answers.has(t)),this.currentOption=0,this.refresh()}orderedAnswers(){return[...this.answers.values()].sort((e,t)=>e.questionIndex-t.questionIndex).map(e=>({...e}))}reportProgress(){this.options.reportProgress?.({answers:this.orderedAnswers(),cancelled:!1})}finish(e){this.settled||(this.settled=!0,this.options.done({answers:this.orderedAnswers(),cancelled:e}))}refresh(){this.invalidate(),this.options.tui.requestRender()}};export{c as QuestionnaireComponent};
1
+ import{Editor as e,Key as t,Markdown as n,matchesKey as r,truncateToWidth as i,visibleWidth as a,wrapTextWithAnsi as o}from"@earendil-works/pi-tui";const s=`tui.select.cancel`;var c=class{options;answers=new Map;selectedOptions=new Map;notes=new Map;editor;currentQuestion=0;currentOption=0;editorMode;collapsed=!1;settled=!1;externalEditorError;cachedLines;cachedWidth;isFocused=!1;constructor(t){this.options=t,this.editor=new e(t.tui,{borderColor:e=>t.theme.fg(`accent`,e),selectList:{selectedPrefix:e=>t.theme.fg(`accent`,e),selectedText:e=>t.theme.fg(`accent`,e),description:e=>t.theme.fg(`muted`,e),scrollInfo:e=>t.theme.fg(`dim`,e),noMatch:e=>t.theme.fg(`warning`,e)}}),this.editor.onSubmit=e=>this.submitEditor(e)}get focused(){return this.isFocused}set focused(e){this.isFocused=e,this.editor.focused=e,this.invalidate()}invalidate(){this.cachedLines=void 0,this.cachedWidth=void 0}cancel(){this.finish(!0)}handleInput(e){if(!this.settled){if(this.options.collapseKey!==`off`&&r(e,this.options.collapseKey)){this.collapsed=!this.collapsed,this.refresh();return}if(!this.collapsed){if(this.editorMode){if(this.options.keybindings.matches(e,s)){this.editorMode=void 0,this.externalEditorError=void 0,this.editor.setText(``),this.refresh();return}if(this.options.keybindings.matches(e,`app.editor.external`)){this.openExternalEditor();return}this.editor.handleInput(e),this.refresh();return}if(this.options.keybindings.matches(e,s)){this.cancel();return}if(r(e,t.up)){this.moveOption(-1);return}if(r(e,t.down)){this.moveOption(1);return}if(r(e,t.tab)||r(e,t.right)){this.moveQuestion(1);return}if(r(e,t.shift(`tab`))||r(e,t.left)){this.moveQuestion(-1);return}if(r(e,`n`)||r(e,t.shift(`n`))){this.openEditor(`notes`);return}if(r(e,t.space)||e===` `){this.toggleCurrentOption();return}r(e,t.enter)&&this.confirmCurrentSelection()}}}render(e){let t=Math.max(1,e);if(this.cachedLines&&this.cachedWidth===t)return this.cachedLines;if(this.cachedWidth=t,this.collapsed){let e=[],n=this.answers.size;return this.addWrapped(e,``,this.options.theme.fg(`accent`,`▸ Questionnaire (${n}/${this.options.params.questions.length} answered) — ${this.options.collapseKey} to expand`),t),this.cachedLines=e,e}let n=[],r=this.question(),i=this.options.theme.fg(`accent`,`─`.repeat(t));return n.push(i),this.addWrapped(n,` `,this.renderTabs(),t),n.push(``),this.addWrapped(n,` `,this.options.theme.bold(r.question),t),n.push(``),this.renderOptions(n,r,t),this.renderPreview(n,r,t),this.renderNotes(n,t),this.renderEditor(n,t),n.push(``),this.addWrapped(n,` `,this.helpText(r),t),n.push(i),this.cachedLines=n,n}question(){let e=this.options.params.questions[this.currentQuestion];if(!e)throw Error(`Questionnaire has no active question`);return e}optionCount(e){return e.options.length+1}renderTabs(){return this.options.params.questions.map((e,t)=>{let n=` ${this.answers.has(t)?`✓`:`○`} ${e.header} `;return t===this.currentQuestion?this.options.theme.bg(`selectedBg`,this.options.theme.fg(`text`,n)):this.options.theme.fg(this.answers.has(t)?`success`:`muted`,n)}).join(` `)}renderOptions(e,t,n){let r=this.selectedOptions.get(this.currentQuestion)??new Set;t.options.forEach((i,a)=>{let o=a===this.currentOption,s=t.multiSelect?r.has(a)?`[x]`:`[ ]`:`${a+1}.`,c=o?this.options.theme.fg(`accent`,`> `):` `,l=`${s} ${i.label}`;this.addWrapped(e,c,this.options.theme.fg(o?`accent`:`text`,l),n),i.description&&this.addWrapped(e,` `,this.options.theme.fg(`muted`,i.description),n)});let i=t.options.length,a=this.currentOption===i;this.addWrapped(e,a?this.options.theme.fg(`accent`,`> `):` `,this.options.theme.fg(a?`accent`:`text`,`✎ Type something.`),n)}renderPreview(e,t,r){if(this.currentOption>=t.options.length)return;let i=t.options[this.currentOption]?.preview;if(!i)return;e.push(``),this.addWrapped(e,` `,this.options.theme.fg(`accent`,this.options.theme.bold(`Preview`)),r);let a=new n(i,0,0,this.options.markdownTheme,{color:e=>this.options.theme.fg(`muted`,e)},{preserveOrderedListMarkers:!0,preserveBackslashEscapes:!0});for(let t of a.render(Math.max(1,r-3)))this.addWrapped(e,` │ `,t||` `,r)}renderNotes(e,t){let n=this.notes.get(this.currentQuestion);n&&this.editorMode!==`notes`&&(e.push(``),this.addWrapped(e,` `,this.options.theme.fg(`muted`,`Note: ${n}`),t))}renderEditor(e,t){if(!this.editorMode)return;e.push(``);let n=this.editorMode===`custom`?`Your answer:`:`Optional note:`;this.addWrapped(e,` `,this.options.theme.fg(`muted`,n),t);for(let n of this.editor.render(Math.max(1,t-2)))e.push(i(` ${n}`,t,``));this.externalEditorError&&this.addWrapped(e,` `,this.options.theme.fg(`warning`,this.externalEditorError),t)}helpText(e){return this.editorMode?`Enter submit • Esc/Ctrl+C return • Ctrl+G external editor`:`Tab/←→ questions • ↑↓ options • ${e.multiSelect?`Space toggle • Enter confirm`:`Enter select`} • N note • Esc/Ctrl+C cancel`}addWrapped(e,t,n,r){let s=Math.min(a(t),Math.max(0,r-1)),c=i(t,s,``),l=Math.max(1,r-s),u=o(n,l);if(u.length===0){e.push(c);return}for(let t=0;t<u.length;t+=1){let n=t===0?c:` `.repeat(s);e.push(i(`${n}${u[t]}`,r,``))}}moveOption(e){let t=this.optionCount(this.question());this.currentOption=(this.currentOption+e+t)%t,this.refresh()}moveQuestion(e){let t=this.options.params.questions.length;this.currentQuestion=(this.currentQuestion+e+t)%t,this.currentOption=0,this.refresh()}toggleCurrentOption(){let e=this.question();if(!e.multiSelect||this.currentOption>=e.options.length)return;let t=this.selectedOptions.get(this.currentQuestion)??new Set;t.has(this.currentOption)?t.delete(this.currentOption):t.add(this.currentOption),this.selectedOptions.set(this.currentQuestion,t),this.refresh()}confirmCurrentSelection(){let e=this.question();if(this.currentOption===e.options.length){this.openEditor(`custom`);return}if(e.multiSelect){let t=this.selectedOptions.get(this.currentQuestion)??new Set;t.size===0&&t.add(this.currentOption);let n=[...t].sort((e,t)=>e-t).flatMap(t=>e.options[t]?[e.options[t].label]:[]);this.recordAnswer({questionIndex:this.currentQuestion,question:e.question,kind:`multi`,answer:null,selected:n,...this.noteFields()});return}let t=e.options[this.currentOption];t&&this.recordAnswer({questionIndex:this.currentQuestion,question:e.question,kind:`option`,answer:t.label,...t.preview?{preview:t.preview}:{},...this.noteFields()})}openEditor(e){this.editorMode=e,this.externalEditorError=void 0,this.editor.setText(e===`notes`?this.notes.get(this.currentQuestion)??``:``),this.refresh()}async openExternalEditor(){let e=this.editorMode;if(e){this.options.tui.stop();try{let t=await this.options.editExternally(this.editor.getText());if(this.settled||this.editorMode!==e)return;t.status===`complete`?(this.editor.setText(t.content),this.externalEditorError=void 0):this.externalEditorError=t.message}catch(e){this.externalEditorError=`External editor failed: ${e instanceof Error?e.message:String(e)}`}finally{this.options.tui.start(),this.refresh()}}}submitEditor(e){let t=this.editorMode;if(this.editorMode=void 0,this.editor.setText(``),t===`notes`){let t=e.trim();t?this.notes.set(this.currentQuestion,t):this.notes.delete(this.currentQuestion);let n=this.answers.get(this.currentQuestion);n&&(this.answers.set(this.currentQuestion,{...n,...t?{notes:t}:{notes:void 0}}),this.reportProgress()),this.refresh();return}t===`custom`&&this.recordAnswer({questionIndex:this.currentQuestion,question:this.question().question,kind:`custom`,answer:e,...this.noteFields()})}noteFields(){let e=this.notes.get(this.currentQuestion);return e?{notes:e}:{}}recordAnswer(e){if(this.answers.set(this.currentQuestion,e),this.reportProgress(),this.answers.size===this.options.params.questions.length){this.finish(!1);return}let t=this.options.params.questions.findIndex((e,t)=>t>this.currentQuestion&&!this.answers.has(t));this.currentQuestion=t>=0?t:this.options.params.questions.findIndex((e,t)=>!this.answers.has(t)),this.currentOption=0,this.refresh()}orderedAnswers(){return[...this.answers.values()].sort((e,t)=>e.questionIndex-t.questionIndex).map(e=>({...e}))}reportProgress(){this.options.reportProgress?.({answers:this.orderedAnswers(),cancelled:!1})}finish(e){this.settled||(this.settled=!0,this.options.done({answers:this.orderedAnswers(),cancelled:e}))}refresh(){this.invalidate(),this.options.tui.requestRender()}};export{c as QuestionnaireComponent};
2
2
  //# sourceMappingURL=questionnaireComponent.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"questionnaireComponent.mjs","names":[],"sources":["../../src/tui/questionnaireComponent.ts"],"sourcesContent":["import type { KeybindingsManager, Theme } from '@earendil-works/pi-coding-agent';\nimport {\n Editor,\n Key,\n Markdown,\n matchesKey,\n type Focusable,\n type MarkdownTheme,\n type TUI,\n truncateToWidth,\n visibleWidth,\n wrapTextWithAnsi,\n} from '@earendil-works/pi-tui';\nimport type { QuestionData, QuestionParams } from '../schemas/questionnaire.js';\nimport type { ExternalEditResult, QuestionAnswer, QuestionnaireResult } from '../types/questionnaire.js';\n\nexport interface QuestionnaireComponentOptions {\n tui: TUI;\n theme: Theme;\n markdownTheme: MarkdownTheme;\n keybindings: KeybindingsManager;\n params: QuestionParams;\n collapseKey: string;\n done: (result: QuestionnaireResult) => void;\n editExternally: (content: string) => Promise<ExternalEditResult>;\n reportProgress?: (result: QuestionnaireResult) => void;\n}\n\ntype EditorMode = 'custom' | 'notes';\n\nconst SELECT_CANCEL_KEYBINDING = 'tui.select.cancel';\nconst EXTERNAL_EDITOR_KEYBINDING = 'app.editor.external';\n\nexport class QuestionnaireComponent implements Focusable {\n private readonly answers = new Map<number, QuestionAnswer>();\n private readonly selectedOptions = new Map<number, Set<number>>();\n private readonly notes = new Map<number, string>();\n private readonly editor: Editor;\n private currentQuestion = 0;\n private currentOption = 0;\n private editorMode: EditorMode | undefined;\n private collapsed = false;\n private settled = false;\n private externalEditorError: string | undefined;\n private cachedLines: string[] | undefined;\n private cachedWidth: number | undefined;\n private isFocused = false;\n\n constructor(private readonly options: QuestionnaireComponentOptions) {\n this.editor = new Editor(options.tui, {\n borderColor: (text) => options.theme.fg('accent', text),\n selectList: {\n selectedPrefix: (text) => options.theme.fg('accent', text),\n selectedText: (text) => options.theme.fg('accent', text),\n description: (text) => options.theme.fg('muted', text),\n scrollInfo: (text) => options.theme.fg('dim', text),\n noMatch: (text) => options.theme.fg('warning', text),\n },\n });\n this.editor.onSubmit = (value) => this.submitEditor(value);\n }\n\n get focused(): boolean {\n return this.isFocused;\n }\n\n set focused(value: boolean) {\n this.isFocused = value;\n this.editor.focused = value;\n this.invalidate();\n }\n\n invalidate(): void {\n this.cachedLines = undefined;\n this.cachedWidth = undefined;\n }\n\n cancel(): void {\n this.finish(true);\n }\n\n handleInput(data: string): void {\n if (this.settled) return;\n\n if (\n this.options.collapseKey !== 'off' &&\n matchesKey(data, this.options.collapseKey as Parameters<typeof matchesKey>[1])\n ) {\n this.collapsed = !this.collapsed;\n this.refresh();\n return;\n }\n\n if (this.collapsed) return;\n\n if (this.editorMode) {\n if (this.options.keybindings.matches(data, SELECT_CANCEL_KEYBINDING)) {\n this.editorMode = undefined;\n this.externalEditorError = undefined;\n this.editor.setText('');\n this.refresh();\n return;\n }\n if (this.options.keybindings.matches(data, EXTERNAL_EDITOR_KEYBINDING)) {\n void this.openExternalEditor();\n return;\n }\n this.editor.handleInput(data);\n this.refresh();\n return;\n }\n\n if (this.options.keybindings.matches(data, SELECT_CANCEL_KEYBINDING)) {\n this.cancel();\n return;\n }\n if (matchesKey(data, Key.up)) {\n this.moveOption(-1);\n return;\n }\n if (matchesKey(data, Key.down)) {\n this.moveOption(1);\n return;\n }\n if (matchesKey(data, Key.tab) || matchesKey(data, Key.right)) {\n this.moveQuestion(1);\n return;\n }\n if (matchesKey(data, Key.shift('tab')) || matchesKey(data, Key.left)) {\n this.moveQuestion(-1);\n return;\n }\n if (matchesKey(data, 'n') || matchesKey(data, Key.shift('n'))) {\n this.openEditor('notes');\n return;\n }\n if (matchesKey(data, Key.space) || data === ' ') {\n this.toggleCurrentOption();\n return;\n }\n if (matchesKey(data, Key.enter)) {\n this.confirmCurrentSelection();\n }\n }\n\n render(width: number): string[] {\n const renderWidth = Math.max(1, width);\n if (this.cachedLines && this.cachedWidth === renderWidth) return this.cachedLines;\n this.cachedWidth = renderWidth;\n if (this.collapsed) {\n const lines: string[] = [];\n const answerCount = this.answers.size;\n this.addWrapped(\n lines,\n '',\n this.options.theme.fg(\n 'accent',\n `▸ Questionnaire (${answerCount}/${this.options.params.questions.length} answered) — ${this.options.collapseKey} to expand`,\n ),\n renderWidth,\n );\n this.cachedLines = lines;\n return lines;\n }\n\n const lines: string[] = [];\n const question = this.question();\n const border = this.options.theme.fg('accent', '─'.repeat(renderWidth));\n lines.push(border);\n this.addWrapped(lines, ' ', this.renderTabs(), renderWidth);\n lines.push('');\n this.addWrapped(lines, ' ', this.options.theme.bold(question.question), renderWidth);\n lines.push('');\n\n this.renderOptions(lines, question, renderWidth);\n this.renderPreview(lines, question, renderWidth);\n this.renderNotes(lines, renderWidth);\n this.renderEditor(lines, renderWidth);\n lines.push('');\n this.addWrapped(lines, ' ', this.helpText(question), renderWidth);\n lines.push(border);\n this.cachedLines = lines;\n return lines;\n }\n\n private question(): QuestionData {\n const question = this.options.params.questions[this.currentQuestion];\n if (!question) throw new Error('Questionnaire has no active question');\n return question;\n }\n\n private optionCount(question: QuestionData): number {\n return question.options.length + 1;\n }\n\n private renderTabs(): string {\n const tabs = this.options.params.questions.map((question, index) => {\n const mark = this.answers.has(index) ? '✓' : '○';\n const label = ` ${mark} ${question.header} `;\n return index === this.currentQuestion\n ? this.options.theme.bg('selectedBg', this.options.theme.fg('text', label))\n : this.options.theme.fg(this.answers.has(index) ? 'success' : 'muted', label);\n });\n return tabs.join(' ');\n }\n\n private renderOptions(lines: string[], question: QuestionData, width: number): void {\n const selected = this.selectedOptions.get(this.currentQuestion) ?? new Set<number>();\n question.options.forEach((option, index) => {\n const focused = index === this.currentOption;\n const checked = question.multiSelect ? (selected.has(index) ? '[x]' : '[ ]') : `${index + 1}.`;\n const prefix = focused ? this.options.theme.fg('accent', '> ') : ' ';\n const label = `${checked} ${option.label}`;\n this.addWrapped(lines, prefix, this.options.theme.fg(focused ? 'accent' : 'text', label), width);\n if (option.description) {\n this.addWrapped(lines, ' ', this.options.theme.fg('muted', option.description), width);\n }\n });\n\n const customIndex = question.options.length;\n const customFocused = this.currentOption === customIndex;\n this.addWrapped(\n lines,\n customFocused ? this.options.theme.fg('accent', '> ') : ' ',\n this.options.theme.fg(customFocused ? 'accent' : 'text', '✎ Type something.'),\n width,\n );\n }\n\n private renderPreview(lines: string[], question: QuestionData, width: number): void {\n if (this.currentOption >= question.options.length) return;\n const preview = question.options[this.currentOption]?.preview;\n if (!preview) return;\n lines.push('');\n this.addWrapped(lines, ' ', this.options.theme.fg('accent', this.options.theme.bold('Preview')), width);\n const markdown = new Markdown(\n preview,\n 0,\n 0,\n this.options.markdownTheme,\n { color: (text) => this.options.theme.fg('muted', text) },\n { preserveOrderedListMarkers: true, preserveBackslashEscapes: true },\n );\n for (const previewLine of markdown.render(Math.max(1, width - 3))) {\n this.addWrapped(lines, ' │ ', previewLine || ' ', width);\n }\n }\n\n private renderNotes(lines: string[], width: number): void {\n const note = this.notes.get(this.currentQuestion);\n if (!note || this.editorMode === 'notes') return;\n lines.push('');\n this.addWrapped(lines, ' ', this.options.theme.fg('muted', `Note: ${note}`), width);\n }\n\n private renderEditor(lines: string[], width: number): void {\n if (!this.editorMode) return;\n lines.push('');\n const label = this.editorMode === 'custom' ? 'Your answer:' : 'Optional note:';\n this.addWrapped(lines, ' ', this.options.theme.fg('muted', label), width);\n for (const line of this.editor.render(Math.max(1, width - 2))) {\n lines.push(truncateToWidth(` ${line}`, width, ''));\n }\n if (this.externalEditorError) {\n this.addWrapped(lines, ' ', this.options.theme.fg('warning', this.externalEditorError), width);\n }\n }\n\n private helpText(question: QuestionData): string {\n if (this.editorMode) return 'Enter submit • Esc/Ctrl+C return • Ctrl+G external editor';\n const selectionHelp = question.multiSelect ? 'Space toggle • Enter confirm' : 'Enter select';\n return `Tab/←→ questions • ↑↓ options • ${selectionHelp} • N note • Esc/Ctrl+C cancel`;\n }\n\n private addWrapped(lines: string[], prefix: string, text: string, width: number): void {\n const prefixWidth = Math.min(visibleWidth(prefix), Math.max(0, width - 1));\n const renderedPrefix = truncateToWidth(prefix, prefixWidth, '');\n const available = Math.max(1, width - prefixWidth);\n const wrapped = wrapTextWithAnsi(text, available);\n if (wrapped.length === 0) {\n lines.push(renderedPrefix);\n return;\n }\n for (let index = 0; index < wrapped.length; index += 1) {\n const linePrefix = index === 0 ? renderedPrefix : ' '.repeat(prefixWidth);\n lines.push(truncateToWidth(`${linePrefix}${wrapped[index]}`, width, ''));\n }\n }\n\n private moveOption(delta: number): void {\n const count = this.optionCount(this.question());\n this.currentOption = (this.currentOption + delta + count) % count;\n this.refresh();\n }\n\n private moveQuestion(delta: number): void {\n const count = this.options.params.questions.length;\n this.currentQuestion = (this.currentQuestion + delta + count) % count;\n this.currentOption = 0;\n this.refresh();\n }\n\n private toggleCurrentOption(): void {\n const question = this.question();\n if (!question.multiSelect || this.currentOption >= question.options.length) return;\n const selected = this.selectedOptions.get(this.currentQuestion) ?? new Set<number>();\n if (selected.has(this.currentOption)) selected.delete(this.currentOption);\n else selected.add(this.currentOption);\n this.selectedOptions.set(this.currentQuestion, selected);\n this.refresh();\n }\n\n private confirmCurrentSelection(): void {\n const question = this.question();\n if (this.currentOption === question.options.length) {\n this.openEditor('custom');\n return;\n }\n\n if (question.multiSelect) {\n const selected = this.selectedOptions.get(this.currentQuestion) ?? new Set<number>();\n if (selected.size === 0) selected.add(this.currentOption);\n const labels = [...selected]\n .sort((left, right) => left - right)\n .flatMap((index) => (question.options[index] ? [question.options[index].label] : []));\n this.recordAnswer({\n questionIndex: this.currentQuestion,\n question: question.question,\n kind: 'multi',\n answer: null,\n selected: labels,\n ...this.noteFields(),\n });\n return;\n }\n\n const option = question.options[this.currentOption];\n if (!option) return;\n this.recordAnswer({\n questionIndex: this.currentQuestion,\n question: question.question,\n kind: 'option',\n answer: option.label,\n ...(option.preview ? { preview: option.preview } : {}),\n ...this.noteFields(),\n });\n }\n\n private openEditor(mode: EditorMode): void {\n this.editorMode = mode;\n this.externalEditorError = undefined;\n this.editor.setText(mode === 'notes' ? (this.notes.get(this.currentQuestion) ?? '') : '');\n this.refresh();\n }\n\n private async openExternalEditor(): Promise<void> {\n const editorMode = this.editorMode;\n if (!editorMode) return;\n this.options.tui.stop();\n try {\n const result = await this.options.editExternally(this.editor.getText());\n if (this.settled || this.editorMode !== editorMode) return;\n if (result.status === 'complete') {\n this.editor.setText(result.content);\n this.externalEditorError = undefined;\n } else {\n this.externalEditorError = result.message;\n }\n } catch (error) {\n this.externalEditorError = `External editor failed: ${error instanceof Error ? error.message : String(error)}`;\n } finally {\n this.options.tui.start();\n this.refresh();\n }\n }\n\n private submitEditor(value: string): void {\n const mode = this.editorMode;\n this.editorMode = undefined;\n this.editor.setText('');\n if (mode === 'notes') {\n const trimmed = value.trim();\n if (trimmed) this.notes.set(this.currentQuestion, trimmed);\n else this.notes.delete(this.currentQuestion);\n const answer = this.answers.get(this.currentQuestion);\n if (answer) {\n this.answers.set(this.currentQuestion, {\n ...answer,\n ...(trimmed ? { notes: trimmed } : { notes: undefined }),\n });\n this.reportProgress();\n }\n this.refresh();\n return;\n }\n if (mode === 'custom') {\n this.recordAnswer({\n questionIndex: this.currentQuestion,\n question: this.question().question,\n kind: 'custom',\n answer: value,\n ...this.noteFields(),\n });\n }\n }\n\n private noteFields(): { notes?: string } {\n const note = this.notes.get(this.currentQuestion);\n return note ? { notes: note } : {};\n }\n\n private recordAnswer(answer: QuestionAnswer): void {\n this.answers.set(this.currentQuestion, answer);\n this.reportProgress();\n if (this.answers.size === this.options.params.questions.length) {\n this.finish(false);\n return;\n }\n const next = this.options.params.questions.findIndex(\n (_question, index) => index > this.currentQuestion && !this.answers.has(index),\n );\n this.currentQuestion =\n next >= 0 ? next : this.options.params.questions.findIndex((_question, index) => !this.answers.has(index));\n this.currentOption = 0;\n this.refresh();\n }\n\n private orderedAnswers(): QuestionAnswer[] {\n return [...this.answers.values()]\n .sort((left, right) => left.questionIndex - right.questionIndex)\n .map((answer) => ({ ...answer }));\n }\n\n private reportProgress(): void {\n this.options.reportProgress?.({ answers: this.orderedAnswers(), cancelled: false });\n }\n\n private finish(cancelled: boolean): void {\n if (this.settled) return;\n this.settled = true;\n this.options.done({ answers: this.orderedAnswers(), cancelled });\n }\n\n private refresh(): void {\n this.invalidate();\n this.options.tui.requestRender();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"questionnaireComponent.mjs","names":[],"sources":["../../src/tui/questionnaireComponent.ts"],"sourcesContent":["import type { KeybindingsManager, Theme } from '@earendil-works/pi-coding-agent';\nimport {\n Editor,\n Key,\n Markdown,\n matchesKey,\n type Focusable,\n type MarkdownTheme,\n type TUI,\n truncateToWidth,\n visibleWidth,\n wrapTextWithAnsi,\n} from '@earendil-works/pi-tui';\nimport type { QuestionData, QuestionParams } from '../schemas/questionnaire.js';\nimport type { ExternalEditResult, QuestionAnswer, QuestionnaireResult } from '../types/questionnaire.js';\n\nexport interface QuestionnaireComponentOptions {\n tui: TUI;\n theme: Theme;\n markdownTheme: MarkdownTheme;\n keybindings: KeybindingsManager;\n params: QuestionParams;\n collapseKey: string;\n done: (result: QuestionnaireResult) => void;\n editExternally: (content: string) => Promise<ExternalEditResult>;\n reportProgress?: (result: QuestionnaireResult) => void;\n}\n\ntype EditorMode = 'custom' | 'notes';\n\nconst SELECT_CANCEL_KEYBINDING = 'tui.select.cancel';\nconst EXTERNAL_EDITOR_KEYBINDING = 'app.editor.external';\n\nexport class QuestionnaireComponent implements Focusable {\n private readonly answers = new Map<number, QuestionAnswer>();\n private readonly selectedOptions = new Map<number, Set<number>>();\n private readonly notes = new Map<number, string>();\n private readonly editor: Editor;\n private currentQuestion = 0;\n private currentOption = 0;\n private editorMode: EditorMode | undefined;\n private collapsed = false;\n private settled = false;\n private externalEditorError: string | undefined;\n private cachedLines: string[] | undefined;\n private cachedWidth: number | undefined;\n private isFocused = false;\n\n constructor(private readonly options: QuestionnaireComponentOptions) {\n this.editor = new Editor(options.tui, {\n borderColor: (text) => options.theme.fg('accent', text),\n selectList: {\n selectedPrefix: (text) => options.theme.fg('accent', text),\n selectedText: (text) => options.theme.fg('accent', text),\n description: (text) => options.theme.fg('muted', text),\n scrollInfo: (text) => options.theme.fg('dim', text),\n noMatch: (text) => options.theme.fg('warning', text),\n },\n });\n this.editor.onSubmit = (value) => this.submitEditor(value);\n }\n\n get focused(): boolean {\n return this.isFocused;\n }\n\n set focused(value: boolean) {\n this.isFocused = value;\n this.editor.focused = value;\n this.invalidate();\n }\n\n invalidate(): void {\n this.cachedLines = undefined;\n this.cachedWidth = undefined;\n }\n\n cancel(): void {\n this.finish(true);\n }\n\n handleInput(data: string): void {\n if (this.settled) return;\n\n if (\n this.options.collapseKey !== 'off' &&\n matchesKey(data, this.options.collapseKey as Parameters<typeof matchesKey>[1])\n ) {\n this.collapsed = !this.collapsed;\n this.refresh();\n return;\n }\n\n if (this.collapsed) return;\n\n if (this.editorMode) {\n if (this.options.keybindings.matches(data, SELECT_CANCEL_KEYBINDING)) {\n this.editorMode = undefined;\n this.externalEditorError = undefined;\n this.editor.setText('');\n this.refresh();\n return;\n }\n if (this.options.keybindings.matches(data, EXTERNAL_EDITOR_KEYBINDING)) {\n void this.openExternalEditor();\n return;\n }\n this.editor.handleInput(data);\n this.refresh();\n return;\n }\n\n if (this.options.keybindings.matches(data, SELECT_CANCEL_KEYBINDING)) {\n this.cancel();\n return;\n }\n if (matchesKey(data, Key.up)) {\n this.moveOption(-1);\n return;\n }\n if (matchesKey(data, Key.down)) {\n this.moveOption(1);\n return;\n }\n if (matchesKey(data, Key.tab) || matchesKey(data, Key.right)) {\n this.moveQuestion(1);\n return;\n }\n if (matchesKey(data, Key.shift('tab')) || matchesKey(data, Key.left)) {\n this.moveQuestion(-1);\n return;\n }\n if (matchesKey(data, 'n') || matchesKey(data, Key.shift('n'))) {\n this.openEditor('notes');\n return;\n }\n if (matchesKey(data, Key.space) || data === ' ') {\n this.toggleCurrentOption();\n return;\n }\n if (matchesKey(data, Key.enter)) {\n this.confirmCurrentSelection();\n }\n }\n\n render(width: number): string[] {\n const renderWidth = Math.max(1, width);\n if (this.cachedLines && this.cachedWidth === renderWidth) return this.cachedLines;\n this.cachedWidth = renderWidth;\n if (this.collapsed) {\n const lines: string[] = [];\n const answerCount = this.answers.size;\n this.addWrapped(\n lines,\n '',\n this.options.theme.fg(\n 'accent',\n `▸ Questionnaire (${answerCount}/${this.options.params.questions.length} answered) — ${this.options.collapseKey} to expand`,\n ),\n renderWidth,\n );\n this.cachedLines = lines;\n return lines;\n }\n\n const lines: string[] = [];\n const question = this.question();\n const border = this.options.theme.fg('accent', '─'.repeat(renderWidth));\n lines.push(border);\n this.addWrapped(lines, ' ', this.renderTabs(), renderWidth);\n lines.push('');\n this.addWrapped(lines, ' ', this.options.theme.bold(question.question), renderWidth);\n lines.push('');\n\n this.renderOptions(lines, question, renderWidth);\n this.renderPreview(lines, question, renderWidth);\n this.renderNotes(lines, renderWidth);\n this.renderEditor(lines, renderWidth);\n lines.push('');\n this.addWrapped(lines, ' ', this.helpText(question), renderWidth);\n lines.push(border);\n this.cachedLines = lines;\n return lines;\n }\n\n private question(): QuestionData {\n const question = this.options.params.questions[this.currentQuestion];\n if (!question) throw new Error('Questionnaire has no active question');\n return question;\n }\n\n private optionCount(question: QuestionData): number {\n return question.options.length + 1;\n }\n\n private renderTabs(): string {\n const tabs = this.options.params.questions.map((question, index) => {\n const mark = this.answers.has(index) ? '✓' : '○';\n const label = ` ${mark} ${question.header} `;\n return index === this.currentQuestion\n ? this.options.theme.bg('selectedBg', this.options.theme.fg('text', label))\n : this.options.theme.fg(this.answers.has(index) ? 'success' : 'muted', label);\n });\n return tabs.join(' ');\n }\n\n private renderOptions(lines: string[], question: QuestionData, width: number): void {\n const selected = this.selectedOptions.get(this.currentQuestion) ?? new Set<number>();\n question.options.forEach((option, index) => {\n const focused = index === this.currentOption;\n const checked = question.multiSelect ? (selected.has(index) ? '[x]' : '[ ]') : `${index + 1}.`;\n const prefix = focused ? this.options.theme.fg('accent', '> ') : ' ';\n const label = `${checked} ${option.label}`;\n this.addWrapped(lines, prefix, this.options.theme.fg(focused ? 'accent' : 'text', label), width);\n if (option.description) {\n this.addWrapped(lines, ' ', this.options.theme.fg('muted', option.description), width);\n }\n });\n\n const customIndex = question.options.length;\n const customFocused = this.currentOption === customIndex;\n this.addWrapped(\n lines,\n customFocused ? this.options.theme.fg('accent', '> ') : ' ',\n this.options.theme.fg(customFocused ? 'accent' : 'text', '✎ Type something.'),\n width,\n );\n }\n\n private renderPreview(lines: string[], question: QuestionData, width: number): void {\n if (this.currentOption >= question.options.length) return;\n const preview = question.options[this.currentOption]?.preview;\n if (!preview) return;\n lines.push('');\n this.addWrapped(lines, ' ', this.options.theme.fg('accent', this.options.theme.bold('Preview')), width);\n const markdown = new Markdown(\n preview,\n 0,\n 0,\n this.options.markdownTheme,\n { color: (text) => this.options.theme.fg('muted', text) },\n { preserveOrderedListMarkers: true, preserveBackslashEscapes: true },\n );\n for (const previewLine of markdown.render(Math.max(1, width - 3))) {\n this.addWrapped(lines, ' │ ', previewLine || ' ', width);\n }\n }\n\n private renderNotes(lines: string[], width: number): void {\n const note = this.notes.get(this.currentQuestion);\n if (!note || this.editorMode === 'notes') return;\n lines.push('');\n this.addWrapped(lines, ' ', this.options.theme.fg('muted', `Note: ${note}`), width);\n }\n\n private renderEditor(lines: string[], width: number): void {\n if (!this.editorMode) return;\n lines.push('');\n const label = this.editorMode === 'custom' ? 'Your answer:' : 'Optional note:';\n this.addWrapped(lines, ' ', this.options.theme.fg('muted', label), width);\n for (const line of this.editor.render(Math.max(1, width - 2))) {\n lines.push(truncateToWidth(` ${line}`, width, ''));\n }\n if (this.externalEditorError) {\n this.addWrapped(lines, ' ', this.options.theme.fg('warning', this.externalEditorError), width);\n }\n }\n\n private helpText(question: QuestionData): string {\n if (this.editorMode) return 'Enter submit • Esc/Ctrl+C return • Ctrl+G external editor';\n const selectionHelp = question.multiSelect ? 'Space toggle • Enter confirm' : 'Enter select';\n return `Tab/←→ questions • ↑↓ options • ${selectionHelp} • N note • Esc/Ctrl+C cancel`;\n }\n\n private addWrapped(lines: string[], prefix: string, text: string, width: number): void {\n const prefixWidth = Math.min(visibleWidth(prefix), Math.max(0, width - 1));\n const renderedPrefix = truncateToWidth(prefix, prefixWidth, '');\n const available = Math.max(1, width - prefixWidth);\n const wrapped = wrapTextWithAnsi(text, available);\n if (wrapped.length === 0) {\n lines.push(renderedPrefix);\n return;\n }\n for (let index = 0; index < wrapped.length; index += 1) {\n const linePrefix = index === 0 ? renderedPrefix : ' '.repeat(prefixWidth);\n lines.push(truncateToWidth(`${linePrefix}${wrapped[index]}`, width, ''));\n }\n }\n\n private moveOption(delta: number): void {\n const count = this.optionCount(this.question());\n this.currentOption = (this.currentOption + delta + count) % count;\n this.refresh();\n }\n\n private moveQuestion(delta: number): void {\n const count = this.options.params.questions.length;\n this.currentQuestion = (this.currentQuestion + delta + count) % count;\n this.currentOption = 0;\n this.refresh();\n }\n\n private toggleCurrentOption(): void {\n const question = this.question();\n if (!question.multiSelect || this.currentOption >= question.options.length) return;\n const selected = this.selectedOptions.get(this.currentQuestion) ?? new Set<number>();\n if (selected.has(this.currentOption)) selected.delete(this.currentOption);\n else selected.add(this.currentOption);\n this.selectedOptions.set(this.currentQuestion, selected);\n this.refresh();\n }\n\n private confirmCurrentSelection(): void {\n const question = this.question();\n if (this.currentOption === question.options.length) {\n this.openEditor('custom');\n return;\n }\n\n if (question.multiSelect) {\n const selected = this.selectedOptions.get(this.currentQuestion) ?? new Set<number>();\n if (selected.size === 0) selected.add(this.currentOption);\n const labels = [...selected]\n .sort((left, right) => left - right)\n .flatMap((index) => (question.options[index] ? [question.options[index].label] : []));\n this.recordAnswer({\n questionIndex: this.currentQuestion,\n question: question.question,\n kind: 'multi',\n answer: null,\n selected: labels,\n ...this.noteFields(),\n });\n return;\n }\n\n const option = question.options[this.currentOption];\n if (!option) return;\n this.recordAnswer({\n questionIndex: this.currentQuestion,\n question: question.question,\n kind: 'option',\n answer: option.label,\n ...(option.preview ? { preview: option.preview } : {}),\n ...this.noteFields(),\n });\n }\n\n private openEditor(mode: EditorMode): void {\n this.editorMode = mode;\n this.externalEditorError = undefined;\n this.editor.setText(mode === 'notes' ? (this.notes.get(this.currentQuestion) ?? '') : '');\n this.refresh();\n }\n\n private async openExternalEditor(): Promise<void> {\n const editorMode = this.editorMode;\n if (!editorMode) return;\n this.options.tui.stop();\n try {\n const result = await this.options.editExternally(this.editor.getText());\n if (this.settled || this.editorMode !== editorMode) return;\n if (result.status === 'complete') {\n this.editor.setText(result.content);\n this.externalEditorError = undefined;\n } else {\n this.externalEditorError = result.message;\n }\n } catch (error) {\n this.externalEditorError = `External editor failed: ${error instanceof Error ? error.message : String(error)}`;\n } finally {\n this.options.tui.start();\n this.refresh();\n }\n }\n\n private submitEditor(value: string): void {\n const mode = this.editorMode;\n this.editorMode = undefined;\n this.editor.setText('');\n if (mode === 'notes') {\n const trimmed = value.trim();\n if (trimmed) this.notes.set(this.currentQuestion, trimmed);\n else this.notes.delete(this.currentQuestion);\n const answer = this.answers.get(this.currentQuestion);\n if (answer) {\n this.answers.set(this.currentQuestion, {\n ...answer,\n ...(trimmed ? { notes: trimmed } : { notes: undefined }),\n });\n this.reportProgress();\n }\n this.refresh();\n return;\n }\n if (mode === 'custom') {\n this.recordAnswer({\n questionIndex: this.currentQuestion,\n question: this.question().question,\n kind: 'custom',\n answer: value,\n ...this.noteFields(),\n });\n }\n }\n\n private noteFields(): { notes?: string } {\n const note = this.notes.get(this.currentQuestion);\n return note ? { notes: note } : {};\n }\n\n private recordAnswer(answer: QuestionAnswer): void {\n this.answers.set(this.currentQuestion, answer);\n this.reportProgress();\n if (this.answers.size === this.options.params.questions.length) {\n this.finish(false);\n return;\n }\n const next = this.options.params.questions.findIndex(\n (_question, index) => index > this.currentQuestion && !this.answers.has(index),\n );\n this.currentQuestion =\n next >= 0 ? next : this.options.params.questions.findIndex((_question, index) => !this.answers.has(index));\n this.currentOption = 0;\n this.refresh();\n }\n\n private orderedAnswers(): QuestionAnswer[] {\n return [...this.answers.values()]\n .sort((left, right) => left.questionIndex - right.questionIndex)\n .map((answer) => ({ ...answer }));\n }\n\n private reportProgress(): void {\n this.options.reportProgress?.({ answers: this.orderedAnswers(), cancelled: false });\n }\n\n private finish(cancelled: boolean): void {\n if (this.settled) return;\n this.settled = true;\n this.options.done({ answers: this.orderedAnswers(), cancelled });\n }\n\n private refresh(): void {\n this.invalidate();\n this.options.tui.requestRender();\n }\n}\n"],"mappings":"oJA8BA,MAAM,EAA2B,oBAGjC,IAAa,EAAb,KAAyD,CAe1B,QAd7B,QAA2B,IAAI,IAC/B,gBAAmC,IAAI,IACvC,MAAyB,IAAI,IAC7B,OACA,gBAA0B,EAC1B,cAAwB,EACxB,WACA,UAAoB,GACpB,QAAkB,GAClB,oBACA,YACA,YACA,UAAoB,GAEpB,YAAY,EAAyD,CAAxC,KAAA,QAAA,EAC3B,KAAK,OAAS,IAAI,EAAO,EAAQ,IAAK,CACpC,YAAc,GAAS,EAAQ,MAAM,GAAG,SAAU,CAAI,EACtD,WAAY,CACV,eAAiB,GAAS,EAAQ,MAAM,GAAG,SAAU,CAAI,EACzD,aAAe,GAAS,EAAQ,MAAM,GAAG,SAAU,CAAI,EACvD,YAAc,GAAS,EAAQ,MAAM,GAAG,QAAS,CAAI,EACrD,WAAa,GAAS,EAAQ,MAAM,GAAG,MAAO,CAAI,EAClD,QAAU,GAAS,EAAQ,MAAM,GAAG,UAAW,CAAI,CACrD,CACF,CAAC,EACD,KAAK,OAAO,SAAY,GAAU,KAAK,aAAa,CAAK,CAC3D,CAEA,IAAI,SAAmB,CACrB,OAAO,KAAK,SACd,CAEA,IAAI,QAAQ,EAAgB,CAC1B,KAAK,UAAY,EACjB,KAAK,OAAO,QAAU,EACtB,KAAK,WAAW,CAClB,CAEA,YAAmB,CACjB,KAAK,YAAc,IAAA,GACnB,KAAK,YAAc,IAAA,EACrB,CAEA,QAAe,CACb,KAAK,OAAO,EAAI,CAClB,CAEA,YAAY,EAAoB,CAC1B,SAAK,QAET,IACE,KAAK,QAAQ,cAAgB,OAC7B,EAAW,EAAM,KAAK,QAAQ,WAA+C,EAC7E,CACA,KAAK,UAAY,CAAC,KAAK,UACvB,KAAK,QAAQ,EACb,MACF,CAEI,SAAK,UAET,IAAI,KAAK,WAAY,CACnB,GAAI,KAAK,QAAQ,YAAY,QAAQ,EAAM,CAAwB,EAAG,CACpE,KAAK,WAAa,IAAA,GAClB,KAAK,oBAAsB,IAAA,GAC3B,KAAK,OAAO,QAAQ,EAAE,EACtB,KAAK,QAAQ,EACb,MACF,CACA,GAAI,KAAK,QAAQ,YAAY,QAAQ,EAAM,qBAA0B,EAAG,CACtE,KAAU,mBAAmB,EAC7B,MACF,CACA,KAAK,OAAO,YAAY,CAAI,EAC5B,KAAK,QAAQ,EACb,MACF,CAEA,GAAI,KAAK,QAAQ,YAAY,QAAQ,EAAM,CAAwB,EAAG,CACpE,KAAK,OAAO,EACZ,MACF,CACA,GAAI,EAAW,EAAM,EAAI,EAAE,EAAG,CAC5B,KAAK,WAAW,EAAE,EAClB,MACF,CACA,GAAI,EAAW,EAAM,EAAI,IAAI,EAAG,CAC9B,KAAK,WAAW,CAAC,EACjB,MACF,CACA,GAAI,EAAW,EAAM,EAAI,GAAG,GAAK,EAAW,EAAM,EAAI,KAAK,EAAG,CAC5D,KAAK,aAAa,CAAC,EACnB,MACF,CACA,GAAI,EAAW,EAAM,EAAI,MAAM,KAAK,CAAC,GAAK,EAAW,EAAM,EAAI,IAAI,EAAG,CACpE,KAAK,aAAa,EAAE,EACpB,MACF,CACA,GAAI,EAAW,EAAM,GAAG,GAAK,EAAW,EAAM,EAAI,MAAM,GAAG,CAAC,EAAG,CAC7D,KAAK,WAAW,OAAO,EACvB,MACF,CACA,GAAI,EAAW,EAAM,EAAI,KAAK,GAAK,IAAS,IAAK,CAC/C,KAAK,oBAAoB,EACzB,MACF,CACI,EAAW,EAAM,EAAI,KAAK,GAC5B,KAAK,wBAAwB,CA/B/B,CAnBA,CAoDF,CAEA,OAAO,EAAyB,CAC9B,IAAM,EAAc,KAAK,IAAI,EAAG,CAAK,EACrC,GAAI,KAAK,aAAe,KAAK,cAAgB,EAAa,OAAO,KAAK,YAEtE,GADA,KAAK,YAAc,EACf,KAAK,UAAW,CAClB,IAAM,EAAkB,CAAC,EACnB,EAAc,KAAK,QAAQ,KAWjC,OAVA,KAAK,WACH,EACA,GACA,KAAK,QAAQ,MAAM,GACjB,SACA,oBAAoB,EAAY,GAAG,KAAK,QAAQ,OAAO,UAAU,OAAO,eAAe,KAAK,QAAQ,YAAY,WAClH,EACA,CACF,EACA,KAAK,YAAc,EACZ,CACT,CAEA,IAAM,EAAkB,CAAC,EACnB,EAAW,KAAK,SAAS,EACzB,EAAS,KAAK,QAAQ,MAAM,GAAG,SAAU,IAAI,OAAO,CAAW,CAAC,EAetE,OAdA,EAAM,KAAK,CAAM,EACjB,KAAK,WAAW,EAAO,IAAK,KAAK,WAAW,EAAG,CAAW,EAC1D,EAAM,KAAK,EAAE,EACb,KAAK,WAAW,EAAO,IAAK,KAAK,QAAQ,MAAM,KAAK,EAAS,QAAQ,EAAG,CAAW,EACnF,EAAM,KAAK,EAAE,EAEb,KAAK,cAAc,EAAO,EAAU,CAAW,EAC/C,KAAK,cAAc,EAAO,EAAU,CAAW,EAC/C,KAAK,YAAY,EAAO,CAAW,EACnC,KAAK,aAAa,EAAO,CAAW,EACpC,EAAM,KAAK,EAAE,EACb,KAAK,WAAW,EAAO,IAAK,KAAK,SAAS,CAAQ,EAAG,CAAW,EAChE,EAAM,KAAK,CAAM,EACjB,KAAK,YAAc,EACZ,CACT,CAEA,UAAiC,CAC/B,IAAM,EAAW,KAAK,QAAQ,OAAO,UAAU,KAAK,iBACpD,GAAI,CAAC,EAAU,MAAU,MAAM,sCAAsC,EACrE,OAAO,CACT,CAEA,YAAoB,EAAgC,CAClD,OAAO,EAAS,QAAQ,OAAS,CACnC,CAEA,YAA6B,CAQ3B,OAPa,KAAK,QAAQ,OAAO,UAAU,KAAK,EAAU,IAAU,CAElE,IAAM,EAAQ,IADD,KAAK,QAAQ,IAAI,CAAK,EAAI,IAAM,IACtB,GAAG,EAAS,OAAO,GAC1C,OAAO,IAAU,KAAK,gBAClB,KAAK,QAAQ,MAAM,GAAG,aAAc,KAAK,QAAQ,MAAM,GAAG,OAAQ,CAAK,CAAC,EACxE,KAAK,QAAQ,MAAM,GAAG,KAAK,QAAQ,IAAI,CAAK,EAAI,UAAY,QAAS,CAAK,CAChF,CACU,CAAC,CAAC,KAAK,GAAG,CACtB,CAEA,cAAsB,EAAiB,EAAwB,EAAqB,CAClF,IAAM,EAAW,KAAK,gBAAgB,IAAI,KAAK,eAAe,GAAK,IAAI,IACvE,EAAS,QAAQ,SAAS,EAAQ,IAAU,CAC1C,IAAM,EAAU,IAAU,KAAK,cACzB,EAAU,EAAS,YAAe,EAAS,IAAI,CAAK,EAAI,MAAQ,MAAS,GAAG,EAAQ,EAAE,GACtF,EAAS,EAAU,KAAK,QAAQ,MAAM,GAAG,SAAU,IAAI,EAAI,KAC3D,EAAQ,GAAG,EAAQ,GAAG,EAAO,QACnC,KAAK,WAAW,EAAO,EAAQ,KAAK,QAAQ,MAAM,GAAG,EAAU,SAAW,OAAQ,CAAK,EAAG,CAAK,EAC3F,EAAO,aACT,KAAK,WAAW,EAAO,SAAU,KAAK,QAAQ,MAAM,GAAG,QAAS,EAAO,WAAW,EAAG,CAAK,CAE9F,CAAC,EAED,IAAM,EAAc,EAAS,QAAQ,OAC/B,EAAgB,KAAK,gBAAkB,EAC7C,KAAK,WACH,EACA,EAAgB,KAAK,QAAQ,MAAM,GAAG,SAAU,IAAI,EAAI,KACxD,KAAK,QAAQ,MAAM,GAAG,EAAgB,SAAW,OAAQ,mBAAmB,EAC5E,CACF,CACF,CAEA,cAAsB,EAAiB,EAAwB,EAAqB,CAClF,GAAI,KAAK,eAAiB,EAAS,QAAQ,OAAQ,OACnD,IAAM,EAAU,EAAS,QAAQ,KAAK,cAAc,EAAE,QACtD,GAAI,CAAC,EAAS,OACd,EAAM,KAAK,EAAE,EACb,KAAK,WAAW,EAAO,IAAK,KAAK,QAAQ,MAAM,GAAG,SAAU,KAAK,QAAQ,MAAM,KAAK,SAAS,CAAC,EAAG,CAAK,EACtG,IAAM,EAAW,IAAI,EACnB,EACA,EACA,EACA,KAAK,QAAQ,cACb,CAAE,MAAQ,GAAS,KAAK,QAAQ,MAAM,GAAG,QAAS,CAAI,CAAE,EACxD,CAAE,2BAA4B,GAAM,yBAA0B,EAAK,CACrE,EACA,IAAK,IAAM,KAAe,EAAS,OAAO,KAAK,IAAI,EAAG,EAAQ,CAAC,CAAC,EAC9D,KAAK,WAAW,EAAO,MAAO,GAAe,IAAK,CAAK,CAE3D,CAEA,YAAoB,EAAiB,EAAqB,CACxD,IAAM,EAAO,KAAK,MAAM,IAAI,KAAK,eAAe,EAC3C,GAAQ,KAAK,aAAe,UACjC,EAAM,KAAK,EAAE,EACb,KAAK,WAAW,EAAO,IAAK,KAAK,QAAQ,MAAM,GAAG,QAAS,SAAS,GAAM,EAAG,CAAK,EACpF,CAEA,aAAqB,EAAiB,EAAqB,CACzD,GAAI,CAAC,KAAK,WAAY,OACtB,EAAM,KAAK,EAAE,EACb,IAAM,EAAQ,KAAK,aAAe,SAAW,eAAiB,iBAC9D,KAAK,WAAW,EAAO,IAAK,KAAK,QAAQ,MAAM,GAAG,QAAS,CAAK,EAAG,CAAK,EACxE,IAAK,IAAM,KAAQ,KAAK,OAAO,OAAO,KAAK,IAAI,EAAG,EAAQ,CAAC,CAAC,EAC1D,EAAM,KAAK,EAAgB,IAAI,IAAQ,EAAO,EAAE,CAAC,EAE/C,KAAK,qBACP,KAAK,WAAW,EAAO,IAAK,KAAK,QAAQ,MAAM,GAAG,UAAW,KAAK,mBAAmB,EAAG,CAAK,CAEjG,CAEA,SAAiB,EAAgC,CAG/C,OAFI,KAAK,WAAmB,4DAErB,mCADe,EAAS,YAAc,+BAAiC,eACtB,8BAC1D,CAEA,WAAmB,EAAiB,EAAgB,EAAc,EAAqB,CACrF,IAAM,EAAc,KAAK,IAAI,EAAa,CAAM,EAAG,KAAK,IAAI,EAAG,EAAQ,CAAC,CAAC,EACnE,EAAiB,EAAgB,EAAQ,EAAa,EAAE,EACxD,EAAY,KAAK,IAAI,EAAG,EAAQ,CAAW,EAC3C,EAAU,EAAiB,EAAM,CAAS,EAChD,GAAI,EAAQ,SAAW,EAAG,CACxB,EAAM,KAAK,CAAc,EACzB,MACF,CACA,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAQ,OAAQ,GAAS,EAAG,CACtD,IAAM,EAAa,IAAU,EAAI,EAAiB,IAAI,OAAO,CAAW,EACxE,EAAM,KAAK,EAAgB,GAAG,IAAa,EAAQ,KAAU,EAAO,EAAE,CAAC,CACzE,CACF,CAEA,WAAmB,EAAqB,CACtC,IAAM,EAAQ,KAAK,YAAY,KAAK,SAAS,CAAC,EAC9C,KAAK,eAAiB,KAAK,cAAgB,EAAQ,GAAS,EAC5D,KAAK,QAAQ,CACf,CAEA,aAAqB,EAAqB,CACxC,IAAM,EAAQ,KAAK,QAAQ,OAAO,UAAU,OAC5C,KAAK,iBAAmB,KAAK,gBAAkB,EAAQ,GAAS,EAChE,KAAK,cAAgB,EACrB,KAAK,QAAQ,CACf,CAEA,qBAAoC,CAClC,IAAM,EAAW,KAAK,SAAS,EAC/B,GAAI,CAAC,EAAS,aAAe,KAAK,eAAiB,EAAS,QAAQ,OAAQ,OAC5E,IAAM,EAAW,KAAK,gBAAgB,IAAI,KAAK,eAAe,GAAK,IAAI,IACnE,EAAS,IAAI,KAAK,aAAa,EAAG,EAAS,OAAO,KAAK,aAAa,EACnE,EAAS,IAAI,KAAK,aAAa,EACpC,KAAK,gBAAgB,IAAI,KAAK,gBAAiB,CAAQ,EACvD,KAAK,QAAQ,CACf,CAEA,yBAAwC,CACtC,IAAM,EAAW,KAAK,SAAS,EAC/B,GAAI,KAAK,gBAAkB,EAAS,QAAQ,OAAQ,CAClD,KAAK,WAAW,QAAQ,EACxB,MACF,CAEA,GAAI,EAAS,YAAa,CACxB,IAAM,EAAW,KAAK,gBAAgB,IAAI,KAAK,eAAe,GAAK,IAAI,IACnE,EAAS,OAAS,GAAG,EAAS,IAAI,KAAK,aAAa,EACxD,IAAM,EAAS,CAAC,GAAG,CAAQ,CAAC,CACzB,MAAM,EAAM,IAAU,EAAO,CAAK,CAAC,CACnC,QAAS,GAAW,EAAS,QAAQ,GAAS,CAAC,EAAS,QAAQ,EAAM,CAAC,KAAK,EAAI,CAAC,CAAE,EACtF,KAAK,aAAa,CAChB,cAAe,KAAK,gBACpB,SAAU,EAAS,SACnB,KAAM,QACN,OAAQ,KACR,SAAU,EACV,GAAG,KAAK,WAAW,CACrB,CAAC,EACD,MACF,CAEA,IAAM,EAAS,EAAS,QAAQ,KAAK,eAChC,GACL,KAAK,aAAa,CAChB,cAAe,KAAK,gBACpB,SAAU,EAAS,SACnB,KAAM,SACN,OAAQ,EAAO,MACf,GAAI,EAAO,QAAU,CAAE,QAAS,EAAO,OAAQ,EAAI,CAAC,EACpD,GAAG,KAAK,WAAW,CACrB,CAAC,CACH,CAEA,WAAmB,EAAwB,CACzC,KAAK,WAAa,EAClB,KAAK,oBAAsB,IAAA,GAC3B,KAAK,OAAO,QAAQ,IAAS,QAAW,KAAK,MAAM,IAAI,KAAK,eAAe,GAAK,GAAM,EAAE,EACxF,KAAK,QAAQ,CACf,CAEA,MAAc,oBAAoC,CAChD,IAAM,EAAa,KAAK,WACnB,KACL,MAAK,QAAQ,IAAI,KAAK,EACtB,GAAI,CACF,IAAM,EAAS,MAAM,KAAK,QAAQ,eAAe,KAAK,OAAO,QAAQ,CAAC,EACtE,GAAI,KAAK,SAAW,KAAK,aAAe,EAAY,OAChD,EAAO,SAAW,YACpB,KAAK,OAAO,QAAQ,EAAO,OAAO,EAClC,KAAK,oBAAsB,IAAA,IAE3B,KAAK,oBAAsB,EAAO,OAEtC,OAAS,EAAO,CACd,KAAK,oBAAsB,2BAA2B,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,GAC7G,QAAU,CACR,KAAK,QAAQ,IAAI,MAAM,EACvB,KAAK,QAAQ,CACf,CAfsB,CAgBxB,CAEA,aAAqB,EAAqB,CACxC,IAAM,EAAO,KAAK,WAGlB,GAFA,KAAK,WAAa,IAAA,GAClB,KAAK,OAAO,QAAQ,EAAE,EAClB,IAAS,QAAS,CACpB,IAAM,EAAU,EAAM,KAAK,EACvB,EAAS,KAAK,MAAM,IAAI,KAAK,gBAAiB,CAAO,EACpD,KAAK,MAAM,OAAO,KAAK,eAAe,EAC3C,IAAM,EAAS,KAAK,QAAQ,IAAI,KAAK,eAAe,EAChD,IACF,KAAK,QAAQ,IAAI,KAAK,gBAAiB,CACrC,GAAG,EACH,GAAI,EAAU,CAAE,MAAO,CAAQ,EAAI,CAAE,MAAO,IAAA,EAAU,CACxD,CAAC,EACD,KAAK,eAAe,GAEtB,KAAK,QAAQ,EACb,MACF,CACI,IAAS,UACX,KAAK,aAAa,CAChB,cAAe,KAAK,gBACpB,SAAU,KAAK,SAAS,CAAC,CAAC,SAC1B,KAAM,SACN,OAAQ,EACR,GAAG,KAAK,WAAW,CACrB,CAAC,CAEL,CAEA,YAAyC,CACvC,IAAM,EAAO,KAAK,MAAM,IAAI,KAAK,eAAe,EAChD,OAAO,EAAO,CAAE,MAAO,CAAK,EAAI,CAAC,CACnC,CAEA,aAAqB,EAA8B,CAGjD,GAFA,KAAK,QAAQ,IAAI,KAAK,gBAAiB,CAAM,EAC7C,KAAK,eAAe,EAChB,KAAK,QAAQ,OAAS,KAAK,QAAQ,OAAO,UAAU,OAAQ,CAC9D,KAAK,OAAO,EAAK,EACjB,MACF,CACA,IAAM,EAAO,KAAK,QAAQ,OAAO,UAAU,WACxC,EAAW,IAAU,EAAQ,KAAK,iBAAmB,CAAC,KAAK,QAAQ,IAAI,CAAK,CAC/E,EACA,KAAK,gBACH,GAAQ,EAAI,EAAO,KAAK,QAAQ,OAAO,UAAU,WAAW,EAAW,IAAU,CAAC,KAAK,QAAQ,IAAI,CAAK,CAAC,EAC3G,KAAK,cAAgB,EACrB,KAAK,QAAQ,CACf,CAEA,gBAA2C,CACzC,MAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,CAAC,CAC9B,MAAM,EAAM,IAAU,EAAK,cAAgB,EAAM,aAAa,CAAC,CAC/D,IAAK,IAAY,CAAE,GAAG,CAAO,EAAE,CACpC,CAEA,gBAA+B,CAC7B,KAAK,QAAQ,iBAAiB,CAAE,QAAS,KAAK,eAAe,EAAG,UAAW,EAAM,CAAC,CACpF,CAEA,OAAe,EAA0B,CACnC,KAAK,UACT,KAAK,QAAU,GACf,KAAK,QAAQ,KAAK,CAAE,QAAS,KAAK,eAAe,EAAG,WAAU,CAAC,EACjE,CAEA,SAAwB,CACtB,KAAK,WAAW,EAChB,KAAK,QAAQ,IAAI,cAAc,CACjC,CACF"}
@@ -1,6 +1,6 @@
1
1
  //#region src/types/questionnaire.d.ts
2
- type AnswerKind = 'option' | 'custom' | 'multi';
3
- interface QuestionAnswer {
2
+ export type AnswerKind = 'option' | 'custom' | 'multi';
3
+ export interface QuestionAnswer {
4
4
  questionIndex: number;
5
5
  question: string;
6
6
  kind: AnswerKind;
@@ -9,10 +9,10 @@ interface QuestionAnswer {
9
9
  notes?: string;
10
10
  preview?: string;
11
11
  }
12
- type QuestionnaireError = 'no_ui' | 'no_custom_ui' | 'no_questions' | 'empty_options' | 'too_many_questions' | 'duplicate_question' | 'duplicate_option_label' | 'reserved_label' | 'session_load_failed' | 'stale_module_cache' |
12
+ export type QuestionnaireError = 'no_ui' | 'no_custom_ui' | 'no_questions' | 'empty_options' | 'too_many_questions' | 'duplicate_question' | 'duplicate_option_label' | 'reserved_label' | 'session_load_failed' | 'stale_module_cache' |
13
13
  /** A client replied with an answer envelope that did not match the questions asked. */
14
14
  'malformed_answers';
15
- interface QuestionnaireResult {
15
+ export interface QuestionnaireResult {
16
16
  answers: QuestionAnswer[];
17
17
  cancelled: boolean;
18
18
  error?: QuestionnaireError;
@@ -20,7 +20,7 @@ interface QuestionnaireResult {
20
20
  awaitingResponse?: boolean;
21
21
  voicePrompt?: string;
22
22
  }
23
- interface ToolTextResult {
23
+ export interface ToolTextResult {
24
24
  content: Array<{
25
25
  type: 'text';
26
26
  text: string;
@@ -29,5 +29,4 @@ interface ToolTextResult {
29
29
  terminate?: boolean;
30
30
  }
31
31
  //#endregion
32
- export { AnswerKind, QuestionAnswer, QuestionnaireError, QuestionnaireResult, ToolTextResult };
33
32
  //# sourceMappingURL=questionnaire.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"questionnaire.d.cts","names":[],"sources":["../../src/types/questionnaire.ts"],"mappings":";KAAY;UAEK;EACf;EACA;EACA,MAAM;EACN;EACA;EACA;EACA;;KAGU;;;UAkBK;EACf,SAAS;EACT;EACA,QAAQ;EACR;EACA;EACA;;UAGe;EACf,SAAS;IAAQ;IAAc;;EAC/B,SAAS;EACT"}
1
+ {"version":3,"file":"questionnaire.d.cts","names":[],"sources":["../../src/types/questionnaire.ts"],"mappings":";YAAY;iBAEK;EACf;EACA;EACA,MAAM;EACN;EACA;EACA;EACA;;YAGU;;;iBAkBK;EACf,SAAS;EACT;EACA,QAAQ;EACR;EACA;EACA;;iBAGe;EACf,SAAS;IAAQ;IAAc;;EAC/B,SAAS;EACT"}
@@ -1,6 +1,6 @@
1
1
  //#region src/types/questionnaire.d.ts
2
- type AnswerKind = 'option' | 'custom' | 'multi';
3
- interface QuestionAnswer {
2
+ export type AnswerKind = 'option' | 'custom' | 'multi';
3
+ export interface QuestionAnswer {
4
4
  questionIndex: number;
5
5
  question: string;
6
6
  kind: AnswerKind;
@@ -9,10 +9,10 @@ interface QuestionAnswer {
9
9
  notes?: string;
10
10
  preview?: string;
11
11
  }
12
- type QuestionnaireError = 'no_ui' | 'no_custom_ui' | 'no_questions' | 'empty_options' | 'too_many_questions' | 'duplicate_question' | 'duplicate_option_label' | 'reserved_label' | 'session_load_failed' | 'stale_module_cache' |
12
+ export type QuestionnaireError = 'no_ui' | 'no_custom_ui' | 'no_questions' | 'empty_options' | 'too_many_questions' | 'duplicate_question' | 'duplicate_option_label' | 'reserved_label' | 'session_load_failed' | 'stale_module_cache' |
13
13
  /** A client replied with an answer envelope that did not match the questions asked. */
14
14
  'malformed_answers';
15
- interface QuestionnaireResult {
15
+ export interface QuestionnaireResult {
16
16
  answers: QuestionAnswer[];
17
17
  cancelled: boolean;
18
18
  error?: QuestionnaireError;
@@ -20,7 +20,7 @@ interface QuestionnaireResult {
20
20
  awaitingResponse?: boolean;
21
21
  voicePrompt?: string;
22
22
  }
23
- interface ToolTextResult {
23
+ export interface ToolTextResult {
24
24
  content: Array<{
25
25
  type: 'text';
26
26
  text: string;
@@ -29,5 +29,4 @@ interface ToolTextResult {
29
29
  terminate?: boolean;
30
30
  }
31
31
  //#endregion
32
- export { AnswerKind, QuestionAnswer, QuestionnaireError, QuestionnaireResult, ToolTextResult };
33
32
  //# sourceMappingURL=questionnaire.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"questionnaire.d.mts","names":[],"sources":["../../src/types/questionnaire.ts"],"mappings":";KAAY;UAEK;EACf;EACA;EACA,MAAM;EACN;EACA;EACA;EACA;;KAGU;;;UAkBK;EACf,SAAS;EACT;EACA,QAAQ;EACR;EACA;EACA;;UAGe;EACf,SAAS;IAAQ;IAAc;;EAC/B,SAAS;EACT"}
1
+ {"version":3,"file":"questionnaire.d.mts","names":[],"sources":["../../src/types/questionnaire.ts"],"mappings":";YAAY;iBAEK;EACf;EACA;EACA,MAAM;EACN;EACA;EACA;EACA;;YAGU;;;iBAkBK;EACf,SAAS;EACT;EACA,QAAQ;EACR;EACA;EACA;;iBAGe;EACf,SAAS;IAAQ;IAAc;;EAC/B,SAAS;EACT"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-user-feedback",
3
- "version": "0.0.1-alpha.47",
3
+ "version": "0.0.1-alpha.53",
4
4
  "description": "Structured user questions with interactive and autonomous Voice handoff for Pi agents.",
5
5
  "keywords": [
6
6
  "ai",
@@ -56,29 +56,29 @@
56
56
  "access": "public"
57
57
  },
58
58
  "dependencies": {
59
- "@deepseek-ai/cordis": "4.0.1",
60
- "typebox": "1.3.19",
61
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.57",
62
- "@agimon-ai/doompi-ui": "0.0.1-alpha.58",
63
- "@agimon-ai/doompi-web-contracts": "0.0.1-alpha.20",
64
- "@agimon-ai/doompi-web-components": "0.0.1-alpha.17"
59
+ "@deepseek-ai/cordis": "4.0.2",
60
+ "typebox": "1.3.25",
61
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.64",
62
+ "@agimon-ai/doompi-web-components": "0.0.1-alpha.23",
63
+ "@agimon-ai/doompi-web-contracts": "0.0.1-alpha.26",
64
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.63"
65
65
  },
66
66
  "devDependencies": {
67
- "@earendil-works/pi-coding-agent": "0.85.0",
68
- "@earendil-works/pi-tui": "0.85.0",
67
+ "@earendil-works/pi-coding-agent": "0.85.1",
68
+ "@earendil-works/pi-tui": "0.85.1",
69
69
  "@tanstack/react-store": "0.11.1",
70
70
  "@tanstack/store": "0.11.1",
71
- "@types/node": "26.4.0",
71
+ "@types/node": "26.4.1",
72
72
  "@types/react": "19.2.18",
73
- "@vitest/coverage-v8": "4.1.11",
73
+ "@vitest/coverage-v8": "5.0.0",
74
74
  "react": "19.2.8",
75
- "tsdown": "0.22.14",
75
+ "tsdown": "0.23.0",
76
76
  "typescript": "7.0.2",
77
- "vitest": "4.1.11"
77
+ "vitest": "5.0.0"
78
78
  },
79
79
  "peerDependencies": {
80
- "@earendil-works/pi-coding-agent": "0.85.0",
81
- "@earendil-works/pi-tui": "0.85.0"
80
+ "@earendil-works/pi-coding-agent": "0.85.1",
81
+ "@earendil-works/pi-tui": "0.85.1"
82
82
  },
83
83
  "peerDependenciesMeta": {
84
84
  "@earendil-works/pi-coding-agent": {
@@ -6,7 +6,7 @@ import {
6
6
  toolTone,
7
7
  } from '@agimon-ai/doompi-web-components';
8
8
  import type { ToolMessageRenderProps } from '@agimon-ai/doompi-web-contracts';
9
- import { askCallSummary, askResultView } from './askUserText.ts';
9
+ import { askCallSummary, askResultView } from '../lib/askUserText.ts';
10
10
 
11
11
  /** The answered list, or the outcome that stood in for it. */
12
12
  function AskUserQuestionBody({ result, output, running, isError }: ToolMessageRenderProps) {
@@ -11,7 +11,7 @@ import {
11
11
  } from '@agimon-ai/doompi-web-components';
12
12
  import type { ToolPromptRenderProps } from '@agimon-ai/doompi-web-contracts';
13
13
  import { type KeyboardEvent, useEffect, useMemo, useRef, useState } from 'react';
14
- import { encodeAnswerEnvelope } from '../types/askUserWire.ts';
14
+ import { encodeAnswerEnvelope } from '../../types/askUserWire.ts';
15
15
  import {
16
16
  chooseOption,
17
17
  CUSTOM_LABEL,
@@ -26,7 +26,7 @@ import {
26
26
  readPromptQuestions,
27
27
  setCustom,
28
28
  setNotes,
29
- } from './questionnaireDraft.ts';
29
+ } from '../lib/questionnaireDraft.ts';
30
30
 
31
31
  /** The step bar: which questions are answered, which one is open, and a click back to any of them. */
32
32
  function StepBar({
package/src/web/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { defineWebPlugin } from '@agimon-ai/doompi-web-contracts';
2
- import { AskUserQuestionToolMessage } from './AskUserQuestionToolMessage.tsx';
3
- import { QuestionnairePrompt } from './QuestionnairePrompt.tsx';
4
- import { readPromptQuestions } from './questionnaireDraft.ts';
2
+ import { AskUserQuestionToolMessage } from './components/AskUserQuestionToolMessage.tsx';
3
+ import { QuestionnairePrompt } from './components/QuestionnairePrompt.tsx';
4
+ import { readPromptQuestions } from './lib/questionnaireDraft.ts';
5
5
 
6
6
  /**
7
7
  * This package's cockpit presence: the timeline card for ask_user_question,
@@ -10,7 +10,7 @@
10
10
  * A step the reader goes back to still has what they chose, and the agent is
11
11
  * asked once rather than once per question.
12
12
  */
13
- import type { QuestionAnswer } from '../types/questionnaire.ts';
13
+ import type { QuestionAnswer } from '../../types/questionnaire.ts';
14
14
 
15
15
  /** The row every question gets, so a reader always has a way past a bad set of options. */
16
16
  export const CUSTOM_LABEL = 'Type something.';
File without changes