cdd-cli 3.1.4 → 3.1.5
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/.eslintrc.json +30 -0
- package/.github/workflows/docs.yml +35 -0
- package/CHANGELOG.md +37 -4
- package/README.md +26 -0
- package/dist/App.js +20 -3
- package/dist/components/ContainerCreationPrompt.js +27 -1
- package/dist/components/ContainerList.js +11 -15
- package/dist/components/ContainerRow.js +73 -14
- package/dist/components/ContainerSection.js +16 -2
- package/dist/components/Footer.js +7 -1
- package/dist/components/Header.js +17 -1
- package/dist/components/LogViewer.js +21 -3
- package/dist/components/MessageFeedback.js +15 -1
- package/dist/components/PromptField.js +31 -1
- package/dist/components/StatsBar.js +46 -8
- package/dist/components/UsageMenu.js +30 -1
- package/dist/helpers/actionHelpers.js +17 -6
- package/dist/helpers/constants.js +30 -0
- package/dist/helpers/dockerService/dockerService.js +19 -0
- package/dist/helpers/dockerService/serviceComponents/containerActions.js +194 -35
- package/dist/helpers/dockerService/serviceComponents/containerList.js +45 -8
- package/dist/helpers/dockerService/serviceComponents/containerLogs.js +16 -6
- package/dist/helpers/dockerService/serviceComponents/containerStats.js +28 -9
- package/dist/helpers/exitWithMessage.js +2 -1
- package/dist/helpers/logger.js +112 -0
- package/dist/helpers/safeCall.js +21 -0
- package/dist/helpers/validationHelpers.js +20 -1
- package/dist/hooks/creation/useContainerActions.js +2 -1
- package/dist/hooks/creation/useContainerCreation.js +5 -4
- package/dist/hooks/creation/useLogsViewer.js +3 -2
- package/dist/hooks/useContainers.js +6 -3
- package/dist/hooks/useControls.js +253 -122
- package/dist/hooks/useLogsStream.js +3 -2
- package/dist/index.js +1 -0
- package/docs/components_ContainerCreationPrompt.jsx.html +114 -0
- package/docs/components_ContainerRow.jsx.html +151 -0
- package/docs/components_ContainerSection.jsx.html +75 -0
- package/docs/components_Footer.jsx.html +66 -0
- package/docs/components_Header.jsx.html +84 -0
- package/docs/components_LogViewer.jsx.html +91 -0
- package/docs/components_MessageFeedback.jsx.html +76 -0
- package/docs/components_PromptField.jsx.html +99 -0
- package/docs/components_StatsBar.jsx.html +105 -0
- package/docs/components_UsageMenu.jsx.html +73 -0
- package/docs/fonts/OpenSans-Bold-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Bold-webfont.svg +1830 -0
- package/docs/fonts/OpenSans-Bold-webfont.woff +0 -0
- package/docs/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
- package/docs/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Italic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Italic-webfont.svg +1830 -0
- package/docs/fonts/OpenSans-Italic-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Light-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Light-webfont.svg +1831 -0
- package/docs/fonts/OpenSans-Light-webfont.woff +0 -0
- package/docs/fonts/OpenSans-LightItalic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
- package/docs/fonts/OpenSans-LightItalic-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Regular-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Regular-webfont.svg +1831 -0
- package/docs/fonts/OpenSans-Regular-webfont.woff +0 -0
- package/docs/global.html +6145 -0
- package/docs/helpers_actionHelpers.js.html +94 -0
- package/docs/helpers_constants.js.html +81 -0
- package/docs/helpers_dockerService_dockerService.js.html +79 -0
- package/docs/helpers_dockerService_serviceComponents_containerActions.js.html +153 -0
- package/docs/helpers_dockerService_serviceComponents_containerList.js.html +81 -0
- package/docs/helpers_dockerService_serviceComponents_containerLogs.js.html +85 -0
- package/docs/helpers_dockerService_serviceComponents_containerStats.js.html +107 -0
- package/docs/helpers_dockerService_serviceComponents_imageUtils.js.html +82 -0
- package/docs/helpers_exitWithMessage.js.html +77 -0
- package/docs/helpers_safeCall.js.html +68 -0
- package/docs/helpers_validationHelpers.js.html +96 -0
- package/docs/hooks_creation_useContainerActions.js.html +120 -0
- package/docs/hooks_creation_useContainerCreation.js.html +152 -0
- package/docs/hooks_creation_useLogsViewer.js.html +102 -0
- package/docs/hooks_useControls.js.html +298 -0
- package/docs/hooks_useLogsStream.js.html +82 -0
- package/docs/index.html +65 -0
- package/docs/index.js.html +65 -0
- package/docs/module-index.html +171 -0
- package/docs/scripts/linenumber.js +25 -0
- package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
- package/docs/scripts/prettify/lang-css.js +2 -0
- package/docs/scripts/prettify/prettify.js +28 -0
- package/docs/styles/jsdoc-default.css +358 -0
- package/docs/styles/prettify-jsdoc.css +111 -0
- package/docs/styles/prettify-tomorrow.css +132 -0
- package/fix-imports.cjs +33 -6
- package/jest.config.cjs +2 -1
- package/package.json +19 -9
- package/src/App.jsx +16 -2
- package/src/components/ContainerCreationPrompt.jsx +26 -0
- package/src/components/ContainerList.jsx +12 -13
- package/src/components/ContainerRow.jsx +55 -20
- package/src/components/ContainerSection.jsx +15 -1
- package/src/components/Footer.jsx +6 -1
- package/src/components/Header.jsx +16 -0
- package/src/components/LogViewer.jsx +19 -2
- package/src/components/MessageFeedback.jsx +14 -0
- package/src/components/PromptField.jsx +30 -0
- package/src/components/StatsBar.jsx +47 -8
- package/src/components/UsageMenu.jsx +38 -8
- package/src/helpers/actionHelpers.js +16 -7
- package/src/helpers/constants.js +30 -0
- package/src/helpers/dockerService/dockerService.js +19 -0
- package/src/helpers/dockerService/serviceComponents/containerActions.js +123 -12
- package/src/helpers/dockerService/serviceComponents/containerList.js +61 -23
- package/src/helpers/dockerService/serviceComponents/containerLogs.js +20 -6
- package/src/helpers/dockerService/serviceComponents/containerStats.js +24 -5
- package/src/helpers/dockerService/serviceComponents/imageUtils.js +1 -1
- package/src/helpers/exitWithMessage.js +2 -1
- package/src/helpers/logger.js +113 -0
- package/src/helpers/safeCall.js +18 -0
- package/src/helpers/validationHelpers.js +21 -3
- package/src/hooks/creation/useContainerActions.js +4 -3
- package/src/hooks/creation/useContainerCreation.js +5 -4
- package/src/hooks/creation/useLogsViewer.js +2 -1
- package/src/hooks/useContainers.js +6 -3
- package/src/hooks/useControls.js +244 -91
- package/src/hooks/useLogsStream.js +2 -1
- package/src/index.js +1 -0
- package/test/actionHelpers.test.js +106 -0
- package/test/containerActions.test.js +138 -0
- package/test/containerList.test.js +79 -0
- package/test/jest.setup.js +7 -0
- package/test/safeCall.test.js +25 -0
- package/test/useContainerCreation.dom.test.js +97 -0
- package/test/validationHelpers.test.js +2 -2
- package/dist/cdd.bundle.js +0 -8
- package/dist/components/ContainerCreator.js +0 -81
- package/dist/components/StatsViewer.js +0 -0
- package/dist/helpers/dockerActions.js +0 -64
- package/dist/helpers/dockerService.js +0 -284
- package/dist/hooks/useStatsPolling.js +0 -54
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"env": {
|
|
3
|
+
"browser": true,
|
|
4
|
+
"node": true,
|
|
5
|
+
"es2021": true,
|
|
6
|
+
"jest": true
|
|
7
|
+
},
|
|
8
|
+
"extends": ["eslint:recommended", "plugin:react/recommended"],
|
|
9
|
+
"parserOptions": {
|
|
10
|
+
"ecmaVersion": 2021,
|
|
11
|
+
"sourceType": "module",
|
|
12
|
+
"ecmaFeatures": { "jsx": true }
|
|
13
|
+
},
|
|
14
|
+
"rules": {
|
|
15
|
+
"no-unsafe-optional-chaining": "error",
|
|
16
|
+
"react/react-in-jsx-scope": "off",
|
|
17
|
+
"no-console": "off",
|
|
18
|
+
"react/prop-types": "off",
|
|
19
|
+
"no-unused-vars": [
|
|
20
|
+
"warn",
|
|
21
|
+
{
|
|
22
|
+
"argsIgnorePattern": "^_",
|
|
23
|
+
"varsIgnorePattern": "^(?:_|React)$"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"settings": {
|
|
28
|
+
"react": { "version": "detect" }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Build and deploy docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-and-deploy:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: write
|
|
13
|
+
pages: write
|
|
14
|
+
id-token: write
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout repository
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Setup Node.js
|
|
21
|
+
uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version: '18'
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: npm ci
|
|
27
|
+
|
|
28
|
+
- name: Build docs
|
|
29
|
+
run: npm run docs
|
|
30
|
+
|
|
31
|
+
- name: Deploy to GitHub Pages (push to gh-pages)
|
|
32
|
+
uses: peaceiris/actions-gh-pages@v3
|
|
33
|
+
with:
|
|
34
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
35
|
+
publish_dir: ./docs
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,43 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
6
6
|
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
- Nothing yet.
|
|
9
|
+
|
|
10
|
+
## [3.1.5] - 2025-10-18
|
|
11
|
+
_Note: I'm sorry about the issues you've run into; this is a brand-new app and I'm still learning, but I'm moving as quickly as I can to deliver a quality experience._
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `safeCall` utility to safely invoke optional callbacks and avoid uncaught errors from consumer callbacks.
|
|
16
|
+
- PropTypes added to core React components for runtime prop validation (ContainerRow, ContainerList, LogViewer, StatsBar, ContainerCreationPrompt, PromptField, MessageFeedback, Header, ContainerSection).
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Container creation now inspects image `EXPOSE` declarations and auto-binds ports when the user leaves the ports step empty, reusing the next free host port to avoid conflicts (e.g. second `nginx` gets `81:80`).
|
|
21
|
+
- CLI clears the terminal when starting and again after exiting with `q` for a cleaner shell experience.
|
|
22
|
+
- Replaced direct optional callback invocations and stream destruction calls with `safeCall` in several modules (`containerLogs`, `useLogsStream`, `useLogsViewer`, `useContainerCreation`, `useContainerActions`, `LogViewer`).
|
|
23
|
+
- ESLint config updated to include `jest` env and allow keeping `import React` for build compatibility.
|
|
24
|
+
- Extracted common magic numbers into `src/helpers/constants.js` and replaced hardcoded values across the codebase:
|
|
25
|
+
- `REFRESH_INTERVALS.CONTAINER_LIST` (3000)
|
|
26
|
+
- `REFRESH_INTERVALS.CONTAINER_STATS` (1500)
|
|
27
|
+
- `MESSAGE_TIMEOUTS.SHORT` (2000) and `MESSAGE_TIMEOUTS.DEFAULT` (3000)
|
|
28
|
+
- `EXIT_DELAY` (500)
|
|
29
|
+
- `TIMEOUTS.CONTAINER_OP` (30000) and `TIMEOUTS.PULL_IMAGE` (300000)
|
|
30
|
+
- Files updated to use constants: `useContainers`, `ContainerRow`, `actionHelpers`, `useControls`, `exitWithMessage`, `containerActions`.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Added unit tests for `safeCall` and adjusted several components to silence lint warnings (unused variables).
|
|
35
|
+
- Clamp `StatsBar` CPU/memory percentages to avoid negative repeat errors when streaming container stats (also refreshed component JSDoc to note the clamping).
|
|
36
|
+
|
|
37
|
+
### Testing
|
|
38
|
+
|
|
39
|
+
- Added additional unit tests for Docker service functions and hooks. All tests pass locally (5 suites, 26 tests).
|
|
40
|
+
- Removed generated `dist/` artifacts and deleted obsolete test files that referenced incompatible test helpers.
|
|
41
|
+
|
|
42
|
+
Incoming fixes and smaller tests / docs updates
|
|
43
|
+
|
|
7
44
|
## [3.1.4] - 2025-10-17
|
|
8
45
|
|
|
9
46
|
### Changed
|
|
@@ -51,10 +88,6 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
51
88
|
|
|
52
89
|
- Various runtime & build issues after modularization (import fixes, exposing container action functions, creation flow wiring).
|
|
53
90
|
|
|
54
|
-
## [Unreleased]
|
|
55
|
-
|
|
56
|
-
- Incoming fixes and smaller tests / docs updates
|
|
57
|
-
|
|
58
91
|
## [3.1.1] - 2025-10-16
|
|
59
92
|
|
|
60
93
|
### Fixed
|
package/README.md
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
<a href="https://github.com/caertos/cdd/actions/workflows/ci.yml">
|
|
7
7
|
<img src="https://github.com/caertos/cdd/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI status" />
|
|
8
8
|
</a>
|
|
9
|
+
<!-- Documentation published to GitHub Pages -->
|
|
10
|
+
<a href="https://caertos.github.io/cdd/">
|
|
11
|
+
<img src="https://img.shields.io/badge/docs-GitHub%20Pages-blue" alt="Docs" />
|
|
12
|
+
</a>
|
|
9
13
|
</p>
|
|
10
14
|
|
|
11
15
|
Short, bilingual README with quickstart, development and tests.
|
|
@@ -46,6 +50,7 @@ cdd
|
|
|
46
50
|
- R: restart selected container
|
|
47
51
|
- C: create container (interactive prompt)
|
|
48
52
|
- L: view logs for selected container
|
|
53
|
+
- D: toggle the on-screen debug log panel
|
|
49
54
|
- E: erase (remove) selected container (confirmation required)
|
|
50
55
|
- Q: quit
|
|
51
56
|
|
|
@@ -113,6 +118,27 @@ Tests are located in `test/` and cover utility helpers.
|
|
|
113
118
|
|
|
114
119
|
---
|
|
115
120
|
|
|
121
|
+
## Logging
|
|
122
|
+
|
|
123
|
+
- The CLI logs high-level `info`, `warn`, and `error` messages by default so you see meaningful feedback without noise.
|
|
124
|
+
- When you need deeper diagnostics (for example, to troubleshoot why stats or logs are not updating), start the CLI with debug logging enabled:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
CDD_LOG_LEVEL=debug cdd # if you installed globally
|
|
128
|
+
# or
|
|
129
|
+
CDD_LOG_LEVEL=debug node dist/index.js
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
- The same effect can be achieved with `LOG_LEVEL=debug`. Set `LOG_LEVEL=warn` if you only want to see warnings and errors.
|
|
133
|
+
- Inside the CLI, press `D` to toggle the live debug log panel; press `D` again or `Esc` to hide it.
|
|
134
|
+
- Redirect output to a file when users report issues so they can share the log easily:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
CDD_LOG_LEVEL=debug cdd > cdd-debug.log 2>&1
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
116
142
|
## License
|
|
117
143
|
|
|
118
144
|
This project is MIT/ISC licensed (see `LICENSE`).
|
package/dist/App.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Main React component for the CDD CLI UI.
|
|
3
|
+
* Handles container listing, action feedback, optional creation prompt,
|
|
4
|
+
* and renders the live debug log panel (toggle with the D shortcut).
|
|
3
5
|
*
|
|
4
6
|
* @component
|
|
5
|
-
* @returns {JSX.Element} The rendered app
|
|
7
|
+
* @returns {JSX.Element} The rendered app
|
|
6
8
|
* @example
|
|
7
|
-
* //
|
|
9
|
+
* // Render the app
|
|
8
10
|
* <App />
|
|
9
11
|
*/
|
|
10
12
|
import React from "react";
|
|
@@ -46,7 +48,22 @@ export default function App() {
|
|
|
46
48
|
}), /*#__PURE__*/React.createElement(Spacer, null), /*#__PURE__*/React.createElement(MessageFeedback, {
|
|
47
49
|
message: controls.message,
|
|
48
50
|
color: controls.messageColor
|
|
49
|
-
}), /*#__PURE__*/React.createElement(UsageMenu, null),
|
|
51
|
+
}), /*#__PURE__*/React.createElement(UsageMenu, null), controls.showDebugLogs && /*#__PURE__*/React.createElement(Box, {
|
|
52
|
+
marginTop: 1,
|
|
53
|
+
flexDirection: "column",
|
|
54
|
+
borderStyle: "round",
|
|
55
|
+
borderColor: "gray",
|
|
56
|
+
padding: 1
|
|
57
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
58
|
+
color: "cyan"
|
|
59
|
+
}, "Debug log \u2014 press D or ESC to close"), controls.debugLogs.length === 0 ? /*#__PURE__*/React.createElement(Text, {
|
|
60
|
+
dimColor: true
|
|
61
|
+
}, "No debug entries yet. Run with CDD_LOG_LEVEL=debug for verbose output.") : controls.debugLogs.slice(-15).map(function (line, idx) {
|
|
62
|
+
return /*#__PURE__*/React.createElement(Text, {
|
|
63
|
+
key: idx,
|
|
64
|
+
color: "gray"
|
|
65
|
+
}, line);
|
|
66
|
+
})), /*#__PURE__*/React.createElement(Footer, null)), controls.showLogs && /*#__PURE__*/React.createElement(LogViewer, {
|
|
50
67
|
logs: controls.logs,
|
|
51
68
|
onExit: controls.exitLogs,
|
|
52
69
|
container: containers[controls.selected]
|
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import { PromptField, PromptMessage } from "./PromptField.js";
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Prompt UI shown when creating a new container.
|
|
8
|
+
* Presents a step-by-step form (image, name, ports, env) and displays
|
|
9
|
+
* a contextual message provided by the creation hook.
|
|
10
|
+
*
|
|
11
|
+
* @param {Object} props
|
|
12
|
+
* @param {number} props.step - Current step index (0..3)
|
|
13
|
+
* @param {string} props.imageName - Value for the image name field
|
|
14
|
+
* @param {string} props.containerName - Value for the container name field
|
|
15
|
+
* @param {string} props.portInput - Value for the ports input field
|
|
16
|
+
* @param {string} props.envInput - Value for the environment variables field
|
|
17
|
+
* @param {string} props.message - Contextual message to display (errors/hints)
|
|
18
|
+
* @param {string} props.messageColor - Color for the contextual message
|
|
19
|
+
* @returns {JSX.Element}
|
|
20
|
+
*/
|
|
4
21
|
export default function ContainerCreationPrompt(props) {
|
|
5
22
|
var step = props.step,
|
|
6
23
|
imageName = props.imageName,
|
|
@@ -45,4 +62,13 @@ export default function ContainerCreationPrompt(props) {
|
|
|
45
62
|
}), /*#__PURE__*/React.createElement(Text, {
|
|
46
63
|
dimColor: true
|
|
47
64
|
}, "Press Enter to continue, Escape to cancel"));
|
|
48
|
-
}
|
|
65
|
+
}
|
|
66
|
+
ContainerCreationPrompt.propTypes = {
|
|
67
|
+
step: PropTypes.number.isRequired,
|
|
68
|
+
imageName: PropTypes.string,
|
|
69
|
+
containerName: PropTypes.string,
|
|
70
|
+
portInput: PropTypes.string,
|
|
71
|
+
envInput: PropTypes.string,
|
|
72
|
+
message: PropTypes.string,
|
|
73
|
+
messageColor: PropTypes.string
|
|
74
|
+
};
|
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
* List component for Docker containers.
|
|
3
3
|
*
|
|
4
4
|
* @component
|
|
5
|
-
* @param {Object} props - Component props
|
|
6
|
-
* @param {Array} props.containers - Containers to display
|
|
7
|
-
* @returns {JSX.Element} Rendered list
|
|
5
|
+
* @param {Object} props - Component props
|
|
6
|
+
* @param {Array} props.containers - Containers to display
|
|
7
|
+
* @returns {JSX.Element} Rendered list
|
|
8
8
|
* @example
|
|
9
9
|
* // Render with containers
|
|
10
10
|
* <ContainerList containers={containers} />
|
|
11
11
|
*/
|
|
12
12
|
import React from "react";
|
|
13
|
-
import
|
|
13
|
+
import PropTypes from 'prop-types';
|
|
14
|
+
import { Box } from "ink";
|
|
14
15
|
import ContainerRow from "./ContainerRow.js";
|
|
15
16
|
export default function ContainerList(_ref) {
|
|
16
17
|
var containers = _ref.containers,
|
|
@@ -19,19 +20,14 @@ export default function ContainerList(_ref) {
|
|
|
19
20
|
return /*#__PURE__*/React.createElement(Box, {
|
|
20
21
|
key: container.id,
|
|
21
22
|
flexDirection: "row",
|
|
22
|
-
alignItems: "flex-start"
|
|
23
|
-
}, /*#__PURE__*/React.createElement(Box, {
|
|
24
|
-
width: 2,
|
|
25
|
-
minWidth: 2,
|
|
26
|
-
justifyContent: "flex-end"
|
|
27
|
-
}, /*#__PURE__*/React.createElement(Text, {
|
|
28
|
-
color: i === selected ? "green" : undefined
|
|
29
|
-
}, i === selected ? "➤" : " ")), /*#__PURE__*/React.createElement(Box, {
|
|
30
|
-
flexGrow: 1,
|
|
31
23
|
paddingLeft: 1
|
|
32
24
|
}, /*#__PURE__*/React.createElement(ContainerRow, {
|
|
33
25
|
container: container,
|
|
34
26
|
isSelected: i === selected
|
|
35
|
-
}))
|
|
27
|
+
}));
|
|
36
28
|
}));
|
|
37
|
-
}
|
|
29
|
+
}
|
|
30
|
+
ContainerList.propTypes = {
|
|
31
|
+
containers: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
32
|
+
selected: PropTypes.number
|
|
33
|
+
};
|
|
@@ -11,7 +11,9 @@ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
|
11
11
|
import React, { useState, useEffect } from "react";
|
|
12
12
|
import { Box, Text } from "ink";
|
|
13
13
|
import { getStats } from "../helpers/dockerService/serviceComponents/containerStats.js";
|
|
14
|
+
import { REFRESH_INTERVALS } from "../helpers/constants.js";
|
|
14
15
|
import StatsBar from "./StatsBar.js";
|
|
16
|
+
import PropTypes from 'prop-types';
|
|
15
17
|
var stateText = function stateText(state) {
|
|
16
18
|
if (state === "running") return {
|
|
17
19
|
text: "🟢 RUNNING",
|
|
@@ -30,8 +32,18 @@ var stateText = function stateText(state) {
|
|
|
30
32
|
color: "gray"
|
|
31
33
|
};
|
|
32
34
|
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Row component that renders information and live stats for a container.
|
|
38
|
+
*
|
|
39
|
+
* @param {Object} props
|
|
40
|
+
* @param {Object} props.container - Container object with id, name, image, state and ports
|
|
41
|
+
* @returns {JSX.Element}
|
|
42
|
+
*/
|
|
33
43
|
export default function ContainerRow(_ref) {
|
|
34
|
-
var container = _ref.container
|
|
44
|
+
var container = _ref.container,
|
|
45
|
+
_ref$isSelected = _ref.isSelected,
|
|
46
|
+
isSelected = _ref$isSelected === void 0 ? false : _ref$isSelected;
|
|
35
47
|
var id = container.id,
|
|
36
48
|
name = container.name,
|
|
37
49
|
image = container.image,
|
|
@@ -48,15 +60,22 @@ export default function ContainerRow(_ref) {
|
|
|
48
60
|
stats = _useState2[0],
|
|
49
61
|
setStats = _useState2[1];
|
|
50
62
|
|
|
51
|
-
// Format ports for display
|
|
63
|
+
// Format ports for display (no leading space to avoid layout shifts)
|
|
52
64
|
var formatPorts = function formatPorts(ports) {
|
|
53
65
|
if (!ports || ports.length === 0) return "";
|
|
54
66
|
if (Array.isArray(ports)) {
|
|
55
|
-
return ports.map(function (p,
|
|
56
|
-
return "
|
|
67
|
+
return ports.map(function (p, _i) {
|
|
68
|
+
return "\uD83D\uDD17 ".concat(p);
|
|
57
69
|
}).join(" ");
|
|
58
70
|
}
|
|
59
|
-
return "
|
|
71
|
+
return "\uD83D\uDD17 ".concat(ports);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Helper to truncate long strings to a max length without adding trailing spaces
|
|
75
|
+
var truncate = function truncate(s) {
|
|
76
|
+
var max = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 20;
|
|
77
|
+
if (!s) return "";
|
|
78
|
+
return s.length > max ? s.slice(0, max - 1) + "…" : s;
|
|
60
79
|
};
|
|
61
80
|
var _useState3 = useState(""),
|
|
62
81
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
@@ -106,7 +125,7 @@ export default function ContainerRow(_ref) {
|
|
|
106
125
|
};
|
|
107
126
|
}();
|
|
108
127
|
fetchStats();
|
|
109
|
-
var timer = setInterval(fetchStats,
|
|
128
|
+
var timer = setInterval(fetchStats, REFRESH_INTERVALS.CONTAINER_STATS);
|
|
110
129
|
return function () {
|
|
111
130
|
isMounted = false;
|
|
112
131
|
clearInterval(timer);
|
|
@@ -116,18 +135,58 @@ export default function ContainerRow(_ref) {
|
|
|
116
135
|
return /*#__PURE__*/React.createElement(Box, {
|
|
117
136
|
flexDirection: "column",
|
|
118
137
|
marginBottom: 1
|
|
119
|
-
}, /*#__PURE__*/React.createElement(Box,
|
|
138
|
+
}, /*#__PURE__*/React.createElement(Box, {
|
|
139
|
+
flexDirection: "row",
|
|
140
|
+
alignItems: "center"
|
|
141
|
+
}, /*#__PURE__*/React.createElement(Box, {
|
|
142
|
+
width: 2,
|
|
143
|
+
minWidth: 2,
|
|
144
|
+
justifyContent: "flex-end",
|
|
145
|
+
paddingRight: 1
|
|
146
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
147
|
+
color: isSelected ? "green" : undefined
|
|
148
|
+
}, isSelected ? "➤" : " ")), /*#__PURE__*/React.createElement(Box, {
|
|
149
|
+
width: 18,
|
|
150
|
+
flexShrink: 1,
|
|
151
|
+
paddingRight: 1
|
|
152
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
120
153
|
color: "cyan"
|
|
121
|
-
}, name
|
|
154
|
+
}, truncate(name, 18))), /*#__PURE__*/React.createElement(Box, {
|
|
155
|
+
width: 18,
|
|
156
|
+
flexShrink: 1,
|
|
157
|
+
paddingRight: 1
|
|
158
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
122
159
|
color: "gray"
|
|
123
|
-
}, image
|
|
160
|
+
}, truncate(image, 18))), /*#__PURE__*/React.createElement(Box, {
|
|
161
|
+
width: 14,
|
|
162
|
+
minWidth: 12,
|
|
163
|
+
paddingRight: 1
|
|
164
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
124
165
|
color: stateInfo.color
|
|
125
|
-
}, stateInfo.text), /*#__PURE__*/React.createElement(
|
|
166
|
+
}, stateInfo.text)), /*#__PURE__*/React.createElement(Box, {
|
|
167
|
+
flexGrow: 1,
|
|
168
|
+
flexShrink: 1,
|
|
169
|
+
paddingLeft: 0,
|
|
170
|
+
paddingRight: 1
|
|
171
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
126
172
|
color: "yellow"
|
|
127
|
-
}, formatPorts(container.ports))
|
|
173
|
+
}, formatPorts(container.ports))), /*#__PURE__*/React.createElement(Box, {
|
|
174
|
+
flexShrink: 0,
|
|
175
|
+
paddingLeft: 1
|
|
176
|
+
}, state === "running" ? /*#__PURE__*/React.createElement(StatsBar, {
|
|
128
177
|
cpu: parseFloat(stats.cpuPercent),
|
|
129
178
|
mem: parseFloat(stats.memPercent)
|
|
130
|
-
})
|
|
179
|
+
}) : null)), statsError && /*#__PURE__*/React.createElement(Text, {
|
|
131
180
|
color: "red"
|
|
132
|
-
}, statsError))
|
|
133
|
-
}
|
|
181
|
+
}, statsError));
|
|
182
|
+
}
|
|
183
|
+
ContainerRow.propTypes = {
|
|
184
|
+
container: PropTypes.shape({
|
|
185
|
+
id: PropTypes.string.isRequired,
|
|
186
|
+
name: PropTypes.string.isRequired,
|
|
187
|
+
image: PropTypes.string,
|
|
188
|
+
state: PropTypes.string,
|
|
189
|
+
ports: PropTypes.oneOfType([PropTypes.array, PropTypes.string])
|
|
190
|
+
}).isRequired,
|
|
191
|
+
isSelected: PropTypes.bool
|
|
192
|
+
};
|
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Text } from "ink";
|
|
3
3
|
import ContainerList from "./ContainerList.js";
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Section that displays the list of containers or a message when none exist.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} props
|
|
10
|
+
* @param {Array<Object>} props.containers - Array of container objects to display
|
|
11
|
+
* @param {number} [props.selected] - Index of the currently selected container
|
|
12
|
+
* @returns {JSX.Element}
|
|
13
|
+
*/
|
|
4
14
|
export default function ContainerSection(_ref) {
|
|
5
15
|
var containers = _ref.containers,
|
|
6
16
|
selected = _ref.selected;
|
|
7
|
-
if (containers.length === 0) {
|
|
17
|
+
if (!containers || containers.length === 0) {
|
|
8
18
|
return /*#__PURE__*/React.createElement(Text, null, "No containers found");
|
|
9
19
|
}
|
|
10
20
|
return /*#__PURE__*/React.createElement(ContainerList, {
|
|
11
21
|
containers: containers,
|
|
12
22
|
selected: selected
|
|
13
23
|
});
|
|
14
|
-
}
|
|
24
|
+
}
|
|
25
|
+
ContainerSection.propTypes = {
|
|
26
|
+
containers: PropTypes.array.isRequired,
|
|
27
|
+
selected: PropTypes.number
|
|
28
|
+
};
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Simple footer displayed at the bottom of the interface.
|
|
6
|
+
*
|
|
7
|
+
* @returns {JSX.Element}
|
|
8
|
+
*/
|
|
3
9
|
export default function Footer() {
|
|
4
10
|
return /*#__PURE__*/React.createElement(Box, {
|
|
5
11
|
justifyContent: "flex-end",
|
|
6
12
|
width: "100%"
|
|
7
13
|
}, /*#__PURE__*/React.createElement(Text, {
|
|
8
14
|
dimColor: true
|
|
9
|
-
}, "Crafted by Carlos Cochero
|
|
15
|
+
}, "Crafted by Carlos Cochero 2025"));
|
|
10
16
|
}
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Header component displayed at the top of the CLI UI.
|
|
8
|
+
*
|
|
9
|
+
* @component
|
|
10
|
+
* @param {Object} props
|
|
11
|
+
* @param {number} props.count - Number of containers currently detected
|
|
12
|
+
* @returns {JSX.Element}
|
|
13
|
+
* @example
|
|
14
|
+
* <Header count={3} />
|
|
15
|
+
*/
|
|
16
|
+
|
|
4
17
|
export default function Header(_ref) {
|
|
5
18
|
var count = _ref.count;
|
|
6
19
|
return /*#__PURE__*/React.createElement(Box, {
|
|
@@ -12,4 +25,7 @@ export default function Header(_ref) {
|
|
|
12
25
|
}, " \u2014 CLI Docker Dashboard")), /*#__PURE__*/React.createElement(Text, {
|
|
13
26
|
color: "gray"
|
|
14
27
|
}, count, " container", count === 1 ? "" : "s", " found"));
|
|
15
|
-
}
|
|
28
|
+
}
|
|
29
|
+
Header.propTypes = {
|
|
30
|
+
count: PropTypes.number.isRequired
|
|
31
|
+
};
|
|
@@ -1,22 +1,40 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Text, useInput } from "ink";
|
|
3
|
+
import { safeCall } from "../helpers/safeCall.js";
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Log viewer overlay component. Shows the most recent lines and closes on ESC.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} props
|
|
10
|
+
* @param {Array<string>} props.logs - Array of log lines
|
|
11
|
+
* @param {Function} [props.onExit] - Optional callback when the viewer closes
|
|
12
|
+
* @param {Object} [props.container] - Optional container metadata (used for title)
|
|
13
|
+
* @returns {JSX.Element}
|
|
14
|
+
*/
|
|
3
15
|
export default function LogViewer(_ref) {
|
|
16
|
+
var _container$name;
|
|
4
17
|
var logs = _ref.logs,
|
|
5
18
|
onExit = _ref.onExit,
|
|
6
19
|
container = _ref.container;
|
|
7
20
|
useInput(function (input, key) {
|
|
8
21
|
if (key.escape) {
|
|
9
|
-
onExit
|
|
22
|
+
safeCall(onExit);
|
|
10
23
|
}
|
|
11
24
|
});
|
|
12
25
|
var visibleLogs = logs.slice(-15);
|
|
13
26
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, {
|
|
14
27
|
color: "green"
|
|
15
|
-
}, (container === null || container === void 0 ? void 0 : container.name)
|
|
28
|
+
}, (_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, {
|
|
16
29
|
dimColor: true
|
|
17
30
|
}, "No logs...") : visibleLogs.map(function (line, idx) {
|
|
18
31
|
return /*#__PURE__*/React.createElement(Text, {
|
|
19
32
|
key: idx
|
|
20
33
|
}, line);
|
|
21
34
|
}));
|
|
22
|
-
}
|
|
35
|
+
}
|
|
36
|
+
LogViewer.propTypes = {
|
|
37
|
+
logs: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
38
|
+
onExit: PropTypes.func,
|
|
39
|
+
container: PropTypes.object
|
|
40
|
+
};
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Small component that shows a feedback message in the UI.
|
|
7
|
+
*
|
|
8
|
+
* @param {Object} props
|
|
9
|
+
* @param {string} props.message - Message to display. If falsy, component returns null.
|
|
10
|
+
* @param {string} [props.color] - Optional color name for the message text.
|
|
11
|
+
* @returns {JSX.Element|null}
|
|
12
|
+
*/
|
|
3
13
|
export default function MessageFeedback(_ref) {
|
|
4
14
|
var message = _ref.message,
|
|
5
15
|
color = _ref.color;
|
|
@@ -9,4 +19,8 @@ export default function MessageFeedback(_ref) {
|
|
|
9
19
|
}, /*#__PURE__*/React.createElement(Text, {
|
|
10
20
|
color: color
|
|
11
21
|
}, message));
|
|
12
|
-
}
|
|
22
|
+
}
|
|
23
|
+
MessageFeedback.propTypes = {
|
|
24
|
+
message: PropTypes.string,
|
|
25
|
+
color: PropTypes.string
|
|
26
|
+
};
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Text } from "ink";
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* PromptField shows a label and the current typed value for an input field.
|
|
7
|
+
* The cursor is represented with a trailing underscore.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} props
|
|
10
|
+
* @param {string} props.label - Label shown above the input value
|
|
11
|
+
* @param {string} props.value - Current value of the input
|
|
12
|
+
* @param {boolean} [props.required=false] - Whether the field is required
|
|
13
|
+
* @returns {JSX.Element}
|
|
14
|
+
*/
|
|
3
15
|
export function PromptField(_ref) {
|
|
4
16
|
var label = _ref.label,
|
|
5
17
|
value = _ref.value,
|
|
@@ -10,6 +22,15 @@ export function PromptField(_ref) {
|
|
|
10
22
|
color: isEmpty ? "red" : "cyan"
|
|
11
23
|
}, value, "_"));
|
|
12
24
|
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Small helper to show a message below prompts (errors, hints, etc.).
|
|
28
|
+
*
|
|
29
|
+
* @param {Object} props
|
|
30
|
+
* @param {string} props.message - Message text to display
|
|
31
|
+
* @param {string} [props.color] - Optional color for the message
|
|
32
|
+
* @returns {JSX.Element|null}
|
|
33
|
+
*/
|
|
13
34
|
export function PromptMessage(_ref2) {
|
|
14
35
|
var message = _ref2.message,
|
|
15
36
|
color = _ref2.color;
|
|
@@ -17,4 +38,13 @@ export function PromptMessage(_ref2) {
|
|
|
17
38
|
return /*#__PURE__*/React.createElement(Text, {
|
|
18
39
|
color: color || "yellow"
|
|
19
40
|
}, message);
|
|
20
|
-
}
|
|
41
|
+
}
|
|
42
|
+
PromptField.propTypes = {
|
|
43
|
+
label: PropTypes.string,
|
|
44
|
+
value: PropTypes.string,
|
|
45
|
+
required: PropTypes.bool
|
|
46
|
+
};
|
|
47
|
+
PromptMessage.propTypes = {
|
|
48
|
+
message: PropTypes.string,
|
|
49
|
+
color: PropTypes.string
|
|
50
|
+
};
|