@jakubmazanec/cli 0.1.0-c5ebb6f → 0.1.0-next.336fe21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/program/Command.d.ts +2 -2
  2. package/dist/program/Command.js +3 -3
  3. package/dist/program/Command.js.map +1 -1
  4. package/dist/program/CommandOptions.d.ts +1 -1
  5. package/dist/program/CommandOptions.js.map +1 -1
  6. package/dist/program/Program.d.ts +8 -3
  7. package/dist/program/Program.js +19 -8
  8. package/dist/program/Program.js.map +1 -1
  9. package/dist/program/ProgramOptions.d.ts +5 -1
  10. package/dist/program/ProgramOptions.js +1 -3
  11. package/dist/program/ProgramOptions.js.map +1 -1
  12. package/dist/program/ProgramStreams.d.ts +1 -1
  13. package/dist/program/ProgramStreams.js +1 -1
  14. package/dist/program/ProgramStreams.js.map +1 -1
  15. package/dist/ui/Failure.d.ts +2 -2
  16. package/dist/ui/Failure.js.map +1 -1
  17. package/dist/ui/Header.d.ts +1 -1
  18. package/dist/ui/Header.js.map +1 -1
  19. package/dist/ui/Help.d.ts +2 -2
  20. package/dist/ui/Help.js.map +1 -1
  21. package/dist/ui/form/SelectInput.d.ts +3 -3
  22. package/dist/ui/form/SelectInput.js +20 -29
  23. package/dist/ui/form/SelectInput.js.map +1 -1
  24. package/dist/ui/form/TextInput.d.ts +1 -1
  25. package/dist/ui/form/TextInput.js +4 -7
  26. package/dist/ui/form/TextInput.js.map +1 -1
  27. package/dist/ui/getErrorDetail.js +9 -0
  28. package/dist/ui/getErrorDetail.js.map +1 -1
  29. package/dist/ui/internals/Wrapper.d.ts +2 -2
  30. package/dist/ui/internals/Wrapper.js +1 -1
  31. package/dist/ui/internals/Wrapper.js.map +1 -1
  32. package/dist/ui/internals/programContext.d.ts +1 -1
  33. package/dist/ui/internals/programContext.js +1 -1
  34. package/dist/ui/internals/programContext.js.map +1 -1
  35. package/dist/ui/useProgram.d.ts +1 -1
  36. package/dist/ui/useProgram.js +1 -1
  37. package/dist/ui/useProgram.js.map +1 -1
  38. package/package.json +8 -7
  39. package/src/program/Command.ts +2 -3
  40. package/src/program/CommandOptions.ts +1 -1
  41. package/src/program/Program.ts +30 -5
  42. package/src/program/ProgramOptions.ts +7 -1
  43. package/src/program/ProgramStreams.ts +1 -1
  44. package/src/ui/Failure.tsx +2 -2
  45. package/src/ui/Header.tsx +1 -1
  46. package/src/ui/Help.tsx +2 -2
  47. package/src/ui/form/SelectInput.tsx +23 -28
  48. package/src/ui/form/TextInput.tsx +5 -10
  49. package/src/ui/getErrorDetail.ts +11 -0
  50. package/src/ui/internals/Wrapper.tsx +2 -2
  51. package/src/ui/internals/programContext.ts +1 -1
  52. package/src/ui/useProgram.ts +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/form/TextInput.tsx"],"sourcesContent":["// TODO: resolve\n/* eslint-disable complexity -- TODO */\nimport chalk from 'chalk';\nimport {Box, type BoxProps, Text, useInput} from 'ink';\nimport {useEffect, useState} from 'react';\n\nimport * as colors from '../colors.js';\nimport * as icons from '../icons.js';\n\n/**\n * Text input component props.\n */\nexport type TextInputProps = BoxProps & {\n /** Text to display as a label. */\n label: string;\n\n /** Text to display when `value` is empty. */\n placeholder?: string | undefined;\n\n /** Value. */\n value: string;\n\n /** Should component listen to Listen to user's input? Useful in case there are multiple input components at the same time and input must be \"routed\" to a specific component. */\n focus?: boolean | undefined;\n\n /** Replace all chars and mask the value. Useful for password inputs. */\n mask?: string | undefined;\n\n /** Show cursor and allow navigation using arrow keys. */\n showCursor?: boolean | undefined;\n\n /** Validation function that is called when value changes. If this function throws an error*/\n validate?: ((value: string) => void) | undefined;\n\n /** Handler function that is called when value changes. */\n onChange: (value: string) => void;\n\n /** Handler function that is called when enter is pressed. */\n onSubmit?: ((value: string) => void) | undefined;\n\n /** Indicates whether value has been accepted. Component then doesn't accept more input. */\n isComplete?: boolean | undefined;\n};\n\n/**\n * Text input component.\n */\nexport function TextInput({\n label,\n validate,\n isComplete,\n value: originalValue,\n placeholder = '',\n focus = true,\n mask,\n showCursor = true,\n onChange,\n onSubmit,\n ...boxProps\n}: TextInputProps) {\n let [validationError, setValidationError] = useState('');\n let [state, setState] = useState({\n cursorOffset: (originalValue || '').length,\n cursorWidth: 0,\n });\n\n useEffect(() => {\n setState((previousState) => {\n if (!focus || !showCursor) {\n return previousState;\n }\n\n let newValue = originalValue || '';\n\n if (previousState.cursorOffset > newValue.length - 1) {\n return {\n cursorOffset: newValue.length,\n cursorWidth: 0,\n };\n }\n\n return previousState;\n });\n }, [originalValue, focus, showCursor]);\n\n let value = mask ? mask.repeat(originalValue.length) : originalValue;\n let renderedValue = value;\n let renderedPlaceholder = placeholder ? chalk[colors.muted](placeholder) : undefined;\n\n // fake mouse cursor; dealing with with actual cursor and ANSI escape codes is inconvinient\n if (showCursor && focus) {\n renderedPlaceholder =\n placeholder.length > 0\n ? chalk.inverse(placeholder[0]) + chalk[colors.muted](placeholder.slice(1))\n : chalk.inverse(' ');\n\n renderedValue = value.length > 0 ? '' : chalk.inverse(' ');\n\n let i = 0;\n\n for (let char of value) {\n if (i >= state.cursorOffset && i <= state.cursorOffset) {\n renderedValue += chalk.inverse(char);\n } else {\n renderedValue += char;\n }\n\n i += 1;\n }\n\n if (value.length > 0 && state.cursorOffset === value.length) {\n renderedValue += chalk.inverse(' ');\n }\n }\n\n useInput(\n (input, key) => {\n if (\n key.upArrow ||\n key.downArrow ||\n (key.ctrl && input === 'c') ||\n (key.shift && key.tab) ||\n key.tab\n ) {\n return;\n }\n\n if (key.return) {\n if (onSubmit) {\n if (validate) {\n try {\n validate(originalValue);\n } catch (error: unknown) {\n setValidationError((error as Error).message);\n\n return;\n }\n }\n\n if (validationError) {\n setValidationError('');\n } else {\n onSubmit(originalValue);\n }\n }\n\n return;\n }\n\n let nextCursorOffset = state.cursorOffset;\n let nextValue = originalValue;\n let nextCursorWidth = 0;\n\n if (key.leftArrow) {\n if (showCursor) {\n nextCursorOffset -= 1;\n }\n } else if (key.rightArrow) {\n if (showCursor) {\n nextCursorOffset += 1;\n }\n } else if (key.backspace || key.delete) {\n if (state.cursorOffset > 0) {\n nextValue =\n originalValue.slice(0, state.cursorOffset - 1) +\n originalValue.slice(state.cursorOffset, originalValue.length);\n\n nextCursorOffset -= 1;\n }\n } else {\n nextValue =\n originalValue.slice(0, state.cursorOffset) +\n input +\n originalValue.slice(state.cursorOffset, originalValue.length);\n\n nextCursorOffset += input.length;\n\n if (input.length > 1) {\n nextCursorWidth = input.length;\n }\n }\n\n if (state.cursorOffset < 0) {\n nextCursorOffset = 0;\n }\n\n if (state.cursorOffset > originalValue.length) {\n nextCursorOffset = originalValue.length;\n }\n\n setState({\n cursorOffset: nextCursorOffset,\n cursorWidth: nextCursorWidth,\n });\n\n if (nextValue !== originalValue) {\n onChange(nextValue);\n }\n },\n {isActive: focus},\n );\n\n return (\n <Box {...boxProps}>\n <Box marginRight={1}>\n <Text>{isComplete ? icons.success : icons.question}</Text>\n </Box>\n\n <Box flexDirection=\"column\">\n <Box marginRight={1}>\n <Text color={colors.highlighted}>{label}</Text>\n </Box>\n {isComplete ? (\n <Text color={colors.body}>{value}</Text>\n ) : (\n <Text color={colors.highlighted}>\n {placeholder ? (value.length > 0 ? renderedValue : renderedPlaceholder) : renderedValue}\n </Text>\n )}\n {validationError ? (\n <Box marginTop={0}>\n <Text color={colors.warning}>{validationError}</Text>\n </Box>\n ) : null}\n </Box>\n </Box>\n );\n}\n"],"names":["chalk","Box","Text","useInput","useEffect","useState","colors","icons","TextInput","label","validate","isComplete","value","originalValue","placeholder","focus","mask","showCursor","onChange","onSubmit","boxProps","validationError","setValidationError","state","setState","cursorOffset","length","cursorWidth","previousState","newValue","repeat","renderedValue","renderedPlaceholder","muted","undefined","inverse","slice","i","char","input","key","upArrow","downArrow","ctrl","shift","tab","return","error","message","nextCursorOffset","nextValue","nextCursorWidth","leftArrow","rightArrow","backspace","delete","isActive","marginRight","success","question","flexDirection","color","highlighted","body","marginTop","warning"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/ui/form/TextInput.tsx"],"sourcesContent":["// TODO: resolve\n/* eslint-disable complexity -- TODO */\nimport chalk from 'chalk';\nimport {Box, type BoxProps, Text, useInput} from 'ink';\nimport {useEffect, useState} from 'react';\n\nimport * as colors from '../colors.js';\nimport * as icons from '../icons.js';\n\n/**\n * Text input component props.\n */\nexport type TextInputProps = BoxProps & {\n /** Text to display as a label. */\n label: string;\n\n /** Text to display when `value` is empty. */\n placeholder?: string | undefined;\n\n /** Value. */\n value: string;\n\n /** Should component listen to Listen to user's input? Useful in case there are multiple input components at the same time and input must be handled only by a specific component. */\n focus?: boolean | undefined;\n\n /** Replace all chars and mask the value. Useful for password inputs. */\n mask?: string | undefined;\n\n /** Show cursor and allow navigation using arrow keys. */\n showCursor?: boolean | undefined;\n\n /** Validation function that is called when value changes. If this function throws an error*/\n validate?: ((value: string) => void) | undefined;\n\n /** Handler function that is called when value changes. */\n onChange: (value: string) => void;\n\n /** Handler function that is called when enter is pressed. */\n onSubmit?: ((value: string) => void) | undefined;\n\n /** Indicates whether value has been accepted. Component then doesn't accept more input. */\n isComplete?: boolean | undefined;\n};\n\n/**\n * Text input component.\n */\nexport function TextInput({\n label,\n validate,\n isComplete,\n value: originalValue,\n placeholder = '',\n focus = true,\n mask,\n showCursor = true,\n onChange,\n onSubmit,\n ...boxProps\n}: TextInputProps) {\n let [validationError, setValidationError] = useState('');\n let [state, setState] = useState({\n cursorOffset: (originalValue || '').length,\n cursorWidth: 0,\n });\n\n useEffect(() => {\n setState((previousState) => {\n if (!focus || !showCursor) {\n return previousState;\n }\n\n let newValue = originalValue || '';\n\n if (previousState.cursorOffset > newValue.length - 1) {\n return {\n cursorOffset: newValue.length,\n cursorWidth: 0,\n };\n }\n\n return previousState;\n });\n }, [originalValue, focus, showCursor]);\n\n let value = mask ? mask.repeat(originalValue.length) : originalValue;\n let renderedValue = value;\n let renderedPlaceholder = placeholder ? chalk[colors.muted](placeholder) : undefined;\n\n if (showCursor && focus) {\n renderedPlaceholder =\n placeholder.length > 0\n ? chalk.inverse(placeholder[0]) + chalk[colors.muted](placeholder.slice(1))\n : chalk.inverse(' ');\n\n renderedValue = value.length > 0 ? '' : chalk.inverse(' ');\n\n for (let [i, character] of Object.entries(value)) {\n if (Number(i) >= state.cursorOffset && Number(i) <= state.cursorOffset) {\n renderedValue += chalk.inverse(character);\n } else {\n renderedValue += character;\n }\n }\n\n if (value.length > 0 && state.cursorOffset === value.length) {\n renderedValue += chalk.inverse(' ');\n }\n }\n\n useInput(\n (input, key) => {\n if (\n key.upArrow ||\n key.downArrow ||\n (key.ctrl && input === 'c') ||\n (key.shift && key.tab) ||\n key.tab\n ) {\n return;\n }\n\n if (key.return) {\n if (onSubmit) {\n if (validate) {\n try {\n validate(originalValue);\n } catch (error: unknown) {\n setValidationError((error as Error).message);\n\n return;\n }\n }\n\n if (validationError) {\n setValidationError('');\n } else {\n onSubmit(originalValue);\n }\n }\n\n return;\n }\n\n let nextCursorOffset = state.cursorOffset;\n let nextValue = originalValue;\n let nextCursorWidth = 0;\n\n if (key.leftArrow) {\n if (showCursor) {\n nextCursorOffset -= 1;\n }\n } else if (key.rightArrow) {\n if (showCursor) {\n nextCursorOffset += 1;\n }\n } else if (key.backspace || key.delete) {\n if (state.cursorOffset > 0) {\n nextValue =\n originalValue.slice(0, state.cursorOffset - 1) +\n originalValue.slice(state.cursorOffset, originalValue.length);\n\n nextCursorOffset -= 1;\n }\n } else {\n nextValue =\n originalValue.slice(0, state.cursorOffset) +\n input +\n originalValue.slice(state.cursorOffset, originalValue.length);\n\n nextCursorOffset += input.length;\n\n if (input.length > 1) {\n nextCursorWidth = input.length;\n }\n }\n\n if (state.cursorOffset < 0) {\n nextCursorOffset = 0;\n }\n\n if (state.cursorOffset > originalValue.length) {\n nextCursorOffset = originalValue.length;\n }\n\n setState({\n cursorOffset: nextCursorOffset,\n cursorWidth: nextCursorWidth,\n });\n\n if (nextValue !== originalValue) {\n onChange(nextValue);\n }\n },\n {isActive: focus},\n );\n\n return (\n <Box {...boxProps}>\n <Box marginRight={1}>\n <Text>{isComplete ? icons.success : icons.question}</Text>\n </Box>\n\n <Box flexDirection=\"column\">\n <Box marginRight={1}>\n <Text color={colors.highlighted}>{label}</Text>\n </Box>\n {isComplete ? (\n <Text color={colors.body}>{value}</Text>\n ) : (\n <Text color={colors.highlighted}>\n {placeholder ? (value.length > 0 ? renderedValue : renderedPlaceholder) : renderedValue}\n </Text>\n )}\n {validationError ? (\n <Box marginTop={0}>\n <Text color={colors.warning}>{validationError}</Text>\n </Box>\n ) : null}\n </Box>\n </Box>\n );\n}\n"],"names":["chalk","Box","Text","useInput","useEffect","useState","colors","icons","TextInput","label","validate","isComplete","value","originalValue","placeholder","focus","mask","showCursor","onChange","onSubmit","boxProps","validationError","setValidationError","state","setState","cursorOffset","length","cursorWidth","previousState","newValue","repeat","renderedValue","renderedPlaceholder","muted","undefined","inverse","slice","i","character","Object","entries","Number","input","key","upArrow","downArrow","ctrl","shift","tab","return","error","message","nextCursorOffset","nextValue","nextCursorWidth","leftArrow","rightArrow","backspace","delete","isActive","marginRight","success","question","flexDirection","color","highlighted","body","marginTop","warning"],"mappings":"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"}
@@ -1,4 +1,6 @@
1
1
  import { ValidationError, ParsingError } from '@jakubmazanec/args';
2
+ import { zodIssuesToStrings } from '@jakubmazanec/zod-utils';
3
+ import { ZodError } from 'zod';
2
4
  /**
3
5
  * Creates detailed information about an error.
4
6
  */ export function getErrorDetail(error) {
@@ -37,6 +39,13 @@ import { ValidationError, ParsingError } from '@jakubmazanec/args';
37
39
  }
38
40
  return errorDetail;
39
41
  }
42
+ if (error instanceof ZodError) {
43
+ let errorDetail = {
44
+ label: 'Error',
45
+ description: zodIssuesToStrings(error.issues).join('\n')
46
+ };
47
+ return errorDetail;
48
+ }
40
49
  if (error instanceof Error) {
41
50
  let errorDetail = {
42
51
  label: 'Error',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/ui/getErrorDetail.ts"],"sourcesContent":["import {ValidationError, ParsingError} from '@jakubmazanec/args';\n\nimport {type ErrorDetail} from './ErrorDetail.js';\n\n/**\n * Creates detailed information about an error.\n */\nexport function getErrorDetail(error: unknown): ErrorDetail {\n // TODO: add all codes\n if (error instanceof ParsingError) {\n switch (error.code) {\n default: {\n return {\n label: 'Parsing error',\n description: error.message,\n };\n }\n }\n }\n\n // TODO: add all codes\n if (error instanceof ValidationError) {\n switch (error.code) {\n default: {\n return {\n label: 'Validation error',\n description: error.message,\n };\n }\n }\n }\n\n // TODO: add all built-in errors\n if (error instanceof ReferenceError) {\n let errorDetail: ErrorDetail = {\n label: 'Error',\n description: error.message,\n };\n\n if (error.stack) {\n errorDetail.stack = error.stack;\n }\n\n return errorDetail;\n }\n\n if (error instanceof Error) {\n let errorDetail: ErrorDetail = {\n label: 'Error',\n description: error.message,\n };\n\n if (error.stack) {\n errorDetail.stack = error.stack;\n }\n\n if (error.cause instanceof Error) {\n errorDetail.cause = {\n description: error.cause.message,\n };\n\n if (error.cause.stack) {\n errorDetail.cause.stack = error.cause.stack;\n }\n }\n\n return errorDetail;\n }\n\n return {\n label: 'Error',\n };\n}\n"],"names":["ValidationError","ParsingError","getErrorDetail","error","code","label","description","message","ReferenceError","errorDetail","stack","Error","cause"],"mappings":"AAAA,SAAQA,eAAe,EAAEC,YAAY,QAAO,qBAAqB;AAIjE;;CAEC,GACD,OAAO,SAASC,eAAeC,KAAc;IAC3C,sBAAsB;IACtB,IAAIA,iBAAiBF,cAAc;QACjC,OAAQE,MAAMC,IAAI;YAChB;gBAAS;oBACP,OAAO;wBACLC,OAAO;wBACPC,aAAaH,MAAMI,OAAO;oBAC5B;gBACF;QACF;IACF;IAEA,sBAAsB;IACtB,IAAIJ,iBAAiBH,iBAAiB;QACpC,OAAQG,MAAMC,IAAI;YAChB;gBAAS;oBACP,OAAO;wBACLC,OAAO;wBACPC,aAAaH,MAAMI,OAAO;oBAC5B;gBACF;QACF;IACF;IAEA,gCAAgC;IAChC,IAAIJ,iBAAiBK,gBAAgB;QACnC,IAAIC,cAA2B;YAC7BJ,OAAO;YACPC,aAAaH,MAAMI,OAAO;QAC5B;QAEA,IAAIJ,MAAMO,KAAK,EAAE;YACfD,YAAYC,KAAK,GAAGP,MAAMO,KAAK;QACjC;QAEA,OAAOD;IACT;IAEA,IAAIN,iBAAiBQ,OAAO;QAC1B,IAAIF,cAA2B;YAC7BJ,OAAO;YACPC,aAAaH,MAAMI,OAAO;QAC5B;QAEA,IAAIJ,MAAMO,KAAK,EAAE;YACfD,YAAYC,KAAK,GAAGP,MAAMO,KAAK;QACjC;QAEA,IAAIP,MAAMS,KAAK,YAAYD,OAAO;YAChCF,YAAYG,KAAK,GAAG;gBAClBN,aAAaH,MAAMS,KAAK,CAACL,OAAO;YAClC;YAEA,IAAIJ,MAAMS,KAAK,CAACF,KAAK,EAAE;gBACrBD,YAAYG,KAAK,CAACF,KAAK,GAAGP,MAAMS,KAAK,CAACF,KAAK;YAC7C;QACF;QAEA,OAAOD;IACT;IAEA,OAAO;QACLJ,OAAO;IACT;AACF"}
1
+ {"version":3,"sources":["../../src/ui/getErrorDetail.ts"],"sourcesContent":["import {ValidationError, ParsingError} from '@jakubmazanec/args';\nimport {zodIssuesToStrings} from '@jakubmazanec/zod-utils';\nimport {ZodError} from 'zod';\n\nimport {type ErrorDetail} from './ErrorDetail.js';\n\n/**\n * Creates detailed information about an error.\n */\nexport function getErrorDetail(error: unknown): ErrorDetail {\n // TODO: add all codes\n if (error instanceof ParsingError) {\n switch (error.code) {\n default: {\n return {\n label: 'Parsing error',\n description: error.message,\n };\n }\n }\n }\n\n // TODO: add all codes\n if (error instanceof ValidationError) {\n switch (error.code) {\n default: {\n return {\n label: 'Validation error',\n description: error.message,\n };\n }\n }\n }\n\n // TODO: add all built-in errors\n if (error instanceof ReferenceError) {\n let errorDetail: ErrorDetail = {\n label: 'Error',\n description: error.message,\n };\n\n if (error.stack) {\n errorDetail.stack = error.stack;\n }\n\n return errorDetail;\n }\n\n if (error instanceof ZodError) {\n let errorDetail: ErrorDetail = {\n label: 'Error',\n description: zodIssuesToStrings(error.issues).join('\\n'),\n };\n\n return errorDetail;\n }\n\n if (error instanceof Error) {\n let errorDetail: ErrorDetail = {\n label: 'Error',\n description: error.message,\n };\n\n if (error.stack) {\n errorDetail.stack = error.stack;\n }\n\n if (error.cause instanceof Error) {\n errorDetail.cause = {\n description: error.cause.message,\n };\n\n if (error.cause.stack) {\n errorDetail.cause.stack = error.cause.stack;\n }\n }\n\n return errorDetail;\n }\n\n return {\n label: 'Error',\n };\n}\n"],"names":["ValidationError","ParsingError","zodIssuesToStrings","ZodError","getErrorDetail","error","code","label","description","message","ReferenceError","errorDetail","stack","issues","join","Error","cause"],"mappings":"AAAA,SAAQA,eAAe,EAAEC,YAAY,QAAO,qBAAqB;AACjE,SAAQC,kBAAkB,QAAO,0BAA0B;AAC3D,SAAQC,QAAQ,QAAO,MAAM;AAI7B;;CAEC,GACD,OAAO,SAASC,eAAeC,KAAc;IAC3C,sBAAsB;IACtB,IAAIA,iBAAiBJ,cAAc;QACjC,OAAQI,MAAMC,IAAI;YAChB;gBAAS;oBACP,OAAO;wBACLC,OAAO;wBACPC,aAAaH,MAAMI,OAAO;oBAC5B;gBACF;QACF;IACF;IAEA,sBAAsB;IACtB,IAAIJ,iBAAiBL,iBAAiB;QACpC,OAAQK,MAAMC,IAAI;YAChB;gBAAS;oBACP,OAAO;wBACLC,OAAO;wBACPC,aAAaH,MAAMI,OAAO;oBAC5B;gBACF;QACF;IACF;IAEA,gCAAgC;IAChC,IAAIJ,iBAAiBK,gBAAgB;QACnC,IAAIC,cAA2B;YAC7BJ,OAAO;YACPC,aAAaH,MAAMI,OAAO;QAC5B;QAEA,IAAIJ,MAAMO,KAAK,EAAE;YACfD,YAAYC,KAAK,GAAGP,MAAMO,KAAK;QACjC;QAEA,OAAOD;IACT;IAEA,IAAIN,iBAAiBF,UAAU;QAC7B,IAAIQ,cAA2B;YAC7BJ,OAAO;YACPC,aAAaN,mBAAmBG,MAAMQ,MAAM,EAAEC,IAAI,CAAC;QACrD;QAEA,OAAOH;IACT;IAEA,IAAIN,iBAAiBU,OAAO;QAC1B,IAAIJ,cAA2B;YAC7BJ,OAAO;YACPC,aAAaH,MAAMI,OAAO;QAC5B;QAEA,IAAIJ,MAAMO,KAAK,EAAE;YACfD,YAAYC,KAAK,GAAGP,MAAMO,KAAK;QACjC;QAEA,IAAIP,MAAMW,KAAK,YAAYD,OAAO;YAChCJ,YAAYK,KAAK,GAAG;gBAClBR,aAAaH,MAAMW,KAAK,CAACP,OAAO;YAClC;YAEA,IAAIJ,MAAMW,KAAK,CAACJ,KAAK,EAAE;gBACrBD,YAAYK,KAAK,CAACJ,KAAK,GAAGP,MAAMW,KAAK,CAACJ,KAAK;YAC7C;QACF;QAEA,OAAOD;IACT;IAEA,OAAO;QACLJ,OAAO;IACT;AACF"}
@@ -1,12 +1,12 @@
1
1
  import { type FC, type PropsWithChildren } from 'react';
2
2
  import { type Program } from '../../program.js';
3
3
  /**
4
- * {@linkcode Wrapper} component props.
4
+ * {@link Wrapper} component props.
5
5
  */
6
6
  export type WrapperProps = {
7
7
  program: Program;
8
8
  };
9
9
  /**
10
- * A component that wraps anything {@linkcode Program} tries to render with an error boundary and all neccessary providers.
10
+ * A component that wraps anything {@link Program} tries to render with an error boundary and all neccessary providers.
11
11
  */
12
12
  export declare const Wrapper: FC<PropsWithChildren<WrapperProps>>;
@@ -5,7 +5,7 @@ import { ErrorBoundary } from 'react-error-boundary';
5
5
  import { programContext } from './programContext.js';
6
6
  import { Failure } from '../Failure.js';
7
7
  /**
8
- * A component that wraps anything {@linkcode Program} tries to render with an error boundary and all neccessary providers.
8
+ * A component that wraps anything {@link Program} tries to render with an error boundary and all neccessary providers.
9
9
  */ export const Wrapper = ({ program, children })=>{
10
10
  let programContextValue = useMemo(()=>({
11
11
  program
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/internals/Wrapper.tsx"],"sourcesContent":["import {Box} from 'ink';\nimport {useMemo, type FC, type PropsWithChildren} from 'react';\nimport {ErrorBoundary} from 'react-error-boundary';\n\nimport {programContext} from './programContext.js';\nimport {type Program} from '../../program.js';\nimport {Failure} from '../Failure.js';\n\n/**\n * {@linkcode Wrapper} component props.\n */\nexport type WrapperProps = {\n program: Program;\n};\n\n/**\n * A component that wraps anything {@linkcode Program} tries to render with an error boundary and all neccessary providers.\n */\nexport const Wrapper: FC<PropsWithChildren<WrapperProps>> = ({program, children}) => {\n let programContextValue = useMemo(\n () => ({\n program,\n }),\n [program],\n );\n\n return (\n <ErrorBoundary FallbackComponent={Failure}>\n <programContext.Provider value={programContextValue}>\n <Box>{children}</Box>\n </programContext.Provider>\n </ErrorBoundary>\n );\n};\n"],"names":["Box","useMemo","ErrorBoundary","programContext","Failure","Wrapper","program","children","programContextValue","FallbackComponent","Provider","value"],"mappings":";AAAA,SAAQA,GAAG,QAAO,MAAM;AACxB,SAAQC,OAAO,QAAwC,QAAQ;AAC/D,SAAQC,aAAa,QAAO,uBAAuB;AAEnD,SAAQC,cAAc,QAAO,sBAAsB;AAEnD,SAAQC,OAAO,QAAO,gBAAgB;AAStC;;CAEC,GACD,OAAO,MAAMC,UAA+C,CAAC,EAACC,OAAO,EAAEC,QAAQ,EAAC;IAC9E,IAAIC,sBAAsBP,QACxB,IAAO,CAAA;YACLK;QACF,CAAA,GACA;QAACA;KAAQ;IAGX,qBACE,KAACJ;QAAcO,mBAAmBL;kBAChC,cAAA,KAACD,eAAeO,QAAQ;YAACC,OAAOH;sBAC9B,cAAA,KAACR;0BAAKO;;;;AAId,EAAE"}
1
+ {"version":3,"sources":["../../../src/ui/internals/Wrapper.tsx"],"sourcesContent":["import {Box} from 'ink';\nimport {useMemo, type FC, type PropsWithChildren} from 'react';\nimport {ErrorBoundary} from 'react-error-boundary';\n\nimport {programContext} from './programContext.js';\nimport {type Program} from '../../program.js';\nimport {Failure} from '../Failure.js';\n\n/**\n * {@link Wrapper} component props.\n */\nexport type WrapperProps = {\n program: Program;\n};\n\n/**\n * A component that wraps anything {@link Program} tries to render with an error boundary and all neccessary providers.\n */\nexport const Wrapper: FC<PropsWithChildren<WrapperProps>> = ({program, children}) => {\n let programContextValue = useMemo(\n () => ({\n program,\n }),\n [program],\n );\n\n return (\n <ErrorBoundary FallbackComponent={Failure}>\n <programContext.Provider value={programContextValue}>\n <Box>{children}</Box>\n </programContext.Provider>\n </ErrorBoundary>\n );\n};\n"],"names":["Box","useMemo","ErrorBoundary","programContext","Failure","Wrapper","program","children","programContextValue","FallbackComponent","Provider","value"],"mappings":";AAAA,SAAQA,GAAG,QAAO,MAAM;AACxB,SAAQC,OAAO,QAAwC,QAAQ;AAC/D,SAAQC,aAAa,QAAO,uBAAuB;AAEnD,SAAQC,cAAc,QAAO,sBAAsB;AAEnD,SAAQC,OAAO,QAAO,gBAAgB;AAStC;;CAEC,GACD,OAAO,MAAMC,UAA+C,CAAC,EAACC,OAAO,EAAEC,QAAQ,EAAC;IAC9E,IAAIC,sBAAsBP,QACxB,IAAO,CAAA;YACLK;QACF,CAAA,GACA;QAACA;KAAQ;IAGX,qBACE,KAACJ;QAAcO,mBAAmBL;kBAChC,cAAA,KAACD,eAAeO,QAAQ;YAACC,OAAOH;sBAC9B,cAAA,KAACR;0BAAKO;;;;AAId,EAAE"}
@@ -4,6 +4,6 @@ export type ProgramContext = {
4
4
  program: Program;
5
5
  };
6
6
  /**
7
- * A React context that stores {@linkcode Program} instance.
7
+ * A React context that stores {@link Program} instance.
8
8
  */
9
9
  export declare const programContext: React.Context<ProgramContext>;
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  /**
3
- * A React context that stores {@linkcode Program} instance.
3
+ * A React context that stores {@link Program} instance.
4
4
  */ export const programContext = React.createContext({});
5
5
 
6
6
  //# sourceMappingURL=programContext.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/internals/programContext.ts"],"sourcesContent":["import React from 'react';\n\nimport {type Program} from '../../program/Program.js';\n\nexport type ProgramContext = {\n program: Program;\n};\n\n/**\n * A React context that stores {@linkcode Program} instance.\n */\nexport const programContext = React.createContext<ProgramContext>({} as ProgramContext);\n"],"names":["React","programContext","createContext"],"mappings":"AAAA,OAAOA,WAAW,QAAQ;AAQ1B;;CAEC,GACD,OAAO,MAAMC,iBAAiBD,MAAME,aAAa,CAAiB,CAAC,GAAqB"}
1
+ {"version":3,"sources":["../../../src/ui/internals/programContext.ts"],"sourcesContent":["import React from 'react';\n\nimport {type Program} from '../../program/Program.js';\n\nexport type ProgramContext = {\n program: Program;\n};\n\n/**\n * A React context that stores {@link Program} instance.\n */\nexport const programContext = React.createContext<ProgramContext>({} as ProgramContext);\n"],"names":["React","programContext","createContext"],"mappings":"AAAA,OAAOA,WAAW,QAAQ;AAQ1B;;CAEC,GACD,OAAO,MAAMC,iBAAiBD,MAAME,aAAa,CAAiB,CAAC,GAAqB"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * A React hook for accessing {@linkcode Program} instance and `exit` function that is used for showing an error (via an error boundary) and exiting.
2
+ * A React hook for accessing {@link Program} instance and `exit` function that is used for showing an error (via an error boundary) and exiting.
3
3
  */
4
4
  export declare function useProgram(): {
5
5
  program: import("../program.js").Program;
@@ -3,7 +3,7 @@ import { useCallback, useContext } from 'react';
3
3
  import { useErrorBoundary } from 'react-error-boundary';
4
4
  import { programContext } from './internals.js';
5
5
  /**
6
- * A React hook for accessing {@linkcode Program} instance and `exit` function that is used for showing an error (via an error boundary) and exiting.
6
+ * A React hook for accessing {@link Program} instance and `exit` function that is used for showing an error (via an error boundary) and exiting.
7
7
  */ export function useProgram() {
8
8
  let { showBoundary } = useErrorBoundary();
9
9
  let { exit: appExit } = useApp();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/ui/useProgram.ts"],"sourcesContent":["import {useApp} from 'ink';\nimport {useCallback, useContext} from 'react';\nimport {useErrorBoundary} from 'react-error-boundary';\n\nimport {programContext} from './internals.js';\n\n/**\n * A React hook for accessing {@linkcode Program} instance and `exit` function that is used for showing an error (via an error boundary) and exiting.\n */\nexport function useProgram() {\n let {showBoundary} = useErrorBoundary();\n let {exit: appExit} = useApp();\n let {program} = useContext(programContext);\n\n let exit = useCallback(\n (error: unknown) => {\n showBoundary(error);\n appExit(error as Error);\n },\n [appExit, showBoundary],\n );\n\n return {program, exit};\n}\n"],"names":["useApp","useCallback","useContext","useErrorBoundary","programContext","useProgram","showBoundary","exit","appExit","program","error"],"mappings":"AAAA,SAAQA,MAAM,QAAO,MAAM;AAC3B,SAAQC,WAAW,EAAEC,UAAU,QAAO,QAAQ;AAC9C,SAAQC,gBAAgB,QAAO,uBAAuB;AAEtD,SAAQC,cAAc,QAAO,iBAAiB;AAE9C;;CAEC,GACD,OAAO,SAASC;IACd,IAAI,EAACC,YAAY,EAAC,GAAGH;IACrB,IAAI,EAACI,MAAMC,OAAO,EAAC,GAAGR;IACtB,IAAI,EAACS,OAAO,EAAC,GAAGP,WAAWE;IAE3B,IAAIG,OAAON,YACT,CAACS;QACCJ,aAAaI;QACbF,QAAQE;IACV,GACA;QAACF;QAASF;KAAa;IAGzB,OAAO;QAACG;QAASF;IAAI;AACvB"}
1
+ {"version":3,"sources":["../../src/ui/useProgram.ts"],"sourcesContent":["import {useApp} from 'ink';\nimport {useCallback, useContext} from 'react';\nimport {useErrorBoundary} from 'react-error-boundary';\n\nimport {programContext} from './internals.js';\n\n/**\n * A React hook for accessing {@link Program} instance and `exit` function that is used for showing an error (via an error boundary) and exiting.\n */\nexport function useProgram() {\n let {showBoundary} = useErrorBoundary();\n let {exit: appExit} = useApp();\n let {program} = useContext(programContext);\n\n let exit = useCallback(\n (error: unknown) => {\n showBoundary(error);\n appExit(error as Error);\n },\n [appExit, showBoundary],\n );\n\n return {program, exit};\n}\n"],"names":["useApp","useCallback","useContext","useErrorBoundary","programContext","useProgram","showBoundary","exit","appExit","program","error"],"mappings":"AAAA,SAAQA,MAAM,QAAO,MAAM;AAC3B,SAAQC,WAAW,EAAEC,UAAU,QAAO,QAAQ;AAC9C,SAAQC,gBAAgB,QAAO,uBAAuB;AAEtD,SAAQC,cAAc,QAAO,iBAAiB;AAE9C;;CAEC,GACD,OAAO,SAASC;IACd,IAAI,EAACC,YAAY,EAAC,GAAGH;IACrB,IAAI,EAACI,MAAMC,OAAO,EAAC,GAAGR;IACtB,IAAI,EAACS,OAAO,EAAC,GAAGP,WAAWE;IAE3B,IAAIG,OAAON,YACT,CAACS;QACCJ,aAAaI;QACbF,QAAQE;IACV,GACA;QAACF;QAASF;KAAa;IAGzB,OAAO;QAACG;QAASF;IAAI;AACvB"}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@jakubmazanec/cli",
3
- "version": "0.1.0-c5ebb6f",
3
+ "version": "0.1.0-next.336fe21",
4
4
  "description": "TypeScript-first library for building CLIs.",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "https://github.com/jakubmazanec/js-tools",
7
+ "url": "git@github.com:jakubmazanec/js-tools.git",
8
8
  "directory": "packages/cli"
9
9
  },
10
10
  "license": "LGPL-3.0-only",
@@ -27,13 +27,14 @@
27
27
  "typecheck": "tsc --project tsconfig.typecheck.json"
28
28
  },
29
29
  "dependencies": {
30
- "@jakubmazanec/args": "1.0.1-c5ebb6f",
31
- "@jakubmazanec/error": "2.0.1-c5ebb6f",
32
- "@jakubmazanec/ts-utils": "2.1.0-c5ebb6f",
33
- "@jakubmazanec/zod-utils": "1.0.0-c5ebb6f",
30
+ "@jakubmazanec/args": "1.0.1-next.336fe21",
31
+ "@jakubmazanec/error": "2.0.1-next.336fe21",
32
+ "@jakubmazanec/ts-utils": "2.1.0-next.336fe21",
33
+ "@jakubmazanec/zod-utils": "1.0.0-next.336fe21",
34
34
  "chalk": "^5.2.0",
35
35
  "lodash": "^4.17.21",
36
- "react-error-boundary": "^4.0.7"
36
+ "react-error-boundary": "^4.0.7",
37
+ "zod": "^3.14.4"
37
38
  },
38
39
  "devDependencies": {
39
40
  "@jest/globals": "^29.5.0",
@@ -12,7 +12,7 @@ import {type Program} from './Program.js';
12
12
  // import {type ProgramCommand} from './ProgramCommand.js';
13
13
 
14
14
  /**
15
- * Command encapsulates piece of a CLI's logic and is executed when {@linkcode Program} instance parses that command from the command line arguments.
15
+ * Command encapsulates piece of a CLI's logic and is executed when {@link Program} instance parses that command from the command line arguments.
16
16
  */
17
17
  export class Command<
18
18
  A extends string,
@@ -21,7 +21,6 @@ export class Command<
21
21
  D extends boolean = false,
22
22
  E extends boolean = false,
23
23
  > {
24
- /* implements ProgramCommand*/
25
24
  readonly name: A;
26
25
  readonly description?: string;
27
26
  readonly parameters: B extends ParametersConfig ? B : null;
@@ -104,7 +103,7 @@ export class Command<
104
103
  }
105
104
 
106
105
  /**
107
- * Executes command action with provided parsed arguments and {@linkcode Program} instance.
106
+ * Executes command action with provided parsed arguments and {@link Program} instance.
108
107
  */
109
108
  async run(
110
109
  parsedArgs: Arguments<{
@@ -1,7 +1,7 @@
1
1
  import {type OptionsConfig, type ParametersConfig} from '@jakubmazanec/args';
2
2
 
3
3
  /**
4
- * New command options.
4
+ * {@link Command.constructor | Command constructor} options parameter.
5
5
  */
6
6
  export type CommandOptions<
7
7
  B extends ParametersConfig,
@@ -1,5 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any -- needed */
2
2
  import {type ParametersConfig, type Argv, type OptionsConfig} from '@jakubmazanec/args';
3
+ import {type ComponentType} from 'react';
4
+ import {type FallbackProps} from 'react-error-boundary';
3
5
 
4
6
  import {type Command} from './Command.js';
5
7
  import {ProgramError} from './ProgramError.js';
@@ -9,7 +11,7 @@ import {Wrapper} from '../ui/internals.js';
9
11
  import {Failure, Help} from '../ui.js';
10
12
 
11
13
  /**
12
- * Program manages and executes {@linkcode Command} instances attached to it based on parsing command line arguments.
14
+ * Program manages and executes {@link Command} instances attached to it based on parsing command line arguments.
13
15
  */
14
16
  export class Program {
15
17
  /** Program name. */
@@ -37,7 +39,10 @@ export class Program {
37
39
  stdout: process.stdout,
38
40
  };
39
41
 
40
- constructor({name, version, description, bin}: ProgramOptions) {
42
+ /** Custom error boundary fallback component */
43
+ ErrorBoundaryFallbackComponent: ComponentType<FallbackProps>;
44
+
45
+ constructor({name, version, description, bin, ErrorBoundaryFallbackComponent}: ProgramOptions) {
41
46
  if (name) {
42
47
  this.name = name;
43
48
  }
@@ -53,6 +58,12 @@ export class Program {
53
58
  if (bin) {
54
59
  this.bin = bin;
55
60
  }
61
+
62
+ if (ErrorBoundaryFallbackComponent) {
63
+ this.ErrorBoundaryFallbackComponent = ErrorBoundaryFallbackComponent;
64
+ } else {
65
+ this.ErrorBoundaryFallbackComponent = Failure;
66
+ }
56
67
  }
57
68
 
58
69
  /**
@@ -70,6 +81,8 @@ export class Program {
70
81
 
71
82
  /**
72
83
  * Renders a React element using Ink and outputs to the configured streams.
84
+ *
85
+ * @param element The React element to render.
73
86
  */
74
87
  async renderElement(element: React.ReactElement): Promise<void> {
75
88
  if (this.isRendering) {
@@ -92,7 +105,9 @@ export class Program {
92
105
  });
93
106
  } catch (error: unknown) {
94
107
  if (error instanceof Error) {
95
- await this.renderElement(createElement(Failure, {error}));
108
+ await this.renderElement(
109
+ createElement(this.ErrorBoundaryFallbackComponent, {error, resetErrorBoundary: () => {}}),
110
+ );
96
111
  }
97
112
  } finally {
98
113
  this.isRendering = false;
@@ -117,7 +132,12 @@ export class Program {
117
132
  let {createElement} = await import('react');
118
133
 
119
134
  for (let error of parsedArgs.errors) {
120
- await this.renderElement(createElement(Failure, {error}));
135
+ await this.renderElement(
136
+ createElement(this.ErrorBoundaryFallbackComponent, {
137
+ error,
138
+ resetErrorBoundary: () => {},
139
+ }),
140
+ );
121
141
  }
122
142
  } else {
123
143
  try {
@@ -126,7 +146,12 @@ export class Program {
126
146
  let {createElement} = await import('react');
127
147
 
128
148
  if (error instanceof Error) {
129
- await this.renderElement(createElement(Failure, {error}));
149
+ await this.renderElement(
150
+ createElement(this.ErrorBoundaryFallbackComponent, {
151
+ error,
152
+ resetErrorBoundary: () => {},
153
+ }),
154
+ );
130
155
  }
131
156
  }
132
157
 
@@ -1,5 +1,8 @@
1
+ import {type ComponentType} from 'react';
2
+ import {type FallbackProps} from 'react-error-boundary';
3
+
1
4
  /**
2
- * Options for creating new {@linkcode Program} instance.
5
+ * {@link Project.constructor | Program constructor} options parameter.
3
6
  */
4
7
  export type ProgramOptions = {
5
8
  /** Program name. */
@@ -13,4 +16,7 @@ export type ProgramOptions = {
13
16
 
14
17
  /** Program executable name. */
15
18
  bin?: string | undefined;
19
+
20
+ /** Custom error boundary fallback component */
21
+ ErrorBoundaryFallbackComponent?: ComponentType<FallbackProps>;
16
22
  };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * {@linkcode Program} streams;
2
+ * {@link Program} streams;
3
3
  */
4
4
  export type ProgramStreams = {
5
5
  stderr: NodeJS.WriteStream;
@@ -7,10 +7,10 @@ import {Header} from './Header.js';
7
7
  import * as icons from './icons.js';
8
8
 
9
9
  /**
10
- * {@linkcode Failure} component props.
10
+ * {@link Failure} component props.
11
11
  */
12
12
  export type FailureProps = BoxProps & {
13
- /** Error that describes a failure state. Function {@linkcode getErrorDetail} is used for getting all the information from the error; you can use it to implement own retrieval logic and pass the result as `errorDetail` prop. */
13
+ /** Error that describes a failure state. Function {@link getErrorDetail} is used for getting all the information from the error; you can use it to implement own retrieval logic and pass the result as `errorDetail` prop. */
14
14
  error: Error;
15
15
 
16
16
  /** Information about the failure state. Overrides information retireved from `error` prop. */
package/src/ui/Header.tsx CHANGED
@@ -4,7 +4,7 @@ import {type PropsWithChildren} from 'react';
4
4
  import {Spinner} from './Spinner.js';
5
5
 
6
6
  /**
7
- * {@linkcode Header} component props.
7
+ * {@link Header} component props.
8
8
  */
9
9
  export type HeaderProps = BoxProps & {
10
10
  /** Icon (recommended is 1 character) to render before the header itself. */
package/src/ui/Help.tsx CHANGED
@@ -7,10 +7,10 @@ import * as icons from './icons.js';
7
7
  import {type Program} from '../program.js';
8
8
 
9
9
  /**
10
- * {@linkcode Help} component props.
10
+ * {@link Help} component props.
11
11
  */
12
12
  export type HelpProps = BoxProps & {
13
- /** {@linkcode Program} instance. */
13
+ /** {@link Program} instance. */
14
14
  program: Program;
15
15
  };
16
16
 
@@ -1,6 +1,6 @@
1
1
  import {Box, type BoxProps, Text, useInput, type Key} from 'ink';
2
2
  import _ from 'lodash';
3
- import {useState, useEffect, useRef, useCallback} from 'react';
3
+ import {useState, useEffect, useRef} from 'react';
4
4
 
5
5
  import {type SelectInputItem} from './SelectInputItem.js';
6
6
  import * as colors from '../colors.js';
@@ -8,7 +8,7 @@ import * as icons from '../icons.js';
8
8
  import {rotate} from '../internals.js';
9
9
 
10
10
  /**
11
- * Select input component props.
11
+ * {@linkcode SelectInput} component props.
12
12
  */
13
13
  type SelectInputProps = BoxProps & {
14
14
  /** Text to display as a label. */
@@ -17,7 +17,7 @@ type SelectInputProps = BoxProps & {
17
17
  /** Items to display in a list. */
18
18
  items: SelectInputItem[];
19
19
 
20
- /** Should component listen to Listen to user's input? Useful in case there are multiple input components at the same time and input must be "routed" to a specific component. */
20
+ /** Should component listen to user's input? Useful in case there are multiple input components at the same time and input must be handled only by a specific component. */
21
21
  focus?: boolean | undefined;
22
22
 
23
23
  /** Index of initially selected item. */
@@ -44,17 +44,16 @@ export function SelectInput({
44
44
  items = [],
45
45
  focus = true,
46
46
  initialIndex = 0,
47
- limit: customLimit,
47
+ limit: userDefinedLimit,
48
48
  onSelect,
49
49
  onHighlight,
50
- marginTop = 0,
51
50
  isComplete,
52
51
  ...boxProps
53
52
  }: SelectInputProps): JSX.Element {
54
- let [rotateIndex, setRotateIndex] = useState(0);
53
+ let [rotatedIndex, setRotatedIndex] = useState(0);
55
54
  let [selectedIndex, setSelectedIndex] = useState(initialIndex);
56
- let hasLimit = typeof customLimit === 'number' && items.length > customLimit;
57
- let limit = hasLimit ? Math.min(customLimit as number, items.length) : items.length;
55
+ let hasLimit = typeof userDefinedLimit === 'number' && items.length > userDefinedLimit;
56
+ let limit = hasLimit ? Math.min(userDefinedLimit as number, items.length) : items.length;
58
57
  let previousItems = useRef<SelectInputItem[]>(items);
59
58
 
60
59
  useEffect(() => {
@@ -64,26 +63,26 @@ export function SelectInput({
64
63
  items.map((item) => item.value),
65
64
  )
66
65
  ) {
67
- setRotateIndex(0);
66
+ setRotatedIndex(0);
68
67
  setSelectedIndex(0);
69
68
  }
70
69
 
71
70
  previousItems.current = items;
72
71
  }, [items]);
73
72
 
74
- let inputHandler = useCallback(
73
+ useInput(
75
74
  (input: string, key: Key) => {
76
75
  if (input === 'k' || key.upArrow) {
77
76
  let lastIndex = (hasLimit ? limit : items.length) - 1;
78
- let atFirstIndex = selectedIndex === 0;
77
+ let isAtFirstIndex = selectedIndex === 0;
79
78
  let nextIndex = hasLimit ? selectedIndex : lastIndex;
80
- let nextRotateIndex = atFirstIndex ? rotateIndex + 1 : rotateIndex;
81
- let nextSelectedIndex = atFirstIndex ? nextIndex : selectedIndex - 1;
79
+ let nextRotatedIndex = isAtFirstIndex ? rotatedIndex + 1 : rotatedIndex;
80
+ let nextSelectedIndex = isAtFirstIndex ? nextIndex : selectedIndex - 1;
82
81
 
83
- setRotateIndex(nextRotateIndex);
82
+ setRotatedIndex(nextRotatedIndex);
84
83
  setSelectedIndex(nextSelectedIndex);
85
84
 
86
- let slicedItems = hasLimit ? rotate(items, nextRotateIndex).slice(0, limit) : items;
85
+ let slicedItems = hasLimit ? rotate(items, nextRotatedIndex).slice(0, limit) : items;
87
86
 
88
87
  if (typeof onHighlight === 'function') {
89
88
  onHighlight(slicedItems[nextSelectedIndex] as SelectInputItem);
@@ -93,13 +92,13 @@ export function SelectInput({
93
92
  if (input === 'j' || key.downArrow) {
94
93
  let isAtLastIndex = selectedIndex === (hasLimit ? limit : items.length) - 1;
95
94
  let nextIndex = hasLimit ? selectedIndex : 0;
96
- let nextRotateIndex = isAtLastIndex ? rotateIndex - 1 : rotateIndex;
95
+ let nextRotatedIndex = isAtLastIndex ? rotatedIndex - 1 : rotatedIndex;
97
96
  let nextSelectedIndex = isAtLastIndex ? nextIndex : selectedIndex + 1;
98
97
 
99
- setRotateIndex(nextRotateIndex);
98
+ setRotatedIndex(nextRotatedIndex);
100
99
  setSelectedIndex(nextSelectedIndex);
101
100
 
102
- let slicedItems = hasLimit ? rotate(items, nextRotateIndex).slice(0, limit) : items;
101
+ let slicedItems = hasLimit ? rotate(items, nextRotatedIndex).slice(0, limit) : items;
103
102
 
104
103
  if (typeof onHighlight === 'function') {
105
104
  onHighlight(slicedItems[nextSelectedIndex] as SelectInputItem);
@@ -107,20 +106,18 @@ export function SelectInput({
107
106
  }
108
107
 
109
108
  if (key.return) {
110
- let slicedItems = hasLimit ? rotate(items, rotateIndex).slice(0, limit) : items;
109
+ let slicedItems = hasLimit ? rotate(items, rotatedIndex).slice(0, limit) : items;
111
110
 
112
111
  if (typeof onSelect === 'function') {
113
112
  onSelect(slicedItems[selectedIndex] as SelectInputItem);
114
113
  }
115
114
  }
116
115
  },
117
- [hasLimit, limit, rotateIndex, selectedIndex, items, onSelect, onHighlight],
116
+ {isActive: focus},
118
117
  );
119
118
 
120
- useInput(inputHandler, {isActive: focus});
121
-
122
- let visibleItem: SelectInputItem[] = hasLimit
123
- ? rotate(items, rotateIndex).slice(0, limit)
119
+ let visibleItems: SelectInputItem[] = hasLimit
120
+ ? rotate(items, rotatedIndex).slice(0, limit)
124
121
  : items;
125
122
 
126
123
  return (
@@ -139,7 +136,7 @@ export function SelectInput({
139
136
  </Text>
140
137
  ) : (
141
138
  <Box flexDirection="column">
142
- {visibleItem.map((item, index) => {
139
+ {visibleItems.map((item, index) => {
143
140
  let isSelected = index === selectedIndex;
144
141
 
145
142
  return (
@@ -151,9 +148,7 @@ export function SelectInput({
151
148
  <Text color={colors.unselected}>{icons.unselectedCircle}</Text>
152
149
  )}
153
150
  </Box>
154
- <Text
155
- color={isSelected ? colors.highlighted : colors.muted}
156
- >{`${item.label}`}</Text>
151
+ <Text color={isSelected ? colors.highlighted : colors.muted}>{item.label}</Text>
157
152
  </Box>
158
153
  );
159
154
  })}
@@ -20,7 +20,7 @@ export type TextInputProps = BoxProps & {
20
20
  /** Value. */
21
21
  value: string;
22
22
 
23
- /** Should component listen to Listen to user's input? Useful in case there are multiple input components at the same time and input must be "routed" to a specific component. */
23
+ /** Should component listen to Listen to user's input? Useful in case there are multiple input components at the same time and input must be handled only by a specific component. */
24
24
  focus?: boolean | undefined;
25
25
 
26
26
  /** Replace all chars and mask the value. Useful for password inputs. */
@@ -87,7 +87,6 @@ export function TextInput({
87
87
  let renderedValue = value;
88
88
  let renderedPlaceholder = placeholder ? chalk[colors.muted](placeholder) : undefined;
89
89
 
90
- // fake mouse cursor; dealing with with actual cursor and ANSI escape codes is inconvinient
91
90
  if (showCursor && focus) {
92
91
  renderedPlaceholder =
93
92
  placeholder.length > 0
@@ -96,16 +95,12 @@ export function TextInput({
96
95
 
97
96
  renderedValue = value.length > 0 ? '' : chalk.inverse(' ');
98
97
 
99
- let i = 0;
100
-
101
- for (let char of value) {
102
- if (i >= state.cursorOffset && i <= state.cursorOffset) {
103
- renderedValue += chalk.inverse(char);
98
+ for (let [i, character] of Object.entries(value)) {
99
+ if (Number(i) >= state.cursorOffset && Number(i) <= state.cursorOffset) {
100
+ renderedValue += chalk.inverse(character);
104
101
  } else {
105
- renderedValue += char;
102
+ renderedValue += character;
106
103
  }
107
-
108
- i += 1;
109
104
  }
110
105
 
111
106
  if (value.length > 0 && state.cursorOffset === value.length) {
@@ -1,4 +1,6 @@
1
1
  import {ValidationError, ParsingError} from '@jakubmazanec/args';
2
+ import {zodIssuesToStrings} from '@jakubmazanec/zod-utils';
3
+ import {ZodError} from 'zod';
2
4
 
3
5
  import {type ErrorDetail} from './ErrorDetail.js';
4
6
 
@@ -44,6 +46,15 @@ export function getErrorDetail(error: unknown): ErrorDetail {
44
46
  return errorDetail;
45
47
  }
46
48
 
49
+ if (error instanceof ZodError) {
50
+ let errorDetail: ErrorDetail = {
51
+ label: 'Error',
52
+ description: zodIssuesToStrings(error.issues).join('\n'),
53
+ };
54
+
55
+ return errorDetail;
56
+ }
57
+
47
58
  if (error instanceof Error) {
48
59
  let errorDetail: ErrorDetail = {
49
60
  label: 'Error',
@@ -7,14 +7,14 @@ import {type Program} from '../../program.js';
7
7
  import {Failure} from '../Failure.js';
8
8
 
9
9
  /**
10
- * {@linkcode Wrapper} component props.
10
+ * {@link Wrapper} component props.
11
11
  */
12
12
  export type WrapperProps = {
13
13
  program: Program;
14
14
  };
15
15
 
16
16
  /**
17
- * A component that wraps anything {@linkcode Program} tries to render with an error boundary and all neccessary providers.
17
+ * A component that wraps anything {@link Program} tries to render with an error boundary and all neccessary providers.
18
18
  */
19
19
  export const Wrapper: FC<PropsWithChildren<WrapperProps>> = ({program, children}) => {
20
20
  let programContextValue = useMemo(
@@ -7,6 +7,6 @@ export type ProgramContext = {
7
7
  };
8
8
 
9
9
  /**
10
- * A React context that stores {@linkcode Program} instance.
10
+ * A React context that stores {@link Program} instance.
11
11
  */
12
12
  export const programContext = React.createContext<ProgramContext>({} as ProgramContext);