execute_context_menu_action.go RawHistoryFolder

1package usecase
2
3import (
4	"context"
5	"fmt"
6
7	"github.com/bnema/dumber/internal/application/port"
8	"github.com/bnema/dumber/internal/domain/entity"
9)
10
11// ExecuteContextMenuActionInput identifies the action and its context.
12type ExecuteContextMenuActionInput struct {
13	Action  port.MenuAction
14	Context port.MenuContext
15}
16
17// ExecuteContextMenuActionUseCase handles shared context menu actions.
18type ExecuteContextMenuActionUseCase struct {
19	clipboard port.Clipboard
20	resolver  port.ImageDataResolver
21	saver     port.ResolvedImageSaver
22	delegator port.MenuActionDelegator
23}
24
25var _ port.ContextMenuActionExecutor = (*ExecuteContextMenuActionUseCase)(nil)
26
27// NewExecuteContextMenuActionUseCase creates a new ExecuteContextMenuActionUseCase.
28func NewExecuteContextMenuActionUseCase(
29	clipboard port.Clipboard,
30	resolver port.ImageDataResolver,
31	saver port.ResolvedImageSaver,
32	delegator port.MenuActionDelegator,
33) *ExecuteContextMenuActionUseCase {
34	return &ExecuteContextMenuActionUseCase{
35		clipboard: clipboard,
36		resolver:  resolver,
37		saver:     saver,
38		delegator: delegator,
39	}
40}
41
42// ContextMenuActionExecutorFactory creates ExecuteContextMenuActionUseCases.
43type ContextMenuActionExecutorFactory struct{}
44
45var _ port.ContextMenuActionExecutorFactory = (*ContextMenuActionExecutorFactory)(nil)
46
47// NewContextMenuActionExecutor creates a new shared context menu action executor.
48func (*ContextMenuActionExecutorFactory) NewContextMenuActionExecutor(
49	clipboard port.Clipboard,
50	resolver port.ImageDataResolver,
51	saver port.ResolvedImageSaver,
52	delegator port.MenuActionDelegator,
53) port.ContextMenuActionExecutor {
54	return NewExecuteContextMenuActionUseCase(clipboard, resolver, saver, delegator)
55}
56
57// Execute handles the requested action.
58func (uc *ExecuteContextMenuActionUseCase) Execute(ctx context.Context, input ExecuteContextMenuActionInput) error {
59	return uc.ExecuteMenuAction(ctx, input.Action, input.Context)
60}
61
62// ExecuteMenuAction handles the requested action.
63func (uc *ExecuteContextMenuActionUseCase) ExecuteMenuAction(
64	ctx context.Context, action port.MenuAction, menuContext port.MenuContext,
65) error {
66	return uc.executeMenuAction(ctx, action, menuContext)
67}
68
69func (uc *ExecuteContextMenuActionUseCase) executeMenuAction(
70	ctx context.Context, action port.MenuAction, menuContext port.MenuContext,
71) error {
72	switch action {
73	case port.MenuActionCopyLink:
74		if menuContext.LinkURI == "" {
75			return fmt.Errorf("copy link: link URI not available")
76		}
77		if uc.clipboard == nil {
78			return fmt.Errorf("copy link: clipboard not available")
79		}
80		if err := uc.clipboard.WriteText(ctx, menuContext.LinkURI); err != nil {
81			return fmt.Errorf("copy link: %w", err)
82		}
83		return nil
84	case port.MenuActionCopyImage:
85		if uc.clipboard == nil {
86			return fmt.Errorf("copy image: clipboard not available")
87		}
88		image, err := uc.resolveImageData(ctx, menuContext.ImageURI)
89		if err != nil {
90			return fmt.Errorf("copy image: %w", err)
91		}
92		if err := uc.clipboard.WriteImage(ctx, image); err != nil {
93			return fmt.Errorf("copy image: %w", err)
94		}
95		return nil
96	case port.MenuActionSaveImage:
97		if uc.saver == nil {
98			return fmt.Errorf("save image: image saver not available")
99		}
100		image, err := uc.resolveImageData(ctx, menuContext.ImageURI)
101		if err != nil {
102			return fmt.Errorf("save image: %w", err)
103		}
104		if err := uc.saver.SaveResolvedImage(ctx, image, menuContext); err != nil {
105			return fmt.Errorf("save image: %w", err)
106		}
107		return nil
108	case port.MenuActionCopySelection:
109		// When selection text is present and clipboard wiring is available, copy it here.
110		// Otherwise intentionally fall through to DelegateMenuAction so native browser copy
111		// behavior can handle document selections that are only accessible inside the engine.
112		if menuContext.SelectionText != "" && uc.clipboard != nil {
113			if err := uc.clipboard.WriteText(ctx, menuContext.SelectionText); err != nil {
114				return fmt.Errorf("copy selection: %w", err)
115			}
116			return nil
117		}
118	}
119
120	// All other normalized actions are delegated to the engine/UI layer.
121	if uc.delegator == nil {
122		return fmt.Errorf("delegate action %s: menu action delegator not available", action)
123	}
124	if err := uc.delegator.DelegateMenuAction(ctx, action, menuContext); err != nil {
125		return fmt.Errorf("delegate action %s: %w", action, err)
126	}
127	return nil
128}
129
130func (uc *ExecuteContextMenuActionUseCase) resolveImageData(ctx context.Context, uri string) (entity.ImageData, error) {
131	if uri == "" {
132		return entity.ImageData{}, fmt.Errorf("image URI not available")
133	}
134	if uc.resolver == nil {
135		return entity.ImageData{}, fmt.Errorf("image data resolver not available")
136	}
137
138	image, err := uc.resolver.ResolveImageData(ctx, uri)
139	if err != nil {
140		return entity.ImageData{}, err
141	}
142	if len(image.Bytes) == 0 {
143		return entity.ImageData{}, fmt.Errorf("image data not available")
144	}
145
146	return image, nil
147}

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