cdd-cli 3.2.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/.atl/skill-registry.md +30 -0
  2. package/.editorconfig +12 -0
  3. package/.eslintrc.json +1 -1
  4. package/.prettierignore +4 -0
  5. package/.prettierrc +8 -0
  6. package/.~lock.ROADMAP.md# +1 -0
  7. package/CHANGELOG.md +80 -0
  8. package/README.es.md +198 -0
  9. package/README.md +81 -23
  10. package/ROADMAP.md +647 -0
  11. package/SECURITY.md +5 -0
  12. package/__mocks__/ink.cjs +17 -0
  13. package/coverage/clover.xml +471 -500
  14. package/coverage/coverage-final.json +16 -16
  15. package/coverage/lcov-report/ContainerCreationPrompt.jsx.html +400 -0
  16. package/coverage/lcov-report/ControlsHUD.jsx.html +256 -0
  17. package/coverage/lcov-report/components/ContainerCreationPrompt.jsx.html +394 -0
  18. package/coverage/lcov-report/components/PromptField.jsx.html +229 -0
  19. package/coverage/lcov-report/components/SuggestionPanel.jsx.html +268 -0
  20. package/coverage/lcov-report/components/index.html +146 -0
  21. package/coverage/lcov-report/helpers/constants.js.html +139 -22
  22. package/coverage/lcov-report/helpers/containerOptionsBuilder.js.html +75 -42
  23. package/coverage/lcov-report/helpers/dockerHubService.js.html +310 -0
  24. package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerActions.js.html +162 -84
  25. package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerList.js.html +50 -32
  26. package/coverage/lcov-report/helpers/dockerService/serviceComponents/index.html +21 -21
  27. package/coverage/lcov-report/helpers/imageNameUtils.js.html +181 -0
  28. package/coverage/lcov-report/helpers/index.html +84 -54
  29. package/coverage/lcov-report/helpers/logger.js.html +68 -68
  30. package/coverage/lcov-report/helpers/safeCall.js.html +15 -15
  31. package/coverage/lcov-report/helpers/validationHelpers.js.html +66 -114
  32. package/coverage/lcov-report/hooks/creation/index.html +20 -20
  33. package/coverage/lcov-report/hooks/creation/useContainerActions.js.html +38 -17
  34. package/coverage/lcov-report/hooks/creation/useContainerCreation.js.html +593 -89
  35. package/coverage/lcov-report/hooks/creation/useLogsViewer.js.html +7 -7
  36. package/coverage/lcov-report/hooks/debug/index.html +5 -5
  37. package/coverage/lcov-report/hooks/debug/useDebugLogs.js.html +35 -20
  38. package/coverage/lcov-report/hooks/index.html +40 -40
  39. package/coverage/lcov-report/hooks/navigation/index.html +19 -19
  40. package/coverage/lcov-report/hooks/navigation/useContainerSelection.js.html +38 -29
  41. package/coverage/lcov-report/hooks/useContainerCommandRouter.js.html +119 -80
  42. package/coverage/lcov-report/hooks/useControls.js.html +306 -123
  43. package/coverage/lcov-report/hooks/useEraseConfirmation.js.html +8 -8
  44. package/coverage/lcov-report/hooks/useExitHandler.js.html +50 -41
  45. package/coverage/lcov-report/index.html +52 -67
  46. package/coverage/lcov.info +899 -952
  47. package/dist/App.js +18 -12
  48. package/dist/components/ContainerCreationPrompt.js +50 -13
  49. package/dist/components/ContainerList.js +3 -3
  50. package/dist/components/ContainerRow.js +20 -20
  51. package/dist/components/ContainerSection.js +3 -3
  52. package/dist/components/ControlsHUD.js +106 -0
  53. package/dist/components/Footer.js +2 -2
  54. package/dist/components/Header.js +7 -7
  55. package/dist/components/LogViewer.js +3 -3
  56. package/dist/components/MessageFeedback.js +2 -2
  57. package/dist/components/PromptField.js +4 -4
  58. package/dist/components/StatsBar.js +5 -5
  59. package/dist/components/SuggestionPanel.js +53 -0
  60. package/dist/components/UsageMenu.js +3 -3
  61. package/dist/helpers/appInfo.js +7 -7
  62. package/dist/helpers/constants.js +154 -1
  63. package/dist/helpers/containerOptionsBuilder.js +3 -3
  64. package/dist/helpers/dockerHubService.js +111 -0
  65. package/dist/helpers/dockerService/dockerService.js +1 -1
  66. package/dist/helpers/dockerService/serviceComponents/containerActions.js +99 -40
  67. package/dist/helpers/dockerService/serviceComponents/containerList.js +11 -11
  68. package/dist/helpers/dockerService/serviceComponents/containerLogs.js +9 -9
  69. package/dist/helpers/dockerService/serviceComponents/containerStats.js +5 -5
  70. package/dist/helpers/dockerService/serviceComponents/imageUtils.js +1 -1
  71. package/dist/helpers/exitWithMessage.js +10 -10
  72. package/dist/helpers/imageNameUtils.js +32 -0
  73. package/dist/helpers/logger.js +13 -13
  74. package/dist/helpers/safeCall.js +2 -2
  75. package/dist/helpers/validationHelpers.js +76 -21
  76. package/dist/hooks/creation/useContainerActions.js +28 -11
  77. package/dist/hooks/creation/useContainerCreation.js +452 -62
  78. package/dist/hooks/creation/useLogsViewer.js +2 -2
  79. package/dist/hooks/debug/useDebugLogs.js +4 -4
  80. package/dist/hooks/useContainerCommandRouter.js +14 -14
  81. package/dist/hooks/useContainers.js +3 -3
  82. package/dist/hooks/useControls.js +85 -38
  83. package/dist/hooks/useEraseConfirmation.js +3 -3
  84. package/dist/index.js +9 -2
  85. package/jest.config.cjs +7 -0
  86. package/package.json +7 -3
  87. package/src/App.jsx +39 -16
  88. package/src/components/ContainerCreationPrompt.jsx +54 -10
  89. package/src/components/ContainerList.jsx +3 -3
  90. package/src/components/ContainerRow.jsx +19 -16
  91. package/src/components/ContainerSection.jsx +3 -3
  92. package/src/components/ControlsHUD.jsx +66 -0
  93. package/src/components/Footer.jsx +2 -2
  94. package/src/components/Header.jsx +7 -7
  95. package/src/components/LogViewer.jsx +5 -3
  96. package/src/components/MessageFeedback.jsx +2 -2
  97. package/src/components/PromptField.jsx +4 -4
  98. package/src/components/StatsBar.jsx +11 -7
  99. package/src/components/SuggestionPanel.jsx +61 -0
  100. package/src/components/UsageMenu.jsx +18 -13
  101. package/src/helpers/appInfo.js +7 -7
  102. package/src/helpers/constants.js +48 -9
  103. package/src/helpers/containerOptionsBuilder.js +16 -5
  104. package/src/helpers/dockerHubService.js +75 -0
  105. package/src/helpers/dockerService/dockerService.js +1 -1
  106. package/src/helpers/dockerService/serviceComponents/containerActions.js +87 -61
  107. package/src/helpers/dockerService/serviceComponents/containerList.js +31 -25
  108. package/src/helpers/dockerService/serviceComponents/containerLogs.js +41 -26
  109. package/src/helpers/dockerService/serviceComponents/containerStats.js +13 -11
  110. package/src/helpers/dockerService/serviceComponents/imageUtils.js +5 -4
  111. package/src/helpers/exitWithMessage.js +13 -7
  112. package/src/helpers/imageNameUtils.js +32 -0
  113. package/src/helpers/logger.js +15 -16
  114. package/src/helpers/safeCall.js +2 -2
  115. package/src/helpers/validationHelpers.js +13 -29
  116. package/src/hooks/creation/useContainerActions.js +32 -12
  117. package/src/hooks/creation/useContainerCreation.js +293 -47
  118. package/src/hooks/creation/useLogsViewer.js +2 -2
  119. package/src/hooks/debug/useDebugLogs.js +15 -10
  120. package/src/hooks/navigation/useContainerSelection.js +17 -14
  121. package/src/hooks/useContainerCommandRouter.js +71 -58
  122. package/src/hooks/useContainers.js +4 -4
  123. package/src/hooks/useControls.js +135 -72
  124. package/src/hooks/useEraseConfirmation.js +3 -3
  125. package/src/hooks/useExitHandler.js +29 -26
  126. package/src/index.js +8 -4
  127. package/test/ContainerCreationPrompt.dom.test.js +313 -0
  128. package/test/ControlsHUD.dom.test.js +125 -0
  129. package/test/SuggestionPanel.dom.test.js +83 -0
  130. package/test/constants.test.js +41 -0
  131. package/test/dockerHubService.test.js +213 -0
  132. package/test/logger.test.js +58 -0
  133. package/test/resolveImageTag.test.js +26 -0
  134. package/test/useContainerCreation.dom.test.js +428 -2
  135. package/test/useContainerCreationHub.dom.test.js +184 -0
  136. package/test/useControls.dom.test.js +406 -4
@@ -23,30 +23,30 @@
23
23
  <div class='clearfix'>
24
24
 
25
25
  <div class='fl pad1y space-right2'>
26
- <span class="strong">27.83% </span>
26
+ <span class="strong">59.13% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>27/97</span>
28
+ <span class='fraction'>68/115</span>
29
29
  </div>
30
30
 
31
31
 
32
32
  <div class='fl pad1y space-right2'>
33
- <span class="strong">10.16% </span>
33
+ <span class="strong">67.77% </span>
34
34
  <span class="quiet">Branches</span>
35
- <span class='fraction'>6/59</span>
35
+ <span class='fraction'>61/90</span>
36
36
  </div>
37
37
 
38
38
 
39
39
  <div class='fl pad1y space-right2'>
40
- <span class="strong">13.04% </span>
40
+ <span class="strong">26.08% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>3/23</span>
42
+ <span class='fraction'>6/23</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
- <span class="strong">31.32% </span>
47
+ <span class="strong">62.96% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>26/83</span>
49
+ <span class='fraction'>68/108</span>
50
50
  </div>
51
51
 
52
52
 
@@ -61,7 +61,7 @@
61
61
  </div>
62
62
  </template>
63
63
  </div>
64
- <div class='status-line low'></div>
64
+ <div class='status-line medium'></div>
65
65
  <pre><table class="coverage">
66
66
  <tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
67
67
  <a name='L2'></a><a href='#L2'>2</a>
@@ -263,7 +263,68 @@
263
263
  <a name='L198'></a><a href='#L198'>198</a>
264
264
  <a name='L199'></a><a href='#L199'>199</a>
265
265
  <a name='L200'></a><a href='#L200'>200</a>
266
- <a name='L201'></a><a href='#L201'>201</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
266
+ <a name='L201'></a><a href='#L201'>201</a>
267
+ <a name='L202'></a><a href='#L202'>202</a>
268
+ <a name='L203'></a><a href='#L203'>203</a>
269
+ <a name='L204'></a><a href='#L204'>204</a>
270
+ <a name='L205'></a><a href='#L205'>205</a>
271
+ <a name='L206'></a><a href='#L206'>206</a>
272
+ <a name='L207'></a><a href='#L207'>207</a>
273
+ <a name='L208'></a><a href='#L208'>208</a>
274
+ <a name='L209'></a><a href='#L209'>209</a>
275
+ <a name='L210'></a><a href='#L210'>210</a>
276
+ <a name='L211'></a><a href='#L211'>211</a>
277
+ <a name='L212'></a><a href='#L212'>212</a>
278
+ <a name='L213'></a><a href='#L213'>213</a>
279
+ <a name='L214'></a><a href='#L214'>214</a>
280
+ <a name='L215'></a><a href='#L215'>215</a>
281
+ <a name='L216'></a><a href='#L216'>216</a>
282
+ <a name='L217'></a><a href='#L217'>217</a>
283
+ <a name='L218'></a><a href='#L218'>218</a>
284
+ <a name='L219'></a><a href='#L219'>219</a>
285
+ <a name='L220'></a><a href='#L220'>220</a>
286
+ <a name='L221'></a><a href='#L221'>221</a>
287
+ <a name='L222'></a><a href='#L222'>222</a>
288
+ <a name='L223'></a><a href='#L223'>223</a>
289
+ <a name='L224'></a><a href='#L224'>224</a>
290
+ <a name='L225'></a><a href='#L225'>225</a>
291
+ <a name='L226'></a><a href='#L226'>226</a>
292
+ <a name='L227'></a><a href='#L227'>227</a>
293
+ <a name='L228'></a><a href='#L228'>228</a>
294
+ <a name='L229'></a><a href='#L229'>229</a>
295
+ <a name='L230'></a><a href='#L230'>230</a>
296
+ <a name='L231'></a><a href='#L231'>231</a>
297
+ <a name='L232'></a><a href='#L232'>232</a>
298
+ <a name='L233'></a><a href='#L233'>233</a>
299
+ <a name='L234'></a><a href='#L234'>234</a>
300
+ <a name='L235'></a><a href='#L235'>235</a>
301
+ <a name='L236'></a><a href='#L236'>236</a>
302
+ <a name='L237'></a><a href='#L237'>237</a>
303
+ <a name='L238'></a><a href='#L238'>238</a>
304
+ <a name='L239'></a><a href='#L239'>239</a>
305
+ <a name='L240'></a><a href='#L240'>240</a>
306
+ <a name='L241'></a><a href='#L241'>241</a>
307
+ <a name='L242'></a><a href='#L242'>242</a>
308
+ <a name='L243'></a><a href='#L243'>243</a>
309
+ <a name='L244'></a><a href='#L244'>244</a>
310
+ <a name='L245'></a><a href='#L245'>245</a>
311
+ <a name='L246'></a><a href='#L246'>246</a>
312
+ <a name='L247'></a><a href='#L247'>247</a>
313
+ <a name='L248'></a><a href='#L248'>248</a>
314
+ <a name='L249'></a><a href='#L249'>249</a>
315
+ <a name='L250'></a><a href='#L250'>250</a>
316
+ <a name='L251'></a><a href='#L251'>251</a>
317
+ <a name='L252'></a><a href='#L252'>252</a>
318
+ <a name='L253'></a><a href='#L253'>253</a>
319
+ <a name='L254'></a><a href='#L254'>254</a>
320
+ <a name='L255'></a><a href='#L255'>255</a>
321
+ <a name='L256'></a><a href='#L256'>256</a>
322
+ <a name='L257'></a><a href='#L257'>257</a>
323
+ <a name='L258'></a><a href='#L258'>258</a>
324
+ <a name='L259'></a><a href='#L259'>259</a>
325
+ <a name='L260'></a><a href='#L260'>260</a>
326
+ <a name='L261'></a><a href='#L261'>261</a>
327
+ <a name='L262'></a><a href='#L262'>262</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
267
328
  <span class="cline-any cline-neutral">&nbsp;</span>
268
329
  <span class="cline-any cline-neutral">&nbsp;</span>
269
330
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -287,22 +348,31 @@
287
348
  <span class="cline-any cline-neutral">&nbsp;</span>
288
349
  <span class="cline-any cline-neutral">&nbsp;</span>
289
350
  <span class="cline-any cline-neutral">&nbsp;</span>
290
- <span class="cline-any cline-yes">24x</span>
351
+ <span class="cline-any cline-yes">96x</span>
291
352
  <span class="cline-any cline-neutral">&nbsp;</span>
292
353
  <span class="cline-any cline-neutral">&nbsp;</span>
293
- <span class="cline-any cline-yes">24x</span>
354
+ <span class="cline-any cline-yes">96x</span>
294
355
  <span class="cline-any cline-neutral">&nbsp;</span>
295
- <span class="cline-any cline-yes">24x</span>
356
+ <span class="cline-any cline-yes">96x</span>
296
357
  <span class="cline-any cline-neutral">&nbsp;</span>
297
358
  <span class="cline-any cline-yes">4x</span>
359
+ <span class="cline-any cline-neutral">&nbsp;</span>
360
+ <span class="cline-any cline-neutral">&nbsp;</span>
361
+ <span class="cline-any cline-neutral">&nbsp;</span>
362
+ <span class="cline-any cline-neutral">&nbsp;</span>
363
+ <span class="cline-any cline-neutral">&nbsp;</span>
298
364
  <span class="cline-any cline-yes">4x</span>
299
365
  <span class="cline-any cline-yes">4x</span>
300
366
  <span class="cline-any cline-yes">4x</span>
301
367
  <span class="cline-any cline-yes">4x</span>
302
368
  <span class="cline-any cline-yes">3x</span>
303
369
  <span class="cline-any cline-yes">3x</span>
370
+ <span class="cline-any cline-yes">2x</span>
371
+ <span class="cline-any cline-neutral">&nbsp;</span>
372
+ <span class="cline-any cline-neutral">&nbsp;</span>
304
373
  <span class="cline-any cline-yes">3x</span>
305
374
  <span class="cline-any cline-neutral">&nbsp;</span>
375
+ <span class="cline-any cline-neutral">&nbsp;</span>
306
376
  <span class="cline-any cline-yes">3x</span>
307
377
  <span class="cline-any cline-yes">3x</span>
308
378
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -316,11 +386,15 @@
316
386
  <span class="cline-any cline-neutral">&nbsp;</span>
317
387
  <span class="cline-any cline-neutral">&nbsp;</span>
318
388
  <span class="cline-any cline-neutral">&nbsp;</span>
319
- <span class="cline-any cline-yes">24x</span>
320
- <span class="cline-any cline-yes">24x</span>
321
- <span class="cline-any cline-yes">24x</span>
389
+ <span class="cline-any cline-yes">96x</span>
390
+ <span class="cline-any cline-yes">96x</span>
391
+ <span class="cline-any cline-yes">96x</span>
392
+ <span class="cline-any cline-neutral">&nbsp;</span>
322
393
  <span class="cline-any cline-neutral">&nbsp;</span>
323
- <span class="cline-any cline-yes">24x</span>
394
+ <span class="cline-any cline-yes">96x</span>
395
+ <span class="cline-any cline-yes">96x</span>
396
+ <span class="cline-any cline-neutral">&nbsp;</span>
397
+ <span class="cline-any cline-yes">96x</span>
324
398
  <span class="cline-any cline-neutral">&nbsp;</span>
325
399
  <span class="cline-any cline-no">&nbsp;</span>
326
400
  <span class="cline-any cline-no">&nbsp;</span>
@@ -339,19 +413,24 @@
339
413
  <span class="cline-any cline-neutral">&nbsp;</span>
340
414
  <span class="cline-any cline-neutral">&nbsp;</span>
341
415
  <span class="cline-any cline-neutral">&nbsp;</span>
342
- <span class="cline-any cline-yes">24x</span>
416
+ <span class="cline-any cline-yes">96x</span>
417
+ <span class="cline-any cline-neutral">&nbsp;</span>
343
418
  <span class="cline-any cline-no">&nbsp;</span>
344
419
  <span class="cline-any cline-neutral">&nbsp;</span>
420
+ <span class="cline-any cline-neutral">&nbsp;</span>
345
421
  <span class="cline-any cline-no">&nbsp;</span>
346
422
  <span class="cline-any cline-no">&nbsp;</span>
347
423
  <span class="cline-any cline-no">&nbsp;</span>
348
424
  <span class="cline-any cline-neutral">&nbsp;</span>
349
425
  <span class="cline-any cline-neutral">&nbsp;</span>
426
+ <span class="cline-any cline-neutral">&nbsp;</span>
350
427
  <span class="cline-any cline-no">&nbsp;</span>
351
428
  <span class="cline-any cline-neutral">&nbsp;</span>
352
429
  <span class="cline-any cline-neutral">&nbsp;</span>
353
430
  <span class="cline-any cline-neutral">&nbsp;</span>
354
- <span class="cline-any cline-yes">24x</span>
431
+ <span class="cline-any cline-neutral">&nbsp;</span>
432
+ <span class="cline-any cline-neutral">&nbsp;</span>
433
+ <span class="cline-any cline-yes">96x</span>
355
434
  <span class="cline-any cline-neutral">&nbsp;</span>
356
435
  <span class="cline-any cline-neutral">&nbsp;</span>
357
436
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -361,13 +440,15 @@
361
440
  <span class="cline-any cline-neutral">&nbsp;</span>
362
441
  <span class="cline-any cline-no">&nbsp;</span>
363
442
  <span class="cline-any cline-no">&nbsp;</span>
364
- <span class="cline-any cline-no">&nbsp;</span>
443
+ <span class="cline-any cline-neutral">&nbsp;</span>
365
444
  <span class="cline-any cline-neutral">&nbsp;</span>
366
445
  <span class="cline-any cline-no">&nbsp;</span>
446
+ <span class="cline-any cline-neutral">&nbsp;</span>
367
447
  <span class="cline-any cline-no">&nbsp;</span>
448
+ <span class="cline-any cline-yes">12x</span>
368
449
  <span class="cline-any cline-neutral">&nbsp;</span>
369
450
  <span class="cline-any cline-neutral">&nbsp;</span>
370
- <span class="cline-any cline-yes">24x</span>
451
+ <span class="cline-any cline-yes">96x</span>
371
452
  <span class="cline-any cline-neutral">&nbsp;</span>
372
453
  <span class="cline-any cline-no">&nbsp;</span>
373
454
  <span class="cline-any cline-no">&nbsp;</span>
@@ -379,69 +460,109 @@
379
460
  <span class="cline-any cline-neutral">&nbsp;</span>
380
461
  <span class="cline-any cline-neutral">&nbsp;</span>
381
462
  <span class="cline-any cline-neutral">&nbsp;</span>
382
- <span class="cline-any cline-yes">24x</span>
383
- <span class="cline-any cline-no">&nbsp;</span>
463
+ <span class="cline-any cline-yes">96x</span>
384
464
  <span class="cline-any cline-neutral">&nbsp;</span>
385
- <span class="cline-any cline-no">&nbsp;</span>
386
- <span class="cline-any cline-no">&nbsp;</span>
465
+ <span class="cline-any cline-yes">27x</span>
387
466
  <span class="cline-any cline-neutral">&nbsp;</span>
467
+ <span class="cline-any cline-yes">27x</span>
388
468
  <span class="cline-any cline-no">&nbsp;</span>
469
+ <span class="cline-any cline-yes">27x</span>
470
+ <span class="cline-any cline-neutral">&nbsp;</span>
471
+ <span class="cline-any cline-yes">27x</span>
389
472
  <span class="cline-any cline-no">&nbsp;</span>
390
473
  <span class="cline-any cline-no">&nbsp;</span>
391
474
  <span class="cline-any cline-no">&nbsp;</span>
392
475
  <span class="cline-any cline-neutral">&nbsp;</span>
393
476
  <span class="cline-any cline-neutral">&nbsp;</span>
394
- <span class="cline-any cline-no">&nbsp;</span>
395
- <span class="cline-any cline-no">&nbsp;</span>
396
- <span class="cline-any cline-no">&nbsp;</span>
477
+ <span class="cline-any cline-yes">27x</span>
478
+ <span class="cline-any cline-neutral">&nbsp;</span>
479
+ <span class="cline-any cline-yes">6x</span>
480
+ <span class="cline-any cline-yes">2x</span>
481
+ <span class="cline-any cline-yes">2x</span>
482
+ <span class="cline-any cline-neutral">&nbsp;</span>
483
+ <span class="cline-any cline-yes">4x</span>
484
+ <span class="cline-any cline-yes">4x</span>
485
+ <span class="cline-any cline-neutral">&nbsp;</span>
486
+ <span class="cline-any cline-neutral">&nbsp;</span>
487
+ <span class="cline-any cline-neutral">&nbsp;</span>
488
+ <span class="cline-any cline-yes">21x</span>
489
+ <span class="cline-any cline-yes">4x</span>
490
+ <span class="cline-any cline-yes">3x</span>
491
+ <span class="cline-any cline-yes">1x</span>
492
+ <span class="cline-any cline-neutral">&nbsp;</span>
493
+ <span class="cline-any cline-neutral">&nbsp;</span>
494
+ <span class="cline-any cline-yes">4x</span>
495
+ <span class="cline-any cline-neutral">&nbsp;</span>
496
+ <span class="cline-any cline-neutral">&nbsp;</span>
497
+ <span class="cline-any cline-neutral">&nbsp;</span>
498
+ <span class="cline-any cline-yes">17x</span>
499
+ <span class="cline-any cline-yes">8x</span>
500
+ <span class="cline-any cline-yes">1x</span>
501
+ <span class="cline-any cline-yes">1x</span>
502
+ <span class="cline-any cline-neutral">&nbsp;</span>
503
+ <span class="cline-any cline-yes">7x</span>
504
+ <span class="cline-any cline-yes">5x</span>
505
+ <span class="cline-any cline-yes">5x</span>
506
+ <span class="cline-any cline-neutral">&nbsp;</span>
397
507
  <span class="cline-any cline-neutral">&nbsp;</span>
398
508
  <span class="cline-any cline-neutral">&nbsp;</span>
509
+ <span class="cline-any cline-yes">11x</span>
399
510
  <span class="cline-any cline-no">&nbsp;</span>
400
511
  <span class="cline-any cline-no">&nbsp;</span>
401
512
  <span class="cline-any cline-no">&nbsp;</span>
513
+ <span class="cline-any cline-neutral">&nbsp;</span>
514
+ <span class="cline-any cline-no">&nbsp;</span>
402
515
  <span class="cline-any cline-no">&nbsp;</span>
403
516
  <span class="cline-any cline-no">&nbsp;</span>
404
517
  <span class="cline-any cline-no">&nbsp;</span>
405
518
  <span class="cline-any cline-neutral">&nbsp;</span>
406
519
  <span class="cline-any cline-neutral">&nbsp;</span>
520
+ <span class="cline-any cline-yes">11x</span>
521
+ <span class="cline-any cline-yes">9x</span>
522
+ <span class="cline-any cline-yes">9x</span>
523
+ <span class="cline-any cline-neutral">&nbsp;</span>
524
+ <span class="cline-any cline-yes">9x</span>
407
525
  <span class="cline-any cline-no">&nbsp;</span>
526
+ <span class="cline-any cline-yes">9x</span>
408
527
  <span class="cline-any cline-no">&nbsp;</span>
409
- <span class="cline-any cline-no">&nbsp;</span>
410
- <span class="cline-any cline-no">&nbsp;</span>
528
+ <span class="cline-any cline-yes">9x</span>
411
529
  <span class="cline-any cline-no">&nbsp;</span>
412
530
  <span class="cline-any cline-neutral">&nbsp;</span>
413
531
  <span class="cline-any cline-neutral">&nbsp;</span>
414
532
  <span class="cline-any cline-neutral">&nbsp;</span>
415
533
  <span class="cline-any cline-neutral">&nbsp;</span>
416
- <span class="cline-any cline-yes">24x</span>
417
- <span class="cline-any cline-no">&nbsp;</span>
534
+ <span class="cline-any cline-neutral">&nbsp;</span>
535
+ <span class="cline-any cline-neutral">&nbsp;</span>
536
+ <span class="cline-any cline-yes">96x</span>
537
+ <span class="cline-any cline-yes">39x</span>
418
538
  <span class="cline-any cline-no">&nbsp;</span>
419
539
  <span class="cline-any cline-no">&nbsp;</span>
420
540
  <span class="cline-any cline-neutral">&nbsp;</span>
421
541
  <span class="cline-any cline-neutral">&nbsp;</span>
422
- <span class="cline-any cline-no">&nbsp;</span>
542
+ <span class="cline-any cline-yes">39x</span>
423
543
  <span class="cline-any cline-no">&nbsp;</span>
424
544
  <span class="cline-any cline-no">&nbsp;</span>
425
545
  <span class="cline-any cline-neutral">&nbsp;</span>
426
546
  <span class="cline-any cline-no">&nbsp;</span>
427
547
  <span class="cline-any cline-neutral">&nbsp;</span>
428
548
  <span class="cline-any cline-neutral">&nbsp;</span>
429
- <span class="cline-any cline-no">&nbsp;</span>
430
- <span class="cline-any cline-no">&nbsp;</span>
431
- <span class="cline-any cline-no">&nbsp;</span>
549
+ <span class="cline-any cline-yes">39x</span>
550
+ <span class="cline-any cline-yes">27x</span>
551
+ <span class="cline-any cline-yes">27x</span>
432
552
  <span class="cline-any cline-neutral">&nbsp;</span>
433
553
  <span class="cline-any cline-neutral">&nbsp;</span>
434
- <span class="cline-any cline-no">&nbsp;</span>
554
+ <span class="cline-any cline-yes">12x</span>
435
555
  <span class="cline-any cline-no">&nbsp;</span>
436
556
  <span class="cline-any cline-no">&nbsp;</span>
437
557
  <span class="cline-any cline-neutral">&nbsp;</span>
438
558
  <span class="cline-any cline-neutral">&nbsp;</span>
439
- <span class="cline-any cline-no">&nbsp;</span>
440
- <span class="cline-any cline-no">&nbsp;</span>
441
- <span class="cline-any cline-no">&nbsp;</span>
559
+ <span class="cline-any cline-yes">12x</span>
560
+ <span class="cline-any cline-yes">12x</span>
561
+ <span class="cline-any cline-yes">12x</span>
562
+ <span class="cline-any cline-neutral">&nbsp;</span>
442
563
  <span class="cline-any cline-neutral">&nbsp;</span>
564
+ <span class="cline-any cline-yes">96x</span>
443
565
  <span class="cline-any cline-neutral">&nbsp;</span>
444
- <span class="cline-any cline-yes">24x</span>
445
566
  <span class="cline-any cline-neutral">&nbsp;</span>
446
567
  <span class="cline-any cline-neutral">&nbsp;</span>
447
568
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -463,20 +584,21 @@
463
584
  <span class="cline-any cline-neutral">&nbsp;</span>
464
585
  <span class="cline-any cline-neutral">&nbsp;</span>
465
586
  <span class="cline-any cline-neutral">&nbsp;</span>
466
- <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import React from "react";
467
- import { useInput } from "ink";
468
- import { useContainerActions } from "./creation/useContainerActions";
469
- import { useContainerCreation } from "./creation/useContainerCreation";
470
- import { useLogsViewer } from "./creation/useLogsViewer";
471
- import { useContainerSelection } from "./navigation/useContainerSelection";
472
- import { useDebugLogs } from "./debug/useDebugLogs";
473
- import { useEraseConfirmation } from "./useEraseConfirmation";
474
- import { useExitHandler } from "./useExitHandler";
475
- import { useContainerCommandRouter } from "./useContainerCommandRouter";
476
- import { getLogsStream } from "../helpers/dockerService/serviceComponents/containerLogs.js";
477
- import { createContainer as svcCreateContainer } from "../helpers/dockerService/serviceComponents/containerActions.js";
478
- import { buildContainerOptions } from "../helpers/containerOptionsBuilder.js";
479
- import { DB_IMAGES } from "../helpers/constants.js";
587
+ <span class="cline-any cline-neutral">&nbsp;</span>
588
+ <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import React from 'react';
589
+ import { useInput } from 'ink';
590
+ import { useContainerActions } from './creation/useContainerActions';
591
+ import { useContainerCreation } from './creation/useContainerCreation';
592
+ import { useLogsViewer } from './creation/useLogsViewer';
593
+ import { useContainerSelection } from './navigation/useContainerSelection';
594
+ import { useDebugLogs } from './debug/useDebugLogs';
595
+ import { useEraseConfirmation } from './useEraseConfirmation';
596
+ import { useExitHandler } from './useExitHandler';
597
+ import { useContainerCommandRouter } from './useContainerCommandRouter';
598
+ import { getLogsStream } from '../helpers/dockerService/serviceComponents/containerLogs.js';
599
+ import { createContainer as svcCreateContainer } from '../helpers/dockerService/serviceComponents/containerActions.js';
600
+ import { buildContainerOptions } from '../helpers/containerOptionsBuilder.js';
601
+ import { DB_IMAGES } from '../helpers/constants.js';
480
602
  &nbsp;
481
603
  // Principal hook to manage user inputs and control the app state
482
604
  /**
@@ -486,7 +608,7 @@ import { DB_IMAGES } from "../helpers/constants.js";
486
608
  * @param {Array&lt;Object&gt;} containers - Current list of Docker containers
487
609
  * @returns {Object} controls - API for the App component
488
610
  */
489
- export function useControls(containers = <span class="branch-0 cbranch-no" title="branch not covered" >[])</span> {
611
+ export function useControls(containers = <span class="branch-0 cbranch-no" title="branch not covered" >[],</span> overrides = {}) {
490
612
  const [creatingContainer, setCreatingContainer] = React.useState(false);
491
613
  &nbsp;
492
614
  // — Modular hooks —
@@ -494,20 +616,29 @@ export function useControls(containers = <span class="branch-0 cbranch-no" title
494
616
  &nbsp;
495
617
  const creation = useContainerCreation({
496
618
  onCreate: async ({ imageName, containerName, portInput, envInput }) =&gt; {
497
- const options = buildContainerOptions({ imageName, containerName, portInput, envInput });
619
+ const options = buildContainerOptions({
620
+ imageName,
621
+ containerName,
622
+ portInput,
623
+ envInput,
624
+ });
498
625
  actions.setMessage(`Creating container ${imageName}...`);
499
- actions.setMessageColor("yellow");
626
+ actions.setMessageColor('yellow');
500
627
  try {
501
628
  const { id, ports } = await svcCreateContainer(imageName, options);
502
- let portMsg = "";
629
+ let portMsg = '';
503
630
  if (ports &amp;&amp; ports.length) {
504
- portMsg = " | Ports: " + ports.map(p =&gt; `${p.hostPort}→${p.containerPort}/${p.protocol}`).join(", ");
631
+ portMsg =
632
+ ' | Ports: ' +
633
+ ports
634
+ .map((p) =&gt; `${p.hostPort}→${p.containerPort}/${p.protocol}`)
635
+ .join(', ');
505
636
  }
506
637
  actions.setMessage(`Created container ${id}${portMsg}`);
507
- actions.setMessageColor("green");
638
+ actions.setMessageColor('green');
508
639
  } catch (err) {
509
640
  actions.setMessage(`Error creating container: ${err.message}`);
510
- actions.setMessageColor("red");
641
+ actions.setMessageColor('red');
511
642
  } finally {
512
643
  setCreatingContainer(false);
513
644
  }
@@ -519,19 +650,23 @@ export function useControls(containers = <span class="branch-0 cbranch-no" title
519
650
  const logsViewer = useLogsViewer();
520
651
  const selection = useContainerSelection(containers.length);
521
652
  const debugLogs = useDebugLogs();
653
+ &nbsp;
654
+ // Allow overrides for testability (e.g. injecting a mock triggerHubSearch)
655
+ const triggerHubSearch = overrides.triggerHubSearch ?? creation.triggerHubSearch;
656
+ const isSearchingHub = overrides.isSearchingHub ?? creation.isSearchingHub;
522
657
  &nbsp;
523
658
  const eraseConfirmation = useEraseConfirmation({
524
659
  onConfirm: <span class="fstat-no" title="function not covered" >()</span> =&gt; {
525
660
  <span class="cstat-no" title="statement not covered" > actions.handleAction({</span>
526
661
  actionFn: <span class="fstat-no" title="function not covered" >as</span>ync (id) =&gt; <span class="cstat-no" title="statement not covered" >await actions.removeContainer(id),</span>
527
- actionLabel: "Erasing",
662
+ actionLabel: 'Erasing',
528
663
  selected: selection.selected,
529
664
  });
530
- <span class="cstat-no" title="statement not covered" > actions.setMessageColor("yellow");</span>
665
+ <span class="cstat-no" title="statement not covered" > actions.setMessageColor('yellow');</span>
531
666
  },
532
667
  onCancel: <span class="fstat-no" title="function not covered" >()</span> =&gt; {
533
- <span class="cstat-no" title="statement not covered" > actions.setMessage("");</span>
534
- <span class="cstat-no" title="statement not covered" > actions.setMessageColor("");</span>
668
+ <span class="cstat-no" title="statement not covered" > actions.setMessage('');</span>
669
+ <span class="cstat-no" title="statement not covered" > actions.setMessageColor('');</span>
535
670
  },
536
671
  });
537
672
  &nbsp;
@@ -539,17 +674,22 @@ export function useControls(containers = <span class="branch-0 cbranch-no" title
539
674
  * Pipe container logs into the viewer while applying a hard limit.
540
675
  * @param {string} containerId - Container identifier used by Docker.
541
676
  */
542
- const startLogsStream = React.useCallback(<span class="fstat-no" title="function not covered" >(c</span>ontainerId) =&gt; {
543
- <span class="cstat-no" title="statement not covered" > getLogsStream(</span>
544
- containerId,
545
- <span class="fstat-no" title="function not covered" > (d</span>ata) =&gt; <span class="cstat-no" title="statement not covered" >logsViewer.setLogs(<span class="fstat-no" title="function not covered" >(p</span>rev) =&gt; {</span>
546
- const newLogs = <span class="cstat-no" title="statement not covered" >[...prev, ...data.split("\n").filter(Boolean)];</span>
547
- <span class="cstat-no" title="statement not covered" > return newLogs.slice(-1000);</span>
548
- }),
549
- <span class="fstat-no" title="function not covered" > ()</span> =&gt; {},
550
- <span class="fstat-no" title="function not covered" > (e</span>rr) =&gt; <span class="cstat-no" title="statement not covered" >logsViewer.setLogs(<span class="fstat-no" title="function not covered" >(p</span>rev) =&gt; <span class="cstat-no" title="statement not covered" >[...prev, `Error: ${err.message}`])</span></span>
551
- );
552
- }, [logsViewer]);
677
+ const startLogsStream = React.useCallback(
678
+ <span class="fstat-no" title="function not covered" > (c</span>ontainerId) =&gt; {
679
+ <span class="cstat-no" title="statement not covered" > getLogsStream(</span>
680
+ containerId,
681
+ <span class="fstat-no" title="function not covered" > (d</span>ata) =&gt;
682
+ <span class="cstat-no" title="statement not covered" > logsViewer.setLogs(<span class="fstat-no" title="function not covered" >(p</span>rev) =&gt; {</span>
683
+ const newLogs = <span class="cstat-no" title="statement not covered" >[...prev, ...data.split('\n').filter(Boolean)];</span>
684
+ <span class="cstat-no" title="statement not covered" > return newLogs.slice(-1000);</span>
685
+ }),
686
+ <span class="fstat-no" title="function not covered" > ()</span> =&gt; {},
687
+ <span class="fstat-no" title="function not covered" > (e</span>rr) =&gt;
688
+ <span class="cstat-no" title="statement not covered" > logsViewer.setLogs(<span class="fstat-no" title="function not covered" >(p</span>rev) =&gt; <span class="cstat-no" title="statement not covered" >[...prev, `Error: ${err.message}`])</span></span>
689
+ );
690
+ },
691
+ [logsViewer]
692
+ );
553
693
  &nbsp;
554
694
  const commandRouter = useContainerCommandRouter({
555
695
  actions,
@@ -560,17 +700,19 @@ export function useControls(containers = <span class="branch-0 cbranch-no" title
560
700
  startLogsStream,
561
701
  onStartErase: <span class="fstat-no" title="function not covered" >()</span> =&gt; {
562
702
  <span class="cstat-no" title="statement not covered" > eraseConfirmation.startErase();</span>
563
- <span class="cstat-no" title="statement not covered" > actions.setMessage("Are you sure you want to erase this container? (y/n)");</span>
564
- <span class="cstat-no" title="statement not covered" > actions.setMessageColor("yellow");</span>
703
+ <span class="cstat-no" title="statement not covered" > actions.setMessage(</span>
704
+ 'Are you sure you want to erase this container? (y/n)'
705
+ );
706
+ <span class="cstat-no" title="statement not covered" > actions.setMessageColor('yellow');</span>
565
707
  },
566
708
  onToggleDebug: <span class="fstat-no" title="function not covered" >()</span> =&gt; <span class="cstat-no" title="statement not covered" >debugLogs.setShowDebugLogs(<span class="fstat-no" title="function not covered" >(p</span>rev) =&gt; <span class="cstat-no" title="statement not covered" >!prev)</span>,</span>
567
- onStartCreate: <span class="fstat-no" title="function not covered" >()</span> =&gt; <span class="cstat-no" title="statement not covered" >setCreatingContainer(true),</span>
709
+ onStartCreate: () =&gt; setCreatingContainer(true),
568
710
  });
569
711
  &nbsp;
570
712
  const exitHandler = useExitHandler({
571
713
  onBeforeExit: <span class="fstat-no" title="function not covered" >()</span> =&gt; {
572
- <span class="cstat-no" title="statement not covered" > actions.setMessage("Exiting...");</span>
573
- <span class="cstat-no" title="statement not covered" > actions.setMessageColor("yellow");</span>
714
+ <span class="cstat-no" title="statement not covered" > actions.setMessage('Exiting...');</span>
715
+ <span class="cstat-no" title="statement not covered" > actions.setMessageColor('yellow');</span>
574
716
  <span class="cstat-no" title="statement not covered" > logsViewer.closeLogs();</span>
575
717
  <span class="cstat-no" title="statement not covered" > debugLogs.setShowDebugLogs(false);</span>
576
718
  },
@@ -579,73 +721,114 @@ export function useControls(containers = <span class="branch-0 cbranch-no" title
579
721
  /**
580
722
  * Route keystrokes to the container creation wizard.
581
723
  */
582
- const processCreationInput = React.useCallback(<span class="fstat-no" title="function not covered" >(i</span>nput, key) =&gt; {
583
- const step = <span class="cstat-no" title="statement not covered" >creation.step;</span>
724
+ const processCreationInput = React.useCallback(
725
+ (input, key) =&gt; {
726
+ const step = creation.step;
584
727
  &nbsp;
585
- const removeLastChar = <span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >(s</span>etter) =&gt; <span class="cstat-no" title="statement not covered" >setter(<span class="fstat-no" title="function not covered" >(v</span>alue) =&gt; <span class="cstat-no" title="statement not covered" >(value || "").slice(0, -1))</span>;</span></span>
586
- const appendChar = <span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >(s</span>etter, current, ch) =&gt; <span class="cstat-no" title="statement not covered" >setter((current || "") + ch);</span></span>
728
+ const removeLastChar = <span class="fstat-no" title="function not covered" >(s</span>etter) =&gt;
729
+ <span class="cstat-no" title="statement not covered" > setter(<span class="fstat-no" title="function not covered" >(v</span>alue) =&gt; <span class="cstat-no" title="statement not covered" >(value || '').slice(0, -1))</span>;</span>
730
+ const appendChar = <span class="fstat-no" title="function not covered" >(s</span>etter, current, ch) =&gt; <span class="cstat-no" title="statement not covered" >setter((current || '') + ch);</span>
587
731
  &nbsp;
588
- <span class="cstat-no" title="statement not covered" > if (key.escape) {</span>
589
- <span class="cstat-no" title="statement not covered" > creation.cancelCreation();</span>
590
- <span class="cstat-no" title="statement not covered" > setCreatingContainer(false);</span>
591
- <span class="cstat-no" title="statement not covered" > return;</span>
592
- }
732
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (key.escape) {
733
+ <span class="cstat-no" title="statement not covered" > creation.cancelCreation();</span>
734
+ <span class="cstat-no" title="statement not covered" > setCreatingContainer(false);</span>
735
+ <span class="cstat-no" title="statement not covered" > return;</span>
736
+ }
593
737
  &nbsp;
594
- <span class="cstat-no" title="statement not covered" > if (input === "\r" || input === "\n") {</span>
595
- <span class="cstat-no" title="statement not covered" > creation.nextStep();</span>
596
- <span class="cstat-no" title="statement not covered" > return;</span>
597
- }
738
+ if (input === '\r' || input === '\n') {
739
+ // If on step 0 with a focused suggestion, apply it instead of advancing
740
+ if (step === 0 &amp;&amp; creation.selectedSuggestionIndex &gt;= 0) {
741
+ creation.applyFocusedSuggestion();
742
+ return;
743
+ }
744
+ creation.nextStep();
745
+ return;
746
+ }
598
747
  &nbsp;
599
- <span class="cstat-no" title="statement not covered" > if (key.backspace || key.delete) {</span>
600
- <span class="cstat-no" title="statement not covered" > if (step === 0) <span class="cstat-no" title="statement not covered" >removeLastChar(creation.setImageName);</span></span>
601
- <span class="cstat-no" title="statement not covered" > if (step === 1) <span class="cstat-no" title="statement not covered" >removeLastChar(creation.setContainerName);</span></span>
602
- <span class="cstat-no" title="statement not covered" > if (step === 2) <span class="cstat-no" title="statement not covered" >removeLastChar(creation.setPortInput);</span></span>
603
- <span class="cstat-no" title="statement not covered" > if (step === 3) <span class="cstat-no" title="statement not covered" >removeLastChar(creation.setEnvInput);</span></span>
604
- <span class="cstat-no" title="statement not covered" > return;</span>
605
- }
748
+ // Tab on step 0: trigger Docker Hub search (with guards)
749
+ if (key.tab) {
750
+ if (step === 0) {
751
+ if (!isSearchingHub &amp;&amp; (creation.imageName || '').trim() !== '') {
752
+ triggerHubSearch();
753
+ }
754
+ }
755
+ return;
756
+ }
606
757
  &nbsp;
607
- <span class="cstat-no" title="statement not covered" > if (input &amp;&amp; input.length === 1 &amp;&amp; !key.ctrl &amp;&amp; !key.meta) {</span>
608
- <span class="cstat-no" title="statement not covered" > if (step === 0) <span class="cstat-no" title="statement not covered" >appendChar(creation.setImageName, creation.imageName, input);</span></span>
609
- <span class="cstat-no" title="statement not covered" > if (step === 1) <span class="cstat-no" title="statement not covered" >appendChar(creation.setContainerName, creation.containerName, input);</span></span>
610
- <span class="cstat-no" title="statement not covered" > if (step === 2) <span class="cstat-no" title="statement not covered" >appendChar(creation.setPortInput, creation.portInput, input);</span></span>
611
- <span class="cstat-no" title="statement not covered" > if (step === 3) <span class="cstat-no" title="statement not covered" >appendChar(creation.setEnvInput, creation.envInput, input);</span></span>
612
- }
613
- }, [creation, setCreatingContainer]);
758
+ // Arrow keys on step 0: navigate suggestion list
759
+ if (step === 0 &amp;&amp; creation.suggestions.length &gt; 0) {
760
+ if (key.upArrow) {
761
+ creation.moveSuggestionSelection(-1);
762
+ return;
763
+ }
764
+ if (key.downArrow) {
765
+ creation.moveSuggestionSelection(1);
766
+ return;
767
+ }
768
+ }
769
+ &nbsp;
770
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (key.backspace || key.delete) {
771
+ <span class="cstat-no" title="statement not covered" > if (step === 0) {</span>
772
+ const newValue = <span class="cstat-no" title="statement not covered" >(creation.imageName || '').slice(0, -1);</span>
773
+ <span class="cstat-no" title="statement not covered" > creation.updateImageInput(newValue);</span>
774
+ }
775
+ <span class="cstat-no" title="statement not covered" > if (step === 1) <span class="cstat-no" title="statement not covered" >removeLastChar(creation.setContainerName);</span></span>
776
+ <span class="cstat-no" title="statement not covered" > if (step === 2) <span class="cstat-no" title="statement not covered" >removeLastChar(creation.setPortInput);</span></span>
777
+ <span class="cstat-no" title="statement not covered" > if (step === 3) <span class="cstat-no" title="statement not covered" >removeLastChar(creation.setEnvInput);</span></span>
778
+ <span class="cstat-no" title="statement not covered" > return;</span>
779
+ }
780
+ &nbsp;
781
+ if (input &amp;&amp; input.length === 1 &amp;&amp; !key.ctrl &amp;&amp; !key.meta) {
782
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (step === 0) {
783
+ creation.updateImageInput((creation.imageName || '') + input);
784
+ }
785
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (step === 1)
786
+ <span class="cstat-no" title="statement not covered" > appendChar(creation.setContainerName, creation.containerName, input);</span>
787
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (step === 2)
788
+ <span class="cstat-no" title="statement not covered" > appendChar(creation.setPortInput, creation.portInput, input);</span>
789
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (step === 3)
790
+ <span class="cstat-no" title="statement not covered" > appendChar(creation.setEnvInput, creation.envInput, input);</span>
791
+ }
792
+ },
793
+ [creation, setCreatingContainer, triggerHubSearch, isSearchingHub]
794
+ );
614
795
  &nbsp;
615
796
  // Single keyboard entry point
616
- useInput(<span class="fstat-no" title="function not covered" >(i</span>nput, key) =&gt; {
617
- <span class="cstat-no" title="statement not covered" > if (eraseConfirmation.confirmErase) {</span>
797
+ useInput((input, key) =&gt; {
798
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (eraseConfirmation.confirmErase) {
618
799
  <span class="cstat-no" title="statement not covered" > eraseConfirmation.processEraseConfirmation(input, key);</span>
619
800
  <span class="cstat-no" title="statement not covered" > return;</span>
620
801
  }
621
802
  &nbsp;
622
- <span class="cstat-no" title="statement not covered" > if (logsViewer.showLogs) {</span>
623
- <span class="cstat-no" title="statement not covered" > if (input === "q" || key.escape) {</span>
803
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (logsViewer.showLogs) {
804
+ <span class="cstat-no" title="statement not covered" > if (input === 'q' || key.escape) {</span>
624
805
  <span class="cstat-no" title="statement not covered" > logsViewer.closeLogs();</span>
625
806
  }
626
807
  <span class="cstat-no" title="statement not covered" > return;</span>
627
808
  }
628
809
  &nbsp;
629
- <span class="cstat-no" title="statement not covered" > if (creatingContainer) {</span>
630
- <span class="cstat-no" title="statement not covered" > processCreationInput(input, key);</span>
631
- <span class="cstat-no" title="statement not covered" > return;</span>
810
+ if (creatingContainer) {
811
+ processCreationInput(input, key);
812
+ return;
632
813
  }
633
814
  &nbsp;
634
- <span class="cstat-no" title="statement not covered" > if (debugLogs.showDebugLogs &amp;&amp; key.escape) {</span>
815
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (debugLogs.showDebugLogs &amp;&amp; <span class="branch-1 cbranch-no" title="branch not covered" >key.escape)</span> {
635
816
  <span class="cstat-no" title="statement not covered" > debugLogs.setShowDebugLogs(false);</span>
636
817
  <span class="cstat-no" title="statement not covered" > return;</span>
637
818
  }
638
819
  &nbsp;
639
- <span class="cstat-no" title="statement not covered" > commandRouter.handleDockerCommands(input);</span>
640
- <span class="cstat-no" title="statement not covered" > exitHandler.handleExitCommand(input);</span>
641
- <span class="cstat-no" title="statement not covered" > selection.handleNavigation(input, key);</span>
820
+ commandRouter.handleDockerCommands(input);
821
+ exitHandler.handleExitCommand(input);
822
+ selection.handleNavigation(input, key);
642
823
  });
643
824
  &nbsp;
644
825
  return {
645
826
  selected: selection.selected,
646
827
  setSelected: selection.setSelected,
647
- message: creatingContainer ? <span class="branch-0 cbranch-no" title="branch not covered" >creation.message </span>: actions.message,
648
- messageColor: creatingContainer ? <span class="branch-0 cbranch-no" title="branch not covered" >creation.messageColor </span>: actions.messageColor,
828
+ message: creatingContainer ? creation.message : actions.message,
829
+ messageColor: creatingContainer
830
+ ? creation.messageColor
831
+ : actions.messageColor,
649
832
  showLogs: logsViewer.showLogs,
650
833
  logs: logsViewer.logs,
651
834
  exitLogs: logsViewer.closeLogs,
@@ -670,7 +853,7 @@ export function useControls(containers = <span class="branch-0 cbranch-no" title
670
853
  <div class='footer quiet pad2 space-top1 center small'>
671
854
  Code coverage generated by
672
855
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
673
- at 2026-04-25T17:40:28.828Z
856
+ at 2026-04-27T22:13:21.150Z
674
857
  </div>
675
858
  <script src="../prettify.js"></script>
676
859
  <script>