tangled
alpha
login
or
join now
treethought.xyz
/
attie
5
fork
atom
AT Protocol Terminal Interface Explorer
5
fork
atom
overview
issues
pulls
pipelines
better search sizing
Cam Sweeney
3 weeks ago
ad6cc0f0
6bd11701
+127
-76
3 changed files
expand all
collapse all
unified
split
at
client.go
ui
app.go
search.go
+18
-6
at/client.go
···
13
"github.com/bluesky-social/indigo/atproto/syntax"
14
)
15
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
16
type Client struct {
17
dir identity.Directory
18
c *atclient.APIClient
···
43
log.WithFields(log.Fields{
44
"handle": idd.Handle,
45
"DID": idd.DID,
46
-
"PDS": idd.PDSEndpoint(),
47
}).Info("identifier resolved")
48
return idd, nil
49
}
···
54
return nil, fmt.Errorf("failed to lookup identifier: %w", err)
55
}
56
return atclient.NewAPIClient(idd.PDSEndpoint()), nil
57
-
}
58
-
59
-
type RepoWithIdentity struct {
60
-
Identity *identity.Identity
61
-
Repo *comatproto.RepoDescribeRepo_Output
62
}
63
64
func (c *Client) GetRepo(ctx context.Context, repo string) (*RepoWithIdentity, error) {
···
13
"github.com/bluesky-social/indigo/atproto/syntax"
14
)
15
16
+
// response wrappers with identity for easier navigation of views
17
+
18
+
type RepoWithIdentity struct {
19
+
Identity *identity.Identity
20
+
Repo *comatproto.RepoDescribeRepo_Output
21
+
}
22
+
23
+
type RecordsWithIdentity struct {
24
+
Identity *identity.Identity
25
+
Records []*agnostic.RepoListRecords_Record
26
+
}
27
+
28
+
type RecordWithIdentity struct {
29
+
Identity *identity.Identity
30
+
Record *agnostic.RepoGetRecord_Output
31
+
}
32
+
33
type Client struct {
34
dir identity.Directory
35
c *atclient.APIClient
···
60
log.WithFields(log.Fields{
61
"handle": idd.Handle,
62
"DID": idd.DID,
63
+
"PDS": idd.PDSEndpoint(),
64
}).Info("identifier resolved")
65
return idd, nil
66
}
···
71
return nil, fmt.Errorf("failed to lookup identifier: %w", err)
72
}
73
return atclient.NewAPIClient(idd.PDSEndpoint()), nil
0
0
0
0
0
74
}
75
76
func (c *Client) GetRepo(ctx context.Context, repo string) (*RepoWithIdentity, error) {
+5
-70
ui/app.go
···
2
3
import (
4
"context"
5
-
"fmt"
6
7
log "github.com/sirupsen/logrus"
8
···
10
"github.com/bluesky-social/indigo/atproto/identity"
11
"github.com/bluesky-social/indigo/atproto/syntax"
12
"github.com/charmbracelet/bubbles/spinner"
13
-
"github.com/charmbracelet/bubbles/textinput"
14
tea "github.com/charmbracelet/bubbletea"
15
"github.com/treethought/goatie/at"
16
)
···
95
switch msg.String() {
96
case "ctrl+c", "q":
97
return a, tea.Quit
0
0
0
0
98
case "esc":
99
switch a.active {
100
case a.repoView:
101
a.active = a.search
102
a.search.loading = false
103
-
return a, nil
104
case a.rlist:
105
a.active = a.repoView
106
return a, nil
···
249
err error
250
}
251
252
-
type CommandPallete struct {
253
-
ti textinput.Model
254
-
err string
255
-
loading bool
256
-
spinner spinner.Model
257
-
}
258
-
259
-
func (c *CommandPallete) Init() tea.Cmd {
260
-
c.ti = textinput.New()
261
-
c.ti.Placeholder = "Enter handle, DID, or AT URI"
262
-
c.ti.Focus()
263
-
c.spinner = spinner.New()
264
-
c.spinner.Spinner = spinner.Dot
265
-
return textinput.Blink
266
-
}
267
-
268
-
func (c *CommandPallete) SetSize(w, h int) {
269
-
c.ti.Width = w - 2
270
-
}
271
-
272
-
func (c *CommandPallete) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
273
-
274
-
switch msg := msg.(type) {
275
-
case tea.KeyMsg:
276
-
switch msg.String() {
277
-
case "enter":
278
-
val := c.ti.Value()
279
-
if val == "" {
280
-
c.err = "Input cannot be empty"
281
-
return c, nil
282
-
}
283
-
id, err := syntax.ParseAtIdentifier(val)
284
-
if err != nil {
285
-
c.err = fmt.Sprintf("Must use handle, DID or AT URI: %s", err.Error())
286
-
return c, nil
287
-
}
288
-
c.err = ""
289
-
c.loading = true
290
-
return c, func() tea.Msg {
291
-
log.Printf("Looking up identifier: %s", id.String())
292
-
return searchSubmitMsg{identifier: id}
293
-
}
294
-
}
295
-
296
-
}
297
-
298
-
var cmds []tea.Cmd
299
-
ti, tcmd := c.ti.Update(msg)
300
-
c.ti = ti
301
-
cmds = append(cmds, tcmd)
302
-
303
-
sp, scmd := c.spinner.Update(msg)
304
-
c.spinner = sp
305
-
cmds = append(cmds, scmd)
306
-
307
-
return c, tea.Batch(cmds...)
308
-
}
309
-
310
-
func (c *CommandPallete) View() string {
311
-
s := fmt.Sprint("Search:\n", c.ti.View())
312
-
if c.err != "" {
313
-
s += fmt.Sprintf("\nError: %s", c.err)
314
-
} else if c.loading {
315
-
s += "\nLoading... " + c.spinner.View()
316
-
}
317
-
return s
318
-
}
···
2
3
import (
4
"context"
0
5
6
log "github.com/sirupsen/logrus"
7
···
9
"github.com/bluesky-social/indigo/atproto/identity"
10
"github.com/bluesky-social/indigo/atproto/syntax"
11
"github.com/charmbracelet/bubbles/spinner"
0
12
tea "github.com/charmbracelet/bubbletea"
13
"github.com/treethought/goatie/at"
14
)
···
93
switch msg.String() {
94
case "ctrl+c", "q":
95
return a, tea.Quit
96
+
case "ctrl+k":
97
+
a.active = a.search
98
+
a.search.loading = false
99
+
return a, a.search.Init()
100
case "esc":
101
switch a.active {
102
case a.repoView:
103
a.active = a.search
104
a.search.loading = false
105
+
return a, a.search.Init()
106
case a.rlist:
107
a.active = a.repoView
108
return a, nil
···
251
err error
252
}
253
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
+104
ui/search.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
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
+
"fmt"
5
+
6
+
log "github.com/sirupsen/logrus"
7
+
8
+
"github.com/bluesky-social/indigo/atproto/syntax"
9
+
"github.com/charmbracelet/bubbles/spinner"
10
+
"github.com/charmbracelet/bubbles/textinput"
11
+
tea "github.com/charmbracelet/bubbletea"
12
+
"github.com/charmbracelet/lipgloss"
13
+
)
14
+
15
+
type CommandPallete struct {
16
+
ti textinput.Model
17
+
err string
18
+
loading bool
19
+
spinner spinner.Model
20
+
width int
21
+
height int
22
+
}
23
+
24
+
func (c *CommandPallete) Init() tea.Cmd {
25
+
c.ti = textinput.New()
26
+
c.ti.Placeholder = "Enter handle, DID, or AT URI"
27
+
c.ti.Focus()
28
+
c.ti.Width = 60
29
+
c.spinner = spinner.New()
30
+
c.spinner.Spinner = spinner.Dot
31
+
return textinput.Blink
32
+
}
33
+
34
+
func (c *CommandPallete) SetSize(w, h int) {
35
+
c.width = w
36
+
c.height = h
37
+
38
+
// Account for border (2 chars) + padding (4 chars) = 6 total
39
+
maxWidth := 128
40
+
availableWidth := w - 6
41
+
42
+
if availableWidth < maxWidth {
43
+
if availableWidth < 20 {
44
+
availableWidth = 20 // Minimum width
45
+
}
46
+
c.ti.Width = availableWidth
47
+
} else {
48
+
c.ti.Width = maxWidth
49
+
}
50
+
}
51
+
52
+
func (c *CommandPallete) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
53
+
54
+
switch msg := msg.(type) {
55
+
case tea.KeyMsg:
56
+
switch msg.String() {
57
+
case "enter":
58
+
val := c.ti.Value()
59
+
if val == "" {
60
+
c.err = "Input cannot be empty"
61
+
return c, nil
62
+
}
63
+
id, err := syntax.ParseAtIdentifier(val)
64
+
if err != nil {
65
+
c.err = fmt.Sprintf("Must use handle, DID or AT URI: %s", err.Error())
66
+
return c, nil
67
+
}
68
+
c.err = ""
69
+
c.loading = true
70
+
return c, func() tea.Msg {
71
+
log.Printf("Looking up identifier: %s", id.String())
72
+
return searchSubmitMsg{identifier: id}
73
+
}
74
+
}
75
+
76
+
}
77
+
78
+
var cmds []tea.Cmd
79
+
ti, tcmd := c.ti.Update(msg)
80
+
c.ti = ti
81
+
cmds = append(cmds, tcmd)
82
+
83
+
sp, scmd := c.spinner.Update(msg)
84
+
c.spinner = sp
85
+
cmds = append(cmds, scmd)
86
+
87
+
return c, tea.Batch(cmds...)
88
+
}
89
+
90
+
var searchStyle = lipgloss.NewStyle().Padding(1, 2).Border(lipgloss.RoundedBorder()).BorderForeground(lipgloss.Color("62"))
91
+
92
+
func (c *CommandPallete) View() string {
93
+
// make centered search box
94
+
s := c.ti.View()
95
+
if c.err != "" {
96
+
s += fmt.Sprintf("\nError: %s", c.err)
97
+
} else if c.loading {
98
+
s += "\nLoading... " + c.spinner.View()
99
+
}
100
+
101
+
// Center the box in the full terminal window
102
+
box := searchStyle.Render(s)
103
+
return lipgloss.Place(c.width, c.height, lipgloss.Center, lipgloss.Center, box)
104
+
}