1package screens
2
3import (
4 "charm.land/bubbles/v2/key"
5 tea "charm.land/bubbletea/v2"
6
7 "git.secluded.site/keld/internal/ui"
8)
9
10// PreviewFunc returns the formatted command preview text. It is
11// called asynchronously from Init via a tea.Cmd. The returned string
12// should match the format of [restic.DryRun].
13type PreviewFunc func() string
14
15// previewMsg carries the result of the async preview generation.
16type previewMsg struct {
17 text string
18}
19
20// Confirm is a Screen adapter that shows the resolved restic command
21// and waits for the user to confirm execution. Enter proceeds,
22// Esc goes back.
23//
24// When showCommand is true, Init returns DoneCmd immediately without
25// calling the preview function — the session completes and the caller
26// prints the dry-run output through the normal --show-command path.
27type Confirm struct {
28 previewFn PreviewFunc
29 showCommand bool
30 partial bool
31 preview string
32 selection string
33}
34
35// NewConfirm creates a confirmation screen. The previewFn is called
36// to generate the command preview text. When showCommand is true,
37// the screen auto-completes without user interaction.
38func NewConfirm(previewFn PreviewFunc, showCommand bool) *Confirm {
39 return &Confirm{
40 previewFn: previewFn,
41 showCommand: showCommand,
42 }
43}
44
45// Init starts the preview generation. In --show-command mode, it
46// skips the preview and returns DoneCmd immediately.
47func (c *Confirm) Init() tea.Cmd {
48 c.selection = ""
49
50 if c.showCommand {
51 return ui.DoneCmd
52 }
53
54 return func() tea.Msg {
55 return previewMsg{text: c.previewFn()}
56 }
57}
58
59// Update handles messages. Esc navigates back, Enter confirms.
60func (c *Confirm) Update(msg tea.Msg) (ui.Screen, tea.Cmd) {
61 switch msg := msg.(type) {
62 case previewMsg:
63 c.preview = msg.text
64 return c, nil
65
66 case tea.KeyPressMsg:
67 if msg.Code == tea.KeyEscape {
68 return c, ui.BackCmd
69 }
70 if msg.Code == tea.KeyEnter && c.preview != "" {
71 c.selection = "confirmed"
72 return c, ui.DoneCmd
73 }
74 }
75
76 return c, nil
77}
78
79// View renders the command preview. When partial is set, a note
80// is appended explaining the preview may be incomplete.
81func (c *Confirm) View() string {
82 if c.preview == "" {
83 return ""
84 }
85 if !c.partial {
86 return c.preview
87 }
88 return c.preview + "\n" + partialNote
89}
90
91// partialNote is shown below the preview when the command has no
92// dedicated TUI screens, meaning the session could not collect all
93// inputs interactively.
94const partialNote = "Note: this preview may be incomplete. " +
95 "Additional prompts may follow after confirmation.\n" +
96 "Use --show-command to see the final command after all inputs are collected."
97
98// Title returns the screen's display title.
99func (c *Confirm) Title() string { return "Confirm execution" }
100
101// KeyBindings returns bindings for the help bar.
102func (c *Confirm) KeyBindings() []key.Binding {
103 return []key.Binding{
104 key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "execute")),
105 }
106}
107
108// SetPartial marks the preview as potentially incomplete because
109// the command has no dedicated TUI screens to collect all inputs.
110func (c *Confirm) SetPartial(partial bool) { c.partial = partial }
111
112// Selection returns "confirmed" after the user presses Enter, or ""
113// if the screen has not been confirmed yet.
114func (c *Confirm) Selection() string { return c.selection }