cdd-cli 4.0.0 → 4.1.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 (130) hide show
  1. package/README.es.md +29 -5
  2. package/README.md +29 -5
  3. package/dist/components/UsageMenu.js +1 -1
  4. package/dist/helpers/appState.js +26 -0
  5. package/dist/helpers/dockerService/serviceComponents/containerExec.js +211 -0
  6. package/dist/hooks/creation/useContainerCreation.js +25 -6
  7. package/dist/hooks/useContainerCommandRouter.js +12 -2
  8. package/dist/hooks/useControls.js +5 -0
  9. package/dist/hooks/useShellMode.js +101 -0
  10. package/dist/index.js +3 -1
  11. package/package.json +4 -1
  12. package/.atl/skill-registry.md +0 -30
  13. package/.editorconfig +0 -12
  14. package/.eslintrc.json +0 -30
  15. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -30
  16. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
  17. package/.github/PULL_REQUEST_TEMPLATE/pull_request.md +0 -14
  18. package/.github/workflows/ci.yml +0 -32
  19. package/.github/workflows/docs.yml +0 -42
  20. package/.prettierignore +0 -4
  21. package/.prettierrc +0 -8
  22. package/.~lock.ROADMAP.md# +0 -1
  23. package/CHANGELOG.md +0 -216
  24. package/CODE_OF_CONDUCT.md +0 -12
  25. package/CONTRIBUTING.md +0 -35
  26. package/ROADMAP.md +0 -647
  27. package/SECURITY.md +0 -5
  28. package/__mocks__/ink.cjs +0 -17
  29. package/_config.yml +0 -13
  30. package/babel.config.cjs +0 -23
  31. package/coverage/clover.xml +0 -575
  32. package/coverage/coverage-final.json +0 -17
  33. package/coverage/lcov-report/ContainerCreationPrompt.jsx.html +0 -400
  34. package/coverage/lcov-report/ControlsHUD.jsx.html +0 -256
  35. package/coverage/lcov-report/base.css +0 -224
  36. package/coverage/lcov-report/block-navigation.js +0 -87
  37. package/coverage/lcov-report/components/ContainerCreationPrompt.jsx.html +0 -394
  38. package/coverage/lcov-report/components/PromptField.jsx.html +0 -229
  39. package/coverage/lcov-report/components/SuggestionPanel.jsx.html +0 -268
  40. package/coverage/lcov-report/components/index.html +0 -146
  41. package/coverage/lcov-report/favicon.png +0 -0
  42. package/coverage/lcov-report/helpers/constants.js.html +0 -352
  43. package/coverage/lcov-report/helpers/containerOptionsBuilder.js.html +0 -244
  44. package/coverage/lcov-report/helpers/dockerHubService.js.html +0 -310
  45. package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerActions.js.html +0 -931
  46. package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerList.js.html +0 -307
  47. package/coverage/lcov-report/helpers/dockerService/serviceComponents/index.html +0 -131
  48. package/coverage/lcov-report/helpers/imageNameUtils.js.html +0 -181
  49. package/coverage/lcov-report/helpers/index.html +0 -206
  50. package/coverage/lcov-report/helpers/logger.js.html +0 -424
  51. package/coverage/lcov-report/helpers/safeCall.js.html +0 -139
  52. package/coverage/lcov-report/helpers/validationHelpers.js.html +0 -346
  53. package/coverage/lcov-report/hooks/creation/index.html +0 -146
  54. package/coverage/lcov-report/hooks/creation/useContainerActions.js.html +0 -313
  55. package/coverage/lcov-report/hooks/creation/useContainerCreation.js.html +0 -982
  56. package/coverage/lcov-report/hooks/creation/useLogsViewer.js.html +0 -238
  57. package/coverage/lcov-report/hooks/debug/index.html +0 -116
  58. package/coverage/lcov-report/hooks/debug/useDebugLogs.js.html +0 -187
  59. package/coverage/lcov-report/hooks/index.html +0 -161
  60. package/coverage/lcov-report/hooks/navigation/index.html +0 -116
  61. package/coverage/lcov-report/hooks/navigation/useContainerSelection.js.html +0 -169
  62. package/coverage/lcov-report/hooks/useContainerCommandRouter.js.html +0 -454
  63. package/coverage/lcov-report/hooks/useControls.js.html +0 -868
  64. package/coverage/lcov-report/hooks/useEraseConfirmation.js.html +0 -202
  65. package/coverage/lcov-report/hooks/useExitHandler.js.html +0 -292
  66. package/coverage/lcov-report/index.html +0 -176
  67. package/coverage/lcov-report/prettify.css +0 -1
  68. package/coverage/lcov-report/prettify.js +0 -2
  69. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  70. package/coverage/lcov-report/sorter.js +0 -210
  71. package/coverage/lcov.info +0 -1163
  72. package/fix-imports.cjs +0 -54
  73. package/jest.config.cjs +0 -14
  74. package/src/App.jsx +0 -102
  75. package/src/components/ContainerCreationPrompt.jsx +0 -107
  76. package/src/components/ContainerList.jsx +0 -32
  77. package/src/components/ContainerRow.jsx +0 -84
  78. package/src/components/ContainerSection.jsx +0 -24
  79. package/src/components/ControlsHUD.jsx +0 -66
  80. package/src/components/Footer.jsx +0 -15
  81. package/src/components/Header.jsx +0 -41
  82. package/src/components/LogViewer.jsx +0 -36
  83. package/src/components/MessageFeedback.jsx +0 -25
  84. package/src/components/PromptField.jsx +0 -48
  85. package/src/components/StatsBar.jsx +0 -70
  86. package/src/components/SuggestionPanel.jsx +0 -61
  87. package/src/components/UsageMenu.jsx +0 -52
  88. package/src/helpers/appInfo.js +0 -23
  89. package/src/helpers/constants.js +0 -89
  90. package/src/helpers/containerOptionsBuilder.js +0 -53
  91. package/src/helpers/dockerHubService.js +0 -75
  92. package/src/helpers/dockerService/dockerService.js +0 -28
  93. package/src/helpers/dockerService/serviceComponents/containerActions.js +0 -282
  94. package/src/helpers/dockerService/serviceComponents/containerList.js +0 -74
  95. package/src/helpers/dockerService/serviceComponents/containerLogs.js +0 -60
  96. package/src/helpers/dockerService/serviceComponents/containerStats.js +0 -69
  97. package/src/helpers/dockerService/serviceComponents/imageUtils.js +0 -32
  98. package/src/helpers/exitWithMessage.js +0 -32
  99. package/src/helpers/imageNameUtils.js +0 -32
  100. package/src/helpers/logger.js +0 -112
  101. package/src/helpers/safeCall.js +0 -18
  102. package/src/helpers/validationHelpers.js +0 -87
  103. package/src/hooks/creation/useContainerActions.js +0 -89
  104. package/src/hooks/creation/useContainerCreation.js +0 -377
  105. package/src/hooks/creation/useLogsViewer.js +0 -51
  106. package/src/hooks/debug/useDebugLogs.js +0 -34
  107. package/src/hooks/navigation/useContainerSelection.js +0 -28
  108. package/src/hooks/useContainerCommandRouter.js +0 -123
  109. package/src/hooks/useContainerStats.js +0 -44
  110. package/src/hooks/useContainers.js +0 -26
  111. package/src/hooks/useControls.js +0 -263
  112. package/src/hooks/useEraseConfirmation.js +0 -39
  113. package/src/hooks/useExitHandler.js +0 -69
  114. package/src/index.js +0 -20
  115. package/test/ContainerCreationPrompt.dom.test.js +0 -313
  116. package/test/ControlsHUD.dom.test.js +0 -125
  117. package/test/SuggestionPanel.dom.test.js +0 -83
  118. package/test/constants.test.js +0 -41
  119. package/test/containerActions.test.js +0 -229
  120. package/test/containerList.test.js +0 -79
  121. package/test/containerOptionsBuilder.test.js +0 -36
  122. package/test/dockerHubService.test.js +0 -213
  123. package/test/jest.setup.js +0 -7
  124. package/test/logger.test.js +0 -58
  125. package/test/resolveImageTag.test.js +0 -26
  126. package/test/safeCall.test.js +0 -25
  127. package/test/useContainerCreation.dom.test.js +0 -567
  128. package/test/useContainerCreationHub.dom.test.js +0 -184
  129. package/test/useControls.dom.test.js +0 -519
  130. package/test/validationHelpers.test.js +0 -111
@@ -1,123 +0,0 @@
1
- import { useCallback } from 'react';
2
-
3
- /**
4
- * Hook responsible solely for routing Docker action keystrokes to the correct handler.
5
- * Has no domain state of its own — it delegates every side-effect to its dependencies.
6
- *
7
- * Handled keys:
8
- * i — start container
9
- * p — stop container
10
- * r — restart container
11
- * e — request erase confirmation
12
- * l — open logs viewer and start log stream
13
- * c — open container creation wizard (reset + activate)
14
- * d — toggle debug log panel
15
- *
16
- * @param {Object} params
17
- * @param {Object} params.actions - API from useContainerActions
18
- * @param {Array<Object>} params.containers - Current list of Docker containers
19
- * @param {number} params.selected - Index of the currently selected container
20
- * @param {Object} params.creation - API from useContainerCreation (must expose resetCreation)
21
- * @param {Object} params.logsViewer - API from useLogsViewer
22
- * @param {Function} params.startLogsStream - Starts the log stream for a container id
23
- * @param {Function} params.onStartErase - Callback: activates confirmErase state
24
- * @param {Function} params.onToggleDebug - Callback: toggles showDebugLogs state
25
- * @param {Function} params.onStartCreate - Callback: sets creatingContainer = true
26
- * @returns {{ handleDockerCommands: Function }}
27
- */
28
- export function useContainerCommandRouter({
29
- actions,
30
- containers,
31
- selected,
32
- creation,
33
- logsViewer,
34
- startLogsStream,
35
- onStartErase,
36
- onToggleDebug,
37
- onStartCreate,
38
- }) {
39
- const handleDockerCommands = useCallback(
40
- (input) => {
41
- const container = containers[selected];
42
-
43
- if (input === 'i') {
44
- actions.handleAction({
45
- actionFn: async (id) => await actions.startContainer(id),
46
- actionLabel: 'Starting',
47
- selected,
48
- stateCheck: (c) =>
49
- (c.state === 'running' || c.status === 'running') &&
50
- 'Container is already running.',
51
- });
52
- return true;
53
- }
54
-
55
- if (input === 'p') {
56
- actions.handleAction({
57
- actionFn: async (id) => await actions.stopContainer(id),
58
- actionLabel: 'Stopping',
59
- selected,
60
- stateCheck: (c) =>
61
- (c.state === 'exited' ||
62
- c.status === 'exited' ||
63
- c.state === 'stopped' ||
64
- c.status === 'stopped') &&
65
- 'Container is already stopped.',
66
- });
67
- return true;
68
- }
69
-
70
- if (input === 'r') {
71
- actions.handleAction({
72
- actionFn: async (id) => await actions.restartContainer(id),
73
- actionLabel: 'Restarting',
74
- selected,
75
- });
76
- return true;
77
- }
78
-
79
- if (input === 'e') {
80
- if (!container) {
81
- return true;
82
- }
83
- onStartErase();
84
- return true;
85
- }
86
-
87
- if (input === 'l') {
88
- if (!container) {
89
- return true;
90
- }
91
- logsViewer.openLogs();
92
- startLogsStream(container.id);
93
- return true;
94
- }
95
-
96
- if (input === 'c') {
97
- creation.resetCreation();
98
- onStartCreate();
99
- return true;
100
- }
101
-
102
- if (input === 'd') {
103
- onToggleDebug();
104
- return true;
105
- }
106
-
107
- return false;
108
- },
109
- [
110
- actions,
111
- containers,
112
- creation,
113
- logsViewer,
114
- onStartCreate,
115
- onStartErase,
116
- onToggleDebug,
117
- selected,
118
- startLogsStream,
119
- ]
120
- );
121
-
122
- return { handleDockerCommands };
123
- }
@@ -1,44 +0,0 @@
1
- import { useState, useEffect } from 'react';
2
- import { getStats } from '../helpers/dockerService/serviceComponents/containerStats.js';
3
- import { REFRESH_INTERVALS } from '../helpers/constants.js';
4
-
5
- const DEFAULT_STATS = { cpuPercent: 0, memPercent: 0, netIO: { rx: 0, tx: 0 } };
6
-
7
- export function useContainerStats(id, state) {
8
- const [stats, setStats] = useState(DEFAULT_STATS);
9
- const [statsError, setStatsError] = useState('');
10
-
11
- useEffect(() => {
12
- if (state !== 'running') {
13
- setStats(DEFAULT_STATS);
14
- setStatsError('');
15
- return;
16
- }
17
-
18
- let isMounted = true;
19
-
20
- const fetchStats = async () => {
21
- try {
22
- const s = await getStats(id);
23
- if (isMounted) {
24
- setStats(s);
25
- setStatsError('');
26
- }
27
- } catch (err) {
28
- if (isMounted) {
29
- setStats(DEFAULT_STATS);
30
- setStatsError('Error fetching stats');
31
- }
32
- }
33
- };
34
-
35
- fetchStats();
36
- const timer = setInterval(fetchStats, REFRESH_INTERVALS.CONTAINER_STATS);
37
- return () => {
38
- isMounted = false;
39
- clearInterval(timer);
40
- };
41
- }, [id, state]);
42
-
43
- return { stats, statsError };
44
- }
@@ -1,26 +0,0 @@
1
- /**
2
- * React hook to manage Docker containers state.
3
- *
4
- * @returns {{containers: Array<Object>}} An object containing the current
5
- * containers array. The hook starts a periodic refresh and fetches the
6
- * container list immediately on mount.
7
- * @example
8
- * // Use in a component
9
- * const { containers } = useContainers();
10
- */
11
- import React, { useState, useEffect } from 'react';
12
- import { getContainers } from '../helpers/dockerService/serviceComponents/containerList';
13
- import { REFRESH_INTERVALS } from '../helpers/constants.js';
14
-
15
- export function useContainers() {
16
- const [containers, setContainers] = useState([]);
17
-
18
- useEffect(() => {
19
- const fetch = async () => setContainers(await getContainers());
20
- fetch();
21
- const timer = setInterval(fetch, REFRESH_INTERVALS.CONTAINER_LIST);
22
- return () => clearInterval(timer);
23
- }, []);
24
-
25
- return { containers };
26
- }
@@ -1,263 +0,0 @@
1
- import React from 'react';
2
- import { useInput } from 'ink';
3
- import { useContainerActions } from './creation/useContainerActions';
4
- import { useContainerCreation } from './creation/useContainerCreation';
5
- import { useLogsViewer } from './creation/useLogsViewer';
6
- import { useContainerSelection } from './navigation/useContainerSelection';
7
- import { useDebugLogs } from './debug/useDebugLogs';
8
- import { useEraseConfirmation } from './useEraseConfirmation';
9
- import { useExitHandler } from './useExitHandler';
10
- import { useContainerCommandRouter } from './useContainerCommandRouter';
11
- import { getLogsStream } from '../helpers/dockerService/serviceComponents/containerLogs.js';
12
- import { createContainer as svcCreateContainer } from '../helpers/dockerService/serviceComponents/containerActions.js';
13
- import { buildContainerOptions } from '../helpers/containerOptionsBuilder.js';
14
- import { DB_IMAGES } from '../helpers/constants.js';
15
-
16
- // Principal hook to manage user inputs and control the app state
17
- /**
18
- * Main hook that wires user input, creation, actions and logs viewing.
19
- * It coordinates the modular hooks and exposes a compact API consumed by the App.
20
- *
21
- * @param {Array<Object>} containers - Current list of Docker containers
22
- * @returns {Object} controls - API for the App component
23
- */
24
- export function useControls(containers = [], overrides = {}) {
25
- const [creatingContainer, setCreatingContainer] = React.useState(false);
26
-
27
- // — Modular hooks —
28
- const actions = useContainerActions({ containers });
29
-
30
- const creation = useContainerCreation({
31
- onCreate: async ({ imageName, containerName, portInput, envInput }) => {
32
- const options = buildContainerOptions({
33
- imageName,
34
- containerName,
35
- portInput,
36
- envInput,
37
- });
38
- actions.setTimedMessage(`Creating container ${imageName}...`, 'yellow');
39
- try {
40
- const { id, ports } = await svcCreateContainer(imageName, options);
41
- let portMsg = '';
42
- if (ports && ports.length) {
43
- portMsg =
44
- ' | Ports: ' +
45
- ports
46
- .map((p) => `${p.hostPort}→${p.containerPort}/${p.protocol}`)
47
- .join(', ');
48
- }
49
- actions.setTimedMessage(`Created container ${id}${portMsg}`, 'green');
50
- } catch (err) {
51
- actions.setTimedMessage(`Error creating container: ${err.message}`, 'red');
52
- } finally {
53
- setCreatingContainer(false);
54
- }
55
- },
56
- onCancel: () => setCreatingContainer(false),
57
- dbImages: DB_IMAGES,
58
- });
59
-
60
- const logsViewer = useLogsViewer();
61
- const selection = useContainerSelection(containers.length);
62
- const debugLogs = useDebugLogs();
63
-
64
- // Allow overrides for testability (e.g. injecting a mock triggerHubSearch)
65
- const triggerHubSearch = overrides.triggerHubSearch ?? creation.triggerHubSearch;
66
- const isSearchingHub = overrides.isSearchingHub ?? creation.isSearchingHub;
67
- const insertNextSuggestedEnv = overrides.insertNextSuggestedEnv !== undefined
68
- ? overrides.insertNextSuggestedEnv
69
- : creation.insertNextSuggestedEnv;
70
-
71
- const eraseConfirmation = useEraseConfirmation({
72
- onConfirm: () => {
73
- actions.handleAction({
74
- actionFn: async (id) => await actions.removeContainer(id),
75
- actionLabel: 'Erasing',
76
- selected: selection.selected,
77
- });
78
- actions.setMessageColor('yellow');
79
- },
80
- onCancel: () => {
81
- actions.setMessage('');
82
- actions.setMessageColor('');
83
- },
84
- });
85
-
86
- /**
87
- * Pipe container logs into the viewer while applying a hard limit.
88
- * @param {string} containerId - Container identifier used by Docker.
89
- */
90
- const startLogsStream = React.useCallback(
91
- (containerId) => {
92
- getLogsStream(
93
- containerId,
94
- (data) =>
95
- logsViewer.setLogs((prev) => {
96
- const newLogs = [...prev, ...data.split('\n').filter(Boolean)];
97
- return newLogs.slice(-1000);
98
- }),
99
- () => {},
100
- (err) =>
101
- logsViewer.setLogs((prev) => [...prev, `Error: ${err.message}`])
102
- );
103
- },
104
- [logsViewer]
105
- );
106
-
107
- const commandRouter = useContainerCommandRouter({
108
- actions,
109
- containers,
110
- selected: selection.selected,
111
- creation,
112
- logsViewer,
113
- startLogsStream,
114
- onStartErase: () => {
115
- eraseConfirmation.startErase();
116
- actions.setMessage(
117
- 'Are you sure you want to erase this container? (y/n)'
118
- );
119
- actions.setMessageColor('yellow');
120
- },
121
- onToggleDebug: () => debugLogs.setShowDebugLogs((prev) => !prev),
122
- onStartCreate: () => setCreatingContainer(true),
123
- });
124
-
125
- const exitHandler = useExitHandler({
126
- onBeforeExit: () => {
127
- actions.setMessage('Exiting...');
128
- actions.setMessageColor('yellow');
129
- logsViewer.closeLogs();
130
- debugLogs.setShowDebugLogs(false);
131
- },
132
- });
133
-
134
- /**
135
- * Route keystrokes to the container creation wizard.
136
- */
137
- const processCreationInput = React.useCallback(
138
- (input, key) => {
139
- const step = creation.step;
140
-
141
- const removeLastChar = (setter) =>
142
- setter((value) => (value || '').slice(0, -1));
143
- const appendChar = (setter, current, ch) => setter((current || '') + ch);
144
-
145
- if (key.escape) {
146
- creation.cancelCreation();
147
- setCreatingContainer(false);
148
- return;
149
- }
150
-
151
- if (input === '\r' || input === '\n') {
152
- // If on step 0 with a focused suggestion, apply it instead of advancing
153
- if (step === 0 && creation.selectedSuggestionIndex >= 0) {
154
- creation.applyFocusedSuggestion();
155
- return;
156
- }
157
- creation.nextStep();
158
- return;
159
- }
160
-
161
- // Tab on step 0: trigger Docker Hub search (with guards)
162
- if (key.tab) {
163
- if (step === 0) {
164
- if (!isSearchingHub && (creation.imageName || '').trim() !== '') {
165
- triggerHubSearch();
166
- }
167
- } else if (step === 3) {
168
- insertNextSuggestedEnv?.();
169
- }
170
- return;
171
- }
172
-
173
- // Arrow keys on step 0: navigate suggestion list
174
- if (step === 0 && creation.suggestions.length > 0) {
175
- if (key.upArrow) {
176
- creation.moveSuggestionSelection(-1);
177
- return;
178
- }
179
- if (key.downArrow) {
180
- creation.moveSuggestionSelection(1);
181
- return;
182
- }
183
- }
184
-
185
- if (key.backspace || key.delete) {
186
- if (step === 0) {
187
- const newValue = (creation.imageName || '').slice(0, -1);
188
- creation.updateImageInput(newValue);
189
- }
190
- if (step === 1) removeLastChar(creation.setContainerName);
191
- if (step === 2) removeLastChar(creation.setPortInput);
192
- if (step === 3) removeLastChar(creation.setEnvInput);
193
- return;
194
- }
195
-
196
- if (input && input.length === 1 && !key.ctrl && !key.meta) {
197
- if (step === 0) {
198
- creation.updateImageInput((creation.imageName || '') + input);
199
- }
200
- if (step === 1)
201
- appendChar(creation.setContainerName, creation.containerName, input);
202
- if (step === 2)
203
- appendChar(creation.setPortInput, creation.portInput, input);
204
- if (step === 3)
205
- appendChar(creation.setEnvInput, creation.envInput, input);
206
- }
207
- },
208
- [creation, setCreatingContainer, triggerHubSearch, isSearchingHub, insertNextSuggestedEnv]
209
- );
210
-
211
- // Single keyboard entry point
212
- useInput((input, key) => {
213
- if (eraseConfirmation.confirmErase) {
214
- eraseConfirmation.processEraseConfirmation(input, key);
215
- return;
216
- }
217
-
218
- if (logsViewer.showLogs) {
219
- if (input === 'q' || key.escape) {
220
- logsViewer.closeLogs();
221
- }
222
- return;
223
- }
224
-
225
- if (creatingContainer) {
226
- processCreationInput(input, key);
227
- return;
228
- }
229
-
230
- if (debugLogs.showDebugLogs && key.escape) {
231
- debugLogs.setShowDebugLogs(false);
232
- return;
233
- }
234
-
235
- commandRouter.handleDockerCommands(input);
236
- exitHandler.handleExitCommand(input);
237
- selection.handleNavigation(input, key);
238
- });
239
-
240
- return {
241
- selected: selection.selected,
242
- setSelected: selection.setSelected,
243
- message: creatingContainer ? creation.message : actions.message,
244
- messageColor: creatingContainer
245
- ? creation.messageColor
246
- : actions.messageColor,
247
- showLogs: logsViewer.showLogs,
248
- logs: logsViewer.logs,
249
- exitLogs: logsViewer.closeLogs,
250
- creatingContainer,
251
- creationStep: creation.step,
252
- imageNameInput: creation.imageName,
253
- containerNameInput: creation.containerName,
254
- portInput: creation.portInput,
255
- envInput: creation.envInput,
256
- creation,
257
- actions,
258
- logsViewer,
259
- confirmErase: eraseConfirmation.confirmErase,
260
- showDebugLogs: debugLogs.showDebugLogs,
261
- debugLogs: debugLogs.debugLogs,
262
- };
263
- }
@@ -1,39 +0,0 @@
1
- import { useState, useCallback } from 'react';
2
-
3
- /**
4
- * Hook to manage the erase confirmation dialog flow.
5
- *
6
- * Encapsulates the `confirmErase` state and the input handler that resolves it.
7
- * The caller is responsible for what happens on confirm/cancel via the callbacks.
8
- *
9
- * @param {Object} params
10
- * @param {Function} params.onConfirm - Called when the user presses y/Y. Should trigger the erase action.
11
- * @param {Function} params.onCancel - Called when the user presses n/N or Escape. Should clean up messages.
12
- * @returns {{ confirmErase: boolean, startErase: Function, processEraseConfirmation: Function }}
13
- */
14
- export function useEraseConfirmation({ onConfirm, onCancel }) {
15
- const [confirmErase, setConfirmErase] = useState(false);
16
-
17
- const startErase = useCallback(() => {
18
- setConfirmErase(true);
19
- }, []);
20
-
21
- const processEraseConfirmation = useCallback(
22
- (input, key) => {
23
- if (input === 'y' || input === 'Y') {
24
- onConfirm();
25
- setConfirmErase(false);
26
- return;
27
- }
28
-
29
- if (input === 'n' || input === 'N' || key.escape) {
30
- setConfirmErase(false);
31
- onCancel();
32
- return;
33
- }
34
- },
35
- [onConfirm, onCancel]
36
- );
37
-
38
- return { confirmErase, startErase, processEraseConfirmation };
39
- }
@@ -1,69 +0,0 @@
1
- import { useCallback } from 'react';
2
- import { useApp } from 'ink';
3
- import { EXIT_DELAY } from '../helpers/constants.js';
4
-
5
- /**
6
- * Hook that encapsulates the application exit sequence triggered by pressing `q`.
7
- *
8
- * Responsibilities:
9
- * - Detect the `q` keypress
10
- * - Invoke an optional `onBeforeExit` callback for resource cleanup (e.g. closing logs)
11
- * - Clear the terminal after a short delay
12
- * - Call Ink's `exit()` and, outside of test environments, `process.exit(0)`
13
- *
14
- * @param {Object} [options]
15
- * @param {Function} [options.onBeforeExit] - Optional callback invoked synchronously
16
- * before the exit delay starts. Use it to close logs, reset UI state, etc.
17
- * @returns {{ handleExitCommand: Function }}
18
- */
19
- export function useExitHandler({ onBeforeExit } = {}) {
20
- const { exit } = useApp();
21
-
22
- /**
23
- * Handle the quit command.
24
- *
25
- * Mirrors the exact logic of `handleExitCommand` in `useControls.js`:
26
- * 1. Guard: return false if input is not 'q'
27
- * 2. Call onBeforeExit (replaces inline logsViewer.closeLogs + setShowDebugLogs(false))
28
- * 3. After EXIT_DELAY: clear terminal, call exit(), then process.exit(0) outside tests
29
- *
30
- * @param {string} input - Raw character input from Ink's useInput
31
- * @returns {boolean} True when the input was consumed, false otherwise
32
- */
33
- const handleExitCommand = useCallback(
34
- (input) => {
35
- if (input !== 'q') {
36
- return false;
37
- }
38
-
39
- if (typeof onBeforeExit === 'function') {
40
- onBeforeExit();
41
- }
42
-
43
- const clearTerminal = () => {
44
- try {
45
- if (process.stdout && process.stdout.isTTY) {
46
- process.stdout.write('\u001Bc');
47
- } else {
48
- console.clear();
49
- }
50
- } catch (err) {
51
- // Ignore clear errors to avoid blocking exit.
52
- }
53
- };
54
-
55
- setTimeout(() => {
56
- clearTerminal();
57
- exit();
58
- if (process.env.NODE_ENV !== 'test') {
59
- setTimeout(() => process.exit(0), 50);
60
- }
61
- }, EXIT_DELAY);
62
-
63
- return true;
64
- },
65
- [exit, onBeforeExit]
66
- );
67
-
68
- return { handleExitCommand };
69
- }
package/src/index.js DELETED
@@ -1,20 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Entry point for the CDD CLI application.
4
- *
5
- * @module index
6
- * @example
7
- * // Run the CLI
8
- * node index.js
9
- */
10
-
11
- import React from 'react';
12
- import { render } from 'ink';
13
- import App from './App.jsx';
14
- import { logger } from './helpers/logger.js';
15
-
16
- process.on('uncaughtException', (err) => logger.error('Uncaught exception', err));
17
- process.on('unhandledRejection', (reason) => logger.error('Unhandled rejection', reason));
18
-
19
- console.clear();
20
- render(<App />);