tangled
alpha
login
or
join now
treethought.xyz
/
attie
5
fork
atom
AT Protocol Terminal Interface Explorer
5
fork
atom
overview
issues
pulls
pipelines
initial work for full record view
Cam Sweeney
1 month ago
e4741310
214fd2e9
+120
-64
3 changed files
expand all
collapse all
unified
split
ui
app.go
collection.go
record.go
+31
-16
ui/app.go
···
16
)
17
18
type App struct {
19
-
client *at.Client
20
-
search *CommandPallete
21
-
repoView *RepoView
22
-
rlist *RecordsList
23
-
active tea.Model
24
-
err string
25
-
w, h int
0
26
}
27
28
func NewApp() *App {
29
search := &CommandPallete{}
30
repoView := NewRepoView()
31
return &App{
32
-
client: at.NewClient(""),
33
-
search: search,
34
-
repoView: repoView,
35
-
rlist: NewRecordsList(nil),
36
-
active: search,
0
37
}
38
}
39
···
45
cmds := []tea.Cmd{}
46
a.search.SetSize(a.w, a.h)
47
a.repoView.SetSize(a.w, a.h)
48
-
if a.rlist != nil {
49
-
a.rlist.SetSize(a.w, a.h)
50
-
}
51
return tea.Batch(cmds...)
52
}
53
-
54
55
func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
56
switch msg := msg.(type) {
···
72
case a.rlist:
73
a.active = a.repoView
74
return a, nil
0
0
0
75
}
76
}
77
···
89
90
case repoLoadedMsg:
91
cmd := a.repoView.SetRepo(msg.repo)
0
92
a.active = a.repoView
93
a.search.loading = false
94
return a, cmd
···
99
100
case recordsLoadedMsg:
101
cmd := a.rlist.SetRecords(msg.records)
0
102
a.active = a.rlist
103
a.search.loading = false
104
return a, cmd
0
0
0
0
0
0
105
106
case repoErrorMsg:
107
a.search.err = msg.err.Error()
···
163
164
type recordsLoadedMsg struct {
165
records []*agnostic.RepoListRecords_Record
0
0
0
0
166
}
167
168
type repoErrorMsg struct {
···
16
)
17
18
type App struct {
19
+
client *at.Client
20
+
search *CommandPallete
21
+
repoView *RepoView
22
+
rlist *RecordsList
23
+
recordView *RecordView
24
+
active tea.Model
25
+
err string
26
+
w, h int
27
}
28
29
func NewApp() *App {
30
search := &CommandPallete{}
31
repoView := NewRepoView()
32
return &App{
33
+
client: at.NewClient(""),
34
+
search: search,
35
+
repoView: repoView,
36
+
rlist: NewRecordsList(nil),
37
+
recordView: NewRecordView(false),
38
+
active: search,
39
}
40
}
41
···
47
cmds := []tea.Cmd{}
48
a.search.SetSize(a.w, a.h)
49
a.repoView.SetSize(a.w, a.h)
50
+
a.rlist.SetSize(a.w, a.h)
51
+
a.recordView.SetSize(a.w, a.h)
0
52
return tea.Batch(cmds...)
53
}
0
54
55
func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
56
switch msg := msg.(type) {
···
72
case a.rlist:
73
a.active = a.repoView
74
return a, nil
75
+
case a.recordView:
76
+
a.active = a.rlist
77
+
return a, nil
78
}
79
}
80
···
92
93
case repoLoadedMsg:
94
cmd := a.repoView.SetRepo(msg.repo)
95
+
a.repoView.SetSize(a.w, a.h) // Set size before switching view
96
a.active = a.repoView
97
a.search.loading = false
98
return a, cmd
···
103
104
case recordsLoadedMsg:
105
cmd := a.rlist.SetRecords(msg.records)
106
+
a.rlist.SetSize(a.w, a.h) // Set size before switching view
107
a.active = a.rlist
108
a.search.loading = false
109
return a, cmd
110
+
111
+
case recordSelectedMsg:
112
+
a.recordView.SetRecord(msg.record)
113
+
a.recordView.SetSize(a.w, a.h) // Set size before switching view
114
+
a.active = a.recordView
115
+
return a, nil
116
117
case repoErrorMsg:
118
a.search.err = msg.err.Error()
···
174
175
type recordsLoadedMsg struct {
176
records []*agnostic.RepoListRecords_Record
177
+
}
178
+
179
+
type recordSelectedMsg struct {
180
+
record *agnostic.RepoListRecords_Record
181
}
182
183
type repoErrorMsg struct {
+75
ui/record.go
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
package ui
2
+
3
+
import (
4
+
"encoding/json"
5
+
"fmt"
6
+
7
+
"github.com/bluesky-social/indigo/api/agnostic"
8
+
"github.com/bluesky-social/indigo/atproto/syntax"
9
+
"github.com/charmbracelet/bubbles/viewport"
10
+
tea "github.com/charmbracelet/bubbletea"
11
+
"github.com/charmbracelet/lipgloss"
12
+
)
13
+
14
+
type RecordView struct {
15
+
record *agnostic.RepoListRecords_Record
16
+
vp viewport.Model
17
+
header string
18
+
preview bool
19
+
}
20
+
21
+
func NewRecordView(preview bool) *RecordView {
22
+
vp := viewport.New(80, 20)
23
+
return &RecordView{
24
+
vp: vp,
25
+
preview: preview,
26
+
}
27
+
}
28
+
29
+
func (rv *RecordView) SetSize(w, h int) {
30
+
rv.vp.Width = w
31
+
rv.vp.Height = h - lipgloss.Height(rv.header)
32
+
}
33
+
34
+
func (rv *RecordView) buildHeader() string {
35
+
if rv.record == nil {
36
+
return ""
37
+
}
38
+
uri, err := syntax.ParseATURI(rv.record.Uri)
39
+
if err != nil {
40
+
return headerStyle.Render(rv.record.Uri)
41
+
}
42
+
header := rv.record.Uri
43
+
if rv.preview {
44
+
header = fmt.Sprintf("%s/%s", uri.Collection(), uri.RecordKey().String())
45
+
}
46
+
return headerStyle.Render(header)
47
+
}
48
+
49
+
func (rv *RecordView) SetRecord(record *agnostic.RepoListRecords_Record) {
50
+
rv.record = record
51
+
if rv.record == nil || rv.record.Value == nil {
52
+
rv.vp.SetContent("")
53
+
return
54
+
}
55
+
data, err := json.MarshalIndent(rv.record.Value, "", " ")
56
+
if err != nil {
57
+
data = fmt.Appendf([]byte{}, "error marshaling record: %v", err)
58
+
}
59
+
rv.vp.SetContent(string(data))
60
+
rv.header = rv.buildHeader()
61
+
}
62
+
63
+
func (rv *RecordView) Init() tea.Cmd {
64
+
return rv.vp.Init()
65
+
}
66
+
67
+
func (rv *RecordView) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
68
+
var cmd tea.Cmd
69
+
rv.vp, cmd = rv.vp.Update(msg)
70
+
return rv, cmd
71
+
}
72
+
73
+
func (rv *RecordView) View() string {
74
+
return lipgloss.JoinVertical(lipgloss.Left, rv.header, rv.vp.View())
75
+
}
+14
-48
ui/records.go
ui/collection.go
···
1
package ui
2
3
import (
4
-
"encoding/json"
5
"fmt"
6
"strings"
7
8
"github.com/bluesky-social/indigo/api/agnostic"
9
"github.com/bluesky-social/indigo/atproto/syntax"
10
"github.com/charmbracelet/bubbles/list"
11
-
"github.com/charmbracelet/bubbles/viewport"
12
tea "github.com/charmbracelet/bubbletea"
13
"github.com/charmbracelet/lipgloss"
14
)
15
16
-
type RecordView struct {
17
-
record *agnostic.RepoListRecords_Record
18
-
vp viewport.Model
19
-
}
20
-
21
-
func NewRecordView() *RecordView {
22
-
vp := viewport.New(80, 20)
23
-
return &RecordView{
24
-
vp: vp,
25
-
}
26
-
}
27
-
28
-
func (rv *RecordView) SetSize(w, h int) {
29
-
rv.vp.Width = w
30
-
rv.vp.Height = h
31
-
}
32
-
33
-
func (rv *RecordView) SetRecord(record *agnostic.RepoListRecords_Record) {
34
-
rv.record = record
35
-
if rv.record == nil || rv.record.Value == nil {
36
-
rv.vp.SetContent("")
37
-
return
38
-
}
39
-
data, err := json.MarshalIndent(rv.record.Value, "", " ")
40
-
if err != nil {
41
-
data = fmt.Appendf([]byte{}, "error marshaling record: %v", err)
42
-
}
43
-
rv.vp.SetContent(string(data))
44
-
}
45
-
46
-
func (rv *RecordView) Init() tea.Cmd {
47
-
return nil
48
-
}
49
-
50
-
func (rv *RecordView) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
51
-
var cmd tea.Cmd
52
-
rv.vp, cmd = rv.vp.Update(msg)
53
-
return rv, cmd
54
-
}
55
-
56
-
func (rv *RecordView) View() string {
57
-
return rv.vp.View()
58
-
}
59
-
60
type RecordsList struct {
61
rlist list.Model
62
-
preview RecordView
63
header string
64
w, h int
65
}
···
108
l.SetFilteringEnabled(true)
109
rl := &RecordsList{
110
rlist: l,
111
-
preview: RecordView{},
112
}
113
rl.SetRecords(records)
114
return rl
···
171
if item, ok := rl.rlist.SelectedItem().(RecordListItem); ok {
172
rl.preview.SetRecord(item.r)
173
}
0
0
0
0
0
0
0
0
0
0
0
0
174
return rl, cmd
175
}
176
···
1
package ui
2
3
import (
0
4
"fmt"
5
"strings"
6
7
"github.com/bluesky-social/indigo/api/agnostic"
8
"github.com/bluesky-social/indigo/atproto/syntax"
9
"github.com/charmbracelet/bubbles/list"
0
10
tea "github.com/charmbracelet/bubbletea"
11
"github.com/charmbracelet/lipgloss"
12
)
13
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
14
type RecordsList struct {
15
rlist list.Model
16
+
preview *RecordView
17
header string
18
w, h int
19
}
···
62
l.SetFilteringEnabled(true)
63
rl := &RecordsList{
64
rlist: l,
65
+
preview: NewRecordView(true),
66
}
67
rl.SetRecords(records)
68
return rl
···
125
if item, ok := rl.rlist.SelectedItem().(RecordListItem); ok {
126
rl.preview.SetRecord(item.r)
127
}
128
+
switch msg := msg.(type) {
129
+
case tea.KeyMsg:
130
+
switch msg.String() {
131
+
case "enter":
132
+
if item, ok := rl.rlist.SelectedItem().(RecordListItem); ok {
133
+
return rl, func() tea.Msg {
134
+
return recordSelectedMsg{record: item.r}
135
+
}
136
+
}
137
+
}
138
+
}
139
+
140
return rl, cmd
141
}
142