Skip to content
This repository was archived by the owner on Sep 18, 2025. It is now read-only.

Commit 6188eff

Browse files
committed
wip filepicker
1 parent c03817c commit 6188eff

File tree

10 files changed

+278
-167
lines changed

10 files changed

+278
-167
lines changed

diff.diff

Lines changed: 0 additions & 124 deletions
This file was deleted.

go.mod

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ require (
1111
github.com/anthropics/anthropic-sdk-go v1.4.0
1212
github.com/aymanbagabas/go-udiff v0.2.0
1313
github.com/bmatcuk/doublestar/v4 v4.8.1
14-
github.com/catppuccin/go v0.3.0
1514
github.com/charlievieth/fastwalk v1.0.11
16-
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250603125125-87aee03b3d4f
15+
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250607113720-eb5e1cf3b09e
1716
github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.3.0.20250602154956-43689cfc0174
1817
github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe
1918
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1.0.20250523195325-2d1af06b557c
@@ -23,9 +22,6 @@ require (
2322
github.com/go-logfmt/logfmt v0.6.0
2423
github.com/google/uuid v1.6.0
2524
github.com/mark3labs/mcp-go v0.17.0
26-
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6
27-
github.com/muesli/reflow v0.3.0
28-
github.com/muesli/termenv v0.16.0
2925
github.com/ncruces/go-sqlite3 v0.25.0
3026
github.com/openai/openai-go v0.1.0-beta.2
3127
github.com/pressly/goose/v3 v3.24.2
@@ -37,6 +33,8 @@ require (
3733
github.com/stretchr/testify v1.10.0
3834
)
3935

36+
require github.com/dustin/go-humanize v1.0.1 // indirect
37+
4038
require (
4139
cloud.google.com/go v0.116.0 // indirect
4240
cloud.google.com/go/auth v0.13.0 // indirect
@@ -60,7 +58,6 @@ require (
6058
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
6159
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
6260
github.com/aws/smithy-go v1.20.3 // indirect
63-
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
6461
github.com/aymerick/douceur v0.2.0 // indirect
6562
github.com/charmbracelet/colorprofile v0.3.1 // indirect
6663
github.com/charmbracelet/x/cellbuf v0.0.14-0.20250516160309-24eee56f89fa // indirect
@@ -85,7 +82,6 @@ require (
8582
github.com/inconshreveable/mousetrap v1.1.0 // indirect
8683
github.com/kylelemons/godebug v1.1.0 // indirect
8784
github.com/lucasb-eyer/go-colorful v1.2.0
88-
github.com/mattn/go-isatty v0.0.20 // indirect
8985
github.com/mattn/go-runewidth v0.0.16 // indirect
9086
github.com/mfridman/interpolate v0.0.2 // indirect
9187
github.com/microcosm-cc/bluemonday v1.0.27 // indirect

go.sum

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -58,24 +58,18 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudr
5858
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ=
5959
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
6060
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
61-
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
62-
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
6361
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
6462
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
6563
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
6664
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
6765
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
6866
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
69-
github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
70-
github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
7167
github.com/charlievieth/fastwalk v1.0.11 h1:5sLT/q9+d9xMdpKExawLppqvXFZCVKf6JHnr2u/ufj8=
7268
github.com/charlievieth/fastwalk v1.0.11/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI=
73-
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250526131538-b3f0c9e42318 h1:f8Q0ybZGxT+St1JfPM7yoz/XFpbmtodcIehaom/9XT8=
74-
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250526131538-b3f0c9e42318/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
75-
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250603122936-f1a3fad2b64e h1:+3I/1v7vbN0Vf8Tjm3Q0zdLQqjOM/TjQBvoRDQtoAss=
76-
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250603122936-f1a3fad2b64e/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
77-
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250603125125-87aee03b3d4f h1:vvNB+i59Wp3L6gYcpuhfAdNjr4/e6qM/st3ySWfmZnU=
78-
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250603125125-87aee03b3d4f/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
69+
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250607105053-36addcd3ab8c h1:Dgy7cOR3skvJjGVnhyaixW6ugxVxLtSjRCiMRSdbXSc=
70+
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250607105053-36addcd3ab8c/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
71+
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250607113720-eb5e1cf3b09e h1:99Ugtt633rqauFsXjZobZmtkNpeaWialfj8dl6COC6A=
72+
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250607113720-eb5e1cf3b09e/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
7973
github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.3.0.20250602154956-43689cfc0174 h1:TlVW+df0rdU/osP0O8DIVS9WFOAzXe3nuiMwJR4n+CA=
8074
github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.3.0.20250602154956-43689cfc0174/go.mod h1:oOn1YZGZyJHxJfh4sFAna9vDzxJRNuErLETr/lnlB/I=
8175
github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40=
@@ -162,21 +156,14 @@ github.com/mark3labs/mcp-go v0.17.0 h1:5Ps6T7qXr7De/2QTqs9h6BKeZ/qdeUeGrgM5lPzi9
162156
github.com/mark3labs/mcp-go v0.17.0/go.mod h1:KmJndYv7GIgcPVwEKJjNcbhVQ+hJGJhrCCB/9xITzpE=
163157
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
164158
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
165-
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
166159
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
167160
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
168161
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
169162
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
170163
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
171164
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
172-
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
173-
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
174165
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
175166
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
176-
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
177-
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
178-
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
179-
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
180167
github.com/ncruces/go-sqlite3 v0.25.0 h1:trugKUs98Zwy9KwRr/EUxZHL92LYt7UqcKqAfpGpK+I=
181168
github.com/ncruces/go-sqlite3 v0.25.0/go.mod h1:n6Z7036yFilJx04yV0mi5JWaF66rUmXn1It9Ux8dx68=
182169
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
@@ -196,7 +183,6 @@ github.com/pressly/goose/v3 v3.24.2 h1:c/ie0Gm8rnIVKvnDQ/scHErv46jrDv9b4I0WRcFJz
196183
github.com/pressly/goose/v3 v3.24.2/go.mod h1:kjefwFB0eR4w30Td2Gj2Mznyw94vSP+2jJYkOVNbD1k=
197184
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
198185
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
199-
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
200186
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
201187
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
202188
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
@@ -312,7 +298,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
312298
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
313299
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
314300
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
315-
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
316301
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
317302
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
318303
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package filepicker
2+
3+
import (
4+
"os"
5+
"strings"
6+
7+
"github.com/charmbracelet/bubbles/v2/filepicker"
8+
tea "github.com/charmbracelet/bubbletea/v2"
9+
"github.com/charmbracelet/lipgloss/v2"
10+
"github.com/opencode-ai/opencode/internal/tui/components/core"
11+
"github.com/opencode-ai/opencode/internal/tui/components/dialogs"
12+
"github.com/opencode-ai/opencode/internal/tui/styles"
13+
)
14+
15+
const (
16+
maxAttachmentSize = int64(5 * 1024 * 1024) // 5MB
17+
FilePickerID = "filepicker"
18+
fileSelectionHight = 10
19+
)
20+
21+
type FilePicker interface {
22+
dialogs.DialogModel
23+
}
24+
25+
type filePicker struct {
26+
wWidth int
27+
wHeight int
28+
width int
29+
filepicker filepicker.Model
30+
selectedFile string
31+
}
32+
33+
func NewFilePickerCmp() FilePicker {
34+
t := styles.CurrentTheme()
35+
fp := filepicker.New()
36+
fp.AllowedTypes = []string{".jpg", ".jpeg", ".png"}
37+
fp.CurrentDirectory, _ = os.UserHomeDir()
38+
fp.ShowPermissions = false
39+
fp.ShowSize = false
40+
fp.AutoHeight = false
41+
fp.Styles = t.S().FilePicker
42+
fp.Cursor = ""
43+
fp.SetHeight(fileSelectionHight)
44+
45+
return &filePicker{filepicker: fp}
46+
}
47+
48+
func (m *filePicker) Init() tea.Cmd {
49+
return m.filepicker.Init()
50+
}
51+
52+
func (m *filePicker) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
53+
switch msg := msg.(type) {
54+
case tea.WindowSizeMsg:
55+
m.wWidth = msg.Width
56+
m.wHeight = msg.Height
57+
m.width = min(70, m.wWidth)
58+
styles := m.filepicker.Styles
59+
styles.Directory = styles.Directory.Width(m.width - 4)
60+
styles.Selected = styles.Selected.PaddingLeft(1).Width(m.width - 4)
61+
styles.DisabledSelected = styles.DisabledSelected.PaddingLeft(1).Width(m.width - 4)
62+
styles.File = styles.File.Width(m.width)
63+
m.filepicker.Styles = styles
64+
return m, nil
65+
}
66+
67+
var cmd tea.Cmd
68+
m.filepicker, cmd = m.filepicker.Update(msg)
69+
70+
// Did the user select a file?
71+
if didSelect, path := m.filepicker.DidSelectFile(msg); didSelect {
72+
// Get the path of the selected file.
73+
m.selectedFile = path
74+
}
75+
76+
return m, cmd
77+
}
78+
79+
func (m *filePicker) View() tea.View {
80+
t := styles.CurrentTheme()
81+
82+
content := lipgloss.JoinVertical(
83+
lipgloss.Left,
84+
t.S().Base.Padding(0, 1, 1, 1).Render(core.Title("Add Image", m.width-4)),
85+
m.imagePreview(),
86+
m.filepicker.View(),
87+
)
88+
return tea.NewView(m.style().Render(content))
89+
}
90+
91+
func (m *filePicker) currentImage() string {
92+
for _, ext := range m.filepicker.AllowedTypes {
93+
if strings.HasSuffix(m.filepicker.HighlightedPath(), ext) {
94+
return m.filepicker.HighlightedPath()
95+
}
96+
}
97+
return ""
98+
}
99+
100+
func (m *filePicker) imagePreview() string {
101+
if m.currentImage() == "" {
102+
return m.imagePreviewStyle().Render()
103+
}
104+
105+
return ""
106+
}
107+
108+
func (m *filePicker) imagePreviewStyle() lipgloss.Style {
109+
t := styles.CurrentTheme()
110+
w, h := m.imagePreviewSize()
111+
return t.S().Base.
112+
Width(w).
113+
Height(h).
114+
Margin(1).
115+
Background(t.BgOverlay)
116+
}
117+
118+
func (m *filePicker) imagePreviewSize() (int, int) {
119+
return m.width - 4, min(20, m.wHeight/2)
120+
}
121+
122+
func (m *filePicker) style() lipgloss.Style {
123+
t := styles.CurrentTheme()
124+
return t.S().Base.
125+
Width(m.width).
126+
Border(lipgloss.RoundedBorder()).
127+
BorderForeground(t.BorderFocus)
128+
}
129+
130+
// ID implements FilePicker.
131+
func (m *filePicker) ID() dialogs.DialogID {
132+
return FilePickerID
133+
}
134+
135+
// Position implements FilePicker.
136+
func (m *filePicker) Position() (int, int) {
137+
row := m.wHeight/4 - 2 // just a bit above the center
138+
col := m.wWidth / 2
139+
col -= m.width / 2
140+
return row, col
141+
}

0 commit comments

Comments
 (0)