cdd-cli 2.0.3 → 3.0.1

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/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ SPDX-License-Identifier: MIT
4
+
5
+ Copyright (c) 2025 caertos
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
@@ -0,0 +1,260 @@
1
+ # Plan de Implementación: Keybindings y Funcionalidades Avanzadas para CDD
2
+
3
+ Este documento te guiará paso a paso para agregar keybindings y la integración de logs y estadísticas en tiempo real a tu proyecto CLI/CDD. Además, incluye recomendaciones de investigación para que puedas aprender y experimentar por ti mismo.
4
+
5
+ ---
6
+
7
+ ## 1. Keybindings (Atajos de Teclado)
8
+
9
+ ### Objetivo
10
+ Permitir la navegación y control de contenedores Docker usando el teclado.
11
+
12
+ ### Acciones y Sugerencias
13
+ - **↑ / ↓**: Navegar entre contenedores
14
+ - **Enter**: Seleccionar contenedor
15
+ - **L**: Mostrar logs en vivo
16
+ - **S**: Mostrar estadísticas
17
+ - **R**: Reiniciar contenedor
18
+ - **I**: Iniciar contenedor
19
+ - **P**: Pausar/Detener contenedor
20
+ - **Q**: Salir
21
+
22
+ ### ¿Cómo hacerlo?
23
+
24
+ ### Usando Ink para Keybindings y UI CLI React
25
+
26
+ Si ya tienes Ink instalado, es la mejor opción para construir tu CLI interactiva con React. Ink te permite crear componentes reutilizables, manejar el estado y capturar eventos de teclado de forma reactiva, todo en la terminal.
27
+
28
+ #### Funciones y hooks clave de Ink:
29
+
30
+ - **`<Text>` y `<Box>`**: Componentes básicos para mostrar texto y organizar la UI en la terminal.
31
+ - **Por qué:** Permiten estructurar y dar formato a la salida, igual que en React web.
32
+
33
+ - **`useInput`**: Hook para capturar cualquier tecla presionada por el usuario.
34
+ - **Cómo:**
35
+ ```js
36
+ import { useInput } from 'ink';
37
+ useInput((input, key) => {
38
+ if (key.upArrow) { /* lógica para ↑ */ }
39
+ if (key.downArrow) { /* lógica para ↓ */ }
40
+ if (input === 'q') { /* salir */ }
41
+ // ...otros keybindings
42
+
43
+ ### ¿Cómo hacerlo?
44
+
45
+ #### Usando Ink para Keybindings y UI CLI React
46
+
47
+ Si ya tienes Ink instalado, es la mejor opción para construir tu CLI interactiva con React. Ink te permite crear componentes reutilizables, manejar el estado y capturar eventos de teclado de forma reactiva, todo en la terminal.
48
+
49
+ ##### Funciones y hooks clave de Ink:
50
+
51
+ - **`<Text>` y `<Box>`**: Componentes básicos para mostrar texto y organizar la UI en la terminal.
52
+ - **Por qué:** Permiten estructurar y dar formato a la salida, igual que en React web.
53
+
54
+ - **`useInput`**: Hook para capturar cualquier tecla presionada por el usuario.
55
+ - **Cómo:**
56
+ ```js
57
+ import { useInput } from 'ink';
58
+ useInput((input, key) => {
59
+ if (key.upArrow) { /* lógica para ↑ */ }
60
+ if (key.downArrow) { /* lógica para ↓ */ }
61
+ if (input === 'q') { /* salir */ }
62
+ // ...otros keybindings
63
+ });
64
+ ```
65
+ - **Por qué:** Es la forma más sencilla y reactiva de manejar atajos de teclado en la terminal con Ink.
66
+
67
+ - **Estado React**: Usa `useState` y `useEffect` igual que en React web para manejar selección, logs, stats, etc.
68
+ - **Por qué:** Permite que la UI se actualice automáticamente cuando cambian los datos o el estado.
69
+
70
+ ##### Ejemplo básico de navegación con Ink:
71
+
72
+ ```js
73
+ import React, { useState } from 'react';
74
+ import { render, Box, Text, useInput } from 'ink';
75
+
76
+ const containers = ['web', 'db', 'cache'];
77
+
78
+ const App = () => {
79
+ const [selected, setSelected] = useState(0);
80
+
81
+ useInput((input, key) => {
82
+ if (key.upArrow) setSelected(i => (i === 0 ? containers.length - 1 : i - 1));
83
+ if (key.downArrow) setSelected(i => (i === containers.length - 1 ? 0 : i + 1));
84
+ if (input === 'q') process.exit();
85
+ // ...otros keybindings
86
+ });
87
+
88
+ return (
89
+ <Box flexDirection="column">
90
+ {containers.map((name, i) => (
91
+ <Text key={name} color={i === selected ? 'green' : undefined}>
92
+ {i === selected ? '>' : ' '} {name}
93
+ </Text>
94
+ ))}
95
+ <Text>Usa ↑/↓ para navegar, Q para salir</Text>
96
+ </Box>
97
+ );
98
+ };
99
+
100
+ render(<App />);
101
+ ```
102
+
103
+ ##### ¿Por qué Ink?
104
+ - Permite una experiencia de desarrollo y mantenimiento muy similar a React web.
105
+ - Facilita la captura de teclas y el renderizado dinámico sin dependencias extra.
106
+ - Es ideal para CLIs interactivas, menús, dashboards y visualización en tiempo real.
107
+
108
+ Consulta la [documentación oficial de Ink](https://github.com/vadimdemedes/ink) para más ejemplos y detalles.
109
+ #### Ejemplo básico de navegación con Ink:
110
+
111
+ ```js
112
+ import React, { useState } from 'react';
113
+ import { render, Box, Text, useInput } from 'ink';
114
+
115
+ const containers = ['web', 'db', 'cache'];
116
+
117
+ const App = () => {
118
+ const [selected, setSelected] = useState(0);
119
+
120
+ useInput((input, key) => {
121
+ if (key.upArrow) setSelected(i => (i === 0 ? containers.length - 1 : i - 1));
122
+ if (key.downArrow) setSelected(i => (i === containers.length - 1 ? 0 : i + 1));
123
+ if (input === 'q') process.exit();
124
+ // ...otros keybindings
125
+ });
126
+
127
+ return (
128
+ <Box flexDirection="column">
129
+ {containers.map((name, i) => (
130
+ <Text key={name} color={i === selected ? 'green' : undefined}>
131
+ {i === selected ? '>' : ' '} {name}
132
+ </Text>
133
+ ))}
134
+ <Text>Usa ↑/↓ para navegar, Q para salir</Text>
135
+ </Box>
136
+ );
137
+ };
138
+
139
+ render(<App />);
140
+ ```
141
+
142
+ #### ¿Por qué Ink?
143
+ - Permite una experiencia de desarrollo y mantenimiento muy similar a React web.
144
+ - Facilita la captura de teclas y el renderizado dinámico sin dependencias extra.
145
+ - Es ideal para CLIs interactivas, menús, dashboards y visualización en tiempo real.
146
+
147
+ Consulta la [documentación oficial de Ink](https://github.com/vadimdemedes/ink) para más ejemplos y detalles.
148
+
149
+ ---
150
+
151
+ ## 2. Integración de Logs y Stats en Tiempo Real
152
+
153
+ ### Logs en Vivo
154
+ - Utiliza el comando `docker logs -f <container>` para obtener logs en streaming.
155
+ - Investiga cómo manejar streams en Node.js con `child_process.spawn`.
156
+ - Si quieres mostrar los logs en la UI, revisa cómo actualizar el estado en tiempo real.
157
+
158
+ ### Estadísticas en Tiempo Real
159
+ - Usa el comando `docker stats <container> --no-stream` para obtener stats puntuales, o sin `--no-stream` para flujo continuo.
160
+ - Investiga cómo parsear la salida de estos comandos y mostrarla en la UI.
161
+ - Considera usar la librería [dockerode](https://github.com/apocas/dockerode) para interactuar con Docker desde Node.js de forma programática.
162
+
163
+ ---
164
+
165
+ ## 3. Recomendaciones de Investigación
166
+
167
+ - **Node.js y Streams**: Aprende sobre el módulo `stream` y cómo manejar datos en tiempo real.
168
+ - **Captura de Teclas en Terminal**: Explora cómo funcionan los eventos de teclado en aplicaciones CLI.
169
+ - **Dockerode**: Investiga esta librería para controlar Docker desde Node.js.
170
+ - **React y Eventos Globales**: Si tu UI es React, revisa cómo manejar eventos globales de teclado.
171
+ - **Websockets**: Si decides separar backend y frontend, aprende sobre websockets para comunicación en tiempo real.
172
+ - **UX en CLI**: Investiga buenas prácticas para interfaces de usuario en la terminal (ejemplo: [blessed](https://github.com/chjj/blessed)).
173
+
174
+
175
+ ## 4. Siguiente Paso Sugerido
176
+
177
+
178
+ ## 5. Recursos Útiles
179
+
180
+
181
+ ---
182
+
183
+ ## 6. Planificación Detallada Paso a Paso
184
+
185
+ ### Paso 1: Definir el tipo de interfaz
186
+ - Si tu aplicación es CLI y ya tienes Ink, úsalo como framework principal para toda la UI y keybindings.
187
+ - Ink te permite crear componentes, manejar el estado y capturar teclas de forma reactiva, todo en la terminal.
188
+ - Si necesitas algo que Ink no cubre, evalúa otras librerías, pero prioriza Ink para evitar dependencias innecesarias.
189
+
190
+ ### Paso 2: Investigar y elegir librerías para keybindings
191
+ - Para CLI con Ink, usa el hook `useInput` para capturar teclas y manejar la navegación y acciones.
192
+ - Haz pruebas simples con `useInput` para capturar teclas y mostrar la tecla presionada o cambiar el estado.
193
+
194
+ ### Paso 3: Implementar navegación entre contenedores (↑/↓)
195
+ - Usa `useState` para el índice seleccionado.
196
+ - Renderiza la lista de contenedores con `<Text>` y `<Box>`, resaltando el seleccionado.
197
+ - Usa `useInput` para capturar las flechas y actualizar el índice.
198
+ - Haz que la navegación sea cíclica para mejor UX.
199
+
200
+ ### Paso 4: Implementar selección de contenedor (Enter)
201
+ - Al presionar Enter, guarda el contenedor seleccionado en el estado.
202
+ - Muestra un menú de acciones o detalles usando componentes condicionales de Ink.
203
+
204
+ ### Paso 5: Implementar acciones sobre el contenedor
205
+ - L: Mostrar logs en vivo
206
+ - S: Mostrar estadísticas en tiempo real
207
+ - R: Reiniciar contenedor
208
+ - I: Iniciar contenedor
209
+ - P: Pausar/Detener contenedor
210
+ - Q: Salir
211
+ - Para cada acción:
212
+ - Usa `useInput` para detectar la tecla y cambiar el estado de la vista.
213
+ - Crea una función que ejecute el comando Docker correspondiente usando `child_process` o `dockerode`.
214
+ - Muestra el resultado en la UI usando componentes Ink.
215
+
216
+ ### Paso 6: Integrar logs en tiempo real
217
+ - Usa `docker logs -f <container>` con `child_process.spawn` para obtener logs en streaming.
218
+ - Lee los datos del stream y actualiza el estado con los logs recibidos.
219
+ - Renderiza los logs en tiempo real usando `<Text>` y el estado de React.
220
+ - Permite salir de la vista de logs con una tecla usando `useInput`.
221
+
222
+ ### Paso 7: Integrar estadísticas en tiempo real
223
+ - Usa `docker stats <container>` o la API de `dockerode` para obtener stats.
224
+ - Actualiza el estado periódicamente (con `setInterval` o un efecto) y renderiza los stats en la UI.
225
+ - Permite salir de la vista de stats con una tecla usando `useInput`.
226
+
227
+ ### Paso 8: Mejorar la experiencia de usuario
228
+ - Agrega mensajes de ayuda o leyenda de teclas usando `<Text>`.
229
+ - Maneja errores de Docker y muestra mensajes claros en la UI.
230
+ - Permite refrescar la lista de contenedores con una tecla usando `useInput`.
231
+
232
+ ### Paso 9: Refactorizar y documentar
233
+ - Organiza el código en componentes Ink y hooks reutilizables.
234
+ - Documenta cada función y componente.
235
+ - Escribe un README con ejemplos de uso y keybindings.
236
+
237
+ ### Paso 10: Pruebas y mejoras
238
+ - Prueba todos los keybindings y flujos en la terminal.
239
+ - Pide feedback a otros usuarios.
240
+ - Agrega nuevas funcionalidades según necesidades.
241
+
242
+ ---
243
+
244
+ ## 7. Consejos para Aprender y Disfrutar el Proceso
245
+ - Investiga cada librería antes de usarla.
246
+ - Haz pruebas pequeñas y ve integrando poco a poco.
247
+ - Lee la documentación oficial de Docker y Node.js.
248
+ - No dudes en romper cosas: ¡así se aprende!
249
+ - Si te atoras, busca ejemplos en GitHub o StackOverflow.
250
+
251
+ ---
252
+ - [Node.js Child Process](https://nodejs.org/api/child_process.html)
253
+ - [Docker CLI Reference](https://docs.docker.com/engine/reference/commandline/cli/)
254
+ - [Inquirer.js](https://www.npmjs.com/package/inquirer)
255
+ - [dockerode](https://github.com/apocas/dockerode)
256
+ - [blessed](https://github.com/chjj/blessed)
257
+
258
+ ---
259
+
260
+ ¡Diviértete programando y aprendiendo! Si tienes dudas sobre algún punto, investiga primero y luego pregunta para profundizar.
package/README.md CHANGED
@@ -149,9 +149,18 @@ CDD-CLI es una herramienta de línea de comandos (CLI) multiplataforma que te pe
149
149
  ```
150
150
 
151
151
  ## Uso
152
- - Al ejecutar `cdd`, verás una tabla con todos tus contenedores Docker.
152
+ - Al ejecutar `cdd`, verás una tabla interactiva con todos tus contenedores Docker.
153
153
  - Los contenedores en ejecución muestran estadísticas de CPU y memoria en tiempo real.
154
- - Usa `Ctrl+C` para salir.
154
+ - Puedes navegar usando las flechas ↑/↓ y controlar los contenedores con atajos de teclado.
155
+ - Usa `Ctrl+C` o la tecla `Q` para salir.
156
+
157
+ ### ⌨️ Atajos de teclado
158
+
159
+ - ↑ / ↓ : Navegar entre contenedores
160
+ - I : Iniciar el contenedor seleccionado
161
+ - P : Parar el contenedor seleccionado
162
+ - L : Ver logs en tiempo real del contenedor seleccionado
163
+ - Q : Salir del dashboard o de la vista de logs
155
164
 
156
165
  ## Funcionalidades principales
157
166
  - 🐳 Visualización clara y compacta de todos los contenedores.
@@ -200,8 +209,10 @@ CDD-CLI is a cross-platform command-line tool (CLI) to monitor and visualize you
200
209
 
201
210
  ## Main features
202
211
  - 🐳 Clear, compact visualization of all containers.
203
- - 🔄 Automatic data refresh.
212
+ - 🔄 Automatic data refresh (every 2 seconds).
213
+ - ⌨️ Keyboard shortcuts for fast actions (navigate, start, stop, logs, quit).
204
214
  - 📊 Live resource usage stats for running containers.
215
+ - 🪵 Real-time log streaming for selected containers.
205
216
  - 🎨 Visual interface with colors and emojis for states.
206
217
  - 👤 Author: Carlos Cochero (2025)
207
218
 
package/dist/App.js CHANGED
@@ -1,28 +1,42 @@
1
1
  import React from "react";
2
2
  import { Box, Text, Spacer } from "ink";
3
3
  import { useContainers } from "./hooks/useContainers.js";
4
- import ContainerRow from "./components/ContainerRow.js";
4
+ import { useControls } from "./hooks/useControls.js";
5
+ import ContainerList from "./components/ContainerList.js";
6
+ import MessageFeedback from "./components/MessageFeedback.js";
5
7
  import Header from "./components/Header.js";
8
+ import LogViewer from "./components/LogViewer.js";
6
9
  export default function App() {
7
10
  var _useContainers = useContainers(),
8
11
  containers = _useContainers.containers;
9
- return /*#__PURE__*/React.createElement(Box, {
12
+ var _useControls = useControls(containers),
13
+ selected = _useControls.selected,
14
+ message = _useControls.message,
15
+ messageColor = _useControls.messageColor,
16
+ showLogs = _useControls.showLogs,
17
+ logs = _useControls.logs,
18
+ exitLogs = _useControls.exitLogs;
19
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, {
10
20
  flexDirection: "column",
11
21
  borderStyle: "round",
12
22
  borderColor: "cyan",
13
23
  padding: 1
14
24
  }, /*#__PURE__*/React.createElement(Header, {
15
25
  count: containers.length
16
- }), /*#__PURE__*/React.createElement(Text, null, " "), containers.length === 0 ? /*#__PURE__*/React.createElement(Text, null, "No containers found") : containers.map(function (container) {
17
- return /*#__PURE__*/React.createElement(ContainerRow, {
18
- key: container.id,
19
- container: container
20
- });
21
- }), /*#__PURE__*/React.createElement(Spacer, null), /*#__PURE__*/React.createElement(Box, {
22
- marginTop: 1
26
+ }), /*#__PURE__*/React.createElement(Text, null, " "), containers.length === 0 ? /*#__PURE__*/React.createElement(Text, null, "No containers found") : /*#__PURE__*/React.createElement(ContainerList, {
27
+ containers: containers,
28
+ selected: selected
29
+ }), /*#__PURE__*/React.createElement(Spacer, null), /*#__PURE__*/React.createElement(MessageFeedback, {
30
+ message: message,
31
+ color: messageColor
32
+ }), /*#__PURE__*/React.createElement(Text, null, "Use \u2191/\u2193 for navigation"), /*#__PURE__*/React.createElement(Text, null, "\u2022I to initiate selected container"), /*#__PURE__*/React.createElement(Text, null, "\u2022P to stop selected container"), /*#__PURE__*/React.createElement(Text, null, "\u2022L to view logs of selected container"), /*#__PURE__*/React.createElement(Text, null, "\u2022Q to quit"), /*#__PURE__*/React.createElement(Box, {
33
+ justifyContent: "flex-end",
34
+ width: "100%"
23
35
  }, /*#__PURE__*/React.createElement(Text, {
24
36
  dimColor: true
25
- }, "Press Ctrl+C to exit")), /*#__PURE__*/React.createElement(Text, {
26
- dimColor: true
27
- }, "Crafted by Carlos Cochero \u2022 2025"));
37
+ }, "Crafted by Carlos Cochero \u2022 2025"))), showLogs && /*#__PURE__*/React.createElement(LogViewer, {
38
+ logs: logs,
39
+ onExit: exitLogs,
40
+ container: containers[selected]
41
+ }));
28
42
  }
@@ -0,0 +1,19 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+ import ContainerRow from "./ContainerRow.js";
4
+ export default function ContainerList(_ref) {
5
+ var containers = _ref.containers,
6
+ selected = _ref.selected;
7
+ return /*#__PURE__*/React.createElement(React.Fragment, null, containers.map(function (container, i) {
8
+ return /*#__PURE__*/React.createElement(Box, {
9
+ key: container.id,
10
+ flexDirection: "row",
11
+ alignItems: "center"
12
+ }, /*#__PURE__*/React.createElement(Text, {
13
+ color: i === selected ? "green" : undefined
14
+ }, i === selected ? "➤" : " "), /*#__PURE__*/React.createElement(ContainerRow, {
15
+ container: container,
16
+ isSelected: i === selected
17
+ }));
18
+ }));
19
+ }
@@ -11,7 +11,7 @@ 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 chalk from "chalk";
14
- import { getStats } from "../dockerService.js";
14
+ import { getStats } from "../helpers/dockerService.js";
15
15
  import StatsBar from "./StatsBar.js";
16
16
  var colorByState = function colorByState(state) {
17
17
  if (state === "running") return chalk.greenBright("🟢 RUNNING");
@@ -67,8 +67,8 @@ export default function ContainerRow(_ref) {
67
67
  return /*#__PURE__*/React.createElement(Box, {
68
68
  flexDirection: "column",
69
69
  marginBottom: 1
70
- }, /*#__PURE__*/React.createElement(Text, null, chalk.cyan(name.padEnd(20)), " ", chalk.gray(image.padEnd(20)), " ", state === "running" ? chalk.greenBright("🟢 RUNNING") : chalk.redBright("\uD83D\uDD34 ".concat(state.toUpperCase()))), state === "running" && /*#__PURE__*/React.createElement(StatsBar, {
70
+ }, /*#__PURE__*/React.createElement(Text, null, chalk.cyan(name.padEnd(20)), " ", chalk.gray(image.padEnd(20)), " ", state === "running" ? chalk.greenBright("🟢 RUNNING") : chalk.redBright("\uD83D\uDD34 ".concat(state.toUpperCase())), " ", chalk.yellow(container.ports), " ", state === "running" && /*#__PURE__*/React.createElement(StatsBar, {
71
71
  cpu: parseFloat(stats.cpuPercent),
72
72
  mem: parseFloat(stats.memPercent)
73
- }));
73
+ })));
74
74
  }
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ import { Text, useInput } from "ink";
3
+ export default function LogViewer(_ref) {
4
+ var logs = _ref.logs,
5
+ onExit = _ref.onExit,
6
+ container = _ref.container;
7
+ useInput(function (input, key) {
8
+ if (key.escape) {
9
+ onExit();
10
+ }
11
+ });
12
+ var visibleLogs = logs.slice(-15);
13
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, {
14
+ color: "green"
15
+ }, (container === null || container === void 0 ? void 0 : container.name) || "Container", " logs, press ESC to exit"), visibleLogs.length === 0 ? /*#__PURE__*/React.createElement(Text, {
16
+ dimColor: true
17
+ }, "No logs...") : visibleLogs.map(function (line, idx) {
18
+ return /*#__PURE__*/React.createElement(Text, {
19
+ key: idx
20
+ }, line);
21
+ }));
22
+ }
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+ export default function MessageFeedback(_ref) {
4
+ var message = _ref.message,
5
+ color = _ref.color;
6
+ if (!message) return null;
7
+ return /*#__PURE__*/React.createElement(Box, {
8
+ marginBottom: 1
9
+ }, /*#__PURE__*/React.createElement(Text, {
10
+ color: color
11
+ }, message));
12
+ }
File without changes
@@ -0,0 +1,61 @@
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
+ // Helper para manejar acciones docker con feedback visual y validación
6
+ export function handleAction(_x) {
7
+ return _handleAction.apply(this, arguments);
8
+ }
9
+ function _handleAction() {
10
+ _handleAction = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(_ref) {
11
+ var containers, selected, actionFn, actionLabel, setMessage, setMessageColor, stateCheck, c, _t;
12
+ return _regenerator().w(function (_context) {
13
+ while (1) switch (_context.p = _context.n) {
14
+ case 0:
15
+ containers = _ref.containers, selected = _ref.selected, actionFn = _ref.actionFn, actionLabel = _ref.actionLabel, setMessage = _ref.setMessage, setMessageColor = _ref.setMessageColor, stateCheck = _ref.stateCheck;
16
+ c = containers[selected];
17
+ if (c) {
18
+ _context.n = 1;
19
+ break;
20
+ }
21
+ return _context.a(2);
22
+ case 1:
23
+ if (!(stateCheck && stateCheck(c))) {
24
+ _context.n = 2;
25
+ break;
26
+ }
27
+ setMessage(stateCheck(c));
28
+ setMessageColor("red");
29
+ setTimeout(function () {
30
+ return setMessage("");
31
+ }, 2000);
32
+ return _context.a(2);
33
+ case 2:
34
+ setMessage("".concat(actionLabel, " container..."));
35
+ setMessageColor("green");
36
+ _context.p = 3;
37
+ _context.n = 4;
38
+ return actionFn(c.id);
39
+ case 4:
40
+ setMessage("".concat(actionLabel, " container..."));
41
+ setMessageColor("green");
42
+ setTimeout(function () {
43
+ return setMessage("");
44
+ }, 3000);
45
+ _context.n = 6;
46
+ break;
47
+ case 5:
48
+ _context.p = 5;
49
+ _t = _context.v;
50
+ setMessage("Failed to ".concat(actionLabel.toLowerCase(), " container."));
51
+ setMessageColor("red");
52
+ setTimeout(function () {
53
+ return setMessage("");
54
+ }, 3000);
55
+ case 6:
56
+ return _context.a(2);
57
+ }
58
+ }, _callee, null, [[3, 5]]);
59
+ }));
60
+ return _handleAction.apply(this, arguments);
61
+ }
@@ -0,0 +1,51 @@
1
+ import { spawn } from "child_process";
2
+
3
+ /**
4
+ * Ejecuta una acción de Docker sobre un contenedor y maneja feedback visual.
5
+ * @param {Object} params
6
+ * @param {Array} params.containers - Lista de contenedores.
7
+ * @param {number} params.selected - Índice del contenedor seleccionado.
8
+ * @param {string} params.action - Acción docker (start, stop, restart).
9
+ * @param {string} params.actionLabel - Texto para feedback (Starting, Stopping, etc).
10
+ * @param {Function} params.setMessage - Setter de mensaje visual.
11
+ * @param {Function} params.setMessageColor - Setter de color del mensaje.
12
+ */
13
+ export function handleDockerAction(_ref) {
14
+ var containers = _ref.containers,
15
+ selected = _ref.selected,
16
+ action = _ref.action,
17
+ actionLabel = _ref.actionLabel,
18
+ setMessage = _ref.setMessage,
19
+ setMessageColor = _ref.setMessageColor;
20
+ if (containers[selected]) {
21
+ var c = containers[selected];
22
+ var id = c.id || c.name;
23
+ // Validación de estado
24
+ if (action === "start" && (c.state === "running" || c.status === "running")) {
25
+ setMessage("Container is already running.");
26
+ setMessageColor("red");
27
+ setTimeout(function () {
28
+ return setMessage("");
29
+ }, 2000);
30
+ return;
31
+ }
32
+ if (action === "stop" && (c.state === "exited" || c.status === "exited" || c.state === "stopped" || c.status === "stopped")) {
33
+ setMessage("Container is already stopped.");
34
+ setMessageColor("red");
35
+ setTimeout(function () {
36
+ return setMessage("");
37
+ }, 2000);
38
+ return;
39
+ }
40
+ setMessage("".concat(actionLabel, " container..."));
41
+ setMessageColor("green");
42
+ var child = spawn("docker", [action, id]);
43
+ child.on("close", function () {
44
+ setMessage("".concat(actionLabel, " container..."));
45
+ setMessageColor("green");
46
+ setTimeout(function () {
47
+ return setMessage("");
48
+ }, 3000);
49
+ });
50
+ }
51
+ }