@shopify/cli-kit 3.31.0 → 3.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/README.md +1 -1
  2. package/dist/constants.d.ts +1 -0
  3. package/dist/constants.js +1 -0
  4. package/dist/constants.js.map +1 -1
  5. package/dist/environment/local.d.ts +1 -0
  6. package/dist/environment/local.js +4 -1
  7. package/dist/environment/local.js.map +1 -1
  8. package/dist/file.d.ts +10 -3
  9. package/dist/file.js +36 -36
  10. package/dist/file.js.map +1 -1
  11. package/dist/index.d.ts +0 -3
  12. package/dist/index.js +0 -3
  13. package/dist/index.js.map +1 -1
  14. package/dist/metadata.d.ts +1 -1
  15. package/dist/metadata.js.map +1 -1
  16. package/dist/monorail.d.ts +1 -1
  17. package/dist/monorail.js.map +1 -1
  18. package/dist/os.d.ts +4 -3
  19. package/dist/os.js +3 -4
  20. package/dist/os.js.map +1 -1
  21. package/dist/output.d.ts +2 -2
  22. package/dist/output.js +1 -1
  23. package/dist/output.js.map +1 -1
  24. package/dist/path.d.ts +5 -5
  25. package/dist/path.js +6 -7
  26. package/dist/path.js.map +1 -1
  27. package/dist/plugins.d.ts +1 -1
  28. package/dist/plugins.js.map +1 -1
  29. package/dist/{json.d.ts → private/common/json.d.ts} +0 -0
  30. package/dist/{json.js → private/common/json.js} +0 -0
  31. package/dist/private/common/json.js.map +1 -0
  32. package/dist/private/node/analytics.d.ts +27 -0
  33. package/dist/private/node/analytics.js +57 -0
  34. package/dist/private/node/analytics.js.map +1 -0
  35. package/dist/private/node/ui/components/Alert.test.js +5 -5
  36. package/dist/private/node/ui/components/Alert.test.js.map +1 -1
  37. package/dist/private/node/ui/components/Banner.js +5 -5
  38. package/dist/private/node/ui/components/Banner.js.map +1 -1
  39. package/dist/private/node/ui/components/Banner.test.js +11 -11
  40. package/dist/private/node/ui/components/Banner.test.js.map +1 -1
  41. package/dist/private/node/ui/components/Command.test.js +3 -3
  42. package/dist/private/node/ui/components/Command.test.js.map +1 -1
  43. package/dist/private/node/ui/components/ConcurrentOutput.d.ts +2 -2
  44. package/dist/private/node/ui/components/ConcurrentOutput.js +1 -1
  45. package/dist/private/node/ui/components/ConcurrentOutput.js.map +1 -1
  46. package/dist/private/node/ui/components/ConcurrentOutput.test.js +3 -3
  47. package/dist/private/node/ui/components/ConcurrentOutput.test.js.map +1 -1
  48. package/dist/private/node/ui/components/FatalError.test.js +9 -9
  49. package/dist/private/node/ui/components/FatalError.test.js.map +1 -1
  50. package/dist/private/node/ui/components/FilePath.test.js +3 -3
  51. package/dist/private/node/ui/components/FilePath.test.js.map +1 -1
  52. package/dist/private/node/ui/components/Link.test.js +5 -5
  53. package/dist/private/node/ui/components/Link.test.js.map +1 -1
  54. package/dist/private/node/ui/components/List.test.js +5 -5
  55. package/dist/private/node/ui/components/List.test.js.map +1 -1
  56. package/dist/private/node/ui/components/SelectInput.d.ts +2 -2
  57. package/dist/private/node/ui/components/SelectInput.js +16 -24
  58. package/dist/private/node/ui/components/SelectInput.js.map +1 -1
  59. package/dist/private/node/ui/components/SelectInput.test.js +62 -32
  60. package/dist/private/node/ui/components/SelectInput.test.js.map +1 -1
  61. package/dist/private/node/ui/components/{Prompt.d.ts → SelectPrompt.d.ts} +4 -3
  62. package/dist/private/node/ui/components/SelectPrompt.js +34 -0
  63. package/dist/private/node/ui/components/SelectPrompt.js.map +1 -0
  64. package/dist/private/node/ui/components/{Prompt.test.d.ts → SelectPrompt.test.d.ts} +0 -0
  65. package/dist/private/node/ui/components/{Prompt.test.js → SelectPrompt.test.js} +7 -6
  66. package/dist/private/node/ui/components/SelectPrompt.test.js.map +1 -0
  67. package/dist/private/node/ui/components/Table.js +1 -1
  68. package/dist/private/node/ui/components/Table.js.map +1 -1
  69. package/dist/private/node/ui/components/Tasks.d.ts +1 -1
  70. package/dist/private/node/ui/components/Tasks.js +3 -3
  71. package/dist/private/node/ui/components/Tasks.js.map +1 -1
  72. package/dist/private/node/ui/components/Tasks.test.js +4 -4
  73. package/dist/private/node/ui/components/Tasks.test.js.map +1 -1
  74. package/dist/private/node/ui/components/TextAnimation.js.map +1 -1
  75. package/dist/private/node/ui/components/TextInput.d.ts +9 -0
  76. package/dist/private/node/ui/components/TextInput.js +74 -0
  77. package/dist/private/node/ui/components/TextInput.js.map +1 -0
  78. package/dist/private/node/ui/components/TextInput.test.d.ts +1 -0
  79. package/dist/private/node/ui/components/TextInput.test.js +139 -0
  80. package/dist/private/node/ui/components/TextInput.test.js.map +1 -0
  81. package/dist/private/node/ui/components/TextPrompt.d.ts +8 -0
  82. package/dist/private/node/ui/components/TextPrompt.js +52 -0
  83. package/dist/private/node/ui/components/TextPrompt.js.map +1 -0
  84. package/dist/private/node/ui/components/TextPrompt.test.d.ts +1 -0
  85. package/dist/private/node/ui/components/TextPrompt.test.js +68 -0
  86. package/dist/private/node/ui/components/TextPrompt.test.js.map +1 -0
  87. package/dist/private/node/ui/components/TokenizedText.test.js +3 -3
  88. package/dist/private/node/ui/components/TokenizedText.test.js.map +1 -1
  89. package/dist/private/node/ui/components/UserInput.test.js +3 -3
  90. package/dist/private/node/ui/components/UserInput.test.js.map +1 -1
  91. package/dist/private/node/ui/hooks/use-layout.d.ts +6 -3
  92. package/dist/private/node/ui/hooks/use-layout.js +17 -8
  93. package/dist/private/node/ui/hooks/use-layout.js.map +1 -1
  94. package/dist/private/node/ui.d.ts +2 -3
  95. package/dist/private/node/ui.js +7 -16
  96. package/dist/private/node/ui.js.map +1 -1
  97. package/dist/public/common/array.js +1 -1
  98. package/dist/public/common/array.js.map +1 -1
  99. package/dist/public/common/collection.js +1 -1
  100. package/dist/public/common/collection.js.map +1 -1
  101. package/dist/public/common/function.js +1 -1
  102. package/dist/public/common/function.js.map +1 -1
  103. package/dist/public/common/lang.js +1 -1
  104. package/dist/public/common/lang.js.map +1 -1
  105. package/dist/public/common/object.js +1 -1
  106. package/dist/public/common/object.js.map +1 -1
  107. package/dist/public/common/url.d.ts +7 -0
  108. package/dist/public/common/url.js +17 -0
  109. package/dist/public/common/url.js.map +1 -0
  110. package/dist/public/node/abort.d.ts +16 -0
  111. package/dist/public/node/abort.js +17 -0
  112. package/dist/public/node/abort.js.map +1 -0
  113. package/dist/public/node/analytics.d.ts +13 -0
  114. package/dist/public/node/analytics.js +82 -0
  115. package/dist/public/node/analytics.js.map +1 -0
  116. package/dist/public/node/archiver.js +1 -1
  117. package/dist/public/node/archiver.js.map +1 -1
  118. package/dist/public/node/base-command.js.map +1 -1
  119. package/dist/public/node/error-handler.js +3 -2
  120. package/dist/public/node/error-handler.js.map +1 -1
  121. package/dist/public/node/framework.js +1 -1
  122. package/dist/public/node/framework.js.map +1 -1
  123. package/dist/public/node/hooks/postrun.js +2 -2
  124. package/dist/public/node/hooks/postrun.js.map +1 -1
  125. package/dist/public/node/hooks/prerun.js +2 -2
  126. package/dist/public/node/hooks/prerun.js.map +1 -1
  127. package/dist/public/node/node-package-manager.d.ts +3 -3
  128. package/dist/public/node/node-package-manager.js +1 -1
  129. package/dist/public/node/node-package-manager.js.map +1 -1
  130. package/dist/public/node/presets.d.ts +1 -1
  131. package/dist/public/node/presets.js.map +1 -1
  132. package/dist/public/node/ruby.d.ts +2 -2
  133. package/dist/public/node/ruby.js +2 -2
  134. package/dist/public/node/ruby.js.map +1 -1
  135. package/dist/public/node/ui.d.ts +12 -17
  136. package/dist/public/node/ui.js +25 -29
  137. package/dist/public/node/ui.js.map +1 -1
  138. package/dist/session/authorize.js +1 -1
  139. package/dist/session/authorize.js.map +1 -1
  140. package/dist/session/redirect-listener.js +6 -3
  141. package/dist/session/redirect-listener.js.map +1 -1
  142. package/dist/system.d.ts +2 -2
  143. package/dist/system.js.map +1 -1
  144. package/dist/testing/ui.d.ts +1 -1
  145. package/dist/testing/ui.js +1 -1
  146. package/dist/testing/ui.js.map +1 -1
  147. package/dist/tsconfig.tsbuildinfo +1 -1
  148. package/dist/ui/inquirer/autocomplete.d.ts +2 -2
  149. package/dist/ui/inquirer/autocomplete.js.map +1 -1
  150. package/dist/ui/inquirer/input.d.ts +2 -2
  151. package/dist/ui/inquirer/input.js.map +1 -1
  152. package/dist/ui/inquirer/password.d.ts +2 -2
  153. package/dist/ui/inquirer/password.js.map +1 -1
  154. package/dist/ui/inquirer/select.d.ts +2 -2
  155. package/dist/ui/inquirer/select.js +2 -2
  156. package/dist/ui/inquirer/select.js.map +1 -1
  157. package/package.json +30 -28
  158. package/dist/abort.d.ts +0 -1
  159. package/dist/abort.js +0 -2
  160. package/dist/abort.js.map +0 -1
  161. package/dist/analytics.d.ts +0 -44
  162. package/dist/analytics.js +0 -154
  163. package/dist/analytics.js.map +0 -1
  164. package/dist/id.d.ts +0 -6
  165. package/dist/id.js +0 -18
  166. package/dist/id.js.map +0 -1
  167. package/dist/json.js.map +0 -1
  168. package/dist/private/node/ui/components/Prompt.js +0 -23
  169. package/dist/private/node/ui/components/Prompt.js.map +0 -1
  170. package/dist/private/node/ui/components/Prompt.test.js.map +0 -1
@@ -0,0 +1,139 @@
1
+ import { TextInput } from './TextInput.js';
2
+ import { sendInput, waitForChange, waitForInputsToBeReady } from '../../../../testing/ui.js';
3
+ import React, { useState } from 'react';
4
+ import { describe, test, expect, vi } from 'vitest';
5
+ import { render } from 'ink-testing-library';
6
+ const ARROW_LEFT = '\u001B[D';
7
+ const ARROW_RIGHT = '\u001B[C';
8
+ const DELETE = '\u007F';
9
+ describe('TextInput', () => {
10
+ test('default state', () => {
11
+ const { lastFrame } = render(React.createElement(TextInput, { value: "", onChange: () => { } }));
12
+ // inverted space escape sequence
13
+ expect(lastFrame()).toMatchInlineSnapshot('" "');
14
+ });
15
+ test('displays value with cursor', () => {
16
+ const { lastFrame } = render(React.createElement(TextInput, { value: "Hello", onChange: () => { } }));
17
+ // inverted space escape sequence after Hello
18
+ expect(lastFrame()).toMatchInlineSnapshot('"Hello "');
19
+ });
20
+ test('displays placeholder', () => {
21
+ const { lastFrame } = render(React.createElement(TextInput, { value: "", placeholder: "Placeholder", onChange: () => { } }));
22
+ // inverted escape sequence around "P", laceholder after that
23
+ expect(lastFrame()).toMatchInlineSnapshot('"Placeholder"');
24
+ });
25
+ test('moves the cursor with arrows', async () => {
26
+ const renderInstance = render(React.createElement(TextInput, { value: "Hello", onChange: () => { } }));
27
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello "');
28
+ await waitForInputsToBeReady();
29
+ await sendInput(renderInstance, ARROW_LEFT);
30
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
31
+ await sendInput(renderInstance, ARROW_LEFT);
32
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
33
+ await sendInput(renderInstance, ARROW_LEFT);
34
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
35
+ await sendInput(renderInstance, ARROW_LEFT);
36
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
37
+ await sendInput(renderInstance, ARROW_LEFT);
38
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
39
+ // cursor can't go before the first character
40
+ renderInstance.stdin.write(ARROW_LEFT);
41
+ await new Promise((resolve) => setTimeout(resolve, 100));
42
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
43
+ await sendInput(renderInstance, ARROW_RIGHT);
44
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
45
+ await sendInput(renderInstance, ARROW_RIGHT);
46
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
47
+ await sendInput(renderInstance, ARROW_RIGHT);
48
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
49
+ await sendInput(renderInstance, ARROW_RIGHT);
50
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
51
+ await sendInput(renderInstance, ARROW_RIGHT);
52
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello "');
53
+ // cursor can't go after the last character
54
+ renderInstance.stdin.write(ARROW_RIGHT);
55
+ await new Promise((resolve) => setTimeout(resolve, 100));
56
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello "');
57
+ });
58
+ test('moves the cursor when deleting', async () => {
59
+ const StatefulTextInput = () => {
60
+ const [value, setValue] = useState('Hello');
61
+ return React.createElement(TextInput, { value: value, onChange: setValue });
62
+ };
63
+ const renderInstance = render(React.createElement(StatefulTextInput, null));
64
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello "');
65
+ await waitForInputsToBeReady();
66
+ await sendInput(renderInstance, DELETE);
67
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hell "');
68
+ await sendInput(renderInstance, DELETE);
69
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hel "');
70
+ await sendInput(renderInstance, DELETE);
71
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"He "');
72
+ await sendInput(renderInstance, DELETE);
73
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"H "');
74
+ await sendInput(renderInstance, DELETE);
75
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('" "');
76
+ // cannot delete after the value has been cleared
77
+ renderInstance.stdin.write(DELETE);
78
+ await new Promise((resolve) => setTimeout(resolve, 100));
79
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('" "');
80
+ });
81
+ test('accepts input', async () => {
82
+ const StatefulTextInput = () => {
83
+ const [value, setValue] = useState('');
84
+ return React.createElement(TextInput, { value: value, onChange: setValue });
85
+ };
86
+ const renderInstance = render(React.createElement(StatefulTextInput, null));
87
+ await waitForInputsToBeReady();
88
+ await sendInput(renderInstance, 'H');
89
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"H "');
90
+ await sendInput(renderInstance, 'ello');
91
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello "');
92
+ });
93
+ test('onChange', async () => {
94
+ const onChange = vi.fn();
95
+ const renderInstance = render(React.createElement(TextInput, { value: "", onChange: onChange }));
96
+ await waitForInputsToBeReady();
97
+ await sendInput(renderInstance, 'X');
98
+ expect(onChange).toHaveBeenCalledWith('X');
99
+ });
100
+ test('deletes at the beginning and in the middle of text', async () => {
101
+ const StatefulTextInput = () => {
102
+ const [value, setValue] = useState('');
103
+ return React.createElement(TextInput, { value: value, onChange: setValue });
104
+ };
105
+ const renderInstance = render(React.createElement(StatefulTextInput, null));
106
+ await waitForInputsToBeReady();
107
+ await sendInput(renderInstance, 'T');
108
+ await sendInput(renderInstance, 'e');
109
+ await sendInput(renderInstance, 's');
110
+ await sendInput(renderInstance, 't');
111
+ await sendInput(renderInstance, ARROW_LEFT);
112
+ await sendInput(renderInstance, DELETE);
113
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Tet"');
114
+ await sendInput(renderInstance, ARROW_LEFT);
115
+ await sendInput(renderInstance, ARROW_LEFT);
116
+ renderInstance.stdin.write(DELETE);
117
+ await new Promise((resolve) => setTimeout(resolve, 100));
118
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Tet"');
119
+ });
120
+ test('adjusts cursor when text is shorter than last value', async () => {
121
+ let resetValue = () => { };
122
+ const StatefulTextInput = () => {
123
+ const [value, setValue] = useState('');
124
+ resetValue = () => setValue('');
125
+ return React.createElement(TextInput, { value: value, onChange: setValue });
126
+ };
127
+ const renderInstance = render(React.createElement(StatefulTextInput, null));
128
+ await waitForInputsToBeReady();
129
+ await sendInput(renderInstance, 'A');
130
+ await sendInput(renderInstance, 'B');
131
+ await waitForChange(resetValue, renderInstance.lastFrame);
132
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('" "');
133
+ await sendInput(renderInstance, 'A');
134
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"A "');
135
+ await sendInput(renderInstance, 'B');
136
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"AB "');
137
+ });
138
+ });
139
+ //# sourceMappingURL=TextInput.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TextInput.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/TextInput.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAC,SAAS,EAAE,aAAa,EAAE,sBAAsB,EAAC,MAAM,2BAA2B,CAAA;AAC1F,OAAO,KAAK,EAAE,EAAC,QAAQ,EAAC,MAAM,OAAO,CAAA;AACrC,OAAO,EAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAC,MAAM,QAAQ,CAAA;AACjD,OAAO,EAAC,MAAM,EAAC,MAAM,qBAAqB,CAAA;AAE1C,MAAM,UAAU,GAAG,UAAU,CAAA;AAC7B,MAAM,WAAW,GAAG,UAAU,CAAA;AAC9B,MAAM,MAAM,GAAG,QAAQ,CAAA;AAEvB,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE;QACzB,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,SAAS,IAAC,KAAK,EAAC,EAAE,EAAC,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC,GAAI,CAAC,CAAA;QAEtE,iCAAiC;QACjC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,wBAAwB,CAAC,CAAA;IACrE,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACtC,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,SAAS,IAAC,KAAK,EAAC,OAAO,EAAC,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC,GAAI,CAAC,CAAA;QAE3E,6CAA6C;QAC7C,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,6BAA6B,CAAC,CAAA;IAC1E,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAChC,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,SAAS,IAAC,KAAK,EAAC,EAAE,EAAC,WAAW,EAAC,aAAa,EAAC,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC,GAAI,CAAC,CAAA;QAEhG,6DAA6D;QAC7D,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,2CAA2C,CAAC,CAAA;IACxF,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;QAC9C,MAAM,cAAc,GAAG,MAAM,CAAC,oBAAC,SAAS,IAAC,KAAK,EAAC,OAAO,EAAC,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC,GAAI,CAAC,CAAA;QAE9E,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,6BAA6B,CAAC,CAAA;QAEvF,MAAM,sBAAsB,EAAE,CAAA;QAC9B,MAAM,SAAS,CAAC,cAAc,EAAE,UAAU,CAAC,CAAA;QAC3C,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,4BAA4B,CAAC,CAAA;QACtF,MAAM,SAAS,CAAC,cAAc,EAAE,UAAU,CAAC,CAAA;QAC3C,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,4BAA4B,CAAC,CAAA;QACtF,MAAM,SAAS,CAAC,cAAc,EAAE,UAAU,CAAC,CAAA;QAC3C,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,4BAA4B,CAAC,CAAA;QACtF,MAAM,SAAS,CAAC,cAAc,EAAE,UAAU,CAAC,CAAA;QAC3C,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,4BAA4B,CAAC,CAAA;QACtF,MAAM,SAAS,CAAC,cAAc,EAAE,UAAU,CAAC,CAAA;QAC3C,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,4BAA4B,CAAC,CAAA;QACtF,6CAA6C;QAC7C,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QACtC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAA;QACxD,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,4BAA4B,CAAC,CAAA;QAEtF,MAAM,SAAS,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;QAC5C,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,4BAA4B,CAAC,CAAA;QACtF,MAAM,SAAS,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;QAC5C,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,4BAA4B,CAAC,CAAA;QACtF,MAAM,SAAS,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;QAC5C,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,4BAA4B,CAAC,CAAA;QACtF,MAAM,SAAS,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;QAC5C,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,4BAA4B,CAAC,CAAA;QACtF,MAAM,SAAS,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;QAC5C,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,6BAA6B,CAAC,CAAA;QACvF,2CAA2C;QAC3C,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;QACvC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAA;QACxD,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,6BAA6B,CAAC,CAAA;IACzF,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;QAChD,MAAM,iBAAiB,GAAG,GAAG,EAAE;YAC7B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAA;YAE3C,OAAO,oBAAC,SAAS,IAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,GAAI,CAAA;QACxD,CAAC,CAAA;QAED,MAAM,cAAc,GAAG,MAAM,CAAC,oBAAC,iBAAiB,OAAG,CAAC,CAAA;QAEpD,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,6BAA6B,CAAC,CAAA;QAEvF,MAAM,sBAAsB,EAAE,CAAA;QAC9B,MAAM,SAAS,CAAC,cAAc,EAAE,MAAM,CAAC,CAAA;QACvC,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,4BAA4B,CAAC,CAAA;QACtF,MAAM,SAAS,CAAC,cAAc,EAAE,MAAM,CAAC,CAAA;QACvC,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,2BAA2B,CAAC,CAAA;QACrF,MAAM,SAAS,CAAC,cAAc,EAAE,MAAM,CAAC,CAAA;QACvC,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,0BAA0B,CAAC,CAAA;QACpF,MAAM,SAAS,CAAC,cAAc,EAAE,MAAM,CAAC,CAAA;QACvC,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,yBAAyB,CAAC,CAAA;QACnF,MAAM,SAAS,CAAC,cAAc,EAAE,MAAM,CAAC,CAAA;QACvC,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,wBAAwB,CAAC,CAAA;QAClF,iDAAiD;QACjD,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QAClC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAA;QACxD,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,wBAAwB,CAAC,CAAA;IACpF,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,eAAe,EAAE,KAAK,IAAI,EAAE;QAC/B,MAAM,iBAAiB,GAAG,GAAG,EAAE;YAC7B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAA;YAEtC,OAAO,oBAAC,SAAS,IAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,GAAI,CAAA;QACxD,CAAC,CAAA;QAED,MAAM,cAAc,GAAG,MAAM,CAAC,oBAAC,iBAAiB,OAAG,CAAC,CAAA;QAEpD,MAAM,sBAAsB,EAAE,CAAA;QAC9B,MAAM,SAAS,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;QACpC,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,yBAAyB,CAAC,CAAA;QACnF,MAAM,SAAS,CAAC,cAAc,EAAE,MAAM,CAAC,CAAA;QACvC,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,6BAA6B,CAAC,CAAA;IACzF,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;QAC1B,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QAExB,MAAM,cAAc,GAAG,MAAM,CAAC,oBAAC,SAAS,IAAC,KAAK,EAAC,EAAE,EAAC,QAAQ,EAAE,QAAQ,GAAI,CAAC,CAAA;QAEzE,MAAM,sBAAsB,EAAE,CAAA;QAC9B,MAAM,SAAS,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;QAEpC,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAA;IAC5C,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QACpE,MAAM,iBAAiB,GAAG,GAAG,EAAE;YAC7B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAA;YAEtC,OAAO,oBAAC,SAAS,IAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,GAAI,CAAA;QACxD,CAAC,CAAA;QAED,MAAM,cAAc,GAAG,MAAM,CAAC,oBAAC,iBAAiB,OAAG,CAAC,CAAA;QAEpD,MAAM,sBAAsB,EAAE,CAAA;QAC9B,MAAM,SAAS,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;QACpC,MAAM,SAAS,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;QACpC,MAAM,SAAS,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;QACpC,MAAM,SAAS,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;QACpC,MAAM,SAAS,CAAC,cAAc,EAAE,UAAU,CAAC,CAAA;QAC3C,MAAM,SAAS,CAAC,cAAc,EAAE,MAAM,CAAC,CAAA;QACvC,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,0BAA0B,CAAC,CAAA;QACpF,MAAM,SAAS,CAAC,cAAc,EAAE,UAAU,CAAC,CAAA;QAC3C,MAAM,SAAS,CAAC,cAAc,EAAE,UAAU,CAAC,CAAA;QAC3C,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QAClC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAA;QACxD,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,0BAA0B,CAAC,CAAA;IACtF,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACrE,IAAI,UAAU,GAAG,GAAG,EAAE,GAAE,CAAC,CAAA;QAEzB,MAAM,iBAAiB,GAAG,GAAG,EAAE;YAC7B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAA;YACtC,UAAU,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;YAE/B,OAAO,oBAAC,SAAS,IAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,GAAI,CAAA;QACxD,CAAC,CAAA;QAED,MAAM,cAAc,GAAG,MAAM,CAAC,oBAAC,iBAAiB,OAAG,CAAC,CAAA;QAEpD,MAAM,sBAAsB,EAAE,CAAA;QAC9B,MAAM,SAAS,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;QACpC,MAAM,SAAS,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;QAEpC,MAAM,aAAa,CAAC,UAAU,EAAE,cAAc,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,wBAAwB,CAAC,CAAA;QAClF,MAAM,SAAS,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;QACpC,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,yBAAyB,CAAC,CAAA;QACnF,MAAM,SAAS,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;QACpC,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,0BAA0B,CAAC,CAAA;IACtF,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import {TextInput} from './TextInput.js'\nimport {sendInput, waitForChange, waitForInputsToBeReady} from '../../../../testing/ui.js'\nimport React, {useState} from 'react'\nimport {describe, test, expect, vi} from 'vitest'\nimport {render} from 'ink-testing-library'\n\nconst ARROW_LEFT = '\\u001B[D'\nconst ARROW_RIGHT = '\\u001B[C'\nconst DELETE = '\\u007F'\n\ndescribe('TextInput', () => {\n test('default state', () => {\n const {lastFrame} = render(<TextInput value=\"\" onChange={() => {}} />)\n\n // inverted space escape sequence\n expect(lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7m \u001b[27m\u001b[39m\"')\n })\n\n test('displays value with cursor', () => {\n const {lastFrame} = render(<TextInput value=\"Hello\" onChange={() => {}} />)\n\n // inverted space escape sequence after Hello\n expect(lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n })\n\n test('displays placeholder', () => {\n const {lastFrame} = render(<TextInput value=\"\" placeholder=\"Placeholder\" onChange={() => {}} />)\n\n // inverted escape sequence around \"P\", laceholder after that\n expect(lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7mP\u001b[27m\u001b[2mlaceholder\u001b[22m\u001b[39m\"')\n })\n\n test('moves the cursor with arrows', async () => {\n const renderInstance = render(<TextInput value=\"Hello\" onChange={() => {}} />)\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, ARROW_LEFT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHell\u001b[7mo\u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, ARROW_LEFT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHel\u001b[7ml\u001b[27mo\u001b[39m\"')\n await sendInput(renderInstance, ARROW_LEFT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHe\u001b[7ml\u001b[27mlo\u001b[39m\"')\n await sendInput(renderInstance, ARROW_LEFT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mH\u001b[7me\u001b[27mllo\u001b[39m\"')\n await sendInput(renderInstance, ARROW_LEFT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7mH\u001b[27mello\u001b[39m\"')\n // cursor can't go before the first character\n renderInstance.stdin.write(ARROW_LEFT)\n await new Promise((resolve) => setTimeout(resolve, 100))\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7mH\u001b[27mello\u001b[39m\"')\n\n await sendInput(renderInstance, ARROW_RIGHT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mH\u001b[7me\u001b[27mllo\u001b[39m\"')\n await sendInput(renderInstance, ARROW_RIGHT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHe\u001b[7ml\u001b[27mlo\u001b[39m\"')\n await sendInput(renderInstance, ARROW_RIGHT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHel\u001b[7ml\u001b[27mo\u001b[39m\"')\n await sendInput(renderInstance, ARROW_RIGHT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHell\u001b[7mo\u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, ARROW_RIGHT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n // cursor can't go after the last character\n renderInstance.stdin.write(ARROW_RIGHT)\n await new Promise((resolve) => setTimeout(resolve, 100))\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n })\n\n test('moves the cursor when deleting', async () => {\n const StatefulTextInput = () => {\n const [value, setValue] = useState('Hello')\n\n return <TextInput value={value} onChange={setValue} />\n }\n\n const renderInstance = render(<StatefulTextInput />)\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHell\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHel\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHe\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mH\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7m \u001b[27m\u001b[39m\"')\n // cannot delete after the value has been cleared\n renderInstance.stdin.write(DELETE)\n await new Promise((resolve) => setTimeout(resolve, 100))\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7m \u001b[27m\u001b[39m\"')\n })\n\n test('accepts input', async () => {\n const StatefulTextInput = () => {\n const [value, setValue] = useState('')\n\n return <TextInput value={value} onChange={setValue} />\n }\n\n const renderInstance = render(<StatefulTextInput />)\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, 'H')\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mH\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, 'ello')\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n })\n\n test('onChange', async () => {\n const onChange = vi.fn()\n\n const renderInstance = render(<TextInput value=\"\" onChange={onChange} />)\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, 'X')\n\n expect(onChange).toHaveBeenCalledWith('X')\n })\n\n test('deletes at the beginning and in the middle of text', async () => {\n const StatefulTextInput = () => {\n const [value, setValue] = useState('')\n\n return <TextInput value={value} onChange={setValue} />\n }\n\n const renderInstance = render(<StatefulTextInput />)\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, 'T')\n await sendInput(renderInstance, 'e')\n await sendInput(renderInstance, 's')\n await sendInput(renderInstance, 't')\n await sendInput(renderInstance, ARROW_LEFT)\n await sendInput(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mTe\u001b[7mt\u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, ARROW_LEFT)\n await sendInput(renderInstance, ARROW_LEFT)\n renderInstance.stdin.write(DELETE)\n await new Promise((resolve) => setTimeout(resolve, 100))\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7mT\u001b[27met\u001b[39m\"')\n })\n\n test('adjusts cursor when text is shorter than last value', async () => {\n let resetValue = () => {}\n\n const StatefulTextInput = () => {\n const [value, setValue] = useState('')\n resetValue = () => setValue('')\n\n return <TextInput value={value} onChange={setValue} />\n }\n\n const renderInstance = render(<StatefulTextInput />)\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, 'A')\n await sendInput(renderInstance, 'B')\n\n await waitForChange(resetValue, renderInstance.lastFrame)\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, 'A')\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mA\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, 'B')\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mAB\u001b[7m \u001b[27m\u001b[39m\"')\n })\n})\n"]}
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ export interface Props {
3
+ message: string;
4
+ onSubmit: (value: string) => void;
5
+ placeholder?: string;
6
+ }
7
+ declare const TextPrompt: React.FC<Props>;
8
+ export { TextPrompt };
@@ -0,0 +1,52 @@
1
+ import { TextInput } from './TextInput.js';
2
+ import { handleCtrlC } from '../../ui.js';
3
+ import useLayout from '../hooks/use-layout.js';
4
+ import React, { useCallback, useState } from 'react';
5
+ import { Box, useApp, useInput, Text } from 'ink';
6
+ import { figures } from 'listr2';
7
+ const TextPrompt = ({ message, onSubmit, placeholder }) => {
8
+ const { oneThird } = useLayout();
9
+ const [answer, setAnswer] = useState('');
10
+ const { exit: unmountInk } = useApp();
11
+ const [submitted, setSubmitted] = useState(false);
12
+ const [valid, setValid] = useState(false);
13
+ const underline = new Array(oneThird - 3).fill('▔');
14
+ useInput(useCallback((input, key) => {
15
+ handleCtrlC(input, key);
16
+ if (key.return) {
17
+ setSubmitted(true);
18
+ if (valid) {
19
+ onSubmit(answer);
20
+ unmountInk();
21
+ }
22
+ }
23
+ }, [answer, onSubmit, valid]));
24
+ const shouldShowError = submitted && !valid;
25
+ const color = shouldShowError ? 'red' : 'cyan';
26
+ const error = shouldShowError ? 'Please enter a value' : undefined;
27
+ return (React.createElement(Box, { flexDirection: "column", marginBottom: 1, width: oneThird },
28
+ React.createElement(Box, null,
29
+ React.createElement(Box, { marginRight: 2 },
30
+ React.createElement(Text, null, "?")),
31
+ React.createElement(Text, null, message)),
32
+ submitted && valid ? (React.createElement(Box, null,
33
+ React.createElement(Box, { marginRight: 2 },
34
+ React.createElement(Text, { color: "cyan" }, figures.tick)),
35
+ React.createElement(Box, { flexGrow: 1 },
36
+ React.createElement(Text, { color: "cyan" }, answer)))) : (React.createElement(Box, { flexDirection: "column" },
37
+ React.createElement(Box, null,
38
+ React.createElement(Box, { marginRight: 2 },
39
+ React.createElement(Text, { color: color }, `>`)),
40
+ React.createElement(Box, { flexGrow: 1 },
41
+ React.createElement(TextInput, { value: answer, onChange: (answer) => {
42
+ setAnswer(answer);
43
+ setValid(answer.length > 0);
44
+ setSubmitted(false);
45
+ }, placeholder: placeholder, color: color }))),
46
+ React.createElement(Box, { marginLeft: 3 },
47
+ React.createElement(Text, { color: color }, underline)),
48
+ error && (React.createElement(Box, { marginLeft: 3 },
49
+ React.createElement(Text, { color: color }, error)))))));
50
+ };
51
+ export { TextPrompt };
52
+ //# sourceMappingURL=TextPrompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TextPrompt.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/TextPrompt.tsx"],"names":[],"mappings":"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","sourcesContent":["import {TextInput} from './TextInput.js'\nimport {handleCtrlC} from '../../ui.js'\nimport useLayout from '../hooks/use-layout.js'\nimport React, {useCallback, useState} from 'react'\nimport {Box, useApp, useInput, Text} from 'ink'\nimport {figures} from 'listr2'\n\nexport interface Props {\n message: string\n onSubmit: (value: string) => void\n placeholder?: string\n}\n\nconst TextPrompt: React.FC<Props> = ({message, onSubmit, placeholder}) => {\n const {oneThird} = useLayout()\n const [answer, setAnswer] = useState<string>('')\n const {exit: unmountInk} = useApp()\n const [submitted, setSubmitted] = useState(false)\n const [valid, setValid] = useState(false)\n const underline = new Array(oneThird - 3).fill('▔')\n\n useInput(\n useCallback(\n (input, key) => {\n handleCtrlC(input, key)\n\n if (key.return) {\n setSubmitted(true)\n\n if (valid) {\n onSubmit(answer)\n unmountInk()\n }\n }\n },\n [answer, onSubmit, valid],\n ),\n )\n\n const shouldShowError = submitted && !valid\n const color = shouldShowError ? 'red' : 'cyan'\n const error = shouldShowError ? 'Please enter a value' : undefined\n\n return (\n <Box flexDirection=\"column\" marginBottom={1} width={oneThird}>\n <Box>\n <Box marginRight={2}>\n <Text>?</Text>\n </Box>\n <Text>{message}</Text>\n </Box>\n {submitted && valid ? (\n <Box>\n <Box marginRight={2}>\n <Text color=\"cyan\">{figures.tick}</Text>\n </Box>\n\n <Box flexGrow={1}>\n <Text color=\"cyan\">{answer}</Text>\n </Box>\n </Box>\n ) : (\n <Box flexDirection=\"column\">\n <Box>\n <Box marginRight={2}>\n <Text color={color}>{`>`}</Text>\n </Box>\n <Box flexGrow={1}>\n <TextInput\n value={answer}\n onChange={(answer) => {\n setAnswer(answer)\n setValid(answer.length > 0)\n setSubmitted(false)\n }}\n placeholder={placeholder}\n color={color}\n />\n </Box>\n </Box>\n <Box marginLeft={3}>\n <Text color={color}>{underline}</Text>\n </Box>\n {error && (\n <Box marginLeft={3}>\n <Text color={color}>{error}</Text>\n </Box>\n )}\n </Box>\n )}\n </Box>\n )\n}\n\nexport {TextPrompt}\n"]}
@@ -0,0 +1,68 @@
1
+ import { TextPrompt } from './TextPrompt.js';
2
+ import { getLastFrameAfterUnmount, sendInput, waitForInputsToBeReady } from '../../../../testing/ui.js';
3
+ import { unstyled } from '../../../../output.js';
4
+ import { render } from 'ink-testing-library';
5
+ import React from 'react';
6
+ import { describe, expect, test, vi } from 'vitest';
7
+ const ENTER = '\r';
8
+ describe('TextPrompt', () => {
9
+ test('default state', () => {
10
+ const { lastFrame } = render(React.createElement(TextPrompt, { onSubmit: () => { }, message: "Test question", placeholder: "Placeholder" }));
11
+ expect(unstyled(lastFrame())).toMatchInlineSnapshot(`
12
+ "? Test question
13
+ > Placeholder
14
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
15
+ "
16
+ `);
17
+ });
18
+ test('validation error', async () => {
19
+ const renderInstance = render(React.createElement(TextPrompt, { onSubmit: () => { }, message: "Test question" }));
20
+ await waitForInputsToBeReady();
21
+ await sendInput(renderInstance, ENTER);
22
+ // testing with styles because the color changes to red
23
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
24
+ "? Test question
25
+ >  
26
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
27
+ Please enter a value
28
+ "
29
+ `);
30
+ await sendInput(renderInstance, 'A');
31
+ // color changes back to valid color
32
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
33
+ "? Test question
34
+ > A 
35
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
36
+ "
37
+ `);
38
+ });
39
+ test('submitting the value', async () => {
40
+ const onSubmit = vi.fn();
41
+ const renderInstance = render(React.createElement(TextPrompt, { onSubmit: onSubmit, message: "Test question" }));
42
+ await waitForInputsToBeReady();
43
+ await sendInput(renderInstance, 'A');
44
+ await sendInput(renderInstance, ENTER);
45
+ expect(onSubmit).toHaveBeenCalledWith('A');
46
+ expect(unstyled(getLastFrameAfterUnmount(renderInstance))).toMatchInlineSnapshot(`
47
+ "? Test question
48
+ ✔ A
49
+ "
50
+ `);
51
+ });
52
+ test('text wrapping', async () => {
53
+ // component width is 80 characters wide in tests but because of the question mark and
54
+ // spaces before the question, we only have 77 characters to work with
55
+ const renderInstance = render(React.createElement(TextPrompt, { onSubmit: () => { }, message: "Test question" }));
56
+ await waitForInputsToBeReady();
57
+ await sendInput(renderInstance, 'A'.repeat(77));
58
+ await sendInput(renderInstance, 'B'.repeat(6));
59
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
60
+ "? Test question
61
+ > AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
62
+ BBBBBB 
63
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
64
+ "
65
+ `);
66
+ });
67
+ });
68
+ //# sourceMappingURL=TextPrompt.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TextPrompt.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/TextPrompt.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {TextPrompt} from './TextPrompt.js'\nimport {getLastFrameAfterUnmount, sendInput, waitForInputsToBeReady} from '../../../../testing/ui.js'\nimport {unstyled} from '../../../../output.js'\nimport {render} from 'ink-testing-library'\nimport React from 'react'\nimport {describe, expect, test, vi} from 'vitest'\n\nconst ENTER = '\\r'\n\ndescribe('TextPrompt', () => {\n test('default state', () => {\n const {lastFrame} = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" placeholder=\"Placeholder\" />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"? Test question\n > Placeholder\n ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n \"\n `)\n })\n\n test('validation error', async () => {\n const renderInstance = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" />)\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, ENTER)\n // testing with styles because the color changes to red\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question\n \u001b[31m>\u001b[39m \u001b[31m\u001b[7m \u001b[27m\u001b[39m\n \u001b[31m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \u001b[31mPlease enter a value\u001b[39m\n \"\n `)\n await sendInput(renderInstance, 'A')\n // color changes back to valid color\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question\n \u001b[36m>\u001b[39m \u001b[36mA\u001b[7m \u001b[27m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \"\n `)\n })\n\n test('submitting the value', async () => {\n const onSubmit = vi.fn()\n const renderInstance = render(<TextPrompt onSubmit={onSubmit} message=\"Test question\" />)\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, 'A')\n await sendInput(renderInstance, ENTER)\n expect(onSubmit).toHaveBeenCalledWith('A')\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toMatchInlineSnapshot(`\n \"? Test question\n ✔ A\n \"\n `)\n })\n\n test('text wrapping', async () => {\n // component width is 80 characters wide in tests but because of the question mark and\n // spaces before the question, we only have 77 characters to work with\n const renderInstance = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" />)\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, 'A'.repeat(77))\n await sendInput(renderInstance, 'B'.repeat(6))\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question\n \u001b[36m>\u001b[39m \u001b[36mAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\u001b[39m\n \u001b[36mBBBBBB\u001b[7m \u001b[27m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \"\n `)\n })\n})\n"]}
@@ -1,8 +1,8 @@
1
1
  import { TokenizedText } from './TokenizedText.js';
2
- import { renderString } from '../../ui.js';
3
2
  import { unstyled } from '../../../../output.js';
4
3
  import { describe, expect, test } from 'vitest';
5
4
  import React from 'react';
5
+ import { render } from 'ink-testing-library';
6
6
  describe('TokenizedText', async () => {
7
7
  test('renders arrays of items separated by spaces', async () => {
8
8
  const item = [
@@ -31,8 +31,8 @@ describe('TokenizedText', async () => {
31
31
  filePath: 'src/this/is/a/test.js',
32
32
  },
33
33
  ];
34
- const { output } = renderString(React.createElement(TokenizedText, { item: item }));
35
- expect(unstyled(output)).toMatchInlineSnapshot(`
34
+ const { lastFrame } = render(React.createElement(TokenizedText, { item: item }));
35
+ expect(unstyled(lastFrame())).toMatchInlineSnapshot(`
36
36
  "Run \`cd verification-app\` Example ( https://example.com )! my-app
37
37
  • Item 1
38
38
  • Item 2
@@ -1 +1 @@
1
- {"version":3,"file":"TokenizedText.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/TokenizedText.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAC,YAAY,EAAC,MAAM,aAAa,CAAA;AACxC,OAAO,EAAC,QAAQ,EAAC,MAAM,uBAAuB,CAAA;AAC9C,OAAO,EAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAC,MAAM,QAAQ,CAAA;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,QAAQ,CAAC,eAAe,EAAE,KAAK,IAAI,EAAE;IACnC,IAAI,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,IAAI,GAAG;YACX,KAAK;YACL;gBACE,OAAO,EAAE,qBAAqB;aAC/B;YACD;gBACE,IAAI,EAAE;oBACJ,GAAG,EAAE,qBAAqB;oBAC1B,KAAK,EAAE,SAAS;iBACjB;aACF;YACD;gBACE,IAAI,EAAE,GAAG;aACV;YACD;gBACE,SAAS,EAAE,QAAQ;aACpB;YACD;gBACE,IAAI,EAAE;oBACJ,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;iBACtC;aACF;YACD;gBACE,QAAQ,EAAE,uBAAuB;aAClC;SACF,CAAA;QAED,MAAM,EAAC,MAAM,EAAC,GAAG,YAAY,CAAC,oBAAC,aAAa,IAAC,IAAI,EAAE,IAAI,GAAI,CAAC,CAAA;QAE5D,MAAM,CAAC,QAAQ,CAAC,MAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;KAM/C,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import {TokenizedText} from './TokenizedText.js'\nimport {renderString} from '../../ui.js'\nimport {unstyled} from '../../../../output.js'\nimport {describe, expect, test} from 'vitest'\nimport React from 'react'\n\ndescribe('TokenizedText', async () => {\n test('renders arrays of items separated by spaces', async () => {\n const item = [\n 'Run',\n {\n command: 'cd verification-app',\n },\n {\n link: {\n url: 'https://example.com',\n label: 'Example',\n },\n },\n {\n char: '!',\n },\n {\n userInput: 'my-app',\n },\n {\n list: {\n items: ['Item 1', 'Item 2', 'Item 3'],\n },\n },\n {\n filePath: 'src/this/is/a/test.js',\n },\n ]\n\n const {output} = renderString(<TokenizedText item={item} />)\n\n expect(unstyled(output!)).toMatchInlineSnapshot(`\n \"Run \\`cd verification-app\\` Example ( https://example.com )! my-app\n • Item 1\n • Item 2\n • Item 3\n \\\\\"src/this/is/a/test.js\\\\\"\"\n `)\n })\n})\n"]}
1
+ {"version":3,"file":"TokenizedText.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/TokenizedText.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAC,QAAQ,EAAC,MAAM,uBAAuB,CAAA;AAC9C,OAAO,EAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAC,MAAM,QAAQ,CAAA;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAC,MAAM,EAAC,MAAM,qBAAqB,CAAA;AAE1C,QAAQ,CAAC,eAAe,EAAE,KAAK,IAAI,EAAE;IACnC,IAAI,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,IAAI,GAAG;YACX,KAAK;YACL;gBACE,OAAO,EAAE,qBAAqB;aAC/B;YACD;gBACE,IAAI,EAAE;oBACJ,GAAG,EAAE,qBAAqB;oBAC1B,KAAK,EAAE,SAAS;iBACjB;aACF;YACD;gBACE,IAAI,EAAE,GAAG;aACV;YACD;gBACE,SAAS,EAAE,QAAQ;aACpB;YACD;gBACE,IAAI,EAAE;oBACJ,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;iBACtC;aACF;YACD;gBACE,QAAQ,EAAE,uBAAuB;aAClC;SACF,CAAA;QAED,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,aAAa,IAAC,IAAI,EAAE,IAAI,GAAI,CAAC,CAAA;QAEzD,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;KAMpD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import {TokenizedText} from './TokenizedText.js'\nimport {unstyled} from '../../../../output.js'\nimport {describe, expect, test} from 'vitest'\nimport React from 'react'\nimport {render} from 'ink-testing-library'\n\ndescribe('TokenizedText', async () => {\n test('renders arrays of items separated by spaces', async () => {\n const item = [\n 'Run',\n {\n command: 'cd verification-app',\n },\n {\n link: {\n url: 'https://example.com',\n label: 'Example',\n },\n },\n {\n char: '!',\n },\n {\n userInput: 'my-app',\n },\n {\n list: {\n items: ['Item 1', 'Item 2', 'Item 3'],\n },\n },\n {\n filePath: 'src/this/is/a/test.js',\n },\n ]\n\n const {lastFrame} = render(<TokenizedText item={item} />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"Run \\`cd verification-app\\` Example ( https://example.com )! my-app\n • Item 1\n • Item 2\n • Item 3\n \\\\\"src/this/is/a/test.js\\\\\"\"\n `)\n })\n})\n"]}
@@ -1,11 +1,11 @@
1
1
  import { UserInput } from './UserInput.js';
2
- import { renderString } from '../../ui.js';
3
2
  import { describe, expect, test } from 'vitest';
4
3
  import React from 'react';
4
+ import { render } from 'ink-testing-library';
5
5
  describe('UserInput', async () => {
6
6
  test('renders correctly', async () => {
7
- const { output } = renderString(React.createElement(UserInput, { userInput: "my-app" }));
8
- expect(output).toMatchInlineSnapshot('"my-app"');
7
+ const { lastFrame } = render(React.createElement(UserInput, { userInput: "my-app" }));
8
+ expect(lastFrame()).toMatchInlineSnapshot('"my-app"');
9
9
  });
10
10
  });
11
11
  //# sourceMappingURL=UserInput.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"UserInput.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/UserInput.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAC,YAAY,EAAC,MAAM,aAAa,CAAA;AACxC,OAAO,EAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAC,MAAM,QAAQ,CAAA;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,QAAQ,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;IAC/B,IAAI,CAAC,mBAAmB,EAAE,KAAK,IAAI,EAAE;QACnC,MAAM,EAAC,MAAM,EAAC,GAAG,YAAY,CAAC,oBAAC,SAAS,IAAC,SAAS,EAAC,QAAQ,GAAG,CAAC,CAAA;QAE/D,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,oBAAoB,CAAC,CAAA;IAC5D,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import {UserInput} from './UserInput.js'\nimport {renderString} from '../../ui.js'\nimport {describe, expect, test} from 'vitest'\nimport React from 'react'\n\ndescribe('UserInput', async () => {\n test('renders correctly', async () => {\n const {output} = renderString(<UserInput userInput=\"my-app\" />)\n\n expect(output).toMatchInlineSnapshot('\"\u001b[36mmy-app\u001b[39m\"')\n })\n})\n"]}
1
+ {"version":3,"file":"UserInput.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/UserInput.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAC,MAAM,QAAQ,CAAA;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAC,MAAM,EAAC,MAAM,qBAAqB,CAAA;AAE1C,QAAQ,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;IAC/B,IAAI,CAAC,mBAAmB,EAAE,KAAK,IAAI,EAAE;QACnC,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,SAAS,IAAC,SAAS,EAAC,QAAQ,GAAG,CAAC,CAAA;QAE5D,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,oBAAoB,CAAC,CAAA;IACjE,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import {UserInput} from './UserInput.js'\nimport {describe, expect, test} from 'vitest'\nimport React from 'react'\nimport {render} from 'ink-testing-library'\n\ndescribe('UserInput', async () => {\n test('renders correctly', async () => {\n const {lastFrame} = render(<UserInput userInput=\"my-app\" />)\n\n expect(lastFrame()).toMatchInlineSnapshot('\"\u001b[36mmy-app\u001b[39m\"')\n })\n})\n"]}
@@ -1,3 +1,6 @@
1
- export default function useLayout(): {
2
- width: number;
3
- };
1
+ interface Layout {
2
+ twoThirds: number;
3
+ oneThird: number;
4
+ }
5
+ export default function useLayout(): Layout;
6
+ export {};
@@ -3,19 +3,28 @@ const MIN_WIDTH = 80;
3
3
  export default function useLayout() {
4
4
  const { stdout } = useStdout();
5
5
  const fullWidth = stdout?.columns ?? MIN_WIDTH;
6
- const twoThirdsOfWidth = Math.floor((fullWidth / 3) * 2);
7
- let width;
6
+ let oneThird;
7
+ let twoThirds;
8
8
  if (fullWidth <= MIN_WIDTH) {
9
- width = fullWidth;
10
- }
11
- else if (twoThirdsOfWidth < MIN_WIDTH) {
12
- width = MIN_WIDTH;
9
+ oneThird = fullWidth;
10
+ twoThirds = fullWidth;
13
11
  }
14
12
  else {
15
- width = twoThirdsOfWidth;
13
+ oneThird = column({ fullWidth, fraction: [1, 3], minWidth: MIN_WIDTH });
14
+ twoThirds = column({ fullWidth, fraction: [2, 3], minWidth: MIN_WIDTH });
16
15
  }
17
16
  return {
18
- width,
17
+ twoThirds,
18
+ oneThird,
19
19
  };
20
20
  }
21
+ function column({ fullWidth, fraction, minWidth, }) {
22
+ const fractionedWidth = Math.floor((fullWidth / fraction[1]) * fraction[0]);
23
+ if (fractionedWidth < minWidth) {
24
+ return minWidth;
25
+ }
26
+ else {
27
+ return fractionedWidth;
28
+ }
29
+ }
21
30
  //# sourceMappingURL=use-layout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-layout.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/hooks/use-layout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,KAAK,CAAA;AAE7B,MAAM,SAAS,GAAG,EAAE,CAAA;AAEpB,MAAM,CAAC,OAAO,UAAU,SAAS;IAC/B,MAAM,EAAC,MAAM,EAAC,GAAG,SAAS,EAAE,CAAA;IAE5B,MAAM,SAAS,GAAG,MAAM,EAAE,OAAO,IAAI,SAAS,CAAA;IAC9C,MAAM,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;IACxD,IAAI,KAAK,CAAA;IAET,IAAI,SAAS,IAAI,SAAS,EAAE;QAC1B,KAAK,GAAG,SAAS,CAAA;KAClB;SAAM,IAAI,gBAAgB,GAAG,SAAS,EAAE;QACvC,KAAK,GAAG,SAAS,CAAA;KAClB;SAAM;QACL,KAAK,GAAG,gBAAgB,CAAA;KACzB;IAED,OAAO;QACL,KAAK;KACN,CAAA;AACH,CAAC","sourcesContent":["import {useStdout} from 'ink'\n\nconst MIN_WIDTH = 80\n\nexport default function useLayout() {\n const {stdout} = useStdout()\n\n const fullWidth = stdout?.columns ?? MIN_WIDTH\n const twoThirdsOfWidth = Math.floor((fullWidth / 3) * 2)\n let width\n\n if (fullWidth <= MIN_WIDTH) {\n width = fullWidth\n } else if (twoThirdsOfWidth < MIN_WIDTH) {\n width = MIN_WIDTH\n } else {\n width = twoThirdsOfWidth\n }\n\n return {\n width,\n }\n}\n"]}
1
+ {"version":3,"file":"use-layout.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/hooks/use-layout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,KAAK,CAAA;AAE7B,MAAM,SAAS,GAAG,EAAE,CAAA;AAOpB,MAAM,CAAC,OAAO,UAAU,SAAS;IAC/B,MAAM,EAAC,MAAM,EAAC,GAAG,SAAS,EAAE,CAAA;IAE5B,MAAM,SAAS,GAAG,MAAM,EAAE,OAAO,IAAI,SAAS,CAAA;IAC9C,IAAI,QAAQ,CAAA;IACZ,IAAI,SAAS,CAAA;IAEb,IAAI,SAAS,IAAI,SAAS,EAAE;QAC1B,QAAQ,GAAG,SAAS,CAAA;QACpB,SAAS,GAAG,SAAS,CAAA;KACtB;SAAM;QACL,QAAQ,GAAG,MAAM,CAAC,EAAC,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAC,CAAC,CAAA;QACrE,SAAS,GAAG,MAAM,CAAC,EAAC,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAC,CAAC,CAAA;KACvE;IAED,OAAO;QACL,SAAS;QACT,QAAQ;KACT,CAAA;AACH,CAAC;AAED,SAAS,MAAM,CAAC,EACd,SAAS,EACT,QAAQ,EACR,QAAQ,GAKT;IACC,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAA;IAE3E,IAAI,eAAe,GAAG,QAAQ,EAAE;QAC9B,OAAO,QAAQ,CAAA;KAChB;SAAM;QACL,OAAO,eAAe,CAAA;KACvB;AACH,CAAC","sourcesContent":["import {useStdout} from 'ink'\n\nconst MIN_WIDTH = 80\n\ninterface Layout {\n twoThirds: number\n oneThird: number\n}\n\nexport default function useLayout(): Layout {\n const {stdout} = useStdout()\n\n const fullWidth = stdout?.columns ?? MIN_WIDTH\n let oneThird\n let twoThirds\n\n if (fullWidth <= MIN_WIDTH) {\n oneThird = fullWidth\n twoThirds = fullWidth\n } else {\n oneThird = column({fullWidth, fraction: [1, 3], minWidth: MIN_WIDTH})\n twoThirds = column({fullWidth, fraction: [2, 3], minWidth: MIN_WIDTH})\n }\n\n return {\n twoThirds,\n oneThird,\n }\n}\n\nfunction column({\n fullWidth,\n fraction,\n minWidth,\n}: {\n fullWidth: number\n fraction: [number, number]\n minWidth: number\n}): number {\n const fractionedWidth = Math.floor((fullWidth / fraction[1]) * fraction[0])\n\n if (fractionedWidth < minWidth) {\n return minWidth\n } else {\n return fractionedWidth\n }\n}\n"]}
@@ -1,8 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import { Logger, LogLevel } from '../../output.js';
3
- import { Props as PromptProps } from '../../private/node/ui/components/Prompt.js';
4
3
  import { ReactElement } from 'react';
5
- import { RenderOptions } from 'ink';
4
+ import { Key, RenderOptions } from 'ink';
6
5
  import { EventEmitter } from 'events';
7
6
  export declare function renderOnce(element: JSX.Element, logLevel?: LogLevel, logger?: Logger): void;
8
7
  export declare function render(element: JSX.Element, options?: RenderOptions): Promise<void>;
@@ -20,5 +19,5 @@ export declare class OutputStream extends EventEmitter {
20
19
  lastFrame: () => string | undefined;
21
20
  }
22
21
  export declare const renderString: (element: ReactElement) => Instance;
23
- export declare function prompt<T>(options: Omit<PromptProps<T>, 'onChoose'>): Promise<T>;
22
+ export declare function handleCtrlC(input: string, key: Key): void;
24
23
  export {};
@@ -1,7 +1,5 @@
1
1
  import { isUnitTest } from '../../environment/local.js';
2
2
  import { collectLog, consoleLog, outputWhereAppropriate } from '../../output.js';
3
- import Prompt from '../../private/node/ui/components/Prompt.js';
4
- import React from 'react';
5
3
  import { render as inkRender } from 'ink';
6
4
  import { EventEmitter } from 'events';
7
5
  export function renderOnce(element, logLevel = 'info', logger = consoleLog) {
@@ -17,7 +15,6 @@ export function render(element, options) {
17
15
  const { waitUntilExit } = inkRender(element, options);
18
16
  return waitUntilExit();
19
17
  }
20
- const TEST_TERMINAL_WIDTH = 80;
21
18
  export class OutputStream extends EventEmitter {
22
19
  constructor(options) {
23
20
  super();
@@ -31,8 +28,8 @@ export class OutputStream extends EventEmitter {
31
28
  }
32
29
  }
33
30
  export const renderString = (element) => {
34
- const stdout = new OutputStream({ columns: isUnitTest() ? TEST_TERMINAL_WIDTH : process.stdout.columns });
35
- const stderr = new OutputStream({ columns: isUnitTest() ? TEST_TERMINAL_WIDTH : process.stderr.columns });
31
+ const stdout = new OutputStream({ columns: process.stdout.columns });
32
+ const stderr = new OutputStream({ columns: process.stderr.columns });
36
33
  const instance = inkRender(element, {
37
34
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
35
  stdout: stdout,
@@ -47,16 +44,10 @@ export const renderString = (element) => {
47
44
  unmount: instance.unmount,
48
45
  };
49
46
  };
50
- export async function prompt(options) {
51
- let onChooseResolve = () => { };
52
- const onChoosePromise = new Promise((resolve) => {
53
- onChooseResolve = resolve;
54
- });
55
- const props = {
56
- ...options,
57
- onChoose: onChooseResolve,
58
- };
59
- await render(React.createElement(Prompt, { ...props }), { exitOnCtrlC: false });
60
- return onChoosePromise;
47
+ export function handleCtrlC(input, key) {
48
+ if (input === 'c' && key.ctrl) {
49
+ // Exceptions thrown in hooks aren't caught by our errorHandler.
50
+ process.exit(1);
51
+ }
61
52
  }
62
53
  //# sourceMappingURL=ui.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../src/private/node/ui.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,4BAA4B,CAAA;AACrD,OAAO,EAAC,UAAU,EAAE,UAAU,EAAoB,sBAAsB,EAAC,MAAM,iBAAiB,CAAA;AAChG,OAAO,MAA8B,MAAM,4CAA4C,CAAA;AACvF,OAAO,KAAqB,MAAM,OAAO,CAAA;AACzC,OAAO,EAAC,MAAM,IAAI,SAAS,EAAgB,MAAM,KAAK,CAAA;AACtD,OAAO,EAAC,YAAY,EAAC,MAAM,QAAQ,CAAA;AAEnC,MAAM,UAAU,UAAU,CAAC,OAAoB,EAAE,WAAqB,MAAM,EAAE,SAAiB,UAAU;IACvG,MAAM,EAAC,MAAM,EAAE,OAAO,EAAC,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;IAE/C,IAAI,MAAM,EAAE;QACV,IAAI,UAAU,EAAE;YAAE,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QAC9C,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;KACjD;IAED,OAAO,EAAE,CAAA;AACX,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,OAAoB,EAAE,OAAuB;IAClE,MAAM,EAAC,aAAa,EAAC,GAAG,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACnD,OAAO,aAAa,EAAE,CAAA;AACxB,CAAC;AAOD,MAAM,mBAAmB,GAAG,EAAE,CAAA;AAC9B,MAAM,OAAO,YAAa,SAAQ,YAAY;IAI5C,YAAY,OAA0B;QACpC,KAAK,EAAE,CAAA;QAIT,UAAK,GAAG,CAAC,KAAa,EAAE,EAAE;YACxB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAA;QACzB,CAAC,CAAA;QAED,cAAS,GAAG,GAAG,EAAE;YACf,OAAO,IAAI,CAAC,UAAU,CAAA;QACxB,CAAC,CAAA;QATC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAA;IAChC,CAAC;CASF;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAqB,EAAY,EAAE;IAC9D,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,EAAC,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,EAAC,CAAC,CAAA;IACvG,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,EAAC,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,EAAC,CAAC,CAAA;IAEvG,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,EAAE;QAClC,8DAA8D;QAC9D,MAAM,EAAE,MAAa;QACrB,8DAA8D;QAC9D,MAAM,EAAE,MAAa;QACrB,KAAK,EAAE,IAAI;QACX,WAAW,EAAE,KAAK;QAClB,YAAY,EAAE,KAAK;KACpB,CAAC,CAAA;IAEF,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE;QAC1B,OAAO,EAAE,QAAQ,CAAC,OAAO;KAC1B,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAI,OAAyC;IACvE,IAAI,eAAe,GAAwB,GAAG,EAAE,GAAE,CAAC,CAAA;IAEnD,MAAM,eAAe,GAAG,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,EAAE;QACjD,eAAe,GAAG,OAAO,CAAA;IAC3B,CAAC,CAAC,CAAA;IAEF,MAAM,KAAK,GAAG;QACZ,GAAG,OAAO;QACV,QAAQ,EAAE,eAAe;KAC1B,CAAA;IAED,MAAM,MAAM,CAAC,oBAAC,MAAM,OAAK,KAAK,GAAI,EAAE,EAAC,WAAW,EAAE,KAAK,EAAC,CAAC,CAAA;IAEzD,OAAO,eAAe,CAAA;AACxB,CAAC","sourcesContent":["import {isUnitTest} from '../../environment/local.js'\nimport {collectLog, consoleLog, Logger, LogLevel, outputWhereAppropriate} from '../../output.js'\nimport Prompt, {Props as PromptProps} from '../../private/node/ui/components/Prompt.js'\nimport React, {ReactElement} from 'react'\nimport {render as inkRender, RenderOptions} from 'ink'\nimport {EventEmitter} from 'events'\n\nexport function renderOnce(element: JSX.Element, logLevel: LogLevel = 'info', logger: Logger = consoleLog) {\n const {output, unmount} = renderString(element)\n\n if (output) {\n if (isUnitTest()) collectLog(logLevel, output)\n outputWhereAppropriate(logLevel, logger, output)\n }\n\n unmount()\n}\n\nexport function render(element: JSX.Element, options?: RenderOptions) {\n const {waitUntilExit} = inkRender(element, options)\n return waitUntilExit()\n}\n\ninterface Instance {\n output: string | undefined\n unmount: () => void\n}\n\nconst TEST_TERMINAL_WIDTH = 80\nexport class OutputStream extends EventEmitter {\n columns: number\n private _lastFrame?: string\n\n constructor(options: {columns: number}) {\n super()\n this.columns = options.columns\n }\n\n write = (frame: string) => {\n this._lastFrame = frame\n }\n\n lastFrame = () => {\n return this._lastFrame\n }\n}\n\nexport const renderString = (element: ReactElement): Instance => {\n const stdout = new OutputStream({columns: isUnitTest() ? TEST_TERMINAL_WIDTH : process.stdout.columns})\n const stderr = new OutputStream({columns: isUnitTest() ? TEST_TERMINAL_WIDTH : process.stderr.columns})\n\n const instance = inkRender(element, {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n stdout: stdout as any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n stderr: stderr as any,\n debug: true,\n exitOnCtrlC: false,\n patchConsole: false,\n })\n\n return {\n output: stdout.lastFrame(),\n unmount: instance.unmount,\n }\n}\n\nexport async function prompt<T>(options: Omit<PromptProps<T>, 'onChoose'>) {\n let onChooseResolve: (choice: T) => void = () => {}\n\n const onChoosePromise = new Promise<T>((resolve) => {\n onChooseResolve = resolve\n })\n\n const props = {\n ...options,\n onChoose: onChooseResolve,\n }\n\n await render(<Prompt {...props} />, {exitOnCtrlC: false})\n\n return onChoosePromise\n}\n"]}
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../src/private/node/ui.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,4BAA4B,CAAA;AACrD,OAAO,EAAC,UAAU,EAAE,UAAU,EAAoB,sBAAsB,EAAC,MAAM,iBAAiB,CAAA;AAEhG,OAAO,EAAM,MAAM,IAAI,SAAS,EAAgB,MAAM,KAAK,CAAA;AAC3D,OAAO,EAAC,YAAY,EAAC,MAAM,QAAQ,CAAA;AAEnC,MAAM,UAAU,UAAU,CAAC,OAAoB,EAAE,WAAqB,MAAM,EAAE,SAAiB,UAAU;IACvG,MAAM,EAAC,MAAM,EAAE,OAAO,EAAC,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;IAE/C,IAAI,MAAM,EAAE;QACV,IAAI,UAAU,EAAE;YAAE,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QAC9C,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;KACjD;IAED,OAAO,EAAE,CAAA;AACX,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,OAAoB,EAAE,OAAuB;IAClE,MAAM,EAAC,aAAa,EAAC,GAAG,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACnD,OAAO,aAAa,EAAE,CAAA;AACxB,CAAC;AAOD,MAAM,OAAO,YAAa,SAAQ,YAAY;IAI5C,YAAY,OAA0B;QACpC,KAAK,EAAE,CAAA;QAIT,UAAK,GAAG,CAAC,KAAa,EAAE,EAAE;YACxB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAA;QACzB,CAAC,CAAA;QAED,cAAS,GAAG,GAAG,EAAE;YACf,OAAO,IAAI,CAAC,UAAU,CAAA;QACxB,CAAC,CAAA;QATC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAA;IAChC,CAAC;CASF;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAqB,EAAY,EAAE;IAC9D,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,EAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,EAAC,CAAC,CAAA;IAClE,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,EAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,EAAC,CAAC,CAAA;IAElE,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,EAAE;QAClC,8DAA8D;QAC9D,MAAM,EAAE,MAAa;QACrB,8DAA8D;QAC9D,MAAM,EAAE,MAAa;QACrB,KAAK,EAAE,IAAI;QACX,WAAW,EAAE,KAAK;QAClB,YAAY,EAAE,KAAK;KACpB,CAAC,CAAA;IAEF,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE;QAC1B,OAAO,EAAE,QAAQ,CAAC,OAAO;KAC1B,CAAA;AACH,CAAC,CAAA;AAED,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,GAAQ;IACjD,IAAI,KAAK,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE;QAC7B,gEAAgE;QAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;KAChB;AACH,CAAC","sourcesContent":["import {isUnitTest} from '../../environment/local.js'\nimport {collectLog, consoleLog, Logger, LogLevel, outputWhereAppropriate} from '../../output.js'\nimport {ReactElement} from 'react'\nimport {Key, render as inkRender, RenderOptions} from 'ink'\nimport {EventEmitter} from 'events'\n\nexport function renderOnce(element: JSX.Element, logLevel: LogLevel = 'info', logger: Logger = consoleLog) {\n const {output, unmount} = renderString(element)\n\n if (output) {\n if (isUnitTest()) collectLog(logLevel, output)\n outputWhereAppropriate(logLevel, logger, output)\n }\n\n unmount()\n}\n\nexport function render(element: JSX.Element, options?: RenderOptions) {\n const {waitUntilExit} = inkRender(element, options)\n return waitUntilExit()\n}\n\ninterface Instance {\n output: string | undefined\n unmount: () => void\n}\n\nexport class OutputStream extends EventEmitter {\n columns: number\n private _lastFrame?: string\n\n constructor(options: {columns: number}) {\n super()\n this.columns = options.columns\n }\n\n write = (frame: string) => {\n this._lastFrame = frame\n }\n\n lastFrame = () => {\n return this._lastFrame\n }\n}\n\nexport const renderString = (element: ReactElement): Instance => {\n const stdout = new OutputStream({columns: process.stdout.columns})\n const stderr = new OutputStream({columns: process.stderr.columns})\n\n const instance = inkRender(element, {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n stdout: stdout as any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n stderr: stderr as any,\n debug: true,\n exitOnCtrlC: false,\n patchConsole: false,\n })\n\n return {\n output: stdout.lastFrame(),\n unmount: instance.unmount,\n }\n}\n\nexport function handleCtrlC(input: string, key: Key) {\n if (input === 'c' && key.ctrl) {\n // Exceptions thrown in hooks aren't caught by our errorHandler.\n process.exit(1)\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { createRequire } from 'node:module';
1
+ import { createRequire } from 'module';
2
2
  const require = createRequire(import.meta.url);
3
3
  /**
4
4
  * Takes a random value from an array.
@@ -1 +1 @@
1
- {"version":3,"file":"array.js","sourceRoot":"","sources":["../../../src/public/common/array.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,aAAa,CAAA;AAGzC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAE9C;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAI,KAAU;IAC/C,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAE,CAAA;AACzD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAI,KAAwB;IACpE,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,SAAS,CAAmC,CAAA;AACrF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CAAI,KAAU;IACtD,OAAO,KAAK,CAAC,MAAM,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAA;AAC7C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,MAAM,CAAI,KAAiC,EAAE,QAA0B;IACrF,MAAM,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;IAC7C,OAAO,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;AACtC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAI,KAAiC,EAAE,GAAG,MAAiB;IACnF,MAAM,gBAAgB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IACrD,OAAO,gBAAgB,CAAC,KAAK,EAAE,GAAG,MAAM,CAAC,CAAA;AAC3C,CAAC","sourcesContent":["import {createRequire} from 'node:module'\nimport type {List, ValueIteratee} from 'lodash'\n\nconst require = createRequire(import.meta.url)\n\n/**\n * Takes a random value from an array.\n * @param array - Array from which we'll select a random item.\n * @returns A random element from the array.\n */\nexport function takeRandomFromArray<T>(array: T[]): T {\n return array[Math.floor(Math.random() * array.length)]!\n}\n\n/**\n * Returns a copy of the array deleting the elemements that are undefined.\n * @param array - The array whose undefined will be deleted.\n * @returns A copy of the array with the undefined elements deleted.\n */\nexport function getArrayRejectingUndefined<T>(array: (T | undefined)[]): T[] {\n return array.filter((item) => item !== undefined) as Exclude<T, null | undefined>[]\n}\n\n/**\n * Returns true if an array contains duplicates.\n * @returns True if the array contains duplicates.\n */\nexport function getArrayContainsDuplicates<T>(array: T[]): boolean {\n return array.length !== new Set(array).size\n}\n\n/**\n * This method is like `_.uniq` except that it accepts `iteratee` which is\n * invoked for each element in `array` to generate the criterion by which\n * uniqueness is computed. The iteratee is invoked with one argument: (value).\n *\n * @param array - The array to inspect.\n * @param iteratee - The iteratee invoked per element.\n * @returns Returns the new duplicate free array.\n */\nexport function uniqBy<T>(array: List<T> | null | undefined, iteratee: ValueIteratee<T>): T[] {\n const lodashUniqBy = require('lodash/uniqBy')\n return lodashUniqBy(array, iteratee)\n}\n\n/**\n * Creates an array of `array` values not included in the other provided arrays using SameValueZero for\n * equality comparisons. The order and references of result values are determined by the first array.\n *\n * @param array - The array to inspect.\n * @param values - The arrays of values to exclude.\n * @returns Returns the new array of filtered values.\n */\nexport function difference<T>(array: List<T> | null | undefined, ...values: List<T>[]): T[] {\n const lodashDifference = require('lodash/difference')\n return lodashDifference(array, ...values)\n}\n"]}
1
+ {"version":3,"file":"array.js","sourceRoot":"","sources":["../../../src/public/common/array.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,QAAQ,CAAA;AAGpC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAE9C;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAI,KAAU;IAC/C,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAE,CAAA;AACzD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAI,KAAwB;IACpE,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,SAAS,CAAmC,CAAA;AACrF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CAAI,KAAU;IACtD,OAAO,KAAK,CAAC,MAAM,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAA;AAC7C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,MAAM,CAAI,KAAiC,EAAE,QAA0B;IACrF,MAAM,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;IAC7C,OAAO,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;AACtC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAI,KAAiC,EAAE,GAAG,MAAiB;IACnF,MAAM,gBAAgB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IACrD,OAAO,gBAAgB,CAAC,KAAK,EAAE,GAAG,MAAM,CAAC,CAAA;AAC3C,CAAC","sourcesContent":["import {createRequire} from 'module'\nimport type {List, ValueIteratee} from 'lodash'\n\nconst require = createRequire(import.meta.url)\n\n/**\n * Takes a random value from an array.\n * @param array - Array from which we'll select a random item.\n * @returns A random element from the array.\n */\nexport function takeRandomFromArray<T>(array: T[]): T {\n return array[Math.floor(Math.random() * array.length)]!\n}\n\n/**\n * Returns a copy of the array deleting the elemements that are undefined.\n * @param array - The array whose undefined will be deleted.\n * @returns A copy of the array with the undefined elements deleted.\n */\nexport function getArrayRejectingUndefined<T>(array: (T | undefined)[]): T[] {\n return array.filter((item) => item !== undefined) as Exclude<T, null | undefined>[]\n}\n\n/**\n * Returns true if an array contains duplicates.\n * @returns True if the array contains duplicates.\n */\nexport function getArrayContainsDuplicates<T>(array: T[]): boolean {\n return array.length !== new Set(array).size\n}\n\n/**\n * This method is like `_.uniq` except that it accepts `iteratee` which is\n * invoked for each element in `array` to generate the criterion by which\n * uniqueness is computed. The iteratee is invoked with one argument: (value).\n *\n * @param array - The array to inspect.\n * @param iteratee - The iteratee invoked per element.\n * @returns Returns the new duplicate free array.\n */\nexport function uniqBy<T>(array: List<T> | null | undefined, iteratee: ValueIteratee<T>): T[] {\n const lodashUniqBy = require('lodash/uniqBy')\n return lodashUniqBy(array, iteratee)\n}\n\n/**\n * Creates an array of `array` values not included in the other provided arrays using SameValueZero for\n * equality comparisons. The order and references of result values are determined by the first array.\n *\n * @param array - The array to inspect.\n * @param values - The arrays of values to exclude.\n * @returns Returns the new array of filtered values.\n */\nexport function difference<T>(array: List<T> | null | undefined, ...values: List<T>[]): T[] {\n const lodashDifference = require('lodash/difference')\n return lodashDifference(array, ...values)\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { createRequire } from 'node:module';
1
+ import { createRequire } from 'module';
2
2
  const require = createRequire(import.meta.url);
3
3
  /**
4
4
  * Creates an object composed of keys generated from the results of running each element of collection through
@@ -1 +1 @@
1
- {"version":3,"file":"collection.js","sourceRoot":"","sources":["../../../src/public/common/collection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,aAAa,CAAA;AAGzC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAE9C;;;;;;;;GAQG;AACH,MAAM,UAAU,OAAO,CACrB,UAA2C,EAC3C,QAA2B;IAI3B,MAAM,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAC/C,OAAO,aAAa,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;AAC5C,CAAC;AAED;;;;;;;;GAQG;AAEH,MAAM,UAAU,SAAS,CAAI,UAAsC,EAAE,QAA0B;IAC7F,MAAM,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IACnD,OAAO,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;AAC9C,CAAC","sourcesContent":["import {createRequire} from 'node:module'\nimport type {List, ValueIteratee} from 'lodash'\n\nconst require = createRequire(import.meta.url)\n\n/**\n * Creates an object composed of keys generated from the results of running each element of collection through\n * iteratee. The corresponding value of each key is an array of the elements responsible for generating the\n * key. The iteratee is invoked with one argument: (value).\n *\n * @param collection - The collection to iterate over.\n * @param iteratee - The function invoked per iteration.\n * @returns Returns the composed aggregate object.\n */\nexport function groupBy<T>(\n collection: ArrayLike<T> | null | undefined,\n iteratee?: ValueIteratee<T>,\n): {\n [index: string]: T[]\n} {\n const lodashBroupBy = require('lodash/groupBy')\n return lodashBroupBy(collection, iteratee)\n}\n\n/**\n * Creates an array of elements split into two groups, the first of which contains elements predicate returns truthy for,\n * while the second of which contains elements predicate returns falsey for.\n * The predicate is invoked with three arguments: (value, index|key, collection).\n *\n * @param collection - The collection to iterate over.\n * @param callback - The function called per iteration.\n * @returns Returns the array of grouped elements.\n */\n\nexport function partition<T>(collection: List<T> | null | undefined, callback: ValueIteratee<T>): [T[], T[]] {\n const lodashPartition = require('lodash/partition')\n return lodashPartition(collection, callback)\n}\n"]}
1
+ {"version":3,"file":"collection.js","sourceRoot":"","sources":["../../../src/public/common/collection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,QAAQ,CAAA;AAGpC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAE9C;;;;;;;;GAQG;AACH,MAAM,UAAU,OAAO,CACrB,UAA2C,EAC3C,QAA2B;IAI3B,MAAM,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAC/C,OAAO,aAAa,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;AAC5C,CAAC;AAED;;;;;;;;GAQG;AAEH,MAAM,UAAU,SAAS,CAAI,UAAsC,EAAE,QAA0B;IAC7F,MAAM,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IACnD,OAAO,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;AAC9C,CAAC","sourcesContent":["import {createRequire} from 'module'\nimport type {List, ValueIteratee} from 'lodash'\n\nconst require = createRequire(import.meta.url)\n\n/**\n * Creates an object composed of keys generated from the results of running each element of collection through\n * iteratee. The corresponding value of each key is an array of the elements responsible for generating the\n * key. The iteratee is invoked with one argument: (value).\n *\n * @param collection - The collection to iterate over.\n * @param iteratee - The function invoked per iteration.\n * @returns Returns the composed aggregate object.\n */\nexport function groupBy<T>(\n collection: ArrayLike<T> | null | undefined,\n iteratee?: ValueIteratee<T>,\n): {\n [index: string]: T[]\n} {\n const lodashBroupBy = require('lodash/groupBy')\n return lodashBroupBy(collection, iteratee)\n}\n\n/**\n * Creates an array of elements split into two groups, the first of which contains elements predicate returns truthy for,\n * while the second of which contains elements predicate returns falsey for.\n * The predicate is invoked with three arguments: (value, index|key, collection).\n *\n * @param collection - The collection to iterate over.\n * @param callback - The function called per iteration.\n * @returns Returns the array of grouped elements.\n */\n\nexport function partition<T>(collection: List<T> | null | undefined, callback: ValueIteratee<T>): [T[], T[]] {\n const lodashPartition = require('lodash/partition')\n return lodashPartition(collection, callback)\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { createRequire } from 'node:module';
1
+ import { createRequire } from 'module';
2
2
  const require = createRequire(import.meta.url);
3
3
  /**
4
4
  * Creates a function that memoizes the result of func. If resolver is provided it determines the cache key for