main 5041353

clipboard.go RawHistoryFolder

1package handlers
2
3import (
4	"context"
5	"encoding/json"
6
7	"github.com/bnema/dumber/internal/application/dto"
8	"github.com/bnema/dumber/internal/application/port"
9	"github.com/bnema/dumber/internal/logging"
10)
11
12// ClipboardHandler handles clipboard-related messages from webviews.
13type ClipboardHandler struct {
14	orchestrator port.ClipboardTextOrchestrator
15}
16
17// NewClipboardHandler creates a new ClipboardHandler.
18func NewClipboardHandler(orchestrator port.ClipboardTextOrchestrator) *ClipboardHandler {
19	return &ClipboardHandler{
20		orchestrator: orchestrator,
21	}
22}
23
24// autoCopyRequest represents the payload for auto-copy selection messages.
25type autoCopyRequest struct {
26	Text string `json:"text"`
27}
28
29type explicitCopyRequest struct {
30	Text   string `json:"text"`
31	Action string `json:"action"`
32}
33
34// HandleAutoCopySelection handles the auto_copy_selection message from JS.
35// It copies the selected text to the clipboard if the feature is enabled.
36func (h *ClipboardHandler) HandleAutoCopySelection() port.WebUIMessageHandler {
37	return port.WebUIMessageHandlerFunc(func(ctx context.Context, webviewID port.WebViewID, payload json.RawMessage) (any, error) {
38		log := logging.FromContext(ctx)
39
40		var req autoCopyRequest
41		if err := json.Unmarshal(payload, &req); err != nil {
42			log.Debug().Err(err).Msg("failed to unmarshal auto-copy payload")
43			return nil, nil // Silently ignore malformed requests
44		}
45		if h.orchestrator == nil {
46			return nil, nil
47		}
48		if err := h.orchestrator.HandleSelectionUpdate(ctx, dto.SelectionClipboardInput{
49			Text:         req.Text,
50			SourceEngine: dto.ClipboardSourceWebKit,
51			ViewID:       uint64(webviewID),
52		}); err != nil {
53			log.Debug().Err(err).Msg("clipboard selection handling failed")
54		}
55
56		return nil, nil
57	})
58}
59
60// HandleExplicitCopy handles the explicit_text_copy message from JS.
61func (h *ClipboardHandler) HandleExplicitCopy() port.WebUIMessageHandler {
62	return port.WebUIMessageHandlerFunc(func(ctx context.Context, webviewID port.WebViewID, payload json.RawMessage) (any, error) {
63		log := logging.FromContext(ctx)
64
65		var req explicitCopyRequest
66		if err := json.Unmarshal(payload, &req); err != nil {
67			log.Debug().Err(err).Msg("failed to unmarshal explicit copy payload")
68			return nil, nil
69		}
70		if h.orchestrator == nil {
71			return nil, nil
72		}
73		if err := h.orchestrator.HandleExplicitCopy(ctx, dto.ExplicitClipboardInput{
74			Text:          req.Text,
75			Action:        req.Action,
76			SourceEngine:  dto.ClipboardSourceWebKit,
77			ViewID:        uint64(webviewID),
78			NativeHandled: true,
79		}); err != nil {
80			log.Debug().Err(err).Msg("clipboard explicit handling failed")
81		}
82
83		return nil, nil
84	})
85}
86
87// RegisterClipboardHandlers registers clipboard handlers with the router.
88func RegisterClipboardHandlers(
89	ctx context.Context,
90	router port.WebUIHandlerRouter,
91	orchestrator port.ClipboardTextOrchestrator,
92) error {
93	handler := NewClipboardHandler(orchestrator)
94
95	// Register auto_copy_selection handler
96	if err := router.RegisterHandler("auto_copy_selection", handler.HandleAutoCopySelection()); err != nil {
97		return err
98	}
99	if err := router.RegisterHandler("explicit_text_copy", handler.HandleExplicitCopy()); err != nil {
100		return err
101	}
102
103	log := logging.FromContext(ctx)
104	log.Info().Msg("registered clipboard handlers")
105
106	return nil
107}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close