Skip to content

Commit 49e84bc

Browse files
implemented progress popup and improved router with atomic id creation
1 parent b2dd452 commit 49e84bc

7 files changed

Lines changed: 151 additions & 21 deletions

File tree

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ require (
3131

3232
require (
3333
github.com/atotto/clipboard v0.1.4 // indirect
34+
github.com/charmbracelet/harmonica v0.2.0 // indirect
3435
github.com/clipperhouse/displaywidth v0.9.0 // indirect
3536
github.com/clipperhouse/stringish v0.1.1 // indirect
3637
github.com/clipperhouse/uax29/v2 v2.5.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlv
2020
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
2121
github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
2222
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
23+
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
24+
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
2325
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
2426
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
2527
github.com/charmbracelet/log v1.0.0 h1:HVVVMmfOorfj3BA9i8X8UL69Hoz9lI0PYwXfJvOdRc4=

ui/tui/models/components/router/controll.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
)
1010

1111
type Controll struct {
12-
rid int
12+
rid uint32
1313
}
1414

1515
func (c *Controll) Push(model *util.Model) tea.Cmd {

ui/tui/models/components/router/msg.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,35 @@ type InitMsg struct {
1818
// Model-RouterControll -> Router
1919

2020
type PushMsg struct {
21-
rid int
21+
rid uint32
2222
Model *util.Model
2323
}
2424
type PopMsg struct {
25-
rid int
25+
rid uint32
2626
Count int
2727
}
2828
type ChangeMsg struct {
29-
rid int
29+
rid uint32
3030
Model *util.Model
3131
}
3232

33-
func (m InitMsg) routerID() int { return m.RouterControll.rid }
34-
func (m PushMsg) routerID() int { return m.rid }
35-
func (m PopMsg) routerID() int { return m.rid }
36-
func (m ChangeMsg) routerID() int { return m.rid }
33+
func (m InitMsg) routerID() uint32 { return m.RouterControll.rid }
34+
func (m PushMsg) routerID() uint32 { return m.rid }
35+
func (m PopMsg) routerID() uint32 { return m.rid }
36+
func (m ChangeMsg) routerID() uint32 { return m.rid }
37+
38+
// *[InitMsg] implements [RouterMsg]
39+
var _ RouterMsg = (*InitMsg)(nil)
40+
41+
// *[PushMsg] implements [RouterMsg]
42+
var _ RouterMsg = (*PushMsg)(nil)
43+
44+
// *[PopMsg] implements [RouterMsg]
45+
var _ RouterMsg = (*PopMsg)(nil)
46+
47+
// *[ChangeMsg] implements [RouterMsg]
48+
var _ RouterMsg = (*ChangeMsg)(nil)
3749

3850
type RouterMsg interface {
39-
routerID() int
51+
routerID() uint32
4052
}

ui/tui/models/components/router/router.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,28 @@
44
package router
55

66
import (
7+
"sync/atomic"
8+
79
"github.com/charmbracelet/bubbles/help"
810
tea "github.com/charmbracelet/bubbletea"
911
"github.com/toeirei/keymaster/ui/tui/util"
1012
)
1113

12-
var routerID = 1 // TODO change to atomic int... just to be sure
14+
var routerId atomic.Uint32
1315

1416
type Model struct {
15-
id int
16-
size util.Size
17-
modelStack []*util.Model
18-
parentKeyMap help.KeyMap
17+
id uint32
18+
size util.Size
19+
modelStack []*util.Model
20+
parentKeyMap help.KeyMap
1921
}
2022

2123
func New(initialModel *util.Model) (*Model, Controll) {
22-
routerID++
24+
rid := routerId.Add(1)
2325
return &Model{
24-
id: routerID - 1,
25-
modelStack: []*util.Model{initialModel},
26-
}, Controll{
27-
rid: routerID - 1,
28-
}
26+
id: rid,
27+
modelStack: []*util.Model{initialModel},
28+
}, Controll{rid}
2929
}
3030

3131
func (m Model) Init() tea.Cmd {
@@ -71,7 +71,7 @@ func (m Model) View() string {
7171
}
7272

7373
func (m *Model) Focus(parentKeyMap help.KeyMap) tea.Cmd {
74-
m.parentKeyMap = parentKeyMap
74+
m.parentKeyMap = parentKeyMap
7575
return (*m.activeModelGet()).Focus(parentKeyMap)
7676
}
7777

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright (c) 2026 Keymaster Team
2+
// Keymaster - SSH key management system
3+
// This source code is licensed under the MIT license found in the LICENSE file.
4+
package popupviews
5+
6+
import (
7+
"sync/atomic"
8+
9+
"github.com/charmbracelet/bubbles/help"
10+
"github.com/charmbracelet/bubbles/progress"
11+
tea "github.com/charmbracelet/bubbletea"
12+
"github.com/toeirei/keymaster/ui/tui/models/helpers/popup"
13+
"github.com/toeirei/keymaster/ui/tui/util"
14+
)
15+
16+
var progressId atomic.Uint32
17+
18+
type ProgressModel struct {
19+
id uint32
20+
title string
21+
status string
22+
23+
size util.Size
24+
progress float64
25+
progressChan ProgressChan
26+
progressModel progress.Model
27+
}
28+
29+
type Progress struct {
30+
Progress float64
31+
Status string
32+
}
33+
34+
type ProgressMsg struct {
35+
pid uint32
36+
progress Progress
37+
}
38+
39+
type ProgressDoneMsg struct {
40+
pid uint32
41+
}
42+
43+
// use to send current progress and close to finish task/close progress popup
44+
type ProgressChan chan Progress
45+
46+
func OpenProgress(title string) (tea.Cmd, ProgressChan) {
47+
model, progressChan := newProgress(title)
48+
return popup.Open(util.ModelPointer(model)), progressChan
49+
}
50+
51+
func newProgress(title string) (*ProgressModel, ProgressChan) {
52+
progressChan := make(ProgressChan)
53+
return &ProgressModel{
54+
id: progressId.Add(1),
55+
title: title,
56+
progressChan: progressChan,
57+
progressModel: progress.New(),
58+
}, progressChan
59+
}
60+
61+
func (m ProgressModel) Init() tea.Cmd {
62+
return tea.Sequence(
63+
m.progressModel.Init(),
64+
m.ListenProgressCmd,
65+
)
66+
}
67+
68+
func (m *ProgressModel) Update(msg tea.Msg) tea.Cmd {
69+
if m.size.UpdateFromMsg(msg) {
70+
m.progressModel.Width = m.size.Width
71+
return nil
72+
}
73+
74+
if msg, ok := msg.(ProgressMsg); ok && msg.pid == m.id {
75+
m.progress = msg.progress.Progress
76+
m.status = msg.progress.Status
77+
78+
return m.ListenProgressCmd
79+
}
80+
81+
if msg, ok := msg.(ProgressDoneMsg); ok && msg.pid == m.id {
82+
return popup.Close()
83+
}
84+
85+
return nil
86+
}
87+
88+
func (m *ProgressModel) ListenProgressCmd() tea.Msg {
89+
progress, ok := <-m.progressChan
90+
if !ok {
91+
return ProgressDoneMsg{m.id}
92+
}
93+
return ProgressMsg{m.id, progress}
94+
}
95+
96+
func (m ProgressModel) View() string {
97+
// TODO only for testing... size of form needs to be made non greedy
98+
// return lipgloss.NewStyle().MaxWidth(40).Render(m.form.View())
99+
return m.progressModel.ViewAs(m.progress)
100+
}
101+
102+
func (m *ProgressModel) Focus(parentKeyMap help.KeyMap) tea.Cmd {
103+
return util.AnnounceKeyMapCmd(parentKeyMap)
104+
}
105+
106+
func (m *ProgressModel) Blur() {}
107+
108+
// *[ProgressModel] implements [util.Model]
109+
var _ util.Model = (*ProgressModel)(nil)

ui/tui/util/size.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,9 @@ func (s *Size) ToMsg() tea.WindowSizeMsg {
2323
Height: s.Height,
2424
}
2525
}
26+
27+
// type DynamicView interface {
28+
// EagerWidth(width int, members int)
29+
// LazyWidth(width int, members int)
30+
// Weight() int
31+
// }

0 commit comments

Comments
 (0)