@muhammedaksam/easiarr 1.4.0 → 1.4.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.
Files changed (109) hide show
  1. package/dist/index.js +29837 -0
  2. package/package.json +15 -15
  3. package/src/VersionInfo.ts +0 -12
  4. package/src/api/arr-api.ts +0 -521
  5. package/src/api/auto-setup-types.ts +0 -64
  6. package/src/api/base-api.ts +0 -216
  7. package/src/api/bazarr-api.ts +0 -297
  8. package/src/api/cloudflare-api.ts +0 -493
  9. package/src/api/custom-format-api.ts +0 -224
  10. package/src/api/grafana-api.ts +0 -230
  11. package/src/api/heimdall-api.ts +0 -141
  12. package/src/api/homarr-api.ts +0 -209
  13. package/src/api/huntarr-api.ts +0 -457
  14. package/src/api/index.ts +0 -9
  15. package/src/api/jellyfin-api.ts +0 -389
  16. package/src/api/jellyseerr-api.ts +0 -559
  17. package/src/api/maintainerr-api.ts +0 -461
  18. package/src/api/naming-config.ts +0 -101
  19. package/src/api/overseerr-api.ts +0 -392
  20. package/src/api/plex-api.ts +0 -308
  21. package/src/api/portainer-api.ts +0 -374
  22. package/src/api/profilarr-api.ts +0 -391
  23. package/src/api/prowlarr-api.ts +0 -488
  24. package/src/api/qbittorrent-api.ts +0 -300
  25. package/src/api/quality-profile-api.ts +0 -272
  26. package/src/api/tautulli-api.ts +0 -244
  27. package/src/api/uptime-kuma-api.ts +0 -343
  28. package/src/apps/categories.ts +0 -14
  29. package/src/apps/index.ts +0 -2
  30. package/src/apps/registry.ts +0 -1275
  31. package/src/compose/caddy-config.ts +0 -130
  32. package/src/compose/generator.ts +0 -287
  33. package/src/compose/index.ts +0 -7
  34. package/src/compose/templates.ts +0 -78
  35. package/src/compose/traefik-config.ts +0 -187
  36. package/src/config/bookmarks-generator.ts +0 -160
  37. package/src/config/defaults.ts +0 -37
  38. package/src/config/homepage-config.ts +0 -381
  39. package/src/config/index.ts +0 -6
  40. package/src/config/manager.ts +0 -150
  41. package/src/config/recyclarr-config.ts +0 -181
  42. package/src/config/schema.ts +0 -310
  43. package/src/config/slskd-config.ts +0 -86
  44. package/src/config/soularr-config.ts +0 -105
  45. package/src/config/trash-quality-definitions.ts +0 -213
  46. package/src/data/lidarr-custom-formats.ts +0 -127
  47. package/src/data/trash-profiles.ts +0 -333
  48. package/src/docker/client.ts +0 -312
  49. package/src/docker/index.ts +0 -18
  50. package/src/index.ts +0 -77
  51. package/src/setup/actions/arr-common.ts +0 -307
  52. package/src/setup/actions/dashboards.ts +0 -79
  53. package/src/setup/actions/download-clients.ts +0 -54
  54. package/src/setup/actions/jellyseerr.ts +0 -331
  55. package/src/setup/actions/media-servers.ts +0 -68
  56. package/src/setup/actions/monitoring.ts +0 -120
  57. package/src/setup/actions/prowlarr.ts +0 -137
  58. package/src/setup/actions/utilities.ts +0 -490
  59. package/src/setup/index.ts +0 -78
  60. package/src/setup/types.ts +0 -144
  61. package/src/structure/manager.ts +0 -129
  62. package/src/ui/App.ts +0 -111
  63. package/src/ui/components/ApplicationSelector.ts +0 -275
  64. package/src/ui/components/CredentialsForm.ts +0 -180
  65. package/src/ui/components/DownloadClientForm.ts +0 -114
  66. package/src/ui/components/FileEditor.ts +0 -91
  67. package/src/ui/components/FooterHint.ts +0 -119
  68. package/src/ui/components/PageLayout.ts +0 -160
  69. package/src/ui/components/UpdateNotification.ts +0 -102
  70. package/src/ui/index.ts +0 -7
  71. package/src/ui/screens/AdvancedSettings.ts +0 -188
  72. package/src/ui/screens/ApiKeyViewer.ts +0 -511
  73. package/src/ui/screens/AppConfigurator.ts +0 -543
  74. package/src/ui/screens/AppManager.ts +0 -284
  75. package/src/ui/screens/ContainerControl.ts +0 -581
  76. package/src/ui/screens/LogsViewer.ts +0 -478
  77. package/src/ui/screens/MainMenu.ts +0 -299
  78. package/src/ui/screens/MonitorDashboard.ts +0 -881
  79. package/src/ui/screens/SecretsEditor.ts +0 -216
  80. package/src/ui/screens/SettingsScreen.ts +0 -633
  81. package/src/ui/screens/index.ts +0 -22
  82. package/src/ui/screens/setup/BaseAppSetupScreen.ts +0 -375
  83. package/src/ui/screens/setup/CloudflaredSetup.ts +0 -460
  84. package/src/ui/screens/setup/FullAutoSetup.ts +0 -766
  85. package/src/ui/screens/setup/HomepageSetup.ts +0 -165
  86. package/src/ui/screens/setup/JellyfinSetup.ts +0 -328
  87. package/src/ui/screens/setup/JellyseerrSetup.ts +0 -242
  88. package/src/ui/screens/setup/ProwlarrSetup.ts +0 -500
  89. package/src/ui/screens/setup/QBittorrentSetup.ts +0 -221
  90. package/src/ui/screens/setup/QuickSetup.ts +0 -1169
  91. package/src/ui/screens/setup/RecyclarrSetup.ts +0 -270
  92. package/src/ui/screens/setup/TRaSHProfileSetup.ts +0 -381
  93. package/src/utils/arch.ts +0 -78
  94. package/src/utils/browser.ts +0 -27
  95. package/src/utils/categories.ts +0 -61
  96. package/src/utils/debug.ts +0 -84
  97. package/src/utils/env.ts +0 -138
  98. package/src/utils/index.ts +0 -8
  99. package/src/utils/logs.ts +0 -119
  100. package/src/utils/migrations/1765626338_rename_env_variables.ts +0 -102
  101. package/src/utils/migrations/1765707135_rename_easiarr_status.ts +0 -91
  102. package/src/utils/migrations/1765732722_remove_cloudflare_dns_api_token.ts +0 -45
  103. package/src/utils/migrations/1769515650_migrate_to_xdg.ts +0 -114
  104. package/src/utils/migrations.ts +0 -165
  105. package/src/utils/password.ts +0 -42
  106. package/src/utils/paths.ts +0 -33
  107. package/src/utils/unraid.ts +0 -103
  108. package/src/utils/update-checker.ts +0 -210
  109. package/src/utils/url-utils.ts +0 -38
@@ -1,581 +0,0 @@
1
- /**
2
- * Container Control Screen (Modernized)
3
- * Two-panel layout with container list and action panel
4
- * Supports individual and bulk container operations
5
- */
6
-
7
- import type { CliRenderer, KeyEvent, RenderContext } from "@opentui/core"
8
-
9
- import {
10
- BoxRenderable,
11
- SelectRenderable,
12
- SelectRenderableEvents,
13
- TabSelectRenderable,
14
- TabSelectRenderableEvents,
15
- TextRenderable,
16
- } from "@opentui/core"
17
-
18
- import type { EasiarrConfig } from "~/config/schema"
19
- import type { ContainerStatus } from "~/docker"
20
- import type { App } from "~/ui"
21
- import {
22
- composeDown,
23
- composeRestart,
24
- composeStop,
25
- composeUp,
26
- getContainerLogs,
27
- getContainerStatuses,
28
- pullImages,
29
- recreateService,
30
- restartContainer as restartSingleContainer,
31
- startContainer,
32
- stopContainer,
33
- } from "~/docker"
34
- import { createPageLayout } from "~/ui/components/PageLayout"
35
-
36
- type Mode = "containers" | "bulk"
37
-
38
- export class ContainerControl {
39
- private renderer: CliRenderer
40
- private container: BoxRenderable
41
- private app: App
42
- private config: EasiarrConfig
43
- private keyHandler: ((k: KeyEvent) => void) | null = null
44
-
45
- // UI Components
46
- private page!: BoxRenderable
47
- private modeTabs!: TabSelectRenderable
48
- private leftPanel!: BoxRenderable
49
- private rightPanel!: BoxRenderable
50
- private statusText!: TextRenderable
51
-
52
- // State
53
- private mode: Mode = "containers"
54
- private containers: ContainerStatus[] = []
55
- private selectedIndex = 0
56
- private isLoading = false
57
- private statusMessage = ""
58
-
59
- constructor(renderer: RenderContext, container: BoxRenderable, app: App, config: EasiarrConfig) {
60
- this.renderer = renderer as CliRenderer
61
- this.container = container
62
- this.app = app
63
- this.config = config
64
-
65
- this.init()
66
- }
67
-
68
- private async init(): Promise<void> {
69
- await this.loadContainers()
70
- this.buildUI()
71
- this.attachKeyHandler()
72
- }
73
-
74
- private async loadContainers(): Promise<void> {
75
- this.isLoading = true
76
- this.containers = await getContainerStatuses()
77
- this.isLoading = false
78
- }
79
-
80
- private buildUI(): void {
81
- const runningCount = this.containers.filter((s) => s.status === "running").length
82
- const totalCount = this.containers.length
83
- const dockerOk = this.containers.length > 0 || !this.isLoading
84
-
85
- const statusText = `${runningCount}/${totalCount} Running`
86
- const finalStepInfo = dockerOk ? statusText : "Docker Unavailable"
87
-
88
- const { container: page, content } = createPageLayout(this.renderer, {
89
- title: "Container Control",
90
- stepInfo: finalStepInfo,
91
- footerHint: [
92
- { type: "key", key: "Tab", value: "Switch Tab" },
93
- { type: "key", key: "↑↓", value: "Navigate" },
94
- { type: "key", key: "Enter", value: "Action" },
95
- { type: "separator" },
96
- { type: "key", key: "s", value: "Start" },
97
- { type: "key", key: "x", value: "Stop" },
98
- { type: "key", key: "r", value: "Restart" },
99
- { type: "key", key: "l", value: "Logs" },
100
- { type: "key", key: "u", value: "Update" },
101
- ],
102
- })
103
- this.page = page
104
-
105
- // Tab selector
106
- this.modeTabs = new TabSelectRenderable(this.renderer, {
107
- id: "mode-tabs",
108
- width: "100%",
109
- options: [
110
- { name: "📦 Containers", value: "containers", description: "" },
111
- { name: "⚡ Bulk Actions", value: "bulk", description: "" },
112
- ],
113
- tabWidth: 18,
114
- showUnderline: false,
115
- showDescription: false,
116
- selectedBackgroundColor: "#4a9eff",
117
- textColor: "#555555",
118
- })
119
-
120
- this.modeTabs.on(TabSelectRenderableEvents.ITEM_SELECTED, (index: number) => {
121
- this.mode = index === 0 ? "containers" : "bulk"
122
- this.updatePanels()
123
- })
124
-
125
- content.add(this.modeTabs)
126
- content.add(new TextRenderable(this.renderer, { content: " " }))
127
-
128
- // Two-panel layout
129
- const panelRow = new BoxRenderable(this.renderer, {
130
- width: "100%",
131
- flexGrow: 1,
132
- flexDirection: "row",
133
- })
134
-
135
- // Left panel - Container list or Bulk options
136
- this.leftPanel = new BoxRenderable(this.renderer, {
137
- width: "60%",
138
- height: "100%",
139
- flexDirection: "column",
140
- border: true,
141
- borderStyle: "single",
142
- borderColor: "#4a9eff",
143
- title: " Containers ",
144
- padding: 1,
145
- })
146
-
147
- // Right panel - Actions or Info
148
- this.rightPanel = new BoxRenderable(this.renderer, {
149
- width: "40%",
150
- height: "100%",
151
- flexDirection: "column",
152
- border: true,
153
- borderStyle: "single",
154
- borderColor: "#666666",
155
- title: " Actions ",
156
- padding: 1,
157
- })
158
-
159
- panelRow.add(this.leftPanel)
160
- panelRow.add(this.rightPanel)
161
- content.add(panelRow)
162
-
163
- // Status bar
164
- this.statusText = new TextRenderable(this.renderer, {
165
- id: "status-bar",
166
- content: "",
167
- fg: "#f1fa8c",
168
- })
169
- content.add(this.statusText)
170
-
171
- this.container.add(page)
172
- this.updatePanels()
173
- }
174
-
175
- private clearPanel(panel: BoxRenderable): void {
176
- const children = [...panel.getChildren()]
177
- for (const child of children) {
178
- if (child.id) panel.remove(child.id)
179
- }
180
- }
181
-
182
- private updatePanels(): void {
183
- if (this.mode === "containers") {
184
- this.renderContainerList()
185
- this.renderContainerActions()
186
- } else {
187
- this.renderBulkOptions()
188
- this.renderBulkInfo()
189
- }
190
- }
191
-
192
- private renderContainerList(): void {
193
- this.clearPanel(this.leftPanel)
194
- this.leftPanel.title = " Containers "
195
-
196
- if (this.containers.length === 0) {
197
- this.leftPanel.add(
198
- new TextRenderable(this.renderer, {
199
- id: "no-containers",
200
- content: "No containers found.\nRun 'Start All' in Bulk Actions tab.",
201
- fg: "#888888",
202
- })
203
- )
204
- return
205
- }
206
-
207
- // Render container list with selection highlight
208
- this.containers.forEach((container, idx) => {
209
- const isSelected = idx === this.selectedIndex
210
- const icon = container.status === "running" ? "🟢" : "🔴"
211
- const prefix = isSelected ? "▶ " : " "
212
-
213
- const row = new BoxRenderable(this.renderer, {
214
- id: `container-row-${idx}`,
215
- width: "100%",
216
- flexDirection: "row",
217
- backgroundColor: isSelected ? "#2a2a4a" : undefined,
218
- })
219
-
220
- row.add(
221
- new TextRenderable(this.renderer, {
222
- id: `container-${idx}`,
223
- content: `${prefix}${icon} ${container.name}`,
224
- fg: isSelected ? "#ffffff" : container.status === "running" ? "#50fa7b" : "#666666",
225
- })
226
- )
227
-
228
- // Show port if available
229
- if (container.ports) {
230
- row.add(
231
- new TextRenderable(this.renderer, {
232
- id: `port-${idx}`,
233
- content: ` (${container.ports.split(",")[0] || ""})`,
234
- fg: "#888888",
235
- })
236
- )
237
- }
238
-
239
- this.leftPanel.add(row)
240
- })
241
- }
242
-
243
- private renderContainerActions(): void {
244
- this.clearPanel(this.rightPanel)
245
- this.rightPanel.title = " Actions "
246
-
247
- const selected = this.containers[this.selectedIndex]
248
- if (!selected) {
249
- this.rightPanel.add(
250
- new TextRenderable(this.renderer, {
251
- id: "no-selection",
252
- content: "No container selected",
253
- fg: "#888888",
254
- })
255
- )
256
- return
257
- }
258
-
259
- // Container info header
260
- this.rightPanel.add(
261
- new TextRenderable(this.renderer, {
262
- id: "selected-name",
263
- content: `📦 ${selected.name}`,
264
- fg: "#4a9eff",
265
- })
266
- )
267
- this.rightPanel.add(
268
- new TextRenderable(this.renderer, {
269
- id: "selected-status",
270
- content: `Status: ${selected.status === "running" ? "🟢 Running" : "🔴 Stopped"}`,
271
- fg: selected.status === "running" ? "#50fa7b" : "#ff5555",
272
- })
273
- )
274
- this.rightPanel.add(new TextRenderable(this.renderer, { content: "" }))
275
-
276
- // Action buttons based on state
277
- const actions =
278
- selected.status === "running"
279
- ? [
280
- { key: "x", label: "Stop Container", action: "stop" },
281
- { key: "r", label: "Restart Container", action: "restart" },
282
- { key: "l", label: "View Logs", action: "logs" },
283
- { key: "u", label: "Update (Pull + Recreate)", action: "update" },
284
- ]
285
- : [
286
- { key: "s", label: "Start Container", action: "start" },
287
- { key: "u", label: "Update (Pull + Recreate)", action: "update" },
288
- ]
289
-
290
- this.rightPanel.add(
291
- new TextRenderable(this.renderer, {
292
- id: "actions-header",
293
- content: "Keyboard Shortcuts:",
294
- fg: "#888888",
295
- })
296
- )
297
-
298
- actions.forEach(({ key, label }, idx) => {
299
- this.rightPanel.add(
300
- new TextRenderable(this.renderer, {
301
- id: `action-${idx}`,
302
- content: ` [${key}] ${label}`,
303
- fg: "#f8f8f2",
304
- })
305
- )
306
- })
307
- }
308
-
309
- private renderBulkOptions(): void {
310
- this.clearPanel(this.leftPanel)
311
- this.leftPanel.title = " Bulk Actions "
312
-
313
- const menu = new SelectRenderable(this.renderer, {
314
- id: "bulk-menu",
315
- width: "100%",
316
- height: 10,
317
- options: [
318
- { name: "▶ Start All", description: "docker compose up -d" },
319
- { name: "⏹ Stop All", description: "docker compose stop" },
320
- { name: "🔄 Restart All", description: "docker compose restart" },
321
- { name: "⬇ Down (Remove)", description: "docker compose down" },
322
- { name: "📥 Pull Updates", description: "docker compose pull" },
323
- { name: "🔃 Refresh Status", description: "Update container list" },
324
- { name: "◀ Back to Main Menu", description: "" },
325
- ],
326
- })
327
-
328
- menu.on(SelectRenderableEvents.ITEM_SELECTED, async (index) => {
329
- await this.executeBulkAction(index)
330
- })
331
-
332
- this.leftPanel.add(menu)
333
-
334
- // Focus the menu when in bulk mode
335
- if (this.mode === "bulk") {
336
- setTimeout(() => menu.focus(), 10)
337
- }
338
- }
339
-
340
- private renderBulkInfo(): void {
341
- this.clearPanel(this.rightPanel)
342
- this.rightPanel.title = " Status "
343
-
344
- const runningCount = this.containers.filter((c) => c.status === "running").length
345
- const stoppedCount = this.containers.length - runningCount
346
-
347
- this.rightPanel.add(
348
- new TextRenderable(this.renderer, {
349
- id: "total-containers",
350
- content: `Total: ${this.containers.length} containers`,
351
- fg: "#f8f8f2",
352
- })
353
- )
354
- this.rightPanel.add(
355
- new TextRenderable(this.renderer, {
356
- id: "running-count",
357
- content: `🟢 Running: ${runningCount}`,
358
- fg: "#50fa7b",
359
- })
360
- )
361
- this.rightPanel.add(
362
- new TextRenderable(this.renderer, {
363
- id: "stopped-count",
364
- content: `🔴 Stopped: ${stoppedCount}`,
365
- fg: stoppedCount > 0 ? "#ff5555" : "#666666",
366
- })
367
- )
368
- this.rightPanel.add(new TextRenderable(this.renderer, { content: "" }))
369
-
370
- // List running containers
371
- if (runningCount > 0) {
372
- this.rightPanel.add(
373
- new TextRenderable(this.renderer, {
374
- id: "running-header",
375
- content: "Running:",
376
- fg: "#888888",
377
- })
378
- )
379
- this.containers
380
- .filter((c) => c.status === "running")
381
- .slice(0, 8)
382
- .forEach((c, idx) => {
383
- this.rightPanel.add(
384
- new TextRenderable(this.renderer, {
385
- id: `running-${idx}`,
386
- content: ` • ${c.name}`,
387
- fg: "#50fa7b",
388
- })
389
- )
390
- })
391
- }
392
- }
393
-
394
- private async executeBulkAction(index: number): Promise<void> {
395
- this.setStatus("Executing...", "#f1fa8c")
396
-
397
- switch (index) {
398
- case 0:
399
- this.setStatus("Starting all containers...", "#f1fa8c")
400
- await composeUp()
401
- this.setStatus("✓ All containers started", "#50fa7b")
402
- break
403
- case 1:
404
- this.setStatus("Stopping all containers...", "#f1fa8c")
405
- await composeStop()
406
- this.setStatus("✓ All containers stopped", "#50fa7b")
407
- break
408
- case 2:
409
- this.setStatus("Restarting all containers...", "#f1fa8c")
410
- await composeRestart()
411
- this.setStatus("✓ All containers restarted", "#50fa7b")
412
- break
413
- case 3:
414
- this.setStatus("Removing containers...", "#f1fa8c")
415
- await composeDown()
416
- this.setStatus("✓ Containers removed", "#50fa7b")
417
- break
418
- case 4:
419
- this.setStatus("Pulling latest images...", "#f1fa8c")
420
- await pullImages()
421
- this.setStatus("✓ Images updated", "#50fa7b")
422
- break
423
- case 5:
424
- this.setStatus("Refreshing...", "#f1fa8c")
425
- break
426
- case 6:
427
- this.cleanup()
428
- this.app.navigateTo("main")
429
- return
430
- }
431
-
432
- await this.loadContainers()
433
- this.updatePanels()
434
- }
435
-
436
- private async executeContainerAction(action: string): Promise<void> {
437
- const selected = this.containers[this.selectedIndex]
438
- if (!selected) return
439
-
440
- const serviceName = selected.name
441
-
442
- switch (action) {
443
- case "start":
444
- this.setStatus(`Starting ${serviceName}...`, "#f1fa8c")
445
- await startContainer(serviceName)
446
- this.setStatus(`✓ ${serviceName} started`, "#50fa7b")
447
- break
448
- case "stop":
449
- this.setStatus(`Stopping ${serviceName}...`, "#f1fa8c")
450
- await stopContainer(serviceName)
451
- this.setStatus(`✓ ${serviceName} stopped`, "#50fa7b")
452
- break
453
- case "restart":
454
- this.setStatus(`Restarting ${serviceName}...`, "#f1fa8c")
455
- await restartSingleContainer(serviceName)
456
- this.setStatus(`✓ ${serviceName} restarted`, "#50fa7b")
457
- break
458
- case "update":
459
- this.setStatus(`Updating ${serviceName}...`, "#f1fa8c")
460
- await recreateService(serviceName)
461
- this.setStatus(`✓ ${serviceName} updated`, "#50fa7b")
462
- break
463
- case "logs":
464
- await this.showLogs(serviceName)
465
- return // Don't refresh after logs
466
- }
467
-
468
- await this.loadContainers()
469
- this.updatePanels()
470
- }
471
-
472
- private async showLogs(serviceName: string): Promise<void> {
473
- this.setStatus(`Fetching logs for ${serviceName}...`, "#f1fa8c")
474
- const result = await getContainerLogs(serviceName, 25)
475
-
476
- this.clearPanel(this.rightPanel)
477
- this.rightPanel.title = ` Logs: ${serviceName} `
478
-
479
- if (!result.success) {
480
- this.rightPanel.add(
481
- new TextRenderable(this.renderer, {
482
- id: "logs-error",
483
- content: "Failed to fetch logs",
484
- fg: "#ff5555",
485
- })
486
- )
487
- return
488
- }
489
-
490
- // Display logs (truncated for UI)
491
- const logLines = result.output.split("\n").slice(-20)
492
- logLines.forEach((line, idx) => {
493
- this.rightPanel.add(
494
- new TextRenderable(this.renderer, {
495
- id: `log-${idx}`,
496
- content: line.substring(0, 60),
497
- fg: "#888888",
498
- })
499
- )
500
- })
501
-
502
- this.setStatus("Press any key to close logs", "#4a9eff")
503
- }
504
-
505
- private setStatus(message: string, color: string): void {
506
- this.statusMessage = message
507
- if (this.statusText) {
508
- this.statusText.content = message
509
- this.statusText.fg = color
510
- }
511
- }
512
-
513
- private attachKeyHandler(): void {
514
- this.keyHandler = (k: KeyEvent) => {
515
- if (this.mode === "containers") {
516
- this.handleContainerModeKey(k)
517
- } else {
518
- this.handleBulkModeKey(k)
519
- }
520
- }
521
- this.renderer.keyInput.on("keypress", this.keyHandler)
522
- }
523
-
524
- private handleContainerModeKey(k: KeyEvent): void {
525
- const maxIndex = this.containers.length - 1
526
-
527
- switch (k.name) {
528
- case "up":
529
- this.selectedIndex = Math.max(0, this.selectedIndex - 1)
530
- this.updatePanels()
531
- break
532
- case "down":
533
- this.selectedIndex = Math.min(maxIndex, this.selectedIndex + 1)
534
- this.updatePanels()
535
- break
536
- case "tab":
537
- this.mode = "bulk"
538
- this.modeTabs.setSelectedIndex(1)
539
- this.updatePanels()
540
- break
541
- case "s":
542
- this.executeContainerAction("start")
543
- break
544
- case "x":
545
- this.executeContainerAction("stop")
546
- break
547
- case "r":
548
- this.executeContainerAction("restart")
549
- break
550
- case "l":
551
- this.executeContainerAction("logs")
552
- break
553
- case "u":
554
- this.executeContainerAction("update")
555
- break
556
- case "q":
557
- case "escape":
558
- this.cleanup()
559
- this.app.navigateTo("main")
560
- break
561
- }
562
- }
563
-
564
- private handleBulkModeKey(k: KeyEvent): void {
565
- if (k.name === "tab") {
566
- this.mode = "containers"
567
- this.modeTabs.setSelectedIndex(0)
568
- this.updatePanels()
569
- } else if (k.name === "q" || k.name === "escape") {
570
- this.cleanup()
571
- this.app.navigateTo("main")
572
- }
573
- }
574
-
575
- private cleanup(): void {
576
- if (this.keyHandler) {
577
- this.renderer.keyInput.off("keypress", this.keyHandler)
578
- this.keyHandler = null
579
- }
580
- }
581
- }