diff --git a/docs/keybindings/Keybindings_de.md b/docs/keybindings/Keybindings_de.md index da130058..7b680e86 100644 --- a/docs/keybindings/Keybindings_de.md +++ b/docs/keybindings/Keybindings_de.md @@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct ## Container
-  : copy container id
+  y: copy container ID
   d: entfernen
   e: hide/show stopped containers
   p: pause
diff --git a/docs/keybindings/Keybindings_en.md b/docs/keybindings/Keybindings_en.md
index 09679c2d..1179da36 100644
--- a/docs/keybindings/Keybindings_en.md
+++ b/docs/keybindings/Keybindings_en.md
@@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
 ## Containers
 
 
-  : copy container id
+  y: copy container ID
   d: remove
   e: hide/show stopped containers
   p: pause
diff --git a/docs/keybindings/Keybindings_es.md b/docs/keybindings/Keybindings_es.md
index 02065ea0..4c3e0138 100644
--- a/docs/keybindings/Keybindings_es.md
+++ b/docs/keybindings/Keybindings_es.md
@@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
 ## Contenedores
 
 
-  : copy container id
+  y: copy container ID
   d: borrar
   e: esconder/mostrar contenedores parados
   p: pausa
diff --git a/docs/keybindings/Keybindings_fr.md b/docs/keybindings/Keybindings_fr.md
index c69006ce..c33a9275 100644
--- a/docs/keybindings/Keybindings_fr.md
+++ b/docs/keybindings/Keybindings_fr.md
@@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
 ## Conteneurs
 
 
-  : copy container id
+  y: copy container ID
   d: supprimer
   e: cacher/montrer les conteneurs arrêtés
   p: pause
diff --git a/docs/keybindings/Keybindings_nl.md b/docs/keybindings/Keybindings_nl.md
index d17b91d3..f8963d88 100644
--- a/docs/keybindings/Keybindings_nl.md
+++ b/docs/keybindings/Keybindings_nl.md
@@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
 ## Containers
 
 
-  : copy container id
+  y: copy container ID
   d: verwijder
   e: verberg gestopte containers
   p: pause
diff --git a/docs/keybindings/Keybindings_pl.md b/docs/keybindings/Keybindings_pl.md
index 974c52aa..533d8691 100644
--- a/docs/keybindings/Keybindings_pl.md
+++ b/docs/keybindings/Keybindings_pl.md
@@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
 ## Kontenery
 
 
-  : copy container id
+  y: copy container ID
   d: usuń
   e: hide/show stopped containers
   p: pause
diff --git a/docs/keybindings/Keybindings_pt.md b/docs/keybindings/Keybindings_pt.md
index 079be7cd..76c73583 100644
--- a/docs/keybindings/Keybindings_pt.md
+++ b/docs/keybindings/Keybindings_pt.md
@@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
 ## Contêineres
 
 
-  : copy container id
+  y: copy container ID
   d: remover
   e: ocultar/mostrar contêineres parados
   p: pausar
diff --git a/docs/keybindings/Keybindings_tr.md b/docs/keybindings/Keybindings_tr.md
index e9f5e1e2..9c012d48 100644
--- a/docs/keybindings/Keybindings_tr.md
+++ b/docs/keybindings/Keybindings_tr.md
@@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
 ## Konteynerler
 
 
-  : copy container id
+  y: copy container ID
   d: kaldır
   e: hide/show stopped containers
   p: pause
diff --git a/docs/keybindings/Keybindings_zh.md b/docs/keybindings/Keybindings_zh.md
index 1c3553c0..57af67a8 100644
--- a/docs/keybindings/Keybindings_zh.md
+++ b/docs/keybindings/Keybindings_zh.md
@@ -16,7 +16,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
 ## 容器
 
 
-  : copy container id
+  y: copy container ID
   d: 移除
   e: 隐藏/显示已停止的容器
   p: 暂停
diff --git a/pkg/commands/os.go b/pkg/commands/os.go
index 5128b1bf..620de164 100644
--- a/pkg/commands/os.go
+++ b/pkg/commands/os.go
@@ -381,5 +381,13 @@ func (c *OSCommand) CopyToClipboard(str string) error {
 
 	c.Log.Debug(utils.ResolvePlaceholderString("Copying '{{str}}' to clipboard", map[string]string{"str": truncated}))
 
+	// Not needed yet
+	//if c.UserConfig().OS.CopyToClipboardCmd != "" {
+	//	cmdStr := utils.ResolvePlaceholderString(c.UserConfig().OS.CopyToClipboardCmd, map[string]string{
+	//		"text": c.Cmd.Quote(str),
+	//	})
+	//	return c.Cmd.NewShell(cmdStr).Run()
+	//}
+
 	return clipboard.WriteAll(str)
 }
diff --git a/pkg/gui/app_status_manager.go b/pkg/gui/app_status_manager.go
index f58b6915..e6e4ec09 100644
--- a/pkg/gui/app_status_manager.go
+++ b/pkg/gui/app_status_manager.go
@@ -1,7 +1,6 @@
 package gui
 
 import (
-	"sync"
 	"time"
 
 	"github.com/jesseduffield/gocui"
@@ -16,15 +15,14 @@ type appStatus struct {
 
 type statusManager struct {
 	statuses []appStatus
-	lock     *sync.Mutex
 }
 
+const (
+	TickIntervalMs = 50
+)
+
 func (m *statusManager) removeStatus(name string) {
 	newStatuses := []appStatus{}
-
-	m.lock.Lock()
-	defer m.lock.Unlock()
-
 	for _, status := range m.statuses {
 		if status.name != name {
 			newStatuses = append(newStatuses, status)
@@ -33,61 +31,40 @@ func (m *statusManager) removeStatus(name string) {
 	m.statuses = newStatuses
 }
 
-func (m *statusManager) addWaitingStatus(name string) {
-	m.lock.Lock()
-	defer m.lock.Unlock()
-
+func (m *statusManager) addStatus(name string, statusType string, duration int) {
 	m.removeStatus(name)
 	newStatus := appStatus{
 		name:       name,
-		statusType: "waiting",
-		duration:   0,
+		statusType: statusType,
+		duration:   duration,
 	}
 	m.statuses = append([]appStatus{newStatus}, m.statuses...)
 }
 
 func (m *statusManager) getStatusString() string {
-	m.lock.Lock()
-	defer m.lock.Unlock()
-
 	if len(m.statuses) == 0 {
 		return ""
 	}
+
 	topStatus := m.statuses[0]
 	if topStatus.statusType == "waiting" {
 		return topStatus.name + " " + utils.Loader()
+	} else if topStatus.statusType == "info" {
+		return topStatus.name
 	}
-	return topStatus.name
-}
 
-// WithStaticWaitingStatus shows a waiting status for a specific duration
-func (gui *Gui) WithStaticWaitingStatus(name string, duration time.Duration) error {
-	return gui.WithWaitingStatus(name, func() error { time.Sleep(duration); return nil })
+	return topStatus.name
 }
 
 // WithWaitingStatus wraps a function and shows a waiting status while the function is still executing
 func (gui *Gui) WithWaitingStatus(name string, f func() error) error {
 	go func() {
-		gui.statusManager.addWaitingStatus(name)
+		go gui.Notify(name, "waiting", 0)()
 
 		defer func() {
 			gui.statusManager.removeStatus(name)
 		}()
 
-		go func() {
-			ticker := time.NewTicker(time.Millisecond * 50)
-			defer ticker.Stop()
-			for range ticker.C {
-				appStatus := gui.statusManager.getStatusString()
-				if appStatus == "" {
-					return
-				}
-				if err := gui.renderString(gui.g, "appStatus", appStatus); err != nil {
-					gui.Log.Warn(err)
-				}
-			}
-		}()
-
 		if err := f(); err != nil {
 			gui.g.Update(func(g *gocui.Gui) error {
 				return gui.createErrorPanel(err.Error())
@@ -97,3 +74,37 @@ func (gui *Gui) WithWaitingStatus(name string, f func() error) error {
 
 	return nil
 }
+
+// Notify sends static notification to the user.
+// duration of 0 will disable the self-cleaning of the notification
+func (gui *Gui) Notify(name string, statusType string, duration int) func() {
+	return func() {
+		gui.statusManager.addStatus(name, statusType, duration)
+
+		defer func() {
+			gui.statusManager.removeStatus(name)
+		}()
+
+		ticker := time.NewTicker(time.Millisecond * TickIntervalMs)
+		tickCount := 0
+		endTick := duration * 1000 / TickIntervalMs
+
+		defer ticker.Stop()
+		for range ticker.C {
+			tickCount++
+			// If no duration, don't terminate early
+			if duration > 0 && tickCount >= endTick {
+				return
+			}
+
+			appStatus := gui.statusManager.getStatusString()
+			if appStatus == "" {
+				return
+			}
+
+			if err := gui.renderString(gui.g, "appStatus", appStatus); err != nil {
+				gui.Log.Warn(err)
+			}
+		}
+	}
+}
diff --git a/pkg/gui/containers_panel.go b/pkg/gui/containers_panel.go
index f8dc9b08..6006c467 100644
--- a/pkg/gui/containers_panel.go
+++ b/pkg/gui/containers_panel.go
@@ -252,8 +252,10 @@ func (gui *Gui) refreshContainersAndServices() error {
 	}
 
 	// keep track of current service selected so that we can reposition our cursor if it moves position in the list
+	gui.DockerCommand.ServiceMutex.Lock()
 	originalSelectedLineIdx := gui.Panels.Services.SelectedIdx
 	selectedService, isServiceSelected := gui.Panels.Services.List.TryGet(originalSelectedLineIdx)
+	gui.DockerCommand.ServiceMutex.Unlock()
 
 	containers, services, err := gui.DockerCommand.RefreshContainersAndServices(
 		gui.Panels.Services.List.GetAllItems(),
@@ -364,10 +366,8 @@ func (gui *Gui) handleCopyContainerId(g *gocui.Gui, v *gocui.View) error {
 		return nil
 	}
 
-	err = gui.WithStaticWaitingStatus(fmt.Sprintf(gui.Tr.CopyContainerIdStatus, utils.TruncateWithEllipsis(ctr.ID, 10)), time.Second*2)
-	if err != nil {
-		return err
-	}
+	formattedStatusText := fmt.Sprintf("Copied %s to clipboard", utils.TruncateWithEllipsis(ctr.ID, 10))
+	go gui.Notify(formattedStatusText, "info", 3)()
 
 	return gui.OSCommand.CopyToClipboard(ctr.ID)
 }
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index 8bfebf76..fa6199f3 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -4,7 +4,6 @@ import (
 	"context"
 	"os"
 	"strings"
-	"sync"
 	"time"
 
 	"github.com/docker/docker/api/types/events"
@@ -147,11 +146,9 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
 		State:         initialState,
 		Config:        config,
 		Tr:            tr,
-		statusManager: &statusManager{
-			lock: &sync.Mutex{},
-		},
-		taskManager: tasks.NewTaskManager(log, tr),
-		ErrorChan:   errorChan,
+		statusManager: &statusManager{},
+		taskManager:   tasks.NewTaskManager(log, tr),
+		ErrorChan:     errorChan,
 	}
 
 	deadlock.Opts.Disable = !gui.Config.Debug
diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go
index 56c37845..fda0de25 100644
--- a/pkg/gui/keybindings.go
+++ b/pkg/gui/keybindings.go
@@ -187,7 +187,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
 		},
 		{
 			ViewName:    "containers",
-			Key:         gocui.KeyCtrlO,
+			Key:         'y',
 			Modifier:    gocui.ModNone,
 			Handler:     gui.handleCopyContainerId,
 			Description: gui.Tr.CopyContainerId,
diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go
index 88828752..36ac5e68 100644
--- a/pkg/i18n/english.go
+++ b/pkg/i18n/english.go
@@ -67,8 +67,6 @@ type TranslationSet struct {
 	ViewLogs                    string
 	UpProject                   string
 	DownProject                 string
-	CopyContainerId             string
-	CopyContainerIdStatus       string
 	ServicesTitle               string
 	ContainersTitle             string
 	StandaloneContainersTitle   string
@@ -111,6 +109,7 @@ type TranslationSet struct {
 	FilterList                  string
 	OpenInBrowser               string
 	SortContainersByState       string
+	CopyContainerId             string
 
 	LogsTitle                 string
 	ConfigTitle               string
@@ -199,8 +198,6 @@ func englishSet() TranslationSet {
 		ViewLogs:                    "view logs",
 		UpProject:                   "up project",
 		DownProject:                 "down project",
-		CopyContainerId:             "copy container id",
-		CopyContainerIdStatus:       "Copied %s to clipboard",
 		RemoveImage:                 "remove image",
 		RemoveVolume:                "remove volume",
 		RemoveNetwork:               "remove network",
@@ -220,6 +217,7 @@ func englishSet() TranslationSet {
 		FilterList:                  "filter list",
 		OpenInBrowser:               "open in browser (first port is http)",
 		SortContainersByState:       "sort containers by state",
+		CopyContainerId:             "copy container ID",
 
 		GlobalTitle:               "Global",
 		MainTitle:                 "Main",