cdd-cli 3.1.7 → 3.1.9

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 (96) hide show
  1. package/.eslintrc.json +1 -1
  2. package/.github/workflows/ci.yml +3 -0
  3. package/.github/workflows/docs.yml +8 -1
  4. package/CHANGELOG.md +23 -0
  5. package/CONTRIBUTING.md +28 -15
  6. package/README.md +56 -88
  7. package/babel.config.cjs +18 -1
  8. package/dist/components/ContainerRow.js +5 -78
  9. package/dist/components/LogViewer.js +4 -9
  10. package/dist/helpers/containerOptionsBuilder.js +58 -0
  11. package/dist/hooks/creation/useContainerCreation.js +16 -1
  12. package/dist/hooks/debug/useDebugLogs.js +46 -0
  13. package/dist/hooks/navigation/useContainerSelection.js +36 -0
  14. package/dist/hooks/useContainerCommandRouter.js +151 -0
  15. package/dist/{helpers/actionHelpers.js → hooks/useContainerStats.js} +76 -78
  16. package/dist/hooks/useControls.js +83 -342
  17. package/dist/hooks/useEraseConfirmation.js +47 -0
  18. package/dist/hooks/useExitHandler.js +68 -0
  19. package/jest.config.cjs +1 -1
  20. package/package.json +2 -3
  21. package/src/components/ContainerRow.jsx +3 -38
  22. package/src/components/LogViewer.jsx +4 -10
  23. package/src/helpers/containerOptionsBuilder.js +42 -0
  24. package/src/hooks/creation/useContainerCreation.js +15 -0
  25. package/src/hooks/debug/useDebugLogs.js +29 -0
  26. package/src/hooks/navigation/useContainerSelection.js +25 -0
  27. package/src/hooks/useContainerCommandRouter.js +110 -0
  28. package/src/hooks/useContainerStats.js +44 -0
  29. package/src/hooks/useControls.js +68 -272
  30. package/src/hooks/useEraseConfirmation.js +39 -0
  31. package/src/hooks/useExitHandler.js +66 -0
  32. package/test/useContainerCreation.dom.test.js +3 -0
  33. package/.babelrc +0 -6
  34. package/FIXES_APPLIED.md +0 -419
  35. package/SECURITY.md +0 -5
  36. package/dist/hooks/useLogsStream.js +0 -41
  37. package/docs/_config.yml +0 -13
  38. package/docs/components_ContainerCreationPrompt.jsx.html +0 -114
  39. package/docs/components_ContainerRow.jsx.html +0 -151
  40. package/docs/components_ContainerSection.jsx.html +0 -75
  41. package/docs/components_Footer.jsx.html +0 -66
  42. package/docs/components_Header.jsx.html +0 -84
  43. package/docs/components_LogViewer.jsx.html +0 -91
  44. package/docs/components_MessageFeedback.jsx.html +0 -76
  45. package/docs/components_PromptField.jsx.html +0 -99
  46. package/docs/components_StatsBar.jsx.html +0 -105
  47. package/docs/components_UsageMenu.jsx.html +0 -73
  48. package/docs/fonts/OpenSans-Bold-webfont.eot +0 -0
  49. package/docs/fonts/OpenSans-Bold-webfont.svg +0 -1830
  50. package/docs/fonts/OpenSans-Bold-webfont.woff +0 -0
  51. package/docs/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
  52. package/docs/fonts/OpenSans-BoldItalic-webfont.svg +0 -1830
  53. package/docs/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
  54. package/docs/fonts/OpenSans-Italic-webfont.eot +0 -0
  55. package/docs/fonts/OpenSans-Italic-webfont.svg +0 -1830
  56. package/docs/fonts/OpenSans-Italic-webfont.woff +0 -0
  57. package/docs/fonts/OpenSans-Light-webfont.eot +0 -0
  58. package/docs/fonts/OpenSans-Light-webfont.svg +0 -1831
  59. package/docs/fonts/OpenSans-Light-webfont.woff +0 -0
  60. package/docs/fonts/OpenSans-LightItalic-webfont.eot +0 -0
  61. package/docs/fonts/OpenSans-LightItalic-webfont.svg +0 -1835
  62. package/docs/fonts/OpenSans-LightItalic-webfont.woff +0 -0
  63. package/docs/fonts/OpenSans-Regular-webfont.eot +0 -0
  64. package/docs/fonts/OpenSans-Regular-webfont.svg +0 -1831
  65. package/docs/fonts/OpenSans-Regular-webfont.woff +0 -0
  66. package/docs/global.html +0 -6145
  67. package/docs/helpers_actionHelpers.js.html +0 -94
  68. package/docs/helpers_constants.js.html +0 -81
  69. package/docs/helpers_dockerService_dockerService.js.html +0 -79
  70. package/docs/helpers_dockerService_serviceComponents_containerActions.js.html +0 -153
  71. package/docs/helpers_dockerService_serviceComponents_containerList.js.html +0 -81
  72. package/docs/helpers_dockerService_serviceComponents_containerLogs.js.html +0 -85
  73. package/docs/helpers_dockerService_serviceComponents_containerStats.js.html +0 -107
  74. package/docs/helpers_dockerService_serviceComponents_imageUtils.js.html +0 -82
  75. package/docs/helpers_exitWithMessage.js.html +0 -77
  76. package/docs/helpers_safeCall.js.html +0 -68
  77. package/docs/helpers_validationHelpers.js.html +0 -96
  78. package/docs/hooks_creation_useContainerActions.js.html +0 -120
  79. package/docs/hooks_creation_useContainerCreation.js.html +0 -152
  80. package/docs/hooks_creation_useLogsViewer.js.html +0 -102
  81. package/docs/hooks_useControls.js.html +0 -298
  82. package/docs/hooks_useLogsStream.js.html +0 -82
  83. package/docs/index.html +0 -65
  84. package/docs/index.js.html +0 -65
  85. package/docs/module-index.html +0 -171
  86. package/docs/scripts/linenumber.js +0 -25
  87. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -202
  88. package/docs/scripts/prettify/lang-css.js +0 -2
  89. package/docs/scripts/prettify/prettify.js +0 -28
  90. package/docs/styles/jsdoc-default.css +0 -358
  91. package/docs/styles/prettify-jsdoc.css +0 -111
  92. package/docs/styles/prettify-tomorrow.css +0 -132
  93. package/esbuild.config.cjs +0 -11
  94. package/src/helpers/actionHelpers.js +0 -52
  95. package/src/hooks/useLogsStream.js +0 -31
  96. package/test/actionHelpers.test.js +0 -106
package/.eslintrc.json CHANGED
@@ -15,7 +15,7 @@
15
15
  "no-unsafe-optional-chaining": "error",
16
16
  "react/react-in-jsx-scope": "off",
17
17
  "no-console": "off",
18
- "react/prop-types": "off",
18
+ "react/prop-types": "warn",
19
19
  "no-unused-vars": [
20
20
  "warn",
21
21
  {
@@ -22,6 +22,9 @@ jobs:
22
22
  - name: Install dependencies
23
23
  run: npm ci
24
24
 
25
+ - name: Lint
26
+ run: npm run lint
27
+
25
28
  - name: Run tests
26
29
  run: npm test
27
30
 
@@ -1,12 +1,16 @@
1
1
  name: Build and deploy docs
2
2
 
3
3
  on:
4
- push:
4
+ workflow_run:
5
+ workflows: ["CI"]
6
+ types:
7
+ - completed
5
8
  branches:
6
9
  - main
7
10
 
8
11
  jobs:
9
12
  build-and-deploy:
13
+ if: ${{ github.event.workflow_run.conclusion == 'success' }}
10
14
  runs-on: ubuntu-latest
11
15
  permissions:
12
16
  contents: write
@@ -28,6 +32,9 @@ jobs:
28
32
  - name: Build docs
29
33
  run: npm run docs
30
34
 
35
+ - name: Ensure .nojekyll
36
+ run: mkdir -p docs && touch docs/.nojekyll
37
+
31
38
  - name: Deploy to GitHub Pages (push to gh-pages)
32
39
  uses: peaceiris/actions-gh-pages@v3
33
40
  with:
package/CHANGELOG.md CHANGED
@@ -7,6 +7,29 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
7
7
  ## [Unreleased]
8
8
  - Nothing yet.
9
9
 
10
+ ## [3.1.9] - 2026-04-11
11
+
12
+ ### Changed
13
+
14
+ - CI: added lint step to the test-and-build job; docs deploy now waits for CI to pass successfully via `workflow_run` trigger.
15
+
16
+ ## [3.1.8] - 2026-04-11
17
+
18
+ ### Refactored
19
+
20
+ - Removed dead code: `actionHelpers`, `useLogsStream`, and esbuild-related artifacts that were no longer in use.
21
+ - Consolidated Babel configuration into a single env-aware file, eliminating duplicate/split config files.
22
+ - Decomposed `useControls` god hook into focused single-responsibility hooks, each owning one concern.
23
+ - Extracted `useContainerStats` hook from `ContainerRow` to separate data-fetching from rendering.
24
+ - Set Jest default environment to `node`; `jsdom` is now opt-in per test file, reducing unnecessary DOM overhead.
25
+
26
+ ### Changed
27
+
28
+ - Removed generated JSDoc output from the repository; added the generated docs directory to `.gitignore`.
29
+ - Enabled `react/prop-types` ESLint rule project-wide and resolved all resulting warnings.
30
+ - Rewrote `README.md` and `CONTRIBUTING.md`: consolidated overlapping content, removed duplication, and updated both for accuracy.
31
+ - Various `docs(style)` and `docs` commits between v3.1.5 and the refactor wave: dark theme, sticky nav, landing page rewrite, CSS consolidation, and English translation for the JSDoc home (GitHub Pages site).
32
+
10
33
  ## [3.1.5] - 2025-10-18
11
34
  _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
35
 
package/CONTRIBUTING.md CHANGED
@@ -1,22 +1,35 @@
1
- # Contributing
1
+ # Contributing to CDD-CLI
2
2
 
3
- Thanks for your interest in contributing! Please follow these guidelines:
3
+ Thanks for your interest in contributing! Here are the guidelines to make collaboration smooth.
4
4
 
5
- - Fork the repository and create a feature branch.
6
- - Keep changes small and focused.
7
- - Add or update tests for new behaviors.
8
- - Run `npm test` and `npm run build` before opening a PR.
9
- - Use conventional commits where possible.
5
+ ## Getting started
10
6
 
11
- For any design or large changes, open an issue first to discuss.
12
- ## Contributing to CDD-CLI
7
+ 1. Fork the repo and create a branch for your feature or fix:
13
8
 
14
- Thanks for your interest in contributing! A few guidelines to make collaboration smooth:
9
+ ```bash
10
+ git checkout -b feat/my-feature
11
+ ```
15
12
 
16
- - Fork the repo and create a branch for your feature/fix: `git checkout -b feat/my-feature`.
17
- - Write tests for any new behavior and ensure existing tests pass: `npm test`.
18
- - Follow the existing code style and run `npm run build` before opening a PR.
19
- - Keep PRs focused and describe motivation and changes.
20
- - Tag reviewers and link related issues.
13
+ 2. Install dependencies and verify everything works before making changes:
14
+
15
+ ```bash
16
+ npm install
17
+ npm test
18
+ npm run build
19
+ ```
20
+
21
+ ## Guidelines
22
+
23
+ - Keep changes small and focused — one concern per PR.
24
+ - Write tests for any new behavior and ensure existing tests pass (`npm test`).
25
+ - Run `npm run build` and verify the CLI starts (`node dist/index.js`) before opening a PR.
26
+ - Use [Conventional Commits](https://www.conventionalcommits.org/) for commit messages.
27
+ - For large or design-changing contributions, open an issue first to discuss the approach.
28
+
29
+ ## Opening a Pull Request
30
+
31
+ - Describe the motivation and what changed.
32
+ - Link any related issues.
33
+ - Tag reviewers if applicable.
21
34
 
22
35
  We appreciate clear, well-tested contributions.
package/README.md CHANGED
@@ -6,34 +6,47 @@
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
9
  <a href="https://caertos.github.io/cdd/">
11
10
  <img src="https://img.shields.io/badge/docs-GitHub%20Pages-blue" alt="Docs" />
12
11
  </a>
12
+ <a href="https://deepwiki.com/Caertos/cdd"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
13
13
  </p>
14
14
 
15
- Short, bilingual README with quickstart, development and tests.
15
+ A terminal dashboard for monitoring and managing Docker containers in real time. Shows live CPU/memory stats, supports keyboard-driven actions, and requires no GUI.
16
16
 
17
17
  ---
18
18
 
19
- ## Quick start (local)
19
+ ## Features
20
+
21
+ - 🐳 Clear, compact view of all Docker containers
22
+ - 🔄 Auto-refresh every few seconds
23
+ - ⌨️ Keyboard shortcuts for fast actions (navigate, start, stop, logs, quit)
24
+ - 📊 Live CPU and memory stats for running containers
25
+ - 🪵 Real-time log streaming for selected containers
26
+ - 🎨 Color-coded states and visual feedback
20
27
 
21
- 1. Clone the repo and install dependencies:
28
+ ---
29
+
30
+ ## Install globally
22
31
 
23
32
  ```bash
24
- git clone https://github.com/caertos/cdd.git
25
- cd cdd
26
- npm install
33
+ npm install -g cdd-cli
34
+ cdd
27
35
  ```
28
36
 
29
- 2. Build and run locally:
37
+ ---
38
+
39
+ ## Quick start (local)
30
40
 
31
41
  ```bash
42
+ git clone https://github.com/caertos/cdd.git
43
+ cd cdd
44
+ npm install
32
45
  npm run build
33
46
  node dist/index.js
34
47
  ```
35
48
 
36
- 3. To test the CLI as a globally available command during development:
49
+ To use it as a global command during development:
37
50
 
38
51
  ```bash
39
52
  npm link
@@ -42,96 +55,65 @@ cdd
42
55
 
43
56
  ---
44
57
 
45
- ## Usage (interactive)
58
+ ## Usage
46
59
 
47
- - Use ↑/↓ to navigate containers.
48
- - I: start selected container
49
- - P: stop selected container
50
- - R: restart selected container
51
- - C: create container (interactive prompt)
52
- - L: view logs for selected container
53
- - D: toggle the on-screen debug log panel
54
- - E: erase (remove) selected container (confirmation required)
55
- - Q: quit
60
+ Use ↑/↓ to navigate containers. Available keyboard shortcuts:
56
61
 
57
- The dashboard auto-refreshes container list every few seconds.
62
+ | Key | Action |
63
+ |-----|--------|
64
+ | `I` | Start selected container |
65
+ | `P` | Stop selected container |
66
+ | `R` | Restart selected container |
67
+ | `C` | Create container (interactive prompt) |
68
+ | `L` | View logs for selected container |
69
+ | `E` | Erase (remove) selected container — confirmation required |
70
+ | `D` | Toggle live debug log panel |
71
+ | `Q` | Quit |
58
72
 
59
73
  ---
60
74
 
61
- ## Install globally
62
-
63
- To install the CLI globally so you can run `cdd` from any terminal, use:
64
-
65
- ```bash
66
- npm install -g cdd-cli
67
- ```
68
-
69
- After installing globally, run the CLI with:
75
+ ## Requirements
70
76
 
71
- ```bash
72
- cdd
73
- ```
77
+ - Node.js >= 18
78
+ - Docker installed and running (CLI connects to the local Docker socket)
74
79
 
75
80
  ---
76
81
 
77
82
  ## Development
78
83
 
79
- - Node.js >= 18 is recommended.
80
- - To run the app from source during development:
81
-
82
84
  ```bash
83
85
  npm install
84
- npm run build
85
- node dist/index.js
86
+ npm run build # compile src/ → dist/
87
+ node dist/index.js # run from compiled output
86
88
  ```
87
89
 
88
- If you change source files, re-run `npm run build` before running the CLI.
90
+ Re-run `npm run build` after any source changes.
89
91
 
90
92
  ---
91
93
 
92
94
  ## Tests
93
95
 
94
- We use Jest for unit tests. Run:
95
-
96
96
  ```bash
97
97
  npm test
98
98
  ```
99
99
 
100
- Tests are located in `test/` and cover utility helpers.
101
-
102
- ---
103
-
104
- ## Contributing
105
-
106
- 1. Fork the repo and create a feature branch.
107
- 2. Add tests for new behavior.
108
- 3. Ensure `npm test` and `npm run build` pass.
109
- 4. Open a Pull Request with a clear description.
110
-
111
- ---
112
-
113
- ## Troubleshooting
114
-
115
- - If you don't see containers, ensure Docker is running and that your user has access to the Docker socket.
116
- - If Docker permissions are required, run the CLI with `sudo` (Linux/macOS) or as Administrator (Windows).
117
- - The project generates `dist/` — keep it out of version control (it's in .gitignore).
100
+ Tests are in `test/` and cover helpers, services, and hooks.
118
101
 
119
102
  ---
120
103
 
121
104
  ## Logging
122
105
 
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:
106
+ By default the CLI shows `info`, `warn`, and `error` messages. For deeper diagnostics:
125
107
 
126
108
  ```bash
127
- CDD_LOG_LEVEL=debug cdd # if you installed globally
109
+ CDD_LOG_LEVEL=debug cdd
128
110
  # or
129
111
  CDD_LOG_LEVEL=debug node dist/index.js
130
112
  ```
131
113
 
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:
114
+ Press `D` inside the CLI to toggle the live debug panel. Press `D` again or `Esc` to hide it.
115
+
116
+ To capture logs to a file:
135
117
 
136
118
  ```bash
137
119
  CDD_LOG_LEVEL=debug cdd > cdd-debug.log 2>&1
@@ -139,34 +121,20 @@ CDD_LOG_LEVEL=debug cdd > cdd-debug.log 2>&1
139
121
 
140
122
  ---
141
123
 
142
- ## License
143
-
144
- This project is MIT/ISC licensed (see `LICENSE`).
124
+ ## Troubleshooting
145
125
 
146
- ```
126
+ - If you don't see containers, ensure Docker is running and your user has access to the Docker socket.
127
+ - On Linux/macOS, try `sudo cdd` if you have permission issues. On Windows, run as Administrator.
128
+ - `dist/` is generated by the build — it's in `.gitignore` and should not be committed.
147
129
 
148
- ## Usage
149
- - When you run `cdd`, you'll see a table with all your Docker containers.
150
- - Running containers show live CPU and memory stats.
151
- - Use `Ctrl+C` to exit.
152
-
153
- ## Main features
154
- - 🐳 Clear, compact visualization of all containers.
155
- - 🔄 Automatic data refresh (every 2 seconds).
156
- - ⌨️ Keyboard shortcuts for fast actions (navigate, start, stop, logs, quit).
157
- - 📊 Live resource usage stats for running containers.
158
- - 🪵 Real-time log streaming for selected containers.
159
- - 🎨 Visual interface with colors and emojis for states.
160
- - 👤 Author: Carlos Cochero (2025)
130
+ ---
161
131
 
162
- ## Requirements
163
- - Node.js >= 18
164
- - Docker installed and running (CLI connects to local Docker socket)
132
+ ## Contributing
165
133
 
166
- ## Troubleshooting
167
- - If you don't see containers, make sure Docker is running and your user has permission to access the Docker socket.
168
- - If you have issues with global install, try with `sudo` (Linux/macOS) or run terminal as administrator (Windows).
134
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
169
135
 
170
136
  ---
171
137
 
172
- ¡Disfruta monitoreando tus contenedores Docker desde la terminal con estilo! / Enjoy monitoring your Docker containers from the terminal in style!
138
+ ## License
139
+
140
+ MIT/ISC — see [`LICENSE`](LICENSE).
package/babel.config.cjs CHANGED
@@ -1,6 +1,23 @@
1
1
  module.exports = {
2
+ env: {
3
+ test: {
4
+ // Jest necesita CJS (transpila módulos) + React
5
+ presets: [
6
+ ['@babel/preset-env', { targets: { node: 'current' } }],
7
+ '@babel/preset-react'
8
+ ]
9
+ },
10
+ // build (NODE_ENV != 'test') → mantiene ESM para el output final
11
+ production: {
12
+ presets: [
13
+ ['@babel/preset-env', { modules: false }],
14
+ '@babel/preset-react'
15
+ ]
16
+ }
17
+ },
18
+ // Fallback para cualquier otro entorno (desarrollo local, etc.)
2
19
  presets: [
3
- ['@babel/preset-env', { targets: { node: 'current' } }],
20
+ ['@babel/preset-env', { modules: false }],
4
21
  '@babel/preset-react'
5
22
  ]
6
23
  };
@@ -1,19 +1,8 @@
1
- function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
2
- function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
3
- function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
4
- function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
5
- function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
6
- function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
7
- function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
8
- function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
9
- function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
10
- function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
11
- import React, { useState, useEffect } from "react";
1
+ import React from "react";
12
2
  import { Box, Text } from "ink";
13
- import { getStats } from "../helpers/dockerService/serviceComponents/containerStats.js";
14
- import { REFRESH_INTERVALS } from "../helpers/constants.js";
15
3
  import StatsBar from "./StatsBar.js";
16
4
  import PropTypes from 'prop-types';
5
+ import { useContainerStats } from '../hooks/useContainerStats.js';
17
6
  var stateText = function stateText(state) {
18
7
  if (state === "running") return {
19
8
  text: "🟢 RUNNING",
@@ -48,17 +37,9 @@ export default function ContainerRow(_ref) {
48
37
  name = container.name,
49
38
  image = container.image,
50
39
  state = container.state;
51
- var _useState = useState({
52
- cpuPercent: 0,
53
- memPercent: 0,
54
- netIO: {
55
- rx: 0,
56
- tx: 0
57
- }
58
- }),
59
- _useState2 = _slicedToArray(_useState, 2),
60
- stats = _useState2[0],
61
- setStats = _useState2[1];
40
+ var _useContainerStats = useContainerStats(id, state),
41
+ stats = _useContainerStats.stats,
42
+ statsError = _useContainerStats.statsError;
62
43
 
63
44
  // Format ports for display (no leading space to avoid layout shifts)
64
45
  var formatPorts = function formatPorts(ports) {
@@ -77,60 +58,6 @@ export default function ContainerRow(_ref) {
77
58
  if (!s) return "";
78
59
  return s.length > max ? s.slice(0, max - 1) + "…" : s;
79
60
  };
80
- var _useState3 = useState(""),
81
- _useState4 = _slicedToArray(_useState3, 2),
82
- statsError = _useState4[0],
83
- setStatsError = _useState4[1];
84
- useEffect(function () {
85
- if (state !== "running") return;
86
- var isMounted = true;
87
- var fetchStats = /*#__PURE__*/function () {
88
- var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
89
- var s, _t;
90
- return _regenerator().w(function (_context) {
91
- while (1) switch (_context.p = _context.n) {
92
- case 0:
93
- _context.p = 0;
94
- _context.n = 1;
95
- return getStats(id);
96
- case 1:
97
- s = _context.v;
98
- if (isMounted) {
99
- setStats(s);
100
- setStatsError("");
101
- }
102
- _context.n = 3;
103
- break;
104
- case 2:
105
- _context.p = 2;
106
- _t = _context.v;
107
- if (isMounted) {
108
- setStats({
109
- cpuPercent: 0,
110
- memPercent: 0,
111
- netIO: {
112
- rx: 0,
113
- tx: 0
114
- }
115
- });
116
- setStatsError("Error fetching stats");
117
- }
118
- case 3:
119
- return _context.a(2);
120
- }
121
- }, _callee, null, [[0, 2]]);
122
- }));
123
- return function fetchStats() {
124
- return _ref2.apply(this, arguments);
125
- };
126
- }();
127
- fetchStats();
128
- var timer = setInterval(fetchStats, REFRESH_INTERVALS.CONTAINER_STATS);
129
- return function () {
130
- isMounted = false;
131
- clearInterval(timer);
132
- };
133
- }, [id, state]);
134
61
  var stateInfo = stateText(state);
135
62
  return /*#__PURE__*/React.createElement(Box, {
136
63
  flexDirection: "column",
@@ -1,10 +1,10 @@
1
1
  import React from "react";
2
- import { Text, useInput } from "ink";
3
- import { safeCall } from "../helpers/safeCall.js";
2
+ import { Text } from "ink";
4
3
  import PropTypes from 'prop-types';
5
4
 
6
5
  /**
7
- * Log viewer overlay component. Shows the most recent lines and closes on ESC.
6
+ * Log viewer overlay component. Shows the most recent lines.
7
+ * Purely presentational — keyboard handling is centralized in useControls.
8
8
  *
9
9
  * @param {Object} props
10
10
  * @param {Array<string>} props.logs - Array of log lines
@@ -15,13 +15,8 @@ import PropTypes from 'prop-types';
15
15
  export default function LogViewer(_ref) {
16
16
  var _container$name;
17
17
  var logs = _ref.logs,
18
- onExit = _ref.onExit,
18
+ _onExit = _ref.onExit,
19
19
  container = _ref.container;
20
- useInput(function (input, key) {
21
- if (key.escape) {
22
- safeCall(onExit);
23
- }
24
- });
25
20
  var visibleLogs = logs.slice(-15);
26
21
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, {
27
22
  color: "green"
@@ -0,0 +1,58 @@
1
+ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
2
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
3
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
4
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
5
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
6
+ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
7
+ /**
8
+ * Builds Docker container creation options from raw user inputs.
9
+ *
10
+ * This is a pure function — it has no side effects and calls no services.
11
+ * It mirrors exactly the option-building logic in useControls.js onCreate callback.
12
+ *
13
+ * @param {object} params
14
+ * @param {string} params.imageName - Docker image name (e.g. "nginx:alpine")
15
+ * @param {string} [params.containerName] - Optional container name
16
+ * @param {string} [params.portInput] - Comma-separated host:container port pairs
17
+ * (e.g. "8080:80,3000:3000")
18
+ * @param {string} [params.envInput] - Comma-separated KEY=VALUE pairs
19
+ * (e.g. "NODE_ENV=production,PORT=3000")
20
+ * @returns {object} Docker container options ready to pass to createContainer
21
+ */
22
+ export function buildContainerOptions(_ref) {
23
+ var _imageName = _ref.imageName,
24
+ containerName = _ref.containerName,
25
+ portInput = _ref.portInput,
26
+ envInput = _ref.envInput;
27
+ var env = (envInput || "").split(",").map(function (s) {
28
+ return s.trim();
29
+ }).filter(Boolean);
30
+ var ports = (portInput || "").split(",").map(function (s) {
31
+ return s.trim();
32
+ }).filter(Boolean);
33
+ var ExposedPorts = {};
34
+ var PortBindings = {};
35
+ ports.forEach(function (pair) {
36
+ var _pair$split = pair.split(":"),
37
+ _pair$split2 = _slicedToArray(_pair$split, 2),
38
+ host = _pair$split2[0],
39
+ cont = _pair$split2[1];
40
+ if (!host || !cont) return;
41
+ var key = "".concat(cont, "/tcp");
42
+ ExposedPorts[key] = {};
43
+ PortBindings[key] = PortBindings[key] || [];
44
+ PortBindings[key].push({
45
+ HostPort: "".concat(host)
46
+ });
47
+ });
48
+ var options = {
49
+ Tty: true
50
+ };
51
+ if (Object.keys(ExposedPorts).length) options.ExposedPorts = ExposedPorts;
52
+ if (Object.keys(PortBindings).length) options.HostConfig = {
53
+ PortBindings: PortBindings
54
+ };
55
+ if (env.length) options.Env = env;
56
+ if (containerName) options.name = containerName;
57
+ return options;
58
+ }
@@ -117,6 +117,20 @@ export function useContainerCreation(_ref) {
117
117
  setMessageColor("yellow");
118
118
  safeCall(onCancel);
119
119
  }
120
+
121
+ /**
122
+ * Resets all creation state to initial values without triggering onCancel.
123
+ * Used by the command router when the user presses 'c' to open the creation wizard.
124
+ */
125
+ function resetCreation() {
126
+ setStep(0);
127
+ setImageName("");
128
+ setContainerName("");
129
+ setPortInput("");
130
+ setEnvInput("");
131
+ setMessage("Insert the name of the image to create: ");
132
+ setMessageColor("yellow");
133
+ }
120
134
  return {
121
135
  step: step,
122
136
  setStep: setStep,
@@ -133,6 +147,7 @@ export function useContainerCreation(_ref) {
133
147
  messageColor: messageColor,
134
148
  setMessageColor: setMessageColor,
135
149
  nextStep: nextStep,
136
- cancelCreation: cancelCreation
150
+ cancelCreation: cancelCreation,
151
+ resetCreation: resetCreation
137
152
  };
138
153
  }
@@ -0,0 +1,46 @@
1
+ function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
2
+ function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
3
+ function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
4
+ function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
5
+ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
6
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
7
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
8
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
9
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
10
+ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
11
+ import { useState, useEffect } from 'react';
12
+ import { logger } from '../../helpers/logger.js';
13
+ export function useDebugLogs() {
14
+ var _useState = useState(false),
15
+ _useState2 = _slicedToArray(_useState, 2),
16
+ showDebugLogs = _useState2[0],
17
+ setShowDebugLogs = _useState2[1];
18
+ var _useState3 = useState([]),
19
+ _useState4 = _slicedToArray(_useState3, 2),
20
+ debugLogs = _useState4[0],
21
+ setDebugLogs = _useState4[1];
22
+ useEffect(function () {
23
+ var unsubscribe = logger.subscribe(function (entry) {
24
+ setDebugLogs(function (prev) {
25
+ var extras = (entry.args || []).map(function (arg) {
26
+ if (typeof arg === "string") return arg;
27
+ if (typeof arg === "number" || typeof arg === "boolean") return String(arg);
28
+ try {
29
+ return JSON.stringify(arg);
30
+ } catch (err) {
31
+ return "[unserializable]";
32
+ }
33
+ }).filter(Boolean);
34
+ var line = extras.length ? "".concat(entry.formatted, " ").concat(extras.join(" ")) : entry.formatted;
35
+ var next = [].concat(_toConsumableArray(prev), [line]);
36
+ return next.slice(-200);
37
+ });
38
+ });
39
+ return unsubscribe;
40
+ }, []);
41
+ return {
42
+ showDebugLogs: showDebugLogs,
43
+ setShowDebugLogs: setShowDebugLogs,
44
+ debugLogs: debugLogs
45
+ };
46
+ }