Skip to content

Commit 38dbdd9

Browse files
authored
fix: improve resize callback handling for better UX (#132)
1 parent 808ca14 commit 38dbdd9

3 files changed

Lines changed: 67 additions & 5 deletions

File tree

cmd/neru/modes.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func (a *App) activateHintMode(action Action) {
7777
// Always resize overlay to the active screen (where mouse is) before collecting elements
7878
// This ensures proper positioning when switching between multiple displays
7979
if a.hintOverlay != nil {
80-
// Use synchronous resize with callback - no artificial delay needed
80+
// Use synchronous resize with timeout to prevent hanging
8181
a.hintOverlay.ResizeToActiveScreenSync()
8282
a.hintOverlayNeedsRefresh = false
8383
}
@@ -172,7 +172,7 @@ func (a *App) activateGridMode(action Action) {
172172
if a.gridCtx != nil && a.gridCtx.gridOverlay != nil {
173173
gridOverlay := *a.gridCtx.gridOverlay
174174

175-
// Use synchronous resize with callback - no artificial delay needed
175+
// Use synchronous resize with timeout to prevent hanging
176176
gridOverlay.ResizeToActiveScreenSync()
177177
a.gridOverlayNeedsRefresh = false
178178
}
@@ -925,7 +925,7 @@ func (a *App) exitMode() {
925925
a.hintOverlay.Hide()
926926
case ModeGrid:
927927
// If we are in mouse up action, remove the mouse up to prevent further dragging
928-
if a.currentAction == ActionMouseUp {
928+
if a.gridCtx.currentAction == ActionMouseUp {
929929
a.logger.Info("Detected MouseUp action, removing mouse up event...")
930930
err := accessibility.LeftMouseUp()
931931
if err != nil {
@@ -943,6 +943,7 @@ func (a *App) exitMode() {
943943
}
944944
// Hide overlays
945945
if a.gridCtx != nil && a.gridCtx.gridOverlay != nil && *a.gridCtx.gridOverlay != nil {
946+
a.logger.Info("Hiding grid overlay")
946947
(*a.gridCtx.gridOverlay).Hide()
947948
}
948949
// Also clear any context menu drawn on hint overlay
@@ -959,6 +960,7 @@ func (a *App) exitMode() {
959960
// Update mode after all cleanup is done
960961
a.currentMode = ModeIdle
961962
a.currentAction = ActionLeftClick
963+
a.gridCtx.currentAction = ActionLeftClick
962964
a.logger.Debug("Mode transition complete",
963965
zap.String("to", "idle"))
964966

internal/grid/grid_overlay.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"strings"
1515
"sync"
1616
"sync/atomic"
17+
"time"
1718
"unsafe"
1819

1920
"github.com/y3owk1n/neru/internal/config"
@@ -121,6 +122,10 @@ func (o *GridOverlay) ResizeToActiveScreenSync() {
121122
gridCallbackMap[id] = done
122123
gridCallbackLock.Unlock()
123124

125+
if o.logger != nil {
126+
o.logger.Debug("Grid overlay resize started", zap.Uint64("callback_id", id))
127+
}
128+
124129
// Pass ID as context (safe - no Go pointers)
125130
// Note: uintptr conversion must happen in same expression to satisfy go vet
126131
C.resizeOverlayToActiveScreenWithCallback(
@@ -129,7 +134,32 @@ func (o *GridOverlay) ResizeToActiveScreenSync() {
129134
*(*unsafe.Pointer)(unsafe.Pointer(&id)),
130135
)
131136

132-
<-done
137+
// Don't wait for callback - continue immediately for better UX
138+
// The resize operation is typically fast and visually complete before callback
139+
// Start a goroutine to handle cleanup when callback eventually arrives
140+
go func() {
141+
if o.logger != nil {
142+
o.logger.Debug("Grid overlay resize background cleanup started", zap.Uint64("callback_id", id))
143+
}
144+
145+
select {
146+
case <-done:
147+
// Callback received, normal cleanup already handled in callback
148+
if o.logger != nil {
149+
o.logger.Debug("Grid overlay resize callback received", zap.Uint64("callback_id", id))
150+
}
151+
case <-time.After(2 * time.Second):
152+
// Long timeout for cleanup only - callback likely failed
153+
gridCallbackLock.Lock()
154+
delete(gridCallbackMap, id)
155+
gridCallbackLock.Unlock()
156+
157+
if o.logger != nil {
158+
o.logger.Debug("Grid overlay resize cleanup timeout - removed callback from map",
159+
zap.Uint64("callback_id", id))
160+
}
161+
}
162+
}()
133163
}
134164

135165
// Draw renders the flat grid with all 3-char cells visible

internal/hints/overlay.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"strings"
1616
"sync"
1717
"sync/atomic"
18+
"time"
1819
"unsafe"
1920

2021
"github.com/y3owk1n/neru/internal/config"
@@ -112,6 +113,10 @@ func (o *Overlay) ResizeToActiveScreenSync() {
112113
hintCallbackMap[id] = done
113114
hintCallbackLock.Unlock()
114115

116+
if o.logger != nil {
117+
o.logger.Debug("Hint overlay resize started", zap.Uint64("callback_id", id))
118+
}
119+
115120
// Pass ID as context (safe - no Go pointers)
116121
// Note: uintptr conversion must happen in same expression to satisfy go vet
117122
C.resizeOverlayToActiveScreenWithCallback(
@@ -120,7 +125,32 @@ func (o *Overlay) ResizeToActiveScreenSync() {
120125
*(*unsafe.Pointer)(unsafe.Pointer(&id)),
121126
)
122127

123-
<-done
128+
// Don't wait for callback - continue immediately for better UX
129+
// The resize operation is typically fast and visually complete before callback
130+
// Start a goroutine to handle cleanup when callback eventually arrives
131+
go func() {
132+
if o.logger != nil {
133+
o.logger.Debug("Hint overlay resize background cleanup started", zap.Uint64("callback_id", id))
134+
}
135+
136+
select {
137+
case <-done:
138+
// Callback received, normal cleanup already handled in callback
139+
if o.logger != nil {
140+
o.logger.Debug("Hint overlay resize callback received", zap.Uint64("callback_id", id))
141+
}
142+
case <-time.After(2 * time.Second):
143+
// Long timeout for cleanup only - callback likely failed
144+
hintCallbackLock.Lock()
145+
delete(hintCallbackMap, id)
146+
hintCallbackLock.Unlock()
147+
148+
if o.logger != nil {
149+
o.logger.Debug("Hint overlay resize cleanup timeout - removed callback from map",
150+
zap.Uint64("callback_id", id))
151+
}
152+
}
153+
}()
124154
}
125155

126156
// DrawHintsWithoutArrow draws hints without arrows

0 commit comments

Comments
 (0)