cdd-cli 3.2.0 → 3.2.2

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 (120) hide show
  1. package/.atl/skill-registry.md +30 -0
  2. package/.editorconfig +12 -0
  3. package/.eslintrc.json +1 -1
  4. package/.prettierignore +4 -0
  5. package/.prettierrc +8 -0
  6. package/CHANGELOG.md +29 -0
  7. package/SECURITY.md +5 -0
  8. package/__mocks__/ink.cjs +17 -0
  9. package/coverage/clover.xml +526 -436
  10. package/coverage/coverage-final.json +19 -15
  11. package/coverage/lcov-report/components/ContainerCreationPrompt.jsx.html +376 -0
  12. package/coverage/lcov-report/components/PromptField.jsx.html +229 -0
  13. package/coverage/lcov-report/components/SuggestionPanel.jsx.html +244 -0
  14. package/coverage/lcov-report/components/index.html +146 -0
  15. package/coverage/lcov-report/helpers/constants.js.html +80 -17
  16. package/coverage/lcov-report/helpers/containerOptionsBuilder.js.html +41 -8
  17. package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerActions.js.html +162 -84
  18. package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerList.js.html +50 -32
  19. package/coverage/lcov-report/helpers/dockerService/serviceComponents/index.html +21 -21
  20. package/coverage/lcov-report/helpers/imageNameUtils.js.html +181 -0
  21. package/coverage/lcov-report/helpers/index.html +27 -12
  22. package/coverage/lcov-report/helpers/logger.js.html +19 -19
  23. package/coverage/lcov-report/helpers/safeCall.js.html +3 -3
  24. package/coverage/lcov-report/helpers/validationHelpers.js.html +28 -76
  25. package/coverage/lcov-report/hooks/creation/index.html +20 -20
  26. package/coverage/lcov-report/hooks/creation/useContainerActions.js.html +38 -17
  27. package/coverage/lcov-report/hooks/creation/useContainerCreation.js.html +416 -89
  28. package/coverage/lcov-report/hooks/creation/useLogsViewer.js.html +7 -7
  29. package/coverage/lcov-report/hooks/debug/index.html +5 -5
  30. package/coverage/lcov-report/hooks/debug/useDebugLogs.js.html +35 -20
  31. package/coverage/lcov-report/hooks/index.html +40 -40
  32. package/coverage/lcov-report/hooks/navigation/index.html +19 -19
  33. package/coverage/lcov-report/hooks/navigation/useContainerSelection.js.html +38 -29
  34. package/coverage/lcov-report/hooks/useContainerCommandRouter.js.html +119 -80
  35. package/coverage/lcov-report/hooks/useControls.js.html +263 -122
  36. package/coverage/lcov-report/hooks/useEraseConfirmation.js.html +8 -8
  37. package/coverage/lcov-report/hooks/useExitHandler.js.html +50 -41
  38. package/coverage/lcov-report/index.html +67 -52
  39. package/coverage/lcov.info +991 -805
  40. package/dist/App.js +11 -11
  41. package/dist/components/ContainerCreationPrompt.js +31 -10
  42. package/dist/components/ContainerList.js +3 -3
  43. package/dist/components/ContainerRow.js +20 -20
  44. package/dist/components/ContainerSection.js +3 -3
  45. package/dist/components/Footer.js +2 -2
  46. package/dist/components/Header.js +7 -7
  47. package/dist/components/LogViewer.js +3 -3
  48. package/dist/components/MessageFeedback.js +2 -2
  49. package/dist/components/PromptField.js +4 -4
  50. package/dist/components/StatsBar.js +5 -5
  51. package/dist/components/SuggestionPanel.js +44 -0
  52. package/dist/components/UsageMenu.js +3 -3
  53. package/dist/helpers/appInfo.js +7 -7
  54. package/dist/helpers/constants.js +114 -0
  55. package/dist/helpers/containerOptionsBuilder.js +3 -3
  56. package/dist/helpers/dockerService/dockerService.js +1 -1
  57. package/dist/helpers/dockerService/serviceComponents/containerActions.js +99 -40
  58. package/dist/helpers/dockerService/serviceComponents/containerList.js +11 -11
  59. package/dist/helpers/dockerService/serviceComponents/containerLogs.js +9 -9
  60. package/dist/helpers/dockerService/serviceComponents/containerStats.js +5 -5
  61. package/dist/helpers/dockerService/serviceComponents/imageUtils.js +1 -1
  62. package/dist/helpers/exitWithMessage.js +10 -10
  63. package/dist/helpers/imageNameUtils.js +32 -0
  64. package/dist/helpers/logger.js +13 -13
  65. package/dist/helpers/safeCall.js +2 -2
  66. package/dist/helpers/validationHelpers.js +76 -21
  67. package/dist/hooks/creation/useContainerActions.js +9 -9
  68. package/dist/hooks/creation/useContainerCreation.js +138 -36
  69. package/dist/hooks/creation/useLogsViewer.js +2 -2
  70. package/dist/hooks/debug/useDebugLogs.js +4 -4
  71. package/dist/hooks/useContainerCommandRouter.js +13 -13
  72. package/dist/hooks/useContainers.js +3 -3
  73. package/dist/hooks/useControls.js +66 -35
  74. package/dist/hooks/useEraseConfirmation.js +3 -3
  75. package/dist/index.js +2 -2
  76. package/jest.config.cjs +7 -0
  77. package/package.json +7 -3
  78. package/src/App.jsx +33 -16
  79. package/src/components/ContainerCreationPrompt.jsx +43 -9
  80. package/src/components/ContainerList.jsx +3 -3
  81. package/src/components/ContainerRow.jsx +19 -16
  82. package/src/components/ContainerSection.jsx +3 -3
  83. package/src/components/Footer.jsx +2 -2
  84. package/src/components/Header.jsx +7 -7
  85. package/src/components/LogViewer.jsx +5 -3
  86. package/src/components/MessageFeedback.jsx +2 -2
  87. package/src/components/PromptField.jsx +4 -4
  88. package/src/components/StatsBar.jsx +11 -7
  89. package/src/components/SuggestionPanel.jsx +53 -0
  90. package/src/components/UsageMenu.jsx +18 -13
  91. package/src/helpers/appInfo.js +7 -7
  92. package/src/helpers/constants.js +30 -9
  93. package/src/helpers/containerOptionsBuilder.js +16 -5
  94. package/src/helpers/dockerService/dockerService.js +1 -1
  95. package/src/helpers/dockerService/serviceComponents/containerActions.js +87 -61
  96. package/src/helpers/dockerService/serviceComponents/containerList.js +31 -25
  97. package/src/helpers/dockerService/serviceComponents/containerLogs.js +41 -26
  98. package/src/helpers/dockerService/serviceComponents/containerStats.js +13 -11
  99. package/src/helpers/dockerService/serviceComponents/imageUtils.js +5 -4
  100. package/src/helpers/exitWithMessage.js +13 -7
  101. package/src/helpers/imageNameUtils.js +32 -0
  102. package/src/helpers/logger.js +15 -15
  103. package/src/helpers/safeCall.js +2 -2
  104. package/src/helpers/validationHelpers.js +13 -29
  105. package/src/hooks/creation/useContainerActions.js +19 -12
  106. package/src/hooks/creation/useContainerCreation.js +150 -41
  107. package/src/hooks/creation/useLogsViewer.js +2 -2
  108. package/src/hooks/debug/useDebugLogs.js +15 -10
  109. package/src/hooks/navigation/useContainerSelection.js +17 -14
  110. package/src/hooks/useContainerCommandRouter.js +71 -58
  111. package/src/hooks/useContainers.js +4 -4
  112. package/src/hooks/useControls.js +115 -68
  113. package/src/hooks/useEraseConfirmation.js +3 -3
  114. package/src/hooks/useExitHandler.js +29 -26
  115. package/src/index.js +4 -4
  116. package/test/ContainerCreationPrompt.dom.test.js +68 -0
  117. package/test/SuggestionPanel.dom.test.js +53 -0
  118. package/test/constants.test.js +34 -0
  119. package/test/useContainerCreation.dom.test.js +123 -0
  120. package/test/useControls.dom.test.js +198 -2
@@ -1,7 +1,12 @@
1
- import { useState } from "react";
2
- import { validatePorts, validateEnvVars } from "../../helpers/validationHelpers.js";
3
- import { IMAGE_PROFILES } from "../../helpers/constants.js";
4
- import { safeCall } from "../../helpers/safeCall.js";
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;
5
10
 
6
11
  /**
7
12
  * Custom hook to manage the container creation flow, step by step.
@@ -14,14 +19,78 @@ import { safeCall } from "../../helpers/safeCall.js";
14
19
  * @param {Object} [params.imageProfiles] - Image profiles map for contextual validation
15
20
  * @returns {Object} Creation state, setters, and helpers
16
21
  */
17
- export function useContainerCreation({ onCreate, onCancel, dbImages = [], imageProfiles = IMAGE_PROFILES }) {
22
+ export function useContainerCreation({
23
+ onCreate,
24
+ onCancel,
25
+ dbImages = [],
26
+ imageProfiles = IMAGE_PROFILES,
27
+ }) {
18
28
  const [step, setStep] = useState(0); // 0: image, 1: name, 2: ports, 3: env
19
- const [imageName, setImageName] = useState("");
20
- const [containerName, setContainerName] = useState("");
21
- const [portInput, setPortInput] = useState("");
22
- const [envInput, setEnvInput] = useState("");
23
- const [message, setMessage] = useState("");
24
- const [messageColor, setMessageColor] = useState("yellow");
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
+ }
25
94
 
26
95
  /**
27
96
  * Advances to the next step, with validation and feedback.
@@ -29,42 +98,70 @@ export function useContainerCreation({ onCreate, onCancel, dbImages = [], imageP
29
98
  function nextStep() {
30
99
  if (step === 0) {
31
100
  if (!imageName.trim()) {
32
- setMessage("Image name cannot be empty.");
33
- setMessageColor("red");
101
+ setMessage('Image name cannot be empty.');
102
+ setMessageColor('red');
34
103
  return;
35
104
  }
36
105
  setStep(1);
37
- setMessage("Optional: Enter container name or leave empty and press Enter");
38
- setMessageColor("yellow");
106
+ setMessage(
107
+ 'Optional: Enter container name or leave empty and press Enter'
108
+ );
109
+ setMessageColor('yellow');
39
110
  return;
40
111
  }
41
112
  if (step === 1) {
42
113
  setStep(2);
43
- setMessage("Optional: Enter ports (format 8080:80,443:443) or leave empty and press Enter");
44
- setMessageColor("yellow");
114
+ setMessage(
115
+ 'Optional: Enter ports (format 8080:80,443:443) or leave empty and press Enter'
116
+ );
117
+ setMessageColor('yellow');
45
118
  return;
46
119
  }
47
120
  if (step === 2) {
48
121
  // Ports are now optional - only validate if provided
49
122
  if (portInput.trim() && !validatePorts(portInput)) {
50
- setMessage("Port format must be host:container and both must be numbers (e.g. 8080:80)");
51
- setMessageColor("red");
123
+ setMessage(
124
+ 'Port format must be host:container and both must be numbers (e.g. 8080:80)'
125
+ );
126
+ setMessageColor('red');
52
127
  return;
53
128
  }
54
129
  setStep(3);
55
- const isDb = dbImages.some(db => imageName.trim().toLowerCase().includes(db));
130
+ const isDb = dbImages.some((db) =>
131
+ imageName.trim().toLowerCase().includes(db)
132
+ );
56
133
  // Check if the image has a profile with required env vars
57
- const baseName = imageName.trim().toLowerCase().split(":")[0].split("/").pop();
134
+ const baseName = imageName
135
+ .trim()
136
+ .toLowerCase()
137
+ .split(':')[0]
138
+ .split('/')
139
+ .pop();
58
140
  const profile = imageProfiles[baseName];
59
141
  if (profile && profile.requiredEnv && profile.requiredEnv.length) {
60
- setMessage(`Required env vars for ${baseName}: ${profile.requiredEnv.join(", ")}. Enter as VAR=val,VAR2=val2`);
61
- setMessageColor("yellow");
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');
62
155
  } else if (isDb) {
63
- setMessage("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.");
64
- setMessageColor("yellow");
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');
65
160
  } else {
66
- setMessage("Optional: Enter environment variables (format VAR1=val1,VAR2=val2) or leave empty and press Enter");
67
- setMessageColor("yellow");
161
+ setMessage(
162
+ 'Optional: Enter environment variables (format VAR1=val1,VAR2=val2) or leave empty and press Enter'
163
+ );
164
+ setMessageColor('yellow');
68
165
  }
69
166
  return;
70
167
  }
@@ -72,8 +169,8 @@ export function useContainerCreation({ onCreate, onCancel, dbImages = [], imageP
72
169
  // Contextual env validation using image profiles
73
170
  const result = validateEnvVars(envInput, imageName, imageProfiles);
74
171
  if (!result.valid) {
75
- setMessage(result.errors.join(" | "));
76
- setMessageColor("red");
172
+ setMessage(result.errors.join(' | '));
173
+ setMessageColor('red');
77
174
  return;
78
175
  }
79
176
  // Final step: call onCreate with all data (safely)
@@ -86,12 +183,15 @@ export function useContainerCreation({ onCreate, onCancel, dbImages = [], imageP
86
183
  */
87
184
  function cancelCreation() {
88
185
  setStep(0);
89
- setImageName("");
90
- setContainerName("");
91
- setPortInput("");
92
- setEnvInput("");
93
- setMessage("");
94
- setMessageColor("yellow");
186
+ setImageName('');
187
+ setContainerName('');
188
+ setPortInput('');
189
+ setEnvInput('');
190
+ setMessage('');
191
+ setMessageColor('yellow');
192
+ setSuggestions([]);
193
+ setSelectedSuggestionIndex(-1);
194
+ setVisibleOffset(0);
95
195
  safeCall(onCancel);
96
196
  }
97
197
 
@@ -101,12 +201,15 @@ export function useContainerCreation({ onCreate, onCancel, dbImages = [], imageP
101
201
  */
102
202
  function resetCreation() {
103
203
  setStep(0);
104
- setImageName("");
105
- setContainerName("");
106
- setPortInput("");
107
- setEnvInput("");
108
- setMessage("Insert the name of the image to create: ");
109
- setMessageColor("yellow");
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);
110
213
  }
111
214
 
112
215
  return {
@@ -124,6 +227,12 @@ export function useContainerCreation({ onCreate, onCancel, dbImages = [], imageP
124
227
  setMessage,
125
228
  messageColor,
126
229
  setMessageColor,
230
+ suggestions,
231
+ selectedSuggestionIndex,
232
+ visibleOffset,
233
+ updateImageInput,
234
+ moveSuggestionSelection,
235
+ applyFocusedSuggestion,
127
236
  nextStep,
128
237
  cancelCreation,
129
238
  resetCreation,
@@ -1,5 +1,5 @@
1
- import { useState, useRef } from "react";
2
- import { safeCall } from "../../helpers/safeCall.js";
1
+ import { useState, useRef } from 'react';
2
+ import { safeCall } from '../../helpers/safeCall.js';
3
3
 
4
4
  /**
5
5
  * Custom hook to manage the logs viewer state and stream reference.
@@ -8,16 +8,21 @@ export function useDebugLogs() {
8
8
  useEffect(() => {
9
9
  const unsubscribe = logger.subscribe((entry) => {
10
10
  setDebugLogs((prev) => {
11
- const extras = (entry.args || []).map((arg) => {
12
- if (typeof arg === "string") return arg;
13
- if (typeof arg === "number" || typeof arg === "boolean") return String(arg);
14
- try {
15
- return JSON.stringify(arg);
16
- } catch (err) {
17
- return "[unserializable]";
18
- }
19
- }).filter(Boolean);
20
- const line = extras.length ? `${entry.formatted} ${extras.join(" ")}` : entry.formatted;
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;
21
26
  const next = [...prev, line];
22
27
  return next.slice(-200);
23
28
  });
@@ -3,23 +3,26 @@ import { useState, useCallback } from 'react';
3
3
  export function useContainerSelection(total) {
4
4
  const [selected, setSelected] = useState(0);
5
5
 
6
- const handleNavigation = useCallback((input, key) => {
7
- if (total === 0) {
8
- return false;
9
- }
6
+ const handleNavigation = useCallback(
7
+ (input, key) => {
8
+ if (total === 0) {
9
+ return false;
10
+ }
10
11
 
11
- if (key.upArrow) {
12
- setSelected((i) => (i === 0 ? total - 1 : i - 1));
13
- return true;
14
- }
12
+ if (key.upArrow) {
13
+ setSelected((i) => (i === 0 ? total - 1 : i - 1));
14
+ return true;
15
+ }
15
16
 
16
- if (key.downArrow) {
17
- setSelected((i) => (i === total - 1 ? 0 : i + 1));
18
- return true;
19
- }
17
+ if (key.downArrow) {
18
+ setSelected((i) => (i === total - 1 ? 0 : i + 1));
19
+ return true;
20
+ }
20
21
 
21
- return false;
22
- }, [total]);
22
+ return false;
23
+ },
24
+ [total]
25
+ );
23
26
 
24
27
  return { selected, setSelected, handleNavigation };
25
28
  }
@@ -1,4 +1,4 @@
1
- import { useCallback } from "react";
1
+ import { useCallback } from 'react';
2
2
 
3
3
  /**
4
4
  * Hook responsible solely for routing Docker action keystrokes to the correct handler.
@@ -36,75 +36,88 @@ export function useContainerCommandRouter({
36
36
  onToggleDebug,
37
37
  onStartCreate,
38
38
  }) {
39
- const handleDockerCommands = useCallback((input) => {
40
- const container = containers[selected];
39
+ const handleDockerCommands = useCallback(
40
+ (input) => {
41
+ const container = containers[selected];
41
42
 
42
- if (input === "i") {
43
- actions.handleAction({
44
- actionFn: async (id) => await actions.startContainer(id),
45
- actionLabel: "Starting",
46
- selected,
47
- stateCheck: (c) =>
48
- (c.state === "running" || c.status === "running") &&
49
- "Container is already running.",
50
- });
51
- return true;
52
- }
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
+ }
53
54
 
54
- if (input === "p") {
55
- actions.handleAction({
56
- actionFn: async (id) => await actions.stopContainer(id),
57
- actionLabel: "Stopping",
58
- selected,
59
- stateCheck: (c) =>
60
- (c.state === "exited" ||
61
- c.status === "exited" ||
62
- c.state === "stopped" ||
63
- c.status === "stopped") &&
64
- "Container is already stopped.",
65
- });
66
- return true;
67
- }
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
+ }
68
69
 
69
- if (input === "r") {
70
- actions.handleAction({
71
- actionFn: async (id) => await actions.restartContainer(id),
72
- actionLabel: "Restarting",
73
- selected,
74
- });
75
- return true;
76
- }
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
+ }
77
78
 
78
- if (input === "e") {
79
- if (!container) {
79
+ if (input === 'e') {
80
+ if (!container) {
81
+ return true;
82
+ }
83
+ onStartErase();
80
84
  return true;
81
85
  }
82
- onStartErase();
83
- return true;
84
- }
85
86
 
86
- if (input === "l") {
87
- if (!container) {
87
+ if (input === 'l') {
88
+ if (!container) {
89
+ return true;
90
+ }
91
+ logsViewer.openLogs();
92
+ startLogsStream(container.id);
88
93
  return true;
89
94
  }
90
- logsViewer.openLogs();
91
- startLogsStream(container.id);
92
- return true;
93
- }
94
95
 
95
- if (input === "c") {
96
- onStartCreate();
97
- creation.resetCreation();
98
- return true;
99
- }
96
+ if (input === 'c') {
97
+ onStartCreate();
98
+ creation.resetCreation();
99
+ return true;
100
+ }
100
101
 
101
- if (input === "d") {
102
- onToggleDebug();
103
- return true;
104
- }
102
+ if (input === 'd') {
103
+ onToggleDebug();
104
+ return true;
105
+ }
105
106
 
106
- return false;
107
- }, [actions, containers, creation, logsViewer, onStartCreate, onStartErase, onToggleDebug, selected, startLogsStream]);
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
+ );
108
121
 
109
122
  return { handleDockerCommands };
110
123
  }
@@ -8,9 +8,9 @@
8
8
  * // Use in a component
9
9
  * const { containers } = useContainers();
10
10
  */
11
- import React, { useState, useEffect } from "react";
12
- import { getContainers } from "../helpers/dockerService/serviceComponents/containerList";
13
- import { REFRESH_INTERVALS } from "../helpers/constants.js";
11
+ import React, { useState, useEffect } from 'react';
12
+ import { getContainers } from '../helpers/dockerService/serviceComponents/containerList';
13
+ import { REFRESH_INTERVALS } from '../helpers/constants.js';
14
14
 
15
15
  export function useContainers() {
16
16
  const [containers, setContainers] = useState([]);
@@ -18,7 +18,7 @@ export function useContainers() {
18
18
  useEffect(() => {
19
19
  const fetch = async () => setContainers(await getContainers());
20
20
  fetch();
21
- const timer = setInterval(fetch, REFRESH_INTERVALS.CONTAINER_LIST);
21
+ const timer = setInterval(fetch, REFRESH_INTERVALS.CONTAINER_LIST);
22
22
  return () => clearInterval(timer);
23
23
  }, []);
24
24