cdd-cli 3.2.2 → 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 (126) hide show
  1. package/README.es.md +222 -0
  2. package/README.md +108 -26
  3. package/dist/App.js +7 -1
  4. package/dist/components/ContainerCreationPrompt.js +26 -10
  5. package/dist/components/ControlsHUD.js +106 -0
  6. package/dist/components/SuggestionPanel.js +16 -7
  7. package/dist/components/UsageMenu.js +1 -1
  8. package/dist/helpers/appState.js +26 -0
  9. package/dist/helpers/constants.js +60 -21
  10. package/dist/helpers/dockerHubService.js +111 -0
  11. package/dist/helpers/dockerService/serviceComponents/containerExec.js +211 -0
  12. package/dist/helpers/logger.js +1 -1
  13. package/dist/hooks/creation/useContainerActions.js +22 -5
  14. package/dist/hooks/creation/useContainerCreation.js +414 -107
  15. package/dist/hooks/useContainerCommandRouter.js +13 -3
  16. package/dist/hooks/useControls.js +28 -7
  17. package/dist/hooks/useShellMode.js +101 -0
  18. package/dist/index.js +10 -1
  19. package/package.json +4 -1
  20. package/.atl/skill-registry.md +0 -30
  21. package/.editorconfig +0 -12
  22. package/.eslintrc.json +0 -30
  23. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -30
  24. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
  25. package/.github/PULL_REQUEST_TEMPLATE/pull_request.md +0 -14
  26. package/.github/workflows/ci.yml +0 -32
  27. package/.github/workflows/docs.yml +0 -42
  28. package/.prettierignore +0 -4
  29. package/.prettierrc +0 -8
  30. package/CHANGELOG.md +0 -165
  31. package/CODE_OF_CONDUCT.md +0 -12
  32. package/CONTRIBUTING.md +0 -35
  33. package/SECURITY.md +0 -5
  34. package/__mocks__/ink.cjs +0 -17
  35. package/_config.yml +0 -13
  36. package/babel.config.cjs +0 -23
  37. package/coverage/clover.xml +0 -694
  38. package/coverage/coverage-final.json +0 -21
  39. package/coverage/lcov-report/base.css +0 -224
  40. package/coverage/lcov-report/block-navigation.js +0 -87
  41. package/coverage/lcov-report/components/ContainerCreationPrompt.jsx.html +0 -376
  42. package/coverage/lcov-report/components/PromptField.jsx.html +0 -229
  43. package/coverage/lcov-report/components/SuggestionPanel.jsx.html +0 -244
  44. package/coverage/lcov-report/components/index.html +0 -146
  45. package/coverage/lcov-report/favicon.png +0 -0
  46. package/coverage/lcov-report/helpers/constants.js.html +0 -298
  47. package/coverage/lcov-report/helpers/containerOptionsBuilder.js.html +0 -244
  48. package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerActions.js.html +0 -931
  49. package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerList.js.html +0 -307
  50. package/coverage/lcov-report/helpers/dockerService/serviceComponents/index.html +0 -131
  51. package/coverage/lcov-report/helpers/imageNameUtils.js.html +0 -181
  52. package/coverage/lcov-report/helpers/index.html +0 -191
  53. package/coverage/lcov-report/helpers/logger.js.html +0 -424
  54. package/coverage/lcov-report/helpers/safeCall.js.html +0 -139
  55. package/coverage/lcov-report/helpers/validationHelpers.js.html +0 -346
  56. package/coverage/lcov-report/hooks/creation/index.html +0 -146
  57. package/coverage/lcov-report/hooks/creation/useContainerActions.js.html +0 -313
  58. package/coverage/lcov-report/hooks/creation/useContainerCreation.js.html +0 -805
  59. package/coverage/lcov-report/hooks/creation/useLogsViewer.js.html +0 -238
  60. package/coverage/lcov-report/hooks/debug/index.html +0 -116
  61. package/coverage/lcov-report/hooks/debug/useDebugLogs.js.html +0 -187
  62. package/coverage/lcov-report/hooks/index.html +0 -161
  63. package/coverage/lcov-report/hooks/navigation/index.html +0 -116
  64. package/coverage/lcov-report/hooks/navigation/useContainerSelection.js.html +0 -169
  65. package/coverage/lcov-report/hooks/useContainerCommandRouter.js.html +0 -454
  66. package/coverage/lcov-report/hooks/useControls.js.html +0 -826
  67. package/coverage/lcov-report/hooks/useEraseConfirmation.js.html +0 -202
  68. package/coverage/lcov-report/hooks/useExitHandler.js.html +0 -292
  69. package/coverage/lcov-report/index.html +0 -206
  70. package/coverage/lcov-report/prettify.css +0 -1
  71. package/coverage/lcov-report/prettify.js +0 -2
  72. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  73. package/coverage/lcov-report/sorter.js +0 -210
  74. package/coverage/lcov.info +0 -1402
  75. package/fix-imports.cjs +0 -54
  76. package/jest.config.cjs +0 -14
  77. package/src/App.jsx +0 -96
  78. package/src/components/ContainerCreationPrompt.jsx +0 -97
  79. package/src/components/ContainerList.jsx +0 -32
  80. package/src/components/ContainerRow.jsx +0 -84
  81. package/src/components/ContainerSection.jsx +0 -24
  82. package/src/components/Footer.jsx +0 -15
  83. package/src/components/Header.jsx +0 -41
  84. package/src/components/LogViewer.jsx +0 -36
  85. package/src/components/MessageFeedback.jsx +0 -25
  86. package/src/components/PromptField.jsx +0 -48
  87. package/src/components/StatsBar.jsx +0 -70
  88. package/src/components/SuggestionPanel.jsx +0 -53
  89. package/src/components/UsageMenu.jsx +0 -52
  90. package/src/helpers/appInfo.js +0 -23
  91. package/src/helpers/constants.js +0 -71
  92. package/src/helpers/containerOptionsBuilder.js +0 -53
  93. package/src/helpers/dockerService/dockerService.js +0 -28
  94. package/src/helpers/dockerService/serviceComponents/containerActions.js +0 -282
  95. package/src/helpers/dockerService/serviceComponents/containerList.js +0 -74
  96. package/src/helpers/dockerService/serviceComponents/containerLogs.js +0 -60
  97. package/src/helpers/dockerService/serviceComponents/containerStats.js +0 -69
  98. package/src/helpers/dockerService/serviceComponents/imageUtils.js +0 -32
  99. package/src/helpers/exitWithMessage.js +0 -32
  100. package/src/helpers/imageNameUtils.js +0 -32
  101. package/src/helpers/logger.js +0 -113
  102. package/src/helpers/safeCall.js +0 -18
  103. package/src/helpers/validationHelpers.js +0 -87
  104. package/src/hooks/creation/useContainerActions.js +0 -76
  105. package/src/hooks/creation/useContainerCreation.js +0 -240
  106. package/src/hooks/creation/useLogsViewer.js +0 -51
  107. package/src/hooks/debug/useDebugLogs.js +0 -34
  108. package/src/hooks/navigation/useContainerSelection.js +0 -28
  109. package/src/hooks/useContainerCommandRouter.js +0 -123
  110. package/src/hooks/useContainerStats.js +0 -44
  111. package/src/hooks/useContainers.js +0 -26
  112. package/src/hooks/useControls.js +0 -247
  113. package/src/hooks/useEraseConfirmation.js +0 -39
  114. package/src/hooks/useExitHandler.js +0 -69
  115. package/src/index.js +0 -16
  116. package/test/ContainerCreationPrompt.dom.test.js +0 -68
  117. package/test/SuggestionPanel.dom.test.js +0 -53
  118. package/test/constants.test.js +0 -34
  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/jest.setup.js +0 -7
  123. package/test/safeCall.test.js +0 -25
  124. package/test/useContainerCreation.dom.test.js +0 -264
  125. package/test/useControls.dom.test.js +0 -313
  126. package/test/validationHelpers.test.js +0 -111
@@ -1,240 +0,0 @@
1
- import { useState } from 'react';
2
- import {
3
- validatePorts,
4
- validateEnvVars,
5
- } from '../../helpers/validationHelpers.js';
6
- import { IMAGE_PROFILES } from '../../helpers/constants.js';
7
- import { safeCall } from '../../helpers/safeCall.js';
8
-
9
- const MAX_VISIBLE = 6;
10
-
11
- /**
12
- * Custom hook to manage the container creation flow, step by step.
13
- * Handles input, validation, and feedback for each creation step.
14
- *
15
- * @param {Object} params
16
- * @param {Function} params.onCreate - Callback when creation is confirmed
17
- * @param {Function} params.onCancel - Callback when creation is cancelled
18
- * @param {Array<string>} params.dbImages - List of DB image names for env var warning (legacy)
19
- * @param {Object} [params.imageProfiles] - Image profiles map for contextual validation
20
- * @returns {Object} Creation state, setters, and helpers
21
- */
22
- export function useContainerCreation({
23
- onCreate,
24
- onCancel,
25
- dbImages = [],
26
- imageProfiles = IMAGE_PROFILES,
27
- }) {
28
- const [step, setStep] = useState(0); // 0: image, 1: name, 2: ports, 3: env
29
- const [imageName, setImageName] = useState('');
30
- const [containerName, setContainerName] = useState('');
31
- const [portInput, setPortInput] = useState('');
32
- const [envInput, setEnvInput] = useState('');
33
- const [message, setMessage] = useState('');
34
- const [messageColor, setMessageColor] = useState('yellow');
35
-
36
- // Autocomplete state
37
- const [suggestions, setSuggestions] = useState([]);
38
- const [selectedSuggestionIndex, setSelectedSuggestionIndex] = useState(-1);
39
- const [visibleOffset, setVisibleOffset] = useState(0);
40
-
41
- /**
42
- * Updates the image name input and recalculates autocomplete suggestions.
43
- * Resets selectedSuggestionIndex to -1 on every keystroke.
44
- *
45
- * @param {string} value - New raw image name typed by user
46
- */
47
- function updateImageInput(value) {
48
- setImageName(value);
49
- setSelectedSuggestionIndex(-1);
50
- setVisibleOffset(0);
51
- if (!value.trim()) {
52
- setSuggestions([]);
53
- return;
54
- }
55
- const lower = value.toLowerCase();
56
- const matches = Object.keys(imageProfiles).filter((key) =>
57
- key.includes(lower)
58
- );
59
- setSuggestions(matches);
60
- }
61
-
62
- /**
63
- * Moves the suggestion selection up (-1) or down (+1).
64
- * Clamps index to [-1, suggestions.length - 1].
65
- * Adjusts visibleOffset to keep selected item in the visible window.
66
- *
67
- * @param {number} direction - -1 (up) or 1 (down)
68
- */
69
- function moveSuggestionSelection(direction) {
70
- setSelectedSuggestionIndex((prev) => {
71
- const next = Math.max(-1, Math.min(suggestions.length - 1, prev + direction));
72
- // Adjust visibleOffset to keep selected in view
73
- setVisibleOffset((offset) => {
74
- if (next < offset) return Math.max(0, next);
75
- if (next >= offset + MAX_VISIBLE) return next - MAX_VISIBLE + 1;
76
- return offset;
77
- });
78
- return next;
79
- });
80
- }
81
-
82
- /**
83
- * Applies the currently focused suggestion to imageName.
84
- * Does NOT advance the step. Clears suggestions after applying.
85
- */
86
- function applyFocusedSuggestion() {
87
- if (selectedSuggestionIndex < 0 || selectedSuggestionIndex >= suggestions.length) return;
88
- const chosen = suggestions[selectedSuggestionIndex];
89
- setImageName(chosen);
90
- setSuggestions([]);
91
- setSelectedSuggestionIndex(-1);
92
- setVisibleOffset(0);
93
- }
94
-
95
- /**
96
- * Advances to the next step, with validation and feedback.
97
- */
98
- function nextStep() {
99
- if (step === 0) {
100
- if (!imageName.trim()) {
101
- setMessage('Image name cannot be empty.');
102
- setMessageColor('red');
103
- return;
104
- }
105
- setStep(1);
106
- setMessage(
107
- 'Optional: Enter container name or leave empty and press Enter'
108
- );
109
- setMessageColor('yellow');
110
- return;
111
- }
112
- if (step === 1) {
113
- setStep(2);
114
- setMessage(
115
- 'Optional: Enter ports (format 8080:80,443:443) or leave empty and press Enter'
116
- );
117
- setMessageColor('yellow');
118
- return;
119
- }
120
- if (step === 2) {
121
- // Ports are now optional - only validate if provided
122
- if (portInput.trim() && !validatePorts(portInput)) {
123
- setMessage(
124
- 'Port format must be host:container and both must be numbers (e.g. 8080:80)'
125
- );
126
- setMessageColor('red');
127
- return;
128
- }
129
- setStep(3);
130
- const isDb = dbImages.some((db) =>
131
- imageName.trim().toLowerCase().includes(db)
132
- );
133
- // Check if the image has a profile with required env vars
134
- const baseName = imageName
135
- .trim()
136
- .toLowerCase()
137
- .split(':')[0]
138
- .split('/')
139
- .pop();
140
- const profile = imageProfiles[baseName];
141
- if (profile && profile.requiredEnv && profile.requiredEnv.length) {
142
- const suggestedPart =
143
- profile.suggestedEnv && profile.suggestedEnv.length
144
- ? ` | Suggested: ${profile.suggestedEnv.join(', ')}`
145
- : '';
146
- setMessage(
147
- `Required env vars for ${baseName}: ${profile.requiredEnv.join(', ')}. Enter as VAR=val,VAR2=val2${suggestedPart}`
148
- );
149
- setMessageColor('yellow');
150
- } else if (profile && profile.suggestedEnv && profile.suggestedEnv.length) {
151
- setMessage(
152
- `Suggested env vars for ${baseName}: ${profile.suggestedEnv.join(', ')}. Enter as VAR=val,VAR2=val2 or leave empty.`
153
- );
154
- setMessageColor('yellow');
155
- } else if (isDb) {
156
- setMessage(
157
- 'Warning: This image usually requires environment variables (e.g. MYSQL_ROOT_PASSWORD=my-secret-pw for MySQL, POSTGRES_PASSWORD=yourpassword for Postgres). Enter them as VAR=val,VAR2=val2 or leave empty and press Enter.'
158
- );
159
- setMessageColor('yellow');
160
- } else {
161
- setMessage(
162
- 'Optional: Enter environment variables (format VAR1=val1,VAR2=val2) or leave empty and press Enter'
163
- );
164
- setMessageColor('yellow');
165
- }
166
- return;
167
- }
168
- if (step === 3) {
169
- // Contextual env validation using image profiles
170
- const result = validateEnvVars(envInput, imageName, imageProfiles);
171
- if (!result.valid) {
172
- setMessage(result.errors.join(' | '));
173
- setMessageColor('red');
174
- return;
175
- }
176
- // Final step: call onCreate with all data (safely)
177
- safeCall(onCreate, { imageName, containerName, portInput, envInput });
178
- }
179
- }
180
-
181
- /**
182
- * Cancels the creation process and resets state.
183
- */
184
- function cancelCreation() {
185
- setStep(0);
186
- setImageName('');
187
- setContainerName('');
188
- setPortInput('');
189
- setEnvInput('');
190
- setMessage('');
191
- setMessageColor('yellow');
192
- setSuggestions([]);
193
- setSelectedSuggestionIndex(-1);
194
- setVisibleOffset(0);
195
- safeCall(onCancel);
196
- }
197
-
198
- /**
199
- * Resets all creation state to initial values without triggering onCancel.
200
- * Used by the command router when the user presses 'c' to open the creation wizard.
201
- */
202
- function resetCreation() {
203
- setStep(0);
204
- setImageName('');
205
- setContainerName('');
206
- setPortInput('');
207
- setEnvInput('');
208
- setMessage('Insert the name of the image to create: ');
209
- setMessageColor('yellow');
210
- setSuggestions([]);
211
- setSelectedSuggestionIndex(-1);
212
- setVisibleOffset(0);
213
- }
214
-
215
- return {
216
- step,
217
- setStep,
218
- imageName,
219
- setImageName,
220
- containerName,
221
- setContainerName,
222
- portInput,
223
- setPortInput,
224
- envInput,
225
- setEnvInput,
226
- message,
227
- setMessage,
228
- messageColor,
229
- setMessageColor,
230
- suggestions,
231
- selectedSuggestionIndex,
232
- visibleOffset,
233
- updateImageInput,
234
- moveSuggestionSelection,
235
- applyFocusedSuggestion,
236
- nextStep,
237
- cancelCreation,
238
- resetCreation,
239
- };
240
- }
@@ -1,51 +0,0 @@
1
- import { useState, useRef } from 'react';
2
- import { safeCall } from '../../helpers/safeCall.js';
3
-
4
- /**
5
- * Custom hook to manage the logs viewer state and stream reference.
6
- * Handles showing/hiding logs and storing log lines.
7
- *
8
- * @returns {Object} Logs viewer state and helpers
9
- * @property {boolean} showLogs - Whether the logs viewer is open
10
- * @property {function} setShowLogs - Setter for showLogs
11
- * @property {Array<string>} logs - Array of log lines
12
- * @property {function} setLogs - Setter for logs
13
- * @property {object} logsStreamRef - Ref to the logs stream (for cleanup)
14
- * @property {function} openLogs - Helper to open logs for a container
15
- * @property {function} closeLogs - Helper to close logs and cleanup
16
- */
17
- export function useLogsViewer() {
18
- const [showLogs, setShowLogs] = useState(false);
19
- const [logs, setLogs] = useState([]);
20
- const logsStreamRef = useRef(null);
21
-
22
- /**
23
- * Opens the logs viewer and resets logs.
24
- */
25
- function openLogs() {
26
- setShowLogs(true);
27
- setLogs([]);
28
- }
29
-
30
- /**
31
- * Closes the logs viewer and cleans up the stream.
32
- */
33
- function closeLogs() {
34
- setShowLogs(false);
35
- setLogs([]);
36
- if (logsStreamRef.current) {
37
- safeCall(logsStreamRef.current.destroy?.bind(logsStreamRef.current));
38
- logsStreamRef.current = null;
39
- }
40
- }
41
-
42
- return {
43
- showLogs,
44
- setShowLogs,
45
- logs,
46
- setLogs,
47
- logsStreamRef,
48
- openLogs,
49
- closeLogs,
50
- };
51
- }
@@ -1,34 +0,0 @@
1
- import { useState, useEffect } from 'react';
2
- import { logger } from '../../helpers/logger.js';
3
-
4
- export function useDebugLogs() {
5
- const [showDebugLogs, setShowDebugLogs] = useState(false);
6
- const [debugLogs, setDebugLogs] = useState([]);
7
-
8
- useEffect(() => {
9
- const unsubscribe = logger.subscribe((entry) => {
10
- setDebugLogs((prev) => {
11
- const extras = (entry.args || [])
12
- .map((arg) => {
13
- if (typeof arg === 'string') return arg;
14
- if (typeof arg === 'number' || typeof arg === 'boolean')
15
- return String(arg);
16
- try {
17
- return JSON.stringify(arg);
18
- } catch (err) {
19
- return '[unserializable]';
20
- }
21
- })
22
- .filter(Boolean);
23
- const line = extras.length
24
- ? `${entry.formatted} ${extras.join(' ')}`
25
- : entry.formatted;
26
- const next = [...prev, line];
27
- return next.slice(-200);
28
- });
29
- });
30
- return unsubscribe;
31
- }, []);
32
-
33
- return { showDebugLogs, setShowDebugLogs, debugLogs };
34
- }
@@ -1,28 +0,0 @@
1
- import { useState, useCallback } from 'react';
2
-
3
- export function useContainerSelection(total) {
4
- const [selected, setSelected] = useState(0);
5
-
6
- const handleNavigation = useCallback(
7
- (input, key) => {
8
- if (total === 0) {
9
- return false;
10
- }
11
-
12
- if (key.upArrow) {
13
- setSelected((i) => (i === 0 ? total - 1 : i - 1));
14
- return true;
15
- }
16
-
17
- if (key.downArrow) {
18
- setSelected((i) => (i === total - 1 ? 0 : i + 1));
19
- return true;
20
- }
21
-
22
- return false;
23
- },
24
- [total]
25
- );
26
-
27
- return { selected, setSelected, handleNavigation };
28
- }
@@ -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
- onStartCreate();
98
- creation.resetCreation();
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
- }