cdd-cli 3.2.0 → 4.0.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.
- package/.atl/skill-registry.md +30 -0
- package/.editorconfig +12 -0
- package/.eslintrc.json +1 -1
- package/.prettierignore +4 -0
- package/.prettierrc +8 -0
- package/.~lock.ROADMAP.md# +1 -0
- package/CHANGELOG.md +80 -0
- package/README.es.md +198 -0
- package/README.md +81 -23
- package/ROADMAP.md +647 -0
- package/SECURITY.md +5 -0
- package/__mocks__/ink.cjs +17 -0
- package/coverage/clover.xml +471 -500
- package/coverage/coverage-final.json +16 -16
- package/coverage/lcov-report/ContainerCreationPrompt.jsx.html +400 -0
- package/coverage/lcov-report/ControlsHUD.jsx.html +256 -0
- package/coverage/lcov-report/components/ContainerCreationPrompt.jsx.html +394 -0
- package/coverage/lcov-report/components/PromptField.jsx.html +229 -0
- package/coverage/lcov-report/components/SuggestionPanel.jsx.html +268 -0
- package/coverage/lcov-report/components/index.html +146 -0
- package/coverage/lcov-report/helpers/constants.js.html +139 -22
- package/coverage/lcov-report/helpers/containerOptionsBuilder.js.html +75 -42
- package/coverage/lcov-report/helpers/dockerHubService.js.html +310 -0
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerActions.js.html +162 -84
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerList.js.html +50 -32
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/index.html +21 -21
- package/coverage/lcov-report/helpers/imageNameUtils.js.html +181 -0
- package/coverage/lcov-report/helpers/index.html +84 -54
- package/coverage/lcov-report/helpers/logger.js.html +68 -68
- package/coverage/lcov-report/helpers/safeCall.js.html +15 -15
- package/coverage/lcov-report/helpers/validationHelpers.js.html +66 -114
- package/coverage/lcov-report/hooks/creation/index.html +20 -20
- package/coverage/lcov-report/hooks/creation/useContainerActions.js.html +38 -17
- package/coverage/lcov-report/hooks/creation/useContainerCreation.js.html +593 -89
- package/coverage/lcov-report/hooks/creation/useLogsViewer.js.html +7 -7
- package/coverage/lcov-report/hooks/debug/index.html +5 -5
- package/coverage/lcov-report/hooks/debug/useDebugLogs.js.html +35 -20
- package/coverage/lcov-report/hooks/index.html +40 -40
- package/coverage/lcov-report/hooks/navigation/index.html +19 -19
- package/coverage/lcov-report/hooks/navigation/useContainerSelection.js.html +38 -29
- package/coverage/lcov-report/hooks/useContainerCommandRouter.js.html +119 -80
- package/coverage/lcov-report/hooks/useControls.js.html +306 -123
- package/coverage/lcov-report/hooks/useEraseConfirmation.js.html +8 -8
- package/coverage/lcov-report/hooks/useExitHandler.js.html +50 -41
- package/coverage/lcov-report/index.html +52 -67
- package/coverage/lcov.info +899 -952
- package/dist/App.js +18 -12
- package/dist/components/ContainerCreationPrompt.js +50 -13
- package/dist/components/ContainerList.js +3 -3
- package/dist/components/ContainerRow.js +20 -20
- package/dist/components/ContainerSection.js +3 -3
- package/dist/components/ControlsHUD.js +106 -0
- package/dist/components/Footer.js +2 -2
- package/dist/components/Header.js +7 -7
- package/dist/components/LogViewer.js +3 -3
- package/dist/components/MessageFeedback.js +2 -2
- package/dist/components/PromptField.js +4 -4
- package/dist/components/StatsBar.js +5 -5
- package/dist/components/SuggestionPanel.js +53 -0
- package/dist/components/UsageMenu.js +3 -3
- package/dist/helpers/appInfo.js +7 -7
- package/dist/helpers/constants.js +154 -1
- package/dist/helpers/containerOptionsBuilder.js +3 -3
- package/dist/helpers/dockerHubService.js +111 -0
- package/dist/helpers/dockerService/dockerService.js +1 -1
- package/dist/helpers/dockerService/serviceComponents/containerActions.js +99 -40
- package/dist/helpers/dockerService/serviceComponents/containerList.js +11 -11
- package/dist/helpers/dockerService/serviceComponents/containerLogs.js +9 -9
- package/dist/helpers/dockerService/serviceComponents/containerStats.js +5 -5
- package/dist/helpers/dockerService/serviceComponents/imageUtils.js +1 -1
- package/dist/helpers/exitWithMessage.js +10 -10
- package/dist/helpers/imageNameUtils.js +32 -0
- package/dist/helpers/logger.js +13 -13
- package/dist/helpers/safeCall.js +2 -2
- package/dist/helpers/validationHelpers.js +76 -21
- package/dist/hooks/creation/useContainerActions.js +28 -11
- package/dist/hooks/creation/useContainerCreation.js +452 -62
- package/dist/hooks/creation/useLogsViewer.js +2 -2
- package/dist/hooks/debug/useDebugLogs.js +4 -4
- package/dist/hooks/useContainerCommandRouter.js +14 -14
- package/dist/hooks/useContainers.js +3 -3
- package/dist/hooks/useControls.js +85 -38
- package/dist/hooks/useEraseConfirmation.js +3 -3
- package/dist/index.js +9 -2
- package/jest.config.cjs +7 -0
- package/package.json +7 -3
- package/src/App.jsx +39 -16
- package/src/components/ContainerCreationPrompt.jsx +54 -10
- package/src/components/ContainerList.jsx +3 -3
- package/src/components/ContainerRow.jsx +19 -16
- package/src/components/ContainerSection.jsx +3 -3
- package/src/components/ControlsHUD.jsx +66 -0
- package/src/components/Footer.jsx +2 -2
- package/src/components/Header.jsx +7 -7
- package/src/components/LogViewer.jsx +5 -3
- package/src/components/MessageFeedback.jsx +2 -2
- package/src/components/PromptField.jsx +4 -4
- package/src/components/StatsBar.jsx +11 -7
- package/src/components/SuggestionPanel.jsx +61 -0
- package/src/components/UsageMenu.jsx +18 -13
- package/src/helpers/appInfo.js +7 -7
- package/src/helpers/constants.js +48 -9
- package/src/helpers/containerOptionsBuilder.js +16 -5
- package/src/helpers/dockerHubService.js +75 -0
- package/src/helpers/dockerService/dockerService.js +1 -1
- package/src/helpers/dockerService/serviceComponents/containerActions.js +87 -61
- package/src/helpers/dockerService/serviceComponents/containerList.js +31 -25
- package/src/helpers/dockerService/serviceComponents/containerLogs.js +41 -26
- package/src/helpers/dockerService/serviceComponents/containerStats.js +13 -11
- package/src/helpers/dockerService/serviceComponents/imageUtils.js +5 -4
- package/src/helpers/exitWithMessage.js +13 -7
- package/src/helpers/imageNameUtils.js +32 -0
- package/src/helpers/logger.js +15 -16
- package/src/helpers/safeCall.js +2 -2
- package/src/helpers/validationHelpers.js +13 -29
- package/src/hooks/creation/useContainerActions.js +32 -12
- package/src/hooks/creation/useContainerCreation.js +293 -47
- package/src/hooks/creation/useLogsViewer.js +2 -2
- package/src/hooks/debug/useDebugLogs.js +15 -10
- package/src/hooks/navigation/useContainerSelection.js +17 -14
- package/src/hooks/useContainerCommandRouter.js +71 -58
- package/src/hooks/useContainers.js +4 -4
- package/src/hooks/useControls.js +135 -72
- package/src/hooks/useEraseConfirmation.js +3 -3
- package/src/hooks/useExitHandler.js +29 -26
- package/src/index.js +8 -4
- package/test/ContainerCreationPrompt.dom.test.js +313 -0
- package/test/ControlsHUD.dom.test.js +125 -0
- package/test/SuggestionPanel.dom.test.js +83 -0
- package/test/constants.test.js +41 -0
- package/test/dockerHubService.test.js +213 -0
- package/test/logger.test.js +58 -0
- package/test/resolveImageTag.test.js +26 -0
- package/test/useContainerCreation.dom.test.js +428 -2
- package/test/useContainerCreationHub.dom.test.js +184 -0
- package/test/useControls.dom.test.js +406 -4
package/dist/App.js
CHANGED
|
@@ -9,17 +9,17 @@
|
|
|
9
9
|
* // Render the app
|
|
10
10
|
* <App />
|
|
11
11
|
*/
|
|
12
|
-
import React from
|
|
13
|
-
import { Box, Text, Spacer } from
|
|
14
|
-
import { useContainers } from
|
|
15
|
-
import { useControls } from
|
|
16
|
-
import ContainerSection from
|
|
17
|
-
import MessageFeedback from
|
|
18
|
-
import Header from
|
|
19
|
-
import LogViewer from
|
|
20
|
-
import ContainerCreationPrompt from
|
|
21
|
-
import UsageMenu from
|
|
22
|
-
import Footer from
|
|
12
|
+
import React from 'react';
|
|
13
|
+
import { Box, Text, Spacer } from 'ink';
|
|
14
|
+
import { useContainers } from './hooks/useContainers.js';
|
|
15
|
+
import { useControls } from './hooks/useControls.js';
|
|
16
|
+
import ContainerSection from './components/ContainerSection.js';
|
|
17
|
+
import MessageFeedback from './components/MessageFeedback.js';
|
|
18
|
+
import Header from './components/Header.js';
|
|
19
|
+
import LogViewer from './components/LogViewer.js';
|
|
20
|
+
import ContainerCreationPrompt from './components/ContainerCreationPrompt.js';
|
|
21
|
+
import UsageMenu from './components/UsageMenu.js';
|
|
22
|
+
import Footer from './components/Footer.js';
|
|
23
23
|
export default function App() {
|
|
24
24
|
var _useContainers = useContainers(),
|
|
25
25
|
containers = _useContainers.containers;
|
|
@@ -32,7 +32,13 @@ export default function App() {
|
|
|
32
32
|
portInput: controls.portInput,
|
|
33
33
|
envInput: controls.envInput,
|
|
34
34
|
message: controls.message,
|
|
35
|
-
messageColor: controls.messageColor
|
|
35
|
+
messageColor: controls.messageColor,
|
|
36
|
+
suggestions: controls.creation.suggestions,
|
|
37
|
+
selectedSuggestionIndex: controls.creation.selectedSuggestionIndex,
|
|
38
|
+
visibleOffset: controls.creation.visibleOffset,
|
|
39
|
+
isSearchingHub: controls.creation.isSearchingHub,
|
|
40
|
+
hubResults: controls.creation.hubResults,
|
|
41
|
+
hasSuggestedEnv: controls.creation.hasSuggestedEnv
|
|
36
42
|
});
|
|
37
43
|
}
|
|
38
44
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { Box
|
|
3
|
-
import { PromptField, PromptMessage } from
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box } from 'ink';
|
|
3
|
+
import { PromptField, PromptMessage } from './PromptField.js';
|
|
4
|
+
import { SuggestionPanel } from './SuggestionPanel.js';
|
|
5
|
+
import { ControlsHUD } from './ControlsHUD.js';
|
|
4
6
|
import PropTypes from 'prop-types';
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -16,6 +18,9 @@ import PropTypes from 'prop-types';
|
|
|
16
18
|
* @param {string} props.envInput - Value for the environment variables field
|
|
17
19
|
* @param {string} props.message - Contextual message to display (errors/hints)
|
|
18
20
|
* @param {string} props.messageColor - Color for the contextual message
|
|
21
|
+
* @param {string[]} [props.suggestions] - Autocomplete suggestions (shown only on step 0)
|
|
22
|
+
* @param {number} [props.selectedSuggestionIndex] - Currently focused suggestion index
|
|
23
|
+
* @param {number} [props.visibleOffset] - First visible suggestion offset
|
|
19
24
|
* @returns {JSX.Element}
|
|
20
25
|
*/
|
|
21
26
|
export default function ContainerCreationPrompt(props) {
|
|
@@ -25,21 +30,33 @@ export default function ContainerCreationPrompt(props) {
|
|
|
25
30
|
portInput = props.portInput,
|
|
26
31
|
envInput = props.envInput,
|
|
27
32
|
message = props.message,
|
|
28
|
-
messageColor = props.messageColor
|
|
33
|
+
messageColor = props.messageColor,
|
|
34
|
+
_props$suggestions = props.suggestions,
|
|
35
|
+
suggestions = _props$suggestions === void 0 ? [] : _props$suggestions,
|
|
36
|
+
_props$selectedSugges = props.selectedSuggestionIndex,
|
|
37
|
+
selectedSuggestionIndex = _props$selectedSugges === void 0 ? -1 : _props$selectedSugges,
|
|
38
|
+
_props$visibleOffset = props.visibleOffset,
|
|
39
|
+
visibleOffset = _props$visibleOffset === void 0 ? 0 : _props$visibleOffset,
|
|
40
|
+
_props$hubResults = props.hubResults,
|
|
41
|
+
hubResults = _props$hubResults === void 0 ? null : _props$hubResults,
|
|
42
|
+
_props$isSearchingHub = props.isSearchingHub,
|
|
43
|
+
isSearchingHub = _props$isSearchingHub === void 0 ? false : _props$isSearchingHub,
|
|
44
|
+
_props$hasSuggestedEn = props.hasSuggestedEnv,
|
|
45
|
+
hasSuggestedEnv = _props$hasSuggestedEn === void 0 ? false : _props$hasSuggestedEn;
|
|
29
46
|
var prompts = [{
|
|
30
|
-
label:
|
|
47
|
+
label: 'Name of the image to create (e.g., nginx:1.27-alpine):',
|
|
31
48
|
value: imageName,
|
|
32
49
|
required: true
|
|
33
50
|
}, {
|
|
34
|
-
label:
|
|
51
|
+
label: 'Name of the container (optional):',
|
|
35
52
|
value: containerName,
|
|
36
53
|
required: false
|
|
37
54
|
}, {
|
|
38
|
-
label:
|
|
55
|
+
label: 'Ports (optional, format 8080:80,443:443):',
|
|
39
56
|
value: portInput,
|
|
40
57
|
required: false
|
|
41
58
|
}, {
|
|
42
|
-
label:
|
|
59
|
+
label: 'Environment variables (optional, format VAR1=val1,VAR2=val2):',
|
|
43
60
|
value: envInput,
|
|
44
61
|
required: false
|
|
45
62
|
}];
|
|
@@ -47,6 +64,9 @@ export default function ContainerCreationPrompt(props) {
|
|
|
47
64
|
label = _ref.label,
|
|
48
65
|
value = _ref.value,
|
|
49
66
|
required = _ref.required;
|
|
67
|
+
var activeItems = hubResults !== null && hubResults !== void 0 ? hubResults : suggestions;
|
|
68
|
+
var showSuggestions = step === 0 && (isSearchingHub || activeItems.length > 0);
|
|
69
|
+
var hasSuggestions = (suggestions === null || suggestions === void 0 ? void 0 : suggestions.length) > 0 || (hubResults === null || hubResults === void 0 ? void 0 : hubResults.length) > 0;
|
|
50
70
|
return /*#__PURE__*/React.createElement(Box, {
|
|
51
71
|
flexDirection: "column",
|
|
52
72
|
borderStyle: "round",
|
|
@@ -56,12 +76,20 @@ export default function ContainerCreationPrompt(props) {
|
|
|
56
76
|
label: label,
|
|
57
77
|
value: value,
|
|
58
78
|
required: required
|
|
79
|
+
}), showSuggestions && /*#__PURE__*/React.createElement(SuggestionPanel, {
|
|
80
|
+
items: activeItems,
|
|
81
|
+
selectedIndex: selectedSuggestionIndex,
|
|
82
|
+
visibleOffset: visibleOffset,
|
|
83
|
+
isLoading: isSearchingHub
|
|
59
84
|
}), /*#__PURE__*/React.createElement(PromptMessage, {
|
|
60
85
|
message: message,
|
|
61
86
|
color: messageColor
|
|
62
|
-
}), /*#__PURE__*/React.createElement(
|
|
63
|
-
|
|
64
|
-
|
|
87
|
+
}), /*#__PURE__*/React.createElement(ControlsHUD, {
|
|
88
|
+
step: step,
|
|
89
|
+
hasSuggestions: hasSuggestions,
|
|
90
|
+
isSearchingHub: isSearchingHub,
|
|
91
|
+
hasSuggestedEnv: hasSuggestedEnv
|
|
92
|
+
}));
|
|
65
93
|
}
|
|
66
94
|
ContainerCreationPrompt.propTypes = {
|
|
67
95
|
step: PropTypes.number.isRequired,
|
|
@@ -70,5 +98,14 @@ ContainerCreationPrompt.propTypes = {
|
|
|
70
98
|
portInput: PropTypes.string,
|
|
71
99
|
envInput: PropTypes.string,
|
|
72
100
|
message: PropTypes.string,
|
|
73
|
-
messageColor: PropTypes.string
|
|
74
|
-
|
|
101
|
+
messageColor: PropTypes.string,
|
|
102
|
+
suggestions: PropTypes.arrayOf(PropTypes.string),
|
|
103
|
+
selectedSuggestionIndex: PropTypes.number,
|
|
104
|
+
visibleOffset: PropTypes.number,
|
|
105
|
+
hubResults: PropTypes.arrayOf(PropTypes.string),
|
|
106
|
+
isSearchingHub: PropTypes.bool,
|
|
107
|
+
hasSuggestedEnv: PropTypes.bool
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Named export for test compatibility with jest ESM interop
|
|
111
|
+
export { ContainerCreationPrompt };
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
* // Render with containers
|
|
10
10
|
* <ContainerList containers={containers} />
|
|
11
11
|
*/
|
|
12
|
-
import React from
|
|
12
|
+
import React from 'react';
|
|
13
13
|
import PropTypes from 'prop-types';
|
|
14
|
-
import { Box } from
|
|
15
|
-
import ContainerRow from
|
|
14
|
+
import { Box } from 'ink';
|
|
15
|
+
import ContainerRow from './ContainerRow.js';
|
|
16
16
|
export default function ContainerList(_ref) {
|
|
17
17
|
var containers = _ref.containers,
|
|
18
18
|
selected = _ref.selected;
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { Box, Text } from
|
|
3
|
-
import StatsBar from
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import StatsBar from './StatsBar.js';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
5
|
import { useContainerStats } from '../hooks/useContainerStats.js';
|
|
6
6
|
var stateText = function stateText(state) {
|
|
7
|
-
if (state ===
|
|
8
|
-
text:
|
|
9
|
-
color:
|
|
7
|
+
if (state === 'running') return {
|
|
8
|
+
text: '🟢 RUNNING',
|
|
9
|
+
color: 'green'
|
|
10
10
|
};
|
|
11
|
-
if (state ===
|
|
12
|
-
text:
|
|
13
|
-
color:
|
|
11
|
+
if (state === 'exited') return {
|
|
12
|
+
text: '🔴 EXITED',
|
|
13
|
+
color: 'red'
|
|
14
14
|
};
|
|
15
|
-
if (state ===
|
|
16
|
-
text:
|
|
17
|
-
color:
|
|
15
|
+
if (state === 'paused') return {
|
|
16
|
+
text: '🟠 PAUSED',
|
|
17
|
+
color: 'yellow'
|
|
18
18
|
};
|
|
19
19
|
return {
|
|
20
20
|
text: state.toUpperCase(),
|
|
21
|
-
color:
|
|
21
|
+
color: 'gray'
|
|
22
22
|
};
|
|
23
23
|
};
|
|
24
24
|
|
|
@@ -43,11 +43,11 @@ export default function ContainerRow(_ref) {
|
|
|
43
43
|
|
|
44
44
|
// Format ports for display (no leading space to avoid layout shifts)
|
|
45
45
|
var formatPorts = function formatPorts(ports) {
|
|
46
|
-
if (!ports || ports.length === 0) return
|
|
46
|
+
if (!ports || ports.length === 0) return '';
|
|
47
47
|
if (Array.isArray(ports)) {
|
|
48
48
|
return ports.map(function (p, _i) {
|
|
49
49
|
return "\uD83D\uDD17 ".concat(p);
|
|
50
|
-
}).join(
|
|
50
|
+
}).join(' ');
|
|
51
51
|
}
|
|
52
52
|
return "\uD83D\uDD17 ".concat(ports);
|
|
53
53
|
};
|
|
@@ -55,8 +55,8 @@ export default function ContainerRow(_ref) {
|
|
|
55
55
|
// Helper to truncate long strings to a max length without adding trailing spaces
|
|
56
56
|
var truncate = function truncate(s) {
|
|
57
57
|
var max = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 20;
|
|
58
|
-
if (!s) return
|
|
59
|
-
return s.length > max ? s.slice(0, max - 1) +
|
|
58
|
+
if (!s) return '';
|
|
59
|
+
return s.length > max ? s.slice(0, max - 1) + '…' : s;
|
|
60
60
|
};
|
|
61
61
|
var stateInfo = stateText(state);
|
|
62
62
|
return /*#__PURE__*/React.createElement(Box, {
|
|
@@ -71,8 +71,8 @@ export default function ContainerRow(_ref) {
|
|
|
71
71
|
justifyContent: "flex-end",
|
|
72
72
|
paddingRight: 1
|
|
73
73
|
}, /*#__PURE__*/React.createElement(Text, {
|
|
74
|
-
color: isSelected ?
|
|
75
|
-
}, isSelected ?
|
|
74
|
+
color: isSelected ? 'green' : undefined
|
|
75
|
+
}, isSelected ? '➤' : ' ')), /*#__PURE__*/React.createElement(Box, {
|
|
76
76
|
width: 18,
|
|
77
77
|
flexShrink: 1,
|
|
78
78
|
paddingRight: 1
|
|
@@ -100,7 +100,7 @@ export default function ContainerRow(_ref) {
|
|
|
100
100
|
}, formatPorts(container.ports))), /*#__PURE__*/React.createElement(Box, {
|
|
101
101
|
flexShrink: 0,
|
|
102
102
|
paddingLeft: 1
|
|
103
|
-
}, state ===
|
|
103
|
+
}, state === 'running' ? /*#__PURE__*/React.createElement(StatsBar, {
|
|
104
104
|
cpu: parseFloat(stats.cpuPercent),
|
|
105
105
|
mem: parseFloat(stats.memPercent)
|
|
106
106
|
}) : null)), statsError && /*#__PURE__*/React.createElement(Text, {
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns the contextual keyboard hints for the current wizard state.
|
|
6
|
+
*
|
|
7
|
+
* @param {number} step - Current wizard step (0-3)
|
|
8
|
+
* @param {boolean} hasSuggestions - Whether suggestions/hub results are visible
|
|
9
|
+
* @param {boolean} isSearchingHub - Whether a Hub search is in progress
|
|
10
|
+
* @param {boolean} [hasSuggestedEnv=false] - Whether the image has suggestedEnv entries
|
|
11
|
+
* @returns {{ key: string, label: string }[]}
|
|
12
|
+
*/
|
|
13
|
+
function getHints(step, hasSuggestions, isSearchingHub) {
|
|
14
|
+
var hasSuggestedEnv = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
15
|
+
if (step === 0) {
|
|
16
|
+
if (hasSuggestions) {
|
|
17
|
+
return [{
|
|
18
|
+
key: '↑↓',
|
|
19
|
+
label: 'Navigate'
|
|
20
|
+
}, {
|
|
21
|
+
key: 'Enter',
|
|
22
|
+
label: 'Select'
|
|
23
|
+
}, {
|
|
24
|
+
key: 'Esc',
|
|
25
|
+
label: 'Cancel'
|
|
26
|
+
}];
|
|
27
|
+
}
|
|
28
|
+
var hints = [];
|
|
29
|
+
if (!isSearchingHub) hints.push({
|
|
30
|
+
key: 'Tab',
|
|
31
|
+
label: 'Search Hub'
|
|
32
|
+
});
|
|
33
|
+
hints.push({
|
|
34
|
+
key: '↑↓',
|
|
35
|
+
label: 'Browse'
|
|
36
|
+
});
|
|
37
|
+
hints.push({
|
|
38
|
+
key: 'Enter',
|
|
39
|
+
label: 'Confirm'
|
|
40
|
+
});
|
|
41
|
+
hints.push({
|
|
42
|
+
key: 'Esc',
|
|
43
|
+
label: 'Cancel'
|
|
44
|
+
});
|
|
45
|
+
return hints;
|
|
46
|
+
}
|
|
47
|
+
if (step === 3) {
|
|
48
|
+
var _hints = [];
|
|
49
|
+
if (hasSuggestedEnv) _hints.push({
|
|
50
|
+
key: 'Tab',
|
|
51
|
+
label: 'Insert next env'
|
|
52
|
+
});
|
|
53
|
+
_hints.push({
|
|
54
|
+
key: 'Enter',
|
|
55
|
+
label: 'Continue'
|
|
56
|
+
});
|
|
57
|
+
_hints.push({
|
|
58
|
+
key: 'Esc',
|
|
59
|
+
label: 'Cancel'
|
|
60
|
+
});
|
|
61
|
+
return _hints;
|
|
62
|
+
}
|
|
63
|
+
return [{
|
|
64
|
+
key: 'Enter',
|
|
65
|
+
label: 'Continue'
|
|
66
|
+
}, {
|
|
67
|
+
key: 'Esc',
|
|
68
|
+
label: 'Cancel'
|
|
69
|
+
}];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Displays a row of contextual keyboard shortcut hints for the active wizard step.
|
|
74
|
+
*
|
|
75
|
+
* @param {Object} props
|
|
76
|
+
* @param {number} props.step - Current wizard step
|
|
77
|
+
* @param {boolean} [props.hasSuggestions=false] - Whether suggestions/hub results are visible
|
|
78
|
+
* @param {boolean} [props.isSearchingHub=false] - Whether a Hub search is in progress
|
|
79
|
+
* @param {boolean} [props.hasSuggestedEnv=false] - Whether the image has suggestedEnv entries
|
|
80
|
+
*/
|
|
81
|
+
function ControlsHUD(_ref) {
|
|
82
|
+
var step = _ref.step,
|
|
83
|
+
_ref$hasSuggestions = _ref.hasSuggestions,
|
|
84
|
+
hasSuggestions = _ref$hasSuggestions === void 0 ? false : _ref$hasSuggestions,
|
|
85
|
+
_ref$isSearchingHub = _ref.isSearchingHub,
|
|
86
|
+
isSearchingHub = _ref$isSearchingHub === void 0 ? false : _ref$isSearchingHub,
|
|
87
|
+
_ref$hasSuggestedEnv = _ref.hasSuggestedEnv,
|
|
88
|
+
hasSuggestedEnv = _ref$hasSuggestedEnv === void 0 ? false : _ref$hasSuggestedEnv;
|
|
89
|
+
var hints = getHints(step, hasSuggestions, isSearchingHub, hasSuggestedEnv);
|
|
90
|
+
return /*#__PURE__*/React.createElement(Box, {
|
|
91
|
+
flexDirection: "row",
|
|
92
|
+
columnGap: 2
|
|
93
|
+
}, hints.map(function (_ref2) {
|
|
94
|
+
var key = _ref2.key,
|
|
95
|
+
label = _ref2.label;
|
|
96
|
+
return /*#__PURE__*/React.createElement(Box, {
|
|
97
|
+
key: key
|
|
98
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
99
|
+
color: "cyan"
|
|
100
|
+
}, "[", key, "]"), /*#__PURE__*/React.createElement(Text, {
|
|
101
|
+
dimColor: true
|
|
102
|
+
}, " ", label));
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
export default ControlsHUD;
|
|
106
|
+
export { getHints, ControlsHUD };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { Box, Text } from
|
|
3
|
-
import chalk from
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
|
-
import { getAppVersion } from
|
|
5
|
+
import { getAppVersion } from '../helpers/appInfo.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Header component displayed at the top of the CLI UI.
|
|
@@ -18,12 +18,12 @@ import { getAppVersion } from "../helpers/appInfo.js";
|
|
|
18
18
|
var VERSION = getAppVersion();
|
|
19
19
|
export default function Header(_ref) {
|
|
20
20
|
var count = _ref.count;
|
|
21
|
-
var formattedVersion = VERSION ===
|
|
21
|
+
var formattedVersion = VERSION === 'unknown' ? 'unknown' : "v".concat(VERSION);
|
|
22
22
|
return /*#__PURE__*/React.createElement(Box, {
|
|
23
23
|
justifyContent: "space-between"
|
|
24
24
|
}, /*#__PURE__*/React.createElement(Text, {
|
|
25
25
|
color: "cyanBright"
|
|
26
|
-
}, "\uD83D\uDC33 ", chalk.bold(
|
|
26
|
+
}, "\uD83D\uDC33 ", chalk.bold('CDD'), /*#__PURE__*/React.createElement(Text, {
|
|
27
27
|
color: "gray"
|
|
28
28
|
}, " \u2014 CLI Docker Dashboard")), /*#__PURE__*/React.createElement(Box, {
|
|
29
29
|
flexDirection: "column",
|
|
@@ -32,7 +32,7 @@ export default function Header(_ref) {
|
|
|
32
32
|
color: "gray"
|
|
33
33
|
}, formattedVersion), /*#__PURE__*/React.createElement(Text, {
|
|
34
34
|
color: "gray"
|
|
35
|
-
}, count, " container", count === 1 ?
|
|
35
|
+
}, count, " container", count === 1 ? '' : 's', " found")));
|
|
36
36
|
}
|
|
37
37
|
Header.propTypes = {
|
|
38
38
|
count: PropTypes.number.isRequired
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { Text } from
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text } from 'ink';
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -20,7 +20,7 @@ export default function LogViewer(_ref) {
|
|
|
20
20
|
var visibleLogs = logs.slice(-15);
|
|
21
21
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, {
|
|
22
22
|
color: "green"
|
|
23
|
-
}, (_container$name = container === null || container === void 0 ? void 0 : container.name) !== null && _container$name !== void 0 ? _container$name :
|
|
23
|
+
}, (_container$name = container === null || container === void 0 ? void 0 : container.name) !== null && _container$name !== void 0 ? _container$name : 'Container', " logs, press ESC to exit"), visibleLogs.length === 0 ? /*#__PURE__*/React.createElement(Text, {
|
|
24
24
|
dimColor: true
|
|
25
25
|
}, "No logs...") : visibleLogs.map(function (line, idx) {
|
|
26
26
|
return /*#__PURE__*/React.createElement(Text, {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { Text } from
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text } from 'ink';
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -19,7 +19,7 @@ export function PromptField(_ref) {
|
|
|
19
19
|
// If the field is required and empty, show in red
|
|
20
20
|
var isEmpty = required && !value.trim();
|
|
21
21
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, null, label), /*#__PURE__*/React.createElement(Text, {
|
|
22
|
-
color: isEmpty ?
|
|
22
|
+
color: isEmpty ? 'red' : 'cyan'
|
|
23
23
|
}, value, "_"));
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -36,7 +36,7 @@ export function PromptMessage(_ref2) {
|
|
|
36
36
|
color = _ref2.color;
|
|
37
37
|
if (!message) return null;
|
|
38
38
|
return /*#__PURE__*/React.createElement(Text, {
|
|
39
|
-
color: color ||
|
|
39
|
+
color: color || 'yellow'
|
|
40
40
|
}, message);
|
|
41
41
|
}
|
|
42
42
|
PromptField.propTypes = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { Text } from
|
|
3
|
-
import chalk from
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
5
|
var BAR_WIDTH = 8;
|
|
6
6
|
|
|
@@ -36,14 +36,14 @@ function makeBar(value, width) {
|
|
|
36
36
|
var normalized = Number.isFinite(value) ? Math.min(Math.max(value, 0), 100) : 0;
|
|
37
37
|
var filled = Math.round(normalized / 100 * safeWidth);
|
|
38
38
|
var empty = Math.max(0, safeWidth - filled);
|
|
39
|
-
var bar =
|
|
39
|
+
var bar = '█'.repeat(filled) + '░'.repeat(empty);
|
|
40
40
|
return colorize(normalized, bar);
|
|
41
41
|
}
|
|
42
42
|
function formatPercent(value) {
|
|
43
43
|
if (!Number.isFinite(value)) return 0;
|
|
44
44
|
var normalized = Math.min(Math.max(value, 0), 100);
|
|
45
45
|
if (normalized >= 10) return Math.round(normalized);
|
|
46
|
-
return normalized.toFixed(1).replace(/\.0$/,
|
|
46
|
+
return normalized.toFixed(1).replace(/\.0$/, '');
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/**
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Renders an autocomplete suggestion panel as a boxed list below the image input.
|
|
7
|
+
* Displays a window of `maxVisible` items starting at `visibleOffset`.
|
|
8
|
+
* The focused row (selectedIndex) is highlighted cyan with a › prefix.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} props
|
|
11
|
+
* @param {string[]} props.items - Full list of suggestion strings
|
|
12
|
+
* @param {number} props.selectedIndex - Currently focused index (-1 = none)
|
|
13
|
+
* @param {number} props.visibleOffset - First item index of the visible window
|
|
14
|
+
* @param {number} [props.maxVisible=6] - Maximum number of items visible at once
|
|
15
|
+
* @returns {JSX.Element|null}
|
|
16
|
+
*/
|
|
17
|
+
export function SuggestionPanel(_ref) {
|
|
18
|
+
var items = _ref.items,
|
|
19
|
+
selectedIndex = _ref.selectedIndex,
|
|
20
|
+
visibleOffset = _ref.visibleOffset,
|
|
21
|
+
_ref$maxVisible = _ref.maxVisible,
|
|
22
|
+
maxVisible = _ref$maxVisible === void 0 ? 6 : _ref$maxVisible,
|
|
23
|
+
_ref$isLoading = _ref.isLoading,
|
|
24
|
+
isLoading = _ref$isLoading === void 0 ? false : _ref$isLoading;
|
|
25
|
+
var hasItems = items && items.length > 0;
|
|
26
|
+
if (!hasItems && !isLoading) return null;
|
|
27
|
+
var visible = hasItems ? items.slice(visibleOffset, visibleOffset + maxVisible) : [];
|
|
28
|
+
return /*#__PURE__*/React.createElement(Box, {
|
|
29
|
+
flexDirection: "column",
|
|
30
|
+
borderStyle: "single",
|
|
31
|
+
borderColor: "cyan",
|
|
32
|
+
paddingX: 1,
|
|
33
|
+
"data-testid": "suggestion-panel"
|
|
34
|
+
}, visible.map(function (item, localIdx) {
|
|
35
|
+
var globalIdx = visibleOffset + localIdx;
|
|
36
|
+
var isFocused = globalIdx === selectedIndex;
|
|
37
|
+
return /*#__PURE__*/React.createElement(Text, {
|
|
38
|
+
key: item,
|
|
39
|
+
color: isFocused ? 'cyan' : undefined,
|
|
40
|
+
dimColor: !isFocused
|
|
41
|
+
}, isFocused ? "\u203A ".concat(item) : " ".concat(item));
|
|
42
|
+
}), isLoading && /*#__PURE__*/React.createElement(Text, {
|
|
43
|
+
dimColor: true,
|
|
44
|
+
color: "gray"
|
|
45
|
+
}, ' [searching Docker Hub...]'));
|
|
46
|
+
}
|
|
47
|
+
SuggestionPanel.propTypes = {
|
|
48
|
+
items: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
49
|
+
selectedIndex: PropTypes.number.isRequired,
|
|
50
|
+
visibleOffset: PropTypes.number.isRequired,
|
|
51
|
+
maxVisible: PropTypes.number,
|
|
52
|
+
isLoading: PropTypes.bool
|
|
53
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { Box, Text } from
|
|
3
|
-
var SHORTCUTS = [
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
var SHORTCUTS = ['Use ↑/↓ for navigation', 'I to initiate selected container', 'P to stop selected container', 'R to restart selected container', 'C to create a container', 'L to view logs of selected container', 'D to toggle debug log panel', 'E to remove selected container', 'Q to quit'];
|
|
4
4
|
var GRID_COLUMNS = 3;
|
|
5
5
|
|
|
6
6
|
/**
|
package/dist/helpers/appInfo.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { readFileSync } from
|
|
2
|
-
import path from
|
|
3
|
-
import { fileURLToPath } from
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
4
|
var cachedVersion;
|
|
5
5
|
export function getAppVersion() {
|
|
6
6
|
if (cachedVersion) {
|
|
@@ -10,12 +10,12 @@ export function getAppVersion() {
|
|
|
10
10
|
var _packageJson$version;
|
|
11
11
|
var __filename = fileURLToPath(import.meta.url);
|
|
12
12
|
var moduleDir = path.dirname(__filename);
|
|
13
|
-
var packageJsonPath = path.resolve(moduleDir,
|
|
14
|
-
var packageJson = JSON.parse(readFileSync(packageJsonPath,
|
|
15
|
-
cachedVersion = (_packageJson$version = packageJson.version) !== null && _packageJson$version !== void 0 ? _packageJson$version :
|
|
13
|
+
var packageJsonPath = path.resolve(moduleDir, '../../package.json');
|
|
14
|
+
var packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
15
|
+
cachedVersion = (_packageJson$version = packageJson.version) !== null && _packageJson$version !== void 0 ? _packageJson$version : 'unknown';
|
|
16
16
|
return cachedVersion;
|
|
17
17
|
} catch (error) {
|
|
18
|
-
cachedVersion =
|
|
18
|
+
cachedVersion = 'unknown';
|
|
19
19
|
return cachedVersion;
|
|
20
20
|
}
|
|
21
21
|
}
|